New CPU test ROM

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

New CPU test ROM

Post by blargg » Sat Jan 12, 2008 3:12 pm

I've been working on a new test framework that can run multiple groups of tests and give better feedback. As a first use of it, here's a set of CPU tests that do most instructions, testing several edge cases. If it fails, it prints the opcode and instruction name. It only tests official instructions right now.

blargg_nes_cpu_test5.zip

These tests work like zexall, trying each instruction with all interesting combinations of inputs and checksumming the registers afterwards. After all the different combinations have been fed to a particular instruction, the checksum is compared with the correct one to determine pass/fail.

EDIT: Replaced with version that doesn't stop on failure, so you get a list of all the instructions that failed.
Last edited by blargg on Sat Jan 19, 2008 8:31 pm, edited 2 times in total.

User avatar
Dwedit
Posts: 4233
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit » Sat Jan 12, 2008 3:14 pm

Can you make it continue after an instruction fails?
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!

User avatar
Zepper
Formerly Fx3
Posts: 3190
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper » Sat Jan 12, 2008 5:12 pm

Woot! Mine passed ok!

MatthewCallis
Posts: 82
Joined: Sat Sep 22, 2007 8:32 am
Location: Seattle, WA
Contact:

Post by MatthewCallis » Sat Jan 12, 2008 8:55 pm

Thank you for your work on these test case ROMs, I found some more bugs!

atari2600a
Posts: 324
Joined: Fri Jun 29, 2007 10:25 pm
Location: Earth, Milkyway Galaxy, The Universe, M-Theory
Contact:

Post by atari2600a » Sat Jan 12, 2008 9:18 pm

Any idea if you're going to add illegal opcode support?

Code: Select all

          *=$0000
loop      JMP loop
          .eof

MatthewCallis
Posts: 82
Joined: Sat Sep 22, 2007 8:32 am
Location: Seattle, WA
Contact:

Post by MatthewCallis » Tue Jan 15, 2008 5:52 pm

I've been trying to get this to pass since you've posted it. How exactly is this testing the functions? I'm failing several but is failing of one dependent on the others? I'm getting this (at bottom) but as I go over the functions, everything seems right. Example: ASL, a

Code: Select all

/* ASL */
#define ARITHMATIC_SHIFT_LEFT_A(CYCLES){
   addr = (memory[program_counter + 1] << 8) | memory[program_counter];
   tmp = memory_read(addr);
   carry_flag = (carry_flag & 0xFE) | ((tmp >> 7) & 0x01);
   tmp <<= 1;
   write_memory(addr, tmp);
   sign_flag = tmp & 0x80;
   zero_flag = !(tmp);
   program_counter += 2;
   cycle_count -= CYCLES;
   break;
}

Image

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Tue Jan 15, 2008 8:49 pm

Is tmp an 8-bit variable, or more? It could be that one of the instructions used to do the tests isn't working correctly. What does kevtris' nestest say?

MatthewCallis
Posts: 82
Joined: Sat Sep 22, 2007 8:32 am
Location: Seattle, WA
Contact:

Post by MatthewCallis » Wed Jan 16, 2008 6:13 am

The variable tmp is an unsigned int; nestest gives all OK as does the older NEStress. Though not all of your other test pass yet if that matters too.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Wed Jan 16, 2008 7:21 am

If tmp is an int, then your code calculates the Z flag wrong!

tmp = memory_read(addr);
tmp <<= 1;
zero_flag = !(tmp);

What happens if this reads 0x80 from memory into tmp? After the <<= 1, tmp=0x100 when it should be 0, and the zero flag is erroneously clear!

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Thu Jan 17, 2008 5:20 pm

New version, tests most unofficial instructions now, more thorough instruction coverage, and includes some documentation and full source code. Switched to MMC1, since apparently UxROM doesn't strictly support CHR ROM. Thanks to bunnyboy and thefox for testing help:
blargg_nes_cpu_test5.zip

User avatar
hap
Posts: 355
Joined: Thu Mar 24, 2005 3:17 pm
Contact:

