* mikeOS 16 bit and amd64 baremetal
[mascara-docs.git] / i86 / mikeos-4.1.2 / source / features / basic.asm
blob7144d6e35eb82c8070ded4a7200c05032f3874e6
1 ; ==================================================================
2 ; MikeOS -- The Mike Operating System kernel
3 ; Copyright (C) 2006 - 2011 MikeOS Developers -- see doc/LICENSE.TXT
5 ; BASIC CODE INTERPRETER
6 ; ==================================================================
8 ; ------------------------------------------------------------------
9 ; Token types
11 %DEFINE VARIABLE 1
12 %DEFINE STRING_VAR 2
13 %DEFINE NUMBER 3
14 %DEFINE STRING 4
15 %DEFINE QUOTE 5
16 %DEFINE CHAR 6
17 %DEFINE UNKNOWN 7
18 %DEFINE LABEL 8
21 ; ------------------------------------------------------------------
22 ; The BASIC interpreter execution starts here...
24 os_run_basic:
25 mov word [orig_stack], sp ; Save stack pointer -- we might jump to the
26 ; error printing code and quit in the middle
27 ; some nested loops, and we want to preserve
28 ; the stack
30 mov word [load_point], ax ; AX was passed as starting location of code
32 mov word [prog], ax ; prog = pointer to current execution point in code
34 add bx, ax ; We were passed the .BAS byte size in BX
35 dec bx
36 dec bx
37 mov word [prog_end], bx ; Make note of program end point
40 call clear_ram ; Clear variables etc. from previous run
41 ; of a BASIC program
45 mainloop:
46 call get_token ; Get a token from the start of the line
48 cmp ax, STRING ; Is the type a string of characters?
49 je .keyword ; If so, let's see if it's a keyword to process
51 cmp ax, VARIABLE ; If it's a variable at the start of the line,
52 je near assign ; this is an assign (eg "X = Y + 5")
54 cmp ax, STRING_VAR ; Same for a string variable (eg $1)
55 je near assign
57 cmp ax, LABEL ; Don't need to do anything here - skip
58 je mainloop
60 mov si, err_syntax ; Otherwise show an error and quit
61 jmp error
64 .keyword:
65 mov si, token ; Start trying to match commands
67 mov di, alert_cmd
68 call os_string_compare
69 jc near do_alert
71 mov di, call_cmd
72 call os_string_compare
73 jc near do_call
75 mov di, cls_cmd
76 call os_string_compare
77 jc near do_cls
79 mov di, cursor_cmd
80 call os_string_compare
81 jc near do_cursor
83 mov di, curschar_cmd
84 call os_string_compare
85 jc near do_curschar
87 mov di, end_cmd
88 call os_string_compare
89 jc near do_end
91 mov di, for_cmd
92 call os_string_compare
93 jc near do_for
95 mov di, getkey_cmd
96 call os_string_compare
97 jc near do_getkey
99 mov di, gosub_cmd
100 call os_string_compare
101 jc near do_gosub
103 mov di, goto_cmd
104 call os_string_compare
105 jc near do_goto
107 mov di, input_cmd
108 call os_string_compare
109 jc near do_input
111 mov di, if_cmd
112 call os_string_compare
113 jc near do_if
115 mov di, load_cmd
116 call os_string_compare
117 jc near do_load
119 mov di, move_cmd
120 call os_string_compare
121 jc near do_move
123 mov di, next_cmd
124 call os_string_compare
125 jc near do_next
127 mov di, pause_cmd
128 call os_string_compare
129 jc near do_pause
131 mov di, peek_cmd
132 call os_string_compare
133 jc near do_peek
135 mov di, poke_cmd
136 call os_string_compare
137 jc near do_poke
139 mov di, port_cmd
140 call os_string_compare
141 jc near do_port
143 mov di, print_cmd
144 call os_string_compare
145 jc near do_print
147 mov di, rand_cmd
148 call os_string_compare
149 jc near do_rand
151 mov di, rem_cmd
152 call os_string_compare
153 jc near do_rem
155 mov di, return_cmd
156 call os_string_compare
157 jc near do_return
159 mov di, save_cmd
160 call os_string_compare
161 jc near do_save
163 mov di, serial_cmd
164 call os_string_compare
165 jc near do_serial
167 mov di, sound_cmd
168 call os_string_compare
169 jc near do_sound
171 mov di, waitkey_cmd
172 call os_string_compare
173 jc near do_waitkey
175 mov si, err_cmd_unknown ; Command not found?
176 jmp error
179 ; ------------------------------------------------------------------
180 ; CLEAR RAM
182 clear_ram:
183 mov al, 0
185 mov di, variables
186 mov cx, 52
187 rep stosb
189 mov di, for_variables
190 mov cx, 52
191 rep stosb
193 mov di, for_code_points
194 mov cx, 52
195 rep stosb
197 mov byte [gosub_depth], 0
199 mov di, gosub_points
200 mov cx, 20
201 rep stosb
203 mov di, string_vars
204 mov cx, 1024
205 rep stosb
210 ; ------------------------------------------------------------------
211 ; ASSIGNMENT
213 assign:
214 cmp ax, VARIABLE ; Are we starting with a number var?
215 je .do_num_var
217 mov di, string_vars ; Otherwise it's a string var
218 mov ax, 128
219 mul bx ; (BX = string number, passed back from get_token)
220 add di, ax
222 push di
224 call get_token
225 mov byte al, [token]
226 cmp al, '='
227 jne near .error
229 call get_token
230 cmp ax, QUOTE
231 je .second_is_quote
233 cmp ax, STRING_VAR
234 jne near .error
236 mov si, string_vars ; Otherwise it's a string var
237 mov ax, 128
238 mul bx ; (BX = string number, passed back from get_token)
239 add si, ax
241 pop di
242 call os_string_copy
244 jmp mainloop
247 .second_is_quote:
248 mov si, token
249 pop di
250 call os_string_copy
252 jmp mainloop
255 .do_num_var:
256 mov ax, 0
257 mov byte al, [token]
258 mov byte [.tmp], al
260 call get_token
261 mov byte al, [token]
262 cmp al, '='
263 jne near .error
265 call get_token
266 cmp ax, NUMBER
267 je .second_is_num
269 cmp ax, VARIABLE
270 je .second_is_variable
272 cmp ax, STRING
273 je near .second_is_string
275 cmp ax, UNKNOWN
276 jne near .error
278 mov byte al, [token] ; Address of string var?
279 cmp al, '&'
280 jne near .error
282 call get_token ; Let's see if there's a string var
283 cmp ax, STRING_VAR
284 jne near .error
286 mov di, string_vars
287 mov ax, 128
288 mul bx
289 add di, ax
291 mov bx, di
293 mov byte al, [.tmp]
294 call set_var
296 jmp mainloop
299 .second_is_variable:
300 mov ax, 0
301 mov byte al, [token]
303 call get_var
304 mov bx, ax
305 mov byte al, [.tmp]
306 call set_var
308 jmp .check_for_more
311 .second_is_num:
312 mov si, token
313 call os_string_to_int
315 mov bx, ax ; Number to insert in variable table
317 mov ax, 0
318 mov byte al, [.tmp]
320 call set_var
323 ; The assignment could be simply "X = 5" etc. Or it could be
324 ; "X = Y + 5" -- ie more complicated. So here we check to see if
325 ; there's a delimiter...
327 .check_for_more:
328 mov word ax, [prog] ; Save code location in case there's no delimiter
329 mov word [.tmp_loc], ax
331 call get_token ; Any more to deal with in this assignment?
332 mov byte al, [token]
333 cmp al, '+'
334 je .theres_more
335 cmp al, '-'
336 je .theres_more
337 cmp al, '*'
338 je .theres_more
339 cmp al, '/'
340 je .theres_more
341 cmp al, '%'
342 je .theres_more
344 mov word ax, [.tmp_loc] ; Not a delimiter, so step back before the token
345 mov word [prog], ax ; that we just grabbed
347 jmp mainloop ; And go back to the code interpreter!
350 .theres_more:
351 mov byte [.delim], al
353 call get_token
354 cmp ax, VARIABLE
355 je .handle_variable
357 mov si, token
358 call os_string_to_int
359 mov bx, ax
361 mov ax, 0
362 mov byte al, [.tmp]
364 call get_var ; This also points SI at right place in variable table
366 cmp byte [.delim], '+'
367 jne .not_plus
369 add ax, bx
370 jmp .finish
372 .not_plus:
373 cmp byte [.delim], '-'
374 jne .not_minus
376 sub ax, bx
377 jmp .finish
379 .not_minus:
380 cmp byte [.delim], '*'
381 jne .not_times
383 mul bx
384 jmp .finish
386 .not_times:
387 cmp byte [.delim], '/'
388 jne .not_divide
390 mov dx, 0
391 div bx
392 jmp .finish
394 .not_divide:
395 mov dx, 0
396 div bx
397 mov ax, dx ; Get remainder
399 .finish:
400 mov bx, ax
401 mov byte al, [.tmp]
402 call set_var
404 jmp .check_for_more
407 .handle_variable:
408 mov ax, 0
409 mov byte al, [token]
411 call get_var
413 mov bx, ax
415 mov ax, 0
416 mov byte al, [.tmp]
418 call get_var
420 cmp byte [.delim], '+'
421 jne .vnot_plus
423 add ax, bx
424 jmp .vfinish
426 .vnot_plus:
427 cmp byte [.delim], '-'
428 jne .vnot_minus
430 sub ax, bx
431 jmp .vfinish
433 .vnot_minus:
434 cmp byte [.delim], '*'
435 jne .vnot_times
437 mul bx
438 jmp .vfinish
440 .vnot_times:
441 cmp byte [.delim], '/'
442 jne .vnot_divide
444 mov dx, 0
445 div bx
446 jmp .finish
448 .vnot_divide:
449 mov dx, 0
450 div bx
451 mov ax, dx ; Get remainder
453 .vfinish:
454 mov bx, ax
455 mov byte al, [.tmp]
456 call set_var
458 jmp .check_for_more
461 .second_is_string:
462 mov di, token
463 mov si, progstart_keyword
464 call os_string_compare
465 je .is_progstart
467 mov si, ramstart_keyword
468 call os_string_compare
469 je .is_ramstart
471 jmp .error
473 .is_progstart:
474 mov ax, 0
475 mov byte al, [.tmp]
477 mov word bx, [load_point]
478 call set_var
480 jmp mainloop
484 .is_ramstart:
485 mov ax, 0
486 mov byte al, [.tmp]
488 mov word bx, [prog_end]
489 inc bx
490 inc bx
491 inc bx
492 call set_var
494 jmp mainloop
497 .error:
498 mov si, err_syntax
499 jmp error
502 .tmp db 0
503 .tmp_loc dw 0
504 .delim db 0
507 ; ==================================================================
508 ; SPECIFIC COMMAND CODE STARTS HERE
510 ; ------------------------------------------------------------------
511 ; ALERT
513 do_alert:
514 call get_token
516 cmp ax, QUOTE
517 je .is_quote
519 mov si, err_syntax
520 jmp error
522 .is_quote:
523 mov ax, token ; First string for alert box
524 mov bx, 0 ; Others are blank
525 mov cx, 0
526 mov dx, 0 ; One-choice box
527 call os_dialog_box
528 jmp mainloop
531 ; ------------------------------------------------------------------
532 ; CALL
534 do_call:
535 call get_token
536 cmp ax, NUMBER
537 je .is_number
539 mov ax, 0
540 mov byte al, [token]
541 call get_var
542 jmp .execute_call
544 .is_number:
545 mov si, token
546 call os_string_to_int
548 .execute_call:
549 mov bx, 0
550 mov cx, 0
551 mov dx, 0
552 mov di, 0
553 mov si, 0
555 call ax
557 jmp mainloop
561 ; ------------------------------------------------------------------
562 ; CLS
564 do_cls:
565 call os_clear_screen
566 jmp mainloop
569 ; ------------------------------------------------------------------
570 ; CURSOR
572 do_cursor:
573 call get_token
575 mov si, token
576 mov di, .on_str
577 call os_string_compare
578 jc .turn_on
580 mov si, token
581 mov di, .off_str
582 call os_string_compare
583 jc .turn_off
585 mov si, err_syntax
586 jmp error
588 .turn_on:
589 call os_show_cursor
590 jmp mainloop
592 .turn_off:
593 call os_hide_cursor
594 jmp mainloop
597 .on_str db "ON", 0
598 .off_str db "OFF", 0
601 ; ------------------------------------------------------------------
602 ; CURSCHAR
604 do_curschar:
605 call get_token
607 cmp ax, VARIABLE
608 je .is_ok
610 mov si, err_syntax
611 jmp error
613 .is_ok:
614 mov ax, 0
615 mov byte al, [token]
617 push ax ; Store variable we're going to use
619 mov ah, 08h
620 mov bx, 0
621 int 10h ; Get char at current cursor location
623 mov bx, 0 ; We only want the lower byte (the char, not attribute)
624 mov bl, al
626 pop ax ; Get the variable back
628 call set_var ; And store the value
630 jmp mainloop
633 ; ------------------------------------------------------------------
634 ; END
636 do_end:
637 mov word sp, [orig_stack]
641 ; ------------------------------------------------------------------
642 ; FOR
644 do_for:
645 call get_token ; Get the variable we're using in this loop
647 cmp ax, VARIABLE
648 jne near .error
650 mov ax, 0
651 mov byte al, [token]
652 mov byte [.tmp_var], al ; Store it in a temporary location for now
654 call get_token
656 mov ax, 0 ; Check it's followed up with '='
657 mov byte al, [token]
658 cmp al, '='
659 jne .error
661 call get_token ; Next we want a number
663 cmp ax, NUMBER
664 jne .error
666 mov si, token ; Convert it
667 call os_string_to_int
670 ; At this stage, we've read something like "FOR X = 1"
671 ; so let's store that 1 in the variable table
673 mov bx, ax
674 mov ax, 0
675 mov byte al, [.tmp_var]
676 call set_var
679 call get_token ; Next we're looking for "TO"
681 cmp ax, STRING
682 jne .error
684 mov ax, token
685 call os_string_uppercase
687 mov si, token
688 mov di, .to_string
689 call os_string_compare
690 jnc .error
693 ; So now we're at "FOR X = 1 TO"
695 call get_token
697 cmp ax, NUMBER
698 jne .error
700 mov si, token ; Get target number
701 call os_string_to_int
703 mov bx, ax
705 mov ax, 0
706 mov byte al, [.tmp_var]
708 sub al, 65 ; Store target number in table
709 mov di, for_variables
710 add di, ax
711 add di, ax
712 mov ax, bx
713 stosw
716 ; So we've got the variable, assigned it the starting number, and put into
717 ; our table the limit it should reach. But we also need to store the point in
718 ; code after the FOR line we should return to if NEXT X doesn't complete the loop...
720 mov ax, 0
721 mov byte al, [.tmp_var]
723 sub al, 65 ; Store code position to return to in table
724 mov di, for_code_points
725 add di, ax
726 add di, ax
727 mov word ax, [prog]
728 stosw
730 jmp mainloop
733 .error:
734 mov si, err_syntax
735 jmp error
738 .tmp_var db 0
739 .to_string db 'TO', 0
742 ; ------------------------------------------------------------------
743 ; GETKEY
745 do_getkey:
746 call get_token
747 cmp ax, VARIABLE
748 je .is_variable
750 mov si, err_syntax
751 jmp error
753 .is_variable:
754 mov ax, 0
755 mov byte al, [token]
757 push ax
759 call os_check_for_key
761 mov bx, 0
762 mov bl, al
764 pop ax
766 call set_var
768 jmp mainloop
771 ; ------------------------------------------------------------------
772 ; GOSUB
774 do_gosub:
775 call get_token ; Get the number (label)
777 cmp ax, STRING
778 je .is_ok
780 mov si, err_goto_notlabel
781 jmp error
783 .is_ok:
784 mov si, token ; Back up this label
785 mov di, .tmp_token
786 call os_string_copy
788 mov ax, .tmp_token
789 call os_string_length
791 mov di, .tmp_token ; Add ':' char to end for searching
792 add di, ax
793 mov al, ':'
794 stosb
795 mov al, 0
796 stosb
799 inc byte [gosub_depth]
801 mov ax, 0
802 mov byte al, [gosub_depth] ; Get current GOSUB nest level
804 cmp al, 9
805 jle .within_limit
807 mov si, err_nest_limit
808 jmp error
811 .within_limit:
812 mov di, gosub_points ; Move into our table of pointers
813 add di, ax ; Table is words (not bytes)
814 add di, ax
815 mov word ax, [prog]
816 stosw ; Store current location before jump
819 mov word ax, [load_point]
820 mov word [prog], ax ; Return to start of program to find label
822 .loop:
823 call get_token
825 cmp ax, LABEL
826 jne .line_loop
828 mov si, token
829 mov di, .tmp_token
830 call os_string_compare
831 jc mainloop
834 .line_loop: ; Go to end of line
835 mov word si, [prog]
836 mov byte al, [si]
837 inc word [prog]
838 cmp al, 10
839 jne .line_loop
841 mov word ax, [prog]
842 mov word bx, [prog_end]
843 cmp ax, bx
844 jg .past_end
846 jmp .loop
849 .past_end:
850 mov si, err_label_notfound
851 jmp error
854 .tmp_token times 30 db 0
857 ; ------------------------------------------------------------------
858 ; GOTO
860 do_goto:
861 call get_token ; Get the next token
863 cmp ax, STRING
864 je .is_ok
866 mov si, err_goto_notlabel
867 jmp error
869 .is_ok:
870 mov si, token ; Back up this label
871 mov di, .tmp_token
872 call os_string_copy
874 mov ax, .tmp_token
875 call os_string_length
877 mov di, .tmp_token ; Add ':' char to end for searching
878 add di, ax
879 mov al, ':'
880 stosb
881 mov al, 0
882 stosb
884 mov word ax, [load_point]
885 mov word [prog], ax ; Return to start of program to find label
887 .loop:
888 call get_token
890 cmp ax, LABEL
891 jne .line_loop
893 mov si, token
894 mov di, .tmp_token
895 call os_string_compare
896 jc mainloop
898 .line_loop: ; Go to end of line
899 mov word si, [prog]
900 mov byte al, [si]
901 inc word [prog]
903 cmp al, 10
904 jne .line_loop
906 mov word ax, [prog]
907 mov word bx, [prog_end]
908 cmp ax, bx
909 jg .past_end
911 jmp .loop
913 .past_end:
914 mov si, err_label_notfound
915 jmp error
918 .tmp_token times 30 db 0
921 ; ------------------------------------------------------------------
922 ; IF
924 do_if:
925 call get_token
927 cmp ax, VARIABLE ; If can only be followed by a variable
928 je .num_var
930 cmp ax, STRING_VAR
931 je near .string_var
933 mov si, err_syntax
934 jmp error
936 .num_var:
937 mov ax, 0
938 mov byte al, [token]
939 call get_var
941 mov dx, ax ; Store value of first part of comparison
943 call get_token ; Get the delimiter
944 mov byte al, [token]
945 cmp al, '='
946 je .equals
947 cmp al, '>'
948 je .greater
949 cmp al, '<'
950 je .less
952 mov si, err_syntax ; If not one of the above, error out
953 jmp error
955 .equals:
956 call get_token ; Is this 'X = Y' (equals another variable?)
958 cmp ax, CHAR
959 je .equals_char
961 mov byte al, [token]
962 call is_letter
963 jc .equals_var
965 mov si, token ; Otherwise it's, eg 'X = 1' (a number)
966 call os_string_to_int
968 cmp ax, dx ; On to the THEN bit if 'X = num' matches
969 je near .on_to_then
971 jmp .finish_line ; Otherwise skip the rest of the line
974 .equals_char:
975 mov ax, 0
976 mov byte al, [token]
978 cmp ax, dx
979 je near .on_to_then
981 jmp .finish_line
984 .equals_var:
985 mov ax, 0
986 mov byte al, [token]
988 call get_var
990 cmp ax, dx ; Do the variables match?
991 je near .on_to_then ; On to the THEN bit if so
993 jmp .finish_line ; Otherwise skip the rest of the line
996 .greater:
997 call get_token ; Greater than a variable or number?
998 mov byte al, [token]
999 call is_letter
1000 jc .greater_var
1002 mov si, token ; Must be a number here...
1003 call os_string_to_int
1005 cmp ax, dx
1006 jl near .on_to_then
1008 jmp .finish_line
1010 .greater_var: ; Variable in this case
1011 mov ax, 0
1012 mov byte al, [token]
1014 call get_var
1016 cmp ax, dx ; Make the comparison!
1017 jl .on_to_then
1019 jmp .finish_line
1021 .less:
1022 call get_token
1023 mov byte al, [token]
1024 call is_letter
1025 jc .less_var
1027 mov si, token
1028 call os_string_to_int
1030 cmp ax, dx
1031 jg .on_to_then
1033 jmp .finish_line
1035 .less_var:
1036 mov ax, 0
1037 mov byte al, [token]
1039 call get_var
1041 cmp ax, dx
1042 jg .on_to_then
1044 jmp .finish_line
1048 .string_var:
1049 mov byte [.tmp_string_var], bl
1051 call get_token
1053 mov byte al, [token]
1054 cmp al, '='
1055 jne .error
1057 call get_token
1058 cmp ax, STRING_VAR
1059 je .second_is_string_var
1061 cmp ax, QUOTE
1062 jne .error
1064 mov si, string_vars
1065 mov ax, 128
1066 mul bx
1067 add si, ax
1068 mov di, token
1069 call os_string_compare
1070 je .on_to_then
1072 jmp .finish_line
1075 .second_is_string_var:
1076 mov si, string_vars
1077 mov ax, 128
1078 mul bx
1079 add si, ax
1081 mov di, string_vars
1082 mov bx, 0
1083 mov byte bl, [.tmp_string_var]
1084 mov ax, 128
1085 mul bx
1086 add di, ax
1088 call os_string_compare
1089 jc .on_to_then
1091 jmp .finish_line
1094 .on_to_then:
1095 call get_token
1097 mov si, token
1098 mov di, then_keyword
1099 call os_string_compare
1101 jc .then_present
1103 mov si, err_syntax
1104 jmp error
1106 .then_present: ; Continue rest of line like any other command!
1107 jmp mainloop
1110 .finish_line: ; IF wasn't fulfilled, so skip rest of line
1111 mov word si, [prog]
1112 mov byte al, [si]
1113 inc word [prog]
1114 cmp al, 10
1115 jne .finish_line
1117 jmp mainloop
1120 .error:
1121 mov si, err_syntax
1122 jmp error
1125 .tmp_string_var db 0
1128 ; ------------------------------------------------------------------
1129 ; INPUT
1131 do_input:
1132 mov al, 0 ; Clear string from previous usage
1133 mov di, .tmpstring
1134 mov cx, 128
1135 rep stosb
1137 call get_token
1139 cmp ax, VARIABLE ; We can only INPUT to variables!
1140 je .number_var
1142 cmp ax, STRING_VAR
1143 je .string_var
1145 mov si, err_syntax
1146 jmp error
1148 .number_var:
1149 mov ax, .tmpstring ; Get input from the user
1150 call os_input_string
1152 mov ax, .tmpstring
1153 call os_string_length
1154 cmp ax, 0
1155 jne .char_entered
1157 mov byte [.tmpstring], '0' ; If enter hit, fill variable with zero
1158 mov byte [.tmpstring + 1], 0
1160 .char_entered:
1161 mov si, .tmpstring ; Convert to integer format
1162 call os_string_to_int
1163 mov bx, ax
1165 mov ax, 0
1166 mov byte al, [token] ; Get the variable where we're storing it...
1167 call set_var ; ...and store it!
1169 call os_print_newline
1171 jmp mainloop
1174 .string_var:
1175 push bx
1177 mov ax, .tmpstring
1178 call os_input_string
1180 mov si, .tmpstring
1181 mov di, string_vars
1183 pop bx
1185 mov ax, 128
1186 mul bx
1188 add di, ax
1189 call os_string_copy
1191 call os_print_newline
1193 jmp mainloop
1196 .tmpstring times 128 db 0
1199 ; ------------------------------------------------------------------
1200 ; LOAD
1202 do_load:
1203 call get_token
1204 cmp ax, QUOTE
1205 je .is_quote
1207 cmp ax, STRING_VAR
1208 jne .error
1210 mov si, string_vars
1211 mov ax, 128
1212 mul bx
1213 add si, ax
1214 jmp .get_position
1216 .is_quote:
1217 mov si, token
1219 .get_position:
1220 mov ax, si
1221 call os_file_exists
1222 jc .file_not_exists
1224 mov dx, ax ; Store for now
1226 call get_token
1228 cmp ax, VARIABLE
1229 je .second_is_var
1231 cmp ax, NUMBER
1232 jne .error
1234 mov si, token
1235 call os_string_to_int
1237 .load_part:
1238 mov cx, ax
1240 mov ax, dx
1242 call os_load_file
1244 mov ax, 0
1245 mov byte al, 'S'
1246 call set_var
1248 mov ax, 0
1249 mov byte al, 'R'
1250 mov bx, 0
1251 call set_var
1253 jmp mainloop
1256 .second_is_var:
1257 mov ax, 0
1258 mov byte al, [token]
1259 call get_var
1260 jmp .load_part
1263 .file_not_exists:
1264 mov ax, 0
1265 mov byte al, 'R'
1266 mov bx, 1
1267 call set_var
1269 call get_token ; Skip past the loading point -- unnecessary now
1271 jmp mainloop
1274 .error:
1275 mov si, err_syntax
1276 jmp error
1279 ; ------------------------------------------------------------------
1280 ; MOVE
1282 do_move:
1283 call get_token
1285 cmp ax, VARIABLE
1286 je .first_is_var
1288 mov si, token
1289 call os_string_to_int
1290 mov dl, al
1291 jmp .onto_second
1293 .first_is_var:
1294 mov ax, 0
1295 mov byte al, [token]
1296 call get_var
1297 mov dl, al
1299 .onto_second:
1300 call get_token
1302 cmp ax, VARIABLE
1303 je .second_is_var
1305 mov si, token
1306 call os_string_to_int
1307 mov dh, al
1308 jmp .finish
1310 .second_is_var:
1311 mov ax, 0
1312 mov byte al, [token]
1313 call get_var
1314 mov dh, al
1316 .finish:
1317 call os_move_cursor
1319 jmp mainloop
1322 ; ------------------------------------------------------------------
1323 ; NEXT
1325 do_next:
1326 call get_token
1328 cmp ax, VARIABLE ; NEXT must be followed by a variable
1329 jne .error
1331 mov ax, 0
1332 mov byte al, [token]
1333 call get_var
1335 inc ax ; NEXT increments the variable, of course!
1337 mov bx, ax
1339 mov ax, 0
1340 mov byte al, [token]
1342 sub al, 65
1343 mov si, for_variables
1344 add si, ax
1345 add si, ax
1346 lodsw ; Get the target number from the table
1348 inc ax ; (Make the loop inclusive of target number)
1349 cmp ax, bx ; Do the variable and target match?
1350 je .loop_finished
1352 mov ax, 0 ; If not, store the updated variable
1353 mov byte al, [token]
1354 call set_var
1356 mov ax, 0 ; Find the code point and go back
1357 mov byte al, [token]
1358 sub al, 65
1359 mov si, for_code_points
1360 add si, ax
1361 add si, ax
1362 lodsw
1364 mov word [prog], ax
1365 jmp mainloop
1368 .loop_finished:
1369 jmp mainloop
1371 .error:
1372 mov si, err_syntax
1373 jmp error
1376 ; ------------------------------------------------------------------
1377 ; PAUSE
1379 do_pause:
1380 call get_token
1382 cmp ax, VARIABLE
1383 je .is_var
1385 mov si, token
1386 call os_string_to_int
1387 jmp .finish
1389 .is_var:
1390 mov ax, 0
1391 mov byte al, [token]
1392 call get_var
1394 .finish:
1395 call os_pause
1396 jmp mainloop
1399 ; ------------------------------------------------------------------
1400 ; PEEK
1402 do_peek:
1403 call get_token
1405 cmp ax, VARIABLE
1406 jne .error
1408 mov ax, 0
1409 mov byte al, [token]
1410 mov byte [.tmp_var], al
1412 call get_token
1414 cmp ax, VARIABLE
1415 je .dereference
1417 cmp ax, NUMBER
1418 jne .error
1420 mov si, token
1421 call os_string_to_int
1423 .store:
1424 mov si, ax
1425 mov bx, 0
1426 mov byte bl, [si]
1427 mov ax, 0
1428 mov byte al, [.tmp_var]
1429 call set_var
1431 jmp mainloop
1433 .dereference:
1434 mov byte al, [token]
1435 call get_var
1436 jmp .store
1438 .error:
1439 mov si, err_syntax
1440 jmp error
1443 .tmp_var db 0
1446 ; ------------------------------------------------------------------
1447 ; POKE
1449 do_poke:
1450 call get_token
1452 cmp ax, VARIABLE
1453 je .first_is_var
1455 cmp ax, NUMBER
1456 jne .error
1458 mov si, token
1459 call os_string_to_int
1461 cmp ax, 255
1462 jg .error
1464 mov byte [.first_value], al
1465 jmp .onto_second
1468 .first_is_var:
1469 mov ax, 0
1470 mov byte al, [token]
1471 call get_var
1473 mov byte [.first_value], al
1475 .onto_second:
1476 call get_token
1478 cmp ax, VARIABLE
1479 je .second_is_var
1481 cmp ax, NUMBER
1482 jne .error
1484 mov si, token
1485 call os_string_to_int
1487 .got_value:
1488 mov di, ax
1489 mov ax, 0
1490 mov byte al, [.first_value]
1491 mov byte [di], al
1493 jmp mainloop
1495 .second_is_var:
1496 mov ax, 0
1497 mov byte al, [token]
1498 call get_var
1499 jmp .got_value
1501 .error:
1502 mov si, err_syntax
1503 jmp error
1506 .first_value db 0
1509 ; ------------------------------------------------------------------
1510 ; PORT
1512 do_port:
1513 call get_token
1514 mov si, token
1516 mov di, .out_cmd
1517 call os_string_compare
1518 jc .do_out_cmd
1520 mov di, .in_cmd
1521 call os_string_compare
1522 jc .do_in_cmd
1524 jmp .error
1527 .do_out_cmd:
1528 call get_token
1529 cmp ax, NUMBER
1530 jne .error
1532 mov si, token
1533 call os_string_to_int ; Now AX = port number
1534 mov dx, ax
1536 call get_token
1537 cmp ax, NUMBER
1538 je .out_is_num
1540 cmp ax, VARIABLE
1541 je .out_is_var
1543 jmp .error
1545 .out_is_num:
1546 mov si, token
1547 call os_string_to_int
1548 call os_port_byte_out
1549 jmp mainloop
1551 .out_is_var:
1552 mov ax, 0
1553 mov byte al, [token]
1554 call get_var
1556 call os_port_byte_out
1557 jmp mainloop
1560 .do_in_cmd:
1561 call get_token
1562 cmp ax, NUMBER
1563 jne .error
1565 mov si, token
1566 call os_string_to_int
1567 mov dx, ax
1569 call get_token
1570 cmp ax, VARIABLE
1571 jne .error
1573 mov byte cl, [token]
1575 call os_port_byte_in
1576 mov bx, 0
1577 mov bl, al
1579 mov al, cl
1580 call set_var
1582 jmp mainloop
1585 .error:
1586 mov si, err_syntax
1587 jmp error
1590 .out_cmd db "OUT", 0
1591 .in_cmd db "IN", 0
1594 ; ------------------------------------------------------------------
1595 ; PRINT
1597 do_print:
1598 call get_token ; Get part after PRINT
1600 cmp ax, QUOTE ; What type is it?
1601 je .print_quote
1603 cmp ax, VARIABLE ; Numerical variable (eg X)
1604 je .print_var
1606 cmp ax, STRING_VAR ; String variable (eg $1)
1607 je .print_string_var
1609 cmp ax, STRING ; Special keyword (eg CHR or HEX)
1610 je .print_keyword
1612 mov si, err_print_type ; We only print quoted strings and vars!
1613 jmp error
1616 .print_var:
1617 mov ax, 0
1618 mov byte al, [token]
1619 call get_var ; Get its value
1621 call os_int_to_string ; Convert to string
1622 mov si, ax
1623 call os_print_string
1625 jmp .newline_or_not
1628 .print_quote: ; If it's quoted text, print it
1629 mov si, token
1630 call os_print_string
1632 jmp .newline_or_not
1635 .print_string_var:
1636 mov si, string_vars
1637 mov ax, 128
1638 mul bx
1639 add si, ax
1640 call os_print_string
1642 jmp .newline_or_not
1645 .print_keyword:
1646 mov si, token
1647 mov di, chr_keyword
1648 call os_string_compare
1649 jc .is_chr
1651 mov di, hex_keyword
1652 call os_string_compare
1653 jc .is_hex
1655 mov si, err_syntax
1656 jmp error
1658 .is_chr:
1659 call get_token
1661 cmp ax, VARIABLE
1662 jne .error
1664 mov ax, 0
1665 mov byte al, [token]
1666 call get_var
1668 mov ah, 0Eh
1669 int 10h
1671 jmp .newline_or_not
1674 .is_hex:
1675 call get_token
1677 cmp ax, VARIABLE
1678 jne .error
1680 mov ax, 0
1681 mov byte al, [token]
1682 call get_var
1684 call os_print_2hex
1686 jmp .newline_or_not
1688 .error:
1689 mov si, err_syntax
1690 jmp error
1694 .newline_or_not:
1695 ; We want to see if the command ends with ';' -- which means that
1696 ; we shouldn't print a newline after it finishes. So we store the
1697 ; current program location to pop ahead and see if there's the ';'
1698 ; character -- otherwise we put the program location back and resume
1699 ; the main loop
1701 mov word ax, [prog]
1702 mov word [.tmp_loc], ax
1704 call get_token
1705 cmp ax, UNKNOWN
1706 jne .ignore
1708 mov ax, 0
1709 mov al, [token]
1710 cmp al, ';'
1711 jne .ignore
1713 jmp mainloop ; And go back to interpreting the code!
1715 .ignore:
1716 call os_print_newline
1718 mov word ax, [.tmp_loc]
1719 mov word [prog], ax
1721 jmp mainloop
1724 .tmp_loc dw 0
1727 ; ------------------------------------------------------------------
1728 ; RAND
1730 do_rand:
1731 call get_token
1732 cmp ax, VARIABLE
1733 jne .error
1735 mov byte al, [token]
1736 mov byte [.tmp], al
1738 call get_token
1739 cmp ax, NUMBER
1740 jne .error
1742 mov si, token
1743 call os_string_to_int
1744 mov word [.num1], ax
1746 call get_token
1747 cmp ax, NUMBER
1748 jne .error
1750 mov si, token
1751 call os_string_to_int
1752 mov word [.num2], ax
1754 mov word ax, [.num1]
1755 mov word bx, [.num2]
1756 call os_get_random
1758 mov bx, cx
1759 mov ax, 0
1760 mov byte al, [.tmp]
1761 call set_var
1763 jmp mainloop
1766 .tmp db 0
1767 .num1 dw 0
1768 .num2 dw 0
1771 .error:
1772 mov si, err_syntax
1773 jmp error
1776 ; ------------------------------------------------------------------
1777 ; REM
1779 do_rem:
1780 mov word si, [prog]
1781 mov byte al, [si]
1782 inc word [prog]
1783 cmp al, 10 ; Find end of line after REM
1784 jne do_rem
1786 jmp mainloop
1789 ; ------------------------------------------------------------------
1790 ; RETURN
1792 do_return:
1793 mov ax, 0
1794 mov byte al, [gosub_depth]
1795 cmp al, 0
1796 jne .is_ok
1798 mov si, err_return
1799 jmp error
1801 .is_ok:
1802 mov si, gosub_points
1803 add si, ax ; Table is words (not bytes)
1804 add si, ax
1805 lodsw
1806 mov word [prog], ax
1807 dec byte [gosub_depth]
1809 jmp mainloop
1812 ; ------------------------------------------------------------------
1813 ; SAVE
1815 do_save:
1816 call get_token
1817 cmp ax, QUOTE
1818 je .is_quote
1820 cmp ax, STRING_VAR
1821 jne near .error
1823 mov si, string_vars
1824 mov ax, 128
1825 mul bx
1826 add si, ax
1827 jmp .get_position
1829 .is_quote:
1830 mov si, token
1832 .get_position:
1833 mov di, .tmp_filename
1834 call os_string_copy
1836 call get_token
1838 cmp ax, VARIABLE
1839 je .second_is_var
1841 cmp ax, NUMBER
1842 jne .error
1844 mov si, token
1845 call os_string_to_int
1847 .set_data_loc:
1848 mov word [.data_loc], ax
1850 call get_token
1852 cmp ax, VARIABLE
1853 je .third_is_var
1855 cmp ax, NUMBER
1856 jne .error
1858 mov si, token
1859 call os_string_to_int
1861 .set_data_size:
1862 mov word [.data_size], ax
1865 mov word ax, .tmp_filename
1866 mov word bx, [.data_loc]
1867 mov word cx, [.data_size]
1869 call os_write_file
1870 jc .save_failure
1872 mov ax, 0
1873 mov byte al, 'R'
1874 mov bx, 0
1875 call set_var
1877 jmp mainloop
1880 .second_is_var:
1881 mov ax, 0
1882 mov byte al, [token]
1883 call get_var
1884 jmp .set_data_loc
1887 .third_is_var:
1888 mov ax, 0
1889 mov byte al, [token]
1890 call get_var
1891 jmp .set_data_size
1894 .save_failure:
1895 mov ax, 0
1896 mov byte al, 'R'
1897 mov bx, 1
1898 call set_var
1900 jmp mainloop
1902 .error:
1903 mov si, err_syntax
1904 jmp error
1907 .filename_loc dw 0
1908 .data_loc dw 0
1909 .data_size dw 0
1911 .tmp_filename times 15 db 0
1914 ; ------------------------------------------------------------------
1915 ; SERIAL
1917 do_serial:
1918 call get_token
1919 mov si, token
1921 mov di, .on_cmd
1922 call os_string_compare
1923 jc .do_on_cmd
1925 mov di, .send_cmd
1926 call os_string_compare
1927 jc .do_send_cmd
1929 mov di, .rec_cmd
1930 call os_string_compare
1931 jc .do_rec_cmd
1933 jmp .error
1935 .do_on_cmd:
1936 call get_token
1937 cmp ax, NUMBER
1938 je .do_on_cmd_ok
1939 jmp .error
1941 .do_on_cmd_ok:
1942 mov si, token
1943 call os_string_to_int
1944 cmp ax, 1200
1945 je .on_cmd_slow_mode
1946 cmp ax, 9600
1947 je .on_cmd_fast_mode
1949 jmp .error
1951 .on_cmd_fast_mode:
1952 mov ax, 0
1953 call os_serial_port_enable
1954 jmp mainloop
1956 .on_cmd_slow_mode:
1957 mov ax, 1
1958 call os_serial_port_enable
1959 jmp mainloop
1962 .do_send_cmd:
1963 call get_token
1964 cmp ax, NUMBER
1965 je .send_number
1967 cmp ax, VARIABLE
1968 je .send_variable
1970 jmp .error
1972 .send_number:
1973 mov si, token
1974 call os_string_to_int
1975 call os_send_via_serial
1976 jmp mainloop
1978 .send_variable:
1979 mov ax, 0
1980 mov byte al, [token]
1981 call get_var
1982 call os_send_via_serial
1983 jmp mainloop
1986 .do_rec_cmd:
1987 call get_token
1988 cmp ax, VARIABLE
1989 jne .error
1991 mov byte al, [token]
1993 mov cx, 0
1994 mov cl, al
1995 call os_get_via_serial
1997 mov bx, 0
1998 mov bl, al
1999 mov al, cl
2000 call set_var
2002 jmp mainloop
2005 .error:
2006 mov si, err_syntax
2007 jmp error
2010 .on_cmd db "ON", 0
2011 .send_cmd db "SEND", 0
2012 .rec_cmd db "REC", 0
2015 ; ------------------------------------------------------------------
2016 ; SOUND
2018 do_sound:
2019 call get_token
2021 cmp ax, VARIABLE
2022 je .first_is_var
2024 mov si, token
2025 call os_string_to_int
2026 jmp .done_first
2028 .first_is_var:
2029 mov ax, 0
2030 mov byte al, [token]
2031 call get_var
2033 .done_first:
2034 call os_speaker_tone
2036 call get_token
2038 cmp ax, VARIABLE
2039 je .second_is_var
2041 mov si, token
2042 call os_string_to_int
2043 jmp .finish
2045 .second_is_var:
2046 mov ax, 0
2047 mov byte al, [token]
2048 call get_var
2050 .finish:
2051 call os_pause
2052 call os_speaker_off
2054 jmp mainloop
2057 ; ------------------------------------------------------------------
2058 ; WAITKEY
2060 do_waitkey:
2061 call get_token
2062 cmp ax, VARIABLE
2063 je .is_variable
2065 mov si, err_syntax
2066 jmp error
2068 .is_variable:
2069 mov ax, 0
2070 mov byte al, [token]
2072 push ax
2074 call os_wait_for_key
2076 cmp ax, 48E0h
2077 je .up_pressed
2079 cmp ax, 50E0h
2080 je .down_pressed
2082 cmp ax, 4BE0h
2083 je .left_pressed
2085 cmp ax, 4DE0h
2086 je .right_pressed
2088 .store:
2089 mov bx, 0
2090 mov bl, al
2092 pop ax
2094 call set_var
2096 jmp mainloop
2099 .up_pressed:
2100 mov ax, 1
2101 jmp .store
2103 .down_pressed:
2104 mov ax, 2
2105 jmp .store
2107 .left_pressed:
2108 mov ax, 3
2109 jmp .store
2111 .right_pressed:
2112 mov ax, 4
2113 jmp .store
2116 ; ==================================================================
2117 ; INTERNAL ROUTINES FOR INTERPRETER
2119 ; ------------------------------------------------------------------
2120 ; Get value of variable character specified in AL (eg 'A')
2122 get_var:
2123 sub al, 65
2124 mov si, variables
2125 add si, ax
2126 add si, ax
2127 lodsw
2131 ; ------------------------------------------------------------------
2132 ; Set value of variable character specified in AL (eg 'A')
2133 ; with number specified in BX
2135 set_var:
2136 mov ah, 0
2137 sub al, 65 ; Remove ASCII codes before 'A'
2139 mov di, variables ; Find position in table (of words)
2140 add di, ax
2141 add di, ax
2142 mov ax, bx
2143 stosw
2147 ; ------------------------------------------------------------------
2148 ; Get token from current position in prog
2150 get_token:
2151 mov word si, [prog]
2152 lodsb
2154 cmp al, 10
2155 je .newline
2157 cmp al, ' '
2158 je .newline
2160 call is_number
2161 jc get_number_token
2163 cmp al, '"'
2164 je get_quote_token
2166 cmp al, 39 ; Quote mark (')
2167 je get_char_token
2169 cmp al, '$'
2170 je near get_string_var_token
2172 jmp get_string_token
2175 .newline:
2176 inc word [prog]
2177 jmp get_token
2181 get_number_token:
2182 mov word si, [prog]
2183 mov di, token
2185 .loop:
2186 lodsb
2187 cmp al, 10
2188 je .done
2189 cmp al, ' '
2190 je .done
2191 call is_number
2192 jc .fine
2194 mov si, err_char_in_num
2195 jmp error
2197 .fine:
2198 stosb
2199 inc word [prog]
2200 jmp .loop
2202 .done:
2203 mov al, 0 ; Zero-terminate the token
2204 stosb
2206 mov ax, NUMBER ; Pass back the token type
2210 get_char_token:
2211 inc word [prog] ; Move past first quote (')
2213 mov word si, [prog]
2214 lodsb
2216 mov byte [token], al
2218 lodsb
2219 cmp al, 39 ; Needs to finish with another quote
2220 je .is_ok
2222 mov si, err_quote_term
2223 jmp error
2225 .is_ok:
2226 inc word [prog]
2227 inc word [prog]
2229 mov ax, CHAR
2233 get_quote_token:
2234 inc word [prog] ; Move past first quote (") char
2235 mov word si, [prog]
2236 mov di, token
2237 .loop:
2238 lodsb
2239 cmp al, '"'
2240 je .done
2241 cmp al, 10
2242 je .error
2243 stosb
2244 inc word [prog]
2245 jmp .loop
2247 .done:
2248 mov al, 0 ; Zero-terminate the token
2249 stosb
2250 inc word [prog] ; Move past final quote
2252 mov ax, QUOTE ; Pass back token type
2255 .error:
2256 mov si, err_quote_term
2257 jmp error
2260 get_string_var_token:
2261 lodsb
2262 mov bx, 0 ; If it's a string var, pass number of string in BX
2263 mov bl, al
2264 sub bl, 49
2266 inc word [prog]
2267 inc word [prog]
2269 mov ax, STRING_VAR
2273 get_string_token:
2274 mov word si, [prog]
2275 mov di, token
2276 .loop:
2277 lodsb
2278 cmp al, 10
2279 je .done
2280 cmp al, ' '
2281 je .done
2282 stosb
2283 inc word [prog]
2284 jmp .loop
2285 .done:
2286 mov al, 0 ; Zero-terminate the token
2287 stosb
2289 mov ax, token
2290 call os_string_uppercase
2292 mov ax, token
2293 call os_string_length ; How long was the token?
2294 cmp ax, 1 ; If 1 char, it's a variable or delimiter
2295 je .is_not_string
2297 mov si, token ; If the token ends with ':', it's a label
2298 add si, ax
2299 dec si
2300 lodsb
2301 cmp al, ':'
2302 je .is_label
2304 mov ax, STRING ; Otherwise it's a general string of characters
2307 .is_label:
2308 mov ax, LABEL
2312 .is_not_string:
2313 mov byte al, [token]
2314 call is_letter
2315 jc .is_var
2317 mov ax, UNKNOWN
2320 .is_var:
2321 mov ax, VARIABLE ; Otherwise probably a variable
2325 ; ------------------------------------------------------------------
2326 ; Set carry flag if AL contains ASCII number
2328 is_number:
2329 cmp al, 48
2330 jl .not_number
2331 cmp al, 57
2332 jg .not_number
2335 .not_number:
2340 ; ------------------------------------------------------------------
2341 ; Set carry flag if AL contains ASCII letter
2343 is_letter:
2344 cmp al, 65
2345 jl .not_letter
2346 cmp al, 90
2347 jg .not_letter
2351 .not_letter:
2356 ; ------------------------------------------------------------------
2357 ; Print error message and quit out
2359 error:
2360 call os_print_newline
2361 call os_print_string ; Print error message
2362 call os_print_newline
2364 mov word sp, [orig_stack] ; Restore the stack to as it was when BASIC started
2366 ret ; And finish
2369 ; Error messages text...
2371 err_char_in_num db "Error: unexpected character in number", 0
2372 err_quote_term db "Error: quoted string or character not terminated correctly", 0
2373 err_print_type db "Error: PRINT command not followed by quoted text or variable", 0
2374 err_cmd_unknown db "Error: unknown command", 0
2375 err_goto_notlabel db "Error: GOTO or GOSUB not followed by label", 0
2376 err_label_notfound db "Error: GOTO or GOSUB label not found", 0
2377 err_return db "Error: RETURN without GOSUB", 0
2378 err_nest_limit db "Error: FOR or GOSUB nest limit exceeded", 0
2379 err_next db "Error: NEXT without FOR", 0
2380 err_syntax db "Error: syntax error", 0
2384 ; ==================================================================
2385 ; DATA SECTION
2387 orig_stack dw 0 ; Original stack location when BASIC started
2389 prog dw 0 ; Pointer to current location in BASIC code
2390 prog_end dw 0 ; Pointer to final byte of BASIC code
2392 load_point dw 0
2394 token_type db 0 ; Type of last token read (eg NUMBER, VARIABLE)
2395 token times 255 db 0 ; Storage space for the token
2397 variables times 26 dw 0 ; Storage space for variables A to Z
2398 for_variables times 26 dw 0 ; Storage for FOR loops
2399 for_code_points times 26 dw 0 ; Storage for code positions where FOR loops start
2401 alert_cmd db "ALERT", 0
2402 call_cmd db "CALL", 0
2403 cls_cmd db "CLS", 0
2404 cursor_cmd db "CURSOR", 0
2405 curschar_cmd db "CURSCHAR", 0
2406 end_cmd db "END", 0
2407 for_cmd db "FOR", 0
2408 gosub_cmd db "GOSUB", 0
2409 goto_cmd db "GOTO", 0
2410 getkey_cmd db "GETKEY", 0
2411 if_cmd db "IF", 0
2412 input_cmd db "INPUT", 0
2413 load_cmd db "LOAD", 0
2414 move_cmd db "MOVE", 0
2415 next_cmd db "NEXT", 0
2416 pause_cmd db "PAUSE", 0
2417 peek_cmd db "PEEK", 0
2418 poke_cmd db "POKE", 0
2419 port_cmd db "PORT", 0
2420 print_cmd db "PRINT", 0
2421 rem_cmd db "REM", 0
2422 rand_cmd db "RAND", 0
2423 return_cmd db "RETURN", 0
2424 save_cmd db "SAVE", 0
2425 serial_cmd db "SERIAL", 0
2426 sound_cmd db "SOUND", 0
2427 waitkey_cmd db "WAITKEY", 0
2429 then_keyword db "THEN", 0
2430 chr_keyword db "CHR", 0
2431 hex_keyword db "HEX", 0
2433 progstart_keyword db "PROGSTART", 0
2434 ramstart_keyword db "RAMSTART", 0
2436 gosub_depth db 0
2437 gosub_points times 10 dw 0 ; Points in code to RETURN to
2439 string_vars times 1024 db 0 ; 8 * 128 byte strings
2442 ; ------------------------------------------------------------------