https://www.youtube.com/watch?v=dxwmPFIUpLY
Welcome to Salient Assembly.
The show where we deep dive into a very specific 6809 assembly language topic.
Today we’re talking about calling subroutines. Subroutines are a very important aspect to programming. When ever you find yourself needing to do the same thing in two different parts of your program your best bet is to create a subroutine for that function. This process is so fundamental it has a name: refactoring.
There are many instructions to facilitate this. But the today we’ll be investiagting Branch to subroutine, and jump to subroutine. The branch version is used in position independant code, and also has a faster, shorter version for smaller branches.
JSR with no parameters
Let’s first talk about a subroutine that requires no parameters to be passed. The JSR instruction will push the address of the next instruction to the stack and then pass control to the subroutine.
The RTS instruction will pull two bytes off the top of the stack and jump to that memory location.
These two instructions are what enable calling subroutines in 6809 assemble language.
For example, let’s say we’re writing a game and we want to write a subroutine that displays the text GAME OVER in the center of the screen.
GameOverCondition
jsr GAMEOVER
jmp NextMiniGame
GAMEOVER
ldx #$50C
ldy #text
loop
lda ,y+
beq WaitGameOver
sta ,x+
bra loop
WaitGameOver
jsr [POLCAT]
beq WaitGameOver
rts
text fcn "GAME OVER"
Here we see the code for a mini game ending. Let’s dig in deep to the specifics
• We execute the jsr instruction. It will push the address of the next instruction on the stack and jump to the operand.
• Do the work of the subroutine. This will copy some text to the middle of the VDG screen.
• Then return to the main program
JSR with calculated address
Sometimes you want to branch to different subroutines based on a condition. One method to do this is with a table of of subroutines. For example:
GameOverCondition
ldb randomNumber
aslb
ldx #gameOverTable
abx
jsr [,x]
jmp nextMiniGame
gameOverTable
fdb WaitGameOver1
fdb WaitGameOver2
WaitGameOver1
...
rts
WaitGameOver2
...
rts
• I load a previously calculated random number that will be 0 or 1.
• Then multiply it by two.
• I add that value to the base address of the subroutine table.
• Then jump to that specific subroutine.
• After the subroutine finishes, it returns to the main program.
Parameter passing
Most of the time a subroutine will take parameters. These are values that change how the subroutine functions. In the example we’ve seen so far we may want to change the subroutine to make the text it displays variable. We also may want to have the text’s final position variable. Let’s go over some methods of passing these parameters to our subroutine.
JSR fixed memory locations
A simple, non-position independant method of passing parameters to a sub-routine is by using fixed memory locations.
GameOverCondition
ldx #text
stx textBufferAddress
ldx #$50C
stx textposition
jsr GAMEOVER
jmp NextMiniGame
GAMEOVER
ldx textposition
ldy textBufferAddress
loop
lda ,y+
beq WaitGameOver
sta ,x+
bra loop
WaitGameOver
jsr [POLCAT]
beq WaitGameOver
rts
textBufferAddress fdb 0
textposition fdb 0
text fcn "GAME OVER"
1. load the address of the text string and store it in the parameter area
2. load the screen position and store it into the parameter area
3. jump to the subroutine
4. load the parameters into registers and perform the function.
5. then return to the main program.
This allows the GAMEOVER subroutine to print different messages. You can pass different text string and different positions to get different results.
JSR register
A faster way to pass parameters would be to use registers
GameOverCondition
ldy #text
ldx #$50C
jsr GAMEOVER
jmp NextMiniGame
GAMEOVER
loop
lda ,y+
beq WaitGameOver
sta ,x+
bra loop
WaitGameOver
jsr [POLCAT]
beq WaitGameOver
rts
text fcn "GAME OVER"
• load the registers with the parameters
• call the subroutine
• notice how the registers are all setup ready for the function to be done.
• and return to the main program.
JSR stack
The stack is also a good way to pass parameters to a subroutine. Since the return address is already stored on the stack it makes sense to put the other parameters there.
GameOverCondition
ldx #text
pshs x
ldx #$50C
pshs x
jsr GAMEOVER
jmp NextMiniGame
GAMEOVER
puls u
puls x
puls y
loop
lda ,y+
beq WaitGameOver
sta ,x+
bra loop
WaitGameOver
jsr [POLCAT]
beq WaitGameOver
tfr u,pc
text fcn "GAME OVER"
• Push the two parameters onto the stack
• Call the subroutine
• Pop the return address off the stack
• pop the parameters off the stack
• perform the function
• transfer the return address to the pc register and return to the main program.
JSR post data
The last method we’ll discuss is when you put the data to be passed after the subroutine call. Care must be taken to return to the proper address when done with the subroutine.
GameOverCondition
jsr GAMEOVER
fdb $50C
fcn "GAME OVER"
jmp NextMiniGame
GAMEOVER
puls x
leay 2,x
ldx ,x
loop
lda ,y+
beq WaitGameOver
sta ,x+
bra loop
WaitGameOver
jsr [POLCAT]
beq WaitGameOver
tfr y,pc
• First we call the subroutine
• immediately after the call, we put two bytes of a destination address and then a string of characters with a zero terminator.
• In the subroutine we load x with the address that was pushed onto the stack. This is technically the return address, but if we used it as such, we would return to data, not code.
• Then we load register Y with the address of the text string
• Then we load register X with the address of the text destination.
• We perform the function
• Then we return from the subroutine by transferring the Y register to the PC register which will conveniently be the proper return address.
That’s it for this episode. I hope you had a good time learning about subroutines and all the various ways you can pass parameters to them.
Bye for now.