Page 1 of 1

Relative addressing & Branches, signed as two's compleme

Posted: Thu Apr 29, 2010 5:08 pm
by Petruza
Hi again, I'm using mainly obelisk.demon.co.uk/6502 and Bnu's doc* as sources for my emu.

*(Bnu only corrected it, but I have no other reference to name it)

That doc says about Relative addressing for branches that:

Code: Select all

The 1  byte number is treated as a signed number - i.e. if bit 7 is 1, the number
  given byt bits 0-6 is negative; if bit 7 is 0, the number is positive. This
  enables a branch displacement of up to 127 bytes in either direction.
  eg  bit no.  7 6 5 4 3 2 1 0    signed value          unsigned value
      value    1 0 1 0 0 1 1 1    -39                   $A7
      value    0 0 1 0 0 1 1 1    +39                   $27
But this is not how 2's complement works. $A7 in 2's complement is -89, not -39
The 6502 uses 2's complement for signed numbers, right?

I read other sources that only specify that the operand for relative addressing is treated as a signed number.

So how does Relative addressing actually work? as the doc says, or treating signed numbers as 2's complement?


Note to admins: If this has already been discussed, my apologies, I searched for Relative, Branch, BCC and didn't find this exact question.

Re: Relative addressing & Branches, signed as two's comp

Posted: Thu Apr 29, 2010 5:14 pm
by sahib
Petruza wrote:Hi again, I'm using mainly obelisk.demon.co.uk/6502 and Bnu's doc* as sources for my emu.

*(Bnu only corrected it, but I have no other reference to name it)

That doc says about Relative addressing for branches that:

Code: Select all

The 1  byte number is treated as a signed number - i.e. if bit 7 is 1, the number
  given byt bits 0-6 is negative; if bit 7 is 0, the number is positive. This
  enables a branch displacement of up to 127 bytes in either direction.
  eg  bit no.  7 6 5 4 3 2 1 0    signed value          unsigned value
      value    1 0 1 0 0 1 1 1    -39                   $A7
      value    0 0 1 0 0 1 1 1    +39                   $27
But this is not how 2's complement works. $A7 in 2's complement is -89, not -39
The 6502 uses 2's complement for signed numbers, right?

I read other sources that only specify that the operand for relative addressing is treated as a signed number.

So how does Relative addressing actually work? as the doc says, or treating signed numbers as 2's complement?


Note to admins: If this has already been discussed, my apologies, I searched for Relative, Branch, BCC and didn't find this exact question.
My CPU emulator defines relative addressing (used in relative branches) like this:

Code: Select all

void M6502::relative()
	{
		byte t = memory->ReadMemory8(PC+1);

		
		address = PC + 2;
		if(t & 0x80)
			address += (t-0x100);
		else
			address += t;
		increment = 2;
	}
it just tests whether the 7th (sign-) bit of the operand is set, and if it is, we're dealing with a negative offset.

Posted: Thu Apr 29, 2010 5:23 pm
by Petruza
But substracting 0x100 to a byte doesn't change it's value.
What does (t-0x100) do?

Posted: Thu Apr 29, 2010 5:25 pm
by sahib
Petruza wrote:But substracting 0x100 to a byte doesn't change it's value.
What does (t-0x100) do?
the code should be read as:

add (t minus 0x100) to the address.

if I was doing t-=0x100, it'd all go ape-shit :D

Posted: Thu Apr 29, 2010 5:29 pm
by Petruza
Oh right, ( t - 0x100 ) casts to more than a byte.
So this is basically getting the 2's complement of t, right?

Because the doc I posted says that if T's sign bit is 1, it's 7 lowest bits should be substracted from PC, and that sounds wrong

PS: Damn I hate signed/unsigned calculations

Posted: Thu Apr 29, 2010 5:35 pm
by tepples
Petruza wrote:But substracting 0x100 to a byte doesn't change it's value.
The "subtracting 0x100" is really a step in sign-extending the byte to a 16-bit integer.
What does (t-0x100) do?
In C at least, it promotes the byte to an int and then does the subtraction.

