nestest Error Code Details

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

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

nestest Error Code Details

Post by MatthewCallis »

Hello all,
I am trying to fix several CPU errors in my emulator but I am having trouble actually tracking them down. Using nestest I have found the errors, great, its opcode __, but what facet of opcode __? Are my flags wrong? Program counter error? Overlooked sign issues?
Does anyone have an idea as to where I might look to find more information for nestest errors?

For example:
I'm focusing on testing CMP absolute,X. I get 064h. I change some values around and get the flags seemingly right, I test again and now I get 061h. What happened? I see it testing the code, so I log it to get:

Code: Select all

A : 40
M : 40
Flags: 0 1 1
A : 40
M : 3f
Flags: 0 1 0
A : 40
M : 41
Flags: 1 0 0
A : 80
M : 0
Flags: 0 1 0
A : 80
M : 80
Flags: 0 1 1
A : 80
M : 81
Flags: 1 0 0
A : 80
M : 7f
Flags: 0 1 0
based on http://homepage.ntlworld.com/cyborgsyst ... 02.htm#CMP it seem more correct, and Super Mario Bros still runs fine.
But I have other CPU issues I need to fix and i was hoping there would be a faster way with a more explained error code.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Re: nestest Error Code Details

Post by Disch »

I'm assuming your flags are N, C, Z ? Would've been nice if you labelled them ;P I thought it was NZC at first

anyway I noticed this:

Code: Select all

A : 80
M : 0
Flags: 0 1 0
should be : 1 1 0 (NCZ)

80 - 0 = 80 (negative)
MatthewCallis
Posts: 82
Joined: Sat Sep 22, 2007 8:32 am
Location: Seattle, WA
Contact:

Post by MatthewCallis »

Okay, I've been playing with nestest some more, and I've got more simple problems.

First, I pass absolute,X test, but only with the following code:

Code: Select all

#define COMP_MEM_AIX(REG, CYCLES){													\
	addr = ((memory[program_counter+1] << 8) | memory[program_counter]) + x_reg;	\
	tmp2 = ((REG) - (addr));														\							\
	tmp = memory_read(addr);														\
	carry_flag = (REG >= tmp) ? 1 : 0;												\
	sign_flag = ((REG < tmp) || (tmp == 0)) ? 1 : 0;								\
	zero_flag = (REG == tmp) ? 1 : 0;												\
	printf("A   : %i \n", (signed char)REG);			\
	printf("M   : %i \n", (signed char)addr);			\
	printf("t   : %i \n", (signed char)tmp2);			\
	printf("Flag: %x : %x : %x\n", sign_flag, carry_flag, zero_flag);\
	program_counter += 2;															\
	cycle_count -= CYCLES;															\
	break;																			\
}
This code just doesn't look right with the '|| (tmp == 0)' portion, however the method used by the docs on the subject I've read (tmp & 0x80). This and every other of the CMP / CPY functions fail and I just keep going back to it so I can move on and fix some other issues.
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

Your absolute,X code looks fine.

But I'm not sure about your N flag setting...

All the N flag means is set according to the high bit of the result (see below). That is... if the previous instruction resulted in $80 or higher (high bit set), N is also set. To make your life easier, just deal with everything as unsigned, since most all other behavior acts as if it were signed.

CMP is really a glorified subtraction instruction. The CPU subtracts the given value from a register and sets flags according to the result -- just as SBC does (although CMP doesn't change the register, or V, and it doesn't involve C in the subtraction)

So N would be set when (REG-tmp) & 0x80 is nonzero.

My CMP macro:

Code: Select all

#define CMP(r)									\
	tmp = r - val;								\
	fC = !(tmp >> 8);							\
	fN = fZ = (u8)tmp
'tmp' is a 16-bit temp value
'r' is register being used (A, X, or Y for CMP, CPX, CPY)
'fC' represents the C flag -- nonzero = set
'fZ' represents the Z flag -- zero = set
'fN' represents the N flag -- fN & 0x80 = set


EDIT --

If your x_reg is signed 8-bit... that's a big problem. Make sure it's unsigned.
mattmatteh
Posts: 345
Joined: Fri Jul 29, 2005 3:40 pm
Location: near chicago

Post by mattmatteh »

