add test suite, help/exit functionality

This commit is contained in:
mtgmonkey 2025-06-27 16:34:26 -04:00
parent a183d55b69
commit 6d162f5ae9
7 changed files with 441 additions and 69 deletions

View file

@ -6,6 +6,9 @@
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
in { in {
packages.${system}.default = pkgs.callPackage ./package.nix {}; packages.${system} = {
default = pkgs.callPackage ./package.nix {};
test = pkgs.callPackage ./test.nix {};
};
}; };
} }

View file

@ -15,8 +15,8 @@ stdenv.mkDerivation {
configurePhase = '' configurePhase = ''
''; '';
buildPhase = '' buildPhase = ''
nasm -f elf main.asm nasm -f elf main_start.asm
ld -m elf_i386 main.o -o main ld -m elf_i386 main_start.o -o main
''; '';
installPhase = '' installPhase = ''
mkdir -p $out/bin mkdir -p $out/bin

View file

@ -7,16 +7,16 @@ slen:
push ebx push ebx
mov ebx, eax mov ebx, eax
slen_nextchar: .loop:
cmp byte [eax], 0 cmp byte [eax], 0
jz slen_finished jz .fin
inc eax inc eax
jmp slen_nextchar jmp .loop
slen_finished: .fin:
sub eax, ebx sub eax, ebx
pop ebx pop ebx
ret ret
; ********************* ; *********************
; clear_bss : BSS -> IO ; clear_bss : BSS -> IO
@ -25,13 +25,13 @@ slen_finished:
clear_bss: clear_bss:
cmp byte [eax], 0 cmp byte [eax], 0
jz clear_bss_finished jz .fin
mov [eax], byte 0 mov [eax], byte 0
inc eax inc eax
jmp clear_bss jmp clear_bss
clear_bss_finished: .fin:
ret ret
; ******************************* ; *******************************
; scmp : String -> String -> Bool ; scmp : String -> String -> Bool
@ -41,25 +41,50 @@ clear_bss_finished:
scmp: scmp:
push ecx push ecx
scmp_nextchar: .loop:
mov cl, byte [ebx] mov cl, byte [ebx]
cmp byte [eax], cl cmp byte [eax], cl
jnz scmp_bad jnz .bad
cmp byte [eax], 0 cmp byte [eax], 0
jz scmp_good jz .good
inc eax inc eax
inc ebx inc ebx
jmp scmp_nextchar jmp .loop
scmp_good: .good:
mov eax, 0 mov eax, 0
jmp scmp_finished jmp .fin
scmp_bad: .bad:
mov eax, 1 mov eax, 1
scmp_finished: .fin:
pop ecx
ret
; ************************
; sdecprint : String -> IO
; prints string 1 shorter
; ************************
sdecprint:
push edx
push ecx
push ebx
push eax
call slen
dec eax
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx pop ecx
pop edx
ret ret
; ********************* ; *********************
@ -104,6 +129,45 @@ sprintln:
pop eax ; initial pop eax ; initial
ret ret
; *******************
; bprint : Byte -> IO
; prints 1 byte
; *******************
bprint:
push edx
push ecx
push ebx
push eax
mov edx, 1
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
; *********************
; bprintln : Byte -> IO
; prints 1 byte + 0Ah
; *********************
bprintln:
call bprint
push eax
mov eax, 0Ah
push eax
mov eax, esp
call bprint
pop eax
pop eax
ret
; ************ ; ************
; exit : IO ; exit : IO
; exit program ; exit program

View file

@ -1,9 +1,18 @@
%include 'lib.asm' %include 'lib.asm'
SECTION .data SECTION .data
welcome_message db 'Welcome to this test program! It is experimental and very subject to change.', 0h s:
prompt db 'Enter a string to print: ', 0h .welcome_message db 'Welcome to this test program! It is experimental and very subject to change.', 0h
exit_str db 'exit', 0Ah, 0h .prompt db 'repl>', 0h
.error db '[ERROR] ', 0h
.command db 'command ', 0h
.not_found db ' not found', 0h
fn:
.exit_name db 'exit', 0Ah, 0h
.help_name db 'help', 0Ah, 0h
.help_content db 'this program has just 2 commands right now: help and exit', 0Ah, ' help - prints this message', 0Ah, ' exit - quits the program', 0Ah, 'Have fun!', 0h
SECTION .bss SECTION .bss
; resb ; 0001 byte ; resb ; 0001 byte
@ -14,48 +23,65 @@ SECTION .bss
sinput: resb 255 ; 255 bytes sinput: resb 255 ; 255 bytes
SECTION .text SECTION .text
global _start start:
_start:
pop ecx ; number of arguments pop ecx ; number of arguments
pop eax ; dispose of first value, the exe pop eax ; dispose of first value, the exe
dec ecx dec ecx
mov eax, welcome_message mov eax, s.welcome_message
call sprintln
.arg_loop:
cmp ecx, 0h
jz .repl
pop eax
call sprintln
dec ecx
jmp .arg_loop
.repl:
mov eax, s.prompt
call sprint
mov eax, sinput
call clear_bss
mov edx, 255 ; max bytes to read
mov ecx, sinput ; buffer
mov ebx, 0 ; (STDIN) where to read from
mov eax, 3 ; (SYS_READ)
int 80h
mov eax, sinput
mov ebx, fn.exit_name
call scmp
cmp byte eax, 0
jz exit
mov eax, sinput
mov ebx, fn.help_name
call scmp
cmp byte eax, 0
jz fn_help
mov eax, s.error
call sprint
mov eax, s.command
call sprint
mov eax, sinput
call sdecprint
mov eax, s.not_found
call sprintln
jmp .repl
fn_help:
push eax
mov eax, fn.help_content
call sprintln call sprintln
next_arg:
cmp ecx, 0h
jz prompt_loop
pop eax pop eax
call sprintln
dec ecx
jmp next_arg
prompt_loop: jmp start.repl
mov eax, prompt
call sprint
mov eax, sinput
call clear_bss
mov edx, 255 ; max bytes to read
mov ecx, sinput ; buffer
mov ebx, 0 ; (STDIN) where to read from
mov eax, 3 ; (SYS_READ)
int 80h
mov eax, sinput
mov ebx, exit_str
call scmp
cmp byte eax, 0
jz done
mov eax, sinput
call sprint
jmp prompt_loop
done:
call exit

7
src/main_start.asm Normal file
View file

@ -0,0 +1,7 @@
%include 'main.asm'
SECTION .text
global _start
_start:
jmp start

242
src/test_start.asm Normal file
View file

@ -0,0 +1,242 @@
%include 'lib.asm'
SECTION .data
ts:
.test_welcome db "Welcome to the test suite!", 0h
.running_test db "running test ", 0h
.ellipsis db "... ", 0h
.error db "[ERROR] ", 0h
.failed db " failed", 0h
.failed_code db " failed with exit code ", 0h
.all_good db "all tests passed", 0h
.divider db "--------------------------------", 0h
slen_zero_length:
.name db "slen_zero_length", 0h
.should db "slen should be 0", 0h
.result db "slen result is ", 0h
scmp_a_zero_length:
.name db "scmp_a_zero_length", 0h
.should db "scmp should be 1", 0h
.result db "scmp result is ", 0h
scmp_b_zero_length:
.name db "scmp_b_zero_length", 0h
.should db "scmp should be 1", 0h
.result db "scmp result is ", 0h
scmp_both_zero_length:
.name db "scmp_both_zero_length", 0h
.should db "scmp should be 0", 0h
.result db "scmp result is ", 0h
scmp_both_short_s:
.name db "scmp_both_short_s", 0h
.should db "scmp should be 0", 0h
.result db "scmp result is ", 0h
cases:
.zero_length db 0h
.short_s db "1 short Test String!", 0h
numerals db "0123456789", 0h
SECTION .text
global _start
_start:
mov eax, ts.test_welcome
call sprintln
mov eax, ts.divider
call sprintln
mov eax, slen_zero_length.name
call sprintln
call slen_zero_length_test
mov eax, ts.divider
call sprintln
mov eax, scmp_a_zero_length.name
call sprintln
call scmp_a_zero_length_test
mov eax, ts.divider
call sprintln
mov eax, scmp_b_zero_length.name
call sprintln
call scmp_b_zero_length_test
mov eax, ts.divider
call sprintln
mov eax, scmp_both_zero_length.name
call sprintln
call scmp_both_zero_length_test
mov eax, ts.divider
call sprintln
mov eax, scmp_both_short_s.name
call sprintln
call scmp_both_short_s_test
mov eax, ts.divider
call sprintln
mov eax, ts.all_good
call sprintln
call exit
slen_zero_length_test:
push eax
mov eax, cases.zero_length
call slen
push eax
mov eax, slen_zero_length.should
call sprintln
mov eax, slen_zero_length.result
call sprint
pop eax
cmp eax, 0
jnz slen_zero_length_test.failed
add eax, numerals
call bprintln
pop eax
ret
.failed:
mov ebx, slen_zero_length.name
call failed_with_code
scmp_a_zero_length_test:
push ebx
push eax
mov eax, cases.zero_length
mov ebx, cases.short_s
call scmp
push eax
mov eax, scmp_a_zero_length.should
call sprintln
mov eax, scmp_a_zero_length.result
call sprint
pop eax
cmp eax, 1
jnz scmp_a_zero_length_test.failed
add eax, numerals
call bprintln
pop eax
pop ebx
ret
.failed:
mov ebx, scmp_a_zero_length.name
call failed_with_code
scmp_b_zero_length_test:
push ebx
push eax
mov eax, cases.short_s
mov ebx, cases.zero_length
call scmp
push eax
mov eax, scmp_b_zero_length.should
call sprintln
mov eax, scmp_b_zero_length.result
call sprint
pop eax
cmp eax, 1
jnz scmp_b_zero_length_test.failed
add eax, numerals
call bprintln
pop eax
pop ebx
ret
.failed:
mov ebx, scmp_b_zero_length.name
call failed_with_code
scmp_both_zero_length_test:
push ebx
push eax
mov eax, cases.zero_length
mov ebx, eax
call scmp
push eax
mov eax, scmp_both_zero_length.should
call sprintln
mov eax, scmp_both_zero_length.result
call sprint
pop eax
cmp eax, 0
jnz scmp_both_zero_length_test.failed
add eax, numerals
call bprintln
pop eax
pop ebx
ret
.failed:
mov ebx, scmp_both_zero_length.name
call failed_with_code
scmp_both_short_s_test:
push ebx
push eax
mov eax, cases.short_s
mov ebx, eax
call scmp
push eax
mov eax, scmp_both_short_s.should
call sprintln
mov eax, scmp_both_short_s.result
call sprint
pop eax
cmp eax, 0
jnz scmp_both_zero_length_test.failed
add eax, numerals
call bprintln
pop eax
pop ebx
ret
.failed:
mov ebx, scmp_both_zero_length.name
call failed_with_code
failed:
mov eax, ts.ellipsis
call sprintln
mov eax, ts.error
call sprint
mov eax, ebx
call sprint
mov eax, ts.failed
call sprint
call exit
failed_with_code:
push eax
mov eax, ts.ellipsis
call sprintln
mov eax, ts.error
call sprint
mov eax, ebx
call sprint
mov eax, ts.failed_code
call sprint
pop eax
add eax, numerals
call bprintln
call exit

30
test.nix Normal file
View file

@ -0,0 +1,30 @@
{
lib,
stdenv,
nasm,
...
}:
stdenv.mkDerivation {
name = "asm-test";
src = ./src;
nativeBuildInputs = [
nasm
];
buildInputs = [
];
configurePhase = ''
'';
buildPhase = ''
nasm -f elf test_start.asm
ld -m elf_i386 test_start.o -o test
'';
installPhase = ''
mkdir -p $out/bin
cp test $out/bin/test
'';
meta = {
license = lib.licenses.wtfpl;
mainProgram = "test";
};
}