Suggestions to follow branch instructions

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Suggestions to follow branch instructions

Post by colt » Sat Nov 09, 2019 6:02 pm

Hello. I was implementing the BPL instruction. Reading the rom's code, it seems it will be taken. This throws the code's execution far away ahead, which makes it quite difficult to locate. I can start to count it, but this is quite a slow process and very, very error prone. So, what can I do to swiftly locate the correct new point of execution in the rom's instructions?

User avatar
koitsu
Posts: 4218
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Suggestions to follow branch instructions

Post by koitsu » Sat Nov 09, 2019 6:40 pm

Branch instructions use what is called "PC-relative" addressing, which means the address to branch to is based on whatever the current PC is at the time. You can thus calculate the effective branch address easily as long as you know the address of where the branch opcode itself is (which you should know). The operand value is effectively signed; the range is thus -128 to +127 bytes, but the catch is that it's from the instruction *following* the branch opcode (i.e. the operand). So, from the branch opcode itself, it's a range of +129 or -126 bytes.

Please see Programming the 65816 (including the 6502, 65C02, and 65802) and refer to these pages:

- Page 114 ("Conditional Branching")
- Chapter 18, for each individual branch opcode that is relevant to the 6502. (The book is for 6502/65c02/65816, the latter two CPUs offer instructions the 6502 does not have)

Quoting the above book, underlined portions relevant:
As Chapter 1 notes, positive numbers are indicated by a zero in the high bit (bit seven), negative
numbers by a one in the high bit. Branching is limited by the signed one-byte operands to 127 bytes forward or
128 bytes backward, counting from the end of the instruction. Because a new value for the program counter
must be calculated if the branch is taken, an extra execution cycle is required.

...
The program counter value to which the operand is added is not the address of the branch instruction
but rather the address of the opcode following the branch instruction. Thus, measured from the branch opcode
itself, branching is limited to 129 bytes forward and 126 bytes backward.
A conditional branch instruction with
an operand of zero will continue with the next instruction regardless of whether the condition tested is true or
false. A branch with an operand of zero is thus a two-byte no-operation instruction, with a variable (by one
cycle) execution time, depending on whether the branch is or isn’t taken.
Happy hacking.

User avatar
rainwarrior
Posts: 7824
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Suggestions to follow branch instructions

Post by rainwarrior » Sat Nov 09, 2019 7:23 pm

colt wrote:So, what can I do to swiftly locate the correct new point of execution in the rom's instructions?
Any disassembler should turn it into a label for you, so you don't have to start counting bytes manually. Debuggers usually have built-in disassemblers that might make it easy to jump around the code in a more interactive way.

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Re: Suggestions to follow branch instructions

Post by colt » Mon Nov 11, 2019 3:59 pm

rainwarrior wrote:
colt wrote:So, what can I do to swiftly locate the correct new point of execution in the rom's instructions?
Any disassembler should turn it into a label for you, so you don't have to start counting bytes manually. Debuggers usually have built-in disassemblers that might make it easy to jump around the code in a more interactive way.
Hi. the dissassembler worked on the rom file and generated an ouput file. There I could locate fairly easily a line where it says:
000001FB: E8 INX
. Being that the branch instruction's lines are
0000001D: 10FB BPL $FB
and
00000022: 10FB BPL $FB
am I correct to infer that they refer to the first line I quoted?

User avatar
dougeff
Posts: 2712
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Suggestions to follow branch instructions

Post by dougeff » Mon Nov 11, 2019 4:12 pm

No.

10 is the opcode for BPL
FB is the branch destination, backwards about 5 bytes. (From the byte following the 10 fb as zero, count back 5)

That particular disassembler (?) doesn't specify the label it is branching to. Maybe find another.

The debugger in FCEUX does a fair job.
nesdoug.com -- blog/tutorial on programming for the NES

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Re: Suggestions to follow branch instructions

Post by colt » Tue Nov 12, 2019 3:46 pm

dougeff wrote:No.

