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
<p class="tab">jsr GAMEOVER</p>
<p class="tab">jmp NextMiniGame</p>
GAMEOVER
<p class="tab">ldx #$50C</p>
<p class="tab">ldy #text</p>
loop
<p class="tab">lda ,y+</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">sta ,x+</p>
<p class="tab">bra loop</p>
WaitGameOver
<p class="tab">jsr [POLCAT]</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">rts</p>
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
<p class="tab">ldb randomNumber</p>
<p class="tab">aslb</p>
<p class="tab">ldx #gameOverTable</p>
<p class="tab">abx</p>
<p class="tab">jsr [,x]</p>
<p class="tab">jmp nextMiniGame</p>
gameOverTable
<p class="tab">fdb WaitGameOver1</p>
<p class="tab">fdb WaitGameOver2</p>
WaitGameOver1
<p class="tab">...</p>
<p class="tab">rts</p>
WaitGameOver2
<p class="tab">...</p>
<p class="tab">rts</p>
• 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
<p class="tab">ldx #text</p>
<p class="tab">stx textBufferAddress</p>
<p class="tab">ldx #$50C</p>
<p class="tab">stx textposition</p>
<p class="tab">jsr GAMEOVER</p>
<p class="tab">jmp NextMiniGame</p>
GAMEOVER
<p class="tab">ldx textposition</p>
<p class="tab">ldy textBufferAddress</p>
loop
<p class="tab">lda ,y+</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">sta ,x+</p>
<p class="tab">bra loop</p>
WaitGameOver
<p class="tab">jsr [POLCAT]</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">rts</p>
textBufferAddress fdb 0
textposition fdb 0
text fcn "GAME OVER"
- load the address of the text string and store it in the parameter area
- load the screen position and store it into the parameter area
- jump to the subroutine
- load the parameters into registers and perform the function.
- 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
<p class="tab">ldy #text</p>
<p class="tab">ldx #$50C</p>
<p class="tab">jsr GAMEOVER</p>
<p class="tab">jmp NextMiniGame</p>
GAMEOVER
loop
<p class="tab">lda ,y+</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">sta ,x+</p>
<p class="tab">bra loop</p>
WaitGameOver
<p class="tab">jsr [POLCAT]</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">rts</p>
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
<p class="tab">ldx #text</p>
<p class="tab">pshs x</p>
<p class="tab">ldx #$50C</p>
<p class="tab">pshs x</p>
<p class="tab">jsr GAMEOVER</p>
<p class="tab">jmp NextMiniGame</p>
GAMEOVER
<p class="tab">puls u</p>
<p class="tab">puls x</p>
<p class="tab">puls y</p>
loop
<p class="tab">lda ,y+</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">sta ,x+</p>
<p class="tab">bra loop</p>
WaitGameOver
<p class="tab">jsr [POLCAT]</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">tfr u,pc</p>
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
<p class="tab">jsr GAMEOVER</p>
<p class="tab">fdb $50C</p>
<p class="tab">fcn "GAME OVER"</p>
<p class="tab">jmp NextMiniGame</p>
GAMEOVER
<p class="tab">puls x</p>
<p class="tab">leay 2,x</p>
<p class="tab">ldx ,x</p>
loop
<p class="tab">lda ,y+</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">sta ,x+</p>
<p class="tab">bra loop</p>
WaitGameOver
<p class="tab">jsr [POLCAT]</p>
<p class="tab">beq WaitGameOver</p>
<p class="tab">tfr y,pc</p>
• 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.