This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

BEQ cannot jump to more than 127 instructions

BEQ cannot jump to more than 127 instructions
by on (#153047)
If I have more than 127 instructions between a BEQ and the corresponding label, it doesn't work:

Code:
TestFunction:

   LDX #$00
   BEQ TestFunctionEnd

   ; Put this command here 128 times:
   INX

TestFunctionEnd:

   RTS
Quote:
Error: Range error (128 not in [-128..127])


What is the general workaround here? I came up with this:
Code:
TestFunction:

   LDX #$00
   BNE ConditionNotMet
   JMP TestFunctionEnd
   
ConditionNotMet:

   ; Put this command here 128 times:
   INX

TestFunctionEnd:

   RTS

Is this the way to go? Or are there other ways?
Re: BEQ cannot jump to more than 127 instructions
by on (#153048)
Correct. Branches can't change the program counter by more than the range of a signed byte: -128 to +127 from the byte after the branch, which means -126 to +129 from the beginning of the branch instruction.

Some assemblers even come with a macro pack that provides long branch macros. For example, JEQ is a BNE over a JMP.
Re: BEQ cannot jump to more than 127 instructions
by on (#153049)
I ran into this as well.

What I did was shorten my code if possible.

I'm guessing you want to save what's in your accumulator instead of moving your X register into it.

Have you thought about pushing A to your stack, adding 128, then pulling A back?

Code:
PHA
TXA
CLC
ADC #$80
TAX
PLA


INX requires 2 clock cycles, so that's 256 clock cycles. Mine should be 15 clock cycles if I counted properly.

Edit: I just realized your code is just an example since you could just LDX #$80 since you're LDXing #$00 beforehand. NVM
Re: BEQ cannot jump to more than 127 instructions
by on (#153050)
darryl.revok wrote:
What I did was shorten my code if possible.

Erm, yes. That's not an option here.

And yes, it was just a nonsensical sample code. The actual problem occured in the NMI, but when I post example code, I try to break down the problem as much as possible.
Re: BEQ cannot jump to more than 127 instructions
by on (#153052)
The canonical way to do this is use the opposite branch instruction (beq <-> bne, bcc <-> bcs, bpl <-> bmi) to skip over a jmp instead.

Code:
   ; short jump
   beq branch
   
   ; long jump
   bne :+
      jmp branch
   :

You could also make a macro (or find a ready-made macro) that automatically detects which to use based on the distance to the branch. See .macpack longbranch.

This has been my completely redundant post. Hello. Sorry.
Re: BEQ cannot jump to more than 127 instructions
by on (#153053)
Another thought, though, in your example it is a branch to an rts. If this is the case, you can replace the jmp instruction with just an rts. Saves a few cycles and a few bytes. Though, personally I tend to leave the jmp if I think it's likely I'll want to add stuff at the end later.

Sometimes it's useful to relocate a block of code to a subroutine if there's a lot of branches to the end of it for this reason. It's not really an optimization, but it might be easier to read or maintain.

Code:
... doing stuff ...
if a goto continue
...
if b goto continue
...
if not c goto continue
...
continue:
... doing more stuff ...

; vs

... doing stuff ...
jsr subroutine
... doing more stuff ...

subroutine:
if a rts
...
if b rts
...
if not c rts
...
rts
Re: BEQ cannot jump to more than 127 instructions
by on (#153055)
The RTS was indeed just in the example code. There would be some more stuff before the RTS in the real code.
Re: BEQ cannot jump to more than 127 instructions
by on (#153058)
tepples wrote:
Some assemblers even come with a macro pack that provides long branch macros. For example, JEQ is a BNE over a JMP.

That's basically the way I've always did it.
Re: BEQ cannot jump to more than 127 instructions
by on (#153098)
I've always been doing it "by hand" but I tried out .macpack longbranch today and I'm in love. :oops:

Edit: okay, I'm a lot less in love now that I know they can only branch backwards. Any forward "longbranch" expands to a jump with these macros. :( I guess it's a limitation of macros. Still, they're convenient enough that when I get a range error when assembling I can just change the b to a j.
Re: BEQ cannot jump to more than 127 instructions
by on (#153106)
Because of the caveats of ca65's longbranch macpack, I have my own macro "ngin_longBranch", which is a prefix for normal branches that always converts it to a long one, and gives a warning in case the long branch was unnecessary:

Code:
ngin_longBranch beq foo ; Gives a warning
  nop
foo:

ngin_longBranch beq bar ; Doesn't give a warning
  .res 1000, $EA
bar:
Re: BEQ cannot jump to more than 127 instructions
by on (#153154)
I solved this problem by branching to a place where I had several jumps:

Code:
Main: ;main loop

;...

State: ;check state and load the corresponding state engine
  lda ScreenState
  cmp #TITLESCREEN ;if Title Screen
  beq JumpTitle ;jump to Title Screen
  cmp #GAMEOVERSCREEN ;...and so on
  beq JumpGameOver
  cmp #GAMESCREEN
  beq JumpMainGame
StateEnd:

  jmp Main

;State jumps:
JumpTitle:
  jmp StateTitle
JumpGameOver:
  jmp StateGameOver
JumpMainGame:
  jmp StateMainGame

  .include "states.asm"


Didn't think of simply using a reversed branch over a jmp.
Re: BEQ cannot jump to more than 127 instructions
by on (#153156)
That's actually a good technique if you have several spots close together that need to branch to the same distant address. You can consolidate them by branching them all to the same nearby jmp instruction. (It's somewhat common to find this technique in existing games.)
Re: BEQ cannot jump to more than 127 instructions
by on (#153159)
I see. In my case they all branch to different distance addresses though, so I don't think there are any benefits of this over the reversed-branch-over-a-jmp?

My solution works as long as I don't put in 129+ bytes of stuff in between the branches and the jumping place, but the reversed-branch-over-a-jmp would be universal.

I actually just changed it to use reversed branches over jmp, and it still works:

Code:
Main:

;......

State: ;check state and load the corresponding engine
  lda ScreenMode
  cmp #TITLESCREEN ;if state is Title Screen
  bne +
  jmp StateTitle ;...then load the Title Screen engine
+ cmp #GAMEOVERSCREEN
  bne + ;beq can only jump 127 byte
  jmp StateGameOver ;so a bne (reverse of beq) is used over a jmp.
+ cmp #GAMESCREEN
  bne +
  jmp StateGame
+
StateEnd:
  jmp Main

;States:
  .include "states.asm"
Re: BEQ cannot jump to more than 127 instructions
by on (#153160)
A taken branch adds an additional 2 (or sometimes 3) cycles, so there can be an advantage to optimize for the most common case. In this case, if the jmp is usually taken, the local branch over it is optimal. If the jmp is rarely taken, the branch to a jmp elsewhere saves those cycles for the common case.

It's probably rare that you'd need to care about this kind of optimization though.
Re: BEQ cannot jump to more than 127 instructions
by on (#153161)
From the 'programming the 65816' document...

"Many times it is possible and sensible to branch to another nearby flow of control statement and
use it to puddle-jump to your final target. Sometimes you will find the branch or jump statement you
need for puddle jumping already within your code because it’s not unusual for two or more segments
of code to conditionally branch to the same place. This method costs you no additional code"

I like the idea of a BEQ statement that branches to another BEQ statement, effectively doubling the branching distance.
Re: BEQ cannot jump to more than 127 instructions
by on (#153166)
Pokun wrote:
Code:
Main: ;main loop

;...

State: ;check state and load the corresponding state engine
  lda ScreenState
  cmp #TITLESCREEN ;if Title Screen
  beq JumpTitle ;jump to Title Screen
  cmp #GAMEOVERSCREEN ;...and so on
  beq JumpGameOver
  cmp #GAMESCREEN
  beq JumpMainGame
StateEnd:

  jmp Main

;State jumps:
JumpTitle:
  jmp StateTitle
JumpGameOver:
  jmp StateGameOver
JumpMainGame:
  jmp StateMainGame

  .include "states.asm"

Wouldn't a jump table or a pointer in RAM (so you can just go JMP (CurrentState)) be more appropriate in this case? It seems wasteful to compare game states like that every frame.
Re: BEQ cannot jump to more than 127 instructions
by on (#153169)
tokumaru wrote:
Wouldn't a jump table or a pointer in RAM (so you can just go JMP (CurrentState)) be more appropriate in this case? It seems wasteful to compare game states like that every frame.

In fairness it's only a few cycles every frame. If you wanted to minimize this we could just put the "main game" branch first (which would be better than a jump table, maybe not as good as an indirect jump). I highly doubt the title or game over screens are hurting for a couple of cycles.
Re: BEQ cannot jump to more than 127 instructions
by on (#153170)
I do something like:
Code:
.macro jsri address

.local @skip, @back

           jmp @skip
    @back: jmp (address)   
    @skip: jsr @back

.endmacro


loop:
    wait_NMI_return:
        bit NMI_done_flag
    bpl wait_NMI_return:
   
    jsri (CurrentState)

jmp loop
Re: BEQ cannot jump to more than 127 instructions
by on (#153172)
rainwarrior wrote:
If you wanted to minimize this we could just put the "main game" branch first

Yeah, I was going to suggest that, since the main game is typically the most CPU-intensive part, probably the only part that will ever have to deal with slowdowns.
Re: BEQ cannot jump to more than 127 instructions
by on (#153189)
Figured out that too so I did exactly that.