BASIC interpreter

This commit is contained in:
deadvey
2026-02-09 20:41:49 +00:00
parent 0f6f8f33f6
commit e4fc63134f
16 changed files with 4501 additions and 4524 deletions

0
.gitignore vendored Executable file → Normal file
View File

0
LICENSE Executable file → Normal file
View File

0
README.md Executable file → Normal file
View File

0
build-linux.sh Executable file → Normal file
View File

0
data/README.md Executable file → Normal file
View File

View File

@@ -1,2 +1,4 @@
PRINT "Hello World!" FOR A = 1 TO 10
PRINT A
NEXT A
END END

View File

@@ -40,6 +40,7 @@ command_result_text: db 'You typed: ', 0
unknown_command: db 'Error: Unkown Command..\n ', 0 unknown_command: db 'Error: Unkown Command..\n ', 0
stringified_int: db 0,0,0,0,0,0 ; Can store up to 6 digits stringified_int: db 0,0,0,0,0,0 ; Can store up to 6 digits
int_tmp: dw 0
; Disk operations ; Disk operations
disk_read_fail: db 'Error: Could not read disk\n', 0 disk_read_fail: db 'Error: Could not read disk\n', 0
file_not_found: db 'File not found\n', 0 file_not_found: db 'File not found\n', 0
@@ -59,3 +60,4 @@ read_write_flag: db 02
empty_byte: db 0 empty_byte: db 0
empty_word: dw 0 empty_word: dw 0
empty_dword: dd 0 empty_dword: dd 0
tmp: db '97', 0

View File

@@ -318,7 +318,6 @@ clear_ram:
assign: assign:
cmp ax, VARIABLE ; Are we starting with a number var? cmp ax, VARIABLE ; Are we starting with a number var?
je .do_num_var je .do_num_var
mov di, string_vars ; Otherwise it's a string var mov di, string_vars ; Otherwise it's a string var
mov ax, 128 mov ax, 128
mul bx ; (BX = string number, passed back from get_token) mul bx ; (BX = string number, passed back from get_token)
@@ -4281,7 +4280,7 @@ vars_loc:
case_cmd db "CASE", 0 case_cmd db "CASE", 0
cls_cmd db "CLS", 0 cls_cmd db "CLS", 0
cursor_cmd db "CURSOR", 0 cursor_cmd db "CURSOR", 0
curschar_cmd db "CURSCHAR", 0 curschar_cmd db "CURSCHAR", 0
curscol_cmd db "CURSCOL", 0 curscol_cmd db "CURSCOL", 0
curspos_cmd db "CURSPOS", 0 curspos_cmd db "CURSPOS", 0
delete_cmd db "DELETE", 0 delete_cmd db "DELETE", 0

2
source/kernel/features/cli.asm Executable file → Normal file
View File

@@ -17,7 +17,7 @@ os_start_cli:
os_read_cli: os_read_cli:
pusha pusha
mov si, user_input mov ax, user_input
call string_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: .output_the_user_input:

View File

@@ -308,6 +308,7 @@ disk_write_file:
; OUT ; OUT
; output_buffer: the list (string) ; output_buffer: the list (string)
disk_list_contents: disk_list_contents:
call disk_load_root
pusha pusha
mov si, root_buffer mov si, root_buffer
mov di, output_buffer mov di, output_buffer
@@ -322,38 +323,22 @@ disk_list_contents:
dec cx dec cx
cmp cx, 0 cmp cx, 0
jne .space_loop jne .space_loop
mov ax, [si+28d]
.file_attribute: call string_cast_from_int
add si, 11d push si
mov si, stringified_int
lodsb
.output_file_size_loop:
stosb
lodsb lodsb
push si cmp al, 0
jne .output_file_size_loop
mov si, read_only .after_fs_loop:
cmp al, 1h
je .add_string
mov si, hidden
cmp al, 2h
je .add_string
mov si, system
cmp al, 4h
je .add_string
mov si, volume
cmp al, 8h
je .add_string
mov si, directory
cmp al, 10h
je .add_string
mov si, archive
cmp al, 20h
je .add_string
.after_file_attributes:
pop si pop si
mov al, 0Ah mov al, 0Ah
stosb stosb
add si, 20d add si, 20h
lodsb ; +1 lodsb ; +1
dec si ; -1 dec si ; -1
cmp al, 0 ; You've come to the end of the root entries cmp al, 0 ; You've come to the end of the root entries
@@ -361,13 +346,6 @@ disk_list_contents:
cmp al, 00E5h ; E5h is a marker that the Fat entry is available cmp al, 00E5h ; E5h is a marker that the Fat entry is available
je .finish je .finish
jmp .loop jmp .loop
.add_string:
.add_string_loop:
lodsb
cmp ax, 00h
je .after_file_attributes
stosb
jmp .add_string_loop
.finish: .finish:
mov cx, output_buffer mov cx, output_buffer
sub di, cx sub di, cx

