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

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

@@ -1,11 +1,11 @@
; How string comparison works
; DI => scasb compares value stored in DI which is 's' with 's' stored in AX register
; and then increments DI if DF is 0
; v
; memory address 1: |s|n|a|t|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
; DI => scasb compares value stored in DI which is 's' with 's' stored in AX register
; and then increments DI if DF is 0
; v
; memory address 1: |s|n|a|t|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
; 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
os_compare_strings:
@@ -44,22 +44,22 @@ os_compare_strings:
string_direct_compare:
pusha
.more:
mov al, [si] ; Retrieve string contents
mov al, [si] ; Retrieve string contents
mov bl, [di]
cmp al, bl ; Compare characters at current location
cmp al, bl ; Compare characters at current location
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
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
.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
.end: ; Both strings terminated at the same position
popa
stc ; Set carry flag
stc ; Set carry flag
ret
@@ -73,8 +73,8 @@ 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
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:
@@ -107,89 +107,84 @@ string_join:
; ------------------------------------------------------------------------
; 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:
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
; OUT: AX = string location (output buffer)
string_cast_from_int: ; TODO I think this algorithm could be optimised
string_cast_from_int:
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
cld ; Write backwards
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
mov bx, 10 ; base
mov cx, 0 ; digit count
.convert_loop:
xor dx, dx
div bx ; AX = AX / base, DX = remainder
add dl, '0'
push dx ; store digit
inc cx
test ax, ax
jnz .convert_loop
.write_loop:
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:
loop .write_loop
mov al, 0
stosb ; null terminator
popa
;mov ax, stringified_int
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
; input: ax points to the string to be counted
; output: ax holds the length
string_length:
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:
lodsb
cmp al, 0
je .finish
inc cl
inc cx
jmp .loop
.finish:
mov ax, cx
pop cx
pop si
ret
@@ -198,15 +193,17 @@ string_length:
; convert a string to fat's filename format
; It will be capitalised and 11 characters long,
; 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
; output: di points to the fat formatted filename
os_format_fat_filename:
pusha
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:
pusha
call string_upper_case
mov ax, si
call string_length ; Stores the length of the string in cl
mov cl, al
xor ch,ch ; Clear ch to reset it to 0
.character_loop:
lodsb
cmp al, 0
je .finish
@@ -215,10 +212,10 @@ os_format_fat_filename:
stosb
inc ch
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
; Work out the number of spaces in between
; the name and extension.
; 8 - name_length(ch)
@@ -231,9 +228,9 @@ os_format_fat_filename:
je .character_loop
dec bl
jmp .spaces_loop
.finish:
popa
ret
.finish:
popa
ret
; Does the inverse of the previous
; Converts a fat filename back to human readable
@@ -287,51 +284,53 @@ string_unformat_fat_filename:
popa
ret
; -------------------------------------------------------------------------------------
; 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
string_upper_case: ; to upper case
pusha
mov di, si
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, 7Ah ; 7Ah = 'z'
jns .loop ; Ignore if it's more than 'z'
cmp al, 61h ; 61h = 'a'
js .loop ; Ignore if it's less than 'a'
sub al, 20h ; Otherwise subtract 20h to capitalise it
dec di
stosb ; Store the new value
jmp .loop ; Next character
lodsb ; Load the character into al
inc di
cmp al, 0
je .finish ; If it's null then the string is finished
cmp al, 7Ah ; 7Ah = 'z'
jns .loop ; Ignore if it's more than 'z'
cmp al, 61h ; 61h = 'a'
js .loop ; Ignore if it's less than 'a'
sub al, 20h ; Otherwise subtract 20h to capitalise it
dec di
stosb ; Store the new value
jmp .loop ; Next character
.finish:
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:
popa
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:
ret
string_print_2hex: