Add shell task and terminal application
This commit is contained in:
parent
75eb29613b
commit
3cd4341986
3
build.sh
3
build.sh
|
@ -19,6 +19,9 @@ echo "assembling launcher"
|
||||||
echo "assembling barclock"
|
echo "assembling barclock"
|
||||||
../fox32asm/target/release/fox32asm barclock/main.asm base_image/barclock.fxf
|
../fox32asm/target/release/fox32asm barclock/main.asm base_image/barclock.fxf
|
||||||
|
|
||||||
|
echo "assembling terminal"
|
||||||
|
../fox32asm/target/release/fox32asm terminal/main.asm base_image/terminal.fxf
|
||||||
|
|
||||||
echo "creating wallpapr.raw"
|
echo "creating wallpapr.raw"
|
||||||
../tools/gfx2inc/target/release/gfx2inc 640 480 launcher/wallpaper.png launcher/wallpaper.inc
|
../tools/gfx2inc/target/release/gfx2inc 640 480 launcher/wallpaper.png launcher/wallpaper.inc
|
||||||
../fox32asm/target/release/fox32asm launcher/wallpaper.inc base_image/wallpapr.raw
|
../fox32asm/target/release/fox32asm launcher/wallpaper.inc base_image/wallpapr.raw
|
||||||
|
|
|
@ -35,3 +35,6 @@ seek: jmp [0x00000D14]
|
||||||
tell: jmp [0x00000D18]
|
tell: jmp [0x00000D18]
|
||||||
read: jmp [0x00000D1C]
|
read: jmp [0x00000D1C]
|
||||||
write: jmp [0x00000D20]
|
write: jmp [0x00000D20]
|
||||||
|
|
||||||
|
; shell jump table
|
||||||
|
new_shell_task: jmp [0x00000E10]
|
||||||
|
|
|
@ -55,6 +55,10 @@ jump_table:
|
||||||
data.32 read
|
data.32 read
|
||||||
data.32 write
|
data.32 write
|
||||||
|
|
||||||
|
; shell jump table
|
||||||
|
org.pad 0x00000E10
|
||||||
|
data.32 new_shell_task
|
||||||
|
|
||||||
; initialization code
|
; initialization code
|
||||||
entry:
|
entry:
|
||||||
mov rsp, SYSTEM_STACK
|
mov rsp, SYSTEM_STACK
|
||||||
|
@ -253,6 +257,7 @@ get_os_version:
|
||||||
|
|
||||||
#include "allocator.asm"
|
#include "allocator.asm"
|
||||||
#include "fxf/fxf.asm"
|
#include "fxf/fxf.asm"
|
||||||
|
#include "shell/shell.asm"
|
||||||
#include "task.asm"
|
#include "task.asm"
|
||||||
#include "window/window.asm"
|
#include "window/window.asm"
|
||||||
#include "vfs.asm"
|
#include "vfs.asm"
|
||||||
|
|
53
kernel/shell/commands/commands.asm
Normal file
53
kernel/shell/commands/commands.asm
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
; command parser
|
||||||
|
|
||||||
|
shell_parse_command:
|
||||||
|
mov r0, shell_text_buf_bottom
|
||||||
|
|
||||||
|
; dir
|
||||||
|
mov r1, shell_dir_command_string
|
||||||
|
call compare_string
|
||||||
|
ifz jmp shell_dir_command
|
||||||
|
|
||||||
|
; disk
|
||||||
|
mov r1, shell_disk_command_string
|
||||||
|
call compare_string
|
||||||
|
ifz jmp shell_disk_command
|
||||||
|
|
||||||
|
; diskrm
|
||||||
|
mov r1, shell_diskrm_command_string
|
||||||
|
call compare_string
|
||||||
|
ifz jmp shell_diskrm_command
|
||||||
|
|
||||||
|
; exit
|
||||||
|
mov r1, shell_exit_command_string
|
||||||
|
call compare_string
|
||||||
|
ifz jmp shell_exit_command
|
||||||
|
|
||||||
|
; help
|
||||||
|
mov r1, shell_help_command_string
|
||||||
|
call compare_string
|
||||||
|
ifz jmp shell_help_command
|
||||||
|
|
||||||
|
; type
|
||||||
|
mov r1, shell_type_command_string
|
||||||
|
call compare_string
|
||||||
|
ifz jmp shell_type_command
|
||||||
|
|
||||||
|
; attempt to run a FXF binary
|
||||||
|
call launch_fxf
|
||||||
|
|
||||||
|
; invalid command
|
||||||
|
mov r0, shell_invalid_command_string
|
||||||
|
call print_str_to_terminal
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_invalid_command_string: data.str "invalid command or FXF binary" data.8 10 data.8 0
|
||||||
|
|
||||||
|
; all commands
|
||||||
|
#include "shell/commands/dir.asm"
|
||||||
|
#include "shell/commands/disk.asm"
|
||||||
|
#include "shell/commands/diskrm.asm"
|
||||||
|
#include "shell/commands/exit.asm"
|
||||||
|
#include "shell/commands/help.asm"
|
||||||
|
#include "shell/commands/type.asm"
|
37
kernel/shell/commands/dir.asm
Normal file
37
kernel/shell/commands/dir.asm
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
; dir command
|
||||||
|
|
||||||
|
shell_dir_command_string: data.str "dir" data.8 0
|
||||||
|
|
||||||
|
shell_dir_command:
|
||||||
|
mov r0, shell_dir_command_list_buffer
|
||||||
|
movz.8 r1, [shell_current_disk]
|
||||||
|
call ryfs_get_file_list
|
||||||
|
|
||||||
|
mov r31, r0
|
||||||
|
mov r3, 0
|
||||||
|
shell_dir_command_loop:
|
||||||
|
; copy one file name from the list buffer to the file buffer
|
||||||
|
mov r0, shell_dir_command_list_buffer
|
||||||
|
add r0, r3
|
||||||
|
mov r1, shell_dir_command_file_buffer
|
||||||
|
mov r2, 11
|
||||||
|
call copy_memory_bytes
|
||||||
|
add r1, 11
|
||||||
|
mov.8 [r1], 0
|
||||||
|
|
||||||
|
; then print the file name to the terminal
|
||||||
|
mov r0, shell_dir_command_file_buffer
|
||||||
|
call print_str_to_terminal
|
||||||
|
|
||||||
|
; new line
|
||||||
|
mov r0, 10
|
||||||
|
call print_character_to_terminal
|
||||||
|
|
||||||
|
; point to next file name in the buffer
|
||||||
|
add r3, 11
|
||||||
|
loop shell_dir_command_loop
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_dir_command_list_buffer: data.fill 0, 341
|
||||||
|
shell_dir_command_file_buffer: data.fill 0, 12
|
39
kernel/shell/commands/disk.asm
Normal file
39
kernel/shell/commands/disk.asm
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
; disk command
|
||||||
|
|
||||||
|
shell_disk_command_string: data.str "disk" data.8 0
|
||||||
|
|
||||||
|
shell_disk_command:
|
||||||
|
call shell_parse_arguments
|
||||||
|
mov r1, 10
|
||||||
|
call string_to_int
|
||||||
|
|
||||||
|
; r0: disk ID
|
||||||
|
|
||||||
|
; check if it's in range
|
||||||
|
cmp r0, 3
|
||||||
|
ifgt jmp shell_disk_command_out_of_range
|
||||||
|
|
||||||
|
; OR it with the IO port to get the current insert state of a disk
|
||||||
|
; if no disk is inserted then prompt the user to insert a disk
|
||||||
|
or r0, 0x80001000
|
||||||
|
in r1, r0
|
||||||
|
cmp r1, 0
|
||||||
|
ifz jmp shell_disk_command_insert_disk
|
||||||
|
|
||||||
|
; set the current disk ID
|
||||||
|
mov.8 [shell_current_disk], r0
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_disk_command_out_of_range:
|
||||||
|
mov r0, shell_disk_command_out_of_range_string
|
||||||
|
call print_str_to_terminal
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_disk_command_insert_disk:
|
||||||
|
out r0, 0
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_disk_command_out_of_range_string: data.str "invalid disk ID" data.8 10 data.8 0
|
30
kernel/shell/commands/diskrm.asm
Normal file
30
kernel/shell/commands/diskrm.asm
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
; eject command
|
||||||
|
|
||||||
|
shell_diskrm_command_string: data.str "diskrm" data.8 0
|
||||||
|
|
||||||
|
shell_diskrm_command:
|
||||||
|
call shell_parse_arguments
|
||||||
|
mov r1, 10
|
||||||
|
call string_to_int
|
||||||
|
|
||||||
|
; r0: disk ID
|
||||||
|
|
||||||
|
; check if it's in range
|
||||||
|
cmp r0, 3
|
||||||
|
ifgt jmp shell_diskrm_command_out_of_range
|
||||||
|
|
||||||
|
; OR it with the IO port to remove a disk
|
||||||
|
or r0, 0x80005000
|
||||||
|
|
||||||
|
; remove disk
|
||||||
|
out r0, 0
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_diskrm_command_out_of_range:
|
||||||
|
mov r0, shell_diskrm_command_out_of_range_string
|
||||||
|
call print_str_to_terminal
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_diskrm_command_out_of_range_string: data.str "invalid disk ID" data.8 10 data.8 0
|
6
kernel/shell/commands/exit.asm
Normal file
6
kernel/shell/commands/exit.asm
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
; exit command
|
||||||
|
|
||||||
|
shell_exit_command_string: data.str "exit" data.8 0
|
||||||
|
|
||||||
|
shell_exit_command:
|
||||||
|
call end_current_task
|
27
kernel/shell/commands/help.asm
Normal file
27
kernel/shell/commands/help.asm
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
; help command
|
||||||
|
|
||||||
|
shell_help_command_string: data.str "help" data.8 0
|
||||||
|
|
||||||
|
shell_help_command:
|
||||||
|
mov r0, shell_help_text
|
||||||
|
call print_str_to_terminal
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_help_text:
|
||||||
|
data.str "fox32os shell" data.8 10
|
||||||
|
data.8 10
|
||||||
|
data.str "(in descriptions, $n is argument n)" data.8 10
|
||||||
|
data.str "command | description" data.8 10
|
||||||
|
data.str "------- | -----------" data.8 10
|
||||||
|
data.str "dir | show contents of selected disk" data.8 10
|
||||||
|
data.str "disk | select disk $0" data.8 10
|
||||||
|
data.str "diskrm | remove disk $0" data.8 10
|
||||||
|
data.str "exit | exit the shell" data.8 10
|
||||||
|
data.str "help | show this help text" data.8 10
|
||||||
|
data.str "type | print file $0 of type $1" data.8 10
|
||||||
|
data.8 10
|
||||||
|
data.str "type the name of an FXF binary to launch" data.8 10
|
||||||
|
data.str "it as a new task; the shell will suspend" data.8 10
|
||||||
|
data.str "until the launched task ends" data.8 10
|
||||||
|
data.8 0
|
90
kernel/shell/commands/type.asm
Normal file
90
kernel/shell/commands/type.asm
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
; type command
|
||||||
|
|
||||||
|
shell_type_command_string: data.str "type" data.8 0
|
||||||
|
|
||||||
|
; FIXME: check string length before blindly copying
|
||||||
|
shell_type_command:
|
||||||
|
call shell_parse_arguments
|
||||||
|
|
||||||
|
; r0: file name
|
||||||
|
; r1: file extension
|
||||||
|
|
||||||
|
; copy empty file name
|
||||||
|
push r1
|
||||||
|
push r0
|
||||||
|
mov r0, shell_type_command_file_empty
|
||||||
|
mov r1, shell_type_command_file
|
||||||
|
mov r2, 11
|
||||||
|
call copy_memory_bytes
|
||||||
|
|
||||||
|
; copy file name
|
||||||
|
pop r0
|
||||||
|
mov r1, shell_type_command_file
|
||||||
|
call custom_copy_string
|
||||||
|
|
||||||
|
; copy file extension
|
||||||
|
pop r0
|
||||||
|
mov r1, shell_type_command_file
|
||||||
|
add r1, 8
|
||||||
|
call custom_copy_string
|
||||||
|
add r1, 3
|
||||||
|
mov.8 [r1], 0
|
||||||
|
|
||||||
|
; open the file
|
||||||
|
mov r0, shell_type_command_file
|
||||||
|
movz.8 r1, [shell_current_disk]
|
||||||
|
mov r2, shell_type_command_file_struct
|
||||||
|
call open
|
||||||
|
cmp r0, 0
|
||||||
|
ifz jmp shell_type_command_file_not_found
|
||||||
|
|
||||||
|
mov r0, shell_type_command_file_struct
|
||||||
|
call ryfs_get_size
|
||||||
|
mov r31, r0
|
||||||
|
shell_type_command_loop:
|
||||||
|
mov r0, 1
|
||||||
|
mov r1, shell_type_command_file_struct
|
||||||
|
mov r2, shell_type_command_file_character_buffer
|
||||||
|
call read
|
||||||
|
|
||||||
|
movz.8 r0, [shell_type_command_file_character_buffer]
|
||||||
|
call print_character_to_terminal
|
||||||
|
loop shell_type_command_loop
|
||||||
|
|
||||||
|
mov r0, 10
|
||||||
|
call print_character_to_terminal
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_type_command_file_not_found:
|
||||||
|
mov r0, shell_type_command_file_not_found_string
|
||||||
|
call print_str_to_terminal
|
||||||
|
mov r0, shell_type_command_file
|
||||||
|
call print_str_to_terminal
|
||||||
|
mov r0, 10
|
||||||
|
call print_character_to_terminal
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
custom_copy_string:
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
custom_copy_string_loop:
|
||||||
|
mov.8 r2, [r0]
|
||||||
|
mov.8 [r1], r2
|
||||||
|
inc r0
|
||||||
|
inc r1
|
||||||
|
cmp.8 [r0], 0
|
||||||
|
ifnz jmp custom_copy_string_loop
|
||||||
|
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_type_command_file: data.fill 0, 12
|
||||||
|
shell_type_command_file_empty: data.str " "
|
||||||
|
shell_type_command_file_struct: data.32 0 data.32 0
|
||||||
|
shell_type_command_file_character_buffer: data.8 0
|
||||||
|
shell_type_command_file_not_found_string: data.str "file not found: " data.8 0
|
107
kernel/shell/launch.asm
Normal file
107
kernel/shell/launch.asm
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
; FXF launcher helper routines
|
||||||
|
|
||||||
|
; launch an FXF binary from a shell entry
|
||||||
|
; inputs:
|
||||||
|
; r0-r3: shell arguments
|
||||||
|
; outputs:
|
||||||
|
; none, does not return if task started successfully
|
||||||
|
; returns if FXF file not found
|
||||||
|
launch_fxf:
|
||||||
|
; clear the first 8 characters of the launch_fxf_name buffer
|
||||||
|
push r0
|
||||||
|
mov r0, launch_fxf_spaces
|
||||||
|
mov r1, launch_fxf_name
|
||||||
|
mov r2, 8
|
||||||
|
call copy_memory_bytes
|
||||||
|
pop r0
|
||||||
|
|
||||||
|
; copy the name into the launch_fxf_name buffer
|
||||||
|
mov r1, launch_fxf_name
|
||||||
|
mov r31, 8
|
||||||
|
launch_fxf_name_loop:
|
||||||
|
mov.8 [r1], [r0]
|
||||||
|
inc r0
|
||||||
|
inc r1
|
||||||
|
cmp.8 [r0], 0
|
||||||
|
ifz jmp launch_fxf_name_loop_done
|
||||||
|
loop launch_fxf_name_loop
|
||||||
|
launch_fxf_name_loop_done:
|
||||||
|
; open the file
|
||||||
|
mov r0, launch_fxf_name
|
||||||
|
movz.8 r1, [shell_current_disk]
|
||||||
|
mov r2, launch_fxf_struct
|
||||||
|
call ryfs_open
|
||||||
|
cmp r0, 0
|
||||||
|
ifz ret
|
||||||
|
|
||||||
|
; allocate memory for the binary
|
||||||
|
mov r0, launch_fxf_struct
|
||||||
|
call ryfs_get_size
|
||||||
|
call allocate_memory
|
||||||
|
cmp r0, 0
|
||||||
|
ifz jmp allocate_error
|
||||||
|
mov [launch_fxf_binary_ptr], r0
|
||||||
|
|
||||||
|
; read the file into memory
|
||||||
|
mov r0, launch_fxf_struct
|
||||||
|
mov r1, [launch_fxf_binary_ptr]
|
||||||
|
call ryfs_read_whole_file
|
||||||
|
|
||||||
|
; allocate a 64KiB stack
|
||||||
|
mov r0, 65536
|
||||||
|
call allocate_memory
|
||||||
|
cmp r0, 0
|
||||||
|
ifz jmp allocate_error
|
||||||
|
mov [launch_fxf_stack_ptr], r0
|
||||||
|
|
||||||
|
; push the argument pointers and terminal stream struct pointer to the task's stack
|
||||||
|
call shell_parse_arguments
|
||||||
|
mov r4, rsp
|
||||||
|
mov rsp, [launch_fxf_stack_ptr]
|
||||||
|
add rsp, 65536 ; point to the end of the stack (stack grows down!!)
|
||||||
|
push r3
|
||||||
|
push r2
|
||||||
|
push r1
|
||||||
|
push r0
|
||||||
|
push [shell_terminal_stream_struct_ptr]
|
||||||
|
sub rsp, 65516
|
||||||
|
mov [launch_fxf_stack_ptr], rsp
|
||||||
|
mov rsp, r4
|
||||||
|
|
||||||
|
; relocate the binary
|
||||||
|
mov r0, [launch_fxf_binary_ptr]
|
||||||
|
call parse_fxf_binary
|
||||||
|
|
||||||
|
; create a new task
|
||||||
|
mov r1, r0
|
||||||
|
call get_unused_task_id
|
||||||
|
mov.8 [launch_fxf_task_id], r0
|
||||||
|
mov r2, [launch_fxf_stack_ptr]
|
||||||
|
add r2, 65516 ; point to the end of the stack (stack grows down!!)
|
||||||
|
mov r3, [launch_fxf_binary_ptr]
|
||||||
|
mov r4, [launch_fxf_stack_ptr]
|
||||||
|
call new_task
|
||||||
|
|
||||||
|
; fall-through to launch_fxf_yield_loop
|
||||||
|
|
||||||
|
; loop until the launched task ends
|
||||||
|
launch_fxf_yield_loop:
|
||||||
|
movz.8 r0, [launch_fxf_task_id]
|
||||||
|
call is_task_id_used
|
||||||
|
ifz jmp shell_task_return
|
||||||
|
call yield_task
|
||||||
|
rjmp launch_fxf_yield_loop
|
||||||
|
|
||||||
|
allocate_error:
|
||||||
|
mov r0, out_of_memory_string
|
||||||
|
call print_str_to_terminal
|
||||||
|
ret
|
||||||
|
|
||||||
|
launch_fxf_name: data.str " fxf"
|
||||||
|
launch_fxf_spaces: data.str " "
|
||||||
|
launch_fxf_struct: data.32 0 data.32 0
|
||||||
|
launch_fxf_task_id: data.8 0
|
||||||
|
launch_fxf_binary_ptr: data.32 0
|
||||||
|
launch_fxf_stack_ptr: data.32 0
|
||||||
|
|
||||||
|
out_of_memory_string: data.str "failed to allocate for new task!" data.8 10 data.8 0
|
309
kernel/shell/shell.asm
Normal file
309
kernel/shell/shell.asm
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
; shell routines
|
||||||
|
|
||||||
|
const CURSOR: 138
|
||||||
|
|
||||||
|
; create a new shell task
|
||||||
|
; inputs:
|
||||||
|
; r0: task ID
|
||||||
|
; r1: pointer to stream struct
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
new_shell_task:
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
push r3
|
||||||
|
push r4
|
||||||
|
push r10
|
||||||
|
|
||||||
|
; allocate a 64KiB stack and push the pointer to the stream struct to it
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
mov r0, 65536
|
||||||
|
call allocate_memory
|
||||||
|
add r0, 65532
|
||||||
|
pop r1
|
||||||
|
mov [r0], r1
|
||||||
|
mov r10, r0
|
||||||
|
pop r0
|
||||||
|
|
||||||
|
; then start the task
|
||||||
|
mov r1, shell_task ; initial instruction pointer
|
||||||
|
mov r2, r10 ; initial stack pointer
|
||||||
|
mov r3, 0 ; pointer to task code block to free when task ends
|
||||||
|
; (zero since we don't want to free any code blocks when the task ends)
|
||||||
|
mov r4, r10 ; pointer to task stack block to free when task ends
|
||||||
|
sub r4, 65536 ; point to the start of the stack block that we allocated above
|
||||||
|
call new_task
|
||||||
|
|
||||||
|
pop r10
|
||||||
|
pop r4
|
||||||
|
pop r3
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
; print a character to the terminal
|
||||||
|
; inputs:
|
||||||
|
; r0: ASCII character
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
print_character_to_terminal:
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
|
||||||
|
mov.8 [shell_char_buffer], r0
|
||||||
|
mov r1, [shell_terminal_stream_struct_ptr]
|
||||||
|
mov r2, shell_char_buffer
|
||||||
|
call write
|
||||||
|
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
ret
|
||||||
|
|
||||||
|
; print a string to the terminal
|
||||||
|
; inputs:
|
||||||
|
; r0: pointer to null-terminated string
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
print_str_to_terminal:
|
||||||
|
push r0
|
||||||
|
push r2
|
||||||
|
|
||||||
|
mov r1, [shell_terminal_stream_struct_ptr]
|
||||||
|
mov r2, r0
|
||||||
|
print_str_to_terminal_loop:
|
||||||
|
call write
|
||||||
|
inc r2
|
||||||
|
cmp.8 [r2], 0x00
|
||||||
|
ifnz jmp print_str_to_terminal_loop
|
||||||
|
|
||||||
|
pop r2
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_task:
|
||||||
|
pop [shell_terminal_stream_struct_ptr]
|
||||||
|
shell_task_return:
|
||||||
|
call shell_clear_buffer
|
||||||
|
call shell_print_prompt
|
||||||
|
shell_task_loop:
|
||||||
|
mov r1, [shell_terminal_stream_struct_ptr]
|
||||||
|
mov r2, shell_char_buffer
|
||||||
|
call read
|
||||||
|
|
||||||
|
movz.8 r0, [shell_char_buffer]
|
||||||
|
cmp.8 r0, 0
|
||||||
|
ifnz call shell_task_parse_key
|
||||||
|
|
||||||
|
call yield_task
|
||||||
|
rjmp shell_task_loop
|
||||||
|
|
||||||
|
shell_task_parse_key:
|
||||||
|
; first, check if enter, delete, or backspace was pressed
|
||||||
|
cmp.8 r0, 0x1C ; enter
|
||||||
|
ifz jmp shell_key_down_enter
|
||||||
|
cmp.8 r0, 0x6F ; delete
|
||||||
|
ifz jmp shell_key_down_backspace
|
||||||
|
cmp.8 r0, 0x0E ; backspace
|
||||||
|
ifz jmp shell_key_down_backspace
|
||||||
|
|
||||||
|
; then, overwrite the cursor
|
||||||
|
mov r1, r0
|
||||||
|
mov r0, 8 ; backspace character
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, r1
|
||||||
|
|
||||||
|
; then, add it to the text buffer and print it to the screen
|
||||||
|
call scancode_to_ascii
|
||||||
|
call print_character_to_terminal
|
||||||
|
call shell_push_character
|
||||||
|
|
||||||
|
; finally, print the cursor
|
||||||
|
mov r0, CURSOR
|
||||||
|
call print_character_to_terminal
|
||||||
|
ret
|
||||||
|
shell_key_down_enter:
|
||||||
|
; clear the cursor from the screen
|
||||||
|
mov r0, 8 ; backspace character
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, ' ' ; space character
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, 8 ; backspace character
|
||||||
|
call print_character_to_terminal
|
||||||
|
|
||||||
|
mov r0, 10 ; line feed
|
||||||
|
call print_character_to_terminal
|
||||||
|
|
||||||
|
mov r0, 0
|
||||||
|
call shell_push_character
|
||||||
|
|
||||||
|
call shell_parse_line
|
||||||
|
call shell_clear_buffer
|
||||||
|
|
||||||
|
call shell_print_prompt
|
||||||
|
ret
|
||||||
|
shell_key_down_backspace:
|
||||||
|
; check if we are already at the start of the prompt
|
||||||
|
mov r1, [shell_text_buf_ptr]
|
||||||
|
cmp r1, shell_text_buf_bottom
|
||||||
|
iflteq ret
|
||||||
|
; delete the last character from the screen, draw the cursor, and pop the last character from the buffer
|
||||||
|
mov r0, 8 ; backspace character
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, ' ' ; space character
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, 8 ; backspace character
|
||||||
|
call print_character_to_terminal
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, CURSOR ; cursor
|
||||||
|
call print_character_to_terminal
|
||||||
|
call shell_delete_character
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_print_prompt:
|
||||||
|
movz.8 r0, [shell_current_disk]
|
||||||
|
add r0, '0'
|
||||||
|
call print_character_to_terminal
|
||||||
|
mov r0, shell_prompt
|
||||||
|
call print_str_to_terminal
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_parse_line:
|
||||||
|
; if the line is empty, just return
|
||||||
|
cmp.8 [shell_text_buf_bottom], 0
|
||||||
|
ifz ret
|
||||||
|
|
||||||
|
; separate the command from the arguments
|
||||||
|
; store the pointer to the arguments
|
||||||
|
mov r0, shell_text_buf_bottom
|
||||||
|
mov r1, ' '
|
||||||
|
call shell_tokenize
|
||||||
|
mov [shell_args_ptr], r0
|
||||||
|
|
||||||
|
call shell_parse_command
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
; return tokens separated by the specified character
|
||||||
|
; returns the next token in the list
|
||||||
|
; inputs:
|
||||||
|
; r0: pointer to null-terminated string
|
||||||
|
; r1: separator character
|
||||||
|
; outputs:
|
||||||
|
; r0: pointer to next token or zero if none
|
||||||
|
shell_tokenize:
|
||||||
|
cmp.8 [r0], r1
|
||||||
|
ifz jmp shell_tokenize_found_token
|
||||||
|
|
||||||
|
cmp.8 [r0], 0
|
||||||
|
ifz mov r0, 0
|
||||||
|
ifz ret
|
||||||
|
|
||||||
|
inc r0
|
||||||
|
jmp shell_tokenize
|
||||||
|
shell_tokenize_found_token:
|
||||||
|
mov.8 [r0], 0
|
||||||
|
inc r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
; parse up to 4 arguments into individual strings
|
||||||
|
; for example, "this is a test" will be converted to
|
||||||
|
; r0: pointer to "this" data.8 0
|
||||||
|
; r1: pointer to "is" data.8 0
|
||||||
|
; r2: pointer to "a" data.8 0
|
||||||
|
; r3: pointer to "test" data.8 0
|
||||||
|
; inputs:
|
||||||
|
; none
|
||||||
|
; outputs:
|
||||||
|
; r0: pointer to 1st null-terminated argument, or zero if none
|
||||||
|
; r1: pointer to 2nd null-terminated argument, or zero if none
|
||||||
|
; r2: pointer to 3rd null-terminated argument, or zero if none
|
||||||
|
; r3: pointer to 4th null-terminated argument, or zero if none
|
||||||
|
shell_parse_arguments:
|
||||||
|
push r31
|
||||||
|
|
||||||
|
mov r0, [shell_args_ptr]
|
||||||
|
mov r1, ' '
|
||||||
|
mov r31, 3
|
||||||
|
push r0
|
||||||
|
shell_parse_arguments_loop:
|
||||||
|
call shell_tokenize
|
||||||
|
push r0
|
||||||
|
loop shell_parse_arguments_loop
|
||||||
|
pop r3
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
|
||||||
|
pop r31
|
||||||
|
ret
|
||||||
|
|
||||||
|
; push a character to the text buffer
|
||||||
|
; inputs:
|
||||||
|
; r0: character
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
shell_push_character:
|
||||||
|
push r1
|
||||||
|
|
||||||
|
mov r1, [shell_text_buf_ptr]
|
||||||
|
cmp r1, shell_text_buf_top
|
||||||
|
ifgteq jmp shell_push_character_end
|
||||||
|
mov.8 [r1], r0
|
||||||
|
inc [shell_text_buf_ptr]
|
||||||
|
shell_push_character_end:
|
||||||
|
pop r1
|
||||||
|
ret
|
||||||
|
|
||||||
|
; pop a character from the text buffer and zero it
|
||||||
|
; inputs:
|
||||||
|
; none
|
||||||
|
; outputs:
|
||||||
|
; r0: character
|
||||||
|
shell_delete_character:
|
||||||
|
push r1
|
||||||
|
|
||||||
|
mov r1, [shell_text_buf_ptr]
|
||||||
|
cmp r1, shell_text_buf_bottom
|
||||||
|
iflteq jmp shell_delete_character_end
|
||||||
|
dec [shell_text_buf_ptr]
|
||||||
|
movz.8 r0, [r1]
|
||||||
|
mov.8 [r1], 0
|
||||||
|
shell_delete_character_end:
|
||||||
|
pop r1
|
||||||
|
ret
|
||||||
|
|
||||||
|
; mark the text buffer as empty
|
||||||
|
; inputs:
|
||||||
|
; none
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
shell_clear_buffer:
|
||||||
|
push r0
|
||||||
|
|
||||||
|
; set the text buffer poinrer to the start of the text buffer
|
||||||
|
mov [shell_text_buf_ptr], shell_text_buf_bottom
|
||||||
|
|
||||||
|
; set the first character as null
|
||||||
|
mov r0, [shell_text_buf_ptr]
|
||||||
|
mov.8 [r0], 0
|
||||||
|
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
shell_text_buf_bottom: data.fill 0, 32
|
||||||
|
shell_text_buf_top:
|
||||||
|
shell_text_buf_ptr: data.32 0 ; pointer to the current input character
|
||||||
|
shell_args_ptr: data.32 0 ; pointer to the beginning of the command arguments
|
||||||
|
|
||||||
|
shell_current_disk: data.8 0
|
||||||
|
|
||||||
|
shell_prompt: data.str "> " data.8 CURSOR data.8 0
|
||||||
|
|
||||||
|
shell_terminal_stream_struct_ptr: data.32 0
|
||||||
|
shell_char_buffer: data.32 0
|
||||||
|
|
||||||
|
#include "shell/commands/commands.asm"
|
||||||
|
#include "shell/launch.asm"
|
104
terminal/main.asm
Normal file
104
terminal/main.asm
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
; terminal
|
||||||
|
|
||||||
|
mov r0, window_struct
|
||||||
|
mov r1, window_title
|
||||||
|
mov r2, 320
|
||||||
|
mov r3, 400
|
||||||
|
mov r4, 32
|
||||||
|
mov r5, 32
|
||||||
|
call new_window
|
||||||
|
|
||||||
|
call get_unused_task_id
|
||||||
|
mov.8 [shell_task_id], r0
|
||||||
|
mov r1, stream_struct
|
||||||
|
call new_shell_task
|
||||||
|
|
||||||
|
event_loop:
|
||||||
|
mov r0, window_struct
|
||||||
|
call get_next_window_event
|
||||||
|
|
||||||
|
cmp r0, EVENT_TYPE_MOUSE_CLICK
|
||||||
|
ifz jmp mouse_down
|
||||||
|
|
||||||
|
cmp r0, EVENT_TYPE_KEY_DOWN
|
||||||
|
ifz jmp key_down
|
||||||
|
|
||||||
|
cmp r0, EVENT_TYPE_KEY_UP
|
||||||
|
ifz jmp key_up
|
||||||
|
|
||||||
|
event_loop_end:
|
||||||
|
movz.8 r0, [shell_task_id]
|
||||||
|
call is_task_id_used
|
||||||
|
ifz jmp close_window
|
||||||
|
call yield_task
|
||||||
|
mov.8 [read_buffer], 0
|
||||||
|
rjmp event_loop
|
||||||
|
|
||||||
|
mouse_down:
|
||||||
|
; check if we are attempting to drag or close the window
|
||||||
|
cmp r2, 16
|
||||||
|
iflteq jmp drag_window
|
||||||
|
|
||||||
|
jmp event_loop_end
|
||||||
|
|
||||||
|
key_down:
|
||||||
|
mov r0, r1
|
||||||
|
|
||||||
|
cmp.8 r0, KEY_LSHIFT
|
||||||
|
ifz push event_loop_end
|
||||||
|
ifz jmp shift_pressed
|
||||||
|
cmp.8 r0, KEY_RSHIFT
|
||||||
|
ifz push event_loop_end
|
||||||
|
ifz jmp shift_pressed
|
||||||
|
cmp.8 r0, KEY_CAPS
|
||||||
|
ifz push event_loop_end
|
||||||
|
ifz jmp caps_pressed
|
||||||
|
|
||||||
|
mov.8 [read_buffer], r0
|
||||||
|
|
||||||
|
jmp event_loop_end
|
||||||
|
|
||||||
|
key_up:
|
||||||
|
mov r0, r1
|
||||||
|
|
||||||
|
cmp.8 r0, KEY_LSHIFT
|
||||||
|
ifz push event_loop_end
|
||||||
|
ifz jmp shift_released
|
||||||
|
cmp.8 r0, KEY_RSHIFT
|
||||||
|
ifz push event_loop_end
|
||||||
|
ifz jmp shift_released
|
||||||
|
|
||||||
|
jmp event_loop_end
|
||||||
|
|
||||||
|
drag_window:
|
||||||
|
cmp r1, 8
|
||||||
|
iflteq jmp event_loop_end
|
||||||
|
mov r0, window_struct
|
||||||
|
call start_dragging_window
|
||||||
|
jmp event_loop_end
|
||||||
|
|
||||||
|
close_window:
|
||||||
|
mov r0, window_struct
|
||||||
|
call destroy_window
|
||||||
|
call end_current_task
|
||||||
|
jmp event_loop_end
|
||||||
|
|
||||||
|
window_title: data.str "Terminal" data.8 0
|
||||||
|
window_struct: data.fill 0, 32
|
||||||
|
|
||||||
|
shell_task_id: data.8 0
|
||||||
|
|
||||||
|
stream_struct:
|
||||||
|
data.8 0x00
|
||||||
|
data.16 0x00
|
||||||
|
data.32 0x00
|
||||||
|
data.8 0x01
|
||||||
|
data.32 stream_get_input
|
||||||
|
data.32 stream_write_to_terminal
|
||||||
|
|
||||||
|
#include "stream.asm"
|
||||||
|
#include "text.asm"
|
||||||
|
|
||||||
|
; include system defs
|
||||||
|
#include "../../fox32rom/fox32rom.def"
|
||||||
|
#include "../fox32os.def"
|
14
terminal/stream.asm
Normal file
14
terminal/stream.asm
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
; stream IO routines
|
||||||
|
|
||||||
|
; write a character to the terminal
|
||||||
|
; inputs:
|
||||||
|
; r0: pointer to ASCII character
|
||||||
|
stream_write_to_terminal:
|
||||||
|
mov r0, [r0]
|
||||||
|
jmp print_character_to_terminal
|
||||||
|
|
||||||
|
stream_get_input:
|
||||||
|
mov r0, [read_buffer]
|
||||||
|
ret
|
||||||
|
|
||||||
|
read_buffer: data.32 0
|
227
terminal/text.asm
Normal file
227
terminal/text.asm
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
; text rendering routines
|
||||||
|
|
||||||
|
const TERMINAL_X_SIZE: 40
|
||||||
|
const TERMINAL_Y_SIZE: 25
|
||||||
|
|
||||||
|
const TEXT_COLOR: 0xFFFFFFFF
|
||||||
|
const BACKGROUND_COLOR: 0xFF000000
|
||||||
|
|
||||||
|
; print a single character to the terminal
|
||||||
|
; inputs:
|
||||||
|
; r0: ASCII character
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
print_character_to_terminal:
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
|
||||||
|
cmp.8 r0, 0 ; null
|
||||||
|
ifz jmp print_character_to_terminal_end
|
||||||
|
cmp.8 r0, 8 ; backspace
|
||||||
|
ifz jmp print_character_to_terminal_bs
|
||||||
|
cmp.8 r0, 10 ; line feed
|
||||||
|
ifz jmp print_character_to_terminal_lf
|
||||||
|
cmp.8 r0, 13 ; carriage return
|
||||||
|
ifz jmp print_character_to_terminal_cr
|
||||||
|
|
||||||
|
; check if we are at the end of this line
|
||||||
|
cmp.8 [terminal_x], TERMINAL_X_SIZE
|
||||||
|
; if so, increment to the next line
|
||||||
|
ifgteq mov.8 [terminal_x], 0
|
||||||
|
ifgteq inc.8 [terminal_y]
|
||||||
|
|
||||||
|
; check if we need to scroll the display
|
||||||
|
cmp.8 [terminal_y], TERMINAL_Y_SIZE
|
||||||
|
ifgteq call scroll_terminal
|
||||||
|
|
||||||
|
; calculate coords for character...
|
||||||
|
movz.8 r1, [terminal_x]
|
||||||
|
movz.8 r2, [terminal_y]
|
||||||
|
mul r2, TERMINAL_X_SIZE
|
||||||
|
add r1, r2
|
||||||
|
add r1, terminal_text_buf
|
||||||
|
|
||||||
|
; ...and print!!
|
||||||
|
mov.8 [r1], r0
|
||||||
|
inc.8 [terminal_x]
|
||||||
|
jmp print_character_to_terminal_end
|
||||||
|
print_character_to_terminal_cr:
|
||||||
|
; return to the beginning of the line
|
||||||
|
mov.8 [terminal_x], 0
|
||||||
|
jmp print_character_to_terminal_end
|
||||||
|
print_character_to_terminal_lf:
|
||||||
|
; return to the beginning of the line and increment the line
|
||||||
|
mov.8 [terminal_x], 0
|
||||||
|
inc.8 [terminal_y]
|
||||||
|
; scroll the display if needed
|
||||||
|
cmp.8 [terminal_y], TERMINAL_Y_SIZE
|
||||||
|
ifgteq call scroll_terminal
|
||||||
|
jmp print_character_to_terminal_end
|
||||||
|
print_character_to_terminal_bs:
|
||||||
|
; go back one character
|
||||||
|
cmp.8 [terminal_x], 0
|
||||||
|
ifnz dec.8 [terminal_x]
|
||||||
|
print_character_to_terminal_end:
|
||||||
|
call redraw_terminal_line
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
; scroll the terminal
|
||||||
|
; inputs:
|
||||||
|
; none
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
scroll_terminal:
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
push r31
|
||||||
|
|
||||||
|
; source
|
||||||
|
mov r0, terminal_text_buf
|
||||||
|
add r0, TERMINAL_X_SIZE
|
||||||
|
|
||||||
|
; destination
|
||||||
|
mov r1, terminal_text_buf
|
||||||
|
|
||||||
|
; size
|
||||||
|
mov r2, TERMINAL_X_SIZE
|
||||||
|
mul r2, 24
|
||||||
|
div r2, 4
|
||||||
|
|
||||||
|
call copy_memory_words
|
||||||
|
|
||||||
|
mov.8 [terminal_x], 0
|
||||||
|
mov.8 [terminal_y], 24
|
||||||
|
|
||||||
|
; clear the last line
|
||||||
|
mov r0, terminal_text_buf
|
||||||
|
add r0, 960 ; 40 * 24
|
||||||
|
mov r31, TERMINAL_X_SIZE
|
||||||
|
scroll_terminal_clear_loop:
|
||||||
|
mov.8 [r0], 0
|
||||||
|
inc r0
|
||||||
|
loop scroll_terminal_clear_loop
|
||||||
|
|
||||||
|
; redraw the screen
|
||||||
|
call redraw_terminal
|
||||||
|
|
||||||
|
pop r31
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
; redraw the whole terminal
|
||||||
|
; inputs:
|
||||||
|
; none
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
redraw_terminal:
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
push r3
|
||||||
|
push r4
|
||||||
|
push r5
|
||||||
|
push r6
|
||||||
|
push r31
|
||||||
|
|
||||||
|
mov r0, window_struct
|
||||||
|
call get_window_overlay_number
|
||||||
|
mov r5, r0
|
||||||
|
|
||||||
|
mov r0, terminal_text_buf
|
||||||
|
mov r1, 0
|
||||||
|
mov r2, 16
|
||||||
|
mov r3, TEXT_COLOR
|
||||||
|
mov r4, BACKGROUND_COLOR
|
||||||
|
mov r31, TERMINAL_Y_SIZE
|
||||||
|
redraw_terminal_loop_y:
|
||||||
|
push r31
|
||||||
|
mov r1, 0
|
||||||
|
mov r31, TERMINAL_X_SIZE
|
||||||
|
redraw_terminal_loop_x:
|
||||||
|
push r0
|
||||||
|
movz.8 r0, [r0]
|
||||||
|
call draw_font_tile_to_overlay
|
||||||
|
movz.8 r0, 8
|
||||||
|
add r1, r0
|
||||||
|
pop r0
|
||||||
|
inc r0
|
||||||
|
loop redraw_terminal_loop_x
|
||||||
|
pop r31
|
||||||
|
movz.8 r6, 16
|
||||||
|
add r2, r6
|
||||||
|
loop redraw_terminal_loop_y
|
||||||
|
|
||||||
|
pop r31
|
||||||
|
pop r6
|
||||||
|
pop r5
|
||||||
|
pop r4
|
||||||
|
pop r3
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
; redraw only the current line
|
||||||
|
; inputs:
|
||||||
|
; none
|
||||||
|
; outputs:
|
||||||
|
; none
|
||||||
|
redraw_terminal_line:
|
||||||
|
push r0
|
||||||
|
push r1
|
||||||
|
push r2
|
||||||
|
push r3
|
||||||
|
push r4
|
||||||
|
push r5
|
||||||
|
push r6
|
||||||
|
push r31
|
||||||
|
|
||||||
|
mov r0, window_struct
|
||||||
|
call get_window_overlay_number
|
||||||
|
mov r5, r0
|
||||||
|
|
||||||
|
movz.8 r0, [terminal_y]
|
||||||
|
mul r0, TERMINAL_X_SIZE
|
||||||
|
add r0, terminal_text_buf
|
||||||
|
|
||||||
|
movz.8 r1, [terminal_y]
|
||||||
|
mov r2, 16
|
||||||
|
mul r2, r1
|
||||||
|
add r2, 16
|
||||||
|
|
||||||
|
mov r1, 0
|
||||||
|
mov r3, TEXT_COLOR
|
||||||
|
mov r4, BACKGROUND_COLOR
|
||||||
|
|
||||||
|
mov r1, 0
|
||||||
|
mov r31, TERMINAL_X_SIZE
|
||||||
|
redraw_terminal_line_loop_x:
|
||||||
|
push r0
|
||||||
|
movz.8 r0, [r0]
|
||||||
|
call draw_font_tile_to_overlay
|
||||||
|
movz.8 r0, 8
|
||||||
|
add r1, r0
|
||||||
|
pop r0
|
||||||
|
inc r0
|
||||||
|
loop redraw_terminal_line_loop_x
|
||||||
|
|
||||||
|
pop r31
|
||||||
|
pop r6
|
||||||
|
pop r5
|
||||||
|
pop r4
|
||||||
|
pop r3
|
||||||
|
pop r2
|
||||||
|
pop r1
|
||||||
|
pop r0
|
||||||
|
ret
|
||||||
|
|
||||||
|
terminal_x: data.8 0
|
||||||
|
terminal_y: data.8 0
|
||||||
|
terminal_text_buf: data.fill 0, 1000 ; 40x25 = 1000 bytes
|
Loading…
Reference in New Issue
Block a user