0
source/kernel/features/power.asm Executable file → Normal file
View File

251
source/kernel/features/strings.asm Executable file → Normal file
View File

@@ -1,11 +1,11 @@
; How string comparison works ; How string comparison works
; DI => scasb compares value stored in DI which is 's' with 's' stored in AX register ; DI => scasb compares value stored in DI which is 's' with 's' stored in AX register
; and then increments DI if DF is 0 ; and then increments DI if DF is 0
; v ; v
; memory address 1: |s|n|a|t|0| ; memory address 1: |s|n|a|t|0|
; memory address 2: |s|n|a|k|e|0| ; memory address 2: |s|n|a|k|e|0|
; ^ ; ^
; SI => lodsb loads value stored at SI to AX and then increments SI if DF is 0 ; SI => lodsb loads value stored at SI to AX and then increments SI if DF is 0
; Additionaly, if the di string ends and the relevant character in si contains a space, it will still return ; Additionaly, if the di string ends and the relevant character in si contains a space, it will still return
; the strings as being equal, this is to allow for command line arquments ; the strings as being equal, this is to allow for command line arquments
os_compare_strings: os_compare_strings:
@@ -44,22 +44,22 @@ os_compare_strings:
string_direct_compare: string_direct_compare:
pusha pusha
.more: .more:
mov al, [si] ; Retrieve string contents mov al, [si] ; Retrieve string contents
mov bl, [di] mov bl, [di]
cmp al, bl ; Compare characters at current location cmp al, bl ; Compare characters at current location
jne .not_same jne .not_same
cmp al, 0 ; End of first string? Must also be end of second cmp al, 0 ; End of first string? Must also be end of second
je .end je .end
inc si inc si
inc di inc di
jmp .more jmp .more
.not_same: ; If unequal lengths with same beginning, the byte .not_same: ; If unequal lengths with same beginning, the byte
popa ; comparison fails at shortest string terminator popa ; comparison fails at shortest string terminator
clc ; Clear carry flag clc ; Clear carry flag
ret ret
.end: ; Both strings terminated at the same position .end: ; Both strings terminated at the same position
popa popa
stc ; Set carry flag stc ; Set carry flag
ret ret
@@ -73,8 +73,8 @@ string_copy:
pusha pusha
.loop: .loop:
; TODO could this cause issue when a character is > 1 byte? ; TODO could this cause issue when a character is > 1 byte?
lodsb ; Load si character into ax and increment si by 1 lodsb ; Load si character into ax and increment si by 1
stosb ; Store ax in di location and increment di by 1 stosb ; Store ax in di location and increment di by 1
cmp ax, 0 ; if ax is a 0, quit cmp ax, 0 ; if ax is a 0, quit
jne .loop jne .loop
.done: .done:
@@ -107,89 +107,84 @@ string_join:
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
; TODO ; TODO
; Converts a null terminated string to an integer
; IN: SI = string location (max 5 chars, up to '65536')
; OUT: AX = number
string_cast_to_int: string_cast_to_int:
ret pusha
xor cx,cx
.loop:
xor ax,ax
lodsb
cmp al, 0
je .finish
sub al, 30h
mov bx, ax
mov ax, 10
mul cx ; Multiple the current value by 10
add ax, bx ; Then add the new digit
mov cx, ax
jmp .loop
.finish:
mov [int_tmp], cx
popa
mov ax, [int_tmp]
ret
; ------------------------------------------------------------------------ ; ------------------------------------------------------------------------
; IN: AX = integer (unsigned)
; OUT: AX -> null-terminated string
; IN: AX = int string_cast_from_int:
; OUT: AX = string location (output buffer)
string_cast_from_int: ; TODO I think this algorithm could be optimised
pusha pusha
; Initialise base to decimal (base 10) cld ; Write backwards
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 mov di, stringified_int
.stringify_loop:
mov cx,1 ;DEL mov bx, 10 ; base
push cx mov cx, 0 ; digit count
push bx
push ax .convert_loop:
; Generate power of 10 xor dx, dx
mov ax, 1 div bx ; AX = AX / base, DX = remainder
mov cx,0 ;DEL add dl, '0'
cmp cx, 1 push dx ; store digit
jl .after_power_loop inc cx
.power_loop: test ax, ax
mul bx jnz .convert_loop
dec cx
cmp cx, 0 .write_loop:
ja .power_loop
.after_power_loop:
mov bx,ax
pop ax pop ax
pop cx
; ax = ax DIV bx
; dx = ax MOD bx
xor dx,dx
div bx
pop bx
add ax, 30h
stosb stosb
mov ax, dx loop .write_loop
dec cx
cmp cx, 0 mov al, 0
ja .stringify_loop stosb ; null terminator
.finish:
popa popa
;mov ax, stringified_int mov ax, stringified_int
ret ret
;----------------------------------------------------------------------------
; Get the length of a string ; Get the length of a string
; 'hello world', 0 is 11 characters long (excluding the terminator) ; 'hello world', 0 is 11 characters long (excluding the terminator)
; input: si points to the string to be counted ; input: ax points to the string to be counted
; output: cl holds the length ; output: ax holds the length
string_length: string_length:
push si push si
xor cl,cl ; Clear the al register push cx
mov si, ax
xor ax,ax ; Clear the cx register
xor cx,cx
.loop: .loop:
lodsb lodsb
cmp al, 0 cmp al, 0
je .finish je .finish
inc cl inc cx
jmp .loop jmp .loop
.finish: .finish:
mov ax, cx
pop cx
pop si pop si
ret ret
@@ -198,15 +193,17 @@ string_length:
; convert a string to fat's filename format ; convert a string to fat's filename format
; It will be capitalised and 11 characters long, ; It will be capitalised and 11 characters long,
; 8 for the filename, 3 for the extension ; 8 for the filename, 3 for the extension
; eg: 'file.txt' -> 'FILE TXT' ; eg: 'file.txt' -> 'FILE TXT'
; input: si points to filename, di points to a free 11 bytes in memory ; input: si points to filename, di points to a free 11 bytes in memory
; output: di points to the fat formatted filename ; output: di points to the fat formatted filename
os_format_fat_filename: os_format_fat_filename:
pusha pusha
call string_upper_case call string_upper_case
call string_length ; Stores the length of the string in cl mov ax, si
xor ch,ch ; Clear ch to reset it to 0 call string_length ; Stores the length of the string in cl
.character_loop: mov cl, al
xor ch,ch ; Clear ch to reset it to 0
.character_loop:
lodsb lodsb
cmp al, 0 cmp al, 0
je .finish je .finish
@@ -216,7 +213,7 @@ os_format_fat_filename:
inc ch inc ch
jmp .character_loop jmp .character_loop
.add_spaces: ; Add the number of spaces as bl holds .add_spaces: ; Add the number of spaces as bl holds
mov al, ' ' ; 20h = space mov al, ' ' ; 20h = space
; Work out the number of spaces in between ; Work out the number of spaces in between
@@ -231,9 +228,9 @@ os_format_fat_filename:
je .character_loop je .character_loop
dec bl dec bl
jmp .spaces_loop jmp .spaces_loop
.finish: .finish:
popa popa
ret ret
; Does the inverse of the previous ; Does the inverse of the previous
; Converts a fat filename back to human readable ; Converts a fat filename back to human readable
@@ -287,51 +284,53 @@ string_unformat_fat_filename:
popa popa
ret ret
; -------------------------------------------------------------------------------------
; Convert a string to all upper/lower case ; Convert a string to all upper/lower case
; INPUT: si pointing to a string ; INPUT: ax pointing to a string
; OUPUT: the same string in memory will now be capitalised/decapitalised ; OUPUT: the same string in memory will now be capitalised/decapitalised
string_upper_case: ; to upper case string_upper_case: ; to upper case
pusha pusha
mov di, si mov si,ax
mov di,si
.loop: .loop:
lodsb ; Load the character into al lodsb ; Load the character into al
inc di inc di
cmp al, 0 cmp al, 0
je .finish ; If it's null then the string is finished je .finish ; If it's null then the string is finished
cmp al, 7Ah ; 7Ah = 'z' cmp al, 7Ah ; 7Ah = 'z'
jns .loop ; Ignore if it's more than 'z' jns .loop ; Ignore if it's more than 'z'
cmp al, 61h ; 61h = 'a' cmp al, 61h ; 61h = 'a'
js .loop ; Ignore if it's less than 'a' js .loop ; Ignore if it's less than 'a'
sub al, 20h ; Otherwise subtract 20h to capitalise it sub al, 20h ; Otherwise subtract 20h to capitalise it
dec di dec di
stosb ; Store the new value stosb ; Store the new value
jmp .loop ; Next character jmp .loop ; Next character
.finish: .finish:
popa popa
ret
os_lower_case: ; to lower case
pusha
mov di, si
.loop:
lodsb ; Load the character into al
inc di
cmp al, 0
je .finish ; If it's null then the string is finished
cmp al, 5Ah ; 5Ah = 'Z'
jns .loop ; Ignore if it's more than 'Z'
cmp al, 41h ; 41h = 'A'
js .loop ; Ignore if it's less than 'A'
add al, 20h ; Otherwise subtract 20h to capitalise it
dec di
stosb ; Store the new value
jmp .loop ; Next character
.finish:
popa
ret
string_lower_case:
ret ret
string_lower_case: ; to lower case
pusha
mov si, ax
mov di, si
.loop:
lodsb ; Load the character into al
inc di
cmp al, 0
je .finish ; If it's null then the string is finished
cmp al, 5Ah ; 5Ah = 'Z'
jns .loop ; Ignore if it's more than 'Z'
cmp al, 41h ; 41h = 'A'
js .loop ; Ignore if it's less than 'A'
add al, 20h ; Otherwise subtract 20h to capitalise it
dec di
stosb ; Store the new value
jmp .loop ; Next character
.finish:
popa
ret
string_input: string_input:
ret ret
string_print_2hex: string_print_2hex:

