JSR

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

NekadZut
Posts: 7
Joined: Fri Aug 04, 2017 4:32 pm
Location: Atlanta, Georgia

JSR

Post by NekadZut »

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
User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2064
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: JSR

Post by FrankenGraphics »

Edit: The following statement was corrected by quitust. Leaving it for the sake of intact history.
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.
User avatar
Quietust
Posts: 1918
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: JSR

Post by Quietust »

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
Maybe you're thinking of the JMP instruction, which gives you the choice between absolute and indirect.
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?
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.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2064
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: JSR

Post by FrankenGraphics »

Ah yes. JMP, not JSR. Pardon me, late night here.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: JSR

Post by Garth »

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:

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.
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 .
http://WilsonMinesCo.com/ lots of 6502 resources
User avatar
Myask
Posts: 965
Joined: Sat Jul 12, 2014 3:04 pm

Re: JSR

Post by Myask »

Of course, you probably want to JSR into what Garth wrote to give you a return address therefrom.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: JSR

Post by Garth »

Myask wrote:Of course, you probably want to JSR into what Garth wrote to give you a return address therefrom.
(as stated in my last few words right before the listing)
http://WilsonMinesCo.com/ lots of 6502 resources
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: JSR

Post by Oziphantom »

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

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)
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 ;)
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: JSR

Post by Garth »

Oziphantom wrote:and the table needs to contain the address-1 you want to call
(as also stated in my comments)
although it is more efficient to split the table into hi/lo and save the asl
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.
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 ;)
Thanks.
http://WilsonMinesCo.com/ lots of 6502 resources
Pokun
Posts: 2675
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: JSR

Post by Pokun »

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.
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: JSR

Post by Oziphantom »

Garth wrote:(as also stated in my comments)
Fair I missed it, my bad.
Garth wrote:
although it is more efficient to split the table into hi/lo and save the asl
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.
if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: JSR

Post by tokumaru »

Oziphantom wrote:if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
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.
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: JSR

Post by Garth »

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.
BSR can be simulated, even with a 16-bit offset, but it's very, very inefficient on the 6502.

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
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: JSR

Post by Garth »

Oziphantom wrote:if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
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.
http://WilsonMinesCo.com/ lots of 6502 resources
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: JSR

Post by Oziphantom »

tokumaru wrote:
Oziphantom wrote:if you are doing LDX #4 JSR JmpOnX, just do JSR XXXX
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.
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.

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
This gives you the same readability as not splitting it, but spares you from having to remember to make everything even and +2 in places.

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 
Post Reply