# File MONITOR.ASM 0000 ;RAM monitor for a system with serial interface and IDE disk and memory expansion board. 0000 ;This program to be loaded by CP/M at 0100h, then it copies itself to memory at DC00h. 0000 ;Assumes serial port has been initialized by ROM monitor. 0000 ;Assumes the UART data port address is 02h and control/status address is 03h 0000 ;Assumes memory configuration is all-RAM 0000 ; 0000 ;The subroutines use these variables in RAM, same area as ROM monitor: 0000 current_location: equ 0xdb00 ;word variable in RAM 0000 line_count: equ 0xdb02 ;byte variable in RAM 0000 byte_count: equ 0xdb03 ;byte variable in RAM 0000 value_pointer: equ 0xdb04 ;word variable in RAM 0000 current_value: equ 0xdb06 ;word variable in RAM 0000 buffer: equ 0xdb08 ;buffer in RAM -- up to stack area 0000 ;Will use stack of calling program (CP/M) which is re-initialized at re-boot. 0000 ; 0000 ; 0000 ; 0000 ;Code to start program and move to higher memory 0000 ; 0000 org 0100h 0100 21 0e 01 ld hl,code_origin ;start of code to transfer 0103 01 cb 07 ld bc,code_end-code_start+1 ;length of code to transfer 0106 11 00 dc ld de,0DC00h ;target of transfer 0109 ed b0 ldir ;Z80 transfer instruction 010b c3 00 dc jp 0DC00h 010e code_origin: ;address of first byte of code before transfer 010e ; 010e org 0DC00h dc00 c3 5a e0 code_start: jp monitor_start dc03 ; dc03 ;Puts a single char (byte value) on serial output dc03 ;Call with char to send in A register. Uses B register dc03 47 write_char: ld b,a ;store char dc04 db 03 write_char_loop: in a,(3) ;check if OK to send dc06 e6 01 and 001h ;check TxRDY bit dc08 ca 04 dc jp z,write_char_loop ;loop if not set dc0b 78 ld a,b ;get char back dc0c d3 02 out (2),a ;send to output dc0e c9 ret ;returns with char in a dc0f ; dc0f ;Subroutine to write a zero-terminated string to serial output dc0f ;Pass address of string in HL register dc0f ;No error checking dc0f db 03 write_string: in a,(3) ;read status dc11 e6 01 and 001h ;check TxRDY bit dc13 ca 0f dc jp z,write_string ;loop if not set dc16 7e ld a,(hl) ;get char from string dc17 a7 and a ;check if 0 dc18 c8 ret z ;yes, finished dc19 d3 02 out (2),a ;no, write char to output dc1b 23 inc hl ;next char in string dc1c c3 0f dc jp write_string ;start over dc1f ; dc1f ;Binary loader. Receive a binary file, place in memory. dc1f ;Address of load passed in HL, length of load (= file length) in BC dc1f db 03 bload: in a,(3) ;get status dc21 e6 02 and 002h ;check RxRDY bit dc23 ca 1f dc jp z,bload ;not ready, loop dc26 db 02 in a,(2) dc28 77 ld (hl),a dc29 23 inc hl dc2a 0b dec bc ;byte counter dc2b 78 ld a,b ;need to test BC this way because dc2c b1 or c ;dec rp instruction does not change flags dc2d c2 1f dc jp nz,bload dc30 c9 ret dc31 ; dc31 ;Binary dump to port. Send a stream of binary data from memory to serial output dc31 ;Address of dump passed in HL, length of dump in BC dc31 db 03 bdump: in a,(3) ;get status dc33 e6 01 and 001h ;check TxRDY bit dc35 ca 31 dc jp z,bdump ;not ready, loop dc38 7e ld a,(hl) dc39 d3 02 out (2),a dc3b 23 inc hl dc3c 0b dec bc dc3d 78 ld a,b ;need to test this way because dc3e b1 or c ;dec rp instruction does not change flags dc3f c2 31 dc jp nz,bdump dc42 c9 ret dc43 ; dc43 ;Subroutine to get a string from serial input, place in buffer. dc43 ;Buffer address passed in HL reg. dc43 ;Uses A,BC,DE,HL registers (including calls to other subroutines). dc43 ;Line entry ends by hitting return key. Return char not included in string (replaced by zero). dc43 ;Backspace editing OK. No error checking. dc43 ; dc43 0e 00 get_line: ld c,000h ;line position dc45 7c ld a,h ;put original buffer address in de dc46 57 ld d,a ;after this don't need to preserve hl dc47 7d ld a,l ;subroutines called don't use de dc48 5f ld e,a dc49 db 03 get_line_next_char: in a,(3) ;get status dc4b e6 02 and 002h ;check RxRDY bit dc4d ca 49 dc jp z,get_line_next_char ;not ready, loop dc50 db 02 in a,(2) ;get char dc52 fe 0d cp 00dh ;check if return dc54 c8 ret z ;yes, normal exit dc55 fe 7f cp 07fh ;check if backspace (VT102 keys) dc57 ca 6b dc jp z,get_line_backspace ;yes, jump to backspace routine dc5a fe 08 cp 008h ;check if backspace (ANSI keys) dc5c ca 6b dc jp z,get_line_backspace ;yes, jump to backspace dc5f cd 03 dc call write_char ;put char on screen dc62 12 ld (de),a ;store char in buffer dc63 13 inc de ;point to next space in buffer dc64 0c inc c ;inc counter dc65 3e 00 ld a,000h dc67 12 ld (de),a ;leaves a zero-terminated string in buffer dc68 c3 49 dc jp get_line_next_char dc6b 79 get_line_backspace: ld a,c ;check current position in line dc6c fe 00 cp 000h ;at beginning of line? dc6e ca 49 dc jp z,get_line_next_char ;yes, ignore backspace, get next char dc71 1b dec de ;no, erase char from buffer dc72 0d dec c ;back up one dc73 3e 00 ld a,000h ;put a zero in buffer where the last char was dc75 12 ld (de),a dc76 21 7b df ld hl,erase_char_string ;ANSI sequence to delete one char from line dc79 cd 0f dc call write_string ;transmits sequence to backspace and erase char dc7c c3 49 dc jp get_line_next_char dc7f ; dc7f ;Creates a two-char hex string from the byte value passed in register A dc7f ;Location to place string passed in HL dc7f ;String is zero-terminated, stored in 3 locations starting at HL dc7f ;Also uses registers b,d, and e dc7f 47 byte_to_hex_string: ld b,a ;store original byte dc80 cb 3f srl a ;shift right 4 times, putting dc82 cb 3f srl a ;high nybble in low-nybble spot dc84 cb 3f srl a ;and zeros in high-nybble spot dc86 cb 3f srl a dc88 16 00 ld d,000h ;prepare for 16-bit addition dc8a 5f ld e,a ;de contains offset dc8b e5 push hl ;temporarily store string target address dc8c 21 e5 dc ld hl,hex_char_table ;use char table to get high-nybble character dc8f 19 add hl,de ;add offset to start of table dc90 7e ld a,(hl) ;get char dc91 e1 pop hl ;get string target address dc92 77 ld (hl),a ;store first char of string dc93 23 inc hl ;point to next string target address dc94 78 ld a,b ;get original byte back from reg b dc95 e6 0f and 00fh ;mask off high-nybble dc97 5f ld e,a ;d still has 000h, now de has offset dc98 e5 push hl ;temp store string target address dc99 21 e5 dc ld hl,hex_char_table ;start of table dc9c 19 add hl,de ;add offset dc9d 7e ld a,(hl) ;get char dc9e e1 pop hl ;get string target address dc9f 77 ld (hl),a ;store second char of string dca0 23 inc hl ;point to third location dca1 3e 00 ld a,000h ;zero to terminate string dca3 77 ld (hl),a ;store the zero dca4 c9 ret ;done dca5 ; dca5 ;Converts a single ASCII hex char to a nybble value dca5 ;Pass char in reg A. Letter numerals must be upper case. dca5 ;Return nybble value in low-order reg A with zeros in high-order nybble if no error. dca5 ;Return 0ffh in reg A if error (char not a valid hex numeral). dca5 ;Also uses b, c, and hl registers. dca5 21 e5 dc hex_char_to_nybble: ld hl,hex_char_table dca8 06 0f ld b,00fh ;no. of valid characters in table - 1. dcaa 0e 00 ld c,000h ;will be nybble value dcac be hex_to_nybble_loop: cp (hl) ;character match here? dcad ca b9 dc jp z,hex_to_nybble_ok ;match found, exit dcb0 05 dec b ;no match, check if at end of table dcb1 fa bb dc jp m,hex_to_nybble_err ;table limit exceded, exit with error dcb4 0c inc c ;still inside table, continue search dcb5 23 inc hl dcb6 c3 ac dc jp hex_to_nybble_loop dcb9 79 hex_to_nybble_ok: ld a,c ;put nybble value in a dcba c9 ret dcbb 3e ff hex_to_nybble_err: ld a,0ffh ;error value dcbd c9 ret dcbe ; dcbe ;Converts a hex character pair to a byte value dcbe ;Called with location of high-order char in HL dcbe ;If no error carry flag clear, returns with byte value in register A, and dcbe ;HL pointing to next mem location after char pair. dcbe ;If error (non-hex char) carry flag set, HL pointing to invalid char dcbe 7e hex_to_byte: ld a,(hl) ;location of character pair dcbf e5 push hl ;store hl (hex_char_to_nybble uses it) dcc0 cd a5 dc call hex_char_to_nybble dcc3 e1 pop hl ;returns with nybble value in a reg, or 0ffh if error dcc4 fe ff cp 0ffh ;non-hex character? dcc6 ca e3 dc jp z,hex_to_byte_err ;yes, exit with error dcc9 cb 27 sla a ;no, move low order nybble to high side dccb cb 27 sla a dccd cb 27 sla a dccf cb 27 sla a dcd1 57 ld d,a ;store high-nybble dcd2 23 inc hl ;get next character of the pair dcd3 7e ld a,(hl) dcd4 e5 push hl ;store hl dcd5 cd a5 dc call hex_char_to_nybble dcd8 e1 pop hl dcd9 fe ff cp 0ffh ;non-hex character? dcdb ca e3 dc jp z,hex_to_byte_err ;yes, exit with error dcde b2 or d ;no, combine with high-nybble dcdf 23 inc hl ;point to next memory location after char pair dce0 37 scf dce1 3f ccf ;no-error exit (carry = 0) dce2 c9 ret dce3 37 hex_to_byte_err: scf ;error, carry flag set dce4 c9 ret dce5 .. hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table dcf5 ; dcf5 ;Subroutine to get a two-byte address from serial input. dcf5 ;Returns with address value in HL dcf5 ;Uses locations in RAM for buffer and variables dcf5 21 08 db address_entry: ld hl,buffer ;location for entered string dcf8 cd 43 dc call get_line ;returns with address string in buffer dcfb 21 08 db ld hl,buffer ;location of stored address entry string dcfe cd be dc call hex_to_byte ;will get high-order byte first dd01 da 17 dd jp c, address_entry_error ;if error, jump dd04 32 01 db ld (current_location+1),a ;store high-order byte, little-endian dd07 21 0a db ld hl,buffer+2 ;point to low-order hex char pair dd0a cd be dc call hex_to_byte ;get low-order byte dd0d da 17 dd jp c, address_entry_error ;jump if error dd10 32 00 db ld (current_location),a ;store low-order byte in lower memory dd13 2a 00 db ld hl,(current_location) ;put memory address in hl dd16 c9 ret dd17 21 b9 df address_entry_error: ld hl,address_error_msg dd1a cd 0f dc call write_string dd1d c3 f5 dc jp address_entry dd20 ; dd20 ;Subroutine to get a decimal string, return a word value dd20 ;Calls decimal_string_to_word subroutine dd20 21 08 db decimal_entry: ld hl,buffer dd23 cd 43 dc call get_line ;returns with DE pointing to terminating zero dd26 21 08 db ld hl,buffer dd29 cd 36 dd call decimal_string_to_word dd2c d0 ret nc ;no error, return with word in hl dd2d 21 2d e0 ld hl,decimal_error_msg ;error, try again dd30 cd 0f dc call write_string dd33 c3 20 dd jp decimal_entry dd36 ; dd36 ;Subroutine to convert a decimal string to a word value dd36 ;Call with address of string in HL, pointer to end of string in DE dd36 ;Carry flag set if error (non-decimal char) dd36 ;Carry flag clear, word value in HL if no error. dd36 42 decimal_string_to_word: ld b,d dd37 4b ld c,e ;use BC as string pointer dd38 22 00 db ld (current_location),hl ;store addr. of start of buffer in RAM word variable dd3b 21 00 00 ld hl,000h ;starting value zero dd3e 22 06 db ld (current_value),hl dd41 21 86 dd ld hl,decimal_place_value ;pointer to values dd44 22 04 db ld (value_pointer),hl dd47 0b decimal_next_char: dec bc ;next char in string (moving right to left) dd48 2a 00 db ld hl,(current_location) ;check if at end of decimal string dd4b 37 scf ;get ready to subtract de from buffer addr. dd4c 3f ccf ;set carry to zero (clear) dd4d ed 42 sbc hl,bc ;keep going if bc > or = hl (buffer address) dd4f da 5b dd jp c,decimal_continue ;borrow means bc > hl dd52 ca 5b dd jp z,decimal_continue ;z means bc = hl dd55 2a 06 db ld hl,(current_value) ;return if de < buffer address (no borrow) dd58 37 scf ;get value back from RAM variable dd59 3f ccf dd5a c9 ret ;return with carry clear, value in hl dd5b 0a decimal_continue: ld a,(bc) ;next char in string (right to left) dd5c d6 30 sub 030h ;ASCII value of zero char dd5e fa 81 dd jp m,decimal_error ;error if char value less than 030h dd61 fe 0a cp 00ah ;error if byte value > or = 10 decimal dd63 f2 81 dd jp p,decimal_error ;a reg now has value of decimal numeral dd66 2a 04 db ld hl,(value_pointer) ;get value to add an put in de dd69 5e ld e,(hl) ;little-endian (low byte in low memory) dd6a 23 inc hl dd6b 56 ld d,(hl) dd6c 23 inc hl ;hl now points to next value dd6d 22 04 db ld (value_pointer),hl dd70 2a 06 db ld hl,(current_value) ;get back current value dd73 3d decimal_add: dec a ;add loop to increase total value dd74 fa 7b dd jp m,decimal_add_done ;end of multiplication dd77 19 add hl,de dd78 c3 73 dd jp decimal_add dd7b 22 06 db decimal_add_done: ld (current_value),hl dd7e c3 47 dd jp decimal_next_char dd81 37 decimal_error: scf dd82 c9 ret dd83 c3 73 dd jp decimal_add dd86 01 00 0a 00 64 00 e8 03 10 27 decimal_place_value: defw 1,10,100,1000,10000 dd90 ; dd90 ;Memory dump dd90 ;Displays a 256-byte block of memory in 16-byte rows. dd90 ;Called with address of start of block in HL dd90 22 00 db memory_dump: ld (current_location),hl ;store address of block to be displayed dd93 3e 00 ld a,000h dd95 32 03 db ld (byte_count),a ;initialize byte count dd98 32 02 db ld (line_count),a ;initialize line count dd9b c3 d0 dd jp dump_new_line dd9e 2a 00 db dump_next_byte: ld hl,(current_location) ;get byte address from storage, dda1 7e ld a,(hl) ;get byte to be converted to string dda2 23 inc hl ;increment address and dda3 22 00 db ld (current_location),hl ;store back dda6 21 08 db ld hl,buffer ;location to store string dda9 cd 7f dc call byte_to_hex_string ;convert ddac 21 08 db ld hl,buffer ;display string ddaf cd 0f dc call write_string ddb2 3a 03 db ld a,(byte_count) ;next byte ddb5 3c inc a ddb6 ca 00 de jp z,dump_done ;stop when 256 bytes displayed ddb9 32 03 db ld (byte_count),a ;not finished yet, store ddbc 3a 02 db ld a,(line_count) ;end of line (16 characters)? ddbf fe 0f cp 00fh ;yes, start new line ddc1 ca d0 dd jp z,dump_new_line ddc4 3c inc a ;no, increment line count ddc5 32 02 db ld (line_count),a ddc8 3e 20 ld a,020h ;print space ddca cd 03 dc call write_char ddcd c3 9e dd jp dump_next_byte ;continue ddd0 3e 00 dump_new_line: ld a,000h ;reset line count to zero ddd2 32 02 db ld (line_count),a ddd5 cd 80 de call write_newline ddd8 2a 00 db ld hl,(current_location) ;location of start of line dddb 7c ld a,h ;high byte of address dddc 21 08 db ld hl, buffer dddf cd 7f dc call byte_to_hex_string ;convert dde2 21 08 db ld hl,buffer dde5 cd 0f dc call write_string ;write high byte dde8 2a 00 db ld hl,(current_location) ddeb 7d ld a,l ;low byte of address ddec 21 08 db ld hl, buffer ddef cd 7f dc call byte_to_hex_string ;convert ddf2 21 08 db ld hl,buffer ddf5 cd 0f dc call write_string ;write low byte ddf8 3e 20 ld a,020h ;space ddfa cd 03 dc call write_char ddfd c3 9e dd jp dump_next_byte ;now write 16 bytes de00 3e 00 dump_done: ld a,000h de02 21 08 db ld hl,buffer de05 77 ld (hl),a ;clear buffer of last string de06 cd 80 de call write_newline de09 c9 ret de0a ; de0a ;Memory load de0a ;Loads RAM memory with bytes entered as hex characters de0a ;Called with address to start loading in HL de0a ;Displays entered data in 16-byte rows. de0a 22 00 db memory_load: ld (current_location),hl de0d 21 e5 df ld hl,data_entry_msg de10 cd 0f dc call write_string de13 c3 5d de jp load_new_line de16 cd 76 de load_next_char: call get_char de19 fe 0d cp 00dh ;return? de1b ca 72 de jp z,load_done ;yes, quit de1e 32 08 db ld (buffer),a de21 cd 76 de call get_char de24 fe 0d cp 00dh ;return? de26 ca 72 de jp z,load_done ;yes, quit de29 32 09 db ld (buffer+1),a de2c 21 08 db ld hl,buffer de2f cd be dc call hex_to_byte de32 da 68 de jp c,load_data_entry_error ;non-hex character de35 2a 00 db ld hl,(current_location) ;get byte address from storage, de38 77 ld (hl),a ;store byte de39 23 inc hl ;increment address and de3a 22 00 db ld (current_location),hl ;store back de3d 3a 08 db ld a,(buffer) de40 cd 03 dc call write_char de43 3a 09 db ld a,(buffer+1) de46 cd 03 dc call write_char de49 3a 02 db ld a,(line_count) ;end of line (16 characters)? de4c fe 0f cp 00fh ;yes, start new line de4e ca 5d de jp z,load_new_line de51 3c inc a ;no, increment line count de52 32 02 db ld (line_count),a de55 3e 20 ld a,020h ;print space de57 cd 03 dc call write_char de5a c3 16 de jp load_next_char ;continue de5d 3e 00 load_new_line: ld a,000h ;reset line count to zero de5f 32 02 db ld (line_count),a de62 cd 80 de call write_newline de65 c3 16 de jp load_next_char ;continue de68 cd 80 de load_data_entry_error: call write_newline de6b 21 12 e0 ld hl,data_error_msg de6e cd 0f dc call write_string de71 c9 ret de72 cd 80 de load_done: call write_newline de75 c9 ret de76 ; de76 ;Get one ASCII character from the serial port. de76 ;Returns with char in A reg. No error checking. de76 db 03 get_char: in a,(3) ;get status de78 e6 02 and 002h ;check RxRDY bit de7a ca 76 de jp z,get_char ;not ready, loop de7d db 02 in a,(2) ;get char de7f c9 ret de80 ; de80 ;Subroutine to start a new line de80 3e 0d write_newline: ld a,00dh ;ASCII carriage return character de82 cd 03 dc call write_char de85 3e 0a ld a,00ah ;new line (line feed) character de87 cd 03 dc call write_char de8a c9 ret de8b ; de8b ;Subroutine to read one disk sector (256 bytes) de8b ;Address to place data passed in HL de8b ;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B de8b ;LBA bits 16 to 23 passed in E de8b disk_read: de8b db 0f rd_status_loop_1: in a,(0fh) ;check status de8d e6 80 and 80h ;check BSY bit de8f c2 8b de jp nz,rd_status_loop_1 ;loop until not busy de92 db 0f rd_status_loop_2: in a,(0fh) ;check status de94 e6 40 and 40h ;check DRDY bit de96 ca 92 de jp z,rd_status_loop_2 ;loop until ready de99 3e 01 ld a,01h ;number of sectors = 1 de9b d3 0a out (0ah),a ;sector count register de9d 79 ld a,c de9e d3 0b out (0bh),a ;lba bits 0 - 7 dea0 78 ld a,b dea1 d3 0c out (0ch),a ;lba bits 8 - 15 dea3 7b ld a,e dea4 d3 0d out (0dh),a ;lba bits 16 - 23 dea6 3e e0 ld a,11100000b ;LBA mode, select drive 0 dea8 d3 0e out (0eh),a ;drive/head register deaa 3e 20 ld a,20h ;Read sector command deac d3 0f out (0fh),a deae db 0f rd_wait_for_DRQ_set: in a,(0fh) ;read status deb0 e6 08 and 08h ;DRQ bit deb2 ca ae de jp z,rd_wait_for_DRQ_set ;loop until bit set deb5 db 0f rd_wait_for_BSY_clear: in a,(0fh) deb7 e6 80 and 80h deb9 c2 b5 de jp nz,rd_wait_for_BSY_clear debc db 0f in a,(0fh) ;clear INTRQ debe db 08 read_loop: in a,(08h) ;get data dec0 77 ld (hl),a dec1 23 inc hl dec2 db 0f in a,(0fh) ;check status dec4 e6 08 and 08h ;DRQ bit dec6 c2 be de jp nz,read_loop ;loop until cleared dec9 c9 ret deca ; deca ;Subroutine to write one disk sector (256 bytes) deca ;Address of data to write to disk passed in HL deca ;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B deca ;LBA bits 16 to 23 passed in E deca disk_write: deca db 0f wr_status_loop_1: in a,(0fh) ;check status decc e6 80 and 80h ;check BSY bit dece c2 ca de jp nz,wr_status_loop_1 ;loop until not busy ded1 db 0f wr_status_loop_2: in a,(0fh) ;check status ded3 e6 40 and 40h ;check DRDY bit ded5 ca d1 de jp z,wr_status_loop_2 ;loop until ready ded8 3e 01 ld a,01h ;number of sectors = 1 deda d3 0a out (0ah),a ;sector count register dedc 79 ld a,c dedd d3 0b out (0bh),a ;lba bits 0 - 7 dedf 78 ld a,b dee0 d3 0c out (0ch),a ;lba bits 8 - 15 dee2 7b ld a,e dee3 d3 0d out (0dh),a ;lba bits 16 - 23 dee5 3e e0 ld a,11100000b ;LBA mode, select drive 0 dee7 d3 0e out (0eh),a ;drive/head register dee9 3e 30 ld a,30h ;Write sector command deeb d3 0f out (0fh),a deed db 0f wr_wait_for_DRQ_set: in a,(0fh) ;read status deef e6 08 and 08h ;DRQ bit def1 ca ed de jp z,wr_wait_for_DRQ_set ;loop until bit set def4 7e write_loop: ld a,(hl) def5 d3 08 out (08h),a ;write data def7 23 inc hl def8 db 0f in a,(0fh) ;read status defa e6 08 and 08h ;check DRQ bit defc c2 f4 de jp nz,write_loop ;write until bit cleared deff db 0f wr_wait_for_BSY_clear: in a,(0fh) df01 e6 80 and 80h df03 c2 ff de jp nz,wr_wait_for_BSY_clear df06 db 0f in a,(0fh) ;clear INTRQ df08 c9 ret df09 ; df09 ;Strings used in subroutines df09 .. 00 length_entry_string: defm "Enter length of file to load (decimal): ",0 df32 .. 00 dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0 df59 .. 00 LBA_entry_string: defm "Enter LBA (decimal, 0 to 65535): ",0 df7b 08 1b .. 00 erase_char_string: defm 008h,01bh,"[K",000h ;ANSI sequence for backspace, erase to end of line. df80 .. 00 address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0 dfb9 .. 00 address_error_msg: defm "\r\nError: invalid hex character, try again: ",0 dfe5 .. 00 data_entry_msg: defm "Enter hex bytes, hit return when finished.\r\n",0 e012 .. 00 data_error_msg: defm "Error: invalid hex byte.\r\n",0 e02d .. 00 decimal_error_msg: defm "\r\nError: invalid decimal number, try again: ",0 e05a ; e05a ;Simple monitor program for CPUville Z80 computer with serial interface. e05a e05a cd 80 de monitor_start: call write_newline ;routine program return here to avoid re-initialization of port e05d 3e 3e ld a,03eh ;cursor symbol e05f cd 03 dc call write_char e062 21 08 db ld hl,buffer e065 cd 43 dc call get_line ;get monitor input string (command) e068 cd 80 de call write_newline e06b cd 6f e0 call parse ;interprets command, returns with address to jump to in HL e06e e9 jp (hl) e06f ; e06f ;Parses an input line stored in buffer for available commands as described in parse table. e06f ;Returns with address of jump to action for the command in HL e06f 01 9a e3 parse: ld bc,parse_table ;bc is pointer to parse_table e072 0a parse_start: ld a,(bc) ;get pointer to match string from parse table e073 5f ld e,a e074 03 inc bc e075 0a ld a,(bc) e076 57 ld d,a ;de will is pointer to strings for matching e077 1a ld a,(de) ;get first char from match string e078 f6 00 or 000h ;zero? e07a ca 95 e0 jp z,parser_exit ;yes, exit no_match e07d 21 08 db ld hl,buffer ;no, parse input string e080 be match_loop: cp (hl) ;compare buffer char with match string char e081 c2 8f e0 jp nz,no_match ;no match, go to next match string e084 f6 00 or 000h ;end of strings (zero)? e086 ca 95 e0 jp z,parser_exit ;yes, matching string found e089 13 inc de ;match so far, point to next char in match string e08a 1a ld a,(de) ;get next character from match string e08b 23 inc hl ;and point to next char in input string e08c c3 80 e0 jp match_loop ;check for match e08f 03 no_match: inc bc ;skip over jump target to e090 03 inc bc e091 03 inc bc ;get address of next matching string e092 c3 72 e0 jp parse_start e095 03 parser_exit: inc bc ;skip to address of jump for match e096 0a ld a,(bc) e097 6f ld l,a e098 03 inc bc e099 0a ld a,(bc) e09a 67 ld h,a ;returns with jump address in hl e09b c9 ret e09c ; e09c ;Actions to be taken on match e09c ; e09c ;Memory dump program e09c ;Input 4-digit hexadecimal address e09c ;Calls memory_dump subroutine e09c 21 e6 e1 dump_jump: ld hl,dump_message ;Display greeting e09f cd 0f dc call write_string e0a2 21 80 df ld hl,address_entry_msg ;get ready to get address e0a5 cd 0f dc call write_string e0a8 cd f5 dc call address_entry ;returns with address in HL e0ab cd 80 de call write_newline e0ae cd 90 dd call memory_dump e0b1 c3 5a e0 jp monitor_start e0b4 ; e0b4 ;Hex loader, displays formatted input e0b4 21 0d e2 load_jump: ld hl,load_message ;Display greeting e0b7 cd 0f dc call write_string ;get address to load e0ba 21 80 df ld hl,address_entry_msg ;get ready to get address e0bd cd 0f dc call write_string e0c0 cd f5 dc call address_entry e0c3 cd 80 de call write_newline e0c6 cd 0a de call memory_load e0c9 c3 5a e0 jp monitor_start e0cc ; e0cc ;Jump and run do the same thing: get an address and jump to it. e0cc 21 3c e2 run_jump: ld hl,run_message ;Display greeting e0cf cd 0f dc call write_string e0d2 21 80 df ld hl,address_entry_msg ;get ready to get address e0d5 cd 0f dc call write_string e0d8 cd f5 dc call address_entry e0db e9 jp (hl) e0dc ; e0dc ;Help and ? do the same thing, display the available commands e0dc 21 ce e1 help_jump: ld hl,help_message e0df cd 0f dc call write_string e0e2 01 9a e3 ld bc,parse_table ;table with pointers to command strings e0e5 0a help_loop: ld a,(bc) ;displays the strings for matching commands, e0e6 6f ld l,a ;getting the string addresses from the e0e7 03 inc bc ;parse table e0e8 0a ld a,(bc) ;pass address of string to hl through a reg e0e9 67 ld h,a e0ea 7e ld a,(hl) ;hl now points to start of match string e0eb f6 00 or 000h ;exit if no_match string e0ed ca 00 e1 jp z,help_done e0f0 c5 push bc ;write_char uses b register e0f1 3e 20 ld a,020h ;space char e0f3 cd 03 dc call write_char e0f6 c1 pop bc e0f7 cd 0f dc call write_string ;writes match string e0fa 03 inc bc ;pass over jump address in table e0fb 03 inc bc e0fc 03 inc bc e0fd c3 e5 e0 jp help_loop e100 c3 5a e0 help_done: jp monitor_start e103 ; e103 ;Binary file load. Need both address to load and length of file e103 21 71 e2 bload_jump: ld hl,bload_message e106 cd 0f dc call write_string e109 21 80 df ld hl,address_entry_msg e10c cd 0f dc call write_string e10f cd f5 dc call address_entry e112 cd 80 de call write_newline e115 e5 push hl e116 21 09 df ld hl,length_entry_string e119 cd 0f dc call write_string e11c cd 20 dd call decimal_entry e11f 44 ld b,h e120 4d ld c,l e121 21 94 e2 ld hl,bload_ready_message e124 cd 0f dc call write_string e127 e1 pop hl e128 cd 1f dc call bload e12b c3 5a e0 jp monitor_start e12e ; e12e ;Binary memory dump. Need address of start of dump and no. bytes e12e 21 b8 e2 bdump_jump: ld hl,bdump_message e131 cd 0f dc call write_string e134 21 80 df ld hl,address_entry_msg e137 cd 0f dc call write_string e13a cd f5 dc call address_entry e13d cd 80 de call write_newline e140 e5 push hl e141 21 32 df ld hl,dump_entry_string e144 cd 0f dc call write_string e147 cd 20 dd call decimal_entry e14a 44 ld b,h e14b 4d ld c,l e14c 21 e8 e2 ld hl,bdump_ready_message e14f cd 0f dc call write_string e152 cd 76 de call get_char e155 e1 pop hl e156 cd 31 dc call bdump e159 c3 5a e0 jp monitor_start e15c ;Disk read. Need memory address to place data, LBA of sector to read e15c 21 0f e3 diskrd_jump: ld hl,diskrd_message e15f cd 0f dc call write_string e162 21 80 df ld hl,address_entry_msg e165 cd 0f dc call write_string e168 cd f5 dc call address_entry e16b cd 80 de call write_newline e16e e5 push hl e16f 21 59 df ld hl,LBA_entry_string e172 cd 0f dc call write_string e175 cd 20 dd call decimal_entry e178 44 ld b,h e179 4d ld c,l e17a 1e 00 ld e,00h e17c e1 pop hl e17d cd 8b de call disk_read e180 c3 5a e0 jp monitor_start e183 21 37 e3 diskwr_jump: ld hl,diskwr_message e186 cd 0f dc call write_string e189 21 80 df ld hl,address_entry_msg e18c cd 0f dc call write_string e18f cd f5 dc call address_entry e192 cd 80 de call write_newline e195 e5 push hl e196 21 59 df ld hl,LBA_entry_string e199 cd 0f dc call write_string e19c cd 20 dd call decimal_entry e19f 44 ld b,h e1a0 4d ld c,l e1a1 1e 00 ld e,00h e1a3 e1 pop hl e1a4 cd ca de call disk_write e1a7 c3 5a e0 jp monitor_start e1aa c3 00 00 cpm_jump: jp 0000h ;reboot CP/M e1ad ;Prints message for no match to entered command e1ad 21 cb e1 no_match_jump: ld hl,no_match_message e1b0 cd 0f dc call write_string e1b3 21 08 db ld hl, buffer e1b6 cd 0f dc call write_string e1b9 c3 5a e0 jp monitor_start e1bc ; e1bc ;Monitor data structures: e1bc ; e1bc .. 00 monitor_message: defm "\r\nROM ver. 8\r\n",0 e1cb .. 00 no_match_message: defm "? ",0 e1ce .. 00 help_message: defm "Commands implemented:\r\n",0 e1e6 .. 00 dump_message: defm "Displays a 256-byte block of memory.\r\n",0 e20d .. 00 load_message: defm "Enter hex bytes starting at memory location.\r\n",0 e23c .. 00 run_message: defm "Will jump to (execute) program at address entered.\r\n",0 e271 .. 00 bload_message: defm "Loads a binary file into memory.\r\n",0 e294 .. 00 bload_ready_message: defm "\n\rReady to receive, start transfer.",0 e2b8 .. 00 bdump_message: defm "Dumps binary data from memory to serial port.\r\n",0 e2e8 .. 00 bdump_ready_message: defm "\n\rReady to send, hit any key to start.",0 e30f .. 00 diskrd_message: defm "Reads one sector from disk to memory.\r\n",0 e337 .. 00 diskwr_message: defm "Writes one sector from memory to disk.\r\n",0 e360 ;Strings for matching: e360 .. 00 dump_string: defm "dump",0 e365 .. 00 load_string: defm "load",0 e36a .. 00 jump_string: defm "jump",0 e36f .. 00 run_string: defm "run",0 e373 .. 00 question_string: defm "?",0 e375 .. 00 help_string: defm "help",0 e37a .. 00 bload_string: defm "bload",0 e380 .. 00 bdump_string: defm "bdump",0 e386 .. 00 diskrd_string: defm "diskrd",0 e38d .. 00 diskwr_string: defm "diskwr",0 e394 .. 00 cpm_string: defm "cpm",0 e398 00 00 no_match_string: defm 0,0 e39a ;Table for matching strings to jumps e39a 60 e3 9c e0 65 e3 b4 e0 parse_table: defw dump_string,dump_jump,load_string,load_jump e3a2 6a e3 cc e0 6f e3 cc e0 defw jump_string,run_jump,run_string,run_jump e3aa 73 e3 dc e0 75 e3 dc e0 defw question_string,help_jump,help_string,help_jump e3b2 7a e3 03 e1 80 e3 2e e1 defw bload_string,bload_jump,bdump_string,bdump_jump e3ba 86 e3 5c e1 8d e3 83 e1 defw diskrd_string,diskrd_jump,diskwr_string,diskwr_jump e3c2 94 e3 aa e1 defw cpm_string,cpm_jump e3c6 98 e3 ad e1 defw no_match_string,no_match_jump e3ca code_end: e3ca end # End of file MONITOR.ASM e3ca