A subroutine is simply a piece of code that you call lots of times within your program. Because it is called so many times, you extract the working code, move it somewhere safe and add an RTS at the end. This is your subroutine - in its draft form !
Where the code used to be in the main source, now simply has a 'BSR sub_routine' in its place. The more times a routine is called, the bigger the saving in your typing and memory usage in the final program. Another major advantage of using subroutines is that you only need to change or correct them once - of course, if you make a mistake then every call to that subroutine is flawed as well !
For example, in a program you have written, you might find that you write the same piece of code numerous times to clear the screen, something like the following :
start blah blah blah : : move.l channel_id,a0 ; First channel id moveq #sd_clear,d0 ; CLS moveq #infinite,d3 ; Infinite timeout trap #3 ; CLS title window : : move.l other_channel_id,a0 ; Another channel id moveq #sd_clear,d0 ; CLS moveq #infinite,d3 ; Infinite timeout trap #3 ; CLS title window : : move.l another_id,a0 ; And another channel id moveq #sd_clear,d0 ; CLS moveq #infinite,d3 ; Infinite timeout trap #3 ; CLS title window : : rts ; All done - back to wherever I came from
and so on. The above code looks duplicated and where you have duplication, you can usually - but not always - extract the duplicate code to a subroutine. We can now rewrite the code above as follows :
start blah blah blah : : move.l channel_id,a0 ; First channel id bsr cls : : move.l other_channel_id,a0 ; Another channel id bsr cls : : move.l another_id,a0 ; And another channel id bsr cls : : rts ; All done - back to wherever I came from *------------------------------------------------------------------------- * Subroutine to clear the SCR or CON channel whose ID is held in A0. *------------------------------------------------------------------------- cls moveq #sd_clear,d0 ; CLS moveq #-1,d3 ; Infinite timeout trap #3 ; CLS title window rts
The code that does the setting up of the various parameters for the system call to clear a channel has been extracted and placed at the end all by itself. An RTS instrcution has been added to allow us to go back to where we came from. The second piece of code is easier (?) to read and will be smaller when finished.
So that is all there is to it. If you remember back to the boring part of this series (what do you mean 'which boring part ?') where I discussed the inner workings of the BSR instruction, you will remember that BSR stacks the address of the instruction that will be exected next (after the BSR), jumps to the address given and continues executing from there until it finds an RTS instruction.
The RTS instruction stop the program in its tracks, sets the PC (2 points if you can remember what PC stands for ...) to the address that was stacked and proceeds to execute from there again. Those of you who are ahead of me at this point will realise that the RTS instruction takes the top 4 bytes off of the stack REGARDLESS of what they are. If they are a valid return address then fine, no problems. If, on the other hand, they are some data, then who knows what will happen when the RTS is executed.
For this reason, it is very important that your stack should be exactly the same on the way out of a subroutine as it was on the way in. Don't do this, for example :
start blah blah blah : : move.l channel_id,a0 ; First channel id bsr cls : : rts *------------------------------------------------------------------------- * BROKEN subroutine to clear the SCR or CON channel whose ID is held in A0. *------------------------------------------------------------------------- cls move.l d0,-(a7) ; Preserve D0 until later moveq #sd_clear,d0 ; CLS moveq #-1,d3 ; Infinite timeout trap #3 ; CLS title window rts ; Program explodes here !
In this example, the old value of D0.L is on the stack on top of the return address. When the RTS instruction is executed it doesn't know (or care) about what is on the stack, it just grabs the top 4 bytes and sets the PC to that 'address'. (You get 2 points if you remembered PC = Program Counter !)