backwards about 5 bytes.
Can you explain why?
(From the byte following the 10 fb as zero, count back 5)
What do you mean "as zero"



The debugger in FCEUX does a fair job.
Thankyou, will give a look.

User avatar
dougeff
Posts: 2712
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Suggestions to follow branch instructions

Post by dougeff » Tue Nov 12, 2019 4:26 pm

branching is signed. branching is relative to the current PC (program count)

01 branch +1
00 branch zero
ff branch -1
fe branch -2
fd branch -3
fc branch -4
fb branch -5
nesdoug.com -- blog/tutorial on programming for the NES

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Re: Suggestions to follow branch instructions

Post by colt » Tue Nov 12, 2019 5:17 pm

dougeff wrote:branching is signed. branching is relative to the current PC (program count)

01 branch +1
00 branch zero
ff branch -1
fe branch -2
fd branch -3
fc branch -4
fb branch -5
Thanks. Given this sequence of bytes: A2 FF 9A AD 02 20 10 FB AD 02 20 10, when it finds the first 10, in my program, it will step back 5 positions, back to FF. As far as I understood, FF was an immediate value that was loaded in the X register, so I don't know what I shall do there, since it's not an instruction, I think. That's way I suspect I misunderstood something.

User avatar
Quietust
Posts: 1567
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Suggestions to follow branch instructions

Post by Quietust » Tue Nov 12, 2019 5:22 pm

colt wrote:
dougeff wrote:branching is signed. branching is relative to the current PC (program count)

01 branch +1
00 branch zero
ff branch -1
fe branch -2
fd branch -3
fc branch -4
fb branch -5
Thanks. Given this sequence of bytes: A2 FF 9A AD 02 20 10 FB AD 02 20 10, when it finds the first 10, in my program, it will step back 5 positions, back to FF. As far as I understood, FF was an immediate value that was loaded in the X register, so I don't know what I shall do there, since it's not an instruction, I think. That's way I suspect I misunderstood something.
Actually, in this case it will branch back to the "AD" 2 bytes afterwards, because the branch is relative to the beginning of the next instruction:
dougeff wrote:10 is the opcode for BPL
FB is the branch destination, backwards about 5 bytes. (From the byte following the 10 fb as zero, count back 5)
On a side note, what technical documents are you using as references for implementing the 6502? I find it hard to believe that they would provide such a poor explanation that you wouldn't be able to understand it straightaway...
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Re: Suggestions to follow branch instructions

Post by colt » Thu Nov 14, 2019 3:50 pm

Quietust wrote: Actually, in this case it will branch back to the "AD" 2 bytes afterwards, because the branch is relative to the beginning of the next instruction:
dougeff wrote:10 is the opcode for BPL
FB is the branch destination, backwards about 5 bytes. (From the byte following the 10 fb as zero, count back 5)
I should have payed more attention to dougeff post. However I do not comprehend what he means by "as zero"". Can I only step back three bytes from the branch instruction byte instead, or this can be variable?
On a side note, what technical documents are you using as references for implementing the 6502? I find it hard to believe that they would provide such a poor explanation that you wouldn't be able to understand it straightaway...
These links:

http://wiki.nesdev.com/w/index.php/INES
https://skilldrick.github.io/easy6502/
https://www.masswerk.at/6502/6502_instruction_set.html
http://www.obelisk.me.uk/6502/reference.html

To the instructions specifically, the last two ones.
Last edited by colt on Fri Nov 15, 2019 2:18 pm, edited 1 time in total.

User avatar
tokumaru
Posts: 11766
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Suggestions to follow branch instructions

Post by tokumaru » Thu Nov 14, 2019 8:19 pm

When he said "as zero" he meant that that's the reference point for the branch offset: an offset of 0 causes the instruction following the branch instruction to get executed, as if no branching had happened. An offset of -2
($fe) willl result in an infinite loop (assuming no interrupt breaks it) because it'll make the CPU jump back to the branch instruction itself.

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Re: Suggestions to follow branch instructions

Post by colt » Fri Nov 15, 2019 2:28 pm

