problem emulating BNE instruction in C++

Topics pertaining to the emulation or simulation of the 65xx microprocessors and their peripheral chips.
ehguacho
Posts: 29
Joined: 24 Jun 2009

Post by ehguacho »

nice code too ;D
as we may see there's a lot of ways to emulate the BNE instruction, but just a few of them are really efficient. my code looks quite confusing or complex right now, but i'll optimize it later, when the emulator has been done. thanks again dude! you really helped me out (:
User avatar
BigDumbDinosaur
Posts: 9428
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Post by BigDumbDinosaur »

GARTHWILSON wrote:
The byte following the $D0 BNE op code is a signed number of bytes relative to the address of the first byte after that operand...
To be pedantic about it, the offset is a signed twos complement value.
ehguacho
Posts: 29
Joined: 24 Jun 2009

Post by ehguacho »

BigDumbDinosaur wrote:
GARTHWILSON wrote:
The byte following the $D0 BNE op code is a signed number of bytes relative to the address of the first byte after that operand...
To be pedantic about it, the offset is a signed twos complement value.
you're totally right my friend, but beleave it or not i found some errors while using 2's compliment. but everything went right with 1's complement. so strange :\
ehguacho
Posts: 29
Joined: 24 Jun 2009

Post by ehguacho »

BigEd wrote:

Code: Select all

  unsigned short int ea;
  ea= memory[PC++];
  if (ea & 0x80) ea -= 0x100;
  if (cond)  {
      PC += ea;
    }  else  {
      PC++;
    }
the most effective way ever seen ;)
teamtempest
Posts: 443
Joined: 08 Nov 2009
Location: Minnesota
Contact:

Post by teamtempest »

ehguacho wrote:
yeeeppp! i finally could make it! here's the code i get so far:

Code: Select all

case 0xD0: // BNE Relative

		printf("%X. BNE Relative      zflag = %i\n",pc,zflag);
		pc += 2;
		if(zflag == 0)
		{
		     pc--;
		     if(memory[pc] > 0x7f) pc -= (~memory[pc] & 0x00ff);
		     else pc += (memory[pc] & 0x00ff);
		}
		break;
I'm a little puzzled by this code. Does it work if the operand at "memory[pc]" has the value zero? As in:

Code: Select all


         bne there:
there:   lda #$80

Which is legal (though useful perhaps only for playing with timing) and when assembled produces:

Code: Select all


        D0 00
        A9 80

In the emulator code above, if "zflag" is not zero, "pc" gets incremented by two (past the zero operand value). If "zflag" is zero, "pc" gets incremented by only one (and still points at the zero operand value). Fetching that zero operand as the next instruction would trigger a BRK.

Unless I'm missing something, I do not think this is correct. Perhaps there is another increment somewhere else in the loop?
kc5tja
Posts: 1706
Joined: 04 Jan 2003

Post by kc5tja »

ehguacho wrote:
BigEd wrote:

Code: Select all

  unsigned short int ea;
  ea= memory[PC++];
  if (ea & 0x80) ea -= 0x100;
  if (cond)  {
      PC += ea;
    }  else  {
      PC++;
    }
the most effective way ever seen ;)
This absolutely will crash. Get rid of the second PC++.

Code: Select all

  unsigned short int ea;
  ea = memory[PC++];
  if (ea & 0x80) ea -= 0x100;
  if (cond) PC += ea;
Remember that the effective address is relative to the PC after the CPU's already read the effective address byte. That is, it's relative to the next instruction.

Since you already post-increment PC when fetching the effective address, PC already points to the next instruction. No need to increment it again if the condition is false.
leeeeee
In Memoriam
Posts: 347
Joined: 30 Aug 2002
Location: UK
Contact:

Post by leeeeee »

Code: Select all

  unsigned short int ea;
  ea = memory[PC++];
  if (cond) {
      ea += 0xFF80;
      ea ^= 0xFF80;
      PC += ea;
      }
Lee.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Post by BigEd »

kc5tja wrote:
BigEd wrote:

Code: Select all

  unsigned short int ea;
  ea= memory[PC++];
  if (ea & 0x80) ea -= 0x100;
  if (cond)  {
      PC += ea;
    }  else  {
      PC++;
    }
