# File 2K_ROM_15.asm 0000 ;ROM monitor for single-board Z80 computer with bus display. 0000 ;Mixed features of v.7 and v.8 ROMs 0000 ;ROM has simple programs to run on slow clock, with input switches and output LEDs 0000 ;Also has monitor program with command to run CP/M 0000 ;Jumps to address on input switches at power-on or reset 0000 ; 0000 org 00000h 0000 db 04 in a,(4) ;Get address from input ports 0002 6f ld l,a 0003 db 05 in a,(5) 0005 67 ld h,a 0006 e9 jp (hl) ;Jump to the address 0007 db 04 Port_Reflector: in a,(4) ;Simple program to test ports 0009 d3 04 out (4),a 000b db 05 in a,(5) 000d d3 05 out (5),a 000f c3 07 00 jp Port_Reflector 0012 3e 00 Simple_Counter: ld a,000h ;One-byte counter for slow clock 0014 d3 04 Loop_1: out (4),a 0016 3c inc a 0017 c3 14 00 jp Loop_1 001a 2e 00 Count_to_a_million: ld l,000h ;Two-byte (16-bit) counter 001c 26 00 ld h,000h ;Clear registers 001e 3e 10 Loop_2: ld a,010h ;Count 16 times, then 0020 3d Loop_3: dec a 0021 c2 20 00 jp nz,Loop_3 0024 23 inc hl ;increment the 16-bit number 0025 7d ld a,l 0026 d3 04 out (4),a ;Output the 16-bit number 0028 7c ld a,h 0029 d3 05 out (5),a 002b c3 1e 00 jp Loop_2 ;Do it again 002e 002e ; 002e ; 002e ;Subroutines for the monitor use these RAM variables: 002e current_location: equ 0xdb00 ;word variable in RAM 002e line_count: equ 0xdb02 ;byte variable in RAM 002e byte_count: equ 0xdb03 ;byte variable in RAM 002e value_pointer: equ 0xdb04 ;word variable in RAM 002e current_value: equ 0xdb06 ;word variable in RAM 002e buffer: equ 0xdb08 ;buffer in RAM -- up to stack area 002e 002e ;Need to have stack in upper RAM, but not in area of CP/M or RAM monitor. 002e ROM_monitor_stack: equ 0xdbff ;upper TPA in RAM, below RAM monitor 002e 002e ;Subroutine to initialize serial port UART 002e ;Needs to be called only once after computer comes out of reset. 002e ;If called while port is active will cause port to fail. 002e ;16x = 9600 baud 002e 3e 4e initialize_port: ld a,04eh ;1 stop bit, no parity, 8-bit char, 16x baud 0030 d3 03 out (3),a ;write to control port 0032 3e 37 ld a,037h ;enable receive and transmit 0034 d3 03 out (3),a ;write to control port 0036 c9 ret 0037 ; 0037 ;Puts a single char (byte value) on serial output 0037 ;Call with char to send in A register. Uses B register 0037 47 write_char: ld b,a ;store char 0038 db 03 write_char_loop: in a,(3) ;check if OK to send 003a e6 01 and 001h ;check TxRDY bit 003c ca 38 00 jp z,write_char_loop ;loop if not set 003f 78 ld a,b ;get char back 0040 d3 02 out (2),a ;send to output 0042 c9 ret ;returns with char in a 0043 ; 0043 ;Subroutine to write a zero-terminated string to serial output 0043 ;Pass address of string in HL register 0043 ;No error checking 0043 db 03 write_string: in a,(3) ;read status 0045 e6 01 and 001h ;check TxRDY bit 0047 ca 43 00 jp z,write_string ;loop if not set 004a 7e ld a,(hl) ;get char from string 004b a7 and a ;check if 0 004c c8 ret z ;yes, finished 004d d3 02 out (2),a ;no, write char to output 004f 23 inc hl ;next char in string 0050 c3 43 00 jp write_string ;start over 0053 ; 0053 ;Binary loader. Receive a binary file, place in memory. 0053 ;Address of load passed in HL, length of load (= file length) in BC 0053 db 03 bload: in a,(3) ;get status 0055 e6 02 and 002h ;check RxRDY bit 0057 ca 53 00 jp z,bload ;not ready, loop 005a db 02 in a,(2) 005c 77 ld (hl),a 005d 23 inc hl 005e 0b dec bc ;byte counter 005f 78 ld a,b ;need to test BC this way because 0060 b1 or c ;dec rp instruction does not change flags 0061 c2 53 00 jp nz,bload 0064 c9 ret 0065 ; 0065 ;Binary dump to port. Send a stream of binary data from memory to serial output 0065 ;Address of dump passed in HL, length of dump in BC 0065 db 03 bdump: in a,(3) ;get status 0067 e6 01 and 001h ;check TxRDY bit 0069 ca 65 00 jp z,bdump ;not ready, loop 006c 7e ld a,(hl) 006d d3 02 out (2),a 006f 23 inc hl 0070 0b dec bc 0071 78 ld a,b ;need to test this way because 0072 b1 or c ;dec rp instruction does not change flags 0073 c2 65 00 jp nz,bdump 0076 c9 ret 0077 ; 0077 ;Subroutine to get a string from serial input, place in buffer. 0077 ;Buffer address passed in HL reg. 0077 ;Uses A,BC,DE,HL registers (including calls to other subroutines). 0077 ;Line entry ends by hitting return key. Return char not included in string (replaced by zero). 0077 ;Backspace editing OK. No error checking. 0077 ; 0077 0e 00 get_line: ld c,000h ;line position 0079 7c ld a,h ;put original buffer address in de 007a 57 ld d,a ;after this don't need to preserve hl 007b 7d ld a,l ;subroutines called don't use de 007c 5f ld e,a 007d db 03 get_line_next_char: in a,(3) ;get status 007f e6 02 and 002h ;check RxRDY bit 0081 ca 7d 00 jp z,get_line_next_char ;not ready, loop 0084 db 02 in a,(2) ;get char 0086 fe 0d cp 00dh ;check if return 0088 c8 ret z ;yes, normal exit 0089 fe 7f cp 07fh ;check if backspace (VT102 keys) 008b ca 9f 00 jp z,get_line_backspace ;yes, jump to backspace routine 008e fe 08 cp 008h ;check if backspace (ANSI keys) 0090 ca 9f 00 jp z,get_line_backspace ;yes, jump to backspace 0093 cd 37 00 call write_char ;put char on screen 0096 12 ld (de),a ;store char in buffer 0097 13 inc de ;point to next space in buffer 0098 0c inc c ;inc counter 0099 3e 00 ld a,000h 009b 12 ld (de),a ;leaves a zero-terminated string in buffer 009c c3 7d 00 jp get_line_next_char 009f 79 get_line_backspace: ld a,c ;check current position in line 00a0 fe 00 cp 000h ;at beginning of line? 00a2 ca 7d 00 jp z,get_line_next_char ;yes, ignore backspace, get next char 00a5 1b dec de ;no, erase char from buffer 00a6 0d dec c ;back up one 00a7 3e 00 ld a,000h ;put a zero in buffer where the last char was 00a9 12 ld (de),a 00aa 21 b5 03 ld hl,erase_char_string ;ANSI sequence to delete one char from line 00ad cd 43 00 call write_string ;transmits sequence to backspace and erase char 00b0 c3 7d 00 jp get_line_next_char 00b3 ; 00b3 ;Creates a two-char hex string from the byte value passed in register A 00b3 ;Location to place string passed in HL 00b3 ;String is zero-terminated, stored in 3 locations starting at HL 00b3 ;Also uses registers b,d, and e 00b3 47 byte_to_hex_string: ld b,a ;store original byte 00b4 cb 3f srl a ;shift right 4 times, putting 00b6 cb 3f srl a ;high nybble in low-nybble spot 00b8 cb 3f srl a ;and zeros in high-nybble spot 00ba cb 3f srl a 00bc 16 00 ld d,000h ;prepare for 16-bit addition 00be 5f ld e,a ;de contains offset 00bf e5 push hl ;temporarily store string target address 00c0 21 19 01 ld hl,hex_char_table ;use char table to get high-nybble character 00c3 19 add hl,de ;add offset to start of table 00c4 7e ld a,(hl) ;get char 00c5 e1 pop hl ;get string target address 00c6 77 ld (hl),a ;store first char of string 00c7 23 inc hl ;point to next string target address 00c8 78 ld a,b ;get original byte back from reg b 00c9 e6 0f and 00fh ;mask off high-nybble 00cb 5f ld e,a ;d still has 000h, now de has offset 00cc e5 push hl ;temp store string target address 00cd 21 19 01 ld hl,hex_char_table ;start of table 00d0 19 add hl,de ;add offset 00d1 7e ld a,(hl) ;get char 00d2 e1 pop hl ;get string target address 00d3 77 ld (hl),a ;store second char of string 00d4 23 inc hl ;point to third location 00d5 3e 00 ld a,000h ;zero to terminate string 00d7 77 ld (hl),a ;store the zero 00d8 c9 ret ;done 00d9 ; 00d9 ;Converts a single ASCII hex char to a nybble value 00d9 ;Pass char in reg A. Letter numerals must be upper case. 00d9 ;Return nybble value in low-order reg A with zeros in high-order nybble if no error. 00d9 ;Return 0ffh in reg A if error (char not a valid hex numeral). 00d9 ;Also uses b, c, and hl registers. 00d9 21 19 01 hex_char_to_nybble: ld hl,hex_char_table 00dc 06 0f ld b,00fh ;no. of valid characters in table - 1. 00de 0e 00 ld c,000h ;will be nybble value 00e0 be hex_to_nybble_loop: cp (hl) ;character match here? 00e1 ca ed 00 jp z,hex_to_nybble_ok ;match found, exit 00e4 05 dec b ;no match, check if at end of table 00e5 fa ef 00 jp m,hex_to_nybble_err ;table limit exceded, exit with error 00e8 0c inc c ;still inside table, continue search 00e9 23 inc hl 00ea c3 e0 00 jp hex_to_nybble_loop 00ed 79 hex_to_nybble_ok: ld a,c ;put nybble value in a 00ee c9 ret 00ef 3e ff hex_to_nybble_err: ld a,0ffh ;error value 00f1 c9 ret 00f2 ; 00f2 ;Converts a hex character pair to a byte value 00f2 ;Called with location of high-order char in HL 00f2 ;If no error carry flag clear, returns with byte value in register A, and 00f2 ;HL pointing to next mem location after char pair. 00f2 ;If error (non-hex char) carry flag set, HL pointing to invalid char 00f2 7e hex_to_byte: ld a,(hl) ;location of character pair 00f3 e5 push hl ;store hl (hex_char_to_nybble uses it) 00f4 cd d9 00 call hex_char_to_nybble 00f7 e1 pop hl ;returns with nybble value in a reg, or 0ffh if error 00f8 fe ff cp 0ffh ;non-hex character? 00fa ca 17 01 jp z,hex_to_byte_err ;yes, exit with error 00fd cb 27 sla a ;no, move low order nybble to high side 00ff cb 27 sla a 0101 cb 27 sla a 0103 cb 27 sla a 0105 57 ld d,a ;store high-nybble 0106 23 inc hl ;get next character of the pair 0107 7e ld a,(hl) 0108 e5 push hl ;store hl 0109 cd d9 00 call hex_char_to_nybble 010c e1 pop hl 010d fe ff cp 0ffh ;non-hex character? 010f ca 17 01 jp z,hex_to_byte_err ;yes, exit with error 0112 b2 or d ;no, combine with high-nybble 0113 23 inc hl ;point to next memory location after char pair 0114 37 scf 0115 3f ccf ;no-error exit (carry = 0) 0116 c9 ret 0117 37 hex_to_byte_err: scf ;error, carry flag set 0118 c9 ret 0119 .. hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table 0129 ; 0129 ;Subroutine to get a two-byte address from serial input. 0129 ;Returns with address value in HL 0129 ;Uses locations in RAM for buffer and variables 0129 21 08 db address_entry: ld hl,buffer ;location for entered string 012c cd 77 00 call get_line ;returns with address string in buffer 012f 21 08 db ld hl,buffer ;location of stored address entry string 0132 cd f2 00 call hex_to_byte ;will get high-order byte first 0135 da 4b 01 jp c, address_entry_error ;if error, jump 0138 32 01 db ld (current_location+1),a ;store high-order byte, little-endian 013b 21 0a db ld hl,buffer+2 ;point to low-order hex char pair 013e cd f2 00 call hex_to_byte ;get low-order byte 0141 da 4b 01 jp c, address_entry_error ;jump if error 0144 32 00 db ld (current_location),a ;store low-order byte in lower memory 0147 2a 00 db ld hl,(current_location) ;put memory address in hl 014a c9 ret 014b 21 f3 03 address_entry_error: ld hl,address_error_msg 014e cd 43 00 call write_string 0151 c3 29 01 jp address_entry 0154 ; 0154 ;Subroutine to get a decimal string, return a word value 0154 ;Calls decimal_string_to_word subroutine 0154 21 08 db decimal_entry: ld hl,buffer 0157 cd 77 00 call get_line ;returns with DE pointing to terminating zero 015a 21 08 db ld hl,buffer 015d cd 6a 01 call decimal_string_to_word 0160 d0 ret nc ;no error, return with word in hl 0161 21 67 04 ld hl,decimal_error_msg ;error, try again 0164 cd 43 00 call write_string 0167 c3 54 01 jp decimal_entry 016a ; 016a ;Subroutine to convert a decimal string to a word value 016a ;Call with address of string in HL, pointer to end of string in DE 016a ;Carry flag set if error (non-decimal char) 016a ;Carry flag clear, word value in HL if no error. 016a 42 decimal_string_to_word: ld b,d 016b 4b ld c,e ;use BC as string pointer 016c 22 00 db ld (current_location),hl ;store addr. of start of buffer in RAM word variable 016f 21 00 00 ld hl,000h ;starting value zero 0172 22 06 db ld (current_value),hl 0175 21 ba 01 ld hl,decimal_place_value ;pointer to values 0178 22 04 db ld (value_pointer),hl 017b 0b decimal_next_char: dec bc ;next char in string (moving right to left) 017c 2a 00 db ld hl,(current_location) ;check if at end of decimal string 017f 37 scf ;get ready to subtract de from buffer addr. 0180 3f ccf ;set carry to zero (clear) 0181 ed 42 sbc hl,bc ;keep going if bc > or = hl (buffer address) 0183 da 8f 01 jp c,decimal_continue ;borrow means bc > hl 0186 ca 8f 01 jp z,decimal_continue ;z means bc = hl 0189 2a 06 db ld hl,(current_value) ;return if de < buffer address (no borrow) 018c 37 scf ;get value back from RAM variable 018d 3f ccf 018e c9 ret ;return with carry clear, value in hl 018f 0a decimal_continue: ld a,(bc) ;next char in string (right to left) 0190 d6 30 sub 030h ;ASCII value of zero char 0192 fa b5 01 jp m,decimal_error ;error if char value less than 030h 0195 fe 0a cp 00ah ;error if byte value > or = 10 decimal 0197 f2 b5 01 jp p,decimal_error ;a reg now has value of decimal numeral 019a 2a 04 db ld hl,(value_pointer) ;get value to add an put in de 019d 5e ld e,(hl) ;little-endian (low byte in low memory) 019e 23 inc hl 019f 56 ld d,(hl) 01a0 23 inc hl ;hl now points to next value 01a1 22 04 db ld (value_pointer),hl 01a4 2a 06 db ld hl,(current_value) ;get back current value 01a7 3d decimal_add: dec a ;add loop to increase total value 01a8 fa af 01 jp m,decimal_add_done ;end of multiplication 01ab 19 add hl,de 01ac c3 a7 01 jp decimal_add 01af 22 06 db decimal_add_done: ld (current_value),hl 01b2 c3 7b 01 jp decimal_next_char 01b5 37 decimal_error: scf 01b6 c9 ret 01b7 c3 a7 01 jp decimal_add 01ba 01 00 0a 00 64 00 e8 03 10 27 decimal_place_value: defw 1,10,100,1000,10000 01c4 ; 01c4 ;Memory dump 01c4 ;Displays a 256-byte block of memory in 16-byte rows. 01c4 ;Called with address of start of block in HL 01c4 22 00 db memory_dump: ld (current_location),hl ;store address of block to be displayed 01c7 3e 00 ld a,000h 01c9 32 03 db ld (byte_count),a ;initialize byte count 01cc 32 02 db ld (line_count),a ;initialize line count 01cf c3 04 02 jp dump_new_line 01d2 2a 00 db dump_next_byte: ld hl,(current_location) ;get byte address from storage, 01d5 7e ld a,(hl) ;get byte to be converted to string 01d6 23 inc hl ;increment address and 01d7 22 00 db ld (current_location),hl ;store back 01da 21 08 db ld hl,buffer ;location to store string 01dd cd b3 00 call byte_to_hex_string ;convert 01e0 21 08 db ld hl,buffer ;display string 01e3 cd 43 00 call write_string 01e6 3a 03 db ld a,(byte_count) ;next byte 01e9 3c inc a 01ea ca 34 02 jp z,dump_done ;stop when 256 bytes displayed 01ed 32 03 db ld (byte_count),a ;not finished yet, store 01f0 3a 02 db ld a,(line_count) ;end of line (16 characters)? 01f3 fe 0f cp 00fh ;yes, start new line 01f5 ca 04 02 jp z,dump_new_line 01f8 3c inc a ;no, increment line count 01f9 32 02 db ld (line_count),a 01fc 3e 20 ld a,020h ;print space 01fe cd 37 00 call write_char 0201 c3 d2 01 jp dump_next_byte ;continue 0204 3e 00 dump_new_line: ld a,000h ;reset line count to zero 0206 32 02 db ld (line_count),a 0209 cd ba 02 call write_newline 020c 2a 00 db ld hl,(current_location) ;location of start of line 020f 7c ld a,h ;high byte of address 0210 21 08 db ld hl, buffer 0213 cd b3 00 call byte_to_hex_string ;convert 0216 21 08 db ld hl,buffer 0219 cd 43 00 call write_string ;write high byte 021c 2a 00 db ld hl,(current_location) 021f 7d ld a,l ;low byte of address 0220 21 08 db ld hl, buffer 0223 cd b3 00 call byte_to_hex_string ;convert 0226 21 08 db ld hl,buffer 0229 cd 43 00 call write_string ;write low byte 022c 3e 20 ld a,020h ;space 022e cd 37 00 call write_char 0231 c3 d2 01 jp dump_next_byte ;now write 16 bytes 0234 3e 00 dump_done: ld a,000h 0236 21 08 db ld hl,buffer 0239 77 ld (hl),a ;clear buffer of last string 023a cd ba 02 call write_newline 023d c9 ret 023e ; 023e ;Memory load 023e ;Loads RAM memory with bytes entered as hex characters 023e ;Called with address to start loading in HL 023e ;Displays entered data in 16-byte rows. 023e 22 00 db memory_load: ld (current_location),hl 0241 21 1f 04 ld hl,data_entry_msg 0244 cd 43 00 call write_string 0247 c3 97 02 jp load_new_line 024a cd b0 02 load_next_char: call get_char 024d fe 0d cp 00dh ;return? 024f ca ac 02 jp z,load_done ;yes, quit 0252 32 08 db ld (buffer),a 0255 cd b0 02 call get_char 0258 fe 0d cp 00dh ;return? 025a ca ac 02 jp z,load_done ;yes, quit 025d 32 09 db ld (buffer+1),a 0260 21 08 db ld hl,buffer 0263 cd f2 00 call hex_to_byte 0266 da a2 02 jp c,load_data_entry_error ;non-hex character 0269 2a 00 db ld hl,(current_location) ;get byte address from storage, 026c 77 ld (hl),a ;store byte 026d 23 inc hl ;increment address and 026e 22 00 db ld (current_location),hl ;store back 0271 3a 08 db ld a,(buffer) 0274 cd 37 00 call write_char 0277 3a 09 db ld a,(buffer+1) 027a cd 37 00 call write_char 027d 3a 02 db ld a,(line_count) ;end of line (16 characters)? 0280 fe 0f cp 00fh ;yes, start new line 0282 ca 97 02 jp z,load_new_line 0285 3c inc a ;no, increment line count 0286 32 02 db ld (line_count),a 0289 3e 20 ld a,020h ;print space 028b cd 37 00 call write_char 028e c3 4a 02 jp load_next_char ;continue 0291 ;This section is to align the jump to disk_read for the cpm_loader 0291 ;Expects disk_read to be at location 0x0294 0291 00 nop 0292 00 nop 0293 00 nop 0294 c3 c5 02 jp disk_read 0297 3e 00 load_new_line: ld a,000h ;reset line count to zero 0299 32 02 db ld (line_count),a 029c cd ba 02 call write_newline 029f c3 4a 02 jp load_next_char ;continue 02a2 cd ba 02 load_data_entry_error: call write_newline 02a5 21 4c 04 ld hl,data_error_msg 02a8 cd 43 00 call write_string 02ab c9 ret 02ac cd ba 02 load_done: call write_newline 02af c9 ret 02b0 ; 02b0 ;Get one ASCII character from the serial port. 02b0 ;Returns with char in A reg. No error checking. 02b0 db 03 get_char: in a,(3) ;get status 02b2 e6 02 and 002h ;check RxRDY bit 02b4 ca b0 02 jp z,get_char ;not ready, loop 02b7 db 02 in a,(2) ;get char 02b9 c9 ret 02ba ; 02ba ;Subroutine to start a new line 02ba 3e 0d write_newline: ld a,00dh ;ASCII carriage return character 02bc cd 37 00 call write_char 02bf 3e 0a ld a,00ah ;new line (line feed) character 02c1 cd 37 00 call write_char 02c4 c9 ret 02c5 ; 02c5 ;Subroutine to read one disk sector (256 bytes) 02c5 ;Address to place data passed in HL 02c5 ;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B 02c5 ;LBA bits 16 to 23 passed in E 02c5 disk_read: 02c5 db 0f rd_status_loop_1: in a,(0fh) ;check status 02c7 e6 80 and 80h ;check BSY bit 02c9 c2 c5 02 jp nz,rd_status_loop_1 ;loop until not busy 02cc db 0f rd_status_loop_2: in a,(0fh) ;check status 02ce e6 40 and 40h ;check DRDY bit 02d0 ca cc 02 jp z,rd_status_loop_2 ;loop until ready 02d3 3e 01 ld a,01h ;number of sectors = 1 02d5 d3 0a out (0ah),a ;sector count register 02d7 79 ld a,c 02d8 d3 0b out (0bh),a ;lba bits 0 - 7 02da 78 ld a,b 02db d3 0c out (0ch),a ;lba bits 8 - 15 02dd 7b ld a,e 02de d3 0d out (0dh),a ;lba bits 16 - 23 02e0 3e e0 ld a,11100000b ;LBA mode, select drive 0 02e2 d3 0e out (0eh),a ;drive/head register 02e4 3e 20 ld a,20h ;Read sector command 02e6 d3 0f out (0fh),a 02e8 db 0f rd_wait_for_DRQ_set: in a,(0fh) ;read status 02ea e6 08 and 08h ;DRQ bit 02ec ca e8 02 jp z,rd_wait_for_DRQ_set ;loop until bit set 02ef db 0f rd_wait_for_BSY_clear: in a,(0fh) 02f1 e6 80 and 80h 02f3 c2 ef 02 jp nz,rd_wait_for_BSY_clear 02f6 db 0f in a,(0fh) ;clear INTRQ 02f8 db 08 read_loop: in a,(08h) ;get data 02fa 77 ld (hl),a 02fb 23 inc hl 02fc db 0f in a,(0fh) ;check status 02fe e6 08 and 08h ;DRQ bit 0300 c2 f8 02 jp nz,read_loop ;loop until cleared 0303 c9 ret 0304 ; 0304 ;Subroutine to write one disk sector (256 bytes) 0304 ;Address of data to write to disk passed in HL 0304 ;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B 0304 ;LBA bits 16 to 23 passed in E 0304 disk_write: 0304 db 0f wr_status_loop_1: in a,(0fh) ;check status 0306 e6 80 and 80h ;check BSY bit 0308 c2 04 03 jp nz,wr_status_loop_1 ;loop until not busy 030b db 0f wr_status_loop_2: in a,(0fh) ;check status 030d e6 40 and 40h ;check DRDY bit 030f ca 0b 03 jp z,wr_status_loop_2 ;loop until ready 0312 3e 01 ld a,01h ;number of sectors = 1 0314 d3 0a out (0ah),a ;sector count register 0316 79 ld a,c 0317 d3 0b out (0bh),a ;lba bits 0 - 7 0319 78 ld a,b 031a d3 0c out (0ch),a ;lba bits 8 - 15 031c 7b ld a,e 031d d3 0d out (0dh),a ;lba bits 16 - 23 031f 3e e0 ld a,11100000b ;LBA mode, select drive 0 0321 d3 0e out (0eh),a ;drive/head register 0323 3e 30 ld a,30h ;Write sector command 0325 d3 0f out (0fh),a 0327 db 0f wr_wait_for_DRQ_set: in a,(0fh) ;read status 0329 e6 08 and 08h ;DRQ bit 032b ca 27 03 jp z,wr_wait_for_DRQ_set ;loop until bit set 032e 7e write_loop: ld a,(hl) 032f d3 08 out (08h),a ;write data 0331 23 inc hl 0332 db 0f in a,(0fh) ;read status 0334 e6 08 and 08h ;check DRQ bit 0336 c2 2e 03 jp nz,write_loop ;write until bit cleared 0339 db 0f wr_wait_for_BSY_clear: in a,(0fh) 033b e6 80 and 80h 033d c2 39 03 jp nz,wr_wait_for_BSY_clear 0340 db 0f in a,(0fh) ;clear INTRQ 0342 c9 ret 0343 ; 0343 ;Strings used in subroutines 0343 .. 00 length_entry_string: defm "Enter length of file to load (decimal): ",0 036c .. 00 dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0 0393 .. 00 LBA_entry_string: defm "Enter LBA (decimal, 0 to 65535): ",0 03b5 08 1b .. 00 erase_char_string: defm 008h,01bh,"[K",000h ;ANSI sequence for backspace, erase to end of line. 03ba .. 00 address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0 03f3 .. 00 address_error_msg: defm "\r\nError: invalid hex character, try again: ",0 041f .. 00 data_entry_msg: defm "Enter hex bytes, hit return when finished.\r\n",0 044c .. 00 data_error_msg: defm "Error: invalid hex byte.\r\n",0 0467 .. 00 decimal_error_msg: defm "\r\nError: invalid decimal number, try again: ",0 0494 ; 0494 ;Simple monitor program for CPUville Z80 computer with serial interface. 0494 31 ff db monitor_cold_start: ld sp,ROM_monitor_stack 0497 cd 2e 00 call initialize_port 049a 21 0d 06 ld hl,monitor_message 049d cd 43 00 call write_string 04a0 cd ba 02 monitor_warm_start: call write_newline ;routine program return here to avoid re-initialization of port 04a3 3e 3e ld a,03eh ;cursor symbol 04a5 cd 37 00 call write_char 04a8 21 08 db ld hl,buffer 04ab cd 77 00 call get_line ;get monitor input string (command) 04ae cd ba 02 call write_newline 04b1 cd b5 04 call parse ;interprets command, returns with address to jump to in HL 04b4 e9 jp (hl) 04b5 ; 04b5 ;Parses an input line stored in buffer for available commands as described in parse table. 04b5 ;Returns with address of jump to action for the command in HL 04b5 01 b9 07 parse: ld bc,parse_table ;bc is pointer to parse_table 04b8 0a parse_start: ld a,(bc) ;get pointer to match string from parse table 04b9 5f ld e,a 04ba 03 inc bc 04bb 0a ld a,(bc) 04bc 57 ld d,a ;de will is pointer to strings for matching 04bd 1a ld a,(de) ;get first char from match string 04be f6 00 or 000h ;zero? 04c0 ca db 04 jp z,parser_exit ;yes, exit no_match 04c3 21 08 db ld hl,buffer ;no, parse input string 04c6 be match_loop: cp (hl) ;compare buffer char with match string char 04c7 c2 d5 04 jp nz,no_match ;no match, go to next match string 04ca f6 00 or 000h ;end of strings (zero)? 04cc ca db 04 jp z,parser_exit ;yes, matching string found 04cf 13 inc de ;match so far, point to next char in match string 04d0 1a ld a,(de) ;get next character from match string 04d1 23 inc hl ;and point to next char in input string 04d2 c3 c6 04 jp match_loop ;check for match 04d5 03 no_match: inc bc ;skip over jump target to 04d6 03 inc bc 04d7 03 inc bc ;get address of next matching string 04d8 c3 b8 04 jp parse_start 04db 03 parser_exit: inc bc ;skip to address of jump for match 04dc 0a ld a,(bc) 04dd 6f ld l,a 04de 03 inc bc 04df 0a ld a,(bc) 04e0 67 ld h,a ;returns with jump address in hl 04e1 c9 ret 04e2 ; 04e2 ;Actions to be taken on match 04e2 ; 04e2 ;Memory dump program 04e2 ;Input 4-digit hexadecimal address 04e2 ;Calls memory_dump subroutine 04e2 21 2c 06 dump_jump: ld hl,dump_message ;Display greeting 04e5 cd 43 00 call write_string 04e8 21 ba 03 ld hl,address_entry_msg ;get ready to get address 04eb cd 43 00 call write_string 04ee cd 29 01 call address_entry ;returns with address in HL 04f1 cd ba 02 call write_newline 04f4 cd c4 01 call memory_dump 04f7 c3 a0 04 jp monitor_warm_start 04fa ; 04fa ;Hex loader, displays formatted input 04fa 21 4c 06 load_jump: ld hl,load_message ;Display greeting 04fd cd 43 00 call write_string ;get address to load 0500 21 ba 03 ld hl,address_entry_msg ;get ready to get address 0503 cd 43 00 call write_string 0506 cd 29 01 call address_entry 0509 cd ba 02 call write_newline 050c cd 3e 02 call memory_load 050f c3 a0 04 jp monitor_warm_start 0512 ; 0512 ;Jump and run do the same thing: get an address and jump to it. 0512 21 69 06 run_jump: ld hl,run_message ;Display greeting 0515 cd 43 00 call write_string 0518 21 ba 03 ld hl,address_entry_msg ;get ready to get address 051b cd 43 00 call write_string 051e cd 29 01 call address_entry 0521 e9 jp (hl) 0522 ; 0522 ;Help and ? do the same thing, display the available commands 0522 21 20 06 help_jump: ld hl,help_message 0525 cd 43 00 call write_string 0528 01 b9 07 ld bc,parse_table ;table with pointers to command strings 052b 0a help_loop: ld a,(bc) ;displays the strings for matching commands, 052c 6f ld l,a ;getting the string addresses from the 052d 03 inc bc ;parse table 052e 0a ld a,(bc) ;pass address of string to hl through a reg 052f 67 ld h,a 0530 7e ld a,(hl) ;hl now points to start of match string 0531 f6 00 or 000h ;exit if no_match string 0533 ca 46 05 jp z,help_done 0536 c5 push bc ;write_char uses b register 0537 3e 20 ld a,020h ;space char 0539 cd 37 00 call write_char 053c c1 pop bc 053d cd 43 00 call write_string ;writes match string 0540 03 inc bc ;pass over jump address in table 0541 03 inc bc 0542 03 inc bc 0543 c3 2b 05 jp help_loop 0546 c3 a0 04 help_done: jp monitor_warm_start 0549 ; 0549 ;Binary file load. Need both address to load and length of file 0549 21 90 06 bload_jump: ld hl,bload_message 054c cd 43 00 call write_string 054f 21 ba 03 ld hl,address_entry_msg 0552 cd 43 00 call write_string 0555 cd 29 01 call address_entry 0558 cd ba 02 call write_newline 055b e5 push hl 055c 21 43 03 ld hl,length_entry_string 055f cd 43 00 call write_string 0562 cd 54 01 call decimal_entry 0565 44 ld b,h 0566 4d ld c,l 0567 21 b3 06 ld hl,bload_ready_message 056a cd 43 00 call write_string 056d e1 pop hl 056e cd 53 00 call bload 0571 c3 a0 04 jp monitor_warm_start 0574 ; 0574 ;Binary memory dump. Need address of start of dump and no. bytes 0574 21 d7 06 bdump_jump: ld hl,bdump_message 0577 cd 43 00 call write_string 057a 21 ba 03 ld hl,address_entry_msg 057d cd 43 00 call write_string 0580 cd 29 01 call address_entry 0583 cd ba 02 call write_newline 0586 e5 push hl 0587 21 6c 03 ld hl,dump_entry_string 058a cd 43 00 call write_string 058d cd 54 01 call decimal_entry 0590 44 ld b,h 0591 4d ld c,l 0592 21 07 07 ld hl,bdump_ready_message 0595 cd 43 00 call write_string 0598 cd b0 02 call get_char 059b e1 pop hl 059c cd 65 00 call bdump 059f c3 a0 04 jp monitor_warm_start 05a2 ;Disk read. Need memory address to place data, LBA of sector to read 05a2 21 2e 07 diskrd_jump: ld hl,diskrd_message 05a5 cd 43 00 call write_string 05a8 21 ba 03 ld hl,address_entry_msg 05ab cd 43 00 call write_string 05ae cd 29 01 call address_entry 05b1 cd ba 02 call write_newline 05b4 e5 push hl 05b5 21 93 03 ld hl,LBA_entry_string 05b8 cd 43 00 call write_string 05bb cd 54 01 call decimal_entry 05be 44 ld b,h 05bf 4d ld c,l 05c0 1e 00 ld e,00h 05c2 e1 pop hl 05c3 cd c5 02 call disk_read 05c6 c3 a0 04 jp monitor_warm_start 05c9 21 56 07 diskwr_jump: ld hl,diskwr_message 05cc cd 43 00 call write_string 05cf 21 ba 03 ld hl,address_entry_msg 05d2 cd 43 00 call write_string 05d5 cd 29 01 call address_entry 05d8 cd ba 02 call write_newline 05db e5 push hl 05dc 21 93 03 ld hl,LBA_entry_string 05df cd 43 00 call write_string 05e2 cd 54 01 call decimal_entry 05e5 44 ld b,h 05e6 4d ld c,l 05e7 1e 00 ld e,00h 05e9 e1 pop hl 05ea cd 04 03 call disk_write 05ed c3 a0 04 jp monitor_warm_start 05f0 21 00 08 cpm_jump: ld hl,0800h 05f3 01 00 00 ld bc,0000h 05f6 1e 00 ld e,00h 05f8 cd c5 02 call disk_read 05fb c3 00 08 jp 0800h 05fe ;Prints message for no match to entered command 05fe 21 1d 06 no_match_jump: ld hl,no_match_message 0601 cd 43 00 call write_string 0604 21 08 db ld hl, buffer 0607 cd 43 00 call write_string 060a c3 a0 04 jp monitor_warm_start 060d ; 060d ;Monitor data structures: 060d ; 060d .. 00 monitor_message: defm "\r\nROM ver. 15\r\n",0 061d .. 00 no_match_message: defm "? ",0 0620 .. 00 help_message: defm "Commands:\r\n",0 062c .. 00 dump_message: defm "Displays 256 bytes of memory.\r\n",0 064c .. 00 load_message: defm "Enter hex bytes in memory.\r\n",0 0669 .. 00 run_message: defm "Will run program at address entered.\r\n",0 0690 .. 00 bload_message: defm "Loads a binary file into memory.\r\n",0 06b3 .. 00 bload_ready_message: defm "\n\rReady to receive, start transfer.",0 06d7 .. 00 bdump_message: defm "Dumps binary data from memory to serial port.\r\n",0 0707 .. 00 bdump_ready_message: defm "\n\rReady to send, hit any key to start.",0 072e .. 00 diskrd_message: defm "Reads one sector from disk to memory.\r\n",0 0756 .. 00 diskwr_message: defm "Writes one sector from memory to disk.\r\n",0 077f ;Strings for matching: 077f .. 00 dump_string: defm "dump",0 0784 .. 00 load_string: defm "load",0 0789 .. 00 jump_string: defm "jump",0 078e .. 00 run_string: defm "run",0 0792 .. 00 question_string: defm "?",0 0794 .. 00 help_string: defm "help",0 0799 .. 00 bload_string: defm "bload",0 079f .. 00 bdump_string: defm "bdump",0 07a5 .. 00 diskrd_string: defm "diskrd",0 07ac .. 00 diskwr_string: defm "diskwr",0 07b3 .. 00 cpm_string: defm "cpm",0 07b7 00 00 no_match_string: defm 0,0 07b9 ;Table for matching strings to jumps 07b9 7f 07 e2 04 84 07 fa 04 parse_table: defw dump_string,dump_jump,load_string,load_jump 07c1 89 07 12 05 8e 07 12 05 defw jump_string,run_jump,run_string,run_jump 07c9 92 07 22 05 94 07 22 05 defw question_string,help_jump,help_string,help_jump 07d1 99 07 49 05 9f 07 74 05 defw bload_string,bload_jump,bdump_string,bdump_jump 07d9 a5 07 a2 05 ac 07 c9 05 defw diskrd_string,diskrd_jump,diskwr_string,diskwr_jump 07e1 b3 07 f0 05 defw cpm_string,cpm_jump 07e5 b7 07 fe 05 defw no_match_string,no_match_jump 07e9 # End of file 2K_ROM_15.asm 07e9