Calculating the backward offset of a branch

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Kris1978
Posts: 41
Joined: 21 May 2018

Calculating the backward offset of a branch

Post by Kris1978 »

Branches can also be done in the reverse direction. Here is a rather inefficient,
but illustrative example:
1 ********************************
2 * AL05-LOOP PROGRAM 2A *
3 ********************************
4 *
5 * OBJ $300
6 ORG $300
7 HOME EQU $FC58
8 *
9 START JSR HOME
10 JMP SETX
11 END RTS
12 *
10 SETX LDX #$FF
11 LOOP STX $700
12 DEX
13 BEQ END
14 JMP LOOP

The Monitor listing for this would be:
*300L
0300- 20 58 FC JSR $FC58
0303- 4C 07 03 JMP $0307
0306- 60 RTS
0307- A2 FF LDX #$FF
0309- 8E 00 07 STX $0700
030C- CA DEX
030D- F0 F7 BEQ $0306
030F- 4C 09 03 JMP $0309

In this example, the branch, if taken, will cause the program to move back
up through the listing. To indicate this branch in the opposite direction, the high
bit is set. This is the same technique that is often used to show negative numbers
in assembly-language programs. Please note that it is not just a matter of setting
the high bit. If that were the case, the value following the BEQ command might be
expected to be$89. (The address of the next instruction ($30C) minus where we
want to go to ($303) equals $09
. Then with the high bit on, we have $89.)
This is almost correct. The actual value is arrived at by subtracting the
branch distance from$100. Thus $100 minus $09 equals $F7.


This is taken from page 29 of the Assembly Lines: The complete book [pdf] by Roger Wagner.


My question is: Why to subtract $030C from $0303 (as mr. Wagner says which I really don't understand why he mentions that addresses) and not $030F (the address of the next instruction-after BEQ) from $0306 (the address of the instruction where we REALLY want to go!)

Normally the Program Counter will point to the next instruction, when branch is executed. And since it is a branch, Program Counter will be pushed into the Stack. So, the offset is calculated from the subtraction of the $030F minus the address of the instruction where the Branch instruction points (i.e. $0306).

Am I missing something?
User avatar
8BIT
Posts: 1787
Joined: 30 Aug 2002
Location: Sacramento, CA
Contact:

Re: Calculating the backward offset of a branch

Post by 8BIT »

As the 6502 is decoding the instruction and opcode, it auto increments the program counter after the byte is read. So after it reads the branch offset, it increments the program counter to point at the opcode of the next instruction. At this point, it will decide if the branch should be taken or not. If it does, then it adds the offset to the program counter (no use of the stack happens for branch instructions) and fetches the opcode from the new address. That is why the math works for calculating the offset. Also, if a branch is taken, 1 additional clock cycle is used to perform the addition. AND, if a page boundary gets crossed (carry of the lower 8 bits of the addition), then 1 more clock cycle is needed to adjust the upper program counter byte.

I am not sure why Mr. Wagner stated to subtract $030C. Perhaps it was a simple error.

I hope this helps.

Daryl
Please visit my website -> https://sbc.rictor.org/
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Calculating the backward offset of a branch

Post by BigEd »

It certainly looks like an error to be looking at 303 and 30C - the instruction before the destination and the instruction before the branch could each be 1, 2 or 3 bytes, so the subtraction could cover quite a range of values, only one of which would be correct!

It's really not a good description, in my view. If you have prepared the reader to understand a little of binary arithmetic, then a negative value will be understood.

Here's the book online:
https://archive.org/details/AssemblyLin ... r/page/n47
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Chromatix »

I'll just leave this here:

Code: Select all

	uint16_t rel()  { int8_t off = read(PC++); carryCycle(PC+off,PC); return PC + off; }
whartung
Posts: 1004
Joined: 13 Dec 2003

Re: Calculating the backward offset of a branch

Post by whartung »

This is my version:

Code: Select all

    public int addrRelative() {
        int value = fetchByte(pc++);
        value = signed(value);
        int addr = pc + value;
        addr &= 0xffff;

        return addr;
    }
What does carryCycle do?
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Chromatix »

It just counts the extra clock cycle if the upper half of the address changes. If cycle counting is disabled, it's a no-op.
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Calculating the backward offset of a branch

Post by BigEd »

What C is hiding here, of course, is the sign-extension of the single byte and the application of the addition to the two-byte PC. And hiding that is a good service to the person programming in C.

At the level of explanation in the original article, you've got a two-byte PC and a one-byte offset, and somehow you have to explain how these are combined. If you wanted (or needed) to explain the extra cycle of a page-crossing branch, then explaining the calculation as one or two operations each on a single byte would help.

It's common enough for emulators - especially first attempts at emulators - to distinguish the forward and backward case, and even to use an addition or a subtraction accordingly. And you can certainly make that work. But it misses something, something which explains how the 6502 was such a simple and low-cost implementation, and also explains the page-crossing cycle.

The original explanation is at that level, I think, of a first attempt at understanding, probably without the benefit that we have today, of a peer group who can comment on an offering, and prior efforts which we can refer to. Back in the days of Apple Assembly Lines, I think community and communication were more limited.
Kris1978
Posts: 41
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Kris1978 »

Thank you all so much! :)
Kris1978
Posts: 41
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Kris1978 »

