BEQ cannot jump to more than 127 instructions

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

BEQ cannot jump to more than 127 instructions

Post by DRW »

If I have more than 127 instructions between a BEQ and the corresponding label, it doesn't work:

Code: Select all

TestFunction:

	LDX #$00
	BEQ TestFunctionEnd

	; Put this command here 128 times:
	INX

TestFunctionEnd:

	RTS
Error: Range error (128 not in [-128..127])
What is the general workaround here? I came up with this:

Code: Select all

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?
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by tepples »

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.
User avatar
darryl.revok
Posts: 520
Joined: Sat Jul 25, 2015 1:22 pm

Re: BEQ cannot jump to more than 127 instructions

Post by darryl.revok »

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: Select all

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
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: BEQ cannot jump to more than 127 instructions

Post by DRW »

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.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by rainwarrior »

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: Select all

	; 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.
Last edited by rainwarrior on Wed Aug 12, 2015 3:41 pm, edited 1 time in total.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by rainwarrior »

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: Select all

... 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
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: BEQ cannot jump to more than 127 instructions

Post by DRW »

The RTS was indeed just in the example code. There would be some more stuff before the RTS in the real code.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: BEQ cannot jump to more than 127 instructions

Post by Drew Sebastino »

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.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by rainwarrior »

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.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by thefox »

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: Select all

ngin_longBranch beq foo ; Gives a warning
  nop
foo:

ngin_longBranch beq bar ; Doesn't give a warning
  .res 1000, $EA
bar:
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Pokun
Posts: 1923
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: BEQ cannot jump to more than 127 instructions

Post by Pokun »

I solved this problem by branching to a place where I had several jumps:

Code: Select all

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.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by rainwarrior »

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.)
Pokun
Posts: 1923
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: BEQ cannot jump to more than 127 instructions

Post by Pokun »

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: Select all

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"
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by rainwarrior »

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.
User avatar
dougeff
Posts: 2875
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: BEQ cannot jump to more than 127 instructions

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
Post Reply