This absolutely will crash.
The error is of course mine: run6502 works, after all. The source is macro-heavy and I flattened it incorrectly. I'll try again:

Code: Select all

unsigned short int ea;
if (cond)  {
   ea= memory[PC++];
   if (ea & 0x80) ea -= 0x100;
   PC += ea;
}  else  {
   PC++;
}
You'll notice it doesn't even read the offset if the condition is false - it's a very efficient emulator.
OwenS
Posts: 105
Joined: 26 Jul 2007

Post by OwenS »

Whats wrong with

Code: Select all

if (cond)  {
   PC += (char) memory[++PC];
}  else  {
   PC++;
}
The 6502 is twos complement. Your machine is twos complement*. Why not use it's ability to handle it natively? It will be much faster than messing around with it yourself and branching

* I say this with 99.99% probability

EDIT: Changed PC++ to ++PC to reflect original ordering
teamtempest
Posts: 443
Joined: 08 Nov 2009
Location: Minnesota
Contact:

Post by teamtempest »

Here's one way:

Code: Select all

  unsigned short int ea;

  ea = cond ? memory[PC++] : 1;
  PC += ea - ((ea & 0x80) << 1);

And another:

Code: Select all

  unsigned short int ea;

  if ( cond ) {
    ea = memory[ PC++ ];
    PC += ea - ((ea & 0x80) << 1);
 }
 else
    PC++;

And one more:

Code: Select all

  unsigned short int ea;

  ea = memory[ PC++ ];
  if ( cond )
    PC += ea - ((ea & 0x80) << 1);

I think I like that last one best, even though it does an "unnecessary read".
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Post by BigEd »

leeeeee wrote:

Code: Select all

  unsigned short int ea;
  ea = memory[PC++];
  if (cond) {
      ea += 0xFF80;
      ea ^= 0xFF80;
      PC += ea;
      }
Nice! Avoiding an extra branch feels like a good thing, although it's never wise to second-guess micro-optimisations. My ultimate preference would be for code which is "obviously" right, and efficient. Although I like this one, I had to think a bit.

Maybe I like OwenS' code best: inform the compiler of signedness, and have it generate the best machine code for sign-extension. It's short and simple, but you can only write it if you already understand very well what you need to do and how your language works.

I don't think I've yet seen code which would be ideal as a teaching example. Maybe you have to write your own, and struggle.

It's interesting that you can get a long way - even in C - without deeply understanding type conversions and machine operations. It's a good thing that so many people like to write an emulator - the value isn't in having one, but in creating one.
OwenS
Posts: 105
Joined: 26 Jul 2007

Post by OwenS »

Particularly on x86, where branches can become hideously expensive (Though unlikely in this case, since it's a very short stream of instructions), avoiding them is good. Might as well use the sign extended load instruction:

Code: Select all

movszS displacement(%base, %index, multiple), %destreg
Where S is the size (b byte, w word, l long, q quad) - thats AT&T syntax, so % designates a register
ehguacho
Posts: 29
Joined: 24 Jun 2009

Post by ehguacho »

well this is the code i wrote and it works nice!

Code: Select all

case 0xd0: // BNE Relative
     pc += 2;
     branch = mem[pc-1];
     if(z_flag == 0) pc += ((signed char)branch);
     break;
heronfisher
Posts: 2
Joined: 12 Mar 2010
Location: london

Post by heronfisher »

This 6502 Cpu emulator was coded in Visual Basic by Don Jarrett. It will prove to be the main CPU emulator used in VB5 Emulators (It's been used in PCSloMo). This is the final release version. Compile it using Visual Basic 5.0.
ehguacho
Posts: 29
Joined: 24 Jun 2009

Post by ehguacho »

sorry for revive old posts, but let me just paste my final code:

(C# Language)

Code: Select all

private void Branch(bool Flag, bool Condition)
{
     PC += 2; // PC points to the next instruction after the branch before the branch is taken
     if (Flag == Condition)
     {
          PC--;
          UInt16 Displacement = Memory[PC++]; // Post-increment the PC
          if (Convert.ToBoolean(Displacement & 0x80)) Displacement -= 0x100;
          PC += Displacement;
     }
            
     return;
}
tested in almost 5 NES roms and works nice.
sorry about my english, i'm from Argentina :S

http://felliniycia.comule.com/
Post Reply