By the way, which book do you consider as the best for self-teaching 6502 assembly language?
User avatar
GARTHWILSON
Forum Moderator
Posts: 8775
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: Calculating the backward offset of a branch

Post by GARTHWILSON »

Kris1978 wrote:
By the way, which book do you consider as the best for self-teaching 6502 assembly language?
No 65xx enthusiast should be without the Eyes & Lichty manual: https://wdc65xx.com/Programming-Manual/ The description there does not do it justice.

There are tons of 1970's 6502 programming books that may have been good for their time, but I think you will really be doing yourself a disservice if you limit yourself to just the original NMOS 6502 instructions. The CMOS 65c02 added new instructions and addressing modes and got rid of the bugs. I compiled a list of the differences at http://wilsonminesco.com/NMOS-CMOSdif/ . Unfortunately most of those often-recommended books came out before the CMOS version of the processor was out.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
User avatar
BigEd
Posts: 11464
Joined: 11 Dec 2008
Location: England
Contact:

Re: Calculating the backward offset of a branch

Post by BigEd »

Zaks' Programming the 6502 was my start, and I'd recommend it. Leventhal is also well thought of.

These days, you could try running through the easy6502 tutorial online. As different people have different learning styles, you need to find what works for you.
whartung
Posts: 1004
Joined: 13 Dec 2003

Re: Calculating the backward offset of a branch

Post by whartung »

I'd be remiss if I didn't add this bit of code as well.

This is from my assembler, so, perhaps, more relevant.

Code: Select all

int result = branchInst.getOperand() - (branchInst.getAddress() + 2);
Let's consider this simple use case:

Code: Select all

0200         BNE LABEL
0202         NOP
0203         NOP
0204 LABEL:  LDA #0
When my assembler first assembles the BNE line, it stores in the Operand value of the instruction, that actual address of the destination. This is no different than if I were assembling, say, a JMP or an absolute LDA.

The Address value is the address of the instruction.

So, in this case, the values for Address is 0200, and the value for Operand is 0204

From the math, we get: result = 0204 - (0200 + 2), which equals 0204 - 0202, which equals 2.

And that's right!

Code: Select all

0000 0200               .ORIGIN $0200
0001 0200 
0002 0200 d0 02         BNE LABEL
0003 0202 ea            NOP
0004 0203 ea            NOP
0005 0204 a9 00 LABEL   LDA #0
0006 0206 
Let's try it in reverse.

Code: Select all

0200 LABEL   LDA #0
0202         NOP
0203         NOP
0204         BNE LABEL
Here, Operand = 0200, and Address = 0204.

So, result = 0200 - (0204 + 2), which equals 0200 - 0206, which equals -6.

-6 is FA in two's complement.
To convert positive 6 to -6, in two's complement, you negate the value and add 1.

Code: Select all

6      = 0000 0110
Negate = 1111 1001
Add 1  = 1111 1010
FA     = 1111 1010
Thus:

Code: Select all

0000 0200               .ORIGIN $0200
0001 0200 
0002 0200 a9 00 LABEL   LDA #0
0003 0202 ea            NOP
0004 0203 ea            NOP
0005 0204 d0 fa         BNE LABEL
So, this code checks out.

This code is a little different from what the simulators are doing at the instruction level, so this little snippet may be more germane.
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Chromatix »

One quibble on terminology: I would say "to negate a two's complement value, you invert all the bits, then add 1." This clearly distinguishes the arithmetic operation from the logical one.

For the same reason, I like the way Lua uses a dedicated concatenation operator (..) instead of overloading addition (+).
Kris1978
Posts: 41
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Kris1978 »

BigEd wrote:
Zaks' Programming the 6502 was my start, and I'd recommend it. Leventhal is also well thought of.

These days, you could try running through the easy6502 tutorial online. As different people have different learning styles, you need to find what works for you.
I have Zak's book (3rd edition) and I have to say that after some chapters of reading, it really confused me rather than helping me in order to learn. :D

I DO want to include the CMOS instruction set! :) 65c02 is the best 8-bit microprocessor IMHO and enhanced Apple IIe Platinum, the best 8-bit machine.

Well, my concern is that most of the books from the late 70's and early 80's "teaching" about 6502 assembly is that they have either a) lots of mistakes or b) the way of teaching is not the ideal for an amateur (at least this is how I feel)

Lance Leventhal's book looks like a very "serious/professional" job (600+ pages) and I may try it.....

And after GARTHWILSON's suggestion, I may as well try Eyes & Lichty manual......

Cheers!
Chromatix
Posts: 1462
Joined: 21 May 2018

Re: Calculating the backward offset of a branch

Post by Chromatix »

From my point of view, the CPU itself is pretty simple and easy to learn. There are three basic areas to master: the operations of the ALU, the addressing modes, and the ways to influence control flow.

What's not so simple is learning to use it effectively, because its simplicity means it has some pretty severe limitations; working around those limitations introduces complexity in your code that isn't inherent in the algorithm for solving your underlying problem. That's why I often sketch out my solutions in C or pseudo-C before translating them into assembly.

With more modern CPU architectures like ARM, the limitations of the CPU itself are much less severe, so your code often looks much more like the algorithm required.
Post Reply