2

I finished writing my 6502 emulator and I'm ready to start testing it. I found the nestest ROM with some documentation, but I'm not sure, what the proper way of loading the ROM is. The author says, that emulators should start at 0xC000, which contains 0 when I load the ROM, so I must be doing something wrong.

So right now my loading procedure looks something like this:

clear memory
set PC to 0x8000
open the file
skip first 16 bytes (iNES header)
load the rest of the file into RAM (starting at 0x8000)
set PC to 0xC000

1 Answers1

3

As per Nick Westgate's comment (hence the instant community wiki), the loading procedure is a bit more complicated than you might naively guess:

For now, you can load 0x4000 bytes starting at offset 0x0010, and map that as ROM into both $8000-$BFFF and $C000-$FFFF of the emulated 6502's memory map.

The 'for now' assumes you're going to write a NES emulator and therefore one day will properly parse the NES-related file format that test is stored in and emulate the NES-specific memory mapping scheme that leads to the mirroring of contents.

Ignore the comment in the post before that you should "[log] your PC (and registers) on every cycle", and the implication of the same in that post and onwards; he means that:

  1. before fetching the first operation code byte, make an internal record of the program counter and other registers;
  2. after reading the final operand byte, log all the values you stored back in step (1) plus the full instruction and its disassembly.

If you've simplified things in your emulator to read and execute each operation atomically and then skip time ahead by the number of cycles you should have spent working then you can probably omit the temporary storage. I suspect the author that generated the sample log had implemented such an emulation. Key clues will be a switch table indexed by opcode that is not in some way a coroutine, and/or a lookup table of instruction lengths.

Further advice:

The NES doesn't actually use a 6502. It uses a clone that omits decimal mode — the decimal flag simply has no effect. So if you're emulating a 6502, expect test results to vary there.

For other good 6502 tests, see:

  • AllSuiteA (tests a whole bunch of things and gives you a pass or fail);
  • Klaus Dormann's tests (slightly more of a hassle; will simply enter an infinite loop somewhere if a particular test fails. You'll need to inspect the source to find out which failure you've got); and
  • Wolfgang Lorenz's tests (a whole bunch of separate tests, originally for running separately on a C64 but easy enough to run without anything implemented beyond a 6502 other than that it'll provide text output of status in PETSCII, so you'll also need a quick lookup table to map that to ASCII).

I used all three of those to bootstrap my most recent 6502 emulator, plus some self-written cycle-by-cycle tests for things like interrupts that you wouldn't expect code that sits inside a 6502's address space to be able to handle.

I found out a lot later that I had a very minor deviation in decimal handling — minor enough to pass all those tests, but not sufficient to pass an exhaustive 6502 comparison. I conveniently found a better test in the Acorn BBC community, within the archive attached to this post. I elected to run that by:

  • load the contents of BCDTEST_beeb at 0x2900;
  • write JSR 2900h to address 0x200;
  • put an RTS at 0xffee, but also make sure you can trap that address;
  • set the program counter to 0x200 and keep running until it is at 0x203;
  • then test that the value at 0x84 is 0. If it is something else, that indicates failure.

To get more feedback, any time the PC goes to 0xffee, output the ASCII character in the A register.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • Thanks for your effort ! So I got the nestest loaded and running,and I'm able to log everything, so that's nice. Yes, one day I'm going to parse the iNES header, once other components are fully implemented. –  Nov 02 '17 at 18:59
  • Does the Ricoh CPU actually have the BCD flag ? I noticed nestest tests for it, which is quite strange since nesdev wiki says that the flag should not have any effect (I thought it doesn't even exist). I don't plan to implement BCD mode, so that's why i initially avoided any tests which would test BCD. –  Nov 02 '17 at 19:03
  • My understanding is that it has it, but does not honour it. So you can `CLD`/`SED` and if you `PHP` you'll be able to see the difference, but `ADC` and `SBC` will always act as binary. Assuming the test is completely NES appropriate, maybe the included tests are that you're ignoring it? – Tommy Nov 02 '17 at 19:06
  • Yes, I was ignoring it completely and wasn't aware that CLD/SED actually affect the flag. –  Nov 02 '17 at 19:09
  • I can't tell you how much I appreciate both this question and this answer existing. I was really racking my brain for what was going on here, and this is a lifesaver. Thank you! – devtanc Sep 20 '19 at 16:45
  • Shouldn't a 6502 go to the reset vector on powerup, i.e. 0xFFFC and from there decide where to start execution? – bartlomiej.n Oct 20 '19 at 07:35
  • 1
    @bartlomiej.n yes, and that's therefore what you'd honour when running the NES-centric tests since those are ROMs that are mapped directly into the processor's address space as soon as it is turned on. But many of the other tests, including the BBC one, are files that once existed on tapes or disks for home computers. So they assume an OS will load them into memory of an already-running machine and then do whatever that OS usually does to launch them. So the 6502 did go to the reset vector on power up, but that was a long time ago. – Tommy Oct 21 '19 at 17:31
  • Thank you @Tommy. So from your explanation I understand that usual NES game ROMs are essentially bare metal applications and their execution should start from the reset vector, but in case of nestest, it's supposed to be ran from an OS and therefore execution should start at 0xC000? Is it correct? I tried running nestest by loading the ROM and starting from 0xFFFC on my emulator, but it ended up executing garbage. – bartlomiej.n Oct 22 '19 at 19:09
  • 1
    Oh, no, nestest is indeed bare metal. But the 6502 doesn't actually start at FFFC, it reads a 16-bit address from FFFC (and FFFD), and starts from wherever that points. It's a vector. And with nestest you've to place it memory twice, once starting at 8000 and a second time starting at C000. – Tommy Oct 22 '19 at 19:13