JSR
Moderator: Moderators
JSR
Do the JSRs in 6502 assembly set the PC to a function's address or the address of a function pointed to in a table high in memory? Like are the two bytes after the JSR the next address the PC needs to be for the function to begin or no?
I've learned in assembly and BASIC functions typically declared at the bottom and are then indexed by a function table. I wanted to make sure that was the same for NES programs because I don't think the JSR's next two bytes are used to address the subroutine called
I've learned in assembly and BASIC functions typically declared at the bottom and are then indexed by a function table. I wanted to make sure that was the same for NES programs because I don't think the JSR's next two bytes are used to address the subroutine called
- FrankenGraphics
- Formerly WheelInventor
- Posts: 2064
- Joined: Thu Apr 14, 2016 2:55 am
- Location: Gothenburg, Sweden
- Contact:
Re: JSR
Edit: The following statement was corrected by quitust. Leaving it for the sake of intact history.
Unlike BASIC, 6502 machine language doesn't do much 'hidden' stuff for you.
The good news is that it's up to you. You can use a literal address (absolute addressing) or point to one (indirect addressing)http://www.obelisk.me.uk/6502/addressing.html
Unlike BASIC, 6502 machine language doesn't do much 'hidden' stuff for you.
Last edited by FrankenGraphics on Mon Aug 14, 2017 4:26 pm, edited 2 times in total.
Re: JSR
Maybe you're thinking of the JMP instruction, which gives you the choice between absolute and indirect.FrankenGraphics wrote:The good news is that it's up to you. You can use a literal address (absolute addressing) or point to one (indirect addressing)
http://www.obelisk.me.uk/6502/addressing.html
JSR has exactly one addressing mode: absolute, where the two bytes after the opcode specify the address of the function you are going to start executing.NekadZut wrote:Do the JSRs in 6502 assembly set the PC to a function's address or the address of a function pointed to in a table high in memory? Like are the two bytes after the JSR the next address the PC needs to be for the function to begin or no?
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.
- FrankenGraphics
- Formerly WheelInventor
- Posts: 2064
- Joined: Thu Apr 14, 2016 2:55 am
- Location: Gothenburg, Sweden
- Contact:
Re: JSR
The 65816 has a JSR (addr,X), which is what you want. Unfortunately it's not on the 6502.
A little closer, a workaround for the 6502's lack of a JSR (addr) (although still not suitable for a table) is self-modifying code, where instead of writing the address to a separate variable, you write it to the operand field of the JSR addr instruction itself, ie, to bytes 2 and 3 of the 3-byte instruction. This of course assumes that the code is in RAM, not ROM.
Ok, so what about a table? The 65c02 (CMOS) has a JMP (addr,X), and you can JSR to this JMP instruction. It still takes additional cycles, and it's not available on the NMOS 6502. You can however simulate the JMP (addr,X) this way on the NMOS 6502, and then JSR to this routine:
Note that the RTS in this case is not being used as a return from subroutine, but as an indirect jump to the desired subroutine! This method does not require any variables, and both the code and the table can be in ROM. This is in chapter 5 of the 6502 stacks treatise indexed at http://wilsonminesco.com/stacks/index.html .
A little closer, a workaround for the 6502's lack of a JSR (addr) (although still not suitable for a table) is self-modifying code, where instead of writing the address to a separate variable, you write it to the operand field of the JSR addr instruction itself, ie, to bytes 2 and 3 of the 3-byte instruction. This of course assumes that the code is in RAM, not ROM.
Ok, so what about a table? The 65c02 (CMOS) has a JMP (addr,X), and you can JSR to this JMP instruction. It still takes additional cycles, and it's not available on the NMOS 6502. You can however simulate the JMP (addr,X) this way on the NMOS 6502, and then JSR to this routine:
Code: Select all
; Start with function (an even number) in X.
LDA TABLE+1,X ; Read high address byte from the actual table, and
PHA ; push it. Low byte comes next, below.
LDA TABLE,X ; Be sure to make the table reflect start addresses
PHA ; minus 1, since RTS increments the address by 1.
RTS ; RTS does the absolute indexed indirect jump.
http://WilsonMinesCo.com/ lots of 6502 resources
Re: JSR
(as stated in my last few words right before the listing)Myask wrote:Of course, you probably want to JSR into what Garth wrote to give you a return address therefrom.
http://WilsonMinesCo.com/ lots of 6502 resources
-
- Posts: 1565
- Joined: Tue Feb 07, 2017 2:03 am
Re: JSR
and the table needs to contain the address-1 you want to call
so .word myFunc-1,otherFunc-1
although it is more efficient to split the table into hi/lo and save the asl if your using an assembler with NES in the name you probably need to use LOW(), HIGH() instead of <, or >, and yes the brackets are important
so .word myFunc-1,otherFunc-1
although it is more efficient to split the table into hi/lo and save the asl
Code: Select all
; Start with function in X.
LDA TABLE_hi,X ; Read high address byte from the actual table, and
PHA ; push it. Low byte comes next, below.
LDA TABLE_lo,X ; Be sure to make the table reflect start addresses
PHA ; minus 1, since RTS increments the address by 1.
RTS ; RTS does the absolute indexed indirect jump.
TABLE_lo .byte <(myFunc-1),<(otherFunc-1)
TABLE_hi .byte >(myFunc-1),>(otherFunc-1)
Re: JSR
(as also stated in my comments)Oziphantom wrote:and the table needs to contain the address-1 you want to call
If X always gets loaded from a constant, then it won't matter, because the constant will always be even. But it would matter if you have more than 128 table entries, since 129 or more would need more than 8 bits of index the way I showed it.although it is more efficient to split the table into hi/lo and save the asl
Thanks.if your using an assembler with NES in the name you probably need to use LOW(), HIGH() instead of <, or >, and yes the brackets are important
http://WilsonMinesCo.com/ lots of 6502 resources
Re: JSR
One of the new instructions in Hudson's 65C02 derivative the Hu6280, is the BSR - Branch to Subroutine instruction. Doesn't seem too useful since the subroutine must be close to the branch, but I guess it can be used in relocatable code since branches are relative.
-
- Posts: 1565
- Joined: Tue Feb 07, 2017 2:03 am
Re: JSR
Fair I missed it, my bad.Garth wrote:(as also stated in my comments)
if you are doing LDX #4 JSR JmpOnX, just do JSR XXXXGarth wrote:If X always gets loaded from a constant, then it won't matter, because the constant will always be even. But it would matter if you have more than 128 table entries, since 129 or more would need more than 8 bits of index the way I showed it.although it is more efficient to split the table into hi/lo and save the asl
Re: JSR
Of course you wouldn't do it exactly like that, but you could have object states represented exclusively by even numbers, for example. As long as there's no overhead in keeping the indices even, not splitting the addresses results in slightly more readable/maintainable code without any performance sacrifices.Oziphantom wrote:if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
Re: JSR
BSR can be simulated, even with a 16-bit offset, but it's very, very inefficient on the 6502.Pokun wrote:One of the new instructions in Hudson's 65C02 derivative the Hu6280, is the BSR - Branch to Subroutine instruction. Doesn't seem too useful since the subroutine must be close to the branch, but I guess it can be used in relocatable code since branches are relative.
I've toyed with the idea of relocatable code for the '02; but as inefficient as it is to make the code itself relocatable, it's even worse with data. I discuss it in chapter 12 of the stacks treatise, at http://wilsonminesco.com/stacks/where-am-I.html . I would still like to have an "Aha!" moment where I find there's a trick that has been hiding from me; but I think it's appropriate to just admit that the 6502 is not suited to relocatable code. It's not totally incapable of it though, nor does every application need to wring maximum performance out of the processor. We sometimes accept compromises in performance to get another desired benefit. This particular section of the stacks treatise is only musings on my part which hopefully will give someone ideas.
http://WilsonMinesCo.com/ lots of 6502 resources
Re: JSR
I don't remember if I've ever used a table of subroutine addresses in 6502; but there can be OS situations where you might want to be able to keep the index as a constant whose name is descriptive of an OS function for example, and then the OS can get updated and addresses can change without having to change the programs that use them.Oziphantom wrote:if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
http://WilsonMinesCo.com/ lots of 6502 resources
-
- Posts: 1565
- Joined: Tue Feb 07, 2017 2:03 am
Re: JSR
Fair, but I feel that creates a lot more work, as now your data tools, have to use evens, all your code has to work in evens, if you ever do an inx, you have to remember to inx, inx. I would suggest a better way to solve the problem is to fix it with the assembler.tokumaru wrote:Of course you wouldn't do it exactly like that, but you could have object states represented exclusively by even numbers, for example. As long as there's no overhead in keeping the indices even, not splitting the addresses results in slightly more readable/maintainable code without any performance sacrifices.Oziphantom wrote:if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
Code: Select all
given
mCallFunctionTable .macro
lda \1.hi,\2
pha
lda \1.lo,\2
pha
rts
.endm
mMakeFunctionTable .macro
lo .byte <(\@)-1
hi .byte >(\@)-1
.endm
you then have
.mCallFunctionTable myTable,x
myTable .mMakeFunctionTable myFunc,myOtherFunc,someOtherFunc,newFunc
Typically you want to keep a list of defines in sync with the functions you call, so if you extract the functions into a assembler variable, you can interleave it with the defines like so
Code: Select all
.var vEntFuncs = []
kEntsUpdateFunctions .block
still = 0
vEntFuncs ..= [entStandStill]
walkLeft = 1
vEntFuncs ..= [entWalkLeft]
walkRight = 2
vEntFuncs ..= [entWalkRight]
jump = 3
vEntFuncs ..= [entJump]
fall = 4
vEntFunc ..= [entFall]
death = 5
vEntFunc ..= [entDeath]
.bend
...
...
...
dispatchEntStateX
.mCallFunctionTable myTable,x
myTable .mMakeFunctionTable vEntFunc