• Please review our updated Terms and Rules here

6502 branch at page boundaries

Dwight Elvey

Veteran Member
Joined
Jun 21, 2003
Messages
4,997
Location
Santa Cruz
Hi
I read the spec for how the relative branch works but still don't get the fine details of exactly what it does.
If the instruction branches forward or backwards when close to a page boundary.
Say the instruction has the branch instruction at xFE which way does it go for a positive or negative branch of say 8 or -8
the same for if at xFF ?
I know someone knows this.
Dwight
 
The branch does what you expect it to do but if the boundary of the page is crossed, the update of the high-byte part of the address will cost one extra cycle.
 
Hi Ruud
I'm aware of the cost of general crossing of a page with the target address. That is not what I'm talking about.
See pdf page 37 ( last page ) of https://www.princeton.edu/~mae412/HANDOUTS/Datasheets/6502.pdf
Notice line T2* I'm assuming if the fetch of the relative number does not work across a page boundary.
The way I read this is:

If the instruction is at 00FFh it will fetch the relative value from 0000h ( not 100h ) and then add with "carry" to the value found at 0000h to address 0001h.
If the instruction is at 00FEh it will fetch the relative value from 00FFh and then add with "carry" to 0000h, not add to the address 0100h .

That is the way I read this. It is the address incrementing that is missing the carry, not the final add of the relative address. Only one add, with
carry, happens as the relative address calculation happens. There is no carry for the PC incrementing, like a normal instruction.
The incrementing of the PC value has no carry, only the final add of the relative branch. I'm told most assemblers will either flag a branch
error or add two nops to avoid this problem.
I could ask Eric if I had his email. I'm sure he'd know.
I recall helping him on his 6502 transistor level board by suggesting that his dynamic nets needed a small capacitor to work.
The 6502 is tuned to run a 1Mhz and not much slower, because of the dynamic nets.
Dwight
 
Last edited:
I've never done much 6502 coding, but I do seem to recall seeing a trick where a negative displacement in a page 0 branch could get you into high memory. The old wetware is fuzzy, however.
 
Sounds to me that you think that the branch does not do what you would intuitively expect it to do around page boundaries, but it does.

I've never seen anyone talk about any consideration for branches and page boundaries, I know I've never given it any thought in my works.

The only place that has issues with this is the early 6502 and indirect JMP. I believe if you try to load an indirect jump from an xxFF address, it loads from xxFF and xx00 rather than xxFF and (xx+1)00. But this was fixed in later revs of the 6502. It's typically an NMOS vs CMOS thing.
 
Trust me, it is a feature of the 6502. I'm just not sure how it manifest itself. I've seen demonstrated but forget what is does.
The one you mention looks similar, " no carry in the middle of the instruction ".
Do note, the indirect doesn't mention this in this description I posted but it does mention it in the relative branch. Maybe you the two confused.
I wouldn't be surprised if it has a similar problem.
Dwight
 
I was intrigued and had to test my recently-acquired breadbin C64 anyway, so I threw together a quick test. I can confirm that, at least on the 6510, the branch instructions behave in the expected manner and do not manifest the wraparound bug that affects JMP-indirect. You can test yourself - at the end of this sequence, $D020 should contain $00:
Code:
POKE 3713, 202 : POKE 3714, 76 : POKE 3715, 129 : POKE 3716,15
POKE 3840, 127
POKE 3968, 232 : POKE 3969, 142 : POKE 3970, 32 : POKE 3971, 208 : POKE 3972, 96
POKE 4093, 162 : POKE 4094, 0 : POKE 4095, 240 : POKE 4096, 128
POKE 4224, 162 : POKE 4225, 8 : POKE 4226, 76 : POKE 4227, 129 : POKE 4228, 15
SYS 4093
 
Last edited:
I'm curious about the relative branch, not the indirect. I guess I'll need to get my SYM2 board out and test it. I need to have some clever way to trap what the code did.
Dwight
 
Not sure there's anything to test. The relative branch Just Works. It does exactly what it's supposed to, and what one would intuitively think it does. The "hard part" of the relative branch is that the value of the branch is based on the PC after the branch instruction. That may not be intuitive.

Consider:
Code:
LOC   CODE         LABEL     INSTRUCTION

