Hacker News new | past | comments | ask | show | jobs | submit login

> (Warning: this pipe currently takes you to an empty room, it's not the only one, and I don't know why.)

If you look into speedruns you'll see that pipe behavior is very strange. One of the top speedruns involves pushing Mario into blocks in order to move the camera by something like 10 px, so that when you go down a certain pipe it goes to the warp zone instead of the bonus area.

Might be worth watching some videos about that speedrun and trying to trace what's going on in the code to allow that. It might get you closer to understanding your own pipe issue.

Unfortunately I don't have a link offhand, but search something like `perfect mario speedrun` and you'll probably find it.




You are thinking of Wrong Warping. Super Mario Bros has, in essence, only one idea for warping out of the level: a table of destinations based on the screen's X location. When you pass an entry, the next entry becomes hot. If an exit is triggered (ie. entering a pipe, climbing a vine, etc), the game will go to the hot entry's starting screen.

Normally, Mario is locked to a maximum screen offset to the right (120 pixels, if I recall) and the table is written with the expectation that Mario will be in bounds when entering the warp; however, there are certain things (bumping things, etc) that that cause Mario to gain a few pixels past the limit. The warp will go to the current hot entry, which is the prior entry to the normal one.

The notorious use is 4-2. You want to use the vine to warp to level 8, but the vine has a huge animation time. There is a warp pipe farther past. It turns out that it's faster to time a series of bumps to get yourself several pixels past the screen limit and use the pipe. The game uses the vine's warp from the table, and now you can continue on without having gone through the vine animation.

So, the emulator may not representing the screen's offset correctly.


Thank you, that's exactly what I was referring to and an excellent explanation.

> So, the emulator may not representing the screen's offset correctly.

Yeah, that or the table's not being filled correctly, or it uses a load instruction that does pointer arithmetic, or something. That's my underinformed theory at least. Probably worth checking out.


Trying to divine the eccentrics of the nes would be a nightmare. In order to emulate the hardware properly, there are several bugs that need to be reproduced such as the oamaddr bug for example. Most small projects like this aren't striving for cycle and bug perfect emulation, just for proof of concept. If you want state of the art emulation for accuracy, look at mesen or bizhawk. Unfortunately they both incorporate so many other features that reading the "core" emulation bits is difficult. So for casual reference, projects like this one are great.

[1] https://github.com/sourmesen/mesen

[2] https://github.com/TASVideos/BizHawk


I've not done NES specifically, but I've written some emulators. As the pack-in game and a cultural landmark, I'd probably be targeting SMB 1 accuracy first and foremost. It doesn't seem to do anything crazy like vertical scrolling, so my ignorant assumption would be that it'd be a good game to start with.

> there are several bugs that need to be reproduced such as the oamaddr bug for example

Given what I've heard about how pipes work, that sounds like a good place to start digging in. Do you have any specific references for that? I'd much rather hear first-hand experience rather than google results, since so much of early console hardware documentation is flat out wrong.


Unfortunately I do not, I've toyed around with small cpu architecture emulation and a touch of gameboy. The bug I was referring to is also called the sprite overflow bug.

http://wiki.nesdev.com/w/index.php/PPU_sprite_evaluation

Edit: There's also a bit of documentation in one of the bizhawk nes cores: https://github.com/TASVideos/BizHawk/tree/master/BizHawk.Emu...


I'll bet you a nickel something screwy is going on with the JMP Indirect logic. If it helps, Super Mario Bros. for the NES has been disassembled with wonderful comments and labels:

https://gist.github.com/1wErt3r/4048722

At some point during every frame, shortly after the NMI routine completes, the game ends up on this particular line of code:

https://gist.github.com/1wErt3r/4048722#file-smbdis-asm-L929

This calculates an address based on the current game mode, and then jumps there. When the player performs any action that changes the level, this mode cycles a few times while the game code blanks the screen, switches gears, and loads in new data. The indirect jump and branching logic that drive the game's mode switching are tricky to get right, and if they're wrong, this little kernel will break in surprising ways.

When I was troubleshooting my branching routines, I had my emulator pause on SMB's first indirect jump, then found that position in the disassembled code and followed along. I mean, you can achieve similar results by unit testing or working to pass all of blargg's tests, but I found it much more fun to trace the emulator in a live environment, running "production" code.


> I'll bet you a nickel something screwy is going on with the JMP Indirect logic.

You've lost a nickel! It was a problem with my signed-offset branch function where a bad cast was allowing 0x80 (-128) to overflow. It was really fun trying to troubleshoot by hooking certain memory accesses and subroutines, but what wound up fixing it was investigating the crash of Blargg's `instr_test-v5/rom_singles/11-stack.nes`. Thank you for your help!


> I'll bet you a nickel something screwy is going on with the JMP Indirect logic

I think we're on the same page, except that you sound like you actually know what you're talking about and I'm just following my gut, also known as `guessing`.

Thanks for the link, that's a great reference!


You are thinking of 4-2. This is the 1-2 pipe.

Here is the video: https://youtu.be/i1AHCaokqhg


Oh I recognize where it is, I've wasted away plenty of time in Mario games over the years.

I brought it up because I'm told that the general idea applies to all pipes. They take you to the secret area for that level, and if there are multiple pipes that lead to different areas then that secret area has to be switched out. That sounds like it's relevant if the secret area in this case is an empty space, perhaps that memory hasn't been initialized or there's a specific load instruction that's doing something different in the emulator as compared to the hardware. (And for all I know the emulator could be following the spec correctly, but the hardware worked differently)

Either way, thank you for the video link.


The key word here is "subpixels."


I understood subpixels to be something different.

The core of what I wanted to point out is the behavior of warp pipes in SMB1, which was outlined quite well here: https://news.ycombinator.com/item?id=21976073

Specifically the part where there's a section in memory somewhere that contains the details of where to warp to, and that varies on Mario's x coordinate.

If a warp pipe in this emulator drops you into an empty level, that sounds to me like that bit of memory isn't getting filled, or there's a jump or load instruction that's supposed to do arithmetic/read args from a register/etc.. and it's not implemented correctly.


Yeah, we're saying two different parts of the related thing. Changing Mario's x coordinate is called manipulating his subpixels, which is necessary to manipulate where the warp pipe sends you.


Ah, I see. When I hear subpixel I think rendering, didn't realize that it was also used for that.


Hi Steve, thanks for all you do for Rust!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: