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 ; ------------------------------------------------------------------
21 ; ------------------------------------------------------------------
22 ; The BASIC interpreter execution starts here...
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
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
37 mov word [prog_end
], bx ; Make note of program end point
40 call clear_ram
; Clear variables etc. from previous run
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)
57 cmp ax, LABEL ; Don't need to do anything here - skip
60 mov si, err_syntax
; Otherwise show an error and quit
65 mov si, token
; Start trying to match commands
68 call os_string_compare
72 call os_string_compare
76 call os_string_compare
80 call os_string_compare
84 call os_string_compare
88 call os_string_compare
92 call os_string_compare
96 call os_string_compare
100 call os_string_compare
104 call os_string_compare
108 call os_string_compare
112 call os_string_compare
116 call os_string_compare
120 call os_string_compare
124 call os_string_compare
128 call os_string_compare
132 call os_string_compare
136 call os_string_compare
140 call os_string_compare
144 call os_string_compare
148 call os_string_compare
152 call os_string_compare
156 call os_string_compare
160 call os_string_compare
164 call os_string_compare
168 call os_string_compare
172 call os_string_compare
175 mov si, err_cmd_unknown
; Command not found?
179 ; ------------------------------------------------------------------
189 mov di, for_variables
193 mov di, for_code_points
197 mov byte [gosub_depth
], 0
210 ; ------------------------------------------------------------------
214 cmp ax, VARIABLE
; Are we starting with a number var?
217 mov di, string_vars
; Otherwise it's a string var
219 mul bx ; (BX = string number, passed back from get_token)
236 mov si, string_vars
; Otherwise it's a string var
238 mul bx ; (BX = string number, passed back from get_token)
270 je .second_is_variable
273 je near .second_is_string
278 mov byte al, [token
] ; Address of string var?
282 call get_token
; Let's see if there's a string var
313 call os_string_to_int
315 mov bx, ax ; Number to insert in variable table
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...
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?
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!
351 mov byte [.delim
], al
358 call os_string_to_int
364 call get_var
; This also points SI at right place in variable table
366 cmp byte [.delim
], '+'
373 cmp byte [.delim
], '-'
380 cmp byte [.delim
], '*'
387 cmp byte [.delim
], '/'
397 mov ax, dx ; Get remainder
420 cmp byte [.delim
], '+'
427 cmp byte [.delim
], '-'
434 cmp byte [.delim
], '*'
441 cmp byte [.delim
], '/'
451 mov ax, dx ; Get remainder
463 mov si, progstart_keyword
464 call os_string_compare
467 mov si, ramstart_keyword
468 call os_string_compare
477 mov word bx, [load_point
]
488 mov word bx, [prog_end
]
507 ; ==================================================================
508 ; SPECIFIC COMMAND CODE STARTS HERE
510 ; ------------------------------------------------------------------
523 mov ax, token
; First string for alert box
524 mov bx, 0 ; Others are blank
526 mov dx, 0 ; One-choice box
531 ; ------------------------------------------------------------------
546 call os_string_to_int
561 ; ------------------------------------------------------------------
569 ; ------------------------------------------------------------------
577 call os_string_compare
582 call os_string_compare
601 ; ------------------------------------------------------------------
617 push ax ; Store variable we're going to use
621 int 10h ; Get char at current cursor location
623 mov bx, 0 ; We only want the lower byte (the char, not attribute)
626 pop ax ; Get the variable back
628 call set_var
; And store the value
633 ; ------------------------------------------------------------------
637 mov word sp, [orig_stack
]
641 ; ------------------------------------------------------------------
645 call get_token
; Get the variable we're using in this loop
652 mov byte [.tmp_var
], al ; Store it in a temporary location for now
656 mov ax, 0 ; Check it's followed up with '='
661 call get_token
; Next we want a number
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
675 mov byte al, [.tmp_var
]
679 call get_token
; Next we're looking for "TO"
685 call os_string_uppercase
689 call os_string_compare
693 ; So now we're at "FOR X = 1 TO"
700 mov si, token
; Get target number
701 call os_string_to_int
706 mov byte al, [.tmp_var
]
708 sub al, 65 ; Store target number in table
709 mov di, for_variables
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...
721 mov byte al, [.tmp_var
]
723 sub al, 65 ; Store code position to return to in table
724 mov di, for_code_points
739 .to_string
db 'TO', 0
742 ; ------------------------------------------------------------------
759 call os_check_for_key
771 ; ------------------------------------------------------------------
775 call get_token
; Get the number (label)
780 mov si, err_goto_notlabel
784 mov si, token
; Back up this label
789 call os_string_length
791 mov di, .tmp_token
; Add ':' char to end for searching
799 inc byte [gosub_depth
]
802 mov byte al, [gosub_depth
] ; Get current GOSUB nest level
807 mov si, err_nest_limit
812 mov di, gosub_points
; Move into our table of pointers
813 add di, ax ; Table is words (not bytes)
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
830 call os_string_compare
834 .
line_loop: ; Go to end of line
842 mov word bx, [prog_end
]
850 mov si, err_label_notfound
854 .tmp_token times
30 db 0
857 ; ------------------------------------------------------------------
861 call get_token
; Get the next token
866 mov si, err_goto_notlabel
870 mov si, token
; Back up this label
875 call os_string_length
877 mov di, .tmp_token
; Add ':' char to end for searching
884 mov word ax, [load_point
]
885 mov word [prog
], ax ; Return to start of program to find label
895 call os_string_compare
898 .
line_loop: ; Go to end of line
907 mov word bx, [prog_end
]
914 mov si, err_label_notfound
918 .tmp_token times
30 db 0
921 ; ------------------------------------------------------------------
927 cmp ax, VARIABLE
; If can only be followed by a variable
941 mov dx, ax ; Store value of first part of comparison
943 call get_token
; Get the delimiter
952 mov si, err_syntax
; If not one of the above, error out
956 call get_token
; Is this 'X = Y' (equals another variable?)
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
971 jmp .finish_line
; Otherwise skip the rest of the line
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
997 call get_token
; Greater than a variable or number?
1002 mov si, token
; Must be a number here...
1003 call os_string_to_int
1010 .
greater_var: ; Variable in this case
1012 mov byte al, [token
]
1016 cmp ax, dx ; Make the comparison!
1023 mov byte al, [token
]
1028 call os_string_to_int
1037 mov byte al, [token
]
1049 mov byte [.tmp_string_var
], bl
1053 mov byte al, [token
]
1059 je .second_is_string_var
1069 call os_string_compare
1075 .
second_is_string_var:
1083 mov byte bl, [.tmp_string_var
]
1088 call os_string_compare
1098 mov di, then_keyword
1099 call os_string_compare
1106 .
then_present: ; Continue rest of line like any other command!
1110 .
finish_line: ; IF wasn't fulfilled, so skip rest of line
1125 .tmp_string_var
db 0
1128 ; ------------------------------------------------------------------
1132 mov al, 0 ; Clear string from previous usage
1139 cmp ax, VARIABLE
; We can only INPUT to variables!
1149 mov ax, .tmpstring
; Get input from the user
1150 call os_input_string
1153 call os_string_length
1157 mov byte [.tmpstring
], '0' ; If enter hit, fill variable with zero
1158 mov byte [.tmpstring
+ 1], 0
1161 mov si, .tmpstring
; Convert to integer format
1162 call os_string_to_int
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
1178 call os_input_string
1191 call os_print_newline
1196 .tmpstring times
128 db 0
1199 ; ------------------------------------------------------------------
1224 mov dx, ax ; Store for now
1235 call os_string_to_int
1258 mov byte al, [token
]
1269 call get_token
; Skip past the loading point -- unnecessary now
1279 ; ------------------------------------------------------------------
1289 call os_string_to_int
1295 mov byte al, [token
]
1306 call os_string_to_int
1312 mov byte al, [token
]
1322 ; ------------------------------------------------------------------
1328 cmp ax, VARIABLE
; NEXT must be followed by a variable
1332 mov byte al, [token
]
1335 inc ax ; NEXT increments the variable, of course!
1340 mov byte al, [token
]
1343 mov si, for_variables
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?
1352 mov ax, 0 ; If not, store the updated variable
1353 mov byte al, [token
]
1356 mov ax, 0 ; Find the code point and go back
1357 mov byte al, [token
]
1359 mov si, for_code_points
1376 ; ------------------------------------------------------------------
1386 call os_string_to_int
1391 mov byte al, [token
]
1399 ; ------------------------------------------------------------------
1409 mov byte al, [token
]
1410 mov byte [.tmp_var
], al
1421 call os_string_to_int
1428 mov byte al, [.tmp_var
]
1434 mov byte al, [token
]
1446 ; ------------------------------------------------------------------
1459 call os_string_to_int
1464 mov byte [.first_value
], al
1470 mov byte al, [token
]
1473 mov byte [.first_value
], al
1485 call os_string_to_int
1490 mov byte al, [.first_value
]
1497 mov byte al, [token
]
1509 ; ------------------------------------------------------------------
1517 call os_string_compare
1521 call os_string_compare
1533 call os_string_to_int
; Now AX = port number
1547 call os_string_to_int
1548 call os_port_byte_out
1553 mov byte al, [token
]
1556 call os_port_byte_out
1566 call os_string_to_int
1573 mov byte cl, [token
]
1575 call os_port_byte_in
1590 .out_cmd
db "OUT", 0
1594 ; ------------------------------------------------------------------
1598 call get_token
; Get part after PRINT
1600 cmp ax, QUOTE
; What type is it?
1603 cmp ax, VARIABLE
; Numerical variable (eg X)
1606 cmp ax, STRING_VAR
; String variable (eg $1)
1607 je .print_string_var
1609 cmp ax, STRING
; Special keyword (eg CHR or HEX)
1612 mov si, err_print_type
; We only print quoted strings and vars!
1618 mov byte al, [token
]
1619 call get_var
; Get its value
1621 call os_int_to_string
; Convert to string
1623 call os_print_string
1628 .
print_quote: ; If it's quoted text, print it
1630 call os_print_string
1640 call os_print_string
1648 call os_string_compare
1652 call os_string_compare
1665 mov byte al, [token
]
1681 mov byte al, [token
]
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
1702 mov word [.tmp_loc
], ax
1713 jmp mainloop
; And go back to interpreting the code!
1716 call os_print_newline
1718 mov word ax, [.tmp_loc
]
1727 ; ------------------------------------------------------------------
1735 mov byte al, [token
]
1743 call os_string_to_int
1744 mov word [.num1
], ax
1751 call os_string_to_int
1752 mov word [.num2
], ax
1754 mov word ax, [.num1
]
1755 mov word bx, [.num2
]
1776 ; ------------------------------------------------------------------
1783 cmp al, 10 ; Find end of line after REM
1789 ; ------------------------------------------------------------------
1794 mov byte al, [gosub_depth
]
1802 mov si, gosub_points
1803 add si, ax ; Table is words (not bytes)
1807 dec byte [gosub_depth
]
1812 ; ------------------------------------------------------------------
1833 mov di, .tmp_filename
1845 call os_string_to_int
1848 mov word [.data_loc
], ax
1859 call os_string_to_int
1862 mov word [.data_size
], ax
1865 mov word ax, .tmp_filename
1866 mov word bx, [.data_loc
]
1867 mov word cx, [.data_size
]
1882 mov byte al, [token
]
1889 mov byte al, [token
]
1911 .tmp_filename times
15 db 0
1914 ; ------------------------------------------------------------------
1922 call os_string_compare
1926 call os_string_compare
1930 call os_string_compare
1943 call os_string_to_int
1945 je .on_cmd_slow_mode
1947 je .on_cmd_fast_mode
1953 call os_serial_port_enable
1958 call os_serial_port_enable
1974 call os_string_to_int
1975 call os_send_via_serial
1980 mov byte al, [token
]
1982 call os_send_via_serial
1991 mov byte al, [token
]
1995 call os_get_via_serial
2011 .send_cmd
db "SEND", 0
2012 .rec_cmd
db "REC", 0
2015 ; ------------------------------------------------------------------
2025 call os_string_to_int
2030 mov byte al, [token
]
2034 call os_speaker_tone
2042 call os_string_to_int
2047 mov byte al, [token
]
2057 ; ------------------------------------------------------------------
2070 mov byte al, [token
]
2074 call os_wait_for_key
2116 ; ==================================================================
2117 ; INTERNAL ROUTINES FOR INTERPRETER
2119 ; ------------------------------------------------------------------
2120 ; Get value of variable character specified in AL (eg 'A')
2131 ; ------------------------------------------------------------------
2132 ; Set value of variable character specified in AL (eg 'A')
2133 ; with number specified in BX
2137 sub al, 65 ; Remove ASCII codes before 'A'
2139 mov di, variables
; Find position in table (of words)
2147 ; ------------------------------------------------------------------
2148 ; Get token from current position in prog
2166 cmp al, 39 ; Quote mark (')
2170 je near get_string_var_token
2172 jmp get_string_token
2194 mov si, err_char_in_num
2203 mov al, 0 ; Zero-terminate the token
2206 mov ax, NUMBER
; Pass back the token type
2211 inc word [prog
] ; Move past first quote (')
2216 mov byte [token
], al
2219 cmp al, 39 ; Needs to finish with another quote
2222 mov si, err_quote_term
2234 inc word [prog
] ; Move past first quote (") char
2248 mov al, 0 ; Zero-terminate the token
2250 inc word [prog
] ; Move past final quote
2252 mov ax, QUOTE
; Pass back token type
2256 mov si, err_quote_term
2260 get_string_var_token:
2262 mov bx, 0 ; If it's a string var, pass number of string in BX
2286 mov al, 0 ; Zero-terminate the token
2290 call os_string_uppercase
2293 call os_string_length
; How long was the token?
2294 cmp ax, 1 ; If 1 char, it's a variable or delimiter
2297 mov si, token
; If the token ends with ':', it's a label
2304 mov ax, STRING
; Otherwise it's a general string of characters
2313 mov byte al, [token
]
2321 mov ax, VARIABLE
; Otherwise probably a variable
2325 ; ------------------------------------------------------------------
2326 ; Set carry flag if AL contains ASCII number
2340 ; ------------------------------------------------------------------
2341 ; Set carry flag if AL contains ASCII letter
2356 ; ------------------------------------------------------------------
2357 ; Print error message and quit out
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
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 ; ==================================================================
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
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
2404 cursor_cmd
db "CURSOR", 0
2405 curschar_cmd
db "CURSCHAR", 0
2408 gosub_cmd
db "GOSUB", 0
2409 goto_cmd
db "GOTO", 0
2410 getkey_cmd
db "GETKEY", 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
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
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 ; ------------------------------------------------------------------