While wandering through the Color/Extended/Disk BASIC Unraveled books trying to figure out how the RAM hooks worked, I came across a technique that I had never used.
So of course I’m going to digress with a bunch of other stuff first.
GOTO and GOSUB
In BASIC, you can run code using GOTO or GOSUB. GOTO jumps to a specific line number and runs from there. If that code needs to get back to the main loop, it has to do so with another GOTO.
10 REM MAIN LOOP
20 A$=INKEY$:IF A$="" THEN 20
30 IF A$="L" THEN GOTO 100
40 IF A$="R" THEN GOTO 200
50 GOTO 10
100 REM MOVE LEFT
...
190 GOTO 10
200 REM MOVE RIGHT
...
290 GOTO 10
This is fine for code that does one specific thing at one specific place, but the routines at 100 and 200 could not be used anywhere else in the program unless after such use they always resumed running at line 10.
GOSUB is often a better option, since it eliminates the need for the subroutine to know where it must GOTO at the end:
10 REM MAIN LOOP
20 A$=INKEY$:IF A$="" THEN 20
30 IF A$="L" THEN GOSUB 100
40 IF A$="R" THEN GOSUB 200
50 GOTO 10
100 REM MOVE LEFT
...
190 RETURN
200 REM MOVE RIGHT
...
290 RETURN
There are inefficiencies to the above code, as well as some potential problems, but it’s good enough for an example.
When GOSUB is seen, BASIC remembers the exact spot after the line number and saves it somewhere. It then jumps to that line number, and when a RETURN is seen, it retrieves the saved location and jumps back there to continue executing.
The location is saved on a stack, so you can GOSUB from a GOSUB from a GOSUB, as long as there is enough memory to remember all those locations.
Stack Notes
Think of the stack like a stack of POST-IT(tm) notes. When a GOSUB happens, the return location is written on a piece of paper, then that paper is placed somewhere. If another GOSUB is seen, that location is written on paper and then stuck on top of the previous one, and so on. You end up with a stack of locations. When a RETURN is seen, it grabs the top piece of paper and returns to that location, then that paper is discarded.
10 PRINT "TEST START"
20 GOSUB 100
30 PRINT "TEST END"
40 END
100 REM FIRST
110 PRINT " FIRST START"
120 GOSUB 200
130 PRINT " FIRST END"
140 RETURN
200 REM SECOND
210 PRINT " SECOND START"
220 PRINT " SECOND END"
230 RETURN
Running that program prints:
TEST START
FIRST START
SECOND START
SECOND END
FIRST END
TEST END
Test calls First which calls Second. When Second returns, it returns back to First. When First returns, it returns back to Start.
If you ever leave a GOSUB with a GOTO, that return location is still there, saved, and that memory is never returned to the BASIC program. This will crash a program:
10 PRINT X
20 X=X+1
30 GOSUB 10
Each GOSUB adds a return location to the BASIC stack, and since the program is recursively calling itself without ever RETURNing, it will eventually run out of BASIC stack space. In the test I just did, I received an ?OM ERROR (out of memory) at count 3247. On a system with less RAM available (smaller RAM, larger program, etc.) that will happen more often.
This is a STACK OVERFLOW, and languages like C, assembly, etc. can all have them. (I assume that’s where the Q&A site www.stackoverflow.com got its name from.)
Some environments have stack checking, and they will terminate the offending program with an error message when this happens. This is what happened with the ?OM ERROR. Beyond BASIC, operating systems generally take care of this stack checking. Programs written in C or 6809 assembly running under OS-9 most certainly will get terminated with a stack overflow if they try to use more than the OS reserved for them. (Ah, if I only understood this way back then. I just knew to keep adding more memory to a command until it ran without crashing…)
Assembly GOTO and GOSUB
In 6809 assembly, a GOTO equivalent would be like a BRx branch instruction or a JMP jump instruction. The earlier BASIC example might look like this in CoCo assembly:
mainloop
jsr [$a002] * Call ROM POLCAT routine, key comes back in A.
beq mainloop * If A="", GOTO mainloop.
cmpa 'L * Compare A to character "L".
beq moveleft * If A="L", GOTO moveleft.
cmpa 'R * Compare A to character "R".
beq moveright * If A="R", GOTO moveright.
bra mainloop * GOTO mainloop.
moveleft
...
bra mainloop * GOTO mainloop.
moveright
...
bra mainloop * GOTO mainloop.
For very simple logic, assembly can be quite similar to BASIC.
GOSUB would be BSR branch subroutine or JSR jump subroutine operation. Here is what the second BASIC example might look like in assembly:
jsr [$a002] * Call ROM POLCAT routine, key comes back in A.
beq mainloop * If A="", GOTO mainloop.
cmpa 'L * Compare A to character "L".
bsr moveleft * If A="L", GOSUB moveleft.
cmpa 'R * Compare A to character "R".
bsr moveright * If A="R", GOSUB moveright.
bra mainloop * GOTO mainloop.
moveleft
...
rts * RETURN.
moveright
...
rts * RETURN.
Very simple code like this would be a good way for a BASIC programmer to tip-toe in to the land of assembly language. It’s quite fun, until you realize how much work is needed for anything that is not as simple ;-)
And now the third example… Since assembly does not have a PRINT command, I created a simple subroutine that uses the ROM CHROUT routine to print out whatever character is in the A register.
* lwasm jsrtest.asm -fbasic -ojsrtest.bas --map
org $3f00
start
* 10 PRINT "TEST START"
ldx #teststartmsg * X=Start of message.
jsr print * GOSUB print.
* 20 GOSUB 100
jsr first * GOSUB first.
ldx #testendmsg * X=Start of message.
* 30 PRINT "TEST END"
jsr print * GOSUB print.
* 40 END
rts * RETURN
first
* 110 PRINT " FIRST START"
ldx #firststartmsg * X=Start of message.
jsr print * GOSUB print.
* 120 GOSUB 200
jsr second
* 130 PRINT " FIRST END"
ldx #firstendmsg * X=Start of message.
jsr print * GOSUB print.
* 140 RETURN
rts * RETURN
second
* 210 PRINT " SECOND START"
ldx #secondstartmsg * X=Start of message.
jsr print * GOSUB print.
* 230 PRINT " SECOND END"
ldx #secondendmsg * X=Start of message.
jsr print * GOSUB print.
* 240 RETURN
rts * RETURN
* PRINT subroutine. Prints the string pointed to by X.
print
lda ,x+
beq done
jsr [$a002]
bra print
done
lda #13
jsr [$a002]
rts
* Data storage for the string messages.
teststartmsg
fcc "TEST START"
fcb 0
testendmsg
fcc "TEST END"
fcb 0
firststartmsg
fcc " FIRST START"
fcb 0
firstendmsg
fcc " FIRST END"
fcb 0
secondstartmsg
fcc " SECOND START"
fcb 0
secondendmsg
fcc " SECOND END"
fcb 0
Here is a BASIC loader for the above assembly routine. You can load and RUN this, then type EXEC &H3F00 to run it.
10 READ A,B
20 IF A=-1 THEN 70
30 FOR C = A TO B
40 READ D:POKE C,D
50 NEXT C
60 GOTO 10
70 END
80 DATA 16128,16267,142,63,62,189,63,45,189,63,16,142,63,73,189,63,45,57,142,63,82,189,63,45,189,63,32,142,63,96,189,63,45,57,142,63,108,189,63,45,142,63,125,189,63,45,57,166,128,39,6,173,159,160,2,32,246,134,13,173,159,160,2,57,84,69,83,84,32
90 DATA 83,84,65,82,84,0,84,69,83,84,32,69,78,68,0,32,32,70,73,82,83,84,32,83,84,65,82,84,0,32,32,70,73,82,83,84,32,69,78,68,0,32,32,32,32,83,69,67,79,78,68,32,83,84,65,82,84,0,32,32,32,32,83,69,67,79,78,68,32,69,78,68,0,-1,-1
Stack Overflow in assembly
Just for fun… Here is the GOSUB crash program in assembly. 99% of this code is just a crappy routine I had to write to print out a decimal number.
org $3f00
start
ldx #0 * X=0
loop
* 10 PRINT X
jsr printx * GOSUB printx.
* 20 X=X+1
leax 1,x * X=X+1
* 30 GOSUB 10
bsr loop * GOSUB loop.
rts * Return to BASIC.
*
* Crappy routine I just put together to try to print out a decimal number.
*
printx
* Init buffer to 000000.
lda #'0
sta numberstring
sta numberstring+1
sta numberstring+2
sta numberstring+3
sta numberstring+4
sta numberstring+5
* X is our counter.
tfr x,d * Copy X to D
tenthousands
cmpd #10000
blt thousands
subd #10000
inc numberstring
bra tenthousands
thousands
cmpd #1000
blt hundreds
subd #1000
inc numberstring+1
bra thousands
hundreds
cmpd #100
blt tens
subd #100
inc numberstring+2
bra hundreds
tens
cmpd #10
blt ones
subd #10
inc numberstring+3
bra hundreds
ones
cmpd #0
blt print
subd #1
inc numberstring+4
print
ldy #numberstring
printloop
lda ,y+
jsr [$a002]
cmpy #bufferend
bne printloop
lda #13
jsr [$a002]
rts
numberstring fcb 5 * Holds 00000-99999
bufferend equ numberstring+5
Thank you for ignoring my poorly-coded “printx” subroutine.
When I run this, it crashes after printing 08141. I believe it is a much smaller number than the BASIC one because it has much less memory for the stack. Since this program starts in memory at the 32K mark (&H3F00), the stack has from end of RAM (&HFF00) down to the end of this program. As the stack grows, without stack checking, it eventually overwrites the running assembly code, crashing the computer.
Let’s pretend we never did that.
What are we learning?
At the start of this article, I mentioned something I just learned from looking at other assembly code. I learned how to get out of an assembly GOSUB routine without needing to return. Just like BASIC, calling a subroutine recursively will cause a crash. Unlike BASIC, there is no stack checking when running raw 6809 code without an operating system, so it can really crash BASIC and require a reset of the computer.
There is a way to GOTO out of an assembly routine without leaving that GOSUB program counter memory on the stack. You simply move the stack pointer by 2 places.
For example, say you had assembly code that was like this BASIC:
10 GOSUB 100
100 GOSUB 200
200 ...
The stack would look like this:
<- Next GOSUB would be stored here.
[200] <- Top of stack. RETURN would use this.
[100]
[ 10]
BASIC has no way to throw away whatever GOSUB entry is on the top of the stack, but it is simple to do in assembly just by adding 2 to the S (stack pointer) register.
start
jsr first * GOSUB first.
rts * RETURN
first
jsr second * GOSUB second.
rts * RETURN
second
leas 2,S * Move stack pointer down two bytes.
rts * RETURN
By the time the code gets to “second”, the assembly stack should look like this:
<- Next bsr/jsr would be stored here.
[first] <- Top of stack. RTS would use this.
[start]
When the second routine does “leas 2,s”, the stack pointer moves down and it looks like this:
[xxxxx] <- Next bsr/jsr would be stored here.
[start] <- Top of stack. RTS would use this.
Side Note: Data on the stack is never erased, but will be overwritten the next time something is stored there. The [xxxxx] is actually still [first].
Now if the subroutine does an RTS, it will be returning to start and not first. Thus, if you add that to the assembly and run it, the output will be:
TEST START
FIRST START
SECOND START
SECOND END
TEST END
I do not know of a legal way to do the same in BASIC, but I am sure there is some POKE that could be done to achieve the same thing.
The Microsoft BASIC ROMs do this trick often, when patching in new routines that override some function.
And now it’s time for a brain break.
Until next time…