How to use blargg's instr_test-v5

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

HastatusXXI
Posts: 40
Joined: Sat Aug 25, 2018 7:21 am

How to use blargg's instr_test-v5

Post by HastatusXXI »

Hello.
My 6502 CPU emulator successfully passed nestest (including instruction timing). Now I'm looking forward to use other tests. I came across blargg's instr_test-v5. However, it seems I'm unable to get it work properly. As the header shows it has 32 kB of PRG data, so I loaded it at $8000. Firstly, it runs the ISC tests. However, it's supposed to leave some output at $6000 and a string at $6004, but I see none when I print these positions.

Am I doing something wrong? Is this the best next step after passing nestest? Does anyone have a log of this test, as the one posted in the wiki for nestest?
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Re: How to use blargg's instr_test-v5

Post by Zepper »

You must fetch the RESET vector from the PRG ROM data at $FFFC (low byte) and $FFFD (high byte).
This test ROM starts at $E000, not $8000.
HastatusXXI
Posts: 40
Joined: Sat Aug 25, 2018 7:21 am

Re: How to use blargg's instr_test-v5

Post by HastatusXXI »

Zepper wrote:You must fetch the RESET vector from the PRG ROM data at $FFFC (low byte) and $FFFD (high byte).
This test ROM starts at $E000, not $8000.
Why does it start at $E000. Isn't it a mapper 0?
User avatar
Zepper
Formerly Fx3
Posts: 3262
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Re: How to use blargg's instr_test-v5

Post by Zepper »

HastatusXXI wrote:
Zepper wrote:You must fetch the RESET vector from the PRG ROM data at $FFFC (low byte) and $FFFD (high byte).
This test ROM starts at $E000, not $8000.
Why does it start at $E000. Isn't it a mapper 0?
It's unrelated. The RESET vector is a 16-bit value that sets up the initial PC register. In other words, it points where the program ROM should start running in the CPU space ($8000-$FFFF), but NOT where to load the ROM data.
As 32k PRG ROM, yup, it's loaded at CPU $8000, but the PC register is determined by the RESET vector.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: How to use blargg's instr_test-v5

Post by koitsu »

Regarding vectors: this is a 6502 thing (specifically 65xxx architecture), but is used on pretty much every CPU architecture known to man.

There are 3 vectors on the 6502: RESET, NMI, and IRQ. IRQ is also used for BRK (e.g. brk opcode). Rather than explain them, I'll just link some documents you can read:

https://www.pagetable.com/?p=410
https://wiki.nesdev.com/w/index.php/CPU_memory_map

The term "vector" effectively means "pointer". The vector *locations* (not the values they hold) are effectively hard-coded into the CPU.

RESET is how code even begin executing on the 6502 when powered on or reset. The CPU reads the 2 bytes at $FFFC-FFFD (RESET vector), in the usual little endian format, sets the PC to the values in those bytes, then begins executing code from there. If this makes it easier: you can think of the RESET vector as the CPU essentially doing jmp ($fffc) when it powers on or is reset (not jmp $fffc!).

The values stored in the vectors are part of the ROM, and are determined at assemble-time of the game/program.

Even if a program/game doesn't have use of a particular vector feature (ex. IRQ/BRK), they still have to point the vector to something that contains some valid code, e.g. for unused IRQ/BRK people often point the vector to an address that contains the code rti (i.e. return from interrupt/do nothing).

Now you understand why mapper 0 games with 16KB PRG are mapped to $C000-FFFF rather than $8000-BFFF, or why in mapper-based games there's often a "fixed bank" that always seems to cover the vector regions of the 6502. This should also help explain to you the importance of the NMI vector (esp. when considering bit 7 of $2000, re: execute NMI on VBlank). The CPU needs those vectors!
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: How to use blargg's instr_test-v5

Post by koitsu »

Oh, one thing I forgot. I guess this could be considered an "advanced" topic, but it's easy overlooked when doing an emulator:

