Undefined opcodes in PRG rom

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Undefined opcodes in PRG rom
by on (#189175)
I'm writing a NES emulator and the CPU is fully implemented, and I'm currently implementing memory mapping to all the other components (PPU, APU, etc), initially only mapping with NROM in mind. I'm not at the point where I'm actually drawing pixels, but I'm able to load and execute NES roms, and it's actually getting past PPU initialization.

The ROM I've been loading is Mario Bros. (not Super Mario Bros, the original 1983 Mario Bros) and my emulator kept crashing because it was encountering an unexpected opcode. My initial thought was that my opcode decoding was wrong, so I booted up FCEUX and looked at the PRG in its debugger. Lo-and-behold, there are undefined opcodes scattered throughout:

Image

My emulator is crashing at offset $8511 specifically ($d3 is the value). What's the deal with this? Would a fully implemented emulator just never attempt to execute this bogus instruction?
Re: Undefined opcodes in PRG rom
by on (#189176)
I don't think that opcode is the real problem here. Mario Bros.' code is built to run only in the $C000-FFFF region. If you're PC has ended up in $8000-BFFF something else has already gone wrong and you're executing from the wrong location, I think. $8511 should be unreachable.

$D3 is an illegal opcode, but it does have a specific function on the 6502/2A03 that you can (and probably should) implement in your emulator, but the game in question does not rely on any illegal opcodes to function.
Re: Undefined opcodes in PRG rom
by on (#189177)
Try stepping into JSR $CD9E. That routine probably handles a jump table, taking a value in A and using it as an index into a table of function pointers that follow the JSR, not unlike a switch statement in C. Games may use a jump table to call update routines for each game state based on the state ID, movement routines for each enemy based on the enemy type, etc. Super Mario Bros. has something similar, called magic_jmp or JumpEngine depending on which disassembly you're looking at, as does the FDS BIOS at $EAFD.

If my guess is correct, the function addresses are $D34A, $E14A, $D3F9, $D3A8, ... which look normal for an NROM-128 game like this. If execution goes off into the weeds after a JSR $CD9E, your JSR or RTS might be wrong.
Re: Undefined opcodes in PRG rom
by on (#189178)
This is a real-time disassembly that starts from the address at the top of the window and doesn't discern code from data. This means that if the top of the window isn't properly aligned to an instruction, it might disassemble things the wrong way, and if it runs into data, it will certainly disassemble garbage.
Re: Undefined opcodes in PRG rom
by on (#189183)
Never mind the alignment, I don't think this part of ROM is even being executed as code, according to this FCEUX screenshot. The PC isn't in the $8000 range, and there's no breakpoint visible.
Re: Undefined opcodes in PRG rom
by on (#189185)
OP was using FCEUX to look at a disassembly of the region where their emulator had crashed, trying to figure out what was going wrong. It's not FCEUX that tried to execute from $8511. I don't think the disassembly is really an issue here.

Tepples' explanation of the jump table subroutine is correct; that's exactly what the code at $CD9E does. Without knowing about that kind of thing, perhaps OP was presuming that the bytes after that JSR might be run as code (but they will not).

I can't speculate where the real problem is, though. I'd suggest tracing execution until the PC moves into $8000-BFFF. The problem is going to be before that, since it shouldn't be running in that region at all for this ROM.
Re: Undefined opcodes in PRG rom
by on (#189209)
Thanks for the replies everyone, I have to digest some of this but it's mostly making sense.

One thing I'm clearly doing wrong in my emulator is starting execution with PC = 0x8000, where instead it should start at 0xC000. Am in incorrect in believing that this should be mostly equivalent, though? For NROM in particular, isn't the address space starting at 0x8000 a mirror of 0xC000? Following up on this, for any particular game, how do we know which offset to being execution at? Is it mapper specific?

Thanks again for all the responses.
Re: Undefined opcodes in PRG rom
by on (#189210)
When a 6502 CPU comes out of reset, it begins execution at the address stored at $FFFC and $FFFD.

  • If these bytes are 00 80, execution starts at $8000.
  • If these bytes are 00 C0, execution starts at $C000.
  • Otherwise, execution doesn't start at $8000 or $C000.

Treating a reset as JMP ($FFFC) will handle 99.9 percent of cases correctly.
Re: Undefined opcodes in PRG rom
by on (#189214)
wbrian wrote:
One thing I'm clearly doing wrong in my emulator is starting execution with PC = 0x8000, where instead it should start at 0xC000. Am in incorrect in believing that this should be mostly equivalent, though? For NROM in particular, isn't the address space starting at 0x8000 a mirror of 0xC000? Following up on this, for any particular game, how do we know which offset to being execution at? Is it mapper specific?


NROM-128 (16k) mirrors data at $8000 and $C000, but I think the canonical position of the code is $C000, mostly because the ROM already has to include the vector table at $FFFA.

What ROM data is mapped to which ranges is mapper specific, but reset will always start from the position specified at $FFFC regardless of mapper.

Now, the game in question does have identical data at $8000 and $C000, and $FFFC does in fact point to $C000. You are correct to believe that it's "mostly" equivalent in this case. For this particular ROM, I don't see any problems caused by starting at $8000 instead in FCEUX. There are potential differences from starting at a mirrored code address, but I don't believe they affect this game.

So, even though you had not been using the reset vector, it's not actually what caused the problem for you. Maybe tepples' suggestion to look at the subroutine at $CD9E would help? Perhaps you haven't implemented JMP (indirect) correctly, or maybe you store an off-by-one return address on the stack, instead of incrementing it after RTS?
Re: Undefined opcodes in PRG rom
by on (#189223)
wbrian wrote:
Thanks for the replies everyone, I have to digest some of this but it's mostly making sense.

One thing I'm clearly doing wrong in my emulator is starting execution with PC = 0x8000, where instead it should start at 0xC000. Am in incorrect in believing that this should be mostly equivalent, though? For NROM in particular, isn't the address space starting at 0x8000 a mirror of 0xC000? Following up on this, for any particular game, how do we know which offset to being execution at? Is it mapper specific?

Thanks again for all the responses.


In short, $FFFC contains a two-byte pointer to the reset vector, the entry point. This is stored in the native little-endian format.