Page 1 of 1

nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 4:27 am
by samfoo
Hey guys and gals,

I'm in the process of emulating the CPU. I've been running it against nestest and comparing my CPU state with the Nintendulator log. Finally I've run across something that's stumped me.

First: I have emulated no additional hardware. All I'm doing is loading the ROM into memory at 0xc000 and letting it go. I have a suspicion that maybe I'm missing something here and that's what the problem is.

I'm stumped on the following line:

Code: Select all

5013  expected: C6C9  0C A9 A9 *NOP $A9A9 = A9                  A:AA X:97 Y:4E P:EF SP:F7
5013  actual  : C6C9  0C A9 A9 *NOP $A9A9 = 00                  A:AA X:97 Y:4E P:EF SP:F7
So, an absolute read from $a9a9 on one of the unofficial NOP opcodes. In Nintendulator, the value at $a9a9 is $a9, but in mine it's $00.

I've done some scanning through the previous N lines (where 'N' is a relatively large number) of the log and nothing seems to write to $a9a9 (though, of course, it's possible I'm missing something).

Is this something to do with writing to a weird memory-space? I realize it's not that big of a deal, since either way -- a NOP is a NOP and none of the opcodes up to that point seem to have been different than Nintendulator, but the one-off is screwing up my automated testing.

Anyone have any ideas?

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 5:53 am
by beannaich
samfoo wrote:All I'm doing is loading the ROM into memory at 0xc000 and letting it go.
If you're only loading it into $C000 then you'll be missing out on the mirror at $8000. I assume that is the problem, no writes to the ROM area would affect anything on an NROM-128 cart.

You can check if $E9A9 contains $A9, that would prove it more easily.

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 5:59 am
by Rid
Shouldn't ROM be loaded at $8000 and not $C000 ?
And if so, wouldn't $A9A9 be part of PRG-ROM?

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 7:22 am
by tepples
A 16 KiB PRG ROM such as Duck Hunt or Donkey Kong is loaded into both $8000 and $C000. What happens at the hardware level is mirroring: the PRG ROM is ignoring A14 when deciding which byte to output.

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 10:47 am
by WedNESday
2 people took it upon themselves to answer an already-answered question. So why not make it 3!

If a ROM has only 1 bank of PRG (16Kb) then it is loaded into both 8000 and C000.

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 10:54 am
by tokumaru
WedNESday wrote:If a ROM has only 1 bank of PRG (16Kb) then it is loaded into both 8000 and C000.
I think that when people say things like "a bank is loaded" it confuses people that are not familiar with how memory chips work. "Loading" implies some sort of copying, which isn't the case. I guess mirroring is kinda hard to explain.

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 1:22 pm
by WedNESday
tokumaru wrote:
WedNESday wrote:If a ROM has only 1 bank of PRG (16Kb) then it is loaded into both 8000 and C000.
I think that when people say things like "a bank is loaded" it confuses people that are not familiar with how memory chips work. "Loading" implies some sort of copying, which isn't the case. I guess mirroring is kinda hard to explain.
And what is wrong with saying copying into 8000 and C000?

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 1:34 pm
by tepples
For the same reason that a value written to $0123 isn't considered "copied" to $0923, $1123, and $1923. The same RAM just appears four times in the memory map. Likewise, a 16 KiB PRG ROM appears twice in the memory map unless it uses CPU A14 as an additional positive enable (which it doesn't).

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 2:01 pm
by koitsu
It might be easier to describe it as a roundabout form of a pointer / indirect addressing, or masking off certain bits of the address or data lines (over my head here).

Using the word "copying" will result in people writing emulators which literally memcpy() a PRG-ROM region instead of implementing the methodology through a pointer. I remember seeing people do this "back in the day", actually; was cute to see their emulators running games which do lots of PRG or CHR page swapping. Let's waste CPU time due to crappy design, yaaeaeaeayyyy...

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 4:12 pm
by beannaich
Mirroring is accomplished very simply by using the AND operator:

Code: Select all

u8_t PeekByte( u16_t address )
{
    switch ( address & 0xF000U )
    {
    case 0x8000U: return prg[ address & 0x3FFFU ]; // $8000 & $3FFF = $0000
    case 0x9000U: return prg[ address & 0x3FFFU ]; // $9000 & $3FFF = $1000
    case 0xA000U: return prg[ address & 0x3FFFU ]; // $A000 & $3FFF = $2000
    case 0xB000U: return prg[ address & 0x3FFFU ]; // $B000 & $3FFF = $3000
    case 0xC000U: return prg[ address & 0x3FFFU ]; // $C000 & $3FFF = $0000
    case 0xD000U: return prg[ address & 0x3FFFU ]; // $D000 & $3FFF = $1000
    case 0xE000U: return prg[ address & 0x3FFFU ]; // $E000 & $3FFF = $2000
    case 0xF000U: return prg[ address & 0x3FFFU ]; // $F000 & $3FFF = $3000
    }
}
This is more or less how my emulator does things. Although you could also use biased pointers:

Code: Select all

u8_t* bank_0 = prg - 0x8000U;
u8_t* bank_1 = prg - 0xC000U;

u8_t PeekByte( u16_t address )
{
    if ( address & 0x8000U ) // A15, ROMSEL
    {
        if ( address & 0x4000U ) // A14, ignored on NROM-128, used here to decide which bank pointer to address
        {
            return bank_1[ address ]; // ( ( prg - $C000 ) + $C000 ) = $0000
        }
        else
        {
            return bank_0[ address ]; // ( ( prg - $8000 ) + $8000 ) = $0000
        }
    }
}
I prefer the switch/case because it's more explicit and easier to read.

Re: nestest unofficial opcode problem

Posted: Thu Mar 14, 2013 5:36 pm
by samfoo
Awesome! Thanks guys.