The C64's BASIC 2.0 (Microsoft BASIC) resides in ROM at $A000–$BFFF. It is a tokenized interpreter: typed commands are compressed into single-byte tokens before storage.
Each BASIC line in memory has this structure:
[link_lo][link_hi][line_lo][line_hi][tokens...][00]
The program ends with a double $00 byte.
Variables follow the program in memory:
| Address | Name | Description |
|---|---|---|
| $2B-$2C (43-44) | TXTTAB | Start of BASIC program text |
| $2D-$2E (45-46) | VARTAB | Start of variable area |
| $2F-$30 (47-48) | ARYTAB | Start of array area |
| $31-$32 (49-50) | STREND | End of array area |
| $33-$34 (51-52) | FRETOP | Top of free string storage |
| $37-$38 (55-56) | MEMSIZ | Top of BASIC RAM |
| $39-$3A (57-58) | CURLIN | Current executing line number ($FF = immediate mode) |
| $3B-$3C (59-60) | OLDLIN | Previous line number |
| $43-$44 (67-68) | DATPTR | Pointer into DATA statements |
| $60-$66 (96-102) | FAC#1 | Floating-point accumulator 1 (5 bytes+sign+exp) |
| $61-$66 (97-102) | FACEXP | FAC1 exponent and mantissa |
| $69-$6E (105-110) | FAC#2 | Floating-point accumulator 2 |
BASIC keywords are stored as single bytes ($80–$CB) in tokenized programs.
| Token | Keyword | Token | Keyword |
|---|---|---|---|
| $80 | END | $A0 | TAB( |
| $81 | FOR | $A1 | TO |
| $82 | NEXT | $A2 | FN |
| $83 | DATA | $A3 | SPC( |
| $84 | INPUT# | $A4 | THEN |
| $85 | INPUT | $A5 | NOT |
| $86 | DIM | $A6 | STEP |
| $87 | READ | $A7 | + |
| $88 | LET | $A8 | - |
| $89 | GOTO | $A9 | * |
| $8A | RUN | $AA | / |
| $8B | IF | $AB | ↑ (power) |
| $8C | RESTORE | $AC | AND |
| $8D | GOSUB | $AD | OR |
| $8E | RETURN | $AE | > |
| $8F | REM | $AF | = |
| $90 | STOP | $B0 | < |
| $91 | ON | $B1 | SGN |
| $92 | WAIT | $B2 | INT |
| $93 | LOAD | $B3 | ABS |
| $94 | SAVE | $B4 | USR |
| $95 | VERIFY | $B5 | FRE |
| $96 | DEF | $B6 | POS |
| $97 | POKE | $B7 | SQR |
| $98 | PRINT# | $B8 | RND |
| $99 | $B9 | LOG | |
| $9A | CONT | $BA | EXP |
| $9B | LIST | $BB | COS |
| $9C | CLR | $BC | SIN |
| $9D | CMD | $BD | TAN |
| $9E | SYS | $BE | ATN |
| $9F | OPEN | $BF | PEEK |
| (more) | CLOSE | $C0 | LEN |
| $C1 | STR$ | $C2 | VAL |
| $C3 | ASC | $C4 | CHR$ |
| $C5 | LEFT$ | $C6 | RIGHT$ |
| $C7 | MID$ | $C8 | GO |
BASIC stores all numbers in 5-byte floating-point format:
Byte 0: Exponent (excess-128, 0=zero)
Byte 1: Mantissa byte 1 (bit 7 is sign: 0=positive, 1=negative)
Byte 2: Mantissa byte 2
Byte 3: Mantissa byte 3
Byte 4: Mantissa byte 4
Value = (–1)^sign × 0.1mmmmm... × 2^(exp–128)
The BASIC math routines can be called from machine language. The floating-point accumulator (FAC1) is at $61–$66.
Key math routines: | Address | Routine | Description | |---------|---------|-------------| | $BC0C | GIVAYF | Convert integer in A/Y to FAC1 | | $B391 | FADDH | Add 0.5 to FAC1 | | $B867 | FMULT | Multiply FAC1 by FAC2 | | $BB07 | FDIVT | Divide FAC2 by FAC1 | | $B849 | FADD | Add FAC2 to FAC1 | | $B850 | FSUB | Subtract FAC1 from FAC2 | | $B8D4 | LOG | LOG of FAC1 → FAC1 | | $BF11 | SQR | Square root of FAC1 → FAC1 | | $B9EA | SIN | Sine of FAC1 (radians) | | $BA28 | COS | Cosine of FAC1 | | $BB0F | TAN | Tangent of FAC1 | | $AE33 | AYINT | Convert FAC1 to 2-byte integer in $14-$15 | | $B7F7 | GETADR | Convert FAC1 to 16-bit address in $14-$15 | | $BD7E | FOUT | Convert FAC1 to ASCII string at $0100 | | $B395 | MOVFM | Move 5-byte float from (A,Y) to FAC1 | | $BBD4 | MOVMF | Move FAC1 to 5 bytes at (A,Y) |
The BASIC interpreter can be extended by patching the BASIC main loop vectors:
| Address | Vector | Default | Purpose |
|---|---|---|---|
| $0300-$0301 | IERROR | $E38B | Error handler |
| $0302-$0303 | IMAIN | $A483 | Main interpreter loop |
| $0304-$0305 | ICRNCH | $A57C | Tokenize (crunch) line |
| $0306-$0307 | IQPLOP | $A71A | De-tokenize for LIST |
| $0308-$0309 | IGONE | $A7E4 | Execute statement |
| $030A-$030B | IEVAL | $AE86 | Evaluate expression |
When BASIC encounters an unrecognized token or wants to execute a statement, it goes through IGONE ($0308/$0309). Patch this to intercept commands.
; Patch BASIC execute vector to add new commands
LDA #<NEWCMD
STA $0308
LDA #>NEWCMD
STA $0309
RTS
NEWCMD ; First, check for our keyword
; In BASIC, current token is in A when we arrive here
CMP #$FE ; some unused token we assigned
BEQ DO_NEWCMD
JMP $A7E4 ; else: jump to original IGONE
DO_NEWCMD:
; parse parameters from BASIC line
; then execute command
RTS
CHRGET ($0073/$0074) gets the next character from the BASIC text.
After calling, A contains the next PETSCII character or token.
; Get next character from BASIC program text
JSR $0073 ; CHRGET — increments and reads next char
; A now has next character/token, with Z flag set if it's a space
Full procedure from The Advanced ML Book — adding a REPEAT command:
PRINT CHR$(147) ' Home + clear (PETSCII 147)
PRINT PEEK(57):PRINT PEEK(58) ' Current line number low/high = PEEK(57)+PEEK(58)*256
PRINT FRE(0) ' Free BASIC RAM in bytes
POKE 43,1 ' Set TXTTAB low byte to 1 (start at $0801+offset)
POKE 44,N ' High byte: program starts at N*256
NEW ' Initialize BASIC for new location
POKE 780, A_VAL ' $030C — A register value for SYS
POKE 781, X_VAL ' $030D — X register
POKE 782, Y_VAL ' $030E — Y register
SYS ADDRESS
RESULT = PEEK(780) ' A register after SYS
| Error | Number | Message |
|---|---|---|
| 1 | TOO MANY FILES | |
| 2 | FILE OPEN | |
| 3 | FILE NOT OPEN | |
| 4 | FILE NOT FOUND | |
| 5 | DEVICE NOT PRESENT | |
| 6 | NOT INPUT FILE | |
| 7 | NOT OUTPUT FILE | |
| 8 | MISSING FILE NAME | |
| 9 | ILLEGAL DEVICE NUMBER | |
| 10 | NEXT WITHOUT FOR | |
| 11 | SYNTAX | |
| 12 | RETURN WITHOUT GOSUB | |
| 13 | OUT OF DATA | |
| 14 | ILLEGAL QUANTITY | |
| 15 | OVERFLOW | |
| 16 | OUT OF MEMORY | |
| 17 | UNDEF'D STATEMENT | |
| 18 | BAD SUBSCRIPT | |
| 19 | REDIM'D ARRAY | |
| 20 | DIVISION BY ZERO | |
| 21 | ILLEGAL DIRECT | |
| 22 | TYPE MISMATCH | |
| 23 | STRING TOO LONG | |
| 24 | FILE DATA | |
| 25 | FORMULA TOO COMPLEX | |
| 26 | CAN'T CONTINUE | |
| 27 | UNDEF'D FUNCTION | |
| 28 | VERIFY | |
| 29 | LOAD |
; Cause BASIC error N from machine language
LDX #N ; error number
JMP $A437 ; BASIC error handler entry
| Code | Effect | Code | Effect |
|---|---|---|---|
| 5 | White text | 156 | Purple text |
| 13 | RETURN | 157 | Cursor left |
| 14 | Lower case | 158 | Yellow text |
| 17 | Cursor down | 159 | Cyan text |
| 18 | Reverse on | 28 | Red text |
| 19 | Home | 29 | Cursor right |
| 20 | Delete | 30 | Green text |
| 28 | Red text | 31 | Blue text |
| 29 | Cursor right | 144 | Black text |
| 30 | Green text | 145 | Cursor up |
| 31 | Blue text | 146 | Reverse off |
| 133-140 | F1-F8 keys | 147 | Clear screen |
| 148 | Insert | 149 | Brown text |
| 150 | LT Red text | 151 | Dark grey |
| 152 | Grey | 153 | LT green |
| 154 | LT blue | 155 | LT grey |
Powered by TurnKey Linux.