.include "instr.inc" .text mem_e000: .2byte 0 mem_e002: .fill 0x0efe mem_ef00: .fill 0xb0 mem_efb0: .fill 0x30 mem_efe0: .fill 0x20 .global _start _start: ld_sp stack # Set up the entrypoint for the breakpoint instruction ld_r0 entry ld_r1 0x98 # 'call' strb_r1_r0 inc_r0 ld_r1 main str_r1_r0 # Explain the debugger ld_r2 str_intro call PrintStr # Execute the breakpoint, jump to main brk # If breakpoint returns here, we've ran out of stack ld_r2 str_dry_stack call PrintStr copr_halt die: jp die main: # Save all the registers push_sp str_r0 mem_save_r0 str_r1 mem_save_r1 str_r2 mem_save_r2 str_r3 mem_save_r3 # Store the stack pointer pop_r0 str_r0 mem_info_sp # Read the return address ld_r0_sp ldr_r0_r0 add_r0 -1 str_r0 mem_info_pc # Read some more stack values ld_r1 (6 * 2) ld_r2 mem_info_stack ldr_r3 mem_info_sp call MemCpy # Print the processor state ld_r2 str_breakpoint call PrintStr main_cmd_loop: ld_r0 0 strb_r0 mem_e002 ld_r2 str_ready call PrintStr # Read input ld_r2 mem_e000 ld_r3 8 call ReadStr cp_r3 0 jp_eq error_too_long ld_r2 mem_e000 call StrTrim # Make sure the input is at most 2 characters ldrb_r0 mem_e002 cp_r0 0 jp_ne error_bad_command # Parse all the commands ldr_r0 mem_e000 cp_r0 ('h' + '\n' * 0x100) jp_eq cmd_help cp_r0 'h' jp_eq cmd_help cp_r0 'r' jp_eq cmd_read cp_r0 ('r' + '\n' * 0x100) jp_eq cmd_read cp_r0 'p' jp_eq cmd_print cp_r0 ('p' + '\n' * 0x100) jp_eq cmd_print cp_r0 'x' jp_eq cmd_exec cp_r0 ('x' + '\n' * 0x100) jp_eq cmd_exec cp_r0 'c' jp_eq cmd_cont cp_r0 ('c' + '\n' * 0x100) jp_eq cmd_cont cp_r0 ('U' + 'C' * 0x100) jp_eq cmd_undoc cp_r0 ('l' + 's' * 0x100) jp_eq cmd_list cp_r0 ('r' + 'f' * 0x100) jp_eq cmd_readfile cp_r0 'w' jp_eq cmd_write cp_r0 ('w' + '\n' * 0x100) jp_eq cmd_write error_bad_command: ld_r2 str_error_bad_command call PrintStr jp main_cmd_loop error_too_long: ld_r2 str_error_too_long call PrintStr jp main_cmd_loop cmd_help: ld_r2 str_help call PrintStr jp main_cmd_loop cmd_read: # Get the address to read ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex cp_r0 -1 jp_eq cmd_read_invalid push_r0 # Get the amount of lines to read ld_r2 str_prompt_lines call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex cp_r0 -1 jp_eq cmd_read_invalid cp_r0 0 jp_eq cmd_read_invalid # r2 = address, r3 = lines ld_r3_r0 pop_r2 cmd_read_loop: # Read 8 bytes push_r2 push_r3 ld_r1 8 ld_r3_r2 ld_r2 mem_data call MemCpy pop_r3 pop_r2 # Store the current read address str_r2 mem_addr # Write it out push_r2 push_r3 ld_r2 str_hexdump call PrintStr pop_r3 pop_r2 # Check if we're done add_r2 8 dec_r3 cp_r3 0 jp_ne cmd_read_loop jp main_cmd_loop cmd_read_invalid: ld_r2 str_error_invalid_input call PrintStr jp main_cmd_loop cmd_write: # Get the address to write ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex cp_r0 -1 jp_eq cmd_write_invalid str_r0 mem_addr ld_r2 str_prompt_hex call PrintStr cmd_write_loop: # Clear the buffer ld_r3 mem_e000 + 2 ld_r0 0 strb_r0_r3 dec_r3 strb_r0_r3 dec_r3 cmd_write_loop_first: # Get the first nybble copr_getc strb_r0_r3 ld_r2_r3 # Check if it's the end or invalid cp_r0 '.' jp_eq cmd_write_end cp_r0 '\n' jp_eq cmd_write_loop_first # Try to convert the nybble push_r3 call ConvertHex pop_r3 cp_r0 -1 jp_eq cmd_write_loop_first inc_r3 cmd_write_loop_second: # Get the second nybble copr_getc strb_r0_r3 ld_r2_r3 # Check if it's the end or invalid cp_r0 '.' jp_eq cmd_write_end cp_r0 '\n' jp_eq cmd_write_loop_second # Try to convert the nybble push_r3 call ConvertHex pop_r3 cp_r0 -1 jp_eq cmd_write_loop_second # Convert and store the full byte dec_r3 ld_r2_r3 call ConvertHex ldr_r1 mem_addr strb_r0_r1 inc_r1 str_r1 mem_addr jp cmd_write_loop # 0xf1d0 cmd_write_end: # Read until newline copr_getc cp_r0 '\n' jp_ne cmd_write_end ld_r2 str_loaded call PrintStr jp main_cmd_loop cmd_write_invalid: ld_r2 str_error_invalid_input call PrintStr jp main_cmd_loop cmd_print: # Get the string to print ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex ld_r2_r0 call PrintStr jp main_cmd_loop cmd_exec: # Get the address to execute ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex # Prompt the user to execute the address push_r0 str_r0 mem_addr ld_r2 str_prompt_exec call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ldrb_r0 mem_e000 cp_r0 'Y' jp_eq cmd_exec_go cp_r0 'y' jp_eq cmd_exec_go # Cancel the call ld_r2 str_error_cancelled call PrintStr pop_r0 jp main_cmd_loop cmd_exec_go: # Call the function pop_r3 call_r3 jp main_cmd_loop cmd_cont: ld_r2 str_continuing call PrintStr # Restore register state and unpause ldr_r0 mem_save_r0 ldr_r1 mem_save_r1 ldr_r2 mem_save_r2 ldr_r3 mem_save_r3 ret cmd_list: ld_r2 str_file_list_header call PrintStr # Read directory ld_r0 0 ld_r1 mem_e000 copr_readblk ld_r3 mem_e000 cmd_list_loop: ldrb_r0_r3 cp_r0 0 jp_eq cmd_list_next # Store block push_r3 strb_r0 mem_data inc_r3 ldr_r0_r3 # Store size push_r3 str_r0 mem_addr # Print block and size ld_r2 str_file_list_entry call PrintStr pop_r3 # Print filename inc_r3 inc_r3 ld_r2_r3 call PrintStr ld_r0 '\n' copr_putc pop_r3 cmd_list_next: add_r3 0x10 cp_r3 mem_ef00 jp_ne cmd_list_loop jp main_cmd_loop cmd_undoc: ld_r2 str_undoc call PrintStr jp main_cmd_loop cmd_readfile: ld_r2 str_prompt_filename call PrintStr # Read directory ld_r0 0 ld_r1 mem_e000 copr_readblk # Get filename to read ld_r2 mem_efb0 ld_r3 0x10 call ReadStr cp_r3 0 jp_eq cmd_readfile_error_too_long ld_r2 mem_efb0 call StrTrim ld_r3 mem_e000 + 3 cmd_readfile_loop: # Check if the filename matches ld_r2 mem_efb0 push_r3 call StrCmp pop_r3 cp_r0 0 jp_eq cmd_readfile_match add_r3 0x10 cp_r3 mem_ef00 + 3 jp_eq cmd_readfile_error_not_found jp cmd_readfile_loop cmd_readfile_match: # Read file block dec_r3 dec_r3 dec_r3 ldrb_r0_r3 # Read file size inc_r3 ldr_r1_r3 # Read file into memory push_r1 ld_r1 mem_e000 copr_readblk # Prompt for address to write to ld_r2 str_prompt_address call PrintStr ld_r2 mem_efe0 ld_r3 0x10 call ReadStr ld_r2 mem_efe0 call ConvertHex pop_r1 cp_r0 -1 jp_eq cmd_readfile_error_invalid_input # Copy the data ld_r2_r0 ld_r3 mem_e000 call MemCpy ld_r2 str_loaded call PrintStr jp main_cmd_loop cmd_readfile_error_not_found: ld_r2 str_error_not_found call PrintStr jp main_cmd_loop cmd_readfile_error_too_long: ld_r2 str_error_too_long call PrintStr jp main_cmd_loop cmd_readfile_error_invalid_input: ld_r2 str_error_invalid_input call PrintStr jp main_cmd_loop str_intro: .ascii "======================================================================\n" .ascii "Welcome to Glitch Research Laboratory Network: Test Server 1 (GRLTS01)\n" .ascii "======================================================================\n" .ascii "GRLTS01 is intended for research purposes only, to aid our engineers\n" .ascii "in migrating their software to the latest GLVM architecture.\n" .ascii "This server will boot into a machine language monitor, providing\n" .ascii "a simple environment for development and testing.\n" .ascii "\n" .ascii "Note that this server is publicly accessible, and thus, should never\n" .ascii "be used to store any confidential information. If storage of data is\n" .ascii "necessary, consider utilizing encryption, or using our dedicated\n" .ascii "storage servers GRLFS01.\n" .ascii "Additionally, consider using GRLTS02 for any serious work - it's\n" .ascii "an authenticated test server, which should be far more secure.\n" .ascii "======================================================================\n" .ascii "Running machine language monitor now.\n" .ascii "======================================================================\n" .ascii "\n\0" str_error_too_long: .ascii "! Input too long error.\n\0" str_error_bad_command: .ascii "! Bad command error (h for help).\n\0" str_error_invalid_input: .ascii "! Invalid input error.\n\0" str_prompt_address: .ascii "> Which address? \0" str_prompt_lines: .ascii "> How many lines? \0" str_prompt_exec: .ascii "> Really exec at " text_ram_word mem_addr .ascii "? Type Y if so: \0" str_error_cancelled: .ascii "! Cancelled action error.\n\0" str_continuing: .ascii "Continuing.\n\0" str_loaded: .ascii "Loaded.\n\0" str_dry_stack: .ascii "! Dry stack. Halting machine.\n\0" str_prompt_filename: .ascii "> Filename? \0" str_error_not_found: .ascii "! File not found error.\n\0" str_prompt_hex: .ascii "> Enter hex data. End with dot \".\" + newline:\n\0" str_undoc: .ascii "Wow, undocumented monitor command! FOOLS2023_{Secret" text_ram_word 0xe000 .ascii "x" text_ram_word 0xe001 .ascii "Command}\n\0" str_help: .ascii "Available commands:\n" .ascii "r :: print memory as hex\n" .ascii "p :: print memory as text\n" .ascii "w :: write hex data to memory\n" .ascii "x :: execute memory\n" .ascii "rf :: load memory from file\n" .ascii "ls :: print file index\n" .ascii "h :: print this help message\n" .ascii "c :: exit monitor and continue\n" .ascii "Please enter hex numbers when prompted.\n" .ascii "If necessary for debugging, you can break into monitor\n" .ascii "with instruction BRK (0x00) and continue with 'c'\n" .ascii "Note: memory region E000-FFFF is used by monitor\n\0" str_file_list_header: .ascii "BLK SIZE NAME\n" .ascii "=======================\n\0" str_file_list_entry: text_ram_byte mem_data .ascii " " text_ram_word mem_addr .ascii " \0" # 0xfa97 str_ready: .ascii "Ready.\n" .ascii "> \0" str_breakpoint: .ascii "*** BREAK INTO MONITOR AT $" text_ram_word mem_info_pc .ascii " ***\n" .ascii "R0=$" text_ram_word mem_save_r0 .ascii " R1=$" text_ram_word mem_save_r1 .ascii " R2=$" text_ram_word mem_save_r2 .ascii " R3=$" text_ram_word mem_save_r3 .ascii "\n" .ascii "SP=$" text_ram_word mem_info_sp .ascii " [" text_ram_byte mem_info_stack + 0 text_ram_byte mem_info_stack + 1 text_ram_byte mem_info_stack + 2 text_ram_byte mem_info_stack + 3 text_ram_byte mem_info_stack + 4 text_ram_byte mem_info_stack + 5 text_ram_byte mem_info_stack + 6 text_ram_byte mem_info_stack + 7 text_ram_byte mem_info_stack + 8 text_ram_byte mem_info_stack + 9 text_ram_byte mem_info_stack + 10 text_ram_byte mem_info_stack + 11 .ascii "]\n\0" str_hexdump: text_ram_word mem_addr .ascii " | " text_ram_byte mem_data + 0 .ascii " " text_ram_byte mem_data + 1 .ascii " " text_ram_byte mem_data + 2 .ascii " " text_ram_byte mem_data + 3 .ascii " " text_ram_byte mem_data + 4 .ascii " " text_ram_byte mem_data + 5 .ascii " " text_ram_byte mem_data + 6 .ascii " " text_ram_byte mem_data + 7 .ascii "\n\0" mem_save_r0: .2byte 0 mem_save_r1: .2byte 0 mem_save_r2: .2byte 0 mem_save_r3: .2byte 0 mem_info_sp: .2byte 0 mem_info_pc: .2byte 0 mem_info_stack: .fill 12 mem_data: .fill 8 mem_addr: .2byte 0 .fill 0xf00 - (. - _start) stack: .fill 0xf0 entry: .fill 0x10