Re: Relative addressing & Branches, signed as two's comp

Posted: Thu Apr 29, 2010 5:48 pm
by tokumaru
Petruza wrote:The 6502 uses 2's complement for signed numbers, right?
Yeah, the document you quoted is probably wrong.

Also, the relative address will be generated based on the value the PC holds after fetching the opcode and the operand of the branch instruction.

This code will generate an offset of -2 ($FE):

Code: Select all

label:
	bne label
And this will generate an offset of 0 ($00):

Code: Select all

	bne label
label:

Posted: Thu Apr 29, 2010 6:07 pm
by blargg
In two's complement, the high bit of an octet has a weight of -128 (the others have the same as in unsigned). So doing (n-0x100) when the high bit of n is set is just a roundabout way of giving the high bit a weight of -128.

Posted: Thu Apr 29, 2010 6:16 pm
by sahib
blargg wrote:In two's complement, the high bit of an octet has a weight of -128 (the others have the same as in unsigned). So doing (n-0x100) when the high bit of n is set is just a roundabout way of giving the high bit a weight of -128.
seemed like a good idea at the timeā„¢. of course you could use a signed char instead, and steer clear of the confusion. I use unsigned chars' (since all 6502 registers etcetera are unsigned, if my memory serves me correct) consequently though, to avoid stupid bugs that otherwise might creep in to the code otherwise and that I'd be prone to miss.

Posted: Thu Apr 29, 2010 9:41 pm
by blargg
Using signed char makes your code less portable. Your approach is more portable, though there are simpler ways that are fully portable, like pc += (n^0x80)-0x80 :)

Posted: Thu Apr 29, 2010 11:03 pm
by sahib
blargg wrote:Using signed char makes your code less portable. Your approach is more portable, though there are simpler ways that are fully portable, like pc += (n^0x80)-0x80 :)
touche! way more elegant!

Posted: Fri Apr 30, 2010 9:56 am
by tepples
blargg wrote:Using signed char makes your code less portable.
Using subdirectories makes your code less portable.

Are there any popular architectures suitable for NES emulation where (int)(signed char)128 != -128? Specifically, what architectures do people need to worry about other than x86, ARM, and PowerPC?

Posted: Fri Apr 30, 2010 10:49 am
by blargg
How about a compiler with a good optimizer or debugger on said platforms? I know that GCC can do some optimizations on signed values which assume no overflow.

Posted: Sat May 01, 2010 1:15 pm
by koitsu
tepples wrote:Are there any popular architectures suitable for NES emulation where (int)(signed char)128 != -128? Specifically, what architectures do people need to worry about other than x86, ARM, and PowerPC?
The only architecture I can think of where this might matter is older PIC chips like the 16C84 or 16F84, which shouldn't be in use any longer.

Re: Relative addressing & Branches, signed as two's comp

Posted: Mon May 24, 2010 11:51 am
by ehguacho
tokumaru wrote:
Petruza wrote:The 6502 uses 2's complement for signed numbers, right?
Yeah, the document you quoted is probably wrong.

Also, the relative address will be generated based on the value the PC holds after fetching the opcode and the operand of the branch instruction.

This code will generate an offset of -2 ($FE):

Code: Select all

label:
	bne label
And this will generate an offset of 0 ($00):

Code: Select all

	bne label
label:
i guess the doc is wrong.
if the given byte is major than 0x7f (bit 7 is set) the value subtracted from the program counter is not the negative signed given byte, the value subtracted is the 2's compliment of the given byte.

this is the way i implemented it and it works perfect:

Code: Select all

PC += 2;
if(FLAG == VALUE)
{
   PC--;
   if(Mem[PC] > 0x7f) PC -= (~Mem[PC]);
   else PC += Mem[PC];
}