tokumaru wrote:When he said "as zero" he meant that that's the reference point for the branch offset: an offset of 0 causes the instruction following the branch instruction to get executed, as if no branching had happened. An offset of -2
($fe) willl result in an infinite loop (assuming no interrupt breaks it) because it'll make the CPU jump back to the branch instruction itself.
So my understanding that:
01 branch: +3 starting from the branch instruction
00 branch: +2 starting from the branch instruction
ff branch: +1 starting from the branch instruction
fe branch: 0 starting from the branch instruction
fd branch: -1 0 starting from the branch instruction
fc branch: -2 0 starting from the branch instruction
fb branch: -3 0 starting from the branch instruction

is correct?


***********EDITED***********

It has to be wrong, since returning three positions when it's FB, the pc returns to point to the "AD" instruction, an absolute load instruction, that increments the pc by 2, or in another words, returns to the Bpl instruction, that will then return to the absolute LDA instruction.... so it generates an infinite loop between these two instructions, according to the current implementation of my program.

User avatar
dougeff
Posts: 2712
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: Suggestions to follow branch instructions

Post by dougeff » Fri Nov 15, 2019 3:41 pm

$2002 is a hardware register. It's value is volatile.

Reading from there gives you this information from the PPU.

Sprite overflow flag. (bit 5)
Sprite 0 Hit flag. (bit 6)
Vertical blank has started (bit 7)

We are interested in bit 7, waiting for vertical blank, as a way to wait for the PPU to warm up.

One easy way to check bit 7 is to use the N (negative) processor status flag, LDA sets this flag if bit 7 of the value loaded is set (ie. the value is negative)

BPL (branch if N clear) and BMI (branch if N set)

The BPL backward... is creating a loop that will repeat while not in v-blank, and exit the loop if we are in v-blank.

Your branch notes are correct.
nesdoug.com -- blog/tutorial on programming for the NES

colt
Posts: 13
Joined: Sat Nov 02, 2019 2:53 pm

Re: Suggestions to follow branch instructions

Post by colt » Sat Nov 16, 2019 2:59 pm

dougeff wrote:$2002 is a hardware register. It's value is volatile.

Reading from there gives you this information from the PPU.
So that means I have to access the rom memory in position 2000h or 8194d and copy it the accumulator (this is what I have done) ?
Sprite overflow flag. (bit 5)
Sprite 0 Hit flag. (bit 6)
Vertical blank has started (bit 7)

We are interested in bit 7, waiting for vertical blank, as a way to wait for the PPU to warm up.

One easy way to check bit 7 is to use the N (negative) processor status flag, LDA sets this flag if bit 7 of the value loaded is set (ie. the value is negative)

BPL (branch if N clear) and BMI (branch if N set)
But doesn't it has to check it first? I mean, the 7° bit of value loaded on the accumulator is checked to see if is equal to one. On this case that would mean to check if is waiting for v-blank. If it is,the status register's bit 7 is set to 1, as would do with any loaded value whose bit 7 would be one.

The BPL backward... is creating a loop that will repeat while not in v-blank, and exit the loop if we are in v-blank.
Here it loads the value of the rom's position 8194, that happens to be 27, that happens to have the bit 7 set to 0.
So it never ends this "ping pong" between the absolute LDAand the BPL. So something is wrong. My hypothesis: It's loading the wrong value (likely because it's accessing the wrong position on the rom memory) . The possible reason to be accessing the wrong position might be that its accessing the memory counting from the first instruction (17° memory position or 16° starting to count from zero) and not from the 0° position.

User avatar
rainwarrior
Posts: 7824
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Suggestions to follow branch instructions

Post by rainwarrior » Sat Nov 16, 2019 3:11 pm

It might be a little easier to discuss if you provided more context. It's really hard to offer any helpful insight with such a tiny view of what you're looking at.

What ROM is this?

Or if you don't want to disclose that, at least show the LDA / BPL instruction you're talking about (i.e. what are the 5 bytes of code in this loop, and where are they loaded in memory). What mapper?

Post Reply