Page 2 of 3
Posted: Fri Jun 26, 2009 3:32 am
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 (:
Posted: Fri Jun 26, 2009 4:22 am
by BigDumbDinosaur
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.
Posted: Fri Jun 26, 2009 12:34 pm
by ehguacho
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 :\
Posted: Tue Dec 15, 2009 3:10 am
by ehguacho
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

Posted: Thu Dec 17, 2009 1:40 am
by teamtempest
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:
Which is legal (though useful perhaps only for playing with timing) and when assembled produces:
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?
Posted: Thu Dec 17, 2009 6:14 am
by kc5tja
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.
Posted: Thu Dec 17, 2009 1:21 pm
by leeeeee
Code: Select all
unsigned short int ea;
ea = memory[PC++];
if (cond) {
ea += 0xFF80;
ea ^= 0xFF80;
PC += ea;
}
Lee.
Posted: Thu Dec 17, 2009 1:49 pm
by BigEd
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.
Posted: Thu Dec 17, 2009 9:16 pm
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
Posted: Fri Dec 18, 2009 2:23 am
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".
Posted: Fri Dec 18, 2009 10:04 am
by BigEd
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.
Posted: Fri Dec 18, 2009 12:13 pm
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
Posted: Sat Dec 19, 2009 10:26 pm
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;
Posted: Tue Mar 16, 2010 10:35 am
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.
Posted: Mon Aug 16, 2010 6:22 pm
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.