i noticed that you are using macro's for some instructions. why? i tried that here once and my emulator went from slow to very slow. it makes the binary larger and causes more cache misses.

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

Post by blargg »

i tried that here once and my emulator went from slow to very slow. it makes the binary larger and causes more cache misses.
The effect depends on how the emulator is written. If some other author finds that macros speed it up, then so be it.
mattmatteh
Posts: 345
Joined: Fri Jul 29, 2005 3:40 pm
Location: near chicago

Post by mattmatteh »

i agree with that. seems that alot of people use that and when i tested it, it was slower. sorry for the off topic commment.

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

Post by MatthewCallis »

mattmatteh wrote:i noticed that you are using macro's for some instructions. why? i tried that here once and my emulator went from slow to very slow. it makes the binary larger and causes more cache misses.
I'll give that a test and see if it actually does cause a slowdown (I'm on OSX v10.4.10), it's not slow as is though so I have nothing to lose.
After reading through the forms and links I found, I also pass NOP, SBX and SLO of the undocumented OP codes.
I can't thank you all enough for your help, as it stands I pass everything except:
(indirect,X) test: 59 STA didn't store the data where it was supposed to
(indirect,Y) test: EC should've wrapped zeropage address

And again I'm not sure what the result location I am looking for from the STA write is; nor do I know where it is refering to for the zeropage address.

The code:

Code: Select all

#define STORE_IDI(REG, CYCLES){									\
	addr = memory[program_counter];								\
	tmp = ((memory[addr + 1] << 8) | memory[addr]) + x_reg;		\
	printf("Address: %x\n", addr);								\
	printf("Memory : %x\n", tmp);								\
	write_memory(tmp, REG);										\
	program_counter++;											\
	cycle_count -= CYCLES;										\
	break;														\
}
What I get is

Code: Select all

Address: 80
Memory : 200
Address: 80
Memory : 202
Address: 80
Memory : 203
Address: ff
Memory : 0
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

MatthewCallis wrote: (indirect,X) test: 59 STA didn't store the data where it was supposed to
(Indirect,X) is not (Indirect),X. The comma inside the parenthesis indicates the addition is performed before the indirection.

Inefficient but verbose pseudocode:

Code: Select all

  temp = Read( PC );  // get pointer address
  temp += X;  // add X to it
  temp &= 0xFF;  // keep it on zero page
  addr = Read( temp ); // get low byte of pointer
  temp += 1;
  temp &= 0xFF;  // again, keep it on zero page
  addr |= Read( temp ) << 8;

  // do your STA or whatever to addr
(indirect,Y) test: EC should've wrapped zeropage address
Same situation as above. The pointer is always read from zero page. So LDA ($FF),Y would get the low byte of the pointer from $00FF and the high byte from $0000 -- NOT $0100. But note (Indirect),Y has the comma outside the parenthesis -- indicating you add Y after the indirection.
albailey
Posts: 177
Joined: Thu Jul 13, 2006 3:15 pm

Post by albailey »

Is nestest hosted anywhere else? The link on the wiki doesnt work for me. Sorry for hijacking the thread.

Al
User avatar
Disch
Posts: 1848
Joined: Wed Nov 10, 2004 6:47 pm

Post by Disch »

http://dischmeister.googlepages.com/nestest.zip


I don't have any readmes or anything. The "automated" ROM is the one that runs through the tests without any display or input (for bare-bones CPU emus to be able to test themselves). IIRC, it puts the error code or success code in zero page somewhere, but I can't remember.


[EDIT: Google Pages is dead. I have uploaded a copy of nestest and its readme. --tepples]
Attachments
nestest not original zip.zip
(12.47 KiB) Downloaded 116 times
albailey
Posts: 177
Joined: Thu Jul 13, 2006 3:15 pm

Post by albailey »

Thanks. I havent written an emulator, but I did write a 6502 core as part of a "not very good disassembler", so this'll be useful for me.


Edit - I still have a lot of work to do before I can truly use it.

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

Post by Zepper »

Notice that if a particular instruction has failed in the test, it does NOT imply the error relies on THAT instruction. ^_^;;
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Notice that if a particular instruction has failed in the test, it does NOT imply the error relies on THAT instruction.
You mean the way another failing instruction could cause an "innocent" instruction to get flagged, due to the inherent paradox of testing the CPU with the very instructions that are being tested?
Post Reply