The 6502 vectors values can point to anywhere in memory space ($0000 to $FFFF) -- that includes RAM.

Pointing RESET to somewhere in RAM would be silly, but it could work if RAM was somehow pre-populated with values (ex. NVRAM, battery-backed SRAM, etc.). That said, I've never seen the RESET vector pointed to anywhere other than ROM. However...

There *are* games which have their NMI or IRQ/BRK vectors set to locations in RAM. These games have code in their RESET vector routines that VERY early on populate those RAM locations with legitimate values so that the next upcoming NMI or IRQ/BRK goes to someplace that works.

For example, assume the RESET vector points to $CF00 (ROM) and NMI vector points to $06FE (RAM). A part of the game code could look like this (paraphrased):

Code: Select all

$C540:
  ;
  ; ... do NMI stuff here, ex. updates nametables, etc.
  ;
  rti

$CF00:
  sei
  clc
  ;
  ; ... do other stuff here, such as waiting for PPU warm-up, etc..
  ;
  lda #$40
  sta $06fe
  lda #$c5
  sta $06ff    ; Points NMI vector to $C540
  ...
  lda #$80
  sta $2000    ; Enable NMI on VBlank (bit 7 = 1)
After this, when the next NMI (VBlank) fires, the CPU will do jmp ($06fe), which in turn will begin executing the code at $C540. If the programmer wants NMI to do something else the next time VBlank fires, they just have to change the bytes in $06fe-06ff to wherever they want.

A commercial game that does this is Final Fantasy 2 (J) -- it's NMI vector is set to $0100, and the game changes the values at $0100-0101 to move NMI around as needed.
HastatusXXI
Posts: 40
Joined: Sat Aug 25, 2018 7:21 am

Re: How to use blargg's instr_test-v5

Post by HastatusXXI »

Thank you both, you helped me a ton! I definitely forgot about the startup process and the RESET vector. That definitely solved it. And thank you koitsu for your advice on vectors. I'm sure I'll have to put it into practice soon. However, the emulator is behaving in a strange way with this ROM. It gets stuck in an infinite loop. I don't know what the matter is, so I guess comparing the output to a log would come in handy. I'm trying to get a log using Nintendulator. Firstly I'm using the nestest ROM so I make sure I'm able to replicate the log from the wiki I've been using so far. I'm not able to do it. I get a very different log. The process I follow is the following:

1. Start Nintendulator.
2. Open nestest ROM.
3. Run it.
4. Open debugging window and click start log.
5. Start all tests (naturally all of them are OK).
6. Click stop log.

The log file I get is really heavy and has nothing to do with the one from the wiki: http://www.qmtpro.com/~nes/misc/nestest.log. PC even reaches locations not shown in the log of the wiki (such as C28F). What am I doing wrong?
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: How to use blargg's instr_test-v5

Post by koitsu »

You'll need to make sure you're using the ***EXACT*** same ROM as what was used to generate the log in http://www.qmtpro.com/~nes/misc/nestest.log

If you're using even slightly a different ROM, the addresses will likely be completely different, likewise with the code. All it takes is a 1 byte change for things to be off.

The author of that site/file (and Nintendulator) is here on the forum, user Quietust. I'll send him a PM/DM asking him to look at this thread.

What you generally want to do is make your emulator output data in the same format/syntax/etc. as Nintendulator (that nestest.log file). Then run your emulator to generate its own log. Then you compare the two log files using a utility like diff (ex. diff -u) to find out what's wrong. Wrong CPU flags or cycle additions (for page crossing) during operations is the most common and these will show up in such a comparison.

Yes, it is a lot of work. If you expected to it to be easy, you were sadly mistaken! Haha :-)

