# File RAM_monitor.asm 0000 ;RAM monitor for a system with serial interface and IDE disk and memory expansion board. 0000 ;This program to be loaded at DC00h by ROM monitor, and run from there. 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 ; 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 ;Can use same stack as ROM monitor. Stack not re-initialized, listed here for information 0000 ;ROM_monitor_stack: equ 0xdbff ;upper TPA in RAM, below RAM monitor 0000 ; 0000 ; 0000 org 0DC00h dc00 d3 01 out (1),a ;change memory configuration to all-RAM dc02 c3 5c e0 jp monitor_start dc05 ; dc05 ;Puts a single char (byte value) on serial output dc05 ;Call with char to send in A register. Uses B register dc05 47 write_char: ld b,a ;store char dc06 db 03 write_char_loop: in a,(3) ;check if OK to send dc08 e6 01 and 001h ;check TxRDY bit dc0a ca 06 dc jp z,write_char_loop ;loop if not set dc0d 78 ld a,b ;get char back dc0e d3 02 out (2),a ;send to output dc10 c9 ret ;returns with char in a dc11 ; dc11 ;Subroutine to write a zero-terminated string to serial output dc11 ;Pass address of string in HL register dc11 ;No error checking dc11 db 03 write_string: in a,(3) ;read status dc13 e6 01 and 001h ;check TxRDY bit dc15 ca 11 dc jp z,write_string ;loop if not set dc18 7e ld a,(hl) ;get char from string dc19 a7 and a ;check if 0 dc1a c8 ret z ;yes, finished dc1b d3 02 out (2),a ;no, write char to output dc1d 23 inc hl ;next char in string dc1e c3 11 dc jp write_string ;start over dc21 ; dc21 ;Binary loader. Receive a binary file, place in memory. dc21 ;Address of load passed in HL, length of load (= file length) in BC dc21 db 03 bload: in a,(3) ;get status dc23 e6 02 and 002h ;check RxRDY bit dc25 ca 21 dc jp z,bload ;not ready, loop dc28 db 02 in a,(2) dc2a 77 ld (hl),a dc2b 23 inc hl dc2c 0b dec bc ;byte counter dc2d 78 ld a,b ;need to test BC this way because dc2e b1 or c ;dec rp instruction does not change flags dc2f c2 21 dc jp nz,bload dc32 c9 ret dc33 ; dc33 ;Binary dump to port. Send a stream of binary data from memory to serial output dc33 ;Address of dump passed in HL, length of dump in BC dc33 db 03 bdump: in a,(3) ;get status dc35 e6 01 and 001h ;check TxRDY bit dc37 ca 33 dc jp z,bdump ;not ready, loop dc3a 7e ld a,(hl) dc3b d3 02 out (2),a dc3d 23 inc hl dc3e 0b dec bc dc3f 78 ld a,b ;need to test this way because dc40 b1 or c ;dec rp instruction does not change flags dc41 c2 33 dc jp nz,bdump dc44 c9 ret dc45 ; dc45 ;Subroutine to get a string from serial input, place in buffer. dc45 ;Buffer address passed in HL reg. dc45 ;Uses A,BC,DE,HL registers (including calls to other subroutines). dc45 ;Line entry ends by hitting return key. Return char not included in string (replaced by zero). dc45 ;Backspace editing OK. No error checking. dc45 ; dc45 0e 00 get_line: ld c,000h ;line position dc47 7c ld a,h ;put original buffer address in de dc48 57 ld d,a ;after this don't need to preserve hl dc49 7d ld a,l ;subroutines called don't use de dc4a 5f ld e,a dc4b db 03 get_line_next_char: in a,(3) ;get status dc4d e6 02 and 002h ;check RxRDY bit dc4f ca 4b dc jp z,get_line_next_char ;not ready, loop dc52 db 02 in a,(2) ;get char dc54 fe 0d cp 00dh ;check if return dc56 c8 ret z ;yes, normal exit dc57 fe 7f cp 07fh ;check if backspace (VT102 keys) dc59 ca 6d dc jp z,get_line_backspace ;yes, jump to backspace routine dc5c fe 08 cp 008h ;check if backspace (ANSI keys) dc5e ca 6d dc jp z,get_line_backspace ;yes, jump to backspace dc61 cd 05 dc call write_char ;put char on screen dc64 12 ld (de),a ;store char in buffer dc65 13 inc de ;point to next space in buffer dc66 0c inc c ;inc counter dc67 3e 00 ld a,000h dc69 12 ld (de),a ;leaves a zero-terminated string in buffer dc6a c3 4b dc jp get_line_next_char dc6d 79 get_line_backspace: ld a,c ;check current position in line dc6e fe 00 cp 000h ;at beginning of line? dc70 ca 4b dc jp z,get_line_next_char ;yes, ignore backspace, get next char dc73 1b dec de ;no, erase char from buffer dc74 0d dec c ;back up one dc75 3e 00 ld a,000h ;put a zero in buffer where the last char was dc77 12 ld (de),a dc78 21 7d df ld hl,erase_char_string ;ANSI sequence to delete one char from line dc7b cd 11 dc call write_string ;transmits sequence to backspace and erase char dc7e c3 4b dc jp get_line_next_char dc81 ; dc81 ;Creates a two-char hex string from the byte value passed in register A dc81 ;Location to place string passed in HL dc81 ;String is zero-terminated, stored in 3 locations starting at HL dc81 ;Also uses registers b,d, and e dc81 47 byte_to_hex_string: ld b,a ;store original byte dc82 cb 3f srl a ;shift right 4 times, putting dc84 cb 3f srl a ;high nybble in low-nybble spot dc86 cb 3f srl a ;and zeros in high-nybble spot dc88 cb 3f srl a dc8a 16 00 ld d,000h ;prepare for 16-bit addition dc8c 5f ld e,a ;de contains offset dc8d e5 push hl ;temporarily store string target address dc8e 21 e7 dc ld hl,hex_char_table ;use char table to get high-nybble character dc91 19 add hl,de ;add offset to start of table dc92 7e ld a,(hl) ;get char dc93 e1 pop hl ;get string target address dc94 77 ld (hl),a ;store first char of string dc95 23 inc hl ;point to next string target address dc96 78 ld a,b ;get original byte back from reg b dc97 e6 0f and 00fh ;mask off high-nybble dc99 5f ld e,a ;d still has 000h, now de has offset dc9a e5 push hl ;temp store string target address dc9b 21 e7 dc ld hl,hex_char_table ;start of table dc9e 19 add hl,de ;add offset dc9f 7e ld a,(hl) ;get char dca0 e1 pop hl ;get string target address dca1 77 ld (hl),a ;store second char of string dca2 23 inc hl ;point to third location dca3 3e 00 ld a,000h ;zero to terminate string dca5 77 ld (hl),a ;store the zero dca6 c9 ret ;done dca7 ; dca7 ;Converts a single ASCII hex char to a nybble value dca7 ;Pass char in reg A. Letter numerals must be upper case. dca7 ;Return nybble value in low-order reg A with zeros in high-order nybble if no error. dca7 ;Return 0ffh in reg A if error (char not a valid hex numeral). dca7 ;Also uses b, c, and hl registers. dca7 21 e7 dc hex_char_to_nybble: ld hl,hex_char_table dcaa 06 0f ld b,00fh ;no. of valid characters in table - 1. dcac 0e 00 ld c,000h ;will be nybble value dcae be hex_to_nybble_loop: cp (hl) ;character match here? dcaf ca bb dc jp z,hex_to_nybble_ok ;match found, exit dcb2 05 dec b ;no match, check if at end of table dcb3 fa bd dc jp m,hex_to_nybble_err ;table limit exceded, exit with error dcb6 0c inc c ;still inside table, continue search dcb7 23 inc hl dcb8 c3 ae dc jp hex_to_nybble_loop dcbb 79 hex_to_nybble_ok: ld a,c ;put nybble value in a dcbc c9 ret dcbd 3e ff hex_to_nybble_err: ld a,0ffh ;error value dcbf c9 ret dcc0 ; dcc0 ;Converts a hex character pair to a byte value dcc0 ;Called with location of high-order char in HL dcc0 ;If no error carry flag clear, returns with byte value in register A, and dcc0 ;HL pointing to next mem location after char pair. dcc0 ;If error (non-hex char) carry flag set, HL pointing to invalid char dcc0 7e hex_to_byte: ld a,(hl) ;location of character pair dcc1 e5 push hl ;store hl (hex_char_to_nybble uses it) dcc2 cd a7 dc call hex_char_to_nybble dcc5 e1 pop hl ;returns with nybble value in a reg, or 0ffh if error dcc6 fe ff cp 0ffh ;non-hex character? dcc8 ca e5 dc jp z,hex_to_byte_err ;yes, exit with error dccb cb 27 sla a ;no, move low order nybble to high side dccd cb 27 sla a dccf cb 27 sla a dcd1 cb 27 sla a dcd3 57 ld d,a ;store high-nybble dcd4 23 inc hl ;get next character of the pair dcd5 7e ld a,(hl) dcd6 e5 push hl ;store hl dcd7 cd a7 dc call hex_char_to_nybble dcda e1 pop hl dcdb fe ff cp 0ffh ;non-hex character? dcdd ca e5 dc jp z,hex_to_byte_err ;yes, exit with error dce0 b2 or d ;no, combine with high-nybble dce1 23 inc hl ;point to next memory location after char pair dce2 37 scf dce3 3f ccf ;no-error exit (carry = 0) dce4 c9 ret dce5 37 hex_to_byte_err: scf ;error, carry flag set dce6 c9 ret dce7 .. hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table dcf7 ; dcf7 ;Subroutine to get a two-byte address from serial input. dcf7 ;Returns with address value in HL dcf7 ;Uses locations in RAM for buffer and variables dcf7 21 08 db address_entry: ld hl,buffer ;location for entered string dcfa cd 45 dc call get_line ;returns with address string in buffer dcfd 21 08 db ld hl,buffer ;location of stored address entry string dd00 cd c0 dc call hex_to_byte ;will get high-order byte first dd03 da 19 dd jp c, address_entry_error ;if error, jump dd06 32 01 db ld (current_location+1),a ;store high-order byte, little-endian dd09 21 0a db ld hl,buffer+2 ;point to low-order hex char pair dd0c cd c0 dc call hex_to_byte ;get low-order byte dd0f da 19 dd jp c, address_entry_error ;jump if error dd12 32 00 db ld (current_location),a ;store low-order byte in lower memory dd15 2a 00 db ld hl,(current_location) ;put memory address in hl dd18 c9 ret dd19 21 bb df address_entry_error: ld hl,address_error_msg dd1c cd 11 dc call write_string dd1f c3 f7 dc jp address_entry dd22 ; dd22 ;Subroutine to get a decimal string, return a word value dd22 ;Calls decimal_string_to_word subroutine dd22 21 08 db decimal_entry: ld hl,buffer dd25 cd 45 dc call get_line ;returns with DE pointing to terminating zero dd28 21 08 db ld hl,buffer dd2b cd 38 dd call decimal_string_to_word dd2e d0 ret nc ;no error, return with word in hl dd2f 21 2f e0 ld hl,decimal_error_msg ;error, try again dd32 cd 11 dc call write_string dd35 c3 22 dd jp decimal_entry dd38 ; dd38 ;Subroutine to convert a decimal string to a word value dd38 ;Call with address of string in HL, pointer to end of string in DE dd38 ;Carry flag set if error (non-decimal char) dd38 ;Carry flag clear, word value in HL if no error. dd38 42 decimal_string_to_word: ld b,d dd39 4b ld c,e ;use BC as string pointer dd3a 22 00 db ld (current_location),hl ;store addr. of start of buffer in RAM word variable dd3d 21 00 00 ld hl,000h ;starting value zero dd40 22 06 db ld (current_value),hl dd43 21 88 dd ld hl,decimal_place_value ;pointer to values dd46 22 04 db ld (value_pointer),hl dd49 0b decimal_next_char: dec bc ;next char in string (moving right to left) dd4a 2a 00 db ld hl,(current_location) ;check if at end of decimal string dd4d 37 scf ;get ready to subtract de from buffer addr. dd4e 3f ccf ;set carry to zero (clear) dd4f ed 42 sbc hl,bc ;keep going if bc > or = hl (buffer address) dd51 da 5d dd jp c,decimal_continue ;borrow means bc > hl dd54 ca 5d dd jp z,decimal_continue ;z means bc = hl dd57 2a 06 db ld hl,(current_value) ;return if de < buffer address (no borrow) dd5a 37 scf ;get value back from RAM variable dd5b 3f ccf dd5c c9 ret ;return with carry clear, value in hl dd5d 0a decimal_continue: ld a,(bc) ;next char in string (right to left) dd5e d6 30 sub 030h ;ASCII value of zero char dd60 fa 83 dd jp m,decimal_error ;error if char value less than 030h dd63 fe 0a cp 00ah ;error if byte value > or = 10 decimal dd65 f2 83 dd jp p,decimal_error ;a reg now has value of decimal numeral dd68 2a 04 db ld hl,(value_pointer) ;get value to add an put in de dd6b 5e ld e,(hl) ;little-endian (low byte in low memory) dd6c 23 inc hl dd6d 56 ld d,(hl) dd6e 23 inc hl ;hl now points to next value dd6f 22 04 db ld (value_pointer),hl dd72 2a 06 db ld hl,(current_value) ;get back current value dd75 3d decimal_add: dec a ;add loop to increase total value dd76 fa 7d dd jp m,decimal_add_done ;end of multiplication dd79 19 add hl,de dd7a c3 75 dd jp decimal_add dd7d 22 06 db decimal_add_done: ld (current_value),hl dd80 c3 49 dd jp decimal_next_char dd83 37 decimal_error: scf dd84 c9 ret dd85 c3 75 dd jp decimal_add dd88 01 00 0a 00 64 00 e8 03 10 27 decimal_place_value: defw 1,10,100,1000,10000 dd92 ; dd92 ;Memory dump dd92 ;Displays a 256-byte block of memory in 16-byte rows. dd92 ;Called with address of start of block in HL dd92 22 00 db memory_dump: ld (current_location),hl ;store address of block to be displayed dd95 3e 00 ld a,000h dd97 32 03 db ld (byte_count),a ;initialize byte count dd9a 32 02 db ld (line_count),a ;initialize line count dd9d c3 d2 dd jp dump_new_line dda0 2a 00 db dump_next_byte: ld hl,(current_location) ;get byte address from storage, dda3 7e ld a,(hl) ;get byte to be converted to string dda4 23 inc hl ;increment address and dda5 22 00 db ld (current_location),hl ;store back dda8 21 08 db ld hl,buffer ;location to store string ddab cd 81 dc call byte_to_hex_string ;convert ddae 21 08 db ld hl,buffer ;display string ddb1 cd 11 dc call write_string ddb4 3a 03 db ld a,(byte_count) ;next byte ddb7 3c inc a ddb8 ca 02 de jp z,dump_done ;stop when 256 bytes displayed ddbb 32 03 db ld (byte_count),a ;not finished yet, store ddbe 3a 02 db ld a,(line_count) ;end of line (16 characters)? ddc1 fe 0f cp 00fh ;yes, start new line ddc3 ca d2 dd jp z,dump_new_line ddc6 3c inc a ;no, increment line count ddc7 32 02 db ld (line_count),a ddca 3e 20 ld a,020h ;print space ddcc cd 05 dc call write_char ddcf c3 a0 dd jp dump_next_byte ;continue ddd2 3e 00 dump_new_line: ld a,000h ;reset line count to zero ddd4 32 02 db ld (line_count),a ddd7 cd 82 de call write_newline ddda 2a 00 db ld hl,(current_location) ;location of start of line dddd 7c ld a,h ;high byte of address ddde 21 08 db ld hl, buffer dde1 cd 81 dc call byte_to_hex_string ;convert dde4 21 08 db ld hl,buffer dde7 cd 11 dc call write_string ;write high byte ddea 2a 00 db ld hl,(current_location) dded 7d ld a,l ;low byte of address ddee 21 08 db ld hl, buffer ddf1 cd 81 dc call byte_to_hex_string ;convert ddf4 21 08 db ld hl,buffer ddf7 cd 11 dc call write_string ;write low byte ddfa 3e 20 ld a,020h ;space ddfc cd 05 dc call write_char ddff c3 a0 dd jp dump_next_byte ;now write 16 bytes de02 3e 00 dump_done: ld a,000h de04 21 08 db ld hl,buffer de07 77 ld (hl),a ;clear buffer of last string de08 cd 82 de call write_newline de0b c9 ret de0c ; de0c ;Memory load de0c ;Loads RAM memory with bytes entered as hex characters de0c ;Called with address to start loading in HL de0c ;Displays entered data in 16-byte rows. de0c 22 00 db memory_load: ld (current_location),hl de0f 21 e7 df ld hl,data_entry_msg de12 cd 11 dc call write_string de15 c3 5f de jp load_new_line de18 cd 78 de load_next_char: call get_char de1b fe 0d cp 00dh ;return? de1d ca 74 de jp z,load_done ;yes, quit de20 32 08 db ld (buffer),a de23 cd 78 de call get_char de26 fe 0d cp 00dh ;return? de28 ca 74 de jp z,load_done ;yes, quit de2b 32 09 db ld (buffer+1),a de2e 21 08 db ld hl,buffer de31 cd c0 dc call hex_to_byte de34 da 6a de jp c,load_data_entry_error ;non-hex character de37 2a 00 db ld hl,(current_location) ;get byte address from storage, de3a 77 ld (hl),a ;store byte de3b 23 inc hl ;increment address and de3c 22 00 db ld (current_location),hl ;store back de3f 3a 08 db ld a,(buffer) de42 cd 05 dc call write_char de45 3a 09 db ld a,(buffer+1) de48 cd 05 dc call write_char de4b 3a 02 db ld a,(line_count) ;end of line (16 characters)? de4e fe 0f cp 00fh ;yes, start new line de50 ca 5f de jp z,load_new_line de53 3c inc a ;no, increment line count de54 32 02 db ld (line_count),a de57 3e 20 ld a,020h ;print space de59 cd 05 dc call write_char de5c c3 18 de jp load_next_char ;continue de5f 3e 00 load_new_line: ld a,000h ;reset line count to zero de61 32 02 db ld (line_count),a de64 cd 82 de call write_newline de67 c3 18 de jp load_next_char ;continue de6a cd 82 de load_data_entry_error: call write_newline de6d 21 14 e0 ld hl,data_error_msg de70 cd 11 dc call write_string de73 c9 ret de74 cd 82 de load_done: call write_newline de77 c9 ret de78 ; de78 ;Get one ASCII character from the serial port. de78 ;Returns with char in A reg. No error checking. de78 db 03 get_char: in a,(3) ;get status de7a e6 02 and 002h ;check RxRDY bit de7c ca 78 de jp z,get_char ;not ready, loop de7f db 02 in a,(2) ;get char de81 c9 ret de82 ; de82 ;Subroutine to start a new line de82 3e 0d write_newline: ld a,00dh ;ASCII carriage return character de84 cd 05 dc call write_char de87 3e 0a ld a,00ah ;new line (line feed) character de89 cd 05 dc call write_char de8c c9 ret de8d ; de8d ;Subroutine to read one disk sector (256 bytes) de8d ;Address to place data passed in HL de8d ;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B de8d ;LBA bits 16 to 23 passed in E de8d disk_read: de8d db 0f rd_status_loop_1: in a,(0fh) ;check status de8f e6 80 and 80h ;check BSY bit de91 c2 8d de jp nz,rd_status_loop_1 ;loop until not busy de94 db 0f rd_status_loop_2: in a,(0fh) ;check status de96 e6 40 and 40h ;check DRDY bit de98 ca 94 de jp z,rd_status_loop_2 ;loop until ready de9b 3e 01 ld a,01h ;number of sectors = 1 de9d d3 0a out (0ah),a ;sector count register de9f 79 ld a,c dea0 d3 0b out (0bh),a ;lba bits 0 - 7 dea2 78 ld a,b dea3 d3 0c out (0ch),a ;lba bits 8 - 15 dea5 7b ld a,e dea6 d3 0d out (0dh),a ;lba bits 16 - 23 dea8 3e e0 ld a,11100000b ;LBA mode, select drive 0 deaa d3 0e out (0eh),a ;drive/head register deac 3e 20 ld a,20h ;Read sector command deae d3 0f out (0fh),a deb0 db 0f rd_wait_for_DRQ_set: in a,(0fh) ;read status deb2 e6 08 and 08h ;DRQ bit deb4 ca b0 de jp z,rd_wait_for_DRQ_set ;loop until bit set deb7 db 0f rd_wait_for_BSY_clear: in a,(0fh) deb9 e6 80 and 80h debb c2 b7 de jp nz,rd_wait_for_BSY_clear debe db 0f in a,(0fh) ;clear INTRQ dec0 db 08 read_loop: in a,(08h) ;get data dec2 77 ld (hl),a dec3 23 inc hl dec4 db 0f in a,(0fh) ;check status dec6 e6 08 and 08h ;DRQ bit dec8 c2 c0 de jp nz,read_loop ;loop until cleared decb c9 ret decc ; decc ;Subroutine to write one disk sector (256 bytes) decc ;Address of data to write to disk passed in HL decc ;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B decc ;LBA bits 16 to 23 passed in E decc disk_write: decc db 0f wr_status_loop_1: in a,(0fh) ;check status dece e6 80 and 80h ;check BSY bit ded0 c2 cc de jp nz,wr_status_loop_1 ;loop until not busy ded3 db 0f wr_status_loop_2: in a,(0fh) ;check status ded5 e6 40 and 40h ;check DRDY bit ded7 ca d3 de jp z,wr_status_loop_2 ;loop until ready deda 3e 01 ld a,01h ;number of sectors = 1 dedc d3 0a out (0ah),a ;sector count register dede 79 ld a,c dedf d3 0b out (0bh),a ;lba bits 0 - 7 dee1 78 ld a,b dee2 d3 0c out (0ch),a ;lba bits 8 - 15 dee4 7b ld a,e dee5 d3 0d out (0dh),a ;lba bits 16 - 23 dee7 3e e0 ld a,11100000b ;LBA mode, select drive 0 dee9 d3 0e out (0eh),a ;drive/head register deeb 3e 30 ld a,30h ;Write sector command deed d3 0f out (0fh),a deef db 0f wr_wait_for_DRQ_set: in a,(0fh) ;read status def1 e6 08 and 08h ;DRQ bit def3 ca ef de jp z,wr_wait_for_DRQ_set ;loop until bit set def6 7e write_loop: ld a,(hl) def7 d3 08 out (08h),a ;write data def9 23 inc hl defa db 0f in a,(0fh) ;read status defc e6 08 and 08h ;check DRQ bit defe c2 f6 de jp nz,write_loop ;write until bit cleared df01 db 0f wr_wait_for_BSY_clear: in a,(0fh) df03 e6 80 and 80h df05 c2 01 df jp nz,wr_wait_for_BSY_clear df08 db 0f in a,(0fh) ;clear INTRQ df0a c9 ret df0b ; df0b ;Strings used in subroutines df0b .. 00 length_entry_string: defm "Enter length of file to load (decimal): ",0 df34 .. 00 dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0 df5b .. 00 LBA_entry_string: defm "Enter LBA (decimal, 0 to 65535): ",0 df7d 08 1b .. 00 erase_char_string: defm 008h,01bh,"[K",000h ;ANSI sequence for backspace, erase to end of line. df82 .. 00 address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0 dfbb .. 00 address_error_msg: defm "\r\nError: invalid hex character, try again: ",0 dfe7 .. 00 data_entry_msg: defm "Enter hex bytes, hit return when finished.\r\n",0 e014 .. 00 data_error_msg: defm "Error: invalid hex byte.\r\n",0 e02f .. 00 decimal_error_msg: defm "\r\nError: invalid decimal number, try again: ",0 e05c ; e05c ;Simple monitor program for CPUville Z80 computer with serial interface. e05c e05c cd 82 de monitor_start: call write_newline ;routine program return here to avoid re-initialization of port e05f 3e 3e ld a,03eh ;cursor symbol e061 cd 05 dc call write_char e064 21 08 db ld hl,buffer e067 cd 45 dc call get_line ;get monitor input string (command) e06a cd 82 de call write_newline e06d cd 71 e0 call parse ;interprets command, returns with address to jump to in HL e070 e9 jp (hl) e071 ; e071 ;Parses an input line stored in buffer for available commands as described in parse table. e071 ;Returns with address of jump to action for the command in HL e071 01 a9 e3 parse: ld bc,parse_table ;bc is pointer to parse_table e074 0a parse_start: ld a,(bc) ;get pointer to match string from parse table e075 5f ld e,a e076 03 inc bc e077 0a ld a,(bc) e078 57 ld d,a ;de will is pointer to strings for matching e079 1a ld a,(de) ;get first char from match string e07a f6 00 or 000h ;zero? e07c ca 97 e0 jp z,parser_exit ;yes, exit no_match e07f 21 08 db ld hl,buffer ;no, parse input string e082 be match_loop: cp (hl) ;compare buffer char with match string char e083 c2 91 e0 jp nz,no_match ;no match, go to next match string e086 f6 00 or 000h ;end of strings (zero)? e088 ca 97 e0 jp z,parser_exit ;yes, matching string found e08b 13 inc de ;match so far, point to next char in match string e08c 1a ld a,(de) ;get next character from match string e08d 23 inc hl ;and point to next char in input string e08e c3 82 e0 jp match_loop ;check for match e091 03 no_match: inc bc ;skip over jump target to e092 03 inc bc e093 03 inc bc ;get address of next matching string e094 c3 74 e0 jp parse_start e097 03 parser_exit: inc bc ;skip to address of jump for match e098 0a ld a,(bc) e099 6f ld l,a e09a 03 inc bc e09b 0a ld a,(bc) e09c 67 ld h,a ;returns with jump address in hl e09d c9 ret e09e ; e09e ;Actions to be taken on match e09e ; e09e ;Memory dump program e09e ;Input 4-digit hexadecimal address e09e ;Calls memory_dump subroutine e09e 21 f5 e1 dump_jump: ld hl,dump_message ;Display greeting e0a1 cd 11 dc call write_string e0a4 21 82 df ld hl,address_entry_msg ;get ready to get address e0a7 cd 11 dc call write_string e0aa cd f7 dc call address_entry ;returns with address in HL e0ad cd 82 de call write_newline e0b0 cd 92 dd call memory_dump e0b3 c3 5c e0 jp monitor_start e0b6 ; e0b6 ;Hex loader, displays formatted input e0b6 21 1c e2 load_jump: ld hl,load_message ;Display greeting e0b9 cd 11 dc call write_string ;get address to load e0bc 21 82 df ld hl,address_entry_msg ;get ready to get address e0bf cd 11 dc call write_string e0c2 cd f7 dc call address_entry e0c5 cd 82 de call write_newline e0c8 cd 0c de call memory_load e0cb c3 5c e0 jp monitor_start e0ce ; e0ce ;Jump and run do the same thing: get an address and jump to it. e0ce 21 4b e2 run_jump: ld hl,run_message ;Display greeting e0d1 cd 11 dc call write_string e0d4 21 82 df ld hl,address_entry_msg ;get ready to get address e0d7 cd 11 dc call write_string e0da cd f7 dc call address_entry e0dd e9 jp (hl) e0de ; e0de ;Help and ? do the same thing, display the available commands e0de 21 dd e1 help_jump: ld hl,help_message e0e1 cd 11 dc call write_string e0e4 01 a9 e3 ld bc,parse_table ;table with pointers to command strings e0e7 0a help_loop: ld a,(bc) ;displays the strings for matching commands, e0e8 6f ld l,a ;getting the string addresses from the e0e9 03 inc bc ;parse table e0ea 0a ld a,(bc) ;pass address of string to hl through a reg e0eb 67 ld h,a e0ec 7e ld a,(hl) ;hl now points to start of match string e0ed f6 00 or 000h ;exit if no_match string e0ef ca 02 e1 jp z,help_done e0f2 c5 push bc ;write_char uses b register e0f3 3e 20 ld a,020h ;space char e0f5 cd 05 dc call write_char e0f8 c1 pop bc e0f9 cd 11 dc call write_string ;writes match string e0fc 03 inc bc ;pass over jump address in table e0fd 03 inc bc e0fe 03 inc bc e0ff c3 e7 e0 jp help_loop e102 c3 5c e0 help_done: jp monitor_start e105 ; e105 ;Binary file load. Need both address to load and length of file e105 21 80 e2 bload_jump: ld hl,bload_message e108 cd 11 dc call write_string e10b 21 82 df ld hl,address_entry_msg e10e cd 11 dc call write_string e111 cd f7 dc call address_entry e114 cd 82 de call write_newline e117 e5 push hl e118 21 0b df ld hl,length_entry_string e11b cd 11 dc call write_string e11e cd 22 dd call decimal_entry e121 44 ld b,h e122 4d ld c,l e123 21 a3 e2 ld hl,bload_ready_message e126 cd 11 dc call write_string e129 e1 pop hl e12a cd 21 dc call bload e12d c3 5c e0 jp monitor_start e130 ; e130 ;Binary memory dump. Need address of start of dump and no. bytes e130 21 c7 e2 bdump_jump: ld hl,bdump_message e133 cd 11 dc call write_string e136 21 82 df ld hl,address_entry_msg e139 cd 11 dc call write_string e13c cd f7 dc call address_entry e13f cd 82 de call write_newline e142 e5 push hl e143 21 34 df ld hl,dump_entry_string e146 cd 11 dc call write_string e149 cd 22 dd call decimal_entry e14c 44 ld b,h e14d 4d ld c,l e14e 21 f7 e2 ld hl,bdump_ready_message e151 cd 11 dc call write_string e154 cd 78 de call get_char e157 e1 pop hl e158 cd 33 dc call bdump e15b c3 5c e0 jp monitor_start e15e ;Disk read. Need memory address to place data, LBA of sector to read e15e 21 1e e3 diskrd_jump: ld hl,diskrd_message e161 cd 11 dc call write_string e164 21 82 df ld hl,address_entry_msg e167 cd 11 dc call write_string e16a cd f7 dc call address_entry e16d cd 82 de call write_newline e170 e5 push hl e171 21 5b df ld hl,LBA_entry_string e174 cd 11 dc call write_string e177 cd 22 dd call decimal_entry e17a 44 ld b,h e17b 4d ld c,l e17c 1e 00 ld e,00h e17e e1 pop hl e17f cd 8d de call disk_read e182 c3 5c e0 jp monitor_start e185 21 46 e3 diskwr_jump: ld hl,diskwr_message e188 cd 11 dc call write_string e18b 21 82 df ld hl,address_entry_msg e18e cd 11 dc call write_string e191 cd f7 dc call address_entry e194 cd 82 de call write_newline e197 e5 push hl e198 21 5b df ld hl,LBA_entry_string e19b cd 11 dc call write_string e19e cd 22 dd call decimal_entry e1a1 44 ld b,h e1a2 4d ld c,l e1a3 1e 00 ld e,00h e1a5 e1 pop hl e1a6 cd cc de call disk_write e1a9 c3 5c e0 jp monitor_start e1ac 21 00 08 cpm_jump: ld hl,0800h e1af 01 00 00 ld bc,0000h e1b2 1e 00 ld e,00h e1b4 cd 8d de call disk_read e1b7 d3 00 out (0),a ;CP/M loader uses ROM routine to read disk e1b9 c3 00 08 jp 0800h e1bc ;Prints message for no match to entered command e1bc 21 da e1 no_match_jump: ld hl,no_match_message e1bf cd 11 dc call write_string e1c2 21 08 db ld hl, buffer e1c5 cd 11 dc call write_string e1c8 c3 5c e0 jp monitor_start e1cb ; e1cb ;Monitor data structures: e1cb ; e1cb .. 00 monitor_message: defm "\r\nROM ver. 8\r\n",0 e1da .. 00 no_match_message: defm "? ",0 e1dd .. 00 help_message: defm "Commands implemented:\r\n",0 e1f5 .. 00 dump_message: defm "Displays a 256-byte block of memory.\r\n",0 e21c .. 00 load_message: defm "Enter hex bytes starting at memory location.\r\n",0 e24b .. 00 run_message: defm "Will jump to (execute) program at address entered.\r\n",0 e280 .. 00 bload_message: defm "Loads a binary file into memory.\r\n",0 e2a3 .. 00 bload_ready_message: defm "\n\rReady to receive, start transfer.",0 e2c7 .. 00 bdump_message: defm "Dumps binary data from memory to serial port.\r\n",0 e2f7 .. 00 bdump_ready_message: defm "\n\rReady to send, hit any key to start.",0 e31e .. 00 diskrd_message: defm "Reads one sector from disk to memory.\r\n",0 e346 .. 00 diskwr_message: defm "Writes one sector from memory to disk.\r\n",0 e36f ;Strings for matching: e36f .. 00 dump_string: defm "dump",0 e374 .. 00 load_string: defm "load",0 e379 .. 00 jump_string: defm "jump",0 e37e .. 00 run_string: defm "run",0 e382 .. 00 question_string: defm "?",0 e384 .. 00 help_string: defm "help",0 e389 .. 00 bload_string: defm "bload",0 e38f .. 00 bdump_string: defm "bdump",0 e395 .. 00 diskrd_string: defm "diskrd",0 e39c .. 00 diskwr_string: defm "diskwr",0 e3a3 .. 00 cpm_string: defm "cpm",0 e3a7 00 00 no_match_string: defm 0,0 e3a9 ;Table for matching strings to jumps e3a9 6f e3 9e e0 74 e3 b6 e0 parse_table: defw dump_string,dump_jump,load_string,load_jump e3b1 79 e3 ce e0 7e e3 ce e0 defw jump_string,run_jump,run_string,run_jump e3b9 82 e3 de e0 84 e3 de e0 defw question_string,help_jump,help_string,help_jump e3c1 89 e3 05 e1 8f e3 30 e1 defw bload_string,bload_jump,bdump_string,bdump_jump e3c9 95 e3 5e e1 9c e3 85 e1 defw diskrd_string,diskrd_jump,diskwr_string,diskwr_jump e3d1 a3 e3 ac e1 defw cpm_string,cpm_jump e3d5 a7 e3 bc e1 defw no_match_string,no_match_jump e3d9 # End of file RAM_monitor.asm e3d9