Post by hap » Wed Jan 30, 2008 3:06 pm

I like it, I got errors with cpu.nes on AB ATX, 9C SYA, 9E SXA. I was able to fix ATX by ORing A with $FF, according to documents it's ORed with $EE, $EF, $FE, or $FF depending on CPU internal state like the program counter. Maybe on the NES it's always ORed with $FF, at least it makes your test program a bit happier on my emu.

I couldn't fix my SYA and SXA implementations, can someone explain how these work? I'm currently using this method:

http://www.s-direktnet.de/homepages/k_nadj/opcodes.html

Code: Select all

SAY    ***
This opcode ANDs the contents of the Y register with  and stores the
result in memory.

One supported mode:

SAY abcd,X      ;9C cd ab    ;No. Cycles= 5

Example:

SAY $7700,X     ;9C 00 77

Equivalent instructions:

PHA
TYA
AND #$78
STA $7700,X
PLA

XAS    ***
This opcode ANDs the contents of the X register with  and stores the
result in memory.

One supported mode:

XAS abcd,Y      ;9E cd ab    ;No. Cycles= 5

Example:

XAS $6430,Y     ;9E 30 64

Equivalent instructions:

PHA
TXA
AND #$65
STA $6430,Y
PLA

tepples
Posts: 21706
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples » Wed Jan 30, 2008 5:13 pm

hap wrote:I was able to fix ATX by ORing A with $FF, according to documents it's ORed with $EE, $EF, $FE, or $FF depending on CPU internal state like the program counter.

$EF in particular looks like the mask for a carry coming out of the lower nibble, as might be seen in hardware handling packed binary-coded decimal, such as the original 6502 CPU manufactured by MOS Technology.

Maybe on the NES it's always ORed with $FF, at least it makes your test program a bit happier on my emu.

This might be the case for the NES, which for patent reasons uses a cut-down 6502 core lacking decimal mode.

User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg » Thu Jan 31, 2008 6:28 am

I just tested and 9C and 9E are screwey. I need to take them off the test. Depending on X and Y they sometimes write to $700, among other things. Maybe someone else can figure them out properly.

dvdmth
Posts: 354
Joined: Wed Mar 22, 2006 8:00 am

Post by dvdmth » Thu Jan 31, 2008 6:56 pm

Opcode $AB is known to work differently depending on the machine. Possible outcomes I've seen reported:

1. OR with $EE always
2. OR with $EE, $EF, $FE, or $FF based on the contents of other registers
3. OR with a seemingly random value (possibly influenced by DMA transfers)
4. Perform no OR operation at all (go straight to the AND step)

I think this opcode is triggering a bus conflict, causing the bits in A to get forced high in some cases (machine-dependent). Unless people test this opcode on multiple NES's to see if it works the same on all of them or not, we should assume the exact behavior to be unpredictable.

Exactly what is causing errors with $9C and $9E? Specifically, do problems show up during page boundary crossing, when there's no page crossing, or both? I suspect that since the store value is affected by the upper byte of the target address (the "fixed" version, with 1 added to it), it's possible that a page crossing would also cause an issue with the upper address lines during the store cycle. I haven't seen any docs that give light to this possibility, but that may be because no one tested it.
"Last version was better," says Floyd. "More bugs. Bugs make game fun."

User avatar
hap
Posts: 355
Joined: Thu Mar 24, 2005 3:17 pm
Contact:

Post by hap » Sat Mar 08, 2008 7:35 am

Specifically, do problems show up during page boundary crossing, when there's no page crossing, or both? I suspect that since the store value is affected by the upper byte of the target address (the "fixed" version, with 1 added to it), it's possible that a page crossing would also cause an issue with the upper address lines during the store cycle.
You're right. Not that I've verified this on the NES, but blargg_nes_cpu_test5 passes on my emu if I mask the high address byte with $00 if there's a page crossing on SYA or SXA. Simply ignoring the write on page crossing made it pass too. Though, ORing it with $700 on page crossing (making it sometimes write to $700 like blargg said), makes it fail.

Post Reply