@use monitor_macros:* @include %bootstrap @include %std_mem @include %std_bump_alloc ; Constants @data text_buffer_size 2048 @data input_buffer_size 37 ; 1 word length + 40 chars row - 2 padding - 2 for prompt @data second_line_idx 5 @data last_line_idx 28 ; Global variable pointers ; 0xE800 - 0xEFFF: sprite buffer space that is unused in this program @data text_cursor_p 0xE800 @data input_string_p 0xE801 @data input_entered_bool 0xE802 @data title_string " »» ATK16 monitor v0.1 »»" @data subtitle_string " Run [help] for a list of commands" @data prompt_string " > " @data help_cmd_string "help" @data clear_cmd_string "clear" @label main ; initialize heap allocator calli bump_reset ; initialize global variables ; BEGIN text_cursor_p = text_mem ldi text_cursor_p RA ldr RA RA ldi vt_text_mem RB ldr RB RB str RB RA ; END ; BEGIN input_string_p = alloc(input_buffer_size) ldi input_buffer_size RA calli bump_alloc ldi input_string_p RA ldr RA RA str RG RA ; END ; BEGIN input_entered_bool = 0 ldi input_entered_bool RA ldr RA RA ldi 0 RB str RB RA ; END ; use memset to clear the text buffer ; BEGIN memset(text_mem, 0, text_buffer_size) ldi vt_text_mem RA ; RA := text buffer address pointer ldr RA RA ; RA := text buffer address ldi text_buffer_size RB ; RB := pointer to text_buffer_size ldr RB RB ; RB := text_buffer_size ldi 0 RC ; RC := 0 calli memset ; END set_graphics_mode gr_text_mode m_newline RA RB ; BEGIN put_string(title_string) ldi title_string RA call put_string ; END m_newline RA RB ; BEGIN put_string(subtitle_string) ldi subtitle_string RA call put_string ; END ; BEGIN print two newlines ldi text_cursor_p RA ldr RA RA ldr RA RB slri RB 6 RB addi RB 2 RB slli RB 6 RB str RB RA ; END ldi prompt_string RA call put_string ; loop forever @label loop ; BEGIN critical section m_set_critical RA RB ldi input_entered_bool RA ldr RA RA ldr RA RB subi RB 0 RB bri zero loop_finally ; if input_entered_bool == 0 (false), skip to end of loop @label loop_input_was_entered ldi 0 RB ; else, run this branch and set input_entered_bool = 0 str RB RA ldi help_cmd_string RA ldi input_string_p RB ldr RB RB ldr RB RB calli string_compare subi RG 1 RG bri zero loop_input_cmd_help ldi clear_cmd_string RA call string_compare subi RG 1 RG bri zero loop_input_cmd_clear @label loop_input_not_recognized @data eval_output_string "Unknown command: " ldi eval_output_string RA call put_string ldi input_string_p RA ldr RA RA ldr RA RA ldi put_string_p RB ldr RB RB callr RB jpi loop_input_print_newline @label loop_input_cmd_help @data help_cmd_output_0 "Available commands:\n help display this help message\n clear clear the text area" ldi help_cmd_output_0 RA ldi put_string_p RB ldr RB RB callr RB jpi loop_input_print_newline @label loop_input_cmd_clear ; BEGIN memset(first_line_p, 24 * 64, 0) ldi second_line_idx RB ldr RB RB ; RB := second line index subi RB 1 RD ; RD := first line index ldi vt_text_mem RC ldr RC RC ; RC := text_mem address (0xF800) slli RD 6 RD ; RD := first line index * 64 add RD RC RA ; RD := pointer to the first line start in the 0xF800-0xFFFF range @data loop_input_cmd_clear_memset_length ${24 * 64} ldi loop_input_cmd_clear_memset_length RB ldr RB RB ldi 0 RC call memset ldi text_cursor_p RB ldr RB RB ldi 1 RC slli RC 6 RC sub RA RC RA str RA RB ; END jpi loop_input_print_newline @label loop_input_print_newline call newline ldi prompt_string RA ldi put_string_p RB ldr RB RB callr RB @label loop_finally m_clear_critical RA RB ; END critical section jpi loop @data row_index_mask ${64 - 1} @label keyboard_isr stack_stash RA RB RC RD ldi vt_kb_pp_addr RA ; RA := Keyboard peripheral address pointer ldr RA RA ; RA := Keyboard peripheral address deref ldr RA RA ; RA := <16-bit keyboard character code> from MMIO register ldi text_cursor_p RC ldr RC RC ldr RC RC ; RC := text_cursor_p mov RA RB ; RB := character code subi RB 5 RB ; if code == 10 (enter key), then run newline handler subi RB 5 RB bri zero keyboard_isr_newline mov RA RB ; if code == 8 (backspace key), then run backspace handler subi RB 5 RB subi RB 3 RB @data keyboard_isr_backspace_p keyboard_isr_backspace ldi keyboard_isr_backspace_p RD ldr RD RD brr zero RD ldi row_index_mask RD ; else code is not special; write it to text buffer ldr RD RD ; if cursor is not at the end of the line and RC RD RD @data row_index_line_end 39 ldi row_index_line_end RB ldr RB RB ; RB := 39 sub RD RB RB ; if row index == 39, then skip writing character bri zero keyboard_isr_end m_put_char RA RB RC ; if row index < 39, then write character to text buffer jpi keyboard_isr_end @label keyboard_isr_newline ; note: RC = text_cursor_p, set in the beginning of the ISR ldi row_index_mask RA ldr RA RA and RC RA RD ; RD := RC & 63, i.e. the position on the row subi RD 3 RD ; RD = length of the command string ldi input_string_p RB ldr RB RB ; RB := &input_string_p ldr RB RB ; RB := input_string_p str RD RB ; input_string_p length := RD (length of command string) ; BEGIN memcopy(last_line_p, input_string_p, input_buffer_size) slri RC 6 RC ; RC := RC >> 6, i.e. the row index slli RC 6 RC ; RC := RC << 6, i.e. index to the first character of the row addi RC 3 RA ; RA := RC + 3, i.e. index to the first character of the input string addi RB 1 RB ; RB := input_string_p + 1, i.e. index to the first character of the input string mov RD RC ldi memcopy RD callr RD ; END ; BEGIN input_entered_bool = 1 ldi input_entered_bool RA ldr RA RA ldi 1 RB str RB RA ; END call newline ldi ${ord(" ")} RA m_put_char RA RB RD jpi keyboard_isr_end @label keyboard_isr_backspace ; check that RC is not at the beginning of the prompt ldi row_index_mask RD ldr RD RD and RC RD RD ; RD := RC & 63, i.e. the position on the row subi RD 3 RD ; if RD == 3, then skip bri zero keyboard_isr_end ldi ${ord(" ")} RD ; then move the cursor back one space and write a space to the screen subi RC 1 RC str RD RC ldi text_cursor_p RD ldr RD RD str RC RD @label keyboard_isr_end stack_restore RA RB RC RD rti ;;; Subroutines @data newline_p newline @label newline ; move cursor to the beginning of the next line ; parameters: none ; returns: none stack_stash RA RB RC RD ; check if the cursor is at the last line ldi text_cursor_p RA ldr RA RA ; RA := &text_cursor_p ldr RA RB ; RB := text_cursor_p ldi vt_text_mem RC ldr RC RC ; RC := text_mem address (0xF800) sub RB RC RB ; RB := difference from start of text_mem slri RB 6 RB ; RB := RB >> 6, i.e. the row index ldi last_line_idx RC ldr RC RC ; RC := last_line_idx sub RB RC RC bri zero newline_at_last_line ; if row index == last_line_idx, go to newline_at_last_line m_newline RA RB jpi newline_end @label newline_at_last_line ldi second_line_idx RB ldr RB RB ; RB := second line index subi RB 1 RD ; RD := first line index slli RB 6 RB ; RB ;= second line index * 64 ldi vt_text_mem RC ldr RC RC ; RC := text_mem address (0xF800) add RB RC RB ; RB := pointer to the second line start in the 0xF800-0xFFFF range slli RD 6 RD ; RD := first line index * 64 add RD RC RD ; RD := pointer to the first line start in the 0xF800-0xFFFF range ; BEGIN memcopy(second_line_p, first_line_p, 24 * 64) @data newline_memcopy_length ${24 * 64} mov RB RA mov RD RB mov RC RD ; preserve text_mem address in RD ldi newline_memcopy_length RC ldr RC RC call memcopy ; END ; BEGIN memset(last_line_p, 0, 64) ldi last_line_idx RA ldr RA RA slli RA 6 RA add RA RD RA ; pointer to last row in text mem ldi 1 RB slli RB 6 RB ; RB := 64 ldi 0 RC call memset ldi text_cursor_p RB ; set text_cursor_p to beginning of last line ldr RB RB str RA RB ; END @label newline_end stack_restore RA RB RC RD return @data put_char_p put_char @label put_char ; put a character at the current cursor position ; parameters: RA = character to put ; returns: none stack_stash RB RC m_put_char RA RB RC stack_restore RB RC return @data put_string_p put_string @label put_string ; put a string at the current cursor position ; parameters: RA = string_p ; returns: none stack_stash RA RB RC RD RE ldr RA RB ; RB := string length addi RA 1 RC ; RC := pointer to first character @label put_string_loop subi RB 0 RB ; exit if RB has been decremented to 0 bri zero put_string_done ; i.e. no more characters to put ldr RC RA ; RA := character to put subi RA 5 RD subi RD 5 RD bri zero put_string_newline m_put_char RA RD RE ; inline the put_char subroutine to avoid function call overhead jpi put_string_continue @label put_string_newline call newline @label put_string_continue inc RC subi RB 1 RB jpi put_string_loop @label put_string_done stack_restore RA RB RC RD RE return ;; Install the ISR ;; note: clobbers current ROM address, best to keep at the end of the source @address vt_ISR0 @begin_override keyboard_isr @end_override