If your emulator is getting stuck in an "infinite loop" somewhere (e.g. instructions being handled wrong), then diff will help you with this as well because it'll show you where things begin to differ. You can start to examine opcodes/etc. based on that. Getting adc/sbc wrong is the all-time #1 6502 core bug, followed by some others, including stack-related operations and opcodes (ex. jsr/rts). Edit: and branch operations sometimes too. :)
Last edited by koitsu on Thu Aug 30, 2018 5:11 pm, edited 1 time in total.
HastatusXXI
Posts: 40
Joined: Sat Aug 25, 2018 7:21 am

Re: How to use blargg's instr_test-v5

Post by HastatusXXI »

koitsu wrote:You'll need to make sure you're using the ***EXACT*** same ROM as what was used to generate the log in http://www.qmtpro.com/~nes/misc/nestest.log

If you're using even slightly a different ROM, the addresses will likely be completely different, likewise with the code. All it takes is a 1 byte change for things to be off.

The author of that site/file (and Nintendulator) is here on the forum, user Quietust. I'll send him a PM/DM asking him to look at this thread.

What you generally want to do is make your emulator output data in the same format/syntax/etc. as Nintendulator. Then run your emulator to generate its own log. Then you compare the two log files using a utility like diff (ex. diff -u) to find out what's wrong. Wrong CPU flags or cycle additions (for page crossing) during operations is the most common and these will show up in such a comparison.

Yes, it is a lot of work. If you expected to it to be easy, you were sadly mistaken! Haha :-)
I did it exactly that way haha. I've spent about 4 days comparing my emulator's logs of nestest with the one from the wiki. It currently works like a charm. Now I want to move on and I'm testing my emu with this other ROM. However, I don't have a log to make the comparison. I just wanted to produce a nestest log in Nintendulator myself to see if I was capable of producing a proper log and after that generate a log of the blargg's ROM for the comparisons.

I thought I might be doing something wrong with Nintendulator since the log looks so different, not only the PC, but also the instructions: most of the log is repetition of: BEQ - CMP.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: How to use blargg's instr_test-v5

Post by koitsu »

Ah I see. Yeah, I dunno what to tell you about that, re: not having a log to compare with.

Basically you have to know what's "normal". Sure, you can run it in Nintendulator itself and let it run for however long it runs, but you're still going to have to examine the .log results to know what's going on. For example, maybe it loops indefinitely forever (and review the log might show you why). Or maybe it's a buggy version. Or maybe it issues brk to indicate a problem has been found or that it passed OK, and expects the emulator to stop/break-on-brk? Or maybe it's testing undocumented opcodes? This is where you really need documentation on the test ROMs for them to be useful.

This is one of several problems with 6502 test ROMs -- there's really no good "debugging output" model that works to say "instruction X was wrong" and have the thing fail in such a way that you know it; you have to have some instructions/things implemented properly to even get that far. PPU stuff is even worse, IMO.

Maybe another emulator author has a "good" run of how the ROM behaves?

That said: Blargg tends to write documentation with nearly everything he does. He's not around on the forum any more for reasons I will not go into here, but he *does* respond to Email. His documentation/readmes include his Email address, else http://blargg.8bitalley.com/ refer to his website for his contact info and other details (see upper right). SUPER nice guy, and crazily intelligent. We miss him. Tell him I said hi. :-)
Rahsennor
Posts: 479
Joined: Thu Aug 20, 2015 3:09 am

Re: How to use blargg's instr_test-v5

Post by Rahsennor »

The nestest log on the wiki is of just the test. The actual nestest.nes ROM contains other code (a graphical menu IIRC) that will also appear in any log you generate with a "finished" emulator. The "infinite" BEQ loops are probably the ROM waiting for user input at the menu.

