;ROM system monitor ;Macro definitions for three levels of nested call and ret #define call0(address) \return: .set $+15\ ldm return\ stm return_jump0+1\ ldm return+1\ stm return_jump0+2 #defcont \ jmp address\ .dw $+2 #define ret0 \ jmp return_jump0 #define call1(address) \return: .set $+15\ ldm return\ stm return_jump1+1\ ldm return+1\ stm return_jump1+2 #defcont \ jmp address\ .dw $+2 #define ret1 \ jmp return_jump1 #define call2(address) \return: .set $+15\ ldm return\ stm return_jump2+1\ ldm return+1\ stm return_jump2+2 #defcont \ jmp address\ .dw $+2 #define ret2 \ jmp return_jump2 ;Buffer location defined by these constant values ;Needs to be in RAM above variables and variable instructions buff_low: .equ 80h ;low byte of buffer address buff_high: .equ 08h ;high byte of buffer address buffer: .equ 0880h ;two-byte address constant .org 0000h return: .db 1fh ;placeholder for first definition of variable label -- NOP ;Initialize port LDI 4EH ;1 stop bit, no parity, 8-bit char, 16x baud OUT 03H ;write to UART control port LDI 37H ;enable receive and transmit OUT 03H ;write to control port ;Opcode initialization for RAM instructions ldi 13h ;jmp opcode stm ld_indexed_stm+3 ;return jumps for indexed instructions stm d_indexed_ldm+3 stm d_indexed_stm+3 stm bl_indexed_stm+3 stm ws_inst+3 stm gl_indexed_stm+3 stm return_jump0 ;other variable jumps stm return_jump1 stm return_jump2 stm run_jump ldi 12h ;stm opcode stm ld_indexed_stm ;indexed store instructions stm d_indexed_stm stm bl_indexed_stm stm gl_indexed_stm ldi 11h ;ldm opcode stm d_indexed_ldm stm ws_inst ldi 00h ;address of ld_stm_back stm ld_indexed_stm+4 ldi 03h stm ld_indexed_stm+5 ldi 59h ;address of d_ldm_back stm d_indexed_ldm+4 ldi 01h stm d_indexed_ldm+5 ldi 78h ;address of d_stm_back stm d_indexed_stm+4 ldi 01h stm d_indexed_stm+5 ldi 0b2h ;address of bl_back stm bl_indexed_stm+4 ldi 04h stm bl_indexed_stm+5 ldi 0f1h ;address of ws_back stm ws_inst+4 ldi 05h stm ws_inst+5 ldi 0C0h ;address of gl_back stm gl_indexed_stm+4 ldi 05h stm gl_indexed_stm+5 ;Print greeting sm_lukewarm ldm sm_greeting stm ws_inst+1 ldm sm_greeting+1 stm ws_inst+2 call0(write_string) ;Warm start for system monitor, re-entry point after commands have finished ;Prompt for routine number input sm_warm ldm sm_prompt stm ws_inst+1 ldm sm_prompt+1 stm ws_inst+2 call0(write_string) ;Get character and jump to monitor routine sm_chk_loop: in 03h ;get status andim 02h ;check RxRDY jpz sm_chk_loop in 02h ;get char from port and echo stm choice sm_echo_loop in 03h andim 01h ;check TxRDY jpz sm_echo_loop ldm choice out 02h cmp '5' jpc sm_bload cmp '4' jpc sm_load cmp '3' jpc sm_run cmp '2' jpc sm_dump jmp sm_warm ;any number other than 2 to 5 results in warm restart ;Memory dump routine ;Get address from input string sm_dump jmp get_address d_addr_back ldm address stm d_indexed_ldm+1 ldm address+1 stm d_indexed_ldm+2 ;Dump 16 lines of 16 characters each ;Set up line counter ldi 16 stm line_counter ;Loop for putting a memory dump line in the buffer ;Start with 4 characters of the starting address of the line, followed by space d_line_loop ldm d_indexed_ldm+2 ;high byte of memory address stm byte call0(byte_to_hex_pair) ldm char_pair stm buffer ;start of line ldm char_pair+1 stm buffer+1 ldm d_indexed_ldm+1 ;low byte of memory address stm byte call0(byte_to_hex_pair) ldm char_pair stm buffer+2 ldm char_pair+1 stm buffer+3 ldi 20h ;space character stm buffer+4 ;Set up for getting 16 memory bytes, converting to characters, and putting in string buffer ldi buff_low addim 5 stm d_indexed_stm+1 ;low byte of location of first character in output string ldi buff_high adcim 0 ;16-bit addition stm d_indexed_stm+2 ;high byte of location of first character in output string ldi 16 stm byte_counter ;number of bytes to get, convert, and display in one line d_byte_loop: jmp d_indexed_ldm ;get byte from memory d_ldm_back: stm byte call0(byte_to_hex_pair) ;convert to hex pair ldi 3 stm nybble_counter ldm char_pair d_nybble_loop: jmp d_indexed_stm ;store char of byte in string buffer d_stm_back: ldm d_indexed_stm+1 ;increment pointer by 16-bit incrementation inc stm d_indexed_stm+1 ldm d_indexed_stm+2 adcim 0 stm d_indexed_stm+2 ;pointing to next spot in buffer ldm nybble_counter dec ;all three characters stored (hex chars plus space)? jpz d_nybble_done ;yes, next byte stm nybble_counter ;no, place next character or space subim 1 ;if nybble count = 1, put a space next jpz d_put_space ldm char_pair+1 ;otherwise, get next char and store jmp d_nybble_loop d_put_space: ldi 20h ;space character jmp d_nybble_loop d_nybble_done: ldm d_indexed_ldm+1 ;increment memory pointer inc stm d_indexed_ldm+1 ldm d_indexed_ldm+2 adcim 0 stm d_indexed_ldm+2 ldm byte_counter dec jpz d_line_done stm byte_counter jmp d_byte_loop d_line_done: ldi 0dh ;newline characters stm buffer+52 ldi 0ah stm buffer+53 ldi 0 stm buffer+54 ;where the end of the line will be ;Write string to screen ldi buff_low stm ws_inst+1 ldi buff_high stm ws_inst+2 call0(write_string) ;Check if 16 lines done ldm line_counter dec jpz d_done stm line_counter jmp d_line_loop d_done: error: jmp sm_warm ;Monitor routine to jump and execute code ;Gets target address from terminal sm_run jmp get_address run_addr_back ldm address stm run_jump+1 ldm address+1 stm run_jump+2 jmp run_jump ;Routine to get hex char pairs from input and load bytes in RAM ;Get address first sm_load jmp get_address ld_addr_back ldm address stm ld_indexed_stm+1 ldm address+1 stm ld_indexed_stm+2 ;Initialize display bytes counter ldi 10h ;16 bytes per line stm byte_counter ;Get characters ;First character of pair ld_get_hi: IN 03H ;Get hi-order nybble of pair ANDIM 02H ;check RxRDY bit JPZ ld_get_hi ;not ready, loop IN 02H ;get char from data port STM temp ;Store character SUBIM 0DH ;Carriage return? JPZ ld_done ;Yes, return to monitor ld_loop_1: IN 03H ;No, output character and validate ANDIM 01H ;check TxRDY bit JPZ ld_loop_1 ;loop if not ready LDM temp ;get char back OUT 02H ;send to UART for output ;Code to validate hex character CMP 30H ;Lower limit of hex characters JPC ld_next_1 ;Char >= 30H, possibly valid JMP ld_invalid ;Char < 30H, invalid hex char ld_next_1: CMP 47H ;ASCII for "G" JPC ld_invalid ;Char is G or greater, invalid CMP 41H ;ASCII for "A" JPC ld_validAF_hi ;Char is valid A-F CMP 3AH ;ASCII for ":" JPC ld_invalid ;Char is ":" or greater, but < "A", invalid JMP ld_valid09_hi ;Char is valid 0-9 ld_validAF_hi: ANDIM 0FH ;Mask off high bits ADDIM 9 ;Adjust ASCII to binary value JMP ld_shift_hi ld_valid09_hi: ANDIM 0FH ;Mask off high bits JMP ld_shift_hi ld_invalid: JMP ld_error ;Invalid hex char, quit ld_shift_hi: STM byte ;Will eventually contain the byte to load STM temp ;Value to add LDI 10H ;Multiply x 16 to shift into high-order nybble STM counter ld_multloop: LDM counter DEC JPZ ld_get_lo ;Have added 16 times, done STM counter LDM temp ;Original nybble ADD byte ;Add to BYTE and store STM byte JMP ld_multloop ;Keep adding ld_get_lo: IN 03H ;Get lo-order nybble of pair ANDIM 02H ;check RxRDY bit JPZ ld_get_lo ;not ready, loop IN 02H ;get char from data port STM temp ;Store character ld_loop2: IN 03H ;Output character ANDIM 01H ;check TxRDY bit JPZ ld_loop2 LDM temp ;When ready, retrieve character and output OUT 02H ld_loop3: IN 03H ANDIM 01H JPZ ld_loop3 LDI 20H ;Space character OUT 02H ;send to UART for output ldm byte_counter ;Check if 16 bytes have been displayed dec stm byte_counter jpz ld_next4 ;Yes, reset counter and write newline jmp ld_next5 ;No, keep going ld_next4: ldi 10h stm byte_counter ;Write newline ld_loop4: IN 03H ANDIM 01H JPZ ld_loop4 LDI 0DH ;Return character OUT 02H ;send to UART for output ld_loop5: IN 03H ANDIM 01H JPZ ld_loop5 LDI 0AH ;Linefeed character OUT 02H ;send to UART for output ;Code to validate hex character ld_next5: LDM temp ;Retrieve character and validate CMP 30H ;Lower limit of hex characters JPC ld_next2 ;Char >= 30H, possibly valid JMP ld_invalid ;Char < 30H, invalid hex char ld_next2: CMP 47H ;ASCII for "G" JPC ld_invalid ;Char is G or greater, invalid CMP 41H ;ASCII for "A" JPC ld_validAF_lo ;Char is valid A-F CMP 3AH ;ASCII for ":" JPC ld_invalid ;Char is ":" or greater, but < "A", invalid JMP ld_valid09_lo ;Char is valid 0-9 ld_validAF_lo: ANDIM 0FH ;Mask off high bits ADDIM 9 ;Now lo nybble correct ADD byte ;Combine with hi nybble stored in BYTE JMP ld_indexed_stm ;Store the byte in RAM ld_valid09_lo: ANDIM 0FH ;Mask off high bits ADD byte ;Now full byte assembled OUT 00H ;Display on LEDs JMP ld_indexed_stm ;Store the byte in RAM ld_stm_back: LDM ld_indexed_stm+1 ;Increment byte pointer, lo byte first INC STM ld_indexed_stm+1 LDM ld_indexed_stm+2 ;Increment hi byte if a carry occurred when lo byte incremented ADCIM 00H STM ld_indexed_stm+2 JMP ld_get_hi ld_done: ld_error: JMP sm_warm ;Return to monitor ;Monitor routine for binary load ;Gets load target address and number of bytes from terminal ;Loads bytes in RAM and returns to monitor sm_bload jmp get_address bl_addr_back ldm address stm bl_indexed_stm+1 ldm address+1 stm bl_indexed_stm+2 ;Gets number of bytes in decimal from input using get_line, called as level 0 subroutine ;Write newline bl_get_bytes ldm bytes_str stm ws_inst+1 ldm bytes_str+1 stm ws_inst+2 call0(write_string) ;Get input decimal number string call0(get_line) ;Get word value from input string ;No error checking for final value -- must be between 0 and 65535 (0000 and FFFF hex) ;No error checking for numerals -- must be 0 to 9 ldi 0 ;starting value stm dp_value ;zero final value variable stm dp_value+1 dp_input ldm gl_str_len ;input string length from get_line cmp 5 jpc dp_setup_5 cmp 4 jpc dp_setup_4 cmp 3 jpc dp_setup_3 cmp 2 jpc dp_setup_2 cmp 1 jpc dp_setup_1 jmp sm_warm dp_setup_5 ldm buffer+4 stm dp_1s ldm buffer+3 stm dp_10s ldm buffer+2 stm dp_100s ldm buffer+1 stm dp_1000s ldm buffer stm dp_10000s jmp dp_10000_mult dp_setup_4 ldm buffer+3 stm dp_1s ldm buffer+2 stm dp_10s ldm buffer+1 stm dp_100s ldm buffer stm dp_1000s jmp dp_1000_mult dp_setup_3 ldm buffer+2 stm dp_1s ldm buffer+1 stm dp_10s ldm buffer stm dp_100s jmp dp_100_mult dp_setup_2 ldm buffer+1 stm dp_1s ldm buffer stm dp_10s jmp dp_10_mult dp_setup_1 ldm buffer stm dp_1s jmp dp_1_mult ;decimal parser multiplication dp_10000_mult ldm dp_10000s subim 30h ;ASCII for '0' jpz dp_10000_done ldi 10h ;hex low byte of 10,000 decimal addm dp_value stm dp_value ldi 27h ;hex high byte of 10,000 decimal adcm dp_value+1 stm dp_value+1 ldm dp_10000s dec stm dp_10000s jmp dp_10000_mult dp_10000_done dp_1000_mult ldm dp_1000s subim 30h ;ASCII for '0' jpz dp_1000_done ldi 0e8h ;hex low byte of 1000 decimal addm dp_value stm dp_value ldi 03h ;hex high byte of 1000 decimal adcm dp_value+1 stm dp_value+1 ldm dp_1000s dec stm dp_1000s jmp dp_1000_mult dp_1000_done dp_100_mult ldm dp_100s subim 30h ;ASCII for '0' jpz dp_100_done ldi 64h ;hex low byte of 100 decimal addm dp_value stm dp_value ldi 00h ;hex high byte of 100 decimal adcm dp_value+1 stm dp_value+1 ldm dp_100s dec stm dp_100s jmp dp_100_mult dp_100_done dp_10_mult ldm dp_10s subim 30h ;ASCII for '0' jpz dp_10_done ldi 0ah ;hex low byte of 10 decimal addm dp_value stm dp_value ldi 00h ;hex high byte of 10 decimal adcm dp_value+1 stm dp_value+1 ldm dp_10s dec stm dp_10s jmp dp_10_mult dp_10_done dp_1_mult ldm dp_1s subim 30h ;ASCII for '0' addm dp_value stm dp_value ldi 00h ;hex high byte of 100 decimal adcm dp_value+1 stm dp_value+1 ;Set up byte counter and write ready string ldm dp_value stm bl_byte_counter ldm dp_value+1 stm bl_byte_counter+1 ldm bl_ready_str stm ws_inst+1 ldm bl_ready_str+1 stm ws_inst+2 call0(write_string) ;Loop to get binary data and store bl_chk_loop: in 03h ;get status andim 02h ;check RxRDY jpz bl_chk_loop in 02h ;get binary from port jmp bl_indexed_stm ;store in RAM bl_back ldm bl_indexed_stm+1;increment pointer inc stm bl_indexed_stm+1 ldm bl_indexed_stm+2 adcim 0 stm bl_indexed_stm+2 ldm bl_byte_counter ;decrement byte counter dec stm bl_byte_counter ldm bl_byte_counter+1 sbbim 0 stm bl_byte_counter+1 jpz bl_low_zero ;check if byte counter = zero jmp bl_chk_loop bl_low_zero ldm bl_byte_counter jpz bl_done ;yes, done -- return to monitor jmp bl_chk_loop ;no, get next byte bl_done jmp sm_warm ;Routine to get address ;Not called as a subroutine, return jump by switch structure get_address ldm addr_str stm ws_inst+1 ldm addr_str+1 stm ws_inst+2 call0(write_string) ;Get hex input string for address call0(get_line) ;Write newline ldm new_line stm ws_inst+1 ldm new_line+1 stm ws_inst+2 call0(write_string) ;No error checking for length of string -- must be exactly 4 hex characters ;Memory address stored in address variable ldm buffer ;first character stm char_pair ldm buffer+1 ;second character stm char_pair+1 call1(hex_pair_to_byte) jpc sm_warm ;non-hex character detected, quit stm address+1 ;high byte of address ldm buffer+2 ;third character stm char_pair ldm buffer+3 ;fourth character stm char_pair+1 call1(hex_pair_to_byte) jpc sm_warm ;non-hex character detected, quit stm address ;low byte of address ;Switch for return jumps ldm choice cmp '5' jpc bl_addr_back cmp '4' jpc ld_addr_back cmp '3' jpc run_addr_back cmp '2' jpc d_addr_back jmp sm_warm ;Get_line subroutine, call as level 0 ;Gets a line from input, puts zero-terminated string in buffer ;Echos characters to screen, except terminating carriage return ;Address of buffer in buff_low and buff_high constants ;Uses RAM variable address instruction gl_indexed_stm ;length of input string returned in gl_str_len ;Returns when carriage return entered get_line: ldi 0 stm gl_str_len ;string length ldi buff_low ;low byte of buffer address stm gl_indexed_stm+1 ldi buff_high ;high byte of buffer address stm gl_indexed_stm+2 gl_chk_loop: in 03h ;get status andim 02h ;check RxRDY jpz gl_chk_loop in 02h ;get char from port stm temp ;save character subim 0dh ;is it a return character? jpz gl_end_of_line ;yes, replace with a zero ldm gl_str_len ;no, increment string length inc stm gl_str_len ldm temp ;get back char jmp gl_store_it ;place character in buffer gl_end_of_line: stm temp ;place zero in temp gl_store_it: jmp gl_indexed_stm ;store character in buffer gl_back: ldm temp ;check if end-of-line (temp = 0) jpz gl_done ;yes, quit gl_out_loop: in 03h ;no, send char to screen andim 01h ;check TxRDY jpz gl_out_loop ;loop if not ready ldm temp out 02h ;output character to port ldm gl_indexed_stm+1 ;increment indexing pointer inc stm gl_indexed_stm+1 ldm gl_indexed_stm+2 adcim 00h ;16-bit addition stm gl_indexed_stm+2 jmp gl_chk_loop gl_done: ret0 ;Write_string subroutine, call as level 0 ;Writes a zero-terminated string to screen at current cursor location ;Must set up address of string to be written in ws_inst+1 and ws_inst+2 write_string: ws_chk_loop: in 03h ;get status andim 01h ;check TxRDY jpz ws_chk_loop jmp ws_inst ;get a character when port ready ws_back: jpz ws_done ;quit if end-of-string out 02h ;output character to port ldm ws_inst+1 ;indexing pointer inc stm ws_inst+1 ldm ws_inst+2 adcim 00h ;16-bit addition stm ws_inst+2 jmp ws_chk_loop ws_done: ret0 ;Subroutine hex_to word -- call as level 0 ;Calls hex_pair_to_byte as level 1 ;Get 16-bit word value from input string in buffer ;No error checking for length of string -- must be exactly 4 hex characters ;16-bit value placed in h2w_value hex_to_word: ldm buffer ;first character stm char_pair ldm buffer+1 ;second character stm char_pair+1 call1(hex_pair_to_byte) ;high-order byte jpc h2w_done ;non-hex character detected, exit with carry set stm h2w_value+1 ;high byte of address ldm buffer+2 ;third character stm char_pair ldm buffer+3 ;fourth character stm char_pair+1 call1(hex_pair_to_byte) ;low-order byte jpc h2w_done ;exit with carry set if error stm h2w_value ;low byte of address h2w_done ret0 ;Subroutine to convert hex character pair to byte, call as level 1 ;Character pair in memory location char_pair, stored hi-low ;Returns with byte in accumulator and carry flag clear if no error ;Returns with character in accumulator and carry flag set if error ;Calls char_to_nybble as level 2 subroutine hex_pair_to_byte: ldm char_pair ;high order character of pair stm temp ;char_to_nybble needs char in TEMP call2(char_to_nybble) jpc c2n_error STM byte ;Will eventually contain the byte to load STM temp ;Value to add LDI 10H ;Multiply x 16 to shift into high-order nybble STM counter MULTLOOP: LDM counter DEC JPZ GET_LO ;Have added 16 times, done STM counter LDM temp ;Original nybble ADD byte ;Add to BYTE and store STM byte JMP MULTLOOP ;Keep adding GET_LO: ldm char_pair+1 stm temp call2(char_to_nybble) jpc c2n_error ADD byte ;Combine with hi nybble stored in BYTE ccf ;in case addition changed it JMP c2b_done ;Done, no error c2n_error: scf c2b_done: ret1 ;Subroutine to convert byte to hex character pair, call as level 0 ;Gets byte from byte variable ;Returns with character pair in memory location char_pair, stored hi-low ; byte_to_hex_pair: ldm byte andim 0f0h ;dealing with high nybble stm temp ldi 00h ;prepare to shift down (divide by 16) stm counter b2h_divide: ldm temp subim 16 jpc b2h_cont ;continue if nybble >=0 ldm counter ;hi-nybble now in low position cmp 10 ;is value <10? jpc b2h_hi_A2F ;yes, jump and convert to char addim 30h ;no, convert to char jmp b2h_store_hi b2h_cont: stm temp ldm counter inc stm counter jmp b2h_divide b2h_hi_A2F: addim 37h b2h_store_hi: stm char_pair ;hi-order hex char ldm byte andim 0fh ;dealing with low-order nybble cmp 10 ;is value <10? jpc b2h_lo_A2F ;yes, jump and convert to char addim 30h ;no, convert to char jmp b2h_store_lo b2h_lo_A2F: addim 37h b2h_store_lo: stm char_pair+1 ;now char pair is in variable ret0 ;Subroutine to convert hex char to nybble, call as level 2 ;Checks for validity of char, 0-9 and A-F (upper case only) ;Carry flag set on exit if error ;Carry flag clear if character valid ;Call with char in temp ;Exits with nybble in lower half of accumulator if no error ;Original character in accumulator if error char_to_nybble: ldm temp ;Get character cmp 30H ;Lower limit of hex characters jpc c2n_next ;Char >= 30H, possibly valid jmp invalid ;Char < 30H, invalid hex char c2n_next: cmp 47h ;ASCII for "G" jpc invalid ;Char is G or greater, invalid cmp 41h ;ASCII for "A" jpc validAF ;Char is valid A-F cmp 3Ah ;ASCII for ":" jpc invalid ;Char is ":" or greater, but < "A", invalid jmp valid09 ;Char is valid 0-9 validAF: andim 0fh ;Mask off high bits addim 9 ;Adjust ASCII to binary value ccf ;exit no error ret2 valid09: andim 0fh ;Mask off high bits ccf ;exit no error ret2 invalid: ldm temp ;put char in accumulator scf ;Set carry flag ret2 ;String constants new_line .dw $+2 .db 0dh,0ah,0 sm_greeting: .dw $+2 .db 0dh,0ah .text "CPUville 8-bit processor system monitor v.1" .db 0dh,0ah,0 sm_prompt .dw $+2 .db 0dh,0ah .text "Enter number: 1=restart 2=dump 3=run 4=load 5=bload " .db 0 addr_str .dw $+2 .db 0dh,0ah .text Address (hex): .db 0 bytes_str .dw $+2 .db 0dh,0ah .text Bytes to load (dec): .db 0 bl_ready_str .dw $+2 .db 0dh,0ah .text Ready, start transfer .db 0dh,0ah,0 ;The following section contains labels for RAM variables and other structures .org 0800h ;Start of RAM ;RAM Variables dp_value .dw 0000h dp_10000s .db 00h dp_1000s .db 00h dp_100s .db 00h dp_10s .db 00h dp_1s .db 00h gl_str_len .db 00h bl_byte_counter .dw 0000h char_pair .dw 0000h h2w_value .dw 0000h address .dw 0000h temp .db 00h choice .db 00h byte .db 00h counter .db 00h line_counter .db 00h char_count .db 00h byte_counter .db 00h nybble_counter .db 00h ;RAM instructions with variable address (must initialize opcode when monitor is in ROM) ;Jump instruction for run routine, must be in RAM run_jump jmp 0000h ;Indexed load for load routine, must be in RAM ld_indexed_stm stm 0000h jmp ld_stm_back ;Indexed load and store instructions for dump, must be in RAM d_indexed_ldm ldm 0000h jmp d_ldm_back d_indexed_stm stm 0000h jmp d_stm_back ;Indexed store instruction for binary loader, must be in RAM bl_indexed_stm: stm 0000h jmp bl_back ;Indexed load instruction for write_string, must be in RAM ws_inst: ldm 0000h jmp ws_back ;Indexed store instruction for get_line, must be in RAM gl_indexed_stm: stm 0000h jmp gl_back ;Return instruction for level 0 call macros, must be in RAM return_jump0: jmp 0000h ;Return instruction for level 1 call macros, must be in RAM return_jump1: jmp 0000h ;Return instruction for level 2 call macros, must be in RAM return_jump2: jmp 0000h return: .set 0000h ;assembler needs variable label set back to original value .end