Salient Assembly Episode 1

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.