0000  A9 00        A         LDA #$00
0002  F0 FC                  BEQ $0000
0004  F0 00                  BEQ $0006
0006  A9 00        B         LDA #$00

Note the first BEQ (F0 FC). It's branch value is -4 (FC is the 2's complement of 4). Because 0000 - 0004 (the start of the instruction after the BEQ) is -4.

Similarly, the second BEQ (F0 00) has an offset of 0, since the PC is already there.

The page boundary, outside of the number of clocks used, has no impact on a relative branch. Only the Indirect JMP is impacted by the page boundary, and that's considered a bug that's been fixed.

And, yes, you can branch backward from zero page into $FFxx.

Doesn't the SYM2 have a single step function?
 
I'm curious about the relative branch, not the indirect. I guess I'll need to get my SYM2 board out and test it. I need to have some clever way to trap what the code did.
Dwight
What do you consider the indirect branch to be? There is no such thing on the 6502. There is the indirect jump (with its associated bug/bugfix mentioned above). Branches and jumps are two distinct operations on the 6502: branches are always 8 bit relative, jumps are 16 bit absolute.
 
Dear Resman
I did not say indirect branch. I said relative. I can see that you might read it that way, but I was only stating that I wasn't interested in indirect anything, I was interested in the relative branch under the conditions I stated.
I recall there was a problem,at least with the early 6502 about crossing a page, with a branch straddling a page boundary.
I do have to get my SYM2 from storage to play with it.
Dwight
 
To my knowledge, there isn't an issue with a conventional branch instruction - and there never was (unless there was an issue with very early devices that escaped into the wild).

From my recollection, the problems were with indirect jumps.

However, Dwight, if you get your SYM out of storage I would be very interested in your results...

Dave
 
Dear Resman
I did not say indirect branch. I said relative. I can see that you might read it that way, but I was only stating that I wasn't interested in indirect anything, I was interested in the relative branch under the conditions I stated.
I recall there was a problem,at least with the early 6502 about crossing a page, with a branch straddling a page boundary.
I do have to get my SYM2 from storage to play with it.
Dwight
Ah, okay. It's an interesting exercise looking at how all branch cases affect the number of clocks executed. You can almost feel the flow of the logic.
 
Isn't this all spelled out in detail the original MOS MCS6500 Microcomputer Family Programming Manual with cycle by cycle examples?

http://www.bitsavers.org/components/mosTechnology/6500-50A_MCS6500pgmManJan76.pdf

5.7 Relative Addressing
Example 5.7 Illustration of relative addressing branch not taken
Example 5.8 Illustration of relative addressing branch positive taken, no crossing of page boundaries
Example 5.9 Illustration of relative addressing branch negative taken, crossing of page boundary

It is interesting to look at what happens on the bus using a logic analyzer, and see things that you might not expect, unless you carefully read the programming manual.

For example in the bus trace below, after the JSR opcode and low byte of the subroutine address are fetched, there is a dummy bus cycle while an internal operation occurs, then the two bytes of the return address are written to the stack before the high byte of the subroutine address is fetched. And the return address that is written to the stack is not the first address of the next instruction for the return, but the address of the last byte of the JSR instruction.

That is all detailed cycle by cycle in the programming manual.
8.1 JSR Jump To Subroutine
Example 8.3 Illustration of JSR instruction

(Who recognizes the code fragment below?)

ia6502-list.png
 
I have been looking at an old listing of FIG Forth for the 6502. I mentions the issue and stated it was best to pad with two nops.
I suspect the feature I was looking at was on pre-1975 processors. Since I have none that old to try it on, it is likely a non-issue.
I do recall a demonstration of the problem years ago at an early Forth meeting.
I got side tracked on updating a 6502 disassembler I'd written years ago, for another project.
Dwight
 
I believe, but not sure, it was that it didn't do the carry while incrementing the PC. As I recall, it only incremented the LSBs of the PC, without carry to the MSB. It still did the add with carry when adding the offset. It was suppose to have no issue with it all on the same page and the offset byte was located across the page. Adding the offset was not the problem. It would be a problem of where it found the offset.
It has been a long time so I don't recall all of it.
Newer chips seem to have this working fine.
I wonder if it effected other multiple byte instructions.
Dwight
 
Back
Top