RUNTIME: the library compiled programs call
What it is
RUNTIME is 4013 bytes of 6502 machine language, normally loaded at $0803-$17AF with BLOAD RUNTIME before a compiled program is run with BRUN. Compiled code is mostly calls: almost every statement, operator and function becomes a JSR into RUNTIME or into the Applesoft ROM, after code that loads the operands. RUNTIME keeps everything the ROM does not provide: integer arithmetic, the variable dispatchers, arrays, FOR/NEXT, the string routines and garbage collector, INPUT and READ, COMMON, ONERR/RESUME, and small entries that check an argument and go on into the ROM's own code for a statement.
The source is written for Apple's DOS Toolkit assembler (EDASM) and assembles to the library byte for byte. It is split into files small enough for the editor; RUNTIME.S1 holds the equates and the ORG, and each file ends with CHN and the name of the next, so assembling RUNTIME.S1 assembles the whole library (see Source files).
To assemble it with the DOS Tool Kit: boot the Tool Kit disk with the source disk in drive 2, RUN EDASM, then at the editor's : prompt type DRIVE 2 and ASM RUNTIME.S1. The assembler reports SUCCESSFUL ASSEMBLY: NO ERRORS and writes RUNTIME.S1.OBJ0, a binary file at $0803 of 4013 bytes, identical to RUNTIME. (The source disk has room for it; rename it RUNTIME to use it.)
Starting a program
Every object file starts with JSR INIT and a 17-byte header (INIT). INIT:
- points the & vector at the program, so
&runs it again; - puts
JMPinstructions at $58 (to FLOATI, which floats the integer accumulator) and $5B (the ONERR handler); - learns the library's own address from the operand of the caller's
JSRand computes the addresses it stores from it; - copies a 15-byte patch over CHRGET (CHRPATCH) that lets the ROM's error handler reach a compiled ONERR handler;
- copies the header to page zero: the first DATA item ($12), the highest address ($14), the COMMON bounds ($69-$6E) and the local bounds ($7B-$80). A program that declares USECOMMON checks that the COMMON bounds are those the previous program left (otherwise error 163, TYPE MISMATCH);
- clears the variables (CLEAR) and resets the stack (RESETSTK), leaving a return address to NORET at the bottom so a RETURN without GOSUB is caught.
How compiled code calls it
Accumulators. Real values are in Applesoft's FAC ($9D-$A2), with ARG ($A5-$AA) as the second operand. Integers are in IACC ($A0 high, $A1 low; these are FAC+3 and FAC+4, which is where AYINT leaves its result) and IARG ($9E/$9F). Strings are pointers to [length][text]: the loaded string at $77/$78 and the pushed string at $79/$7A.
Real and integer entries. A routine that takes a number usually has two entries 3 bytes apart. The real entry is JSR AYINT (or for an address JSR $58, which floats IACC), and the code generator uses whichever matches the operand's type. Byte arguments go through the ROM's CONINT (error ILLEGAL QUANTITY above 255); addresses through GETADR.
Inline operands. Some calls are followed by data the routine reads through its return address and then returns past: the loop variable's address after the loop start and NEXT; a literal's address after STRLIT; the handler's address after ONERR; a count and line addresses after ON GOTO/GOSUB; an array's descriptor after the array dispatchers.
Variables. Compiled code reaches every variable through a small routine of its own, which loads an operation number and the variable's address and jumps to a dispatcher:
| Dispatcher | Operations |
|---|---|
| DISPREAL real | 0 load FAC, 1 store, 2 add, 3 variable - FAC, 4 multiply, 5 compare, 6 load ARG, 7 variable / FAC |
| DISPINT integer | 0 load IACC, 1 store, 2 add, 3 variable - IACC, 4 multiply, 5 compare; 6-13 the real operations with the variable used as a real |
| DISPSTR string | load (carry set) or store (carry clear) the string pointer |
| ARRREAL, ARRSTR, ARRINT arrays | load an element (carry set) or leave its address for ARRSTORE (carry clear) |
Memory and strings
String space runs down from HIMEM; $6F/$70 is its bottom. New strings are made below it (NEWSTR); when there is no room the garbage collector (GARBAG) packs the strings still in use at the top. Its roots are the COMMON strings, the local strings and the two string accumulators; literals and DATA items stay where they are in the program. An empty string is kept at HIMEM-1, and cleared string variables point at it. REM! CLEAR CHAIN (CLRCHAIN) collects garbage keeping only the COMMON strings, then clears the locals, ready for BRUN of the next program.
FOR loops
The loop start stores the initial value and pushes the variable's address; the limit and step push the rest. A frame is 16 bytes, from the top of the stack:
| Bytes | Real loop | Integer loop |
|---|---|---|
| 1-2 | the marker $09B0 | the marker |
| 3-4 | loop address - 1 | byte 4 = 0 marks an integer loop |
| 5-9 | step (packed real) | step in 5-6 |
| 10-14 | limit (packed real) | loop address in 8-9, limit in 10-11 |
| 15-16 | the variable's address | the variable's address |
The marker is also a valid return address: a subroutine that RETURNs with loops still open lands at FORUNWND, which drops the frame and returns again. NEXT finds the variable's frame, dropping inner loops, adds the step and compares with the limit in the step's direction. POP passes over frames to drop a GOSUB return.
Errors
Every error ends at ERREXIT with Applesoft's error number in X. Without ONERR the object address of the failing call becomes the "line number" of the message, so errors report addresses in the compiled program. With ONERR the patched CHRGET passes control to the handler, and RESUME (with the RESUME/debug option, which puts DEBUG before every statement) restarts the failing statement.
| Number | Error | Raised by |
|---|---|---|
| 0 | NEXT WITHOUT FOR | NEXT |
| 16 | SYNTAX | READ of a bad number |
| 22 | RETURN WITHOUT GOSUB | POP, RETURN on an empty stack |
| 42 | OUT OF DATA | READ |
| 53 | ILLEGAL QUANTITY | hi-res coordinates, shape number, ASC, LEFT$/RIGHT$/MID$ count, byte arguments |
| 69 | OVERFLOW | integer arithmetic, integer loops |
| 77 | OUT OF MEMORY | strings, CLEAR, HIMEM: |
| 107 | BAD SUBSCRIPT | array elements |
| 163 | TYPE MISMATCH | a USECOMMON program whose COMMON block differs |
| 176 | STRING TOO LONG | concatenation |
| 254, 255 | bad input, Control-C | INPUT (with ONERR) |
Entries by kind
| Kind | Entries |
|---|---|
| program | INIT, PGMEND, STOP, DEBUG, ONERR, RESUME, CLRCOM, CLEAR, CLRCHAIN, HIMEM |
| flow | POP, ONGOTO, ONGOSUB, FORINIT, NEXT, FORLIM, FORSTEP |
| integers | DISPINT, INEG, ISUBT, IMOVE, FLOATI, LOGORI, LOGANDI |
| relations | RELNE, RELGT, RELGE, RELEQ, RELLE, RELLT, STRGE, STRLT, STRLE, STRGT, STREQ, STRNE |
| strings | DISPSTR, STRLIT, PRSTR, CONCAT, PUSHSTR, CHRFN, STRFN, LEFT, RIGHT, MID2, MID3, VALFN, ASCFN, LENFN, FREFN |
| input and output | INPBEG, INPNUM, INPSTR, INPEND, READNUM, READSTR, RESTORE, GETNUM, GETSTR, PRINTN, COMMA, TAB, SPC, PRNUM, INNUM |
| screen and graphics | VTAB, HTAB, COLOR, PLOT, HLIN, VLIN, SCRN, HCOLOR, HPLOT, HPLOTTO, DRAW, XDRAW, DRAWAT, XDRAWAT, ROT, SCALE, PDL, POS |
| memory | SVADDR, PEEK, CALL, POKE, WAIT, WAIT3, SPEED |
| arrays | ARRREAL, ARRSTR, ARRINT, ARRSTORE, PUSHSUB |
Source files
| File | Addresses | Characters |
|---|---|---|
| RUNTIME.S1 | $0803-$08DA | 10969 |
| RUNTIME.S2 | $08DB-$0B10 | 9988 |
| RUNTIME.S3 | $0B11-$0E33 | 11854 |
| RUNTIME.S4 | $0E34-$1191 | 12045 |
| RUNTIME.S5 | $1192-$14FC | 12014 |
| RUNTIME.S6 | $14FD-$17AF | 9506 |