Open the log file in an editor and search for the first and last addresses of the routine you want to compare. Assuming you ran it exactly once while logging (and the ROM doesn't bankswitch) the addresses should also appear exactly once each. Trim everything before and after and you've got your reference log.
HastatusXXI
Posts: 40
Joined: Sat Aug 25, 2018 7:21 am

Re: How to use blargg's instr_test-v5

Post by HastatusXXI »

Rahsennor wrote:The nestest log on the wiki is of just the test. The actual nestest.nes ROM contains other code (a graphical menu IIRC) that will also appear in any log you generate with a "finished" emulator. The "infinite" BEQ loops are probably the ROM waiting for user input at the menu.

Open the log file in an editor and search for the first and last addresses of the routine you want to compare. Assuming you ran it exactly once while logging (and the ROM doesn't bankswitch) the addresses should also appear exactly once each. Trim everything before and after and you've got your reference log.
I'll try that later on today when I get my hands on my computer. It sounds like that might be it.

I tried blergg's instr_test-v6, the one provided by Zepper here: https://forums.nesdev.com/viewtopic.php ... 1&start=30 and Nintendulator doesn't pass the third test. As Nintendulator is considered a perfectly accurate emulator I guess the ROM is corrupted in some way, isn't it? If that's the case I'll write an email to blargg as koitsu suggested to ask him for a correct version we can link to the wiki.
User avatar
Quietust
Posts: 1920
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: How to use blargg's instr_test-v5

Post by Quietust »

koitsu wrote:You'll need to make sure you're using the ***EXACT*** same ROM as what was used to generate the log in http://www.qmtpro.com/~nes/misc/nestest.log

If you're using even slightly a different ROM, the addresses will likely be completely different, likewise with the code. All it takes is a 1 byte change for things to be off.

What you generally want to do is make your emulator output data in the same format/syntax/etc. as Nintendulator (that nestest.log file). Then run your emulator to generate its own log. Then you compare the two log files using a utility like diff (ex. diff -u) to find out what's wrong. Wrong CPU flags or cycle additions (for page crossing) during operations is the most common and these will show up in such a comparison.
It's important to note that nestest.nes can be run in two completely different ways: one is to load it like a normal ROM image (i.e. starting at the reset vector) to get an interactive menu, and the other is to ignore the RESET vector and jump directly to $C000 to run the program in Automation mode (which is designed to not rely on a working PPU/APU or controller input, so you can focus on just getting the CPU working by itself). If you want to compare your emulator's output to the above logfile, you must run it in Automation mode.

The full documentation for nestest.nes can be found here (and the ROM image here, in case you have a different version).
koitsu wrote:For example, assume the RESET vector points to $CF00 (ROM) and NMI vector points to $06FE (RAM). A part of the game code could look like this (paraphrased):

Code: Select all

$C540:
  ;
  ; ... do NMI stuff here, ex. updates nametables, etc.
  ;
  rti

$CF00:
  sei
  clc
  ;
  ; ... do other stuff here, such as waiting for PPU warm-up, etc..
  ;
  lda #$40
  sta $06fe
  lda #$c5
  sta $06ff    ; Points NMI vector to $C540
  ...
  lda #$80
  sta $2000    ; Enable NMI on VBlank (bit 7 = 1)
After this, when the next NMI (VBlank) fires, the CPU will do jmp ($06fe), which in turn will begin executing the code at $C540.
Minor correction: you'd set the NMI vector to $06FD and add "LDA #$6C" + "STA $06FD" to the above code (to insert a "JMP indirect" opcode) - otherwise, the 6502 would just jump directly to $06FE and execute an RTI ($40) instruction.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
HastatusXXI
Posts: 40
Joined: Sat Aug 25, 2018 7:21 am

Re: How to use blargg's instr_test-v5

Post by HastatusXXI »

HastatusXXI wrote:
Rahsennor wrote:The nestest log on the wiki is of just the test. The actual nestest.nes ROM contains other code (a graphical menu IIRC) that will also appear in any log you generate with a "finished" emulator. The "infinite" BEQ loops are probably the ROM waiting for user input at the menu.

Open the log file in an editor and search for the first and last addresses of the routine you want to compare. Assuming you ran it exactly once while logging (and the ROM doesn't bankswitch) the addresses should also appear exactly once each. Trim everything before and after and you've got your reference log.
I'll try that later on today when I get my hands on my computer. It sounds like that might be it.

I tried blergg's instr_test-v6, the one provided by Zepper here: https://forums.nesdev.com/viewtopic.php ... 1&start=30 and Nintendulator doesn't pass the third test. As Nintendulator is considered a perfectly accurate emulator I guess the ROM is corrupted in some way, isn't it? If that's the case I'll write an email to blargg as koitsu suggested to ask him for a correct version we can link to the wiki.
I tried what you suggested. There are coincidences, indeed. However, I can't generate exactly the same log. The address $C000 doesn't appear on my log (I tried both the ROM on the wiki and the one provided by Quietust in this post). There's no clue neither of the instruction JMP $C5F5 nor any of the adjacent to it. I started the log from the powerup of Nintendulator, but I can only get intermediate instructions from the log of the wiki recorded. I need to be able to generate complete logs in order to test my emulator properly.

P.S.: Just to clarify: my emulator passes nestest successfully already. I'm only using nestest now with Nintendulator to learn to generate logs, as I have the log of the wiki for comparison. Now I want to test my emulator with blargg's tests, but blergg's instr_test-v6, the one provided by Zepper here: https://forums.nesdev.com/viewtopic.php ... 1&start=30 seems to be corrupted, as Nintendulator doesn't pass the third test.

I guess Quietust wants me to set a breakpoint at $C000 in Nintendulator and run nestest from there. I'm trying it now, but the emulator skips to $C004 for some reason.

Edit: OMG, I saw you can edit the PC (of course) in Nintendulator. I set it to $C000. Sorry for my clumsiness. I could generate the log properly at last.

Edit2: the test provided by Zepper works perfectly on Nintendulator. It's blargg's instr_test-v5 (all_instrs.nes) from the Wiki the one Nintendulator is not able to pass (fails the 3rd test).
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: How to use blargg's instr_test-v5

Post by koitsu »

Thanks Quietust. Yeah, my NMI-routine-in-RAM explanation is incorrect (incl. the code); pretty obvious it's been a while since I've done this. Here's an accurate/better description:

For vectors, the CPU basically doing this:

RESET: jmp ($fffc)
NMI: jsr ($fffa) (sort of, see below)
IRQ/BRK: jsr ($fffe) (sort of, see below)

While there is no indirect JSR on the 6502, and there are very important omitted details of how NMI and IRQ/BRK work (stack operations are involved; please see here for actual behaviour), "conceptually" it acts as a "after-the-instruction-is-executed JSR to the address stored in this vector" thing.

Going back to NMI in RAM and my example, let's try to do it right this time:

Assume RESET vector contains bytes $00 cf (address $CF00) and NMI vector contains bytes $00 06 (address $0600). A part of the game code would look like this (paraphrased);

Code: Select all

$C540:
  ;
  ; ... do NMI stuff here, ex. updates nametables, etc.
  ;
  rti

$CF00:
  sei
  clc
  ;
  ; ... do other stuff here, such as waiting for PPU warm-up, etc..
  ;
  lda #$4c     ; Instruction $4C, JMP absolute
  sta $0600
  lda #$40     ; Low byte of address $C540
  sta $0601
  lda #$c5     ; High byte of address $C540
  sta $0602
  ...
  lda #$80
  sta $2000    ; Enable NMI on VBlank (bit 7 = 1)
When NMI executes, it's going to load the vector contents into PC, so PC=$0600. $0600 now contains the bytes $4c 40 c5, which is decoded/executed as jmp $c540. From that point forward, the program only has to changes what's in $0601 and $0602 if it wants to "move" what NMI executes. Final Fantasy 2 (J) uses this technique (NMI vector points to $0100).
Last edited by koitsu on Sat Sep 01, 2018 9:40 pm, edited 1 time in total.
Post Reply