├── IL.inc ├── README.md ├── Tiny_BASIC.docx ├── basic.il ├── config.inc ├── ctmon65.inc ├── make.bat ├── mytb.asm ├── mytb.hex ├── mytb.lst ├── storage.asm ├── support.asm └── xkim.inc /IL.inc: -------------------------------------------------------------------------------- 1 | nolist 2 | ;===================================================== 3 | ; IL.inc 4 | ; These are macros for IL instructions 5 | ; 6 | XINIT macro 7 | db 0 8 | endm 9 | ; 10 | DONE macro 11 | db 1 12 | endm 13 | ; 14 | PRS macro 15 | db 2 16 | endm 17 | ; 18 | PRN macro 19 | db 3 20 | endm 21 | ; 22 | SPC macro 23 | db 4 24 | endm 25 | ; 26 | NLINE macro 27 | db 5 28 | endm 29 | ; 30 | ; My NXT is a bit different in that it takes one 31 | ; parameter, which is an address. If the BASIC 32 | ; program is currently running then move to the 33 | ; next line and continue execution. However, if 34 | ; in direct mode, jump to the specified IL label. 35 | ; 36 | NXT macro addr 37 | db 6 38 | dw addr 39 | endm 40 | ; 41 | XFER macro 42 | db 7 43 | endm 44 | ; 45 | SAV macro 46 | db 8 47 | endm 48 | ; 49 | RSTR macro 50 | db 9 51 | endm 52 | ; 53 | CMPR macro 54 | db 10 55 | endm 56 | ; 57 | INNUM macro 58 | db 11 59 | endm 60 | ; 61 | FIN macro 62 | db 12 63 | endm 64 | ; 65 | ; ERR is followed by an error number. The error 66 | ; code is printed along with the line number. 67 | ; Control is passed to the statement set with 68 | ; the ERRGOTO statement. 69 | ; 70 | ERR macro ecode 71 | db 13 72 | dw ecode 73 | endm 74 | ; 75 | ADD macro 76 | db 14 77 | endm 78 | ; 79 | SUB macro 80 | db 15 81 | endm 82 | ; 83 | NEG macro 84 | db 16 85 | endm 86 | ; 87 | MUL macro 88 | db 17 89 | endm 90 | ; 91 | DIV macro 92 | db 18 93 | endm 94 | ; 95 | STORE macro 96 | db 19 97 | endm 98 | ; 99 | IND macro 100 | db 20 101 | endm 102 | ; 103 | LST macro 104 | db 21 105 | endm 106 | ; 107 | INIT macro 108 | db 22 109 | endm 110 | ; 111 | GETLINE macro 112 | db 23 113 | endm 114 | ; 115 | INSERT macro 116 | db 24 117 | endm 118 | ; 119 | RTN macro 120 | db 25 121 | endm 122 | ; 123 | EXIT macro 124 | db 26 125 | endm 126 | ; 127 | LIT macro value 128 | db 27 129 | dw value 130 | endm 131 | ; 132 | CALL macro addr 133 | db 28 134 | dw addr 135 | endm 136 | ; 137 | ; IJMP will set the IL PC to the specified value. 138 | ; 139 | IJMP macro addr 140 | db 29 141 | dw addr 142 | endm 143 | ; 144 | VINIT macro 145 | db 30 146 | endm 147 | ; 148 | ; ERRGOTO sets the point in the code where the IL 149 | ; interpreter will go after any error. 150 | ; 151 | ERRGOTO macro addr 152 | db 31 153 | dw addr 154 | endm 155 | ; 156 | TST macro addr,string 157 | db 32 158 | db (addr-*)-1 159 | db string,0 160 | endm 161 | ; 162 | TSTV macro addr 163 | db 33 164 | db (addr-*)-1 165 | endm 166 | ; 167 | TSTL macro addr 168 | db 34 169 | db (addr-*)-1 170 | endm 171 | ; 172 | TSTN macro addr 173 | db 35 174 | db (addr-*)-1 175 | endm 176 | ; 177 | ; FREE returns the amount of free RAM on top of 178 | ; the stack. This is the amount of room the user 179 | ; program has available. 180 | ; 181 | FREE macro 182 | db 36 183 | endm 184 | ; 185 | ; RANDOM takes the top item off the stack and 186 | ; replaces it with a random number that is 187 | ; MOD the initial value. Ie, if the TOS is 188 | ; 42 then RANDOM returns a value from 0 to 41. 189 | ; 190 | RANDOM macro 191 | db 37 192 | endm 193 | ; 194 | ; ABS will replace the top of stack with the 195 | ; absolute value. 196 | ; 197 | ABS macro 198 | db 38 199 | endm 200 | ; 201 | ; OPENREAD opens a file for reading, as in getting 202 | ; statements from it. 203 | ; 204 | OPENREAD macro 205 | db 39 206 | endm 207 | ; 208 | ; OPENWRITE opens a file for writing, as in saving 209 | ; the current program to it. 210 | ; 211 | OPENWRITE macro 212 | db 40 213 | endm 214 | ; 215 | ; DCLOSE closes any open disk file. 216 | ; 217 | DCLOSE macro 218 | db 41 219 | endm 220 | ; 221 | ; DGETLINE gets one line from the disk file and puts it 222 | ; into LINBUFF. 223 | ; 224 | DGETLINE macro 225 | db 42 226 | endm 227 | ; 228 | ; DLIST saves the program to an open disk file. 229 | ; 230 | DLIST macro 231 | db 43 232 | endm 233 | ; 234 | list 235 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 6502 Tiny BASIC 2 | 3 | This is Bob Applegate's (bob@corshamtech.com) spin of a Tiny BASIC interpreter for the 6502. It uses an IL approach, like proposed by Dr Dobb's Journal in the first few issues. This is not fancy, it's not bug free, and it's not amazing by any means, but it was fun to write and decent enough to do fun stuff and run demos. 4 | 5 | ## Features 6 | * Pure 6502 code. 7 | * Small, as per Tiny. Intepreter and user code fits in 0200-13FF. 8 | * Can support Corsham Technologies SD Card System to save/load files. 9 | * 16 bit integer math. 10 | * Built using IL, so you can change the languge by changing just the IL. 11 | 12 | ## Keywords 13 | * LET 14 | * GOTO 15 | * GOSUB 16 | * PRINT 17 | * IF 18 | * INPUT 19 | * RETURN 20 | * END 21 | * LIST 22 | * RUN 23 | * NEW 24 | * EXIT 25 | * REM 26 | 27 | And if the SD functions are enabled: 28 | 29 | * SAVE 30 | * LOAD 31 | 32 | Functions: 33 | 34 | * FREE() 35 | * RND(x) 36 | * ABS(x) 37 | 38 | ## Requirements 39 | 40 | * RAM from 0200 to 13FF. 41 | * Must have xKIM for the SD related commands to work. 42 | 43 | # Nota Bene 44 | 45 | I've been slowly doing bug fixes but there are probably plenty more. Since it is tiny, there is minimal error checking and little tolerance for odd errors. 46 | 47 | Send bug reports to bob@corshamtech.com. 48 | 49 | ## License 50 | [GNU GPLv3](https://choosealicense.com/licenses/gpl-3.0/) 51 | -------------------------------------------------------------------------------- /Tiny_BASIC.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CorshamTech/6502-Tiny-BASIC/992fa916f20551cfd1f36877252291355ee73528/Tiny_BASIC.docx -------------------------------------------------------------------------------- /basic.il: -------------------------------------------------------------------------------- 1 | LET; 2 | ;===================================================== 3 | ;===================================================== 4 | ;===================================================== 5 | ; This is the IL of the BASIC (or whatever) language. 6 | ; Because of the way macros are implemented by as65, 7 | ; labels can't be on the same line as a macro 8 | ; invocation, so that's why labels are on separate 9 | ; lines. 10 | ; 11 | IL equ * 12 | 13 | ;THE IL CONTROL SECTION 14 | 15 | START: 16 | INIT ;INITIALIZE 17 | NLINE ;WRITE CRLF 18 | ERRGOTO CO ;where to go after an error 19 | VINIT ;clear all variables 20 | ; 21 | ; This is where we jump to get a line of commands or 22 | ; a program from the user. 23 | ; 24 | CO: 25 | GETLINE ;WRITE PROMPT AND GET LINE 26 | TSTL XEC ;TEST FOR LINE NUMBER 27 | INSERT ;INSERT IT (MAY BE DELETE) 28 | IJMP CO 29 | XEC: 30 | XINIT ;INITIALIZE 31 | 32 | ;STATEMENT EXECUTOR 33 | 34 | STMT: 35 | TST S1,"LET" ;IS STATEMENT A LET 36 | TSTV ERRVEC ;YES, PLACE VAR ADDRESS ON AESTK 37 | TST ERRVEC,"=" ;(This line originally omitted) 38 | CALL EXPR ;PLACE EXPR VALUE ON AESTK 39 | DONE ;REPORT ERROR IF NOT NEXT 40 | STORE ;STORE RESULT 41 | NXT CO ;AND SEQUENCE TO NEXT 42 | IJMP STMT 43 | S1: 44 | TST S3,"GO" ;GOTO OT GOSUB? 45 | TST S2,"TO" ;YES...TO, OR...SUB 46 | CALL EXPR ;GET LABEL 47 | DONE ;ERROR IF CR NOT NEXT 48 | XFER ;SET UP AND JUMP 49 | S2: 50 | TST ERRVEC,"SUB" ;ERROR IF NO MATCH 51 | CALL EXPR ;GET DESTINATION 52 | DONE ;ERROR IF CR NOT NEXT 53 | SAV ;SAVE RETURN LINE 54 | XFER ;AND JUMP 55 | S3: 56 | TST S8,"PRINT" ;PRINT 57 | S4: 58 | TST S7,QUOTE ;TEST FOR QUOTE 59 | PRS ;PRINT STRING 60 | S5: 61 | TST S6A,COMMA ;IS THERE MORE? 62 | SPC ;SPACE TO NEXT ZONE 63 | IJMP S4 ;YES JUMP BACK 64 | ; 65 | ; If a semicolon, don't do anything. 66 | ; 67 | S6A: 68 | TST S6,SEMICOLON 69 | IJMP S4 70 | S6: 71 | DONE ;ERROR IF CR NOT NEXT 72 | NLINE 73 | NXT CO 74 | IJMP STMT 75 | ; 76 | ; A jump for code too far away for relative branch 77 | ; 78 | ERRVEC: 79 | IJMP UNKNOWN 80 | ; 81 | S7: 82 | CALL EXPR 83 | PRN ;PRINT IT 84 | IJMP S5 ;IS THERE MORE? 85 | S8: 86 | TST S9,"IF" ;IF STATEMENT 87 | CALL EXPR ;GET EXPRESSION 88 | CALL RELOP ;DETERMINE OPR AND PUT ON STK 89 | CALL EXPR ;GET EXPRESSION 90 | TST UNKNOWN,"THEN" ;(This line originally omitted) 91 | CMPR ;PERFORM COMPARISON -- PERFORMS NXT IF FALSE 92 | IJMP STMT 93 | S9: 94 | TST S12,"INPUT" ;INPUT STATEMENT 95 | S10: 96 | TSTV UNKNOWN ;GET VAR ADDRESS (Originally CALL VAR = nonexist) 97 | INNUM ;MOVE NUMBER FROM TTY TO AESTK 98 | STORE ;STORE IT 99 | TST S11,COMMA ;IS THERE MORE? 100 | IJMP S10 ;YES 101 | 102 | S11: 103 | DONE ;MUST BE CR 104 | NXT CO ;SEQUENCE TO NEXT 105 | IJMP STMT 106 | S12: 107 | TST S13,"RETURN" ;RETURN STATEMENT 108 | DONE ;MUST BE CR 109 | RSTR ;RESTORE LINE NUMBER OF CALL 110 | NXT CO ;SEQUENCE TO NEXT STATEMENT 111 | IJMP STMT 112 | S13: 113 | TST S14,"END" 114 | FIN 115 | S14: 116 | TST S15,"LIST" ;LIST COMMAND 117 | DONE 118 | LST 119 | IJMP CO 120 | S15: 121 | TST S16,"RUN" ;RUN COMMAND 122 | DONE 123 | VINIT ;clear variables 124 | LIT 1 ;GOTO line 1 125 | XFER ;Bob's addition 126 | ; EXIT 127 | IJMP STMT ;and run! 128 | S16: 129 | TST S17A,"NEW" ;clear program 130 | DONE 131 | IJMP START 132 | 133 | S17A: 134 | TST S17,"EXIT" ;allow them to exit BASIC 135 | EXIT 136 | 137 | S17: 138 | TST S17B,"REM" ;REMark. Skip rest of line 139 | NXT CO 140 | IJMP STMT 141 | ; 142 | ; Commands related to saving/restoring programs 143 | ; to/from mass storage. 144 | ; 145 | S17B: 146 | if (XKIM || CTMON65) && DISK_ACCESS 147 | TST S17C,"SAVE" 148 | OPENWRITE 149 | DLIST 150 | DCLOSE 151 | IJMP CO 152 | 153 | S17C: 154 | TST UNKNOWN,"LOAD" 155 | OPENREAD 156 | S17CLP: 157 | DGETLINE ;get line from file 158 | TSTL S17EOL ;no line num means EOL 159 | INSERT ;put it into the program 160 | IJMP S17CLP ;keep going 161 | S17EOL 162 | DCLOSE ;close disk file 163 | IJMP CO ;back to start 164 | endif 165 | ; 166 | ; Else, unknown command. 167 | ; 168 | UNKNOWN: 169 | ERR ERR_SYNTAX ;SYNTAX ERROR 170 | 171 | ;----------------------------------------------------- 172 | EXPR: 173 | TST E0,"-" 174 | CALL TERM ;TEST FOR UNARY -. 175 | NEG ;GET VALUE 176 | IJMP E1 ;NEGATE IT 177 | E0: 178 | TST E1A,"+" ;LOOK FOR MORE 179 | E1A: 180 | CALL TERM ;TEST FOR UNARY + 181 | E1: 182 | TST E2,"+" ;LEADING TERM 183 | CALL TERM 184 | ADD 185 | IJMP E1 186 | E2: 187 | TST E3,"-" ;ANY MORE? 188 | CALL TERM ;DIFFERENCE TERM 189 | SUB 190 | IJMP E1 191 | E3: 192 | T2: 193 | RTN ;ANY MORE? 194 | TERM: 195 | CALL FACT 196 | T0: 197 | TST T1,"*" 198 | CALL FACT ;PRODUCT FACTOR. 199 | MUL 200 | IJMP T0 201 | T1: 202 | TST T2,"/" 203 | CALL FACT ;QUOTIENT FACTOR. 204 | DIV 205 | IJMP T0 206 | 207 | UNKNOWNVEC: 208 | IJMP UNKNOWN 209 | 210 | ; 211 | ; Factor an expression. Always test for functions 212 | ; first or else they'll be confused for variables. 213 | ; 214 | FACT: 215 | TST F2A,"FREE()" 216 | FREE 217 | RTN 218 | ; 219 | ; RND() is supposed to have an argument but if none 220 | ; was provided, just assume a large value. 221 | ; 222 | F2A: 223 | TST F2B,"RND(" 224 | TST F2A1,")" 225 | LIT 32766 226 | RANDOM 227 | RTN 228 | F2A1: 229 | CALL FACT ;GET RANGE 230 | TST UNKNOWN,")" 231 | RANDOM 232 | RTN 233 | 234 | F2B: 235 | TST F2C,"ABS(" 236 | CALL FACT ;get value 237 | TST UNKNOWN,")" 238 | ABS 239 | RTN 240 | 241 | F2C: 242 | TSTV F0 243 | IND ;YES, GET THE VALUE. 244 | RTN 245 | F0: 246 | TSTN F1 ;NUMBER, GET ITS VALUE. 247 | RTN 248 | F1: 249 | TST F2A,"(" ;PARENTHESIZED EXPR. 250 | CALL EXPR 251 | TST F2,")" 252 | RTN 253 | 254 | F2: 255 | ERR ERR_SYNTAX ;ERROR. 256 | 257 | RELOP: 258 | TST iR0,"=" 259 | LIT 2 ;= 260 | RTN 261 | iR0: 262 | TST R4,"<" 263 | TST iR1,"=" 264 | LIT 3 ;<= 265 | RTN 266 | iR1: 267 | TST R3,">" 268 | LIT 5 ;<> 269 | RTN 270 | R3: 271 | LIT 1 ;< 272 | RTN 273 | R4: 274 | TST UNKNOWNVEC,">" 275 | TST R5,"=" 276 | LIT 6 ;>= 277 | RTN 278 | R5: 279 | TST R6,"<" 280 | LIT 1 281 | RTN ;(This line originally omitted) 282 | R6: 283 | LIT 4 ;>??? 284 | RTN 285 | 286 | ILEND equ * 287 | -------------------------------------------------------------------------------- /config.inc: -------------------------------------------------------------------------------- 1 | ;********************************************************* 2 | ; FILE: config.inc 3 | ; 4 | ; General configuration file for the Corsham Technologies 5 | ; CTMON65 monitor. 6 | ;********************************************************* 7 | ; 8 | ; Current version and revision 9 | ; 10 | VERSION equ 0 11 | REVISION equ 1 12 | ; 13 | ;FALSE equ 0 14 | ;TRUE equ !FALSE 15 | ; 16 | ; SS-50 bus constants 17 | ; 18 | IO_BASE equ $e000 19 | IO_SIZE equ 16 20 | ; 21 | ; Memory usage 22 | ; 23 | ZERO_PAGE_START equ $00f0 24 | ROM_START equ $f000 25 | RAM_START equ $df00 26 | ; 27 | ; If enabled, turn on buffered input code. 28 | ; 29 | BUFFERED_INPUT equ FALSE 30 | ; 31 | MAX_ARGC equ 5 32 | ; 33 | ; If enabled, the debugger will display the flag register 34 | ; in ASCII. Nice, but takes more code. 35 | ; 36 | FULL_STATUS equ TRUE 37 | ; 38 | ; Enable EXTENDED_CMDS to allow linking external commands 39 | ; to the command handler. 40 | ; 41 | EXTENDED_CMDS equ FALSE 42 | ; 43 | ; Define to enable SD related functions 44 | ; 45 | SD_ENABLED equ TRUE 46 | ; 47 | ; Size of the keyboard buffer 48 | ; 49 | BUFFER_SIZE equ 132 50 | 51 | -------------------------------------------------------------------------------- /ctmon65.inc: -------------------------------------------------------------------------------- 1 | ;********************************************************* 2 | ; FILE: ctmon65.inc 3 | ; 4 | ; Applications wishing to run under CTMON65 should include 5 | ; this file, as it defines vectors and other pieces of 6 | ; necessary data. 7 | ;********************************************************* 8 | ; 9 | include "config.inc" 10 | ; 11 | bss 12 | org ROM_START 13 | ; 14 | ;========================================================= 15 | ; Jump table to common functions. The entries in this 16 | ; table are used by external programs, so nothing can be 17 | ; moved or removed from this table. New entries always 18 | ; go at the end. Many of these are internal functions 19 | ; and I figured they might be handy for others. 20 | ; 21 | RESET ds 3 22 | WARM ds 3 23 | ; 24 | ; These are the major and minor revision numbers so that 25 | ; code can check to see which CTMON65 version is running. 26 | ; 27 | CTMON65ver ds 1 28 | CTMON65rev ds 1 29 | ds 1 ;unused 30 | ; 31 | ; Console related functions 32 | ; 33 | cin ds 3 34 | cout ds 3 35 | cstatus ds 3 36 | putsil ds 3 37 | getline ds 3 38 | crlf ds 3 39 | HexA ds 3 40 | ; 41 | ; Low-level functions to access the SD card system 42 | ; 43 | if SD_ENABLED 44 | xParInit ds 3 45 | xParSetWrite ds 3 46 | xParSetRead ds 3 47 | xParWriteByte ds 3 48 | xParReadByte ds 3 49 | ; 50 | ; Higher level SD card functions 51 | ; 52 | DiskPing ds 3 53 | DiskDir ds 3 54 | DiskDirNext ds 3 55 | DiskOpenRead ds 3 56 | DiskOpenWrite ds 3 57 | DiskRead ds 3 58 | DiskWrite ds 3 59 | DiskClose ds 3 60 | endif ;SD_ENABLED 61 | ; 62 | org RAM_START 63 | ; 64 | ; The use of memory starting from here will remain 65 | ; constant through different versions of CTMON65. 66 | ; 67 | IRQvec ds 2 68 | NMIvec ds 2 69 | ; 70 | ; Before a L(oad) command, these are set to $FF. 71 | ; After loading, if they are different, jump to 72 | ; that address. 73 | ; 74 | AutoRun ds 2 75 | ; 76 | ; Pointer to the subroutine that gets the next input 77 | ; character. Used for doing disk/console input. 78 | ; 79 | inputVector ds 2 80 | ; 81 | ; Same thing for output. 82 | ; 83 | outputVector ds 2 84 | ; 85 | ; Buffer for GETLINE 86 | ; 87 | buffer ds BUFFER_SIZE 88 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .\as65 -l -s2 -m mytb.asm 5 | 6 | -------------------------------------------------------------------------------- /mytb.asm: -------------------------------------------------------------------------------- 1 | ;===================================================== 2 | ; Bob's Tiny BASIC 3 | ; 4 | ; While working on the Corsham Technologies KIM Clone 5 | ; project, I wanted to include a TINY BASIC since that 6 | ; was a highly desirable feature of early computers. 7 | ; 8 | ; Rather than negotiating copyright issues for 9 | ; existing BASICs, I decided to just write one from 10 | ; scratch. 11 | ; 12 | ; 10/07/2017 13 | ; 14 | ; This implements a stripped down Tiny BASIC 15 | ; interpreter using the Interpretive Language (IL) 16 | ; method as described in the first few issues of 17 | ; Dr Dobb's Journal. The IL interpreter can be used 18 | ; to write various languages simply by changing the 19 | ; IL code rather than the interpreter itself. 20 | ; 21 | ; 10/15/2021 v0.4 - Bob Applegate 22 | ; * Fxed major bug in findLine that 23 | ; caused corrupted lines, crashes, etc. 24 | ; * If no parameter given to RND, assume 25 | ; 32766. 26 | ; * No more error 5 when a program 27 | ; reaches the end without an END. 28 | ; 29 | ; www.corshamtech.com 30 | ; bob@corshamtech.com 31 | ; 32 | ;===================================================== 33 | ; 34 | ; Create TRUE and FALSE values for conditionals. 35 | ; 36 | FALSE equ 0 37 | TRUE equ ~FALSE 38 | ; 39 | ;--------------------------------------------------------- 40 | ; One of these must be set to indicate which environment 41 | ; Tiny BASIC will be running in. Here are the current 42 | ; environments: 43 | ; 44 | ; KIM - This is a bare KIM-1. You'll need to add a few 45 | ; more K of RAM. 46 | ; 47 | ; XKIM - The Corsham Technologies xKIM extended monitor, 48 | ; which enhances, without replacing, the standard KIM 49 | ; monitor. It gives access to routines to save/load files 50 | ; to a micro SD card. 51 | ; 52 | ; CTMON65 is a from-scratch monitor written for the 53 | ; Corsham Tech SS-50 6502 CPU board, but the monitor can 54 | ; easily be ported to other systems. It has support for 55 | ; using a micro SD card for file storage/retrieval. 56 | ; 57 | KIM equ FALSE ;Basic KIM-1, no extensions 58 | XKIM equ TRUE ;Corsham Tech xKIM monitor 59 | CTMON65 equ FALSE ;Corsham Tech CTMON65 60 | ; 61 | ; If set, include disk functions. 62 | ; 63 | DISK_ACCESS equ TRUE 64 | ; 65 | ; If ILTRACE is set then dump out the address of every 66 | ; IL opcode before executing it. 67 | ; 68 | ILTRACE equ FALSE 69 | ; 70 | ; If FIXED is set, put the IL code and the user 71 | ; program space at fixed locations in memory. This is 72 | ; meant only for debugging. 73 | ; 74 | FIXED equ FALSE 75 | ; 76 | ; Sets the arithmetic stack depth. This is *TINY* 77 | ; BASIC, so keep this small! 78 | ; 79 | STACKSIZE equ 8 ;number of entries 80 | ; 81 | ; Common ASCII constants 82 | ; 83 | BEL equ $07 84 | BS equ $08 85 | TAB equ $09 86 | LF equ $0A 87 | CR equ $0D 88 | QUOTE equ $22 89 | SPACE equ ' ' 90 | COMMA equ ',' 91 | SEMICOLON equ ';' 92 | ; 93 | ; These are error codes 94 | ; 95 | ERR_NONE equ 0 96 | ERR_EXPR equ 1 ;expression error 97 | ERR_UNDER equ 2 ;stack underflow 98 | ERR_OVER equ 3 ;stack overflow 99 | ERR_EXTRA_STUFF equ 4 ;Stuff at end of line 100 | ERR_SYNTAX equ 5 ;various syntax errors 101 | ERR_DIVIDE_ZERO equ 6 ;divide by zero 102 | ERR_READ_FAIL equ 7 ;error loading file 103 | ERR_WRITE_FAIL equ 8 ;error saving file 104 | ERR_NO_FILENAME equ 9 105 | ; 106 | ;===================================================== 107 | ; Zero page storage. 108 | ; 109 | bss 110 | org $0040 111 | ILTrace ds 1 ;non-zero means tracing 112 | variables ds 26*2 ;2 bytes, A-Z 113 | variablesEnd equ * 114 | ILPC ds 2 ;IL program counter 115 | dpl ds 2 116 | tempIL ds 2 117 | tempIlY ds 1 118 | offset ds 1 119 | lineLength ds 1 120 | ; 121 | ; CURPTR is a pointer to curent BASIC line being 122 | ; executed. Always points to start of line, CUROFF 123 | ; is the offset to the current character. 124 | ; 125 | CURPTR ds 2 126 | CUROFF ds 1 127 | ; 128 | ; R0 and R1 are used for arithmetic operations and 129 | ; general use. 130 | ; 131 | R0 ds 2 ;arithmetic register 0 132 | R1 ds 2 ;arithmetic register 1 133 | ; 134 | ; This is zero if in immediate mode, or non-zero 135 | ; if currently running a program. Any input from 136 | ; the main loop clears this, and the XFER IL 137 | ; statement will set it. 138 | ; 139 | RunMode ds 1 140 | ; 141 | ; Used for line insertion/removal. 142 | ; 143 | FROM ds 2 144 | ; 145 | ;===================================================== 146 | ; 147 | code 148 | org $0200 149 | ; 150 | ; Cold start is at $0200. Warm start is at $0203. 151 | ; 152 | TBasicCold jmp cold2 ;jump around vectors 153 | warm jmp warm2 154 | ; 155 | ; These are the user-supplied vectors to I/O routines. 156 | ; If you want, you can just patch these in the binary 157 | ; file, but it would be better to change the source 158 | ; code. 159 | ; 160 | if KIM 161 | OUTCH jmp $1ea0 ;output char in A 162 | GETCH jmp $1e5a ;get char in A (blocks) 163 | CRLF jmp $1e2f ;print CR/LF 164 | OUTHEX jmp $1e3b ;print A as hex 165 | MONITOR jmp $1c4f ;return to monitor 166 | endif 167 | if XKIM 168 | include "xkim.inc" 169 | code 170 | OUTCH jmp $1ea0 171 | GETCH jmp xkGETCH 172 | CRLF jmp $1e2f ;print CR/LF 173 | OUTHEX jmp xkPRTBYT 174 | MONITOR jmp extKIM 175 | puts equ putsil 176 | BUFFER_SIZE equ 132 177 | endif 178 | if CTMON65 179 | include "ctmon65.inc" 180 | code 181 | OUTCH jmp cout 182 | GETCH jmp cin 183 | CRLF jmp crlf 184 | OUTHEX jmp HexA 185 | MONITOR jmp WARM 186 | puts equ putsil 187 | endif 188 | ; 189 | cold2 jsr puts 190 | db CR,LF 191 | db "Bob's Tiny BASIC v0.3" 192 | db CR,LF 193 | db "https://github.com/CorshamTech/6502-Tiny-BASIC" 194 | db CR,LF,0 195 | ; 196 | lda #IL&$ff 197 | sta ILPC 198 | lda #IL>>8 199 | sta ILPC+1 200 | ; 201 | lda #ProgramStart&$ff ;user prog 202 | sta PROGRAMEND 203 | lda #ProgramStart>>8 204 | sta PROGRAMEND+1 205 | ; 206 | ; Initialize the pseudo-random number sequence... 207 | ; 208 | lda #$5a 209 | sta rtemp1 210 | lda #%10011101 211 | sta random 212 | lda #%01011011 213 | sta random+1 214 | ; 215 | jmp coldtwo 216 | ; 217 | ; This is the warm start entry point 218 | ; 219 | warm2 jsr CRLF 220 | lda errGoto 221 | sta ILPC 222 | lda errGoto+1 223 | sta ILPC+1 224 | ; 225 | ; And continue with both starts here 226 | ; 227 | coldtwo jsr SetOutConsole 228 | ; 229 | ; The ILTrace flag is now run-time settable. 230 | ; 231 | lda #ILTRACE&$ff 232 | sta ILTrace 233 | ; 234 | lda #0 235 | sta RunMode 236 | sta LINBUF 237 | lda #LINBUF&$ff 238 | sta CURPTR 239 | lda #LINBUF>>8 240 | sta CURPTR+1 ;fall through... 241 | ; 242 | ;===================================================== 243 | ; This is the top of the IL interpreter. This fetches 244 | ; and executes the instruction currently pointed to 245 | ; by ILPC and adjusts ILPC to point to the next 246 | ; instruction to execute. 247 | ; 248 | NextIL lda ILTrace 249 | beq NextIL2 250 | jsr dbgLine 251 | NextIL2 ldy CUROFF 252 | jsr SkipSpaces 253 | sty CUROFF 254 | ; 255 | jsr getILByte 256 | ; 257 | ; When the handler is called, these are the conditions 258 | ; of several important items: 259 | ; 260 | ; (ILPC) will point to the byte AFTER the IL 261 | ; opcode being executed. 262 | ; 263 | ; (CURPTR),CUROFF will point to the start of the 264 | ; next word in the input buffer. Ie, the next word 265 | ; in the user program. 266 | ; 267 | asl a 268 | cmp #ILTBLend-ILTBL+2 269 | bcc ILgood 270 | ; 271 | ; This handles an illegal IL opcode. This is serious 272 | ; and there's no way to recover. 273 | ; 274 | ILbad jsr puts 275 | db CR,LF 276 | db "Illegal IL " 277 | db 0 278 | ; 279 | ; Well this is awkward, we need to back up the IL 280 | ; by one since it no longer points to the current 281 | ; opcode. 282 | ; 283 | jsr decIL 284 | ; 285 | ldy #0 286 | lda (ILPC),y 287 | jsr OUTHEX 288 | jsr puts 289 | db " at ",0 290 | lda ILPC+1 291 | jsr OUTHEX 292 | lda ILPC 293 | jsr OUTHEX 294 | jsr CRLF 295 | jmp MONITOR 296 | ; 297 | ; Just jump to the address (ILPC),y. Have to do 298 | ; some goofy stuff. 299 | ; 300 | ILgood tay ;move index into Y 301 | lda ILTBL,y 302 | sta dpl 303 | lda ILTBL+1,y 304 | sta dpl+1 305 | jmp (dpl) ;go to handler 306 | ; 307 | ;===================================================== 308 | ; This is the IL jump table. The IL opcode is 309 | ; mulitplied by two, then looked-up in this table. 310 | ; There is absolutely nothing special about the order 311 | ; of entries here... they all decode at exactly the 312 | ; same speed. However the entry number must match the 313 | ; values in IL.inc. 314 | ; 315 | ILTBL dw iXINIT ;0 316 | dw iDONE ;1 317 | dw iPRS ;2 318 | dw iPRN ;3 319 | dw iSPC ;4 320 | dw iNLINE ;5 321 | dw iNXT ;6 322 | dw iXFER ;7 323 | dw iSAV ;8 324 | dw iRSTR ;9 325 | dw iCMPR ;10 326 | dw iINNUM ;11 327 | dw iFIN ;12 328 | dw iERR ;13 329 | dw iADD ;14 330 | dw iSUB ;15 331 | dw iNEG ;16 332 | dw iMUL ;17 333 | dw iDIV ;18 334 | dw iSTORE ;19 335 | dw iIND ;20 336 | dw iLST ;21 337 | dw iINIT ;22 338 | dw iGETLINE 339 | dw iINSRT ;24 340 | dw iRTN ;25 341 | dw MONITOR ;26 342 | dw iLIT ;27 343 | dw iCALL ;28 344 | dw iJMP ;29 345 | dw iVINIT ;30 346 | dw iERRGOTO 347 | dw iTST ;32 348 | dw iTSTV ;33 349 | dw iTSTL ;34 350 | dw iTSTN ;35 351 | dw iFREE ;36 352 | dw iRANDOM ;37 353 | dw iABS ;38 354 | ; 355 | ; Disk functions. There must be pointers 356 | ; to functions even if no disk is supported. 357 | ; Makes things easier in IL.inc. 358 | ; 359 | if DISK_ACCESS 360 | dw iOPENREAD 361 | dw iOPENWRITE 362 | dw iDCLOSE ;41 363 | dw iDGETLINE ;Life, universe, everything 364 | dw iDLIST ;43 365 | else 366 | dw NextIL ;39 367 | dw NextIL ;40 368 | dw NextIL ;41 369 | dw NextIL ;42 370 | dw NextIL ;43 371 | endif 372 | ; 373 | ILTBLend equ * 374 | ; 375 | ;===================================================== 376 | ;===================================================== 377 | ;===================================================== 378 | ; This marks the start of the handlers for IL opcodes. 379 | ;===================================================== 380 | ;===================================================== 381 | ;===================================================== 382 | ; 383 | ; 384 | iINIT lda #0 ;clear IL stack pointer 385 | sta retStackPtr 386 | ; 387 | lda #ProgramStart&$ff ;user prog 388 | sta CURPTR 389 | sta PROGRAMEND 390 | lda #ProgramStart>>8 391 | sta CURPTR+1 392 | sta PROGRAMEND+1 393 | ; 394 | ; fall into XINIT... 395 | ; 396 | ;===================================================== 397 | ; This initializes for the start of the next line of 398 | ; BASIC text. 399 | ; 400 | iXINIT lda #0 401 | sta mathStackPtr ;clear math stack 402 | goodExit jmp NextIL 403 | ; 404 | ;===================================================== 405 | ; Verify there is nothing else on this input line. 406 | ; If there is, generate an error. 407 | ; 408 | iDONE ldy CUROFF 409 | jsr SkipSpaces 410 | lda (CURPTR),y 411 | beq doneadv 412 | ldx #ERR_EXTRA_STUFF 413 | lda #0 414 | jmp iErr2 415 | ; 416 | ; Advance to the next line 417 | ; 418 | doneadv 419 | ; jsr FindNext2 420 | jmp NextIL 421 | ; 422 | ;===================================================== 423 | ; Print the string until a closing quote 424 | ; 425 | iPRS ldy CUROFF 426 | ; 427 | ; Odd logic here. The main loop skipped any leading 428 | ; whitespace inside the quoted text, so move back to 429 | ; the quote, then move forward again. 430 | ; 431 | lda #'"' ;pre-load with char to find 432 | iPRS3 dey ;move back one 433 | cmp (CURPTR),y ;quote? 434 | bne iPRS3 435 | iny 436 | sty CUROFF 437 | ; 438 | iPRS2 lda (CURPTR),y 439 | beq PRSend2 ;end of line! 440 | cmp #'"' 441 | beq PRSend 442 | jsr OUTCH 443 | inc CUROFF 444 | ldy CUROFF 445 | bne iPRS2 446 | PRSend iny ;skip closing quote 447 | sty CUROFF 448 | PRSend2 jmp NextIL 449 | ; 450 | ;===================================================== 451 | ; Pop the top off the stack and print it as a signed 452 | ; decimal number. 453 | ; 454 | iPRN jsr popR0 455 | jsr PrintDecimal 456 | jmp NextIL 457 | ; 458 | ;===================================================== 459 | ; Space to next zone. Currently the code does not 460 | ; keep track of which column the output is on, so 461 | ; just print a tab. 462 | ; 463 | iSPC lda #TAB 464 | jsr OUTCH 465 | jmp NextIL 466 | ; 467 | ;===================================================== 468 | ; If in immediate mode, jump to the address following 469 | ; the NXT instruction. Else move to the next line of 470 | ; user code and continue. 471 | ; 472 | iNXT lda RunMode 473 | bne iNxtRun ;in run mode 474 | ; 475 | ; Get address and jump to it. 476 | ; 477 | jmp iJMP 478 | ; 479 | iNxtRun jsr FindNextLine 480 | jsr AtEnd 481 | bne iNxtRun2 ;not at end 482 | ; 483 | ; At the end of the program. Pretend an END statement 484 | ; was found. 485 | ; 486 | iFINv jmp iFIN 487 | ; 488 | iNxtRun2 jsr getILWord ;ignore next word 489 | jmp NextIL 490 | ; 491 | ;===================================================== 492 | ; XFER takes the number on top of the stack and looks 493 | ; for that line in the program, or the next line 494 | ; higher. Ie, if it's 1 but there is no line 1, then 495 | ; find the next one after that. 496 | ; 497 | iXFER jsr popR0 498 | jsr findLine 499 | iXFER2 jsr AtEnd ;at end of user program? 500 | beq iFINv 501 | ldy #2 ;point to start of text 502 | sty CUROFF 503 | lda #$ff 504 | sta RunMode 505 | ; 506 | ; Transfer IL to STMT. I don't like having this 507 | ; hard-coded; fix it. 508 | ; 509 | lda #STMT&$ff 510 | sta ILPC 511 | lda #STMT>>8 512 | sta ILPC+1 513 | jmp NextIL 514 | ; 515 | ; Run 516 | ; 517 | iXferok 518 | lda #$ff 519 | sta RunMode ;we're running 520 | ; 521 | ; Need a more elegant way to do this 522 | ; 523 | lda #STMT&$ff 524 | sta ILPC 525 | lda #STMT>>8 526 | sta ILPC+1 527 | jmp NextIL 528 | ; 529 | ;===================================================== 530 | ; Save the pointer to the next line to the call stack. 531 | ; 532 | iSAV 533 | jmp ILbad 534 | ; 535 | ;===================================================== 536 | ; Pop the next line from the call stack. 537 | ; 538 | iRSTR 539 | jmp ILbad 540 | ; 541 | ;===================================================== 542 | ; Compare items on stack. Okay, so on input there are 543 | ; three things on the stack 544 | ; 545 | ; EXPR2 <- Top of stack 546 | ; OP <- relational operator, next on stack 547 | ; EXPR1 <- last item on stack 548 | ; 549 | ; Comparison is: EXPR1 EXPR2 550 | ; 551 | ; Operator is one of... 552 | ; 553 | ; 2 is = 554 | ; 1 is < 555 | ; 3 is <= 556 | ; 5 is <> 557 | ; 4 is > 558 | ; 6 is >= 559 | ; 560 | ; Those are bit-mapped: 561 | ; 562 | ; xxxxxGEL 563 | ; 564 | ; G = Greater than 565 | ; E = Equal 566 | ; L = Less than 567 | ; 568 | ; If the comparison is false, do a NXT, ie, move to the 569 | ; next line and continue. If true, continue executing 570 | ; on this line. 571 | ; 572 | REL_LT equ %001 573 | REL_EQUAL equ %010 574 | REL_GT equ %100 575 | ; 576 | iCMPR jsr popR1 577 | jsr popMQ ;operator in MQ 578 | jsr popR0 579 | ; 580 | ; See if they are equal or not 581 | ; 582 | lda R0 583 | cmp R1 584 | bne iCMPRnoteq ;try not equal 585 | lda R0+1 586 | cmp R1+1 587 | bne iCMPRnoteq 588 | ; 589 | ; Equal, set the flag in MQ+1 590 | ; 591 | lda #REL_EQUAL 592 | bne iCMPcom 593 | ; 594 | ; See if EXPR1 (R0) < EXPR2 (R1) 595 | ; See www.6502.org/tutorials/compare_beyond.html 596 | ; 597 | iCMPRnoteq lda R0 598 | cmp R1 599 | lda R0+1 600 | sbc R1+1 601 | bvc iCMPR_2 602 | eor #$80 603 | iCMPR_2 bmi iCMPlt 604 | lda #REL_GT 605 | bne iCMPcom 606 | iCMPlt lda #REL_LT ;R0 < R1 607 | iCMPcom ora MQ+1 608 | ; 609 | ; Now compare the end result with what the caller 610 | ; was looking for. 611 | ; 612 | and MQ 613 | beq iCMPno ;no match 614 | jmp NextIL 615 | ; 616 | ; R0 > R1 617 | ; 618 | iCMPgt lda #REL_GT 619 | bne iCMPcom 620 | ; 621 | ; Not a match, so jump to the next line of code. 622 | ; 623 | iCMPno jsr FindNextLine 624 | jmp iXFER2 625 | ; 626 | ;===================================================== 627 | ; Get a line of text from the user, convert to a 628 | ; number, leave on top of stack. 629 | ; 630 | iINNUM lda CUROFF ;save state before GetLine 631 | pha 632 | lda CURPTR+1 633 | pha 634 | lda CURPTR 635 | pha 636 | ; 637 | lda #'?' 638 | jsr GetLine 639 | jsr getDecimal 640 | jsr pushR0 ;put onto stack 641 | ; 642 | pla 643 | sta CURPTR 644 | pla 645 | sta CURPTR+1 646 | pla 647 | sta CUROFF 648 | ; 649 | jmp NextIL 650 | ; 651 | ;===================================================== 652 | ; Stop the currently running program. Actually very 653 | ; simple to do... clear the RunMode flag, then set the 654 | ; ILPC to the standard handler and continue running. 655 | ; 656 | iFIN lda #0 657 | sta RunMode 658 | ; 659 | lda errGoto 660 | sta ILPC 661 | lda errGoto+1 662 | sta ILPC+1 663 | jmp NextIL 664 | ; 665 | ;===================================================== 666 | ; Handle the ERR opcode. Following the instruction is 667 | ; a 16 bit error number. Print an error message, and 668 | ; if we're in run mode, print the line number. Stop 669 | ; program execution and return to the initial state. 670 | ; 671 | iERR jsr getILWord ;get err code 672 | ; 673 | ; Enter here with the error code in X (LSB) and A (MSB). 674 | ; 675 | iErr2 stx R0 676 | sta R0+1 677 | ; 678 | jsr puts 679 | db "Error ",0 680 | jsr PrintDecimal 681 | ; 682 | lda RunMode ;running? 683 | beq iERR2 ;nope 684 | jsr puts 685 | db " at line ",0 686 | ldy #0 687 | lda (CURPTR),y 688 | sta R0 689 | iny 690 | lda (CURPTR),y 691 | sta R0+1 692 | jsr PrintDecimal 693 | ; 694 | iERR2 jsr CRLF 695 | lda #0 696 | sta RunMode ;fall through... 697 | ; 698 | ;===================================================== 699 | ; Reset the IL to be back at the idle loop. Does not 700 | ; clear variables so the user can see what state 701 | ; the program is in. 702 | ; 703 | ResetIL lda #0 704 | sta retStackPtr 705 | lda errGoto 706 | sta ILPC 707 | lda errGoto+1 708 | sta ILPC+1 709 | jmp NextIL 710 | ; 711 | ;===================================================== 712 | ; Pop two items off stack, add them, then place the 713 | ; result back onto the stack. 714 | ; 715 | iADD jsr popR0 716 | jsr popR1 717 | clc 718 | lda R0 719 | adc R1 720 | sta R0 721 | lda R0+1 722 | adc R1+1 723 | sta R0+1 724 | jmp pushR0nextIl 725 | ; 726 | ;===================================================== 727 | ; Pop two items off the stack. Subtract the top of 728 | ; stack from the lower entry. 729 | ; 730 | iSUB jsr popR1 731 | jsr popR0 732 | sec 733 | lda R0 734 | sbc R1 735 | sta R0 736 | lda R0+1 737 | sbc R1+1 738 | sta R0+1 739 | jmp pushR0nextIl 740 | ; 741 | ;===================================================== 742 | ; Negate the top of stack. 743 | ; 744 | iNEG jsr popR0 745 | lda R0 746 | eor #$ff 747 | sta R0 748 | lda R0+1 749 | eor #$ff 750 | sta R0+1 751 | inc R0 752 | bne iNEG2 753 | inc R0+1 754 | iNEG2 jmp pushR0nextIl 755 | ; 756 | ;===================================================== 757 | ; Multiply top two items on the stack, put the results 758 | ; on top. This uses the algorithm documented on page 759 | ; 115 of "Microprocessor Programming for Computer 760 | ; Hobbyists" by Neill Graham. 761 | ; 762 | iMUL jsr popR0 ;AC 763 | jsr popR1 ;OP 764 | ; 765 | lda R0 766 | sta MQ 767 | lda R0+1 768 | sta MQ+1 769 | lda #0 ;clear result 770 | sta R0 771 | sta R0+1 772 | ; 773 | ldx #16 ;number of bits in value 774 | multloop asl R0 775 | rol R0+1 776 | asl MQ 777 | rol MQ+1 778 | bcc multno ;skip add if no carry 779 | ; 780 | ; Add R1 back into R0 781 | ; 782 | clc 783 | lda R0 784 | adc R1 785 | sta R0 786 | lda R0+1 787 | adc R1+1 788 | sta R0+1 789 | ; 790 | multno dex ;did all bits yet? 791 | bne multloop 792 | ; 793 | pushR0nextIl jsr pushR0 ;OP 794 | jmp NextIL 795 | ; 796 | ;===================================================== 797 | ; Divide the top of stack into the next to top item. 798 | ; Leave results on stack. Taken from: 799 | ; http://codebase64.org/doku.php?id=base:16bit_division_16-bit_result 800 | ; 801 | ; MQ = R0 / R1 802 | ; Remainder is in R0 803 | ; 804 | iDIV jsr popR1 805 | jsr popR0 806 | ; 807 | ; Check for divide by zero 808 | ; 809 | lda R1 810 | ora R1+1 811 | beq divby0 812 | ; 813 | jsr SaveSigns 814 | lda #0 ;preset remainder to 0 815 | sta MQ 816 | sta MQ+1 817 | ldx #16 ;repeat for each bit: ... 818 | 819 | divloop asl R0 ;dividend lb & hb*2, msb -> Carry 820 | rol R0+1 821 | rol MQ ;remainder lb & hb * 2 + msb from carry 822 | rol MQ+1 823 | lda MQ 824 | sec 825 | sbc R1 ;substract divisor to see if it fits in 826 | tay ;lb result -> Y, for we may need it later 827 | lda MQ+1 828 | sbc R1+1 829 | bcc skip ;if carry=0 then divisor didn't fit in yet 830 | 831 | sta MQ+1 ;else save substraction result as new remainder, 832 | sty MQ 833 | inc R0 ;and INCrement result cause divisor fit in 1 times 834 | 835 | skip dex 836 | bne divloop 837 | jsr RestoreSigns 838 | jmp pushR0nextIl 839 | ; 840 | ; Indicate divide-by-zero error 841 | ; 842 | divby0 ldx #ERR_DIVIDE_ZERO 843 | lda #0 844 | jmp iErr2 845 | ; 846 | ;===================================================== 847 | ; This pops the top two items off the stack. The top 848 | ; item is a data value and the other is an index into 849 | ; the variable table. Save the value into that entry. 850 | ; 851 | iSTORE jsr popR0 ;data 852 | jsr popR1 ;index 853 | ldx R1 ;get index 854 | lda R0 855 | sta variables,x 856 | lda R0+1 857 | sta variables+1,x 858 | jmp NextIL 859 | ; 860 | ;===================================================== 861 | ; Replaces the top of stack with the variable whose 862 | ; index it represents. 863 | ; 864 | iIND jsr popR1 865 | ldx R1 ;get index 866 | lda variables,x 867 | sta R0 868 | lda variables+1,x 869 | sta R0+1 870 | jmp pushR0nextIl 871 | ; 872 | ;===================================================== 873 | ; List the current BASIC program in memory. Uses R0, 874 | ; tempIly, and dpl. 875 | ; 876 | iLST jsr SetOutConsole 877 | iLST2 lda #ProgramStart&$ff 878 | sta dpl 879 | lda #ProgramStart>>8 880 | sta dpl+1 881 | ; 882 | ; dpl/dph point to the current line. See if we're at 883 | ; the end of the program. 884 | ; 885 | iLSTloop lda dpl 886 | cmp PROGRAMEND 887 | bne iLstNotEnd 888 | lda dpl+1 889 | cmp PROGRAMEND+1 890 | beq iLstdone 891 | ; 892 | iLstNotEnd ldy #0 893 | lda (dpl),y ;line number LSB 894 | sta R0 895 | iny 896 | lda (dpl),y ;line number MSB 897 | sta R0+1 898 | iny 899 | sty tempIlY 900 | jsr PrintDecimal 901 | lda #SPACE 902 | jsr VOUTCH 903 | ldy tempIlY 904 | iLSTl2 lda (dpl),y 905 | beq iLST3 ;end of this line 906 | sty tempIlY 907 | jsr VOUTCH 908 | ldy tempIlY 909 | iny 910 | bne iLSTl2 ;do next char 911 | ; 912 | ; End of this line. Print CR/LF, then move to the 913 | ; next line. 914 | ; 915 | iLST3 iny 916 | clc 917 | tya 918 | adc dpl 919 | sta dpl 920 | lda dpl+1 921 | adc #0 922 | sta dpl+1 923 | ; 924 | ; Have to manually do CR/LF so it uses the vectored 925 | ; output function. 926 | ; 927 | lda #CR 928 | jsr VOUTCH 929 | lda #LF 930 | jsr VOUTCH 931 | jmp iLSTloop ;do next line 932 | ; 933 | iLstdone jsr SetOutConsole 934 | jmp NextIL 935 | ; 936 | ;===================================================== 937 | ; Get a line of text into LINBUF. Terminate with a 938 | ; null byte. 939 | ; 940 | iGETLINE lda #'>' ;prompt character 941 | jsr GetLine 942 | ; 943 | lda #0 944 | sta RunMode 945 | jmp NextIL 946 | ; 947 | ;===================================================== 948 | ; This is called when the input buffer contains a line 949 | ; typed in by the user that starts with a line number. 950 | ; Insert the line into the program or delete the line 951 | ; if there is nothing after the line number, 952 | ; 953 | iINSRT ldy #0 954 | jsr getDecimal ;convert line # 955 | jsr SkipSpaces 956 | sty offset ;save for now 957 | ; 958 | ; Now find the line OR the next higher line OR the 959 | ; end of the program. 960 | ; 961 | jsr findLine 962 | ; 963 | ; If the line exists, it needs to be removed. 964 | ; 965 | bne insert2 ;jump if not found 966 | ; 967 | ; Get length of line to be removed 968 | ; 969 | jsr getCURPTRLength ;results in Y 970 | sty lineLength 971 | ; 972 | ; Compute the new end of the program first. 973 | ; 974 | sec 975 | lda PROGRAMEND 976 | sbc lineLength 977 | sta PROGRAMEND 978 | lda PROGRAMEND+1 979 | sbc #0 980 | sta PROGRAMEND+1 981 | ; 982 | ; Copy CURPTR into R1 for working 983 | ; 984 | lda CURPTR 985 | sta R1 986 | lda CURPTR+1 987 | sta R1+1 988 | ; 989 | ; See if we're at the end. 990 | ; 991 | InsDelChk lda R1 992 | cmp PROGRAMEND 993 | bne InsDelLoop 994 | lda R1+1 995 | cmp PROGRAMEND+1 996 | beq insert2 997 | ; 998 | ; Move one byte, move to next location. 999 | ; 1000 | InsDelLoop ldy lineLength 1001 | lda (R1),y 1002 | ldy #0 1003 | sta (R1),y 1004 | inc R1 1005 | bne InsDelChk 1006 | inc R1+1 1007 | jmp InsDelChk 1008 | ; 1009 | ; Deletion is done. 1010 | ; If the new line is empty we're done. 1011 | ; 1012 | insert2 ldy offset ;get back ptr 1013 | lda LINBUF,y ;next byte 1014 | beq mvUpFini ;empty line 1015 | ; 1016 | ; CURPTR points to where the line will be inserted. 1017 | ; 1018 | jsr getLineLength ;get bytes needed 1019 | ; 1020 | lda PROGRAMEND 1021 | sta FROM 1022 | lda PROGRAMEND+1 1023 | sta FROM+1 1024 | ; 1025 | mvup1 ldy #0 1026 | lda (FROM),y 1027 | ldy lineLength 1028 | sta (FROM),y 1029 | ; 1030 | lda FROM 1031 | cmp CURPTR 1032 | bne mvUpMore 1033 | lda FROM+1 1034 | cmp CURPTR+1 1035 | beq mvUpDone 1036 | ; 1037 | ; Not done yet 1038 | ; 1039 | mvUpMore lda FROM ;decrement FROM 1040 | bne mvUpMore2 1041 | dec FROM+1 1042 | mvUpMore2 dec FROM 1043 | jmp mvup1 1044 | ; 1045 | ; All done with copy. 1046 | ; 1047 | mvUpDone clc 1048 | lda lineLength 1049 | adc PROGRAMEND 1050 | sta PROGRAMEND 1051 | lda PROGRAMEND+1 1052 | adc #0 1053 | sta PROGRAMEND+1 1054 | ; 1055 | ldy #0 ;copy line number first 1056 | lda R0 1057 | sta (CURPTR),y 1058 | iny 1059 | lda R0+1 1060 | sta (CURPTR),y 1061 | iny 1062 | ; 1063 | ldx offset 1064 | mvUpLoop2 lda LINBUF,x 1065 | sta (CURPTR),y 1066 | beq mvUpFini 1067 | inx 1068 | iny 1069 | bne mvUpLoop2 1070 | ; 1071 | mvUpFini jmp NextIL 1072 | ; 1073 | ;===================================================== 1074 | ; Pops the top value of the ILPC stack and stores it 1075 | ; in ILPC. Ie, return from an IL subroutine. 1076 | ; 1077 | iRTN jsr popILPC 1078 | jmp NextIL 1079 | ; 1080 | ;===================================================== 1081 | ; NLINE 1082 | ; 1083 | iNLINE jsr CRLF ;user supplied sub 1084 | jmp NextIL 1085 | ; 1086 | ;===================================================== 1087 | ; This saves the current ILPC value on the stack, then 1088 | ; jumps to the address specified by the next two bytes. 1089 | ; 1090 | iCALL jsr pushILPC ;save ILPC 1091 | ; 1092 | ; Jmp to a specific location in the IL code. The new 1093 | ; address immediately follows the opcode. 1094 | ; 1095 | iJMP jsr getILWord 1096 | stx ILPC 1097 | sta ILPC+1 1098 | jmp NextIL 1099 | ; 1100 | ;===================================================== 1101 | ; Push the next two bytes onto the arithmetic stack. 1102 | ; 1103 | iLIT jsr getILWord 1104 | stx R0 1105 | sta R0+1 1106 | jsr pushR0 1107 | jmp NextIL 1108 | ; 1109 | ;===================================================== 1110 | ; Initialize all variables. Ie, set to zero. 1111 | ; 1112 | iVINIT lda #0 1113 | ldx #0 1114 | Vinit2 sta variables,x 1115 | inx 1116 | cpx #variablesEnd-variables 1117 | bne Vinit2 1118 | jmp NextIL 1119 | ; 1120 | ;===================================================== 1121 | ; Set the address of the error handler. After any 1122 | ; error, set to the ILPC to the specified location. 1123 | ; 1124 | iERRGOTO jsr getILWord 1125 | stx errGoto 1126 | sta errGoto+1 1127 | jmp NextIL 1128 | ; 1129 | ;===================================================== 1130 | ; TST is followed by an 8 bit signed offset, then a 1131 | ; null terminated string. Compare the string against 1132 | ; the string starting at (CURPTR),CUROFF. If the 1133 | ; strings match, continue executing the next IL 1134 | ; opcode. Else, add the offset to ILPC. 1135 | ; 1136 | iTST jsr getILByte 1137 | sta offset 1138 | ; 1139 | jsr saveIL ;in case of failure 1140 | ldy CUROFF 1141 | sty dpl ;save for later 1142 | ; 1143 | iTSTloop jsr getILByte ;get next char 1144 | beq iTSTm ;match! 1145 | ldy dpl 1146 | cmp (CURPTR),y 1147 | bne iTSTfail ;mismatch 1148 | iny 1149 | sty dpl 1150 | bne iTSTloop 1151 | ; 1152 | ; It's a match! Clean up a bit. 1153 | ; 1154 | iTSTm ldy dpl 1155 | sty CUROFF 1156 | jmp NextIL 1157 | ; 1158 | ; Not a match, reset ILPC and then move to the 1159 | ; offset. 1160 | ; 1161 | iTSTfail jsr restoreIL 1162 | jmp tstBranch 1163 | ; 1164 | ;===================================================== 1165 | ; TSTV is followed by an 8 bit signed offset. If the 1166 | ; value at (CURPTR),CUROFF appears to be a variable 1167 | ; name, move to the next IL statement. Else, add the 1168 | ; offset to ILPC. 1169 | ; 1170 | iTSTV jsr getILByte ;offset 1171 | sta offset 1172 | ; 1173 | ldy CUROFF 1174 | jsr SkipSpaces 1175 | lda (CURPTR),y 1176 | ; 1177 | cmp #'A' 1178 | bcc tstBranch 1179 | cmp #'Z'+1 1180 | bcs tstBranch 1181 | ; 1182 | ; The condition is true, so convert to an index, push 1183 | ; it onto the stack and continue running. 1184 | ; 1185 | sec 1186 | sbc #'A' ;index is zero based 1187 | asl a ;multiply by two 1188 | sta R0 1189 | lda #0 1190 | sta R0+1 1191 | jsr pushR0 ;put index onto stack 1192 | inc CUROFF ;move to next input char 1193 | jmp NextIL 1194 | ; 1195 | ;===================================================== 1196 | ; TSTL seems basically the same as TSTN, but leave the 1197 | ; value in R0 instead of pushing onto stack. 1198 | ; 1199 | iTSTL jsr getILByte 1200 | sta offset 1201 | ; 1202 | ldy CUROFF 1203 | jsr SkipSpaces 1204 | lda (CURPTR),y 1205 | ; 1206 | cmp #'0' 1207 | bcc tstBranch 1208 | cmp #'9'+1 1209 | bcs tstBranch 1210 | ; 1211 | ; It's a digit, so convert to a number. 1212 | ; 1213 | jsr getDecimal 1214 | jmp NextIL 1215 | ; 1216 | ;===================================================== 1217 | ; TSTN checks for a number. This is very simplistic; 1218 | ; if the character is a digit, assume it's a number. 1219 | ; Convert to a number and push it onto the stack. 1220 | ; 1221 | iTSTN jsr getILByte 1222 | sta offset 1223 | ; 1224 | ldy CUROFF 1225 | jsr SkipSpaces 1226 | lda (CURPTR),y 1227 | ; 1228 | cmp #'-' ;negative? 1229 | beq iTSTN_1 1230 | cmp #'0' 1231 | bcc tstBranch 1232 | cmp #'9'+1 1233 | bcs tstBranch 1234 | ; 1235 | ; It's a digit, so convert to a number. 1236 | ; 1237 | iTSTN_1 jsr getDecimal 1238 | sty CUROFF 1239 | jsr pushR0 ;save onto stack 1240 | jmp NextIL 1241 | ; 1242 | ; Common jump point for all TSTx instructions that 1243 | ; fail to meet the requirements. This takes the 1244 | ; offset and adds/subtracts to/from ILPC. 1245 | ; 1246 | tstBranch lda offset ;get signed offset 1247 | bpl tstPositive 1248 | ; 1249 | ; Do negative branch. Do sign extension. 1250 | ; 1251 | clc 1252 | adc ILPC 1253 | sta ILPC 1254 | lda ILPC+1 1255 | adc #$ff 1256 | sta ILPC+1 1257 | jmp NextIL ;keep going 1258 | ; 1259 | tstPositive clc 1260 | adc ILPC 1261 | sta ILPC 1262 | lda ILPC+1 1263 | adc #0 1264 | sta ILPC+1 1265 | jmp NextIL 1266 | ; 1267 | ;===================================================== 1268 | ; This places the number of free bytes on top of the 1269 | ; stack. 1270 | ; 1271 | iFREE jsr GetSizes 1272 | jsr pushR0 1273 | jmp NextIL 1274 | ; 1275 | ;===================================================== 1276 | ; Generate a random number from 0-FFFF and then MOD 1277 | ; it with the value on top of stack. Leaves number on 1278 | ; stack 1279 | ; 1280 | iRANDOM jsr popR1 ;mod value 1281 | ; 1282 | ; If the value is zero, just return a one. 1283 | ; 1284 | lda R0 1285 | ora R0+1 1286 | beq irandom1 1287 | ; 1288 | lda random+1 1289 | sta rtemp1 1290 | lda random 1291 | asl a 1292 | rol rtemp1 1293 | asl a 1294 | rol rtemp1 1295 | clc 1296 | adc random 1297 | pha 1298 | lda rtemp1 1299 | adc random+1 1300 | sta random+1 1301 | pla 1302 | adc #$11 1303 | sta random 1304 | lda random+1 1305 | adc #$36 1306 | sta random+1 1307 | 1308 | lda random 1309 | sta R0 1310 | lda random+1 1311 | and #$7f ;make positive 1312 | sta R0+1 1313 | ; 1314 | ; R0 contains the number and R1 contains the max value. 1315 | ; 1316 | iRANDOM_2 lda R0 1317 | cmp R1 1318 | bne iRANDOM_1 1319 | lda R0+1 1320 | cmp R1+1 1321 | bne iRANDOM_1 ;need to subtract 1322 | ; 1323 | ; Subtract R1 from R0 1324 | ; 1325 | iRANDOM_sub sec 1326 | lda R0 1327 | sbc R1 1328 | sta R0 1329 | lda R0+1 1330 | sbc R1+1 1331 | sta R0+1 1332 | jmp iRANDOM_2 1333 | ; 1334 | ; See if R1 > R0. If so, branch to subtract. 1335 | ; 1336 | iRANDOM_1 lda R0 1337 | cmp R1 1338 | lda R0+1 1339 | sbc R1+1 1340 | bvc iRANDOM_4 1341 | eor #$80 1342 | iRANDOM_4 bpl iRANDOM_sub 1343 | ; 1344 | ; All done. Almost. Add one, then push the result. 1345 | ; 1346 | irandom1 inc R0 1347 | bne iRANDOM_3 1348 | inc R0+1 1349 | iRANDOM_3 jsr pushR0 ;return value 1350 | jmp NextIL 1351 | ; 1352 | ;===================================================== 1353 | ; Replace TOS with its absolute value. 1354 | ; 1355 | iABS jsr popR0 1356 | lda R0+1 1357 | bpl iABS_1 ;already positive 1358 | eor #$ff 1359 | sta R0+1 1360 | lda R0 1361 | eor #$ff 1362 | sta R0 1363 | inc R0 1364 | bne iABS_1 1365 | inc R0+1 1366 | iABS_1 jsr pushR0 1367 | jmp NextIL 1368 | ; 1369 | include "support.asm" 1370 | if DISK_ACCESS 1371 | include "storage.asm" 1372 | endif 1373 | include "IL.inc" 1374 | ; 1375 | if FIXED 1376 | org $1000 1377 | endif 1378 | include "basic.il" 1379 | PROGEND equ * 1380 | 1381 | ;===================================================== 1382 | ;===================================================== 1383 | ;===================================================== 1384 | ; These are storage items not in page zero. 1385 | ; 1386 | bss 1387 | org PROGEND 1388 | mathStack ds STACKSIZE*2 1389 | mathStackPtr ds 1 1390 | retStack ds STACKSIZE*2 1391 | retStackPtr ds 1 1392 | callStack ds STACKSIZE*2 1393 | callStackPtr ds 1 1394 | LINBUF ds 80 1395 | getlinx ds 1 1396 | printtx ds 1 ;temp X for print funcs 1397 | diddigit ds 1 ;for leading zero suppression 1398 | putsy ds 1 1399 | errGoto ds 2 ;where to set ILPC on err 1400 | MQ ds 2 ;used for some math 1401 | sign ds 1 ;0 = positive, else negative 1402 | rtemp1 ds 1 1403 | random ds 2 1404 | BOutVec ds 2 1405 | if XKIM 1406 | buffer ds BUFFER_SIZE 1407 | endif 1408 | ; 1409 | ; PROGRAMEND is the end of the user's BASIC program. 1410 | ; More precisely, it is one byte past the end. Or, 1411 | ; it's where the next line added to the end will be 1412 | ; placed. 1413 | ; 1414 | PROGRAMEND ds 2 1415 | HighMem ds 2 ;highest location 1416 | UsedMem ds 2 ;size of user program 1417 | FreeMem ds 2 ;amount of free memory 1418 | ; 1419 | ;===================================================== 1420 | ; This is the start of the user's BASIC program space. 1421 | ; 1422 | ; PERSONAL GOAL: This should be no larger than $0DFF. 1423 | ; 0200-05FF = 1K 1424 | ; 0200-09FF = 2K 1425 | ; 0200-0DFF = 3K 1426 | ; 0200-11FF = 4K 1427 | ; 0200-13FF = 4.5K 1428 | ; 1429 | if FIXED 1430 | org $2000 1431 | endif 1432 | ProgramStart equ * 1433 | ; 1434 | if CTMON65 || XKIM 1435 | code 1436 | org AutoRun 1437 | dw TBasicCold 1438 | endif 1439 | ; 1440 | end 1441 | 1442 | -------------------------------------------------------------------------------- /mytb.hex: -------------------------------------------------------------------------------- 1 | :200200004C15024C86024CA01E4C06E04C2F1E4C15E04C00E0200FE00D0A426F6227732072 2 | :2002200054696E792042415349432076302E330D0A68747470733A2F2F6769746875622EDE 3 | :20024000636F6D2F436F727368616D546563682F363530322D54696E792D42415349430D76 4 | :200260000A00A98B8575A90C8576A9848D7C0FA90F8D7D0FA95A8DF30EA99D8DF40EA95B16 5 | :200280008DF50E4C9302200C02ADEE0E8575ADEF0E857620AB0BA9008540A90085858D9A59 6 | :2002A0000EA99A857EA90E857FA540F00320330BA480202A0B84802053080AC95A903320F1 7 | :2002C0000FE00D0A496C6C6567616C20494C2000206008A000B175200F02200FE020617406 8 | :2002E0002000A576200F02A575200F02200C024C1202A8B900038577B9010385786C7700BC 9 | :200300006B0373038603A903B203CB06BA03D203FF0302040504470467047804C404DA04C3 10 | :20032000F004080542058B059E05AE0558030D061906C5061202DE06D106D406EB06F9069E 11 | :2003400005072E0754076E07B107BA073208B90BE50B440C040C3E0CA9008D880EA98485FD 12 | :200360007E8D7C0FA90F857F8D7D0FA9008D770E4CA902A480202A0BB17EF007A204A90072 13 | :200380004C7B044CA902A480A92288D17ED0FBC88480B17EF010C922F009200602E680A4F9 14 | :2003A00080D0EFC884804CA90220850A20FD084CA902A9092006024CA902A585D0034CD482 15 | :2003C0000620D90820F008D0034C6704204F084CA90220850A20960820F008F0ECA002847F 16 | :2003E00080A9FF8585A9998575A90C85764CA902A9FF8585A9998575A90C85764CA9024C07 17 | :20040000BF024CBF0220980A20AB0A20850AA581C583D00AA582C584D004A902D014A58187 18 | :20042000C583A582E584500249803004A904D002A9010DF10E2DF00EF0074CA902A904D0CA 19 | :20044000F120D9084CD803A58048A57F48A57E48A93F20E009206909205F0A68857E6885DB 20 | :200460007F6885804CA902A9008585ADEE0E8575ADEF0E85764CA902204F08868185822002 21 | :200480000FE04572726F72200020FD08A585F01B200FE0206174206C696E652000A000B1AC 22 | :2004A0007E8581C8B17E858220FD08200C02A9008585A9008D880EADEE0E8575ADEF0E8516 23 | :2004C000764CA90220850A20980A18A58165838581A582658485824C3C0520980A20850AFD 24 | :2004E00038A581E5838581A582E58485824C3C0520850AA58149FF8581A58249FF8582E68D 25 | :2005000081D002E6824C3C0520850A20980AA5818DF00EA5828DF10EA90085818582A21056 26 | :20052000068126820EF00E2EF10E900D18A58165838581A58265848582CAD0E4205F0A4C20 27 | :20054000A90220980A20850AA5830584F03620C00AA9008DF00E8DF10EA210068126822EEF 28 | :20056000F00E2EF10EADF00E38E583A8ADF10EE58490088DF10E8CF00EE681CAD0DD20FBA1 29 | :200580000A4C3C05A206A9004C7B0420850A20980AA683A5819541A58295424CA902209865 30 | :2005A0000AA683B5418581B54285824C3C0520AB0BA9848577A90F8578A577CD7C0FD00782 31 | :2005C000A578CD7D0FF040A000B1778581C8B1778582C8847B20FD08A92020B60BA47BB14A 32 | :2005E00077F00A847B20B60BA47BC8D0F2C8189865778577A57869008578A90D20B60BA9EE 33 | :200600000A20B60B4CB90520AB0B4CA902A93E20E009A90085854CA902A000206909202A02 34 | :200620000B847C209608D03D20420A847D38AD7C0FE57D8D7C0FAD7D0FE9008D7D0FA57E2F 35 | :200640008583A57F8584A583CD7C0FD007A584CD7D0FF011A47DB183A0009183E683D0E663 36 | :20066000E6844C4606A47CB99A0EF05620310AAD7C0F8586AD7D0F8587A000B186A47D9145 37 | :2006800086A586C57ED006A587C57FF00BA586D002C687C6864C790618A57D6D7C0F8D7C89 38 | :2006A0000FAD7D0F69008D7D0FA000A581917EC8A582917EC8A67CBD9A0E917EF004E8C89B 39 | :2006C000D0F54CA9022083084CA902200C024CA902206908204F08867585764CA902204F33 40 | :2006E0000886818582205F0A4CA902A900A2009541E8E034D0F94CA902204F088EEE0E8DF9 41 | :20070000EF0E4CA902205308857C204D0AA4808477205308F00BA477D17ED00CC88477D084 42 | :20072000F0A47784804CA90220560A4C9107205308857CA480202A0BB17EC9419053C95B1A 43 | :20074000B04F38E9410A8581A9008582205F0AE6804CA902205308857CA480202A0BB17E6E 44 | :20076000C930902DC93AB0292069094CA902205308857CA480202A0BB17EC92DF008C93053 45 | :20078000900FC93AB00B2069098480205F0A4CA902A57C100E1865758575A57669FF85763D 46 | :2007A0004CA9021865758575A576690085764CA90220780B205F0A4CA90220980AA58105D0 47 | :2007C00082F063ADF50E8DF30EADF40E0A2EF30E0A2EF30E186DF40E48ADF30E6DF50E8D6B 48 | :2007E000F50E6869118DF40EADF50E69368DF50EADF40E8581ADF50E297F8582A581C58324 49 | :20080000D016A582C584D01038A581E5838581A582E58485824CFC07A581C583A582E584F2 50 | :200820005002498010E2E681D002E682205F0A4CA90220850AA582101049FF8582A58149D6 51 | :20084000FF8581E681D002E682205F0A4CA902205308AAA000B17508E675D002E67628606E 52 | :20086000A575D002C676C67560AC880EA57518690299780E08C8A57628690099780EC88CC8 53 | :20088000880E60AC880E88B9780E857688B9780E85758C880E60A984857EA90F857FA57E0C 54 | :2008A000CD7C0FD00BA57FCD7D0FD004A9011860A581A000D17ED008C8A582D17ED0016036 55 | :2008C000A001B17EC582900BD00788B17EC5819002386020D9084C9E08A0028480B17EF0B0 56 | :2008E00003C8D0F9C89818657E857E9002E67F60A57ECD7C0FD005A57FCD7D0F60A582104B 57 | :2009000017A92D20B60BA58149FF8581A58249FF8582E681D002E682A2008EEC0EA000A50F 58 | :200920008138FD61098581A582FD6209102E18A5817D610985818EEB0E980900D005ADEC03 59 | :200940000EF0099809308DEC0E20B60BAEEB0EE8E8E008D0C8A58109304CB60B8582C8D055 60 | :20096000BE1027E80364000A00A9008581858285778413B17EC92DD002E677B17EC93090D4 61 | :2009800036C93AB03238E9304806812682A5818583A5828584068126820681268218A5817A 62 | :2009A00065838581A582658485826818658185819002E682C8D0C4A5810582F016A577F0B1 63 | :2009C00012A58149FF8581A58249FF8582E681D002E682A5818510A5828511A577851260EF 64 | :2009E000A29A867EA20E867F4868480900F008200602A920200602A2008EEA0E200902C9D4 65 | :200A00000DF00DC908F021AEEA0E9D9A0EE8D0E9A900AEEA0E9D9A0E8580200C02A00020D7 66 | :200A20002A0BB17EF0C36860AEEA0EF0CCCA4CF909A200B99A0EF004C8E8D0F7E8E8E886B1 67 | :200A40007D60A002B17EF003C8D0F9C860A5758579A576857A60A5798575A57A857660AE6A 68 | :200A6000770EA5819D670EE8A5829D670EE88E770E60AE770EA5839D670EE8A5849D670EAD 69 | :200A8000E88E770E60AE770ECABD670E8582CABD670E85818E770E60AE770ECABD670E859C 70 | :200AA00084CABD670E85838E770E60AE770ECABD670E8DF10ECABD670E8DF00E8E770E6086 71 | :200AC000A9008DF20EA5821013EEF20E49FF8582A58149FF8581E681D002E682A584101AF1 72 | :200AE00048ADF20E49018DF20E6849FF8584A58349FF8583E683D002E68460ADF20EF0282F 73 | :200B0000A581D002C682C681A58149FF8581A58249FF8582A583D002C684C683A58349FF67 74 | :200B20008583A58449FF858460C8B17EF004C920F0F760200FE0494C50433A2000A57620EC 75 | :200B40000F02A575200F02A920200602A000B175200F02200FE02C204355525054523A20BC 76 | :200B600000A57F200F02A57E200F02A92B200602A580200F024C0C02A9FF8D7E0FA9138D15 77 | :200B80007F0F38AD7E0FED7C0F8D820F8581AD7F0FED7D0F8D830F858238AD7C0FE9848D75 78 | :200BA000800FAD7D0FE90F8D810F60A9068DF60EA9028DF70E606CF60EA480B17ED007A9D8 79 | :200BC00000A2094C7B041898657EA8A57F6900AA204BE09007A207A9004C7B04A900858977 80 | :200BE00085884CA902A480B17EF0D41898657EA8A57F6900AA2054E09007A900A2084C7B63 81 | :200C0000044CA902A29A867EA20E867FA2008EEA0E204A0CB016C90DF00DC90AF009AEEA49 82 | :200C20000E9D9A0EE8D0E7AEEA0EF0E2AEEA0EA9009D9A0E8580A000202A0B4CA902207332 83 | :200C40000C4CB1052051E04CA902A689E488D013A984A20EA0F8204EE0B0108588C900F077 84 | :200C60000AA200BDF80EE886891860A900858985883860A97E8DF60EA90C8DF70E608DF856 85 | :200C80000EA901A0F8A20E2057E06016051F910C1E172204181D910C0020154C455400215E 86 | :200CA0004E204C3D001CA00D011306910C1D990C2019474F002008544F001CA00D01072070 87 | :200CC0002E535542001CA00D010807202C5052494E5400201D22000220062C00041DD30C97 88 | :200CE00020053B001DD30C010506910C1D990C1D9D0D1CA00D031DD80C20174946001CA017 89 | :200D00000D1C2F0E1CA00D20945448454E000A1D990C2018494E5055540021810B13200548 90 | :200D20002C001D1A0D0106910C1D990C200F52455455524E00010906910C1D990C200545F5 91 | :200D40004E44000C200A4C4953540001151D910C200D52554E00011E1B0100071D990C2079 92 | :200D6000084E455700011D8B0C200645584954001A200A52454D0006910C1D990C200B5361 93 | :200D800041564500282B291D910C20114C4F414400272A2204181D920D291D910C0D0500B0 94 | :200DA00020092D001CC90D101DB20D20022B001CC90D20092B001CC90D0E1DB20D20092D3A 95 | :200DC000001CC90D0F1DB20D191CE50D20092A001CE50D111DCC0D20EF2F001CE50D121D2C 96 | :200DE000CC0D1D9D0D20094652454528290024192017524E442800200729001BFE7F251911 97 | :200E00001CE50D209829002519200E41425328001CE50D20882900261921021419230119CE 98 | :200E200020CE28001CA00D20032900190D050020063D001B02001920163C0020063D001BD3 99 | :200E400003001920063E001B0500191B01001920913E0020063D001B06001920063C001B9B 100 | :070E60000100191B04001939 101 | :02DFF800000225 102 | :00000001FF 103 | -------------------------------------------------------------------------------- /storage.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ;===================================================== 3 | ;===================================================== 4 | ;===================================================== 5 | ; This file contains the functions for saving and 6 | ; restoring programs from some sort of mass storage 7 | ; device. This particular version is for using the 8 | ; Corsham Tech SD Card System. 9 | ;===================================================== 10 | ;===================================================== 11 | ;===================================================== 12 | 13 | bss 14 | diskBufLength ds 1 15 | diskBufOffset ds 1 16 | code 17 | 18 | ; 19 | ;===================================================== 20 | ; Open a file for reading as a program. The next 21 | ; thing on the line should be the filename. 22 | ; 23 | iOPENREAD 24 | if XKIM || CTMON65 25 | ldy CUROFF 26 | lda (CURPTR),y 27 | bne iOPENfn ;might be filename 28 | ; 29 | ; No filename supplied. 30 | ; 31 | iOPENnofn lda #0 32 | ldx #ERR_NO_FILENAME 33 | jmp iErr2 34 | ; 35 | ; Add the offset into the buffer start 36 | ; 37 | iOPENfn clc 38 | tya 39 | adc CURPTR 40 | tay ;LSB 41 | lda CURPTR+1 42 | adc #0 43 | tax 44 | jsr DiskOpenRead ;attempt to open file 45 | bcc Ropenok ;branch if opened ok 46 | ; 47 | ; Open failed 48 | ; 49 | Rdfail ldx #ERR_READ_FAIL 50 | Rdfail2 lda #0 51 | jmp iErr2 52 | ; 53 | ; Clear counts and offsets so the next read will 54 | ; cause the file to be read. 55 | ; 56 | Ropenok lda #0 57 | sta diskBufOffset 58 | sta diskBufLength 59 | jmp NextIL 60 | endif 61 | ; 62 | ;===================================================== 63 | iOPENWRITE 64 | if XKIM || CTMON65 65 | ldy CUROFF 66 | lda (CURPTR),y 67 | beq iOPENnofn 68 | ; 69 | clc 70 | tya 71 | adc CURPTR 72 | tay ;LSB 73 | lda CURPTR+1 74 | adc #0 75 | tax 76 | jsr DiskOpenWrite ;attempt to open file 77 | bcc Wopenok ;branch if opened ok 78 | ; 79 | ; Open failed 80 | ; 81 | Wdfail lda #0 82 | ldx #ERR_WRITE_FAIL 83 | jmp iErr2 84 | ; 85 | Wopenok jmp NextIL 86 | endif 87 | ; 88 | ;===================================================== 89 | ; Gets a line of input from the disk file and puts it 90 | ; into LINBUF. 91 | ; 92 | ; On exit: 93 | ; CURPTR points to LINBUF 94 | ; LINBUF contains the line with 0 at the end. 95 | ; Y has offset to first non-space character 96 | ; CURROFF has the same as Y. 97 | ; 98 | iDGETLINE 99 | if XKIM || CTMON65 100 | ldx #LINBUF&$ff 101 | stx CURPTR 102 | ldx #LINBUF>>8 103 | stx CURPTR+1 104 | ; 105 | ldx #0 ;offset 106 | iDgetLoop stx getlinx 107 | jsr getNextFileByte 108 | bcs iGetEOF 109 | cmp #CR 110 | beq iGetEOL 111 | cmp #LF 112 | beq iGetEOL 113 | ldx getlinx 114 | sta LINBUF,x 115 | inx 116 | bne iDgetLoop 117 | ; 118 | ; Handle end of line. If the line has nothing, loop 119 | ; back and get another line. 120 | ; 121 | iGetEOL ldx getlinx ;blank line? 122 | beq iDgetLoop ;yes, ignore it 123 | ; 124 | ; This can fall through when there is a line, or 125 | ; called directly when EOF is encountered. 126 | ; 127 | iGetEOF ldx getlinx 128 | lda #0 129 | sta LINBUF,x 130 | sta CUROFF 131 | ldy #0 132 | jsr SkipSpaces 133 | jmp NextIL 134 | endif 135 | ; 136 | ;===================================================== 137 | ; Does a LIST to a file file. 138 | ; 139 | iDLIST 140 | if XKIM || CTMON65 141 | jsr SetOutDisk 142 | jmp iLST2 143 | endif 144 | ; 145 | ;===================================================== 146 | ; Closes any pending disk file. Okay to call if there 147 | ; is no open file. 148 | ; 149 | iDCLOSE 150 | if XKIM || CTMON65 151 | jsr DiskClose 152 | jmp NextIL 153 | endif 154 | ; 155 | ;===================================================== 156 | ; This gets the next byte from an open disk file. If 157 | ; there are no more bytes left, this returns C set. 158 | ; Else, C is clear and A contains the character. 159 | ; 160 | getNextFileByte 161 | if XKIM || CTMON65 162 | ldx diskBufOffset 163 | cpx diskBufLength 164 | bne hasdata ;branch if still data 165 | ; 166 | ; There is no data left in the buffer, so read a 167 | ; block from the SD system. 168 | ; 169 | lda #BUFFER_SIZE 170 | ldx #buffer>>8 171 | ldy #buffer&$ff 172 | jsr DiskRead 173 | bcs getNextEof 174 | ; 175 | ; A contains the number of bytes actually read. 176 | ; 177 | sta diskBufLength ;save length 178 | cmp #0 ;shouldn't happen 179 | beq getNextEof 180 | ; 181 | ldx #0 182 | hasdata lda buffer,x 183 | inx 184 | stx diskBufOffset 185 | clc 186 | rts 187 | ; 188 | getNextEof lda #0 189 | sta diskBufOffset 190 | sta diskBufLength 191 | sec 192 | rts 193 | ; 194 | ;===================================================== 195 | ; Set output vector to the disk output function 196 | ; 197 | SetOutDisk lda #DOUT&$ff 198 | sta BOutVec 199 | lda #DOUT/256 200 | sta BOutVec+1 201 | rts 202 | ; 203 | ;===================================================== 204 | 205 | DOUT sta buffer 206 | lda #1 207 | ldy #buffer&$ff 208 | ldx #buffer/256 209 | jsr DiskWrite 210 | ; 211 | ; need error checking here 212 | ; 213 | rts 214 | endif 215 | 216 | 217 | -------------------------------------------------------------------------------- /support.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ;===================================================== 3 | ;===================================================== 4 | ;===================================================== 5 | ; This marks the start of support functions used by 6 | ; the IL opcodes. These are support functions, NOT 7 | ; the IL code. 8 | ;===================================================== 9 | ;===================================================== 10 | ;===================================================== 11 | ; This gets the next two bytes pointed to by ILPC and 12 | ; returns them; X contains LSB, A contains MSB. ILPC 13 | ; is advanced by two, and Y contains 0 on return. 14 | ; 15 | getILWord jsr getILByte ;LSB 16 | tax 17 | ; 18 | ;===================================================== 19 | ; This gets the next byte pointed to by ILPC and 20 | ; returns it in A. On return, X is unchanged but Y 21 | ; contains 0. 22 | ; 23 | getILByte ldy #0 24 | lda (ILPC),y ;get byte 25 | php ;save status 26 | inc ILPC ;inc LSB 27 | bne getILb2 ;branch if no overflow 28 | inc ILPC+1 ;inc MSB 29 | getILb2 plp ;restore status 30 | rts 31 | ; 32 | ;===================================================== 33 | ; Decrement ILPC by one. 34 | ; 35 | decIL lda ILPC 36 | bne decIL2 37 | dec ILPC+1 38 | decIL2 dec ILPC 39 | rts 40 | ; 41 | ;===================================================== 42 | ; Push the ILPC onto the return stack. Actually, this 43 | ; pushes the address of ILPC+2 since that's the next 44 | ; address to execute. 45 | ; 46 | pushILPC ldy retStackPtr 47 | lda ILPC 48 | clc 49 | adc #2 50 | sta retStack,y 51 | php ;save C bit 52 | iny 53 | lda ILPC+1 54 | plp ;restore C 55 | adc #0 56 | sta retStack,y 57 | iny 58 | sty retStackPtr 59 | rts 60 | ; 61 | ;===================================================== 62 | ; Pull the top entry from return stack and put into 63 | ; ILPC. 64 | ; 65 | popILPC ldy retStackPtr 66 | dey 67 | lda retStack,y 68 | sta ILPC+1 69 | dey 70 | lda retStack,y 71 | sta ILPC 72 | sty retStackPtr 73 | rts 74 | ; 75 | ;===================================================== 76 | ; This searches for a specific line number that is in 77 | ; R0. There are three possible return conditions: 78 | ; 79 | ; Exact match was found: 80 | ; * Z set 81 | ; * CURPTR points to two-byte line number for that 82 | ; line. 83 | ; 84 | ; Next highest line found: 85 | ; * Z cleared 86 | ; * C set 87 | ; * CURPTR points to two-byte line number for that 88 | ; line. 89 | ; 90 | ; End of program reached: 91 | ; * Z cleared 92 | ; * C cleared 93 | ; * CURPTR points to first free byte at end of 94 | ; program. Ie, it has save value as PROGRAMEND. 95 | ; 96 | ; A, X, and Y are all undefined on return. 97 | ; 98 | findLine lda #ProgramStart&$ff 99 | sta CURPTR 100 | lda #ProgramStart>>8 101 | sta CURPTR+1 102 | ; 103 | ; At end of code? 104 | ; 105 | iXFER1 lda CURPTR 106 | cmp PROGRAMEND 107 | bne xfer2 ;not end 108 | lda CURPTR+1 109 | cmp PROGRAMEND+1 110 | bne xfer2 111 | ; 112 | ; Line not found and the end of the program was 113 | ; reached. Return Z and C both clear. 114 | ; 115 | lda #1 ;clear Z 116 | clc ;clear C 117 | rts 118 | ; 119 | ; Check for an exact match first 120 | ; 121 | xfer2 lda R0 122 | ldy #0 123 | cmp (CURPTR),y 124 | bne xfernotit 125 | iny 126 | lda R0+1 127 | cmp (CURPTR),y 128 | bne xfernotit 129 | ; 130 | ; This is exactly the line we want. 131 | ; 132 | rts 133 | ; 134 | ; See if this line is greater than the one we're 135 | ; searching for. 136 | ; 137 | xfernotit ldy #1 138 | lda (CURPTR),y ;compare MSB first 139 | cmp R0+1 140 | bcc xfer3 141 | bne xfer4 142 | dey 143 | lda (CURPTR),y ;compare LSB 144 | cmp R0 145 | bcc xfer3 146 | ; 147 | ; This line is greater than the one we want, so 148 | ; return Z clear and C set. 149 | ; 150 | xfer4: sec 151 | rts ;both conditions set 152 | ; 153 | ; Not the line (or droid) we're looking for. Move to 154 | ; the next line. 155 | ; 156 | xfer3 jsr FindNextLine 157 | jmp iXFER1 158 | ; 159 | ;===================================================== 160 | ; This advances CURPTR to the next line. If there 161 | ; are no more lines, this leaves CURPTR equal to 162 | ; ProgramEnd. Returns CUROFF set to 2. This assumes 163 | ; CURPTR is pointing to a valid line on entry. This 164 | ; pointer points to the two-byte line number. 165 | ; 166 | FindNextLine ldy #2 ;skip line number 167 | sty CUROFF ;this is the new offset 168 | ; 169 | FindNext2 lda (CURPTR),y 170 | beq FindNext3 ;found end 171 | iny 172 | bne FindNext2 173 | FindNext3 iny ;skip null byte 174 | tya 175 | clc 176 | adc CURPTR 177 | sta CURPTR 178 | bcc FindNext4 ;exit 179 | inc CURPTR+1 180 | FindNext4 rts 181 | ; 182 | ;===================================================== 183 | ; This compares CURPTR to PROGRAMEND and returns Z set 184 | ; if they are equal, Z clear if not. 185 | ; 186 | AtEnd lda CURPTR 187 | cmp PROGRAMEND 188 | bne atendexit 189 | lda CURPTR+1 190 | cmp PROGRAMEND+1 191 | atendexit rts 192 | ; 193 | ;===================================================== 194 | ; Print the contents of R0 as a signed decimal number. 195 | ; Does leading zero suppression. 196 | ; 197 | PrintDecimal lda R0+1 ;MSB has sign 198 | bpl pplus ;it's a positive number 199 | ; 200 | ; Negative numbers need more work. Invert all the bits, 201 | ; then add one. 202 | ; 203 | lda #'-' 204 | jsr VOUTCH ;print the negative sign 205 | ; 206 | lda R0 ;invert bits 207 | eor #$ff 208 | sta R0 209 | lda R0+1 210 | eor #$ff 211 | sta R0+1 212 | inc R0 ;add one 213 | bne pplus 214 | inc R0+1 215 | ; 216 | ; Print the value in R0 as a positive number. 217 | ; 218 | pplus ldx #0 ;start of subtraction table 219 | stx diddigit ;no digits yet 220 | pploop ldy #0 ;result of division 221 | pploop2 lda R0 ;LSB 222 | sec 223 | sbc dectable,x 224 | sta R0 225 | lda R0+1 226 | sbc dectable+1,x 227 | bpl pplusok ;no underflow 228 | ; 229 | ; Else, underflow. Add back in the LSB of the 230 | ; table to R0. 231 | ; 232 | clc 233 | lda R0 234 | adc dectable,x 235 | sta R0 236 | ; 237 | ; Print the value in Y. Actually, see if Y is zero and 238 | ; whether any digit has been printed yet. If Y isn't 239 | ; zero or we've printed a digit, go ahead and print. 240 | ; 241 | stx printtx 242 | tya 243 | ora #0 ;set flags 244 | bne pprintit ;non-zero, print 245 | ; 246 | lda diddigit 247 | beq pprintno ;don't print 248 | ; 249 | pprintit tya 250 | ora #'0' 251 | sta diddigit 252 | jsr VOUTCH 253 | pprintno ldx printtx 254 | ; 255 | ; Move to the next table entry 256 | ; 257 | inx 258 | inx 259 | cpx #dectableend-dectable 260 | bne pploop ;not at end 261 | ; 262 | ; At the end. R0 contains the final value 263 | ; to print. 264 | ; 265 | lda R0 266 | ora #'0' 267 | jmp VOUTCH 268 | ; 269 | ; Finish doing the subtraction. 270 | ; 271 | pplusok sta R0+1 272 | iny 273 | bne pploop2 274 | ; 275 | ; Table of powers-of-ten 276 | ; 277 | dectable dw 10000 278 | dw 1000 279 | dw 100 280 | dw 10 281 | dectableend equ * 282 | ; 283 | ;===================================================== 284 | ; Convert an ASCII string to a number. On input, 285 | ; (CURPTR),Y points to the first digit. This gets 286 | ; digit-by-digit until finding a non-number. Returns 287 | ; Y pointing to the non-digit, and R0 contains the 288 | ; number. This does NOT check for valid ranges, so 289 | ; a value like "123456789" will produce something, 290 | ; but not what you had expected. 291 | ; 292 | getDecimal lda #0 293 | sta R0 294 | sta R0+1 295 | sta dpl ;temporary negative flag 296 | ; 297 | ; See if it's negative... 298 | ; 299 | sty $0013 300 | lda (CURPTR),y 301 | cmp #'-' 302 | bne getDecLoop 303 | inc dpl ;it's negative 304 | ; 305 | getDecLoop lda (CURPTR),y 306 | cmp #'0' 307 | bcc getDdone 308 | cmp #'9'+1 309 | bcs getDdone 310 | sec 311 | sbc #'0' ;convert to binary 312 | pha 313 | ; 314 | ; Now multiply R0 by 10. Remember that 315 | ; 2*N + 8*N = 10*N. 316 | ; 317 | asl R0 318 | rol R0+1 ;*2 319 | lda R0 320 | sta R1 321 | lda R0+1 322 | sta R1+1 323 | asl R0 324 | rol R0+1 ;*4 325 | asl R0 326 | rol R0+1 ;*8 327 | clc ;now add the partial sums... 328 | lda R0 ;...to get *10 329 | adc R1 330 | sta R0 331 | lda R0+1 332 | adc R1+1 333 | sta R0+1 334 | ; 335 | ; Add in the new digit 336 | ; 337 | pla 338 | clc 339 | adc R0 340 | sta R0 341 | bcc getD2 342 | inc R0+1 343 | ; 344 | ; Move to next character 345 | ; 346 | getD2 iny 347 | bne getDecLoop 348 | ; 349 | ; All done with digits, so now deal with it being 350 | ; negative. If zero, then don't check for negative 351 | ; flag. Ie, -0 is stored as 0. 352 | ; 353 | getDdone lda R0 354 | ora R0+1 355 | beq getDone2 ;zero 356 | lda dpl 357 | beq getDone2 ;positive 358 | ; 359 | ; Invert all the bits, then add one. 360 | ; 361 | lda R0 362 | eor #$ff 363 | sta R0 364 | lda R0+1 365 | eor #$ff 366 | sta R0+1 367 | ; 368 | inc R0 369 | bne getDone2 370 | inc R0+1 371 | getDone2 372 | lda R0 373 | sta $0010 374 | lda R0+1 375 | sta $0011 376 | lda dpl 377 | sta $012 378 | 379 | rts 380 | ; 381 | ;===================================================== 382 | ; Print the string that immediately follows the JSR to 383 | ; this function. Stops when a null byte is found, 384 | ; then returns to the instruction immediately 385 | ; following the null. 386 | ; 387 | ; Thanks to Ross Archer for this code. 388 | ; http://www.6502.org/source/io/primm.htm 389 | ; 390 | if KIM 391 | puts sty putsy 392 | pla ;low part of "return" address 393 | ;(data start address) 394 | sta dpl 395 | pla 396 | sta dpl+1 ;high part of "return" address 397 | ;(data start address) 398 | ;Note: we're pointing one short 399 | psinb ldy #1 400 | lda (dpl),y ;Get next string character 401 | inc dpl ;update the pointer 402 | bne psinc ;if not, we're pntng to next char 403 | inc dpl+1 ;account for page crossing 404 | psinc ora #0 ;Set flags according to contents of 405 | ; Accumulator 406 | beq psix1 ;don't print the final NULL 407 | jsr OUTCH ;write it out 408 | jmp psinb ;back around 409 | psix1 inc dpl 410 | bne psix2 411 | inc dpl+1 ;account for page crossing 412 | psix2 ldy putsy 413 | jmp (dpl) ;return to byte following NULL 414 | endif 415 | ; 416 | ;===================================================== 417 | ; Gets a line of input into LINBUF. 418 | ; 419 | ; On entry: 420 | ; A contains the prompt character, or 0 if none. 421 | ; 422 | ; On exit: 423 | ; CURPTR points to LINBUF 424 | ; LINBUF contains the line with 0 at the end. 425 | ; Y has offset to first non-space character 426 | ; CURROFF has the same as Y. 427 | ; 428 | GetLine ldx #LINBUF&$ff 429 | stx CURPTR 430 | ldx #LINBUF>>8 431 | stx CURPTR+1 432 | ; 433 | ; Prompt 434 | ; 435 | pha ;save for retries 436 | GetLinePr pla ;restore 437 | pha ;save again 438 | ora #0 ;any prompt? 439 | beq getlinenp 440 | jsr OUTCH 441 | lda #' ' 442 | jsr OUTCH ;space after prompt 443 | ; 444 | getlinenp ldx #0 ;offset into LINBUF 445 | getline1 stx getlinx 446 | jsr GETCH 447 | if CTMON65 448 | pha 449 | jsr cout 450 | pla 451 | endif 452 | cmp #CR 453 | beq getlind ;end of line 454 | cmp #BS ;backspace? 455 | beq getlinebs 456 | ldx getlinx 457 | sta LINBUF,x 458 | inx 459 | bne getline1 460 | ; 461 | ; CR was hit 462 | ; 463 | getlind lda #0 464 | ldx getlinx 465 | sta LINBUF,x 466 | sta CUROFF 467 | ; 468 | ; Output a CR/LF 469 | ; 470 | jsr CRLF 471 | ; 472 | ; If a blank line, prompt again. 473 | ; 474 | ldy #0 475 | jsr SkipSpaces 476 | lda (CURPTR),y 477 | beq GetLinePr ;empty line 478 | pla ;get rid of prompt char 479 | rts 480 | ; 481 | ; Backspace was hit 482 | ; 483 | getlinebs ldx getlinx 484 | beq getline1 ;at start of line 485 | dex 486 | jmp getline1 487 | ; 488 | ;===================================================== 489 | ; Count the length of the line currently in LINBUF 490 | ; starting at offset Y. Returns the length in X. The 491 | ; starting offset in Y should point past the ASCII 492 | ; line number. Also counts the trailing NULL and two 493 | ; extra bytes for where the line number will be. 494 | ; 495 | getLineLength ldx #0 ;size 496 | getLineL2 lda LINBUF,y 497 | beq getLineL3 498 | iny 499 | inx 500 | bne getLineL2 501 | getLineL3 inx ;count null at end 502 | inx ;line number LSB 503 | inx ;MSB 504 | stx lineLength 505 | rts 506 | ; 507 | ;===================================================== 508 | ; Count the length of the line pointed to by CURPTR. 509 | ; This also counts the line number and the terminating 510 | ; null. Ie, this string returns 8: 511 | ; 512 | ; Hello 513 | ; 514 | ; Another way of looking at it: add the return value 515 | ; to the CURPTR and it'll point to the next line's 516 | ; line number. Returns the value in Y. 517 | ; 518 | getCURPTRLength ldy #2 ;skip line number 519 | getCLineL2 lda (CURPTR),y 520 | beq getCLineL3 521 | iny 522 | bne getCLineL2 523 | getCLineL3 iny ;count null at end 524 | rts 525 | ; 526 | ;===================================================== 527 | ; This saves ILPC. This saves to a single save area, 528 | ; so it can't be called more than once. 529 | ; 530 | saveIL lda ILPC 531 | sta tempIL 532 | lda ILPC+1 533 | sta tempIL+1 534 | rts 535 | ; 536 | ;===================================================== 537 | ; This restores ILPC. 538 | ; 539 | restoreIL lda tempIL 540 | sta ILPC 541 | lda tempIL+1 542 | sta ILPC+1 543 | rts 544 | ; 545 | ;===================================================== 546 | ; This pushes R0 onto the stack. 547 | ; 548 | pushR0 ldx mathStackPtr 549 | lda R0 550 | sta mathStack,x 551 | inx 552 | lda R0+1 553 | sta mathStack,x 554 | inx 555 | stx mathStackPtr 556 | rts 557 | ; 558 | ;===================================================== 559 | ; This pushes R1 onto the stack 560 | ; 561 | pushR1 ldx mathStackPtr 562 | lda R1 563 | sta mathStack,x 564 | inx 565 | lda R1+1 566 | sta mathStack,x 567 | inx 568 | stx mathStackPtr 569 | rts 570 | ; 571 | ;===================================================== 572 | ; This pops TOS and places it in R0. 573 | ; 574 | popR0 ldx mathStackPtr 575 | dex 576 | lda mathStack,x 577 | sta R0+1 578 | dex 579 | lda mathStack,x 580 | sta R0 581 | stx mathStackPtr 582 | rts 583 | ; 584 | ;===================================================== 585 | ; This pops TOS and places it in R1. 586 | ; 587 | popR1 ldx mathStackPtr 588 | dex 589 | lda mathStack,x 590 | sta R1+1 591 | dex 592 | lda mathStack,x 593 | sta R1 594 | stx mathStackPtr 595 | rts 596 | ; 597 | ;===================================================== 598 | ; This pops TOS and places it in MQ. 599 | ; 600 | popMQ ldx mathStackPtr 601 | dex 602 | lda mathStack,x 603 | sta MQ+1 604 | dex 605 | lda mathStack,x 606 | sta MQ 607 | stx mathStackPtr 608 | rts 609 | ; 610 | ;===================================================== 611 | ; This assists with multiplication and division by 612 | ; looking at R0 and R1 and saving a flag as to what 613 | ; sign the result will be. Math is always done on 614 | ; positive numbers, so this converts negative numbers 615 | ; into positives. On exit, R0 and R1 are both 616 | ; positive. If the signs were different then 'signs' 617 | ; will be non-zero. 618 | ; 619 | SaveSigns lda #0 620 | sta sign ;assume positive 621 | lda R0+1 ;MSB 622 | bpl SaveSigns1 623 | inc sign ;it's negative 624 | eor #$ff ;flip bits 625 | sta R0+1 626 | lda R0 627 | eor #$ff 628 | sta R0 629 | inc R0 630 | bne SaveSigns1 631 | inc R0+1 632 | SaveSigns1 lda R1+1 633 | bpl SaveSigns2 634 | pha 635 | lda sign 636 | eor #1 637 | sta sign 638 | pla 639 | eor #$ff ;flip bits 640 | sta R1+1 641 | lda R1 642 | eor #$ff 643 | sta R1 644 | inc R1 645 | bne SaveSigns2 646 | inc R1+1 647 | SaveSigns2 rts 648 | ; 649 | ;===================================================== 650 | ; This looks at the value of 'signs' and will convert 651 | ; both R0 and R1 to negative if set. 652 | ; 653 | RestoreSigns lda sign 654 | beq restoresigns2 655 | ; 656 | lda R0 657 | bne restoresigns3 658 | dec R0+1 659 | restoresigns3 dec R0 660 | lda R0 661 | eor #$ff 662 | sta R0 663 | lda R0+1 664 | eor #$ff 665 | sta R0+1 666 | ; 667 | lda R1 668 | bne restoresigns4 669 | dec R1+1 670 | restoresigns4 dec R1 671 | lda R1 672 | eor #$ff 673 | sta R1 674 | lda R1+1 675 | eor #$ff 676 | sta R1+1 677 | ; 678 | restoresigns2 rts 679 | ; 680 | ;===================================================== 681 | ; Skip over spaces. Returns Y with the offset to 682 | ; either the last character in the line, or the first 683 | ; non-space character. 684 | ; 685 | skipsp2 iny 686 | SkipSpaces lda (CURPTR),y 687 | beq Skip3 ;end of line 688 | cmp #SPACE 689 | beq skipsp2 690 | Skip3 rts 691 | ; 692 | ;===================================================== 693 | ; This is some debug logic which displays the current 694 | ; value of the ILPC and the line buffer. 695 | ; 696 | dbgLine jsr puts 697 | db "ILPC: ",0 698 | lda ILPC+1 699 | jsr OUTHEX 700 | lda ILPC 701 | jsr OUTHEX 702 | lda #SPACE 703 | jsr OUTCH 704 | ldy #0 705 | lda (ILPC),y 706 | jsr OUTHEX 707 | ; 708 | ; Display the CURPTR value and offset 709 | ; 710 | jsr puts 711 | db ", CURPTR: ",0 712 | lda CURPTR+1 713 | jsr OUTHEX 714 | lda CURPTR 715 | jsr OUTHEX 716 | lda #'+' 717 | jsr OUTCH 718 | lda CUROFF 719 | jsr OUTHEX 720 | ; 721 | jmp CRLF 722 | ; 723 | ;===================================================== 724 | ; This function might go away eventually, but was 725 | ; added to provide data for other pieces of code. 726 | ; It has some ties to the operating environment that 727 | ; will need to be customized for the target system. 728 | ; 729 | GetSizes 730 | ; 731 | ; Here is machine specific code to get the highest 732 | ; memory location that can be used by BASIC. 733 | ; 734 | if ProgramStart < $2000 735 | lda #$ff 736 | sta HighMem ;$13ff for KIM-1 737 | lda #$13 738 | sta HighMem+1 739 | else 740 | lda #$ff 741 | sta HighMem ;$CFFF otherwise 742 | lda #$cf 743 | sta HighMem+1 744 | endif 745 | ; 746 | ; This computes the available memory remaining. 747 | ; 748 | sec 749 | lda HighMem 750 | sbc PROGRAMEND 751 | sta FreeMem 752 | sta R0 753 | lda HighMem+1 754 | sbc PROGRAMEND+1 755 | sta FreeMem+1 756 | sta R0+1 757 | ; 758 | ; This computes the size of the current user program. 759 | ; 760 | sec 761 | lda PROGRAMEND 762 | sbc #ProgramStart&$ff 763 | sta UsedMem 764 | lda PROGRAMEND+1 765 | sbc #ProgramStart>>8 766 | sta UsedMem+1 767 | ; 768 | rts 769 | ; 770 | ;===================================================== 771 | ; Set output vector to the console output function 772 | ; 773 | SetOutConsole lda #OUTCH&$ff 774 | sta BOutVec 775 | lda #OUTCH/256 776 | sta BOutVec+1 777 | rts 778 | ; 779 | ;===================================================== 780 | ; Jump to the output function in BOutVec 781 | ; 782 | VOUTCH jmp (BOutVec) 783 | 784 | -------------------------------------------------------------------------------- /xkim.inc: -------------------------------------------------------------------------------- 1 | ;===================================================== 2 | ; This file contains vectors for Corsham Technologies' 3 | ; xKIM monitor. Last update 12/12/2021 for v1.8. 4 | ; 5 | ; Note that some subroutine names have changed 6 | ; slightly to fix duplicate names in the KIM monitor. 7 | ; 8 | ; See the xKIM User Manual for documentation on the 9 | ; data in this file. 10 | ; 11 | ; www.corshamtech.com 12 | ; https://github.com/CorshamTech/xKIM 13 | ; 14 | xKIM_BASE equ $e000 15 | bss 16 | origBss equ * ;SAVE BSS!!! 17 | org xKIM_BASE 18 | ; 19 | ; Main functions 20 | ; 21 | extKIM ds 3 ;extended monitor 22 | xkOUTCH ds 3 ;output A to console 23 | xkGETCH ds 3 ;get a key and echo 24 | xkGETCHne ds 3 ;no echo - KIM can't do it 25 | xKIM_res_0 ds 3 ;future - console stat 26 | putsil ds 3 ;print string after JSR 27 | getHex ds 3 ;get hex value in A 28 | xkPRTBYT ds 3 ;print A as hex 29 | getStartAddr ds 3 30 | getEndAddr ds 3 31 | getAddrRange ds 3 32 | ; 33 | ; future use 34 | ; 35 | xkHexDump ds 3 ;perform a hex dump 36 | xkMemEdit ds 3 ;edit memory 37 | loadHexConsole ds 3 ;load hex via console 38 | loadHexFile ds 3 ;load hex from SD 39 | doDiskDir ds 3 ;do directory of SD card 40 | calcOffset ds 3 ;compute branch offset 41 | ; 42 | ; SD card functions 43 | ; 44 | ; org xKIM_BASE+$0033 45 | xParInit ds 3 46 | xParSetWrite ds 3 47 | xParSetRead ds 3 48 | xParWriteByte ds 3 49 | xParReadByte ds 3 50 | DiskPing ds 3 51 | DiskDir ds 3 52 | DiskDirNext ds 3 53 | DiskOpenRead ds 3 54 | DiskRead ds 3 55 | DiskClose ds 3 56 | DiskOpenWrite ds 3 57 | DiskWrite ds 3 58 | ; 59 | org $dff8 60 | AutoRun ds 2 61 | ColdFlag ds 2 62 | ExtensionAddr ds 2 63 | HighestAddress ds 2 64 | ; 65 | ; New vectors will go here. 66 | ; 67 | ; 68 | ; Now restore BSS! 69 | ; 70 | org origBss 71 | 72 | --------------------------------------------------------------------------------