Relative addressing & Branches, signed as two's compleme

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
User avatar
Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina

Relative addressing & Branches, signed as two's compleme

Post 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.
User avatar
sahib
Posts: 26
Joined: Fri Jan 08, 2010 10:41 pm
Location: peninsular scandinavia

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

Post 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.
User avatar
Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina

Post by Petruza »

But substracting 0x100 to a byte doesn't change it's value.
What does (t-0x100) do?
User avatar
sahib
Posts: 26
Joined: Fri Jan 08, 2010 10:41 pm
Location: peninsular scandinavia

Post 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
User avatar
Petruza
Posts: 311
Joined: Mon Dec 22, 2008 10:45 pm
Location: Argentina

Post 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
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post 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.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

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

Post 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:
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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.
User avatar
sahib
Posts: 26
Joined: Fri Jan 08, 2010 10:41 pm
Location: peninsular scandinavia

Post 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.
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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 :)
User avatar
sahib
Posts: 26
Joined: Fri Jan 08, 2010 10:41 pm
Location: peninsular scandinavia

Post 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!
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post 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?
User avatar
blargg
Posts: 3717
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post 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.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Post 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.
User avatar
ehguacho
Posts: 83
Joined: Tue Mar 09, 2010 11:12 pm
Location: Rosario, Argentina
Contact:

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

Post 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];
}
sorry about my english, i'm from argentina...

http://nestate.uuuq.com
Post Reply