This iNES file tests 6502 instruction timing for all documented instructions except branches, both with and without a page crossing. It also tests some of the undocumented NOP instructions, but no others. If it finds a problem, it prints the opcode and the number of clocks it timed along with the correct timing. It's a bit complex so post if you encounter any odd problems. No source code yet.
cpu_timing_test.zip
Just how long does this take to run? I get the text "6502 TIMING TEST (12 SECONDS)" for a split second followed by a solid black screen for about 12 seconds, then the screen flashes white and turns blue-violet (and then flashes white every so often).
I'm not sure what's happening on your end. I made a few changes to the text console initialization in the version below, but I'm not seeing what could be happening. What kind of hardware are you running it on?
cpu_timing_test2.nes
first run i got fail op :$f8
then uncommented flag_d = 1;
then i got passed
i could search the forums if the search worked, how does the nes handle the d flag ?
both test (cpu_timing_test.nes and cpu_timing_test2.nes) ran the same except the second one removes '6502 timing test (12 seconds).
matt
i just tested nestest and nestress again.
with my d flag code commented out i get:
nestest: fail code 13 flag test
nestress: cpu all ok
with my d flag code enabled i get:
nestest: all ok
nestress: cpu sbc error in d-flag
i am stumped
matt
Evidently, that ROM does something screwy that my emulator doesn't like; exactly
what is going on, I have no idea. Naturally, it works fine on my CopyNES's RAM cart.
If anybody's interested, a partial CPU log can be found at
http://qmt.ath.cx/~nes/cputiming.zip
i tried to unzip your file Quietust and it failed:
skipping: cpu_timing_test2-1.20060915_005436.debug need PK compat. v4.6 (can do v2.1)
how are you zipping that ?
matt
Here's the (somewhat unpolished) main source, in case it helps:
cpu_timing_test.asm The main obscure thing I'm doing is filling the stack with the value $02 to allow thousands of repeated RTS and RTI in those tests, without having to keep refilling the stack. The test loop is copied to $200-$210 or so. RTS should return to $203, and RTI to $202 (the loops are adjusted accordingly). This will require proper stack wrap-around, for one.
mattmatteh, not sure why your emulator is failing. The SED instruction should add two clocks to execution time and set the D flag. The D flag has no effect other than it being remembered; the NES 6502 does not have a decimal mode. I can't imagine why not setting that flag would affect anything timing related. I've found Nestress to be useless as a test since it reports errors for some things when they are working correctly. I recommend
Kevtris' NESTEST for a thorough CPU test.
i just noticed that your first version gives failed op f8 unknown error, and the second gives unknown error f8.
i looked at your source and if i read that right, there are 4 locations that jump to error. is that the code where it would cause fail ? perhaps you could separate those errors ?
thanks
matt
mattmatteh, try this and post the 16-bit hex value it prints at the end of the unknown error report:
cpu_timing_test3.asm
blargg, $0456
thanks
matt
(g'night)
Fixed $E2 timing, test is passed now.
mattmatteh, looks like a CPU error that NESTEST isn't catching. The timing for $F8 is correct, but it's failing in the comparison routine that has a bunch of SBCs in it. Hmmm, could it be that you're supporting 6502's decimal mode?!? The NES does not have that mode; it ignores the D flag. That would explain why it worked when you disabled the flag_d = 1 code! Sounds like your problem. Argh, I guess NESTEST isn't bulletproof after all. :)
mattmatteh wrote:
i tried to unzip your file Quietust and it failed:
skipping: cpu_timing_test2-1.20060915_005436.debug need PK compat. v4.6 (can do v2.1)
how are you zipping that ?
matt
The file is BZIP2-compressed - 7-ZIP should be able to extract it properly, and I'd be surprised if WinRAR or WinZip were unable to do the same.
I get "fail op :$04 UNKNOWN ERROR". May it be that I'm not implementing 2-byte NOPs? Which are those ops? Are those "official" (I mean, are those in the real thing)?
Thx.
Aha, I think I see what's going on.
Apparently, something is going haywire in my CPU when I get an interrupt in the middle of a BRK instruction - it's supposed to switch vectors to the active interrupt (but still push flags as if it was a BRK), but somehow it's loading the
reset vector instead of NMI.
[edit]
Yep, sure enough. Not quite sure what I was thinking when I wrote this part:
Code:
static __forceinline void IN_BRK (void)
{
CPU_MemGet(CPU.PC);
Push(CPU.PCH);
Push(CPU.PCL);
CPU_JoinFlags();
Push(CPU.P | 0x10);
#ifndef NSFPLAYER
if (CPU.WantNMI)
{
CPU.WantNMI = FALSE;
CPU.PCL = CPU_MemGet(0xFFFC);
CPU.PCH = CPU_MemGet(0xFFFD);
}
else
{
CPU.FI = 1;
CPU.PCL = CPU_MemGet(0xFFFE);
CPU.PCH = CPU_MemGet(0xFFFF);
}
#else
CPU.FI = 1;
CPU.PCL = CPU_MemGet(0xFFFE);
CPU.PCH = CPU_MemGet(0xFFFF);
#endif
}
That should be 0xFFFA/0xFFFB in the WantNMI section. That, and the "CPU.FI = 1;" part should be handled for all interrupt modes. Neat, though, that your test happened to uncover an obscure bug in my CPU code entirely by accident.
blargg, that was exactly it. now all 3 versions of your test pass. thanks!
Quietust, so yours pass now ?
Muchaserres, opcode 04 is a DOP, or double no-np. it is a NOP with zero page addressing mode.
matt
mattmatteh wrote:
Muchaserres, opcode 04 is a DOP, or double no-np. it is a NOP with zero page addressing mode.
But are those opcodes "official", are they in the real thing? Any doc with precise info on this?
Thx.
if they were not in the real thing, we wouldnt care. or would we make those up? :)
they are part of the undocumented opcodes. nintendo does use them or support them. some might be from another programming manual that was from the c64 or something. they worked there. but every possible opcode will do something, including jam or halt.
look on the nesdev site and the undocumented opcodes.
Quietest, forgot to post before, 7 zip worked fine, thanks
matt
Quote:
But are those opcodes "official", are they in the real thing?
They are unofficial opcodes that the NES (
my NES, anyway, and apparently Quietust's) recognize. I've encountered some games that use them, so I added them to my emulator and this timing test. I've encountered only a few that use other undocumented instructions, but I haven't investigated those much yet. Read the readme.txt where I list these NOPs and their lengths.
BTW, mattmatteh, each version of the test tests the same thing, just I was making slight changes to the framework to help find bugs in yours and Quietust's emulators.
I should add that I wrote this timing test partly because I found some really lame timing errors in my CPU emulator a few days ago. I hadn't realized that STA and similar do
not have an extra cycle for page crossing in the indexed addressing modes. Fixing that unfortunetely broke a bunch of my movies, so I wanted to be sure I didn't have anything else like that.
mattmatteh wrote:
Quietust, so yours pass now ?
I don't know - I checked it at work during a break (using sourcecode downloaded from my website), and I won't have a chance to fix and recompile until I get home in about 2 hours. Hopefully, though, it'll work properly.
[edit]
Just fixed the bug and reran the ROM - passed with flying colors. Er, with a black and white "PASSED" message.
Neat, PASSED. I noticed that q and i have different cycle tables, though switching between the two doesn't seem to affect the success of the program-- they all look undoc- related (i have a hunch that q's table is more correcter than mine
.
On the boring old topic of "undocumented" opcodes: yes, at least in the 2 revisions of the NES i have tested (front- and top-loading NTSC), the unallocated portion of the opcode space does contain the much-lamented illegal/undocumented/etc. instructions. As has been stated before, they are undefined and implementation (i.e. die design) specific, and i'm sure NCL would not allow their use back in the days of NES game approval.
"Reserved for future use" essentially translates to "Future behaviour not guaranteed". Future behaviour usually is the result of circuitry redesign or changes in manufacturing processes.
Nice work, blargg.
I do plan on eventually getting the timing of the undocumented opcodes too. One problem is that their somewhat unknown behavior makes it easy to crash when testing them. Since my devcart's bootloader is in battery RAM, it gets overwritten easily, and my bootloader reprogrammer has been really flaky.
blargg wrote:
I do plan on eventually getting the timing of the undocumented opcodes too. One problem is that their somewhat unknown behavior makes it easy to crash when testing them. Since my devcart's bootloader is in battery RAM, it gets overwritten easily, and my bootloader reprogrammer has been really flaky.
I found that most of them were identical to the C=64 6510 undocumented ops. If you stay away from the 0x2h HLT opcodes, most of them are stable.
Though admittedly, as you've noted, nearly none are required for accurate reproduction of released NES titles.
baisoku wrote:
Neat, PASSED. I noticed that q and i have different cycle tables, though switching between the two doesn't seem to affect the success of the program-- they all look undoc- related (i have a hunch that q's table is more correcter than mine
.
Err, "cycle table"? My emulator's CPU core doesn't have any "cycle tables" in it - it emulates every instruction as a series of memory accesses, each of which updates the PPU and APU states. I knew that my instruction timing was correct to begin with (from running previous tests), but I ran blargg's test ROM anyways just to make sure that it was working properly - little did I know that it would turn up an obscure bug in my BRK instruction emulation.
i dont have a cycle table either. a double switch. do the addressing mode and instruction. i have a cycle couter that is incremented for each cycle of the instruction. a little different from quietust's
matt
What about opcode $EB? It does not appear as official anywhere, nor in the test's readme, but it's actually being tested. It seems to be a copy of opcode $E9. Is it official or undocumented? Is it any different from opcode $E9?
Anyway, by adding it to my core I pass the test.
i have opcode eb and e9 as sbc, imm addressing mode. i think one of those might be an undocumented, but can not remember.
matt
Quietust wrote:
Err, "cycle table"? My emulator's CPU core doesn't have any "cycle tables" in it - it emulates every instruction as a series of memory accesses, each of which updates the PPU and APU states.
Yeah, you're right. I think i generated it based off of some trace log you posted a while back of your emulator running kevtris' cpu test. Whatever.
Yeah, it also tests $EB as an undocumented equivalent to $E9. Sorry about that. I've added these little doc/code tweaks to my local copy, and I hope to make one more update (including source). It'd be nice if there were a way to select what set of instructions to test, i.e. documented only, documented + $EB + NOPs, documented + all undocumented.
blargg wrote:
It'd be nice if there were a way to select what set of instructions to test, i.e. documented only, documented + $EB + NOPs, documented + all undocumented.
Maybe just read what buttons are pressed at initialization time? Though maybe this won't work on your setup- i remember you mentioning you transfer data through the controller ports...
Good idea. I could have it default to documented instructions only, then enable the current extras with A, and eventually all opcodes with B. It would of course print a message on screen confirming what all it's testing. (The limitation of my devcart is that the controller strobe line is also used to send data to the PC, which isn't much of an issue usually).
Too bad here, unfortunately. I call "CPU clock" one PPU access, rendering 3 pixels. My CPU core is simple regarding the instruction set, of how each opcode is emulated. For some obscure reason, your test ROM is giving +1 cycle error for opcodes $01 and $04 (right now). Opcode $04 is odd... if I take out 1 PPU access, it displays -1 cycle; else, +1 cycle. Go figure...
Any help?
Excellent work, blargg. Very helpful, just like all your other test roms.
OK, I've improved the instruction timing test a lot:
* Now tests either official instructions only, official + all unofficial, or the previous subset of unofficial instructions
* Readme has a lot more useful information now
* Properly reports emulator timing even if it's taking 1 or 9 clocks for an instruction
* Made it less susceptible to unknown error if emulator's timing is off
* Clears decimal mode before using SBC, in case emulator erroneously implements the 6502's decimal mode
* Source code is now included
Thanks for all the feedback and ideas. This test turned out nice.
cpu_timing_test5.zip
EDIT: Unofficial NOP opcode $89 isn't treated as official anymore.
Working nicely, excluding 1 minor fault: the 'official instructions only' test includes unofficial NOP opcode $89.
using the controller buttons on start up doesnt work here. is it possible there could be a delay? or really fancy a menu :)
i am running this from the terminal and using the keyboard for input, holding the A or B input goes to the app as an argument.
thanks
matt
No need to get upset Fx3, it caused this thread to become off-topic, so tepples moved it into a new topic.
I just can't stop improving this (or getting enough sleep!). I improved the page crossing testing to catch one-off errors in either direction. The base addresses now end in $FD. For the normal case, X and Y are set to 2, yielding $FF for the low byte. For the page crossing case, X and Y are set to 3, yielding $00 for the low byte and a carry. The old test used a base address that ended in $80 and set X and Y to 0 for normal, $FE for page crossing. This wouldn't catch any one-off errors.
cpu_timing_test6.zip
And I added a 1/2 second delay for mattmatteh (I actually thought about this issue before but figured it wouldn't be a problem).
(Fx3: I asked tepples to split the hardware discussion off since it wasn't related to the number of clocks an instruction takes to execute, and I though it was a good topic that deserved its own thread. Seriously, chill out.)
thanks blargg. that worked. test6 caught an error in one of my addressing modes, but the others did not. and also didnt have all the undocumented coded yet; only the ones that nestest and nesstress test for. also had the wrong addressing mode for opcode b3.
matt
As usual, blargg, excellent work. My undoc op code was handling page crossing all wrong-like.
Sorry to bump an old thread, but I'm encountering an error in cpu_timing_test6 and the readme says to post questions and I'm not sure what I'm doing wrong.
Fail OP: $40
UNKNOWN ERROR
$FEDF
$40 is an RTI and the readme says that RTI can be a special case that might reveal non timing problems in an emulator.
I'm able to pass branch_timing_tests (all 3. Basic, Backward and Forward)
I also pass nestest (normal ops)
Any ideas what I might be doing wrong with RTI?
I have it set to 6 cycles (for timing)
The guts of my RTI looks like this
Code:
setFlags(popByteFromStack());
int lowByte = popByteFromStack() & 0xFF;
int highByte = popByteFromStack() & 0xFF;
int address = ((highByte << 8) | lowByte) & 0xFFFF;
setProgramCounter(address);
And my doInterrupt looks like this
Code:
int tempPC = getProgramCounter();
byte lowByte = (byte)(tempPC & 0xFF);
byte highByte = (byte)((tempPC >> 8)& 0xFF);
pushByteOnStack(highByte);
pushByteOnStack(lowByte);
pushByteOnStack(getFlags());
setProgramCounter(address);
And help, info or insight is appreciated.
Edit- make the code more readable
Al
i'll make a simple question here in order to not open a new thread needlessly.
about this line:
Code:
if (CPU.WantNMI)
i guess this may be helpful to me so shed some light in the BRK instruction behaviour. every time the procesor goes into a NMI routine i have to push the PC (PC.H first, PC.L then), push the status byte, reset the interrupt flag and load PC with address at the NMI vector. then, when a RTI instruction is found the PC is loaded with address pulled from the stack. the problem comes with the brk instruction. this lines had been extracted from Qeed's doc:
Code:
if (cpu.nmi) //the quirk
{
cpu.nmi = 0;
cpu.pc = rd(0xFFFA);
cpu.pc |= (rd(0xFFFB) << 8);
}
else
{
cpu.pc = rd(0xFFFE);
cpu.pc |= (rd(0xFFFF) << 8);
}
what's that "if(cpu.nmi)"??? what i'm understanding is that when the processor goes into a NMI routine i have to set a flag telling the CPU have a NMI pending, and then that flag is reseted when a RTI intruction is found, but that this flag has nothing to do with the processor's interrupt flag. then, when a BRK instruction is found i have to:
* if there's a NMI pending -> load the PC with the address stored in $FFFA/$FFFB
* if there's no NMI pending -> load the PC with the address stored in $FFFE/$FFFF
also, this differs with Quietust post. in Qeed's doc, if there's a NMI pending the address to load from is $FFFA/$FFFB, and in Quiestust post is $FFFC/$FFFD.
am i getting it correctly?
I think the CPU basically transforms an IRQ or BRK into an NMI if NMI happens to be triggered near the beginning of vectoring.
Mods: please split this and the previous message, as they aren't related to CPU timing.
actually the message i've posted before is related with the topic. look at Quitetust's message and at my message and you'll see. were talking about the same thing: the behaviour of the BRK instruction.