0
source/kernel/features/text.asm Executable file → Normal file
View File

View File

@@ -17,7 +17,6 @@ util_cat:
util_ls: util_ls:
pusha pusha
call disk_load_root
call disk_list_contents call disk_list_contents
mov si, output_buffer mov si, output_buffer

View File

@@ -11,11 +11,9 @@ start:
mov si, boot_message mov si, boot_message
call os_print_string call os_print_string
; TESTING mov si, tmp
;mov ax, 3 mov ah, 0Eh
;call string_cast_from_int int 10h
;mov si, stringified_int
;call os_print_string
call os_start_cli call os_start_cli
hlt hlt

View File

@@ -4,8 +4,8 @@ sudo make
sudo chown $(whoami) disk_images/* sudo chown $(whoami) disk_images/*
qemu-system-i386\ qemu-system-i386\
-drive file=disk_images/crawos.img,if=floppy,format=raw\ -drive file=disk_images/crawos.img,if=floppy,format=raw\
-m 512m\ -m 8m\
-object memory-backend-file,id=pc.ram,size=512m,mem-path=/dev/shm/qemu-ram,share=on\ -object memory-backend-file,id=pc.ram,size=8m,mem-path=/dev/shm/qemu-ram,share=on\
-machine memory-backend=pc.ram\ -machine memory-backend=pc.ram\
-d in_asm,int -D ./detailed.log\ -d in_asm,int -D ./detailed.log\
$1 $2 $1 $2