--- name: basic-programming description: > Use this skill for Commodore 64 BASIC 2.0 programming, extending BASIC with new commands, BASIC internals (tokenization, interpreter), math routines, floating-point access from ML, and BASIC program structure. Sources: COMPUTE!'s VIC-20 and C64 Tool Kit: BASIC, The Advanced ML Book, The Anatomy of the C64. --- # BASIC 2.0 Programming on the Commodore 64 ## BASIC Overview 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. ### Program Storage Format Each BASIC line in memory has this structure: ``` [link_lo][link_hi][line_lo][line_hi][tokens...][00] ``` - **link**: 16-bit pointer to next line (absolute address) - **line number**: 16-bit line number - **tokens**: statement tokens and literal data - **00**: end of line marker The program ends with a double $00 byte. ### Variable Storage Variables follow the program in memory: 1. **Numeric variables**: 7 bytes each (2-char name + 5-byte float or 4-byte int) 2. **String variables**: 7 bytes (2-char name + length + 2-byte pointer) 3. **Arrays**: variable-length (name + dimension info + elements) --- ## Key BASIC Locations | 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 Token Table 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 | PRINT | $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 Floating-Point Format 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) ### Using BASIC Math from ML 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) | --- ## Extending BASIC with New Commands The BASIC interpreter can be extended by patching the **BASIC main loop vectors**: ### RAM Vectors Involved | 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 | ### Method 1: Patch IGONE (execute vector) When BASIC encounters an unrecognized token or wants to execute a statement, it goes through IGONE ($0308/$0309). Patch this to intercept commands. ```asm ; Patch BASIC execute vector to add new commands 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 ``` ### Method 2: Using the CHRGET routine `CHRGET` ($0073/$0074) gets the next character from the BASIC text. After calling, A contains the next PETSCII character or token. ```asm ; 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 ``` ### Defining New Keyword with Token Full procedure from _The Advanced ML Book_ — adding a `REPEAT` command: 1. Intercept ICRNCH to recognize the keyword text and replace with a token byte 2. Intercept IQPLOP to de-tokenize back to text for LIST 3. Intercept IGONE to execute when the token is encountered --- ## Useful BASIC Tricks ### Fast Screen Clear ```basic PRINT CHR$(147) ' Home + clear (PETSCII 147) ``` ### Peeking Inside Running Program ```basic PRINT PEEK(57):PRINT PEEK(58) ' Current line number low/high = PEEK(57)+PEEK(58)*256 ``` ### Finding Free Memory ```basic PRINT FRE(0) ' Free BASIC RAM in bytes ``` ### Placing Program at Custom Address ```basic 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 ``` ### Calling ML from BASIC with SYS Register Passing ```basic 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 ``` --- ## BASIC Error Numbers | 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 | | ### Triggering BASIC Error from ML ```asm ; Cause BASIC error N from machine language LDX #N ; error number JMP $A437 ; BASIC error handler entry ``` --- ## PETSCII Control Codes (CHR$() values) | 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 |