diff --git a/.gitignore b/.gitignore
index 6048758..a9932ad 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*.swp
disk_images/*
tmp-loop/*
+detailed.log
diff --git a/README.md b/README.md
index 2ca1dbb..644c23e 100755
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
Kernel written in Assmebly
I'm using a modified JazzOS Bootloader
+And the MikeOS BASIC Interpreter
diff --git a/data/hello.bas b/data/hello.bas
new file mode 100644
index 0000000..2632cc6
--- /dev/null
+++ b/data/hello.bas
@@ -0,0 +1,2 @@
+PRINT "Hello World!"
+END
diff --git a/data/hello.cws b/data/hello.cws
deleted file mode 100644
index 05cc6aa..0000000
--- a/data/hello.cws
+++ /dev/null
@@ -1 +0,0 @@
-output 'Hello World'
diff --git a/log.txt b/log.txt
new file mode 100644
index 0000000..e69de29
diff --git a/source/bootload/boot.asm b/source/bootload/boot.asm
index 0ecb953..bf5b334 100644
--- a/source/bootload/boot.asm
+++ b/source/bootload/boot.asm
@@ -10,7 +10,7 @@ bdb_bytes_per_sector: dw 200h ; = 512d
bdb_sectors_per_cluster: db 01h ; sector = cluster
bdb_reserved_sectors: dw 01h
bdb_fat_count: db 02h ; We've got a fat1 and fat2
-bdb_dir_entries_count: dw 0E0h ; Maximum number of root directory entries
+bdb_dir_entries_count: dw 0E0h ; = 224d Maximum number of root directory entries
bdb_total_sectors: dw 0B40h ; = 2880d
bdb_media_descriptor_type: db 0F0h ; ignore
bdb_sectors_per_fat: dw 09h
@@ -68,7 +68,7 @@ main:
shl ax,5 ; ax *= 32 (shifting 5 times)
xor dx,dx ; Clear dx (remainder)
div word [bdb_bytes_per_sector] ;(32*num of entries)/bytes per sector = total number of sectors we need to read
- test dx,dx ; See if there's a remainder
+ test dx,dx ; See if there's a remainder, ie the root directory doesn't take up a whole number of sectors
jz root_dir_after
inc ax ; Add one if there's a remainder (this is like rounding up)
diff --git a/source/kernel/data.asm b/source/kernel/data.asm
index 93b3422..6fe51cd 100644
--- a/source/kernel/data.asm
+++ b/source/kernel/data.asm
@@ -22,20 +22,23 @@ ebr_volume_label: db 'CRAWOS0.0.6' ; must be 11 bytes
ebr_system_id: db 'FAT12 ' ; must be 8 bytes
+; String operations
fat12_file_name: db ' ' ; 11 free bytes to store a filename in
-boot_message: db 'OK] Kernel successfully loaded!\n\n', 0
+boot_message: db 'OK] Kernel successfully loaded!\n"HELP" to see a list of available commands\n', 0
user_input: times 20 db 0
prompt_length: db 20
prompt: db 'sh > ', 0
help_string: db 'HELP', 0
clear_string: db 'CLEAR', 0
reboot_string: db 'REBOOT', 0
-pong_string: db 'PONG', 0
+basic_string: db 'BAS', 0
cat_string: db 'CAT', 0
ls_string: db 'LS', 0
-help_text: db 'This is for Cowards:\n"LS" to list the directory,\n"CAT" to output the contents of a file,\n"HELP" for this helpful text,\n"CLEAR" to clear the screen,\n"REBOOT" or esc to reboot\n', 0
+help_text: db 'This is for Cowards:\n"LS" to list the directory,\n"CAT" to output the contents of a file,\n"BAS" to run a basic script,\n"HELP" for this helpful text,\n"CLEAR" to clear the screen,\n"REBOOT" or esc to reboot\n', 0
+basic_text: db 'BASIC PROGRAM BEGUN:\n', 0
command_result_text: db 'You typed: ', 0
unknown_command: db 'Error: Unkown Command..\n ', 0
+stringified_int: db 0,0,0,0,0,0 ; Can store up to 6 digits
; Disk operations
disk_read_fail: db 'Error: Could not read disk\n', 0
diff --git a/source/kernel/features/basic.asm b/source/kernel/features/basic.asm
index e69de29..10d34cf 100644
--- a/source/kernel/features/basic.asm
+++ b/source/kernel/features/basic.asm
@@ -0,0 +1,4349 @@
+; ==================================================================
+; MikeOS -- The Mike Operating System kernel
+; Copyright (C) 2006 - 2022 MikeOS Developers -- see doc/LICENSE.TXT
+;
+; BASIC CODE INTERPRETER (4.5)
+; ==================================================================
+
+; ------------------------------------------------------------------
+; Token types
+
+%DEFINE VARIABLE 1
+%DEFINE STRING_VAR 2
+%DEFINE NUMBER 3
+%DEFINE STRING 4
+%DEFINE QUOTE 5
+%DEFINE CHAR 6
+%DEFINE UNKNOWN 7
+%DEFINE LABEL 8
+
+
+; ------------------------------------------------------------------
+; The BASIC interpreter execution starts here -- a parameter string
+; is passed in SI and copied into the first string, unless SI = 0
+
+basic_run_basic:
+ mov word [orig_stack], sp ; Save stack pointer -- we might jump to the
+ ; error printing code and quit in the middle
+ ; some nested loops, and we want to preserve
+ ; the stack
+
+ mov word [load_point], ax ; AX was passed as starting location of code
+
+ mov word [prog], ax ; prog = pointer to current execution point in code
+
+ add bx, ax ; We were passed the .BAS byte size in BX
+ dec bx
+ dec bx
+ mov word [prog_end], bx ; Make note of program end point
+
+ call clear_ram ; Clear variables etc. from previous run
+ ; of a BASIC program
+
+ cmp si, 0 ; Passed a parameter string?
+ je mainloop
+
+ mov di, string_vars ; If so, copy it into $1
+ call string_copy
+
+
+
+mainloop:
+ call get_token ; Get a token from the start of the line
+ ;mov si, basic_text
+ ;call os_print_string
+
+ cmp ax, STRING ; Is the type a string of characters?
+ je .keyword ; If so, let's see if it's a keyword to process
+
+ cmp ax, VARIABLE ; If it's a variable at the start of the line,
+ je near assign ; this is an assign (eg "X = Y + 5")
+
+ cmp ax, STRING_VAR ; Same for a string variable (eg $1)
+ je near assign
+
+ cmp ax, LABEL ; Don't need to do anything here - skip
+ je mainloop
+
+ mov si, err_syntax ; Otherwise show an error and quit
+ jmp error
+
+
+.keyword:
+ mov si, token ; Start trying to match commands
+
+ mov di, alert_cmd
+ call string_direct_compare
+ jc near do_alert
+
+ mov di, askfile_cmd
+ call string_direct_compare
+ jc near do_askfile
+
+ mov di, break_cmd
+ call string_direct_compare
+ jc near do_break
+
+ mov di, case_cmd
+ call string_direct_compare
+ jc near do_case
+
+ mov di, call_cmd
+ call string_direct_compare
+ jc near do_call
+
+ mov di, cls_cmd
+ call string_direct_compare
+ jc near do_cls
+
+ mov di, cursor_cmd
+ call string_direct_compare
+ jc near do_cursor
+
+ mov di, curschar_cmd
+ call string_direct_compare
+ jc near do_curschar
+
+ mov di, curscol_cmd
+ call string_direct_compare
+ jc near do_curscol
+
+ mov di, curspos_cmd
+ call string_direct_compare
+ jc near do_curspos
+
+ mov di, delete_cmd
+ call string_direct_compare
+ jc near do_delete
+
+ mov di, do_cmd
+ call string_direct_compare
+ jc near do_do
+
+ mov di, end_cmd
+ call string_direct_compare
+ jc near do_end
+
+ mov di, else_cmd
+ call string_direct_compare
+ jc near do_else
+
+ mov di, files_cmd
+ call string_direct_compare
+ jc near do_files
+
+ mov di, for_cmd
+ call string_direct_compare
+ jc near do_for
+
+ mov di, getkey_cmd
+ call string_direct_compare
+ jc near do_getkey
+
+ mov di, gosub_cmd
+ call string_direct_compare
+ jc near do_gosub
+
+ mov di, goto_cmd
+ call string_direct_compare
+ jc near do_goto
+
+ mov di, if_cmd
+ call string_direct_compare
+ jc near do_if
+
+ mov di, include_cmd
+ call string_direct_compare
+ jc near do_include
+
+ mov di, ink_cmd
+ call string_direct_compare
+ jc near do_ink
+
+ mov di, input_cmd
+ call string_direct_compare
+ jc near do_input
+
+ mov di, len_cmd
+ call string_direct_compare
+ jc near do_len
+
+ mov di, listbox_cmd
+ call string_direct_compare
+ jc near do_listbox
+
+ mov di, load_cmd
+ call string_direct_compare
+ jc near do_load
+
+ mov di, loop_cmd
+ call string_direct_compare
+ jc near do_loop
+
+ mov di, move_cmd
+ call string_direct_compare
+ jc near do_move
+
+ mov di, next_cmd
+ call string_direct_compare
+ jc near do_next
+
+ mov di, number_cmd
+ call string_direct_compare
+ jc near do_number
+
+ mov di, page_cmd
+ call string_direct_compare
+ jc near do_page
+
+ mov di, pause_cmd
+ call string_direct_compare
+ jc near do_pause
+
+ mov di, peek_cmd
+ call string_direct_compare
+ jc near do_peek
+
+ mov di, peekint_cmd
+ call string_direct_compare
+ jc near do_peekint
+
+ mov di, poke_cmd
+ call string_direct_compare
+ jc near do_poke
+
+ mov di, pokeint_cmd
+ call string_direct_compare
+ jc near do_pokeint
+
+ mov di, port_cmd
+ call string_direct_compare
+ jc near do_port
+
+ mov di, print_cmd
+ call string_direct_compare
+ jc near do_print
+
+ mov di, rand_cmd
+ call string_direct_compare
+ jc near do_rand
+
+ mov di, read_cmd
+ call string_direct_compare
+ jc near do_read
+
+ mov di, rem_cmd
+ call string_direct_compare
+ jc near do_rem
+
+ mov di, rename_cmd
+ call string_direct_compare
+ jc near do_rename
+
+ mov di, return_cmd
+ call string_direct_compare
+ jc near do_return
+
+ mov di, save_cmd
+ call string_direct_compare
+ jc near do_save
+
+ mov di, serial_cmd
+ call string_direct_compare
+ jc near do_serial
+
+ mov di, size_cmd
+ call string_direct_compare
+ jc near do_size
+
+ mov di, sound_cmd
+ call string_direct_compare
+ jc near do_sound
+
+ mov di, string_cmd
+ call string_direct_compare
+ jc near do_string
+
+ mov di, waitkey_cmd
+ call string_direct_compare
+ jc near do_waitkey
+
+ mov si, err_cmd_unknown ; Command not found?
+ jmp error
+
+
+; ------------------------------------------------------------------
+; CLEAR RAM
+
+clear_ram:
+ pusha
+ mov al, 0
+
+ mov di, variables
+ mov cx, 52
+ rep stosb
+
+ mov di, for_variables
+ mov cx, 52
+ rep stosb
+
+ mov di, for_code_points
+ mov cx, 52
+ rep stosb
+
+ mov di, do_loop_store
+ mov cx, 10
+ rep stosb
+
+ mov byte [gosub_depth], 0
+ mov byte [loop_in], 0
+
+ mov di, gosub_points
+ mov cx, 20
+ rep stosb
+
+ mov di, string_vars
+ mov cx, 1024
+ rep stosb
+
+ mov byte [ink_colour], 7 ; White ink
+
+ popa
+ ret
+
+
+; ------------------------------------------------------------------
+; ASSIGNMENT
+
+assign:
+ cmp ax, VARIABLE ; Are we starting with a number var?
+ je .do_num_var
+
+ mov di, string_vars ; Otherwise it's a string var
+ mov ax, 128
+ mul bx ; (BX = string number, passed back from get_token)
+ add di, ax
+
+ push di
+
+ call get_token
+ mov byte al, [token]
+ cmp al, '='
+ jne near .error
+
+ call get_token ; See if second is quote
+ cmp ax, QUOTE
+ je .second_is_quote
+
+ cmp ax, STRING_VAR
+ jne near .error
+
+ mov si, string_vars ; Otherwise it's a string var
+ mov ax, 128
+ mul bx ; (BX = string number, passed back from get_token)
+ add si, ax
+
+ pop di
+ call string_copy
+
+ jmp .string_check_for_more
+
+
+.second_is_quote:
+ mov si, token
+ pop di
+ call string_copy
+
+
+.string_check_for_more:
+ push di
+ mov word ax, [prog] ; Save code location in case there's no delimiter
+ mov word [.tmp_loc], ax
+
+ call get_token ; Any more to deal with in this assignment?
+ mov byte al, [token]
+ cmp al, '+'
+ je .string_theres_more
+
+ mov word ax, [.tmp_loc] ; Not a delimiter, so step back before the token
+ mov word [prog], ax ; that we just grabbed
+
+ pop di
+ jmp mainloop ; And go back to the code interpreter!
+
+
+.string_theres_more:
+ call get_token
+ cmp ax, STRING_VAR
+ je .another_string_var
+ cmp ax, QUOTE
+ je .another_quote
+ cmp ax, VARIABLE
+ je .add_number_var
+ jmp .error
+
+
+.another_string_var:
+ pop di
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx ; (BX = string number, passed back from get_token)
+ add si, ax
+
+ mov ax, di
+ mov cx, di
+ mov bx, si
+ call string_join
+
+ jmp .string_check_for_more
+
+
+
+.another_quote:
+ pop di
+
+ mov ax, di
+ mov cx, di
+ mov bx, token
+ call string_join
+
+ jmp .string_check_for_more
+
+
+.add_number_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ call string_cast_from_int
+
+ mov bx, ax
+ pop di
+ mov ax, di
+ mov cx, di
+ call string_join
+
+ jmp .string_check_for_more
+
+
+
+.do_num_var:
+ mov ax, 0
+ mov byte al, [token]
+ mov byte [.tmp], al
+
+ call get_token
+ mov byte al, [token]
+ cmp al, '='
+ jne near .error
+
+ call get_token
+ cmp ax, NUMBER
+ je .second_is_num
+
+ cmp ax, VARIABLE
+ je .second_is_variable
+
+ cmp ax, STRING
+ je near .second_is_string
+
+ cmp ax, UNKNOWN
+ jne near .error
+
+ mov byte al, [token] ; Address of string var?
+ cmp al, '&'
+ jne near .error
+
+ call get_token ; Let's see if there's a string var
+ cmp ax, STRING_VAR
+ jne near .error
+
+ mov di, string_vars
+ mov ax, 128
+ mul bx
+ add di, ax
+
+ mov bx, di
+
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp mainloop
+
+
+.second_is_variable:
+ mov ax, 0
+ mov byte al, [token]
+
+ call get_var
+ mov bx, ax
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp .check_for_more
+
+
+.second_is_num:
+ mov si, token
+ call string_cast_to_int
+
+ mov bx, ax ; Number to insert in variable table
+
+ mov ax, 0
+ mov byte al, [.tmp]
+
+ call set_var
+
+
+ ; The assignment could be simply "X = 5" etc. Or it could be
+ ; "X = Y + 5" -- ie more complicated. So here we check to see if
+ ; there's a delimiter...
+
+.check_for_more:
+ mov word ax, [prog] ; Save code location in case there's no delimiter
+ mov word [.tmp_loc], ax
+
+ call get_token ; Any more to deal with in this assignment?
+ mov byte al, [token]
+ cmp al, '+'
+ je .theres_more
+ cmp al, '-'
+ je .theres_more
+ cmp al, '*'
+ je .theres_more
+ cmp al, '/'
+ je .theres_more
+ cmp al, '%'
+ je .theres_more
+
+ mov word ax, [.tmp_loc] ; Not a delimiter, so step back before the token
+ mov word [prog], ax ; that we just grabbed
+
+ jmp mainloop ; And go back to the code interpreter!
+
+
+.theres_more:
+ mov byte [.delim], al
+
+ call get_token
+ cmp ax, VARIABLE
+ je .handle_variable
+
+ mov si, token
+ call string_cast_to_int
+ mov bx, ax
+
+ mov ax, 0
+ mov byte al, [.tmp]
+
+ call get_var ; This also points SI at right place in variable table
+
+ cmp byte [.delim], '+'
+ jne .not_plus
+
+ add ax, bx
+ jmp .finish
+
+.not_plus:
+ cmp byte [.delim], '-'
+ jne .not_minus
+
+ sub ax, bx
+ jmp .finish
+
+.not_minus:
+ cmp byte [.delim], '*'
+ jne .not_times
+
+ mul bx
+ jmp .finish
+
+.not_times:
+ cmp byte [.delim], '/'
+ jne .not_divide
+
+ cmp bx, 0
+ je .divide_zero
+
+ mov dx, 0
+ div bx
+ jmp .finish
+
+.not_divide:
+ mov dx, 0
+ div bx
+ mov ax, dx ; Get remainder
+
+.finish:
+ mov bx, ax
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp .check_for_more
+
+.divide_zero:
+ mov si, err_divide_by_zero
+ jmp error
+
+.handle_variable:
+ mov ax, 0
+ mov byte al, [token]
+
+ call get_var
+
+ mov bx, ax
+
+ mov ax, 0
+ mov byte al, [.tmp]
+
+ call get_var
+
+ cmp byte [.delim], '+'
+ jne .vnot_plus
+
+ add ax, bx
+ jmp .vfinish
+
+.vnot_plus:
+ cmp byte [.delim], '-'
+ jne .vnot_minus
+
+ sub ax, bx
+ jmp .vfinish
+
+.vnot_minus:
+ cmp byte [.delim], '*'
+ jne .vnot_times
+
+ mul bx
+ jmp .vfinish
+
+.vnot_times:
+ cmp byte [.delim], '/'
+ jne .vnot_divide
+
+ mov dx, 0
+ div bx
+ jmp .finish
+
+.vnot_divide:
+ mov dx, 0
+ div bx
+ mov ax, dx ; Get remainder
+
+.vfinish:
+ mov bx, ax
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp .check_for_more
+
+
+.second_is_string: ; These are "X = word" functions
+ mov di, token
+
+ mov si, ink_keyword
+ call string_direct_compare
+ je .is_ink
+
+ mov si, progstart_keyword
+ call string_direct_compare
+ je .is_progstart
+
+ mov si, ramstart_keyword
+ call string_direct_compare
+ je .is_ramstart
+
+ mov si, timer_keyword
+ call string_direct_compare
+ je .is_timer
+
+ mov si, variables_keyword
+ call string_direct_compare
+ je .is_variables
+
+ mov si, version_keyword
+ call string_direct_compare
+ je .is_version
+
+ jmp .error
+
+
+.is_ink:
+ mov ax, 0
+ mov byte al, [.tmp]
+
+ mov bx, 0
+ mov byte bl, [ink_colour]
+ call set_var
+
+ jmp mainloop
+
+
+.is_progstart:
+ mov ax, 0
+ mov byte al, [.tmp]
+
+ mov word bx, [load_point]
+ call set_var
+
+ jmp mainloop
+
+
+.is_ramstart:
+ mov ax, 0
+ mov byte al, [.tmp]
+
+ mov word bx, [prog_end]
+ inc bx
+ inc bx
+ inc bx
+ call set_var
+
+ jmp mainloop
+
+
+.is_timer:
+ mov ah, 0
+ int 1Ah
+ mov bx, dx
+
+ mov ax, 0
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp mainloop
+
+
+.is_variables:
+ mov bx, vars_loc
+ mov ax, 0
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp mainloop
+
+
+.is_version:
+ call misc_get_api_version
+
+ mov bh, 0
+ mov bl, al
+ mov al, [.tmp]
+ call set_var
+
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .tmp db 0
+ .tmp_loc dw 0
+ .delim db 0
+
+
+; ==================================================================
+; SPECIFIC COMMAND CODE STARTS HERE
+
+; ------------------------------------------------------------------
+; ALERT
+
+do_alert:
+ mov bh, [work_page] ; Store the cursor position
+ mov ah, 03h
+ int 10h
+
+ call get_token
+
+ cmp ax, QUOTE
+ je .is_quote
+
+ cmp ax, STRING_VAR
+ je .is_string
+
+ mov si, err_syntax
+ jmp error
+
+.is_string:
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add ax, si
+ jmp .display_message
+
+.is_quote:
+ mov ax, token ; First string for alert box
+
+.display_message:
+ mov bx, 0 ; Others are blank
+ mov cx, 0
+ mov dx, 0 ; One-choice box
+ call graphics_dialogue_box
+
+ mov bh, [work_page] ; Move the cursor back
+ mov ah, 02h
+ int 10h
+
+ jmp mainloop
+
+
+;-------------------------------------------------------------------
+; ASKFILE
+
+do_askfile:
+ mov bh, [work_page] ; Store the cursor position
+ mov ah, 03h
+ int 10h
+
+ call get_token
+
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars ; Get the string location
+ mov ax, 128
+ mul bx
+ add ax, si
+ mov word [.tmp], ax
+
+ call graphics_file_selector ; Present the selector
+
+ mov word di, [.tmp] ; Copy the string
+ mov si, ax
+ call string_copy
+
+ mov bh, [work_page] ; Move the cursor back
+ mov ah, 02h
+ int 10h
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+.data:
+ .tmp dw 0
+
+
+; ------------------------------------------------------------------
+; BREAK
+
+do_break:
+ mov si, err_break
+ jmp error
+
+
+; ------------------------------------------------------------------
+; CALL
+
+do_call:
+ call get_token
+ cmp ax, NUMBER
+ je .is_number
+
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ jmp .execute_call
+
+.is_number:
+ mov si, token
+ call string_cast_to_int
+
+.execute_call:
+ mov bx, 0
+ mov cx, 0
+ mov dx, 0
+ mov di, 0
+ mov si, 0
+
+ call ax
+
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; CASE
+
+do_case:
+ call get_token
+ cmp ax, STRING
+ jne .error
+
+ mov si, token
+
+ mov di, upper_keyword
+ call string_direct_compare
+ jc .uppercase
+
+ mov di, lower_keyword
+ call string_direct_compare
+ jc .lowercase
+
+ jmp .error
+
+.uppercase:
+ call get_token
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add ax, si
+
+ call string_upper_case
+
+ jmp mainloop
+
+.lowercase:
+ call get_token
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add ax, si
+
+ call string_lower_case
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; CLS
+
+do_cls:
+ mov ah, 5
+ mov byte al, [work_page]
+ int 10h
+
+ call os_set_text_mode
+
+ mov ah, 5
+ mov byte al, [disp_page]
+ int 10h
+
+ jmp mainloop
+
+
+
+; ------------------------------------------------------------------
+; CURSOR
+
+do_cursor:
+ call get_token
+
+ mov si, token
+ mov di, .on_str
+ call string_direct_compare
+ jc .turn_on
+
+ mov si, token
+ mov di, .off_str
+ call string_direct_compare
+ jc .turn_off
+
+ mov si, err_syntax
+ jmp error
+
+.turn_on:
+ call keyboard_show_cursor
+ jmp mainloop
+
+.turn_off:
+ call keyboard_hide_cursor
+ jmp mainloop
+
+
+ .on_str db "ON", 0
+ .off_str db "OFF", 0
+
+
+; ------------------------------------------------------------------
+; CURSCHAR
+
+do_curschar:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .is_variable
+
+ mov si, err_syntax
+ jmp error
+
+.is_variable:
+ mov ax, 0
+ mov byte al, [token]
+
+ push ax ; Store variable we're going to use
+
+ mov ah, 08h
+ mov bx, 0
+ mov byte bh, [work_page]
+ int 10h ; Get char at current cursor location
+
+ mov bx, 0 ; We only want the lower byte (the char, not attribute)
+ mov bl, al
+
+ pop ax ; Get the variable back
+
+ call set_var ; And store the value
+
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; CURSCOL
+
+do_curscol:
+ call get_token
+
+ cmp ax, VARIABLE
+ jne .error
+
+ mov ah, 0
+ mov byte al, [token]
+ push ax
+
+ mov ah, 8
+ mov bx, 0
+ mov byte bh, [work_page]
+ int 10h
+ mov bh, 0
+ mov bl, ah ; Get colour for higher byte; ignore lower byte (char)
+
+ pop ax
+ call set_var
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; CURSPOS
+
+do_curspos:
+ mov byte bh, [work_page]
+ mov ah, 3
+ int 10h
+
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov ah, 0 ; Get the column in the first variable
+ mov byte al, [token]
+ mov bx, 0
+ mov bl, dl
+ call set_var
+
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov ah, 0 ; Get the row to the second
+ mov byte al, [token]
+ mov bx, 0
+ mov bl, dh
+ call set_var
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; DELETE
+
+do_delete:
+ call get_token
+ cmp ax, QUOTE
+ je .is_quote
+
+ cmp ax, STRING_VAR
+ jne near .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+ jmp .get_filename
+
+.is_quote:
+ mov si, token
+
+.get_filename:
+ mov ax, si
+ call disk_file_exists
+ jc .no_file
+
+ call disk_remove_file
+ jc .del_fail
+
+ jmp .returngood
+
+.no_file:
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 2
+ call set_var
+ jmp mainloop
+
+.returngood:
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 0
+ call set_var
+ jmp mainloop
+
+.del_fail:
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 1
+ call set_var
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; DO
+
+do_do:
+ cmp byte [loop_in], 20
+ je .loop_max
+ mov word di, do_loop_store
+ mov byte al, [loop_in]
+ mov ah, 0
+ add di, ax
+ mov word ax, [prog]
+ sub ax, 3
+ stosw
+ inc byte [loop_in]
+ inc byte [loop_in]
+ jmp mainloop
+
+.loop_max:
+ mov si, err_doloop_maximum
+ jmp error
+
+
+;-------------------------------------------------------------------
+; ELSE
+
+do_else:
+ cmp byte [last_if_true], 1
+ je .last_true
+
+ inc word [prog]
+ jmp mainloop
+
+.last_true:
+ mov word si, [prog]
+
+.next_line:
+ lodsb
+ cmp al, 10
+ jne .next_line
+
+ dec si
+ mov word [prog], si
+
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; END
+
+do_end:
+ mov ah, 5 ; Restore active page
+ mov al, 0
+ int 10h
+
+ mov byte [work_page], 0
+ mov byte [disp_page], 0
+
+ mov word sp, [orig_stack]
+ ret
+
+
+; ------------------------------------------------------------------
+; FILES
+
+do_files:
+ mov ax, .filelist ; get a copy of the filelist
+ call disk_file_list
+
+ mov si, ax
+
+ call keyboard_get_cursor_pos ; move cursor to start of line
+ mov dl, 0
+ call keyboard_move_cursor
+
+ mov ah, 9 ; print character function
+ mov bh, [work_page] ; define parameters (page, colour, times)
+ mov bl, [ink_colour]
+ mov cx, 1
+.file_list_loop:
+ lodsb ; get a byte from the list
+ cmp al, ',' ; a comma means the next file, so create a new line for it
+ je .nextfile
+
+ cmp al, 0 ; the list is null terminated
+ je .end_of_list
+
+ int 10h ; okay, it's not a comma or a null so print it
+
+ call keyboard_get_cursor_pos ; find the location of the cursor
+ inc dl ; move the cursor forward
+ call keyboard_move_cursor
+
+ jmp .file_list_loop ; keep going until the list is finished
+
+.nextfile:
+ call keyboard_get_cursor_pos ; if the column is over 60 we need a new line
+ cmp dl, 60
+ jge .newline
+
+.next_column: ; print spaces until the next column
+ mov al, ' '
+ int 10h
+
+ inc dl
+ call keyboard_move_cursor
+
+ cmp dl, 15
+ je .file_list_loop
+
+ cmp dl, 30
+ je .file_list_loop
+
+ cmp dl, 45
+ je .file_list_loop
+
+ cmp dl, 60
+ je .file_list_loop
+
+ jmp .next_column
+
+.newline:
+ call os_print_newline ; create a new line
+ jmp .file_list_loop
+
+.end_of_list:
+ call os_print_newline
+ jmp mainloop ; preform next command
+
+.data:
+ .filelist times 256 db 0
+
+
+; ------------------------------------------------------------------
+; FOR
+
+do_for:
+ call get_token ; Get the variable we're using in this loop
+
+ cmp ax, VARIABLE
+ jne near .error
+
+ mov ax, 0
+ mov byte al, [token]
+ mov byte [.tmp_var], al ; Store it in a temporary location for now
+
+ call get_token
+
+ mov ax, 0 ; Check it's followed up with '='
+ mov byte al, [token]
+ cmp al, '='
+ jne .error
+
+ call get_token ; Next we want a number
+
+ cmp ax, VARIABLE
+ je .first_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token ; Convert it
+ call string_cast_to_int
+ jmp .continue
+
+.first_is_var:
+ mov ax, 0 ; It's a variable, so get it's value
+ mov al, [token]
+ call get_var
+
+ ; At this stage, we've read something like "FOR X = 1"
+ ; so let's store that 1 in the variable table
+
+.continue:
+ mov bx, ax
+ mov ax, 0
+ mov byte al, [.tmp_var]
+ call set_var
+
+
+ call get_token ; Next we're looking for "TO"
+
+ cmp ax, STRING
+ jne .error
+
+ mov ax, token
+ call string_upper_case
+
+ mov si, token
+ mov di, .to_string
+ call string_direct_compare
+ jnc .error
+
+
+ ; So now we're at "FOR X = 1 TO"
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+.second_is_number:
+ mov si, token ; Get target number
+ call string_cast_to_int
+ jmp .continue2
+
+.second_is_var:
+ mov ax, 0 ; It's a variable, so get it's value
+ mov al, [token]
+ call get_var
+
+.continue2:
+ mov bx, ax
+
+ mov ax, 0
+ mov byte al, [.tmp_var]
+
+ sub al, 65 ; Store target number in table
+ mov di, for_variables
+ add di, ax
+ add di, ax
+ mov ax, bx
+ stosw
+
+
+ ; So we've got the variable, assigned it the starting number, and put into
+ ; our table the limit it should reach. But we also need to store the point in
+ ; code after the FOR line we should return to if NEXT X doesn't complete the loop...
+
+ mov ax, 0
+ mov byte al, [.tmp_var]
+
+ sub al, 65 ; Store code position to return to in table
+ mov di, for_code_points
+ add di, ax
+ add di, ax
+ mov word ax, [prog]
+ stosw
+
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .tmp_var db 0
+ .to_string db 'TO', 0
+
+
+; ------------------------------------------------------------------
+; GETKEY
+
+do_getkey:
+ call get_token
+ cmp ax, VARIABLE
+ je .is_variable
+
+ mov si, err_syntax
+ jmp error
+
+.is_variable:
+ mov ax, 0
+ mov byte al, [token]
+
+ push ax
+
+ call keyboard_check_key
+
+ cmp ax, 48E0h
+ je .up_pressed
+
+ cmp ax, 50E0h
+ je .down_pressed
+
+ cmp ax, 4BE0h
+ je .left_pressed
+
+ cmp ax, 4DE0h
+ je .right_pressed
+
+.store:
+ mov bx, 0
+ mov bl, al
+
+ pop ax
+
+ call set_var
+
+ jmp mainloop
+
+.up_pressed:
+ mov ax, 1
+ jmp .store
+
+.down_pressed:
+ mov ax, 2
+ jmp .store
+
+.left_pressed:
+ mov ax, 3
+ jmp .store
+
+.right_pressed:
+ mov ax, 4
+ jmp .store
+
+; ------------------------------------------------------------------
+; GOSUB
+
+do_gosub:
+ call get_token ; Get the number (label)
+
+ cmp ax, STRING
+ je .is_ok
+
+ mov si, err_goto_notlabel
+ jmp error
+
+.is_ok:
+ mov si, token ; Back up this label
+ mov di, .tmp_token
+ call string_copy
+
+ mov ax, .tmp_token
+ call string_length
+
+ mov di, .tmp_token ; Add ':' char to end for searching
+ add di, ax
+ mov al, ':'
+ stosb
+ mov al, 0
+ stosb
+
+
+ inc byte [gosub_depth]
+
+ mov ax, 0
+ mov byte al, [gosub_depth] ; Get current GOSUB nest level
+
+ cmp al, 9
+ jle .within_limit
+
+ mov si, err_nest_limit
+ jmp error
+
+
+.within_limit:
+ mov di, gosub_points ; Move into our table of pointers
+ add di, ax ; Table is words (not bytes)
+ add di, ax
+ mov word ax, [prog]
+ stosw ; Store current location before jump
+
+
+ mov word ax, [load_point]
+ mov word [prog], ax ; Return to start of program to find label
+
+.loop:
+ call get_token
+
+ cmp ax, LABEL
+ jne .line_loop
+
+ mov si, token
+ mov di, .tmp_token
+ call string_direct_compare
+ jc mainloop
+
+
+.line_loop: ; Go to end of line
+ mov word si, [prog]
+ mov byte al, [si]
+ inc word [prog]
+ cmp al, 10
+ jne .line_loop
+
+ mov word ax, [prog]
+ mov word bx, [prog_end]
+ cmp ax, bx
+ jg .past_end
+
+ jmp .loop
+
+
+.past_end:
+ mov si, err_label_notfound
+ jmp error
+
+
+ .tmp_token times 30 db 0
+
+
+; ------------------------------------------------------------------
+; GOTO
+
+do_goto:
+ call get_token ; Get the next token
+
+ cmp ax, STRING
+ je .is_ok
+
+ mov si, err_goto_notlabel
+ jmp error
+
+.is_ok:
+ mov si, token ; Back up this label
+ mov di, .tmp_token
+ call string_copy
+
+ mov ax, .tmp_token
+ call string_length
+
+ mov di, .tmp_token ; Add ':' char to end for searching
+ add di, ax
+ mov al, ':'
+ stosb
+ mov al, 0
+ stosb
+
+ mov word ax, [load_point]
+ mov word [prog], ax ; Return to start of program to find label
+
+.loop:
+ call get_token
+
+ cmp ax, LABEL
+ jne .line_loop
+
+ mov si, token
+ mov di, .tmp_token
+ call string_direct_compare
+ jc mainloop
+
+.line_loop: ; Go to end of line
+ mov word si, [prog]
+ mov byte al, [si]
+ inc word [prog]
+
+ cmp al, 10
+ jne .line_loop
+
+ mov word ax, [prog]
+ mov word bx, [prog_end]
+ cmp ax, bx
+ jg .past_end
+
+ jmp .loop
+
+.past_end:
+ mov si, err_label_notfound
+ jmp error
+
+
+ .tmp_token times 30 db 0
+
+
+; ------------------------------------------------------------------
+; IF
+
+do_if:
+ call get_token
+
+ cmp ax, VARIABLE ; If can only be followed by a variable
+ je .num_var
+
+ cmp ax, STRING_VAR
+ je near .string_var
+
+ mov si, err_syntax
+ jmp error
+
+.num_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+ mov dx, ax ; Store value of first part of comparison
+
+ call get_token ; Get the delimiter
+ mov byte al, [token]
+ cmp al, '='
+ je .equals
+ cmp al, '>'
+ je .greater
+ cmp al, '<'
+ je .less
+ cmp al, '!'
+ je .not_equals
+
+ mov si, err_syntax ; If not one of the above, error out
+ jmp error
+
+.equals:
+ call get_token ; Is this 'X = Y' (equals another variable?)
+
+ cmp ax, CHAR
+ je .equals_char
+
+ mov byte al, [token]
+ call is_letter
+ jc .equals_var
+
+ mov si, token ; Otherwise it's, eg 'X = 1' (a number)
+ call string_cast_to_int
+
+ cmp ax, dx ; On to the THEN bit if 'X = num' matches
+ je near .on_to_then
+
+ jmp .finish_line ; Otherwise skip the rest of the line
+
+
+.equals_char:
+ mov ax, 0
+ mov byte al, [token]
+
+ cmp ax, dx
+ je near .on_to_then
+
+ jmp .finish_line
+
+
+.equals_var:
+ mov ax, 0
+ mov byte al, [token]
+
+ call get_var
+
+ cmp ax, dx ; Do the variables match?
+ je near .on_to_then ; On to the THEN bit if so
+
+ jmp .finish_line ; Otherwise skip the rest of the line
+
+
+.not_equals:
+ mov byte al, [token + 1]
+ cmp al, '='
+ jne .error
+
+ call get_token
+
+ cmp ax, CHAR
+ je .not_equals_char
+
+ cmp ax, VARIABLE
+ je .not_equals_var
+
+ cmp ax, NUMBER
+ je .not_equals_number
+
+ mov si, err_syntax
+ jmp error
+
+.not_equals_char:
+ mov ax, 0
+ mov byte al, [token]
+
+ cmp ax, dx
+ jne near .on_to_then
+
+ jmp .finish_line
+
+.not_equals_var:
+ mov ax, 0
+ mov byte al, [token]
+
+ call get_var
+
+ cmp ax, dx
+ jne near .on_to_then
+
+ jmp .finish_line
+
+.not_equals_number:
+ mov si, token
+ call string_cast_to_int
+
+ cmp ax, dx
+ jne near .on_to_then
+
+ jmp .finish_line
+
+
+.greater:
+ call get_token ; Greater than a variable or number?
+ mov byte al, [token]
+ call is_letter
+ jc .greater_var
+
+ mov si, token ; Must be a number here...
+ call string_cast_to_int
+
+ cmp ax, dx
+ jl near .on_to_then
+
+ jmp .finish_line
+
+.greater_var: ; Variable in this case
+ mov ax, 0
+ mov byte al, [token]
+
+ call get_var
+
+ cmp ax, dx ; Make the comparison!
+ jl .on_to_then
+
+ jmp .finish_line
+
+.less:
+ call get_token
+ mov byte al, [token]
+ call is_letter
+ jc .less_var
+
+ mov si, token
+ call string_cast_to_int
+
+ cmp ax, dx
+ jg .on_to_then
+
+ jmp .finish_line
+
+.less_var:
+ mov ax, 0
+ mov byte al, [token]
+
+ call get_var
+
+ cmp ax, dx
+ jg .on_to_then
+
+ jmp .finish_line
+
+
+
+.string_var:
+ mov byte [.tmp_string_var], bl
+
+ call get_token
+
+ mov byte al, [token]
+ mov byte [.tmp_string_op], al
+
+ cmp al, '='
+ je .get_second
+
+ cmp al, '!'
+ jne .error
+
+ mov byte al, [token + 1]
+
+ cmp al, '='
+ jne .error
+
+.get_second:
+ call get_token
+ cmp ax, STRING_VAR
+ je .second_is_string_var
+
+ cmp ax, QUOTE
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+ mov di, token
+
+ mov al, [.tmp_string_op]
+
+ cmp al, '='
+ je .string_equals
+
+ cmp al, '!'
+ je .string_not_equals
+
+ jmp .error
+
+
+.second_is_string_var:
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ mov di, string_vars
+ mov bx, 0
+ mov byte bl, [.tmp_string_var]
+ mov ax, 128
+ mul bx
+ add di, ax
+
+ mov al, [.tmp_string_op]
+
+ cmp al, '!'
+ je .string_not_equals
+
+.string_equals:
+ call string_direct_compare
+ jc .on_to_then
+
+ jmp .finish_line
+
+.string_not_equals:
+ call string_direct_compare
+ jnc .on_to_then
+
+ jmp .finish_line
+
+.on_to_then:
+ call get_token
+
+ mov si, token ; Look for AND for more comparison
+ mov di, and_keyword
+ call string_direct_compare
+ jc do_if
+
+ mov si, token ; Look for THEN to perform more operations
+ mov di, then_keyword
+ call string_direct_compare
+ jc .then_present
+
+ mov si, err_syntax
+ jmp error
+
+.then_present: ; Continue rest of line like any other command!
+ mov byte [last_if_true], 1
+ jmp mainloop
+
+
+.finish_line: ; IF wasn't fulfilled, so skip rest of line
+ mov word si, [prog]
+ mov byte al, [si]
+ inc word [prog]
+ cmp al, 10
+ jne .finish_line
+
+ mov byte [last_if_true], 0
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .tmp_string_var db 0
+ .tmp_string_op db 0
+
+
+; ------------------------------------------------------------------
+; INCLUDE
+
+do_include:
+ call get_token
+ cmp ax, QUOTE
+ je .is_ok
+
+ mov si, err_syntax
+ jmp error
+
+.is_ok:
+ mov ax, token
+ mov word cx, [prog_end]
+ inc cx ; Add a bit of space after original code
+ inc cx
+ inc cx
+ push cx
+ call disk_load_file
+ jc .load_fail
+
+ pop cx
+ add cx, bx
+ mov word [prog_end], cx
+
+ jmp mainloop
+
+
+.load_fail:
+ pop cx
+ mov si, err_file_notfound
+ jmp error
+
+
+; ------------------------------------------------------------------
+; INK
+
+do_ink:
+ call get_token ; Get column
+
+ cmp ax, VARIABLE
+ je .first_is_var
+
+ mov si, token
+ call string_cast_to_int
+ mov byte [ink_colour], al
+ jmp mainloop
+
+.first_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ mov byte [ink_colour], al
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; INPUT
+
+do_input:
+ mov byte [.tmpstring], 0
+
+ call get_token
+
+ cmp ax, VARIABLE ; We can only INPUT to variables!
+ je .number_var
+
+ cmp ax, STRING_VAR
+ je .string_var
+
+ mov si, err_syntax
+ jmp error
+
+.number_var:
+ mov ax, .tmpstring ; Get input from the user
+ mov bx, 6
+ call string_input
+
+ mov ax, .tmpstring
+ call string_length
+ cmp ax, 0
+ jne .char_entered
+
+ mov byte [.tmpstring], '0' ; If enter hit, fill variable with zero
+ mov byte [.tmpstring + 1], 0
+
+.char_entered:
+ mov si, .tmpstring ; Convert to integer format
+ call string_cast_to_int
+ mov bx, ax
+
+ mov ax, 0
+ mov byte al, [token] ; Get the variable where we're storing it...
+ call set_var ; ...and store it!
+
+ call os_print_newline
+
+ jmp mainloop
+
+
+.string_var:
+ mov ax, 128
+ mul bx
+ add ax, string_vars
+
+ mov bx, 128
+ call string_input
+
+ call os_print_newline
+
+ jmp mainloop
+
+
+ .tmpstring times 6 db 0
+
+
+; -----------------------------------------------------------
+; LEN
+
+do_len:
+ call get_token
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ mov ax, si
+ call string_length
+ mov word [.num1], ax
+
+ call get_token
+ cmp ax, VARIABLE
+ je .is_ok
+
+ mov si, err_syntax
+ jmp error
+
+.is_ok:
+ mov ax, 0
+ mov byte al, [token]
+ mov bl, al
+ jmp .finish
+
+.finish:
+ mov bx, [.num1]
+ mov byte al, [token]
+ call set_var
+ mov ax, 0
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .num1 dw 0
+
+
+; ------------------------------------------------------------------
+; LISTBOX
+
+do_listbox:
+ mov bh, [work_page] ; Store the cursor position
+ mov ah, 03h
+ int 10h
+
+ call get_token
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ mov word [.s1], si
+
+ call get_token
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ mov word [.s2], si
+
+ call get_token
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ mov word [.s3], si
+
+
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov byte al, [token]
+ mov byte [.var], al
+
+ mov word ax, [.s1]
+ mov word bx, [.s2]
+ mov word cx, [.s3]
+
+ call graphics_list_dialogue
+ jc .esc_pressed
+
+ pusha
+ mov bh, [work_page] ; Move the cursor back
+ mov ah, 02h
+ int 10h
+ popa
+
+ mov bx, ax
+ mov ax, 0
+ mov byte al, [.var]
+ call set_var
+
+ jmp mainloop
+
+
+.esc_pressed:
+ mov ax, 0
+ mov byte al, [.var]
+ mov bx, 0
+ call set_var
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+ .s1 dw 0
+ .s2 dw 0
+ .s3 dw 0
+ .var db 0
+
+
+; ------------------------------------------------------------------
+; LOAD
+
+do_load:
+ call get_token
+ cmp ax, QUOTE
+ je .is_quote
+
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+ jmp .get_position
+
+.is_quote:
+ mov si, token
+
+.get_position:
+ mov ax, si
+ call disk_file_exists
+ jc .file_not_exists
+
+ mov dx, ax ; Store for now
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+
+.load_part:
+ mov cx, ax
+
+ mov ax, dx
+
+ call disk_load_file
+
+ mov ax, 0
+ mov byte al, 'S'
+ call set_var
+
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 0
+ call set_var
+
+ jmp mainloop
+
+
+.second_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ jmp .load_part
+
+
+.file_not_exists:
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 1
+ call set_var
+
+ call get_token ; Skip past the loading point -- unnecessary now
+
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; LOOP
+
+do_loop:
+ cmp byte [loop_in], 0
+ je .no_do
+
+ dec byte [loop_in]
+ dec byte [loop_in]
+
+ mov dx, 0
+
+ call get_token
+ mov di, token
+
+ mov si, .endless_word
+ call string_direct_compare
+ jc .loop_back
+
+ mov si, .while_word
+ call string_direct_compare
+ jc .while_set
+
+ mov si, .until_word
+ call string_direct_compare
+ jnc .error
+
+.get_first_var:
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov al, [token]
+ call get_var
+ mov cx, ax
+
+.check_equals:
+ call get_token
+ cmp ax, UNKNOWN
+ jne .error
+
+ mov ax, [token]
+ cmp al, '='
+ je .sign_ok
+ cmp al, '>'
+ je .sign_ok
+ cmp al, '<'
+ je .sign_ok
+ jmp .error
+ .sign_ok:
+ mov byte [.sign], al
+
+.get_second_var:
+ call get_token
+
+ cmp ax, NUMBER
+ je .second_is_num
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ cmp ax, CHAR
+ jne .error
+
+.second_is_char:
+ mov ah, 0
+ mov al, [token]
+ jmp .check_true
+
+.second_is_var:
+ mov al, [token]
+ call get_var
+ jmp .check_true
+
+.second_is_num:
+ mov si, token
+ call string_cast_to_int
+
+.check_true:
+ mov byte bl, [.sign]
+ cmp bl, '='
+ je .sign_equals
+
+ cmp bl, '>'
+ je .sign_greater
+
+ jmp .sign_lesser
+
+.sign_equals:
+ cmp ax, cx
+ jne .false
+ jmp .true
+
+.sign_greater:
+ cmp ax, cx
+ jge .false
+ jmp .true
+
+.sign_lesser:
+ cmp ax, cx
+ jle .false
+ jmp .true
+.true:
+ cmp dx, 1
+ je .loop_back
+ jmp mainloop
+.false:
+ cmp dx, 1
+ je mainloop
+
+.loop_back:
+ mov word si, do_loop_store
+ mov byte al, [loop_in]
+ mov ah, 0
+ add si, ax
+ lodsw
+ mov word [prog], ax
+ jmp mainloop
+
+.while_set:
+ mov dx, 1
+ jmp .get_first_var
+
+.no_do:
+ mov si, err_loop
+ jmp error
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+.data:
+ .while_word db "WHILE", 0
+ .until_word db "UNTIL", 0
+ .endless_word db "ENDLESS", 0
+ .sign db 0
+
+
+; ------------------------------------------------------------------
+; MOVE
+
+do_move:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .first_is_var
+
+ mov si, token
+ call string_cast_to_int
+ mov dl, al
+ jmp .onto_second
+
+.first_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ mov dl, al
+
+.onto_second:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ mov si, token
+ call string_cast_to_int
+ mov dh, al
+ jmp .finish
+
+.second_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ mov dh, al
+
+.finish:
+ mov byte bh, [work_page]
+ mov ah, 2
+ int 10h
+
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; NEXT
+
+do_next:
+ call get_token
+
+ cmp ax, VARIABLE ; NEXT must be followed by a variable
+ jne .error
+
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+ inc ax ; NEXT increments the variable, of course!
+
+ mov bx, ax
+
+ mov ax, 0
+ mov byte al, [token]
+
+ sub al, 65
+ mov si, for_variables
+ add si, ax
+ add si, ax
+ lodsw ; Get the target number from the table
+
+ inc ax ; (Make the loop inclusive of target number)
+ cmp ax, bx ; Do the variable and target match?
+ je .loop_finished
+
+ mov ax, 0 ; If not, store the updated variable
+ mov byte al, [token]
+ call set_var
+
+ mov ax, 0 ; Find the code point and go back
+ mov byte al, [token]
+ sub al, 65
+ mov si, for_code_points
+ add si, ax
+ add si, ax
+ lodsw
+
+ mov word [prog], ax
+ jmp mainloop
+
+
+.loop_finished:
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+
+;-------------------------------------------------------------------
+; NUMBER
+
+do_number:
+ call get_token ; Check if it's string to number, or number to string
+
+ cmp ax, STRING_VAR
+ je .is_string
+
+ cmp ax, VARIABLE
+ je .is_variable
+
+ jmp .error
+
+.is_string:
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+ mov [.tmp], si
+
+ call get_token
+
+ mov si, [.tmp]
+
+ cmp ax, VARIABLE
+ jne .error
+
+ call string_cast_to_int
+ mov bx, ax
+
+ mov ax, 0
+ mov byte al, [token]
+ call set_var
+
+ jmp mainloop
+
+.is_variable:
+
+ mov ax, 0 ; Get the value of the number
+ mov byte al, [token]
+ call get_var
+
+ call string_cast_from_int ; Convert to a string
+ mov [.tmp], ax
+
+ call get_token ; Get the second parameter
+
+ mov si, [.tmp]
+
+ cmp ax, STRING_VAR ; Make sure it's a string variable
+ jne .error
+
+ mov di, string_vars ; Locate string variable
+ mov ax, 128
+ mul bx
+ add di, ax
+
+ call string_copy ; Save converted string
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .tmp dw 0
+
+
+;-------------------------------------------------------------------
+; PAGE
+
+do_page:
+ call get_token
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+ mov byte [work_page], al ; Set work page variable
+
+ call get_token
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+ mov byte [disp_page], al ; Set display page variable
+
+ ; Change display page -- AL should already be present from the string_cast_to_int
+ mov ah, 5
+ int 10h
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; PAUSE
+
+do_pause:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .is_var
+
+ mov si, token
+ call string_cast_to_int
+ jmp .finish
+
+.is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+.finish:
+ call misc_pause
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; PEEK
+
+do_peek:
+ call get_token
+
+ cmp ax, VARIABLE
+ jne .error
+
+ mov ax, 0
+ mov byte al, [token]
+ mov byte [.tmp_var], al
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .dereference
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+
+.store:
+ mov si, ax
+ mov bx, 0
+ mov byte bl, [si]
+ mov ax, 0
+ mov byte al, [.tmp_var]
+ call set_var
+
+ jmp mainloop
+
+.dereference:
+ mov byte al, [token]
+ call get_var
+ jmp .store
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .tmp_var db 0
+
+
+
+; ------------------------------------------------------------------
+; PEEKINT
+
+do_peekint:
+ call get_token
+
+ cmp ax, VARIABLE
+ jne .error
+
+.get_second:
+ mov al, [token]
+ mov cx, ax
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .address_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+.address_is_number:
+ mov si, token
+ call string_cast_to_int
+ jmp .load_data
+
+.address_is_var:
+ mov al, [token]
+ call get_var
+
+.load_data:
+ mov si, ax
+ mov bx, [si]
+ mov ax, cx
+ call set_var
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+
+; ------------------------------------------------------------------
+; POKE
+
+do_poke:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .first_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+
+ cmp ax, 255
+ jg .error
+
+ mov byte [.first_value], al
+ jmp .onto_second
+
+
+.first_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+ mov byte [.first_value], al
+
+.onto_second:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+
+.got_value:
+ mov di, ax
+ mov ax, 0
+ mov byte al, [.first_value]
+ mov byte [di], al
+
+ jmp mainloop
+
+.second_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ jmp .got_value
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .first_value db 0
+
+
+
+
+; ------------------------------------------------------------------
+; POKEINT
+
+do_pokeint:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .data_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+.data_is_num:
+ mov si, token
+ call string_cast_to_int
+ jmp .get_second
+
+.data_is_var:
+ mov al, [token]
+ call get_var
+
+.get_second:
+ mov cx, ax
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .address_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+.address_is_num:
+ mov si, token
+ call string_cast_to_int
+ jmp .save_data
+
+.address_is_var:
+ mov al, [token]
+ call get_var
+
+.save_data:
+ mov si, ax
+ mov [si], cx
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+
+
+; ------------------------------------------------------------------
+; PORT
+
+do_port:
+ call get_token
+ mov si, token
+
+ mov di, .out_cmd
+ call string_direct_compare
+ jc .do_out_cmd
+
+ mov di, .in_cmd
+ call string_direct_compare
+ jc .do_in_cmd
+
+ jmp .error
+
+
+.do_out_cmd:
+ call get_token
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int ; Now AX = port number
+ mov dx, ax
+
+ call get_token
+ cmp ax, NUMBER
+ je .out_is_num
+
+ cmp ax, VARIABLE
+ je .out_is_var
+
+ jmp .error
+
+.out_is_num:
+ mov si, token
+ call string_cast_to_int
+ call port_byte_out
+ jmp mainloop
+
+.out_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+ call port_byte_out
+ jmp mainloop
+
+
+.do_in_cmd:
+ call get_token
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+ mov dx, ax
+
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov byte cl, [token]
+
+ call port_byte_in
+ mov bx, 0
+ mov bl, al
+
+ mov al, cl
+ call set_var
+
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .out_cmd db "OUT", 0
+ .in_cmd db "IN", 0
+
+
+; ------------------------------------------------------------------
+; PRINT
+
+do_print:
+ call get_token ; Get part after PRINT
+
+ cmp ax, QUOTE ; What type is it?
+ je .print_quote
+
+ cmp ax, VARIABLE ; Numerical variable (eg X)
+ je .print_var
+
+ cmp ax, STRING_VAR ; String variable (eg $1)
+ je .print_string_var
+
+ cmp ax, STRING ; Special keyword (eg CHR or HEX)
+ je .print_keyword
+
+ mov si, err_print_type ; We only print quoted strings and vars!
+ jmp error
+
+
+.print_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var ; Get its value
+
+ call string_cast_from_int ; Convert to string
+ mov si, ax
+ call os_print_string
+
+ jmp .newline_or_not
+
+
+.print_quote: ; If it's quoted text, print it
+ mov si, token
+.print_quote_loop:
+ lodsb
+ cmp al, 0
+ je .newline_or_not
+
+ mov ah, 09h
+ mov byte bl, [ink_colour]
+ mov byte bh, [work_page]
+ mov cx, 1
+ int 10h
+
+ mov ah, 3
+ int 10h
+
+ cmp dl, 79
+ jge .quote_newline
+ inc dl
+
+.move_cur_quote:
+ mov byte bh, [work_page]
+ mov ah, 02h
+ int 10h
+ jmp .print_quote_loop
+
+
+.quote_newline:
+ cmp dh, 24
+ je .move_cur_quote
+ mov dl, 0
+ inc dh
+ jmp .move_cur_quote
+
+.print_string_var:
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ jmp .print_quote_loop
+
+
+.print_keyword:
+ mov si, token
+ mov di, chr_keyword
+ call string_direct_compare
+ jc .is_chr
+
+ mov di, hex_keyword
+ call string_direct_compare
+ jc .is_hex
+
+ mov si, err_syntax
+ jmp error
+
+.is_chr:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .is_chr_variable
+
+ cmp ax, NUMBER
+ je .is_chr_number
+
+.is_chr_variable:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ jmp .print_chr
+
+.is_chr_number:
+ mov si, token
+ call string_cast_to_int
+
+.print_chr:
+ mov ah, 09h
+ mov byte bl, [ink_colour]
+ mov byte bh, [work_page]
+ mov cx, 1
+ int 10h
+
+ mov ah, 3 ; Move the cursor forward
+ int 10h
+ inc dl
+ cmp dl, 79
+ jg .end_line ; If it's over the end of the line
+.move_cur:
+ mov ah, 2
+ int 10h
+
+ jmp .newline_or_not
+
+
+.is_hex:
+ call get_token
+
+ cmp ax, VARIABLE
+ jne .error
+
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+ call string_print_2hex
+
+ jmp .newline_or_not
+
+.end_line:
+ mov dl, 0
+ inc dh
+ cmp dh, 25
+ jl .move_cur
+ mov dh, 24
+ mov dl, 79
+ jmp .move_cur
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+
+.newline_or_not:
+ ; We want to see if the command ends with ';' -- which means that
+ ; we shouldn't print a newline after it finishes. So we store the
+ ; current program location to pop ahead and see if there's the ';'
+ ; character -- otherwise we put the program location back and resume
+ ; the main loop
+
+ mov word ax, [prog]
+ mov word [.tmp_loc], ax
+
+ call get_token
+ cmp ax, UNKNOWN
+ jne .ignore
+
+ mov ax, 0
+ mov al, [token]
+ cmp al, ';'
+ jne .ignore
+
+ jmp mainloop ; And go back to interpreting the code!
+
+.ignore:
+ mov ah, 5
+ mov al, [work_page]
+ int 10h
+
+ mov bh, [work_page]
+ call os_print_newline
+
+ mov ah, 5
+ mov al, [disp_page]
+
+ mov word ax, [.tmp_loc]
+ mov word [prog], ax
+
+ jmp mainloop
+
+
+ .tmp_loc dw 0
+
+
+; ------------------------------------------------------------------
+; RAND
+
+do_rand:
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov byte al, [token]
+ mov byte [.tmp], al
+
+ call get_token
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+ mov word [.num1], ax
+
+ call get_token
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+ mov word [.num2], ax
+
+ mov word ax, [.num1]
+ mov word bx, [.num2]
+ call math_get_random
+
+ mov bx, cx
+ mov ax, 0
+ mov byte al, [.tmp]
+ call set_var
+
+ jmp mainloop
+
+
+ .tmp db 0
+ .num1 dw 0
+ .num2 dw 0
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+; ------------------------------------------------------------------
+; READ
+
+do_read:
+ call get_token ; Get the next token
+
+ cmp ax, STRING ; Check for a label
+ je .is_ok
+
+ mov si, err_goto_notlabel
+ jmp error
+
+.is_ok:
+ mov si, token ; Back up this label
+ mov di, .tmp_token
+ call string_copy
+
+ mov ax, .tmp_token
+ call string_length
+
+ mov di, .tmp_token ; Add ':' char to end for searching
+ add di, ax
+ mov al, ':'
+ stosb
+ mov al, 0
+ stosb
+
+ call get_token ; Now get the offset variable
+ cmp ax, VARIABLE
+ je .second_part_is_var
+
+ mov si, err_syntax
+ jmp error
+
+
+.second_part_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+ cmp ax, 0 ; Want to be searching for at least the first byte!
+ jg .var_bigger_than_zero
+
+ mov si, err_syntax
+ jmp error
+
+
+.var_bigger_than_zero:
+ mov word [.to_skip], ax
+
+
+ call get_token ; And now the var to store result into
+ cmp ax, VARIABLE
+ je .third_part_is_var
+
+ mov si, err_syntax
+ jmp error
+
+
+.third_part_is_var: ; Keep it for later
+ mov ax, 0
+ mov byte al, [token]
+ mov byte [.var_to_use], al
+
+
+
+ ; OK, so now we have all the stuff we need. Let's search for the label
+
+ mov word ax, [prog] ; Store current location
+ mov word [.curr_location], ax
+
+ mov word ax, [load_point]
+ mov word [prog], ax ; Return to start of program to find label
+
+.loop:
+ call get_token
+
+ cmp ax, LABEL
+ jne .line_loop
+
+ mov si, token
+ mov di, .tmp_token
+ call string_direct_compare
+ jc .found_label
+
+.line_loop: ; Go to end of line
+ mov word si, [prog]
+ mov byte al, [si]
+ inc word [prog]
+
+ cmp al, 10
+ jne .line_loop
+
+ mov word ax, [prog]
+ mov word bx, [prog_end]
+ cmp ax, bx
+ jg .past_end
+
+ jmp .loop
+
+.past_end:
+ mov si, err_label_notfound
+ jmp error
+
+
+.found_label:
+ mov word cx, [.to_skip] ; Skip requested number of data entries
+
+.data_skip_loop:
+ push cx
+ call get_token
+ pop cx
+ loop .data_skip_loop
+
+ cmp ax, NUMBER
+ je .data_is_num
+
+ mov si, err_syntax
+ jmp error
+
+.data_is_num:
+ mov si, token
+ call string_cast_to_int
+
+ mov bx, ax
+ mov ax, 0
+ mov byte al, [.var_to_use]
+ call set_var
+
+ mov word ax, [.curr_location]
+ mov word [prog], ax
+
+ jmp mainloop
+
+
+ .curr_location dw 0
+
+ .to_skip dw 0
+ .var_to_use db 0
+ .tmp_token times 30 db 0
+
+
+; ------------------------------------------------------------------
+; REM
+
+do_rem:
+ mov word si, [prog]
+ mov byte al, [si]
+ inc word [prog]
+ cmp al, 10 ; Find end of line after REM
+ jne do_rem
+
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; RENAME
+
+do_rename:
+ call get_token
+
+ cmp ax, STRING_VAR ; Is it a string or a quote?
+ je .first_is_string
+
+ cmp ax, QUOTE
+ je .first_is_quote
+
+ jmp .error
+
+.first_is_string:
+ mov si, string_vars ; Locate string
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ jmp .save_file1
+
+.first_is_quote:
+ mov si, token ; The location of quotes is provided
+
+.save_file1:
+ mov word di, .file1 ; The filename is saved to temporary strings because
+ call string_copy ; getting a second quote will overwrite the previous
+
+.get_second:
+ call get_token
+
+ cmp ax, STRING_VAR
+ je .second_is_string
+
+ cmp ax, QUOTE
+ je .second_is_quote
+
+ jmp .error
+
+.second_is_string:
+ mov si, string_vars ; Locate second string
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ jmp .save_file2
+
+.second_is_quote:
+ mov si, token
+
+.save_file2:
+ mov word di, .file2
+ call string_copy
+
+.check_exists:
+ mov word ax, .file1 ; Check if the source file exists
+ call disk_file_exists
+ jc .file_not_found ; If it doesn't exists set "R = 1"
+
+ clc
+ mov ax, .file2 ; The second file is the destination and should not exist
+ call disk_file_exists
+ jnc .file_exists ; If it exists set "R = 3"
+
+.rename:
+ mov word ax, .file1 ; Seem to be okay, lets rename
+ mov word bx, .file2
+ call disk_rename_file
+
+ jc .rename_failed ; If it failed set "R = 2", usually caused by a read-only disk
+
+ mov ax, 0 ; It worked sucessfully, so set "R = 0" to indicate no error
+ mov byte al, 'R'
+ mov bx, 0
+ call set_var
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+.file_not_found:
+ mov ax, 0 ; Set R variable to 1
+ mov byte al, 'R'
+ mov bx, 1
+ call set_var
+
+ jmp mainloop
+
+.rename_failed:
+ mov ax, 0 ; Set R variable to 2
+ mov byte al, 'R'
+ mov bx, 2
+ call set_var
+
+ jmp mainloop
+
+.file_exists:
+ mov ax, 0
+ mov byte al, 'R' ; Set R variable to 3
+ mov bx, 3
+ call set_var
+
+ jmp mainloop
+
+.data:
+ .file1 times 12 db 0
+ .file2 times 12 db 0
+
+
+; ------------------------------------------------------------------
+; RETURN
+
+do_return:
+ mov ax, 0
+ mov byte al, [gosub_depth]
+ cmp al, 0
+ jne .is_ok
+
+ mov si, err_return
+ jmp error
+
+.is_ok:
+ mov si, gosub_points
+ add si, ax ; Table is words (not bytes)
+ add si, ax
+ lodsw
+ mov word [prog], ax
+ dec byte [gosub_depth]
+
+ jmp mainloop
+
+
+; ------------------------------------------------------------------
+; SAVE
+
+do_save:
+ call get_token
+ cmp ax, QUOTE
+ je .is_quote
+
+ cmp ax, STRING_VAR
+ jne near .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+ jmp .get_position
+
+.is_quote:
+ mov si, token
+
+.get_position:
+ mov di, .tmp_filename
+ call string_copy
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+
+.set_data_loc:
+ mov word [.data_loc], ax
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .third_is_var
+
+ cmp ax, NUMBER
+ jne .error
+
+ mov si, token
+ call string_cast_to_int
+
+.check_exists:
+ mov word [.data_size], ax
+ mov word ax, .tmp_filename
+ call disk_file_exists
+ jc .write_file
+ jmp .file_exists_fail
+
+.write_file:
+
+ mov word ax, .tmp_filename
+ mov word bx, [.data_loc]
+ mov word cx, [.data_size]
+
+ call disk_write_file
+ jc .save_failure
+
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 0
+ call set_var
+
+ jmp mainloop
+
+
+.second_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ jmp .set_data_loc
+
+
+.third_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ jmp .check_exists
+
+.file_exists_fail:
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 2
+ call set_var
+ jmp mainloop
+
+.save_failure:
+ mov ax, 0
+ mov byte al, 'R'
+ mov bx, 1
+ call set_var
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .filename_loc dw 0
+ .data_loc dw 0
+ .data_size dw 0
+
+ .tmp_filename times 15 db 0
+
+
+; ------------------------------------------------------------------
+; SERIAL
+
+do_serial:
+ call get_token
+ mov si, token
+
+ mov di, .on_cmd
+ call string_direct_compare
+ jc .do_on_cmd
+
+ mov di, .send_cmd
+ call string_direct_compare
+ jc .do_send_cmd
+
+ mov di, .rec_cmd
+ call string_direct_compare
+ jc .do_rec_cmd
+
+ jmp .error
+
+.do_on_cmd:
+ call get_token
+ cmp ax, NUMBER
+ je .do_on_cmd_ok
+ jmp .error
+
+.do_on_cmd_ok:
+ mov si, token
+ call string_cast_to_int
+ cmp ax, 1200
+ je .on_cmd_slow_mode
+ cmp ax, 9600
+ je .on_cmd_fast_mode
+
+ jmp .error
+
+.on_cmd_fast_mode:
+ mov ax, 0
+ call port_serial_enable
+ jmp mainloop
+
+.on_cmd_slow_mode:
+ mov ax, 1
+ call port_serial_enable
+ jmp mainloop
+
+
+.do_send_cmd:
+ call get_token
+ cmp ax, NUMBER
+ je .send_number
+
+ cmp ax, VARIABLE
+ je .send_variable
+
+ jmp .error
+
+.send_number:
+ mov si, token
+ call string_cast_to_int
+ call port_send_via_serial
+ jmp mainloop
+
+.send_variable:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+ call port_send_via_serial
+ jmp mainloop
+
+
+.do_rec_cmd:
+ call get_token
+ cmp ax, VARIABLE
+ jne .error
+
+ mov byte al, [token]
+
+ mov cx, 0
+ mov cl, al
+ call port_get_via_serial
+
+ mov bx, 0
+ mov bl, al
+ mov al, cl
+ call set_var
+
+ jmp mainloop
+
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+
+ .on_cmd db "ON", 0
+ .send_cmd db "SEND", 0
+ .rec_cmd db "REC", 0
+
+
+; ------------------------------------------------------------------
+; SIZE
+
+do_size:
+ call get_token
+
+ cmp ax, STRING_VAR
+ je .is_string
+
+ cmp ax, QUOTE
+ je .is_quote
+
+ jmp .error
+
+.is_string:
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+
+ mov ax, si
+ jmp .get_size
+
+.is_quote:
+ mov ax, token
+
+.get_size:
+ call disk_file_size
+ jc .file_not_found
+
+ mov ax, 0
+ mov al, 'S'
+ call set_var
+
+ mov ax, 0
+ mov al, 'R'
+ mov bx, 0
+ call set_var
+
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+.file_not_found:
+ mov ax, 0
+ mov al, [token]
+ mov bx, 0
+ call set_var
+
+ mov ax, 0
+ mov al, 'R'
+ mov bx, 1
+ call set_var
+
+ jmp mainloop
+
+
+
+; ------------------------------------------------------------------
+; SOUND
+
+do_sound:
+ call get_token
+
+ cmp ax, VARIABLE
+ je .first_is_var
+
+ mov si, token
+ call string_cast_to_int
+ jmp .done_first
+
+.first_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+.done_first:
+ call sound_speaker_tone
+
+ call get_token
+
+ cmp ax, VARIABLE
+ je .second_is_var
+
+ mov si, token
+ call string_cast_to_int
+ jmp .finish
+
+.second_is_var:
+ mov ax, 0
+ mov byte al, [token]
+ call get_var
+
+.finish:
+ call misc_pause
+ call sound_speaker_off
+
+ jmp mainloop
+
+
+;-------------------------------------------------------------------
+; STRING
+do_string:
+ call get_token ; The first parameter is the word 'GET', 'SET', LOAD or SAVE
+ mov si, token
+
+ mov di, .get_cmd
+ call string_direct_compare
+ jc .set_str
+
+ mov di, .set_cmd
+ call string_direct_compare
+ jc .get_str
+
+ mov di, .load_cmd
+ call string_direct_compare
+ jc .load_str
+
+ mov di, .save_cmd
+ call string_direct_compare
+ jc .save_str
+
+ jmp .error
+
+ .set_str:
+ mov cx, 1
+ jmp .check_second
+ .get_str:
+ mov cx, 2
+ jmp .check_second
+
+ .load_str:
+ mov cx, 3
+ jmp .check_second
+
+ .save_str:
+ mov cx, 4
+
+.check_second:
+ call get_token ; The next should be a string variable, locate it
+
+ cmp ax, STRING_VAR
+ jne .error
+
+ mov si, string_vars
+ mov ax, 128
+ mul bx
+ add si, ax
+ mov word [.string_loc], si
+
+.check_third:
+ call get_token ; Now there should be a number
+
+ cmp ax, NUMBER
+ je .third_is_number
+
+ cmp ax, VARIABLE
+ je .third_is_variable
+
+ jmp .error
+
+.third_is_number:
+ mov si, token
+ call string_cast_to_int
+ jmp .got_number
+
+.third_is_variable:
+ mov ah, 0
+ mov al, [token]
+ call get_var
+ jmp .got_number
+
+.got_number:
+ cmp ax, 128
+ jg .outrange
+ cmp ax, 0
+ je .outrange
+ sub ax, 1
+ mov dx, ax
+
+ cmp cx, 3 ; load/save only need two variables
+ je .load_var
+
+ cmp cx, 4
+ je .save_var
+
+.check_forth:
+ call get_token ; Next a numerical variable
+
+ cmp ax, VARIABLE
+ jne .error
+
+ mov byte al, [token]
+ mov byte [.tmp], al
+
+ cmp cx, 2
+ je .set_var
+
+.get_var:
+ mov word si, [.string_loc] ; Move to string location
+ add si, dx ; Add offset
+ lodsb ; Load data
+ mov ah, 0
+ mov bx, ax ; Set data in numerical variable
+ mov byte al, [.tmp]
+ call set_var
+ jmp mainloop
+
+.set_var:
+ mov byte al, [.tmp] ; Retrieve the variable
+ call get_var ; Get it's value
+ mov di, [.string_loc] ; Locate the string
+ add di, dx ; Add the offset
+ stosb ; Store data
+ jmp mainloop
+
+.load_var:
+ inc dx
+ mov si, dx
+ mov di, [.string_loc]
+ call string_copy
+ jmp mainloop
+
+.save_var:
+ inc dx
+ mov si, [.string_loc]
+ mov di, dx
+ call string_copy
+ jmp mainloop
+
+.error:
+ mov si, err_syntax
+ jmp error
+
+.outrange:
+ mov dx, ax
+ dec dx
+
+ cmp cx, 3
+ je .load_var
+
+ cmp cx, 4
+ je .save_var
+ mov si, err_string_range
+ jmp error
+
+.data:
+ .get_cmd db "GET", 0
+ .set_cmd db "SET", 0
+ .load_cmd db "LOAD", 0
+ .save_cmd db "STORE", 0
+ .string_loc dw 0
+ .tmp db 0
+
+
+
+; ------------------------------------------------------------------
+; WAITKEY
+
+do_waitkey:
+ call get_token
+ cmp ax, VARIABLE
+ je .is_variable
+
+ mov si, err_syntax
+ jmp error
+
+.is_variable:
+ mov ax, 0
+ mov byte al, [token]
+
+ push ax
+
+ call keyboard_wait_for_key
+
+ cmp ax, 48E0h
+ je .up_pressed
+
+ cmp ax, 50E0h
+ je .down_pressed
+
+ cmp ax, 4BE0h
+ je .left_pressed
+
+ cmp ax, 4DE0h
+ je .right_pressed
+
+.store:
+ mov bx, 0
+ mov bl, al
+
+ pop ax
+
+ call set_var
+
+ jmp mainloop
+
+
+.up_pressed:
+ mov ax, 1
+ jmp .store
+
+.down_pressed:
+ mov ax, 2
+ jmp .store
+
+.left_pressed:
+ mov ax, 3
+ jmp .store
+
+.right_pressed:
+ mov ax, 4
+ jmp .store
+
+
+; ==================================================================
+; INTERNAL ROUTINES FOR INTERPRETER
+
+; ------------------------------------------------------------------
+; Get value of variable character specified in AL (eg 'A')
+
+get_var:
+ mov ah, 0
+ sub al, 65
+ mov si, variables
+ add si, ax
+ add si, ax
+ lodsw
+ ret
+
+
+; ------------------------------------------------------------------
+; Set value of variable character specified in AL (eg 'A')
+; with number specified in BX
+
+set_var:
+ mov ah, 0
+ sub al, 65 ; Remove ASCII codes before 'A'
+
+ mov di, variables ; Find position in table (of words)
+ add di, ax
+ add di, ax
+ mov ax, bx
+ stosw
+ ret
+
+
+; ------------------------------------------------------------------
+; Get token from current position in prog
+
+get_token:
+ mov word si, [prog]
+ lodsb
+
+ cmp al, 10
+ je .newline
+
+ cmp al, ' '
+ je .newline
+
+ call is_number
+ jc get_number_token
+
+ cmp al, '"'
+ je get_quote_token
+
+ cmp al, 39 ; Quote mark (')
+ je get_char_token
+
+ cmp al, '$'
+ je near get_string_var_token
+
+ jmp get_string_token
+
+
+.newline:
+ inc word [prog]
+ jmp get_token
+
+
+
+get_number_token:
+ mov word si, [prog]
+ mov di, token
+
+.loop:
+ lodsb
+ cmp al, 10
+ je .done
+ cmp al, ' '
+ je .done
+ call is_number
+ jc .fine
+
+ mov si, err_char_in_num
+ jmp error
+
+.fine:
+ stosb
+ inc word [prog]
+ jmp .loop
+
+.done:
+ mov al, 0 ; Zero-terminate the token
+ stosb
+
+ mov ax, NUMBER ; Pass back the token type
+ ret
+
+
+get_char_token:
+ inc word [prog] ; Move past first quote (')
+
+ mov word si, [prog]
+ lodsb
+
+ mov byte [token], al
+
+ lodsb
+ cmp al, 39 ; Needs to finish with another quote
+ je .is_ok
+
+ mov si, err_quote_term
+ jmp error
+
+.is_ok:
+ inc word [prog]
+ inc word [prog]
+
+ mov ax, CHAR
+ ret
+
+
+get_quote_token:
+ inc word [prog] ; Move past first quote (") char
+ mov word si, [prog]
+ mov di, token
+.loop:
+ lodsb
+ cmp al, '"'
+ je .done
+ cmp al, 10
+ je .error
+ stosb
+ inc word [prog]
+ jmp .loop
+
+.done:
+ mov al, 0 ; Zero-terminate the token
+ stosb
+ inc word [prog] ; Move past final quote
+
+ mov ax, QUOTE ; Pass back token type
+ ret
+
+.error:
+ mov si, err_quote_term
+ jmp error
+
+
+get_string_var_token:
+ lodsb
+ mov bx, 0 ; If it's a string var, pass number of string in BX
+ mov bl, al
+ sub bl, 49
+
+ inc word [prog]
+ inc word [prog]
+
+ mov ax, STRING_VAR
+ ret
+
+
+get_string_token:
+ mov word si, [prog]
+ mov di, token
+.loop:
+ lodsb
+ cmp al, 10
+ je .done
+ cmp al, ' '
+ je .done
+ stosb
+ inc word [prog]
+ jmp .loop
+.done:
+ mov al, 0 ; Zero-terminate the token
+ stosb
+
+ mov ax, token
+ call string_upper_case
+
+ mov ax, token
+ call string_length ; How long was the token?
+ cmp ax, 1 ; If 1 char, it's a variable or delimiter
+ je .is_not_string
+
+ mov si, token ; If the token ends with ':', it's a label
+ add si, ax
+ dec si
+ lodsb
+ cmp al, ':'
+ je .is_label
+
+ mov ax, STRING ; Otherwise it's a general string of characters
+ ret
+
+.is_label:
+ mov ax, LABEL
+ ret
+
+
+.is_not_string:
+ mov byte al, [token]
+ call is_letter
+ jc .is_var
+
+ mov ax, UNKNOWN
+ ret
+
+.is_var:
+ mov ax, VARIABLE ; Otherwise probably a variable
+ ret
+
+
+; ------------------------------------------------------------------
+; Set carry flag if AL contains ASCII number
+
+is_number:
+ cmp al, 48
+ jl .not_number
+ cmp al, 57
+ jg .not_number
+ stc
+ ret
+.not_number:
+ clc
+ ret
+
+
+; ------------------------------------------------------------------
+; Set carry flag if AL contains ASCII letter
+
+is_letter:
+ cmp al, 65
+ jl .not_letter
+ cmp al, 90
+ jg .not_letter
+ stc
+ ret
+
+.not_letter:
+ clc
+ ret
+
+
+; ------------------------------------------------------------------
+; Print error message and quit out
+
+error:
+ mov ah, 5 ; Revert display page
+ mov al, 0
+ int 10h
+
+ mov byte [work_page], 0
+ mov byte [disp_page], 0
+
+ call os_print_newline
+ call os_print_string ; Print error message
+
+
+ mov si, line_num_starter
+ call os_print_string
+
+
+ ; And now print the line number where the error occurred. We do this
+ ; by working from the start of the program to the current point,
+ ; counting the number of newline characters along the way
+
+ mov word si, [load_point]
+ mov word bx, [prog]
+ mov cx, 1
+
+.loop:
+ lodsb
+ cmp al, 10
+ jne .not_newline
+ inc cx
+.not_newline:
+ cmp si, bx
+ je .finish
+ jmp .loop
+.finish:
+
+ mov ax, cx
+ call string_cast_from_int
+ mov si, ax
+ call os_print_string
+
+
+ call os_print_newline
+
+ mov word sp, [orig_stack] ; Restore the stack to as it was when BASIC started
+
+ ret ; And finish
+
+
+ ; Error messages text...
+
+ err_char_in_num db "Error: unexpected char in number", 0
+ err_cmd_unknown db "Error: unknown command", 0
+ err_divide_by_zero db "Error: attempt to divide by zero", 0
+ err_doloop_maximum db "Error: DO/LOOP nesting limit exceeded", 0
+ err_file_notfound db "Error: file not found", 0
+ err_goto_notlabel db "Error: GOTO or GOSUB not followed by label", 0
+ err_label_notfound db "Error: label not found", 0
+ err_nest_limit db "Error: FOR or GOSUB nest limit exceeded", 0
+ err_next db "Error: NEXT without FOR", 0
+ err_loop db "Error: LOOP without DO", 0
+ err_print_type db "Error: PRINT not followed by quoted text or variable", 0
+ err_quote_term db "Error: quoted string or char not terminated correctly", 0
+ err_return db "Error: RETURN without GOSUB", 0
+ err_string_range db "Error: string location out of range", 0
+ err_syntax db "Error: syntax error", 0
+ err_break db "BREAK CALLED", 0
+
+ line_num_starter db " - line ", 0
+
+
+; ==================================================================
+; DATA SECTION
+
+ orig_stack dw 0 ; Original stack location when BASIC started
+
+ prog dw 0 ; Pointer to current location in BASIC code
+ prog_end dw 0 ; Pointer to final byte of BASIC code
+
+ load_point dw 0
+
+ token_type db 0 ; Type of last token read (eg NUMBER, VARIABLE)
+ token times 255 db 0 ; Storage space for the token
+
+vars_loc:
+ variables times 26 dw 0 ; Storage space for variables A to Z
+
+ for_variables times 26 dw 0 ; Storage for FOR loops
+ for_code_points times 26 dw 0 ; Storage for code positions where FOR loops start
+
+ do_loop_store times 10 dw 0 ; Storage for DO loops
+ loop_in db 0 ; Loop level
+
+ last_if_true db 1 ; Checking for 'ELSE'
+
+ ink_colour db 0 ; Text printing colour
+ work_page db 0 ; Page to print to
+ disp_page db 0 ; Page to display
+
+ alert_cmd db "ALERT", 0
+ askfile_cmd db "ASKFILE", 0
+ break_cmd db "BREAK", 0
+ call_cmd db "CALL", 0
+ case_cmd db "CASE", 0
+ cls_cmd db "CLS", 0
+ cursor_cmd db "CURSOR", 0
+ curschar_cmd db "CURSCHAR", 0
+ curscol_cmd db "CURSCOL", 0
+ curspos_cmd db "CURSPOS", 0
+ delete_cmd db "DELETE", 0
+ do_cmd db "DO", 0
+ else_cmd db "ELSE", 0
+ end_cmd db "END", 0
+ files_cmd db "FILES", 0
+ for_cmd db "FOR", 0
+ gosub_cmd db "GOSUB", 0
+ goto_cmd db "GOTO", 0
+ getkey_cmd db "GETKEY", 0
+ if_cmd db "IF", 0
+ include_cmd db "INCLUDE", 0
+ ink_cmd db "INK", 0
+ input_cmd db "INPUT", 0
+ len_cmd db "LEN", 0
+ listbox_cmd db "LISTBOX", 0
+ load_cmd db "LOAD", 0
+ loop_cmd db "LOOP", 0
+ move_cmd db "MOVE", 0
+ next_cmd db "NEXT", 0
+ number_cmd db "NUMBER", 0
+ page_cmd db "PAGE", 0
+ pause_cmd db "PAUSE", 0
+ peek_cmd db "PEEK", 0
+ peekint_cmd db "PEEKINT", 0
+ poke_cmd db "POKE", 0
+ pokeint_cmd db "POKEINT", 0
+ port_cmd db "PORT", 0
+ print_cmd db "PRINT", 0
+ rand_cmd db "RAND", 0
+ read_cmd db "READ", 0
+ rem_cmd db "REM", 0
+ rename_cmd db "RENAME", 0
+ return_cmd db "RETURN", 0
+ save_cmd db "SAVE", 0
+ serial_cmd db "SERIAL", 0
+ size_cmd db "SIZE", 0
+ sound_cmd db "SOUND", 0
+ string_cmd db "STRING", 0
+ waitkey_cmd db "WAITKEY", 0
+
+ and_keyword db "AND", 0
+ then_keyword db "THEN", 0
+ chr_keyword db "CHR", 0
+ hex_keyword db "HEX", 0
+
+ lower_keyword db "LOWER", 0
+ upper_keyword db "UPPER", 0
+
+ ink_keyword db "INK", 0
+ progstart_keyword db "PROGSTART", 0
+ ramstart_keyword db "RAMSTART", 0
+ timer_keyword db "TIMER", 0
+ variables_keyword db "VARIABLES", 0
+ version_keyword db "VERSION", 0
+
+ gosub_depth db 0
+ gosub_points times 10 dw 0 ; Points in code to RETURN to
+
+ string_vars times 1024 db 0 ; 8 * 128 byte strings
+
+
+; ------------------------------------------------------------------
+
diff --git a/source/kernel/features/cli.asm b/source/kernel/features/cli.asm
index 9b02459..cc72c60 100755
--- a/source/kernel/features/cli.asm
+++ b/source/kernel/features/cli.asm
@@ -18,7 +18,7 @@ os_start_cli:
os_read_cli:
pusha
mov si, user_input
- call os_upper_case ; Make the input uppercase so it's case insensitive
+ call string_upper_case ; Make the input uppercase so it's case insensitive
.output_the_user_input:
call os_print_newline
@@ -45,6 +45,13 @@ os_read_cli:
cmp cl, 1
je power_reboot
+ ; Basic
+ mov si, user_input
+ mov di, basic_string
+ call os_compare_strings
+ cmp cl, 1
+ je basic
+
; Cat
mov si, user_input
mov di, cat_string
@@ -59,13 +66,6 @@ os_read_cli:
cmp cl, 1
je ls
- ; Pong
- mov si, user_input
- mov di, pong_string
- call os_compare_strings
- cmp cl, 1
- je pong
-
jmp .unkown
.unkown:
@@ -88,10 +88,9 @@ help:
call os_print_string
call os_read_cli.finish
-pong:
- call game_pong
+basic:
+ call util_basic
call os_read_cli.finish
-
cat:
call util_cat
call os_read_cli.finish
diff --git a/source/kernel/features/disk.asm b/source/kernel/features/disk.asm
index e9a4327..d0e892b 100644
--- a/source/kernel/features/disk.asm
+++ b/source/kernel/features/disk.asm
@@ -173,7 +173,7 @@ disk_clear_output_buffer:
; TODO use predefined data for calculations
disk_load_file:
pusha
- call os_string_length ; cl = string length
+ call string_length ; cl = string length
cmp cl, 11
ja .filename_too_long
@@ -244,7 +244,7 @@ disk_load_file:
disk_write_file:
pusha
; Check if file name is too long
- call os_string_length ; cl = string length
+ call string_length ; cl = string length
cmp cl, 11
ja .filename_too_long
; Convert file name to a fat filename
@@ -302,6 +302,7 @@ disk_write_file:
ret
; TODO support long file names
+; TODO don't work twice
; Store a list of the files in file_buffer
; in a human readable format
; OUT
@@ -374,3 +375,14 @@ disk_list_contents:
stosb
popa
ret
+
+disk_file_exists:
+ ret
+disk_file_size:
+ ret
+disk_file_list:
+ ret
+disk_remove_file:
+ ret
+disk_rename_file:
+ ret
diff --git a/source/kernel/features/graphics.asm b/source/kernel/features/graphics.asm
index e15a90c..766a68b 100644
--- a/source/kernel/features/graphics.asm
+++ b/source/kernel/features/graphics.asm
@@ -51,3 +51,10 @@ section .data
y_end dw 0
colour db 1111b
+graphics_dialogue_box:
+ ret
+
+graphics_file_selector:
+ ret
+graphics_list_dialogue:
+ ret
diff --git a/source/kernel/features/keyboard.asm b/source/kernel/features/keyboard.asm
index 4a2733d..330d19e 100644
--- a/source/kernel/features/keyboard.asm
+++ b/source/kernel/features/keyboard.asm
@@ -7,10 +7,10 @@ os_read_input:
hlt
jmp os_read_input
-.key_pressed:
- mov ah, 10h
- int 16h
- ret
+ .key_pressed:
+ mov ah, 10h
+ int 16h
+ ret
; -------------------------------------------
@@ -18,60 +18,73 @@ os_display_input:
pusha
mov cx, [prompt_length]
-.loop:
- call .check_key_pressed
+ .loop:
+ call .check_key_pressed
- jmp .loop
+ jmp .loop
-.check_key_pressed:
- call os_read_input
+ .check_key_pressed:
+ call os_read_input
- cmp al, 08h
- je .backspace
+ cmp al, 08h
+ je .backspace
- cmp al, 0Dh
- je .enter_key
+ cmp al, 0Dh
+ je .enter_key
- cmp al, 1Bh
- je .esc_key
+ cmp al, 1Bh
+ je .esc_key
- cmp cx, 0
- jb .check_key_pressed
+ cmp cx, 0
+ jb .check_key_pressed
- call .print_current_input
- dec cx
- jmp .check_key_pressed
+ call .print_current_input
+ dec cx
+ jmp .check_key_pressed
-.esc_key:
- call power_reboot
+ .esc_key:
+ call power_reboot
-.enter_key:
- mov al, 0
- stosb
- popa
- call os_read_cli
+ .enter_key:
+ mov al, 0
+ stosb
+ popa
+ call os_read_cli
-.backspace:
- call .move_cursor_back ; then .move_cursor_back
- call .loop ; Else .loop
+ .backspace:
+ call .move_cursor_back ; then .move_cursor_back
+ call .loop ; Else .loop
-.move_cursor_back:
- mov ah, 0Eh
+ .move_cursor_back:
+ mov ah, 0Eh
- mov al, 08h
- int 10h
- mov al, 20h
- int 10h
- mov al, 08h
- int 10h
+ mov al, 08h
+ int 10h
+ mov al, 20h
+ int 10h
+ mov al, 08h
+ int 10h
- dec di
- jmp os_display_input
+ dec di
+ jmp os_display_input
-.print_current_input:
- stosb
+ .print_current_input:
+ stosb
- mov ah, 0Eh
- int 10h
+ mov ah, 0Eh
+ int 10h
+ ret
+
+keyboard_get_cursor_pos: ;TODO
+ ret
+keyboard_wait_for_key: ;TODO
+ ret
+keyboard_show_cursor:
+ ret
+keyboard_hide_cursor:
+ ret
+keyboard_move_cursor:
+ ret
+keyboard_check_key:
ret
diff --git a/source/kernel/features/math.asm b/source/kernel/features/math.asm
index b98cb25..1a03e3c 100644
--- a/source/kernel/features/math.asm
+++ b/source/kernel/features/math.asm
@@ -52,3 +52,5 @@ math_lba_to_chs:
pop ax
ret
+math_get_random:
+ ret
diff --git a/source/kernel/features/misc.asm b/source/kernel/features/misc.asm
new file mode 100644
index 0000000..3c49f31
--- /dev/null
+++ b/source/kernel/features/misc.asm
@@ -0,0 +1,4 @@
+misc_pause:
+ ret
+misc_get_api_version:
+ ret
diff --git a/source/kernel/features/ports.asm b/source/kernel/features/ports.asm
new file mode 100644
index 0000000..d187c7a
--- /dev/null
+++ b/source/kernel/features/ports.asm
@@ -0,0 +1,10 @@
+port_byte_out:
+ ret
+port_byte_in:
+ ret
+port_serial_enable:
+ ret
+port_send_via_serial:
+ ret
+port_get_via_serial:
+ ret
diff --git a/source/kernel/features/sound.asm b/source/kernel/features/sound.asm
index e69de29..0f967b5 100644
--- a/source/kernel/features/sound.asm
+++ b/source/kernel/features/sound.asm
@@ -0,0 +1,4 @@
+sound_speaker_tone:
+ ret
+sound_speaker_off:
+ ret
diff --git a/source/kernel/features/strings.asm b/source/kernel/features/strings.asm
index 4f16f7d..3cb4878 100755
--- a/source/kernel/features/strings.asm
+++ b/source/kernel/features/strings.asm
@@ -11,35 +11,176 @@
os_compare_strings:
cld
-.compare:
- mov al, 0
- scasb
- je .di_ended
- dec di
- lodsb
- scasb ; Compare di to si
- jne .unequal ; If they are not equal, jump to .unequal
- jmp .compare ; Finally, repeat
+ .compare:
+ mov al, 0
+ scasb
+ je .di_ended
+ dec di
+ lodsb
+ scasb ; Compare di to si
+ jne .unequal ; If they are not equal, jump to .unequal
+ jmp .compare ; Finally, repeat
-.unequal:
- mov cl, 0 ; Change to 0 if unquality is proven
- ret
-.di_ended:
- lodsb
- cmp al, 20h ; 20h = space
- je .equal
- cmp al, 0
- je .equal
- jmp .unequal
-.equal:
- mov cl, 1
+ .unequal:
+ mov cl, 0 ; Change to 0 if unquality is proven
+ ret
+ .di_ended:
+ lodsb
+ cmp al, 20h ; 20h = space
+ je .equal
+ cmp al, 0
+ je .equal
+ jmp .unequal
+ .equal:
+ mov cl, 1
+ ret
+
+; ------------------------------------------------------------------
+; os_string_compare -- See if two strings match
+; IN: SI = string one, DI = string two
+; OUT: carry set if same, clear if different
+; from MikeOS kernel
+
+string_direct_compare:
+ pusha
+ .more:
+ mov al, [si] ; Retrieve string contents
+ mov bl, [di]
+ cmp al, bl ; Compare characters at current location
+ jne .not_same
+ cmp al, 0 ; End of first string? Must also be end of second
+ je .end
+ inc si
+ inc di
+ jmp .more
+ .not_same: ; If unequal lengths with same beginning, the byte
+ popa ; comparison fails at shortest string terminator
+ clc ; Clear carry flag
+ ret
+ .end: ; Both strings terminated at the same position
+ popa
+ stc ; Set carry flag
+ ret
+
+
+; --------------------------------------------------------------------
+; Copies a string from si to di
+; IN:
+; si: points to first character of source string
+; OUT:
+; di: points to first character of destination string
+string_copy:
+ pusha
+ .loop:
+ ; TODO could this cause issue when a character is > 1 byte?
+ lodsb ; Load si character into ax and increment si by 1
+ stosb ; Store ax in di location and increment di by 1
+ cmp ax, 0 ; if ax is a 0, quit
+ jne .loop
+ .done:
+ popa
+ ret
+
+; --------------------------------------------------------------------
+; Joins two strings together into a new string
+; IN/OUT:
+; ax: string1
+; bx: string2
+; cx: result string
+
+string_join:
+ pusha
+ .copy_string1:
+ mov si, ax
+ mov di, cx
+ call string_copy
+ mov ax, cx ; TODO check this bit
+ call string_length
+ add cx, ax
+ .copy_string2:
+ mov si, bx
+ mov di, dx
+ call string_copy
+ .done:
+ popa
+ ret
+
+; ------------------------------------------------------------------------
+; TODO
+string_cast_to_int:
ret
+; ------------------------------------------------------------------------
+
+; IN: AX = int
+; OUT: AX = string location (output buffer)
+string_cast_from_int: ; TODO I think this algorithm could be optimised
+ pusha
+ ; Initialise base to decimal (base 10)
+ mov bx, 10
+ ; clear the strigified int location
+ mov cx, 6
+ push ax
+ xor ax,ax
+ .clear_loop:
+ stosb
+ dec cx
+ cmp cx, 0
+ ja .clear_loop
+ ; setup
+ pop ax
+ push ax
+ mov cx, -1
+ ; loop over number to get the value of count (cx)
+ .count_loop:
+ xor dx,dx
+ div bx ; ax = ax // bx
+ inc cx
+ cmp ax, 0
+ ja .count_loop ; Jump if greater
+ pop ax
+ ; loop over count downwards until count is 0
+ mov di, stringified_int
+ .stringify_loop:
+ mov cx,1 ;DEL
+ push cx
+ push bx
+ push ax
+ ; Generate power of 10
+ mov ax, 1
+ mov cx,0 ;DEL
+ cmp cx, 1
+ jl .after_power_loop
+ .power_loop:
+ mul bx
+ dec cx
+ cmp cx, 0
+ ja .power_loop
+ .after_power_loop:
+ mov bx,ax
+ pop ax
+ pop cx
+ ; ax = ax DIV bx
+ ; dx = ax MOD bx
+ xor dx,dx
+ div bx
+ pop bx
+ add ax, 30h
+ stosb
+ mov ax, dx
+ dec cx
+ cmp cx, 0
+ ja .stringify_loop
+ .finish:
+ popa
+ ;mov ax, stringified_int
+ ret
+
; Get the length of a string
; 'hello world', 0 is 11 characters long (excluding the terminator)
; input: si points to the string to be counted
; output: cl holds the length
-os_string_length:
+string_length:
push si
xor cl,cl ; Clear the al register
.loop:
@@ -52,6 +193,8 @@ os_string_length:
pop si
ret
+
+
; convert a string to fat's filename format
; It will be capitalised and 11 characters long,
; 8 for the filename, 3 for the extension
@@ -60,8 +203,8 @@ os_string_length:
; output: di points to the fat formatted filename
os_format_fat_filename:
pusha
- call os_upper_case
- call os_string_length ; Stores the length of the string in cl
+ call string_upper_case
+ call string_length ; Stores the length of the string in cl
xor ch,ch ; Clear ch to reset it to 0
.character_loop:
lodsb
@@ -147,7 +290,7 @@ string_unformat_fat_filename:
; Convert a string to all upper/lower case
; INPUT: si pointing to a string
; OUPUT: the same string in memory will now be capitalised/decapitalised
-os_upper_case: ; to upper case
+string_upper_case: ; to upper case
pusha
mov di, si
.loop:
@@ -187,3 +330,9 @@ os_lower_case: ; to lower case
popa
ret
+string_lower_case:
+ ret
+string_input:
+ ret
+string_print_2hex:
+ ret
diff --git a/source/kernel/features/utils.asm b/source/kernel/features/utils.asm
index 7bdd263..f11b14a 100644
--- a/source/kernel/features/utils.asm
+++ b/source/kernel/features/utils.asm
@@ -17,12 +17,30 @@ util_cat:
util_ls:
pusha
- call disk_clear_output_buffer
+ call disk_load_root
call disk_list_contents
mov si, output_buffer
call os_print_string
+ call disk_clear_output_buffer
popa
ret
+util_basic:
+ pusha
+
+ call disk_load_root
+
+ ; TODO make this more consistent to account for double spaces
+ ;add si, 3 ; Move si to after 'BAS'
+ call disk_load_file
+
+ mov si, file_buffer
+
+ mov ax, si
+ mov si, 0
+ call basic_run_basic
+
+ popa
+ ret
diff --git a/source/kernel/kernel.asm b/source/kernel/kernel.asm
index 6700bd0..da309db 100644
--- a/source/kernel/kernel.asm
+++ b/source/kernel/kernel.asm
@@ -10,8 +10,12 @@ start:
mov si, boot_message
call os_print_string
- mov si, help_text
- call os_print_string
+
+ ; TESTING
+ ;mov ax, 3
+ ;call string_cast_from_int
+ ;mov si, stringified_int
+ ;call os_print_string
call os_start_cli
hlt
@@ -24,16 +28,18 @@ halt:
; FEATURES -- Code to pull into the kernel
%INCLUDE "source/kernel/features/text.asm"
%INCLUDE "source/kernel/features/keyboard.asm"
- %INCLUDE "source/kernel/features/cli.asm"
+ %INCLUDE "source/kernel/features/ports.asm"
%INCLUDE "source/kernel/features/power.asm"
%INCLUDE "source/kernel/features/strings.asm"
%INCLUDE "source/kernel/features/graphics.asm"
+ %INCLUDE "source/kernel/features/sound.asm"
%INCLUDE "source/kernel/features/disk.asm"
%INCLUDE "source/kernel/features/math.asm"
%INCLUDE "source/kernel/features/time.asm"
%INCLUDE "source/kernel/features/utils.asm"
-; GAMES -- Games that I wrote for it
- %INCLUDE "source/kernel/games/pong.asm"
+ %INCLUDE "source/kernel/features/cli.asm"
+ %INCLUDE "source/kernel/features/misc.asm"
+ %INCLUDE "source/kernel/features/basic.asm"
; DATA/VARIABLES
%INCLUDE "source/kernel/data.asm"
diff --git a/source/kernel/programs/ed.asm b/source/kernel/programs/ed.asm
new file mode 100644
index 0000000..e69de29
diff --git a/test-linux.sh b/test-linux.sh
index af9b2fa..1d9e3fa 100755
--- a/test-linux.sh
+++ b/test-linux.sh
@@ -5,5 +5,7 @@ sudo chown $(whoami) disk_images/*
qemu-system-i386\
-drive file=disk_images/crawos.img,if=floppy,format=raw\
-m 512m\
- -object memory-backend-file,id=pc.ram,size=512m,mem-path=/dev/shm/qemu-ram,share=on -machine memory-backend=pc.ram\
- $1 $2
+ -object memory-backend-file,id=pc.ram,size=512m,mem-path=/dev/shm/qemu-ram,share=on\
+ -machine memory-backend=pc.ram\
+ -d in_asm,int -D ./detailed.log\
+ $1 $2
diff --git a/to_implement b/to_implement
new file mode 100644
index 0000000..e69de29