# File 2K_ROM_8.asm
0000			;ROM monitor for a system with serial interface and IDE disk and memory expansion board. 
0000			;The disk extension board has 64K RAM -- computer board memory decoder disabled (J2 off). 
0000			;The disk extension board uses ports 2 and 3 for the serial interface, and 8 to 15 for the disk 
0000			;Therefore the computer board I/O decoder is also disabled (J1 off) 
0000			;Output to port 0 will cause memory configuration flip-flop to activate 2K ROM 0000-07FF with 62K RAM 0800-FFFF 
0000			;Output to port 1 will cause memory configuration flip-flop to activate all RAM 0000-FFFF 
0000			; 
0000						org	00000h 
0000 c3 63 04					jp 	monitor_cold_start			 
0003			; 
0003			;The following code is for a system with a serial port. 
0003			;Assumes the UART data port address is 02h and control/status address is 03h 
0003			; 
0003			;The subroutines for the serial port use these variables in RAM: 
0003			current_location:	equ	0xdb00		;word variable in RAM 
0003			line_count:		equ	0xdb02		;byte variable in RAM 
0003			byte_count:		equ	0xdb03		;byte variable in RAM 
0003			value_pointer:		equ	0xdb04		;word variable in RAM 
0003			current_value:		equ	0xdb06		;word variable in RAM 
0003			buffer:			equ	0xdb08		;buffer in RAM -- up to stack area 
0003			;Need to have stack in upper RAM, but not in area of CP/M or RAM monitor. 
0003			ROM_monitor_stack:	equ	0xdbff		;upper TPA in RAM, below RAM monitor 
0003			; 
0003			;Subroutine to initialize serial port UART 
0003			;Needs to be called only once after computer comes out of reset. 
0003			;If called while port is active will cause port to fail. 
0003			;16x = 9600 baud 
0003 3e 4e		initialize_port:	ld 	a,04eh			;1 stop bit, no parity, 8-bit char, 16x baud 
0005 d3 03					out 	(3),a			;write to control port 
0007 3e 37					ld 	a,037h			;enable receive and transmit 
0009 d3 03					out 	(3),a			;write to control port 
000b c9						ret 
000c			; 
000c			;Puts a single char (byte value) on serial output 
000c			;Call with char to send in A register. Uses B register 
000c 47			write_char:		ld	b,a			;store char 
000d db 03		write_char_loop:	in	a,(3)			;check if OK to send 
000f e6 01					and	001h			;check TxRDY bit 
0011 ca 0d 00					jp 	z,write_char_loop	;loop if not set 
0014 78						ld 	a,b			;get char back 
0015 d3 02					out 	(2),a			;send to output 
0017 c9						ret				;returns with char in a 
0018			; 
0018			;Subroutine to write a zero-terminated string to serial output 
0018			;Pass address of string in HL register 
0018			;No error checking 
0018 db 03		write_string:		in 	a,(3)			;read status 
001a e6 01					and 	001h			;check TxRDY bit 
001c ca 18 00					jp 	z,write_string		;loop if not set 
001f 7e						ld 	a,(hl)			;get char from string 
0020 a7						and 	a			;check if 0 
0021 c8						ret 	z			;yes, finished 
0022 d3 02					out 	(2),a			;no, write char to output 
0024 23						inc 	hl			;next char in string 
0025 c3 18 00					jp 	write_string		;start over 
0028			; 
0028			;Binary loader. Receive a binary file, place in memory. 
0028			;Address of load passed in HL, length of load (= file length) in BC 
0028 db 03		bload:			in 	a,(3)			;get status 
002a e6 02					and 	002h			;check RxRDY bit 
002c ca 28 00					jp 	z,bload			;not ready, loop 
002f db 02					in	a,(2) 
0031 77						ld	(hl),a 
0032 23						inc	hl 
0033 0b						dec	bc			;byte counter 
0034 78						ld	a,b			;need to test BC this way because 
0035 b1						or	c			;dec rp instruction does not change flags 
0036 c2 28 00					jp	nz,bload 
0039 c9						ret 
003a			; 
003a			;Binary dump to port. Send a stream of binary data from memory to serial output 
003a			;Address of dump passed in HL, length of dump in BC 
003a db 03		bdump:			in 	a,(3)			;get status 
003c e6 01					and 	001h			;check TxRDY bit 
003e ca 3a 00					jp 	z,bdump			;not ready, loop 
0041 7e						ld	a,(hl) 
0042 d3 02					out	(2),a 
0044 23						inc	hl 
0045 0b						dec	bc 
0046 78						ld	a,b			;need to test this way because 
0047 b1						or	c			;dec rp instruction does not change flags 
0048 c2 3a 00					jp	nz,bdump 
004b c9						ret 
004c			; 
004c			;Subroutine to get a string from serial input, place in buffer. 
004c			;Buffer address passed in HL reg. 
004c			;Uses A,BC,DE,HL registers (including calls to other subroutines). 
004c			;Line entry ends by hitting return key. Return char not included in string (replaced by zero). 
004c			;Backspace editing OK. No error checking. 
004c			; 
004c 0e 00		get_line:		ld	c,000h			;line position 
004e 7c						ld	a,h			;put original buffer address in de 
004f 57						ld	d,a			;after this don't need to preserve hl 
0050 7d						ld	a,l			;subroutines called don't use de 
0051 5f						ld	e,a 
0052 db 03		get_line_next_char:	in 	a,(3)			;get status 
0054 e6 02					and 	002h			;check RxRDY bit 
0056 ca 52 00					jp 	z,get_line_next_char	;not ready, loop 
0059 db 02					in 	a,(2)			;get char 
005b fe 0d					cp	00dh			;check if return 
005d c8						ret	z			;yes, normal exit 
005e fe 7f					cp	07fh			;check if backspace (VT102 keys) 
0060 ca 74 00					jp	z,get_line_backspace	;yes, jump to backspace routine 
0063 fe 08					cp	008h			;check if backspace (ANSI keys) 
0065 ca 74 00					jp	z,get_line_backspace	;yes, jump to backspace 
0068 cd 0c 00					call	write_char		;put char on screen 
006b 12						ld	(de),a			;store char in buffer 
006c 13						inc	de			;point to next space in buffer 
006d 0c						inc	c			;inc counter 
006e 3e 00					ld	a,000h 
0070 12						ld	(de),a			;leaves a zero-terminated string in buffer 
0071 c3 52 00					jp	get_line_next_char 
0074 79			get_line_backspace:	ld	a,c			;check current position in line 
0075 fe 00					cp	000h			;at beginning of line? 
0077 ca 52 00					jp	z,get_line_next_char	;yes, ignore backspace, get next char 
007a 1b						dec	de			;no, erase char from buffer 
007b 0d						dec	c			;back up one 
007c 3e 00					ld	a,000h			;put a zero in buffer where the last char was 
007e 12						ld	(de),a 
007f 21 84 03					ld	hl,erase_char_string	;ANSI sequence to delete one char from line 
0082 cd 18 00					call	write_string		;transmits sequence to backspace and erase char 
0085 c3 52 00					jp	get_line_next_char 
0088			; 
0088			;Creates a two-char hex string from the byte value passed in register A 
0088			;Location to place string passed in HL 
0088			;String is zero-terminated, stored in 3 locations starting at HL 
0088			;Also uses registers b,d, and e 
0088 47			byte_to_hex_string:	ld	b,a			;store original byte 
0089 cb 3f					srl	a			;shift right 4 times, putting 
008b cb 3f					srl	a			;high nybble in low-nybble spot 
008d cb 3f					srl	a			;and zeros in high-nybble spot 
008f cb 3f					srl	a 
0091 16 00					ld	d,000h			;prepare for 16-bit addition 
0093 5f						ld	e,a			;de contains offset 
0094 e5						push	hl			;temporarily store string target address 
0095 21 ee 00					ld	hl,hex_char_table	;use char table to get high-nybble character 
0098 19						add	hl,de			;add offset to start of table 
0099 7e						ld	a,(hl)			;get char 
009a e1						pop	hl			;get string target address 
009b 77						ld	(hl),a			;store first char of string 
009c 23						inc	hl			;point to next string target address 
009d 78						ld	a,b			;get original byte back from reg b 
009e e6 0f					and	00fh			;mask off high-nybble 
00a0 5f						ld	e,a			;d still has 000h, now de has offset 
00a1 e5						push	hl			;temp store string target address 
00a2 21 ee 00					ld	hl,hex_char_table	;start of table 
00a5 19						add	hl,de			;add offset 
00a6 7e						ld	a,(hl)			;get char 
00a7 e1						pop	hl			;get string target address 
00a8 77						ld	(hl),a			;store second char of string 
00a9 23						inc	hl			;point to third location 
00aa 3e 00					ld	a,000h			;zero to terminate string 
00ac 77						ld	(hl),a			;store the zero 
00ad c9						ret				;done 
00ae			; 
00ae			;Converts a single ASCII hex char to a nybble value 
00ae			;Pass char in reg A. Letter numerals must be upper case. 
00ae			;Return nybble value in low-order reg A with zeros in high-order nybble if no error. 
00ae			;Return 0ffh in reg A if error (char not a valid hex numeral). 
00ae			;Also uses b, c, and hl registers. 
00ae 21 ee 00		hex_char_to_nybble:	ld	hl,hex_char_table 
00b1 06 0f					ld	b,00fh			;no. of valid characters in table - 1. 
00b3 0e 00					ld	c,000h			;will be nybble value 
00b5 be			hex_to_nybble_loop:	cp	(hl)			;character match here? 
00b6 ca c2 00					jp	z,hex_to_nybble_ok	;match found, exit 
00b9 05						dec	b			;no match, check if at end of table 
00ba fa c4 00					jp	m,hex_to_nybble_err	;table limit exceded, exit with error 
00bd 0c						inc	c			;still inside table, continue search 
00be 23						inc	hl 
00bf c3 b5 00					jp	hex_to_nybble_loop 
00c2 79			hex_to_nybble_ok:	ld	a,c			;put nybble value in a 
00c3 c9						ret 
00c4 3e ff		hex_to_nybble_err:	ld	a,0ffh			;error value 
00c6 c9						ret 
00c7			; 
00c7			;Converts a hex character pair to a byte value 
00c7			;Called with location of high-order char in HL 
00c7			;If no error carry flag clear, returns with byte value in register A, and 
00c7			;HL pointing to next mem location after char pair. 
00c7			;If error (non-hex char) carry flag set, HL pointing to invalid char 
00c7 7e			hex_to_byte:		ld	a,(hl)			;location of character pair 
00c8 e5						push	hl			;store hl (hex_char_to_nybble uses it) 
00c9 cd ae 00					call	hex_char_to_nybble 
00cc e1						pop	hl			;returns with nybble value in a reg, or 0ffh if error 
00cd fe ff					cp	0ffh			;non-hex character? 
00cf ca ec 00					jp	z,hex_to_byte_err	;yes, exit with error 
00d2 cb 27					sla	a			;no, move low order nybble to high side 
00d4 cb 27					sla	a 
00d6 cb 27					sla	a 
00d8 cb 27					sla	a 
00da 57						ld	d,a			;store high-nybble 
00db 23						inc	hl			;get next character of the pair 
00dc 7e						ld	a,(hl) 
00dd e5						push	hl			;store hl 
00de cd ae 00					call	hex_char_to_nybble 
00e1 e1						pop	hl 
00e2 fe ff					cp	0ffh			;non-hex character? 
00e4 ca ec 00					jp	z,hex_to_byte_err	;yes, exit with error 
00e7 b2						or	d			;no, combine with high-nybble 
00e8 23						inc	hl			;point to next memory location after char pair 
00e9 37						scf 
00ea 3f						ccf				;no-error exit (carry = 0) 
00eb c9						ret 
00ec 37			hex_to_byte_err:	scf				;error, carry flag set 
00ed c9						ret 
00ee ..			hex_char_table:		defm	"0123456789ABCDEF"	;ASCII hex table 
00fe			; 
00fe			;Subroutine to get a two-byte address from serial input. 
00fe			;Returns with address value in HL 
00fe			;Uses locations in RAM for buffer and variables 
00fe 21 08 db		address_entry:		ld	hl,buffer		;location for entered string 
0101 cd 4c 00					call	get_line		;returns with address string in buffer 
0104 21 08 db					ld	hl,buffer		;location of stored address entry string 
0107 cd c7 00					call	hex_to_byte		;will get high-order byte first 
010a da 20 01					jp	c, address_entry_error	;if error, jump 
010d 32 01 db					ld	(current_location+1),a	;store high-order byte, little-endian 
0110 21 0a db					ld	hl,buffer+2		;point to low-order hex char pair 
0113 cd c7 00					call	hex_to_byte		;get low-order byte 
0116 da 20 01					jp	c, address_entry_error	;jump if error 
0119 32 00 db					ld	(current_location),a	;store low-order byte in lower memory 
011c 2a 00 db					ld	hl,(current_location)	;put memory address in hl 
011f c9						ret 
0120 21 c2 03		address_entry_error:	ld	hl,address_error_msg 
0123 cd 18 00					call	write_string 
0126 c3 fe 00					jp	address_entry 
0129			; 
0129			;Subroutine to get a decimal string, return a word value 
0129			;Calls decimal_string_to_word subroutine 
0129 21 08 db		decimal_entry:		ld	hl,buffer 
012c cd 4c 00					call	get_line		;returns with DE pointing to terminating zero 
012f 21 08 db					ld	hl,buffer 
0132 cd 3f 01					call	decimal_string_to_word 
0135 d0						ret	nc			;no error, return with word in hl 
0136 21 36 04					ld	hl,decimal_error_msg	;error, try again 
0139 cd 18 00					call	write_string 
013c c3 29 01					jp	decimal_entry 
013f			; 
013f			;Subroutine to convert a decimal string to a word value 
013f			;Call with address of string in HL, pointer to end of string in DE 
013f			;Carry flag set if error (non-decimal char) 
013f			;Carry flag clear, word value in HL if no error. 
013f 42			decimal_string_to_word:	ld	b,d 
0140 4b						ld	c,e			;use BC as string pointer 
0141 22 00 db					ld	(current_location),hl	;store addr. of start of buffer in RAM word variable 
0144 21 00 00					ld	hl,000h			;starting value zero 
0147 22 06 db					ld	(current_value),hl 
014a 21 8f 01					ld	hl,decimal_place_value	;pointer to values 
014d 22 04 db					ld	(value_pointer),hl 
0150 0b			decimal_next_char:	dec	bc			;next char in string (moving right to left) 
0151 2a 00 db					ld	hl,(current_location)	;check if at end of decimal string 
0154 37						scf				;get ready to subtract de from buffer addr. 
0155 3f						ccf				;set carry to zero (clear) 
0156 ed 42					sbc	hl,bc			;keep going if bc > or = hl (buffer address) 
0158 da 64 01					jp	c,decimal_continue	;borrow means bc > hl 
015b ca 64 01					jp	z,decimal_continue	;z means bc = hl 
015e 2a 06 db					ld	hl,(current_value)	;return if de < buffer address (no borrow) 
0161 37						scf				;get value back from RAM variable 
0162 3f						ccf 
0163 c9						ret				;return with carry clear, value in hl 
0164 0a			decimal_continue:	ld	a,(bc)			;next char in string (right to left) 
0165 d6 30					sub	030h			;ASCII value of zero char 
0167 fa 8a 01					jp	m,decimal_error		;error if char value less than 030h 
016a fe 0a					cp	00ah			;error if byte value > or = 10 decimal 
016c f2 8a 01					jp	p,decimal_error		;a reg now has value of decimal numeral 
016f 2a 04 db					ld	hl,(value_pointer)	;get value to add an put in de 
0172 5e						ld	e,(hl)			;little-endian (low byte in low memory) 
0173 23						inc	hl 
0174 56						ld	d,(hl) 
0175 23						inc	hl			;hl now points to next value 
0176 22 04 db					ld	(value_pointer),hl 
0179 2a 06 db					ld	hl,(current_value)	;get back current value 
017c 3d			decimal_add:		dec	a			;add loop to increase total value 
017d fa 84 01					jp	m,decimal_add_done	;end of multiplication 
0180 19						add	hl,de 
0181 c3 7c 01					jp	decimal_add 
0184 22 06 db		decimal_add_done:	ld	(current_value),hl 
0187 c3 50 01					jp	decimal_next_char 
018a 37			decimal_error:		scf 
018b c9						ret 
018c c3 7c 01					jp	decimal_add 
018f 01 00 0a 00 64 00 e8 03 10 27	decimal_place_value:	defw	1,10,100,1000,10000 
0199			; 
0199			;Memory dump 
0199			;Displays a 256-byte block of memory in 16-byte rows. 
0199			;Called with address of start of block in HL 
0199 22 00 db		memory_dump:		ld	(current_location),hl	;store address of block to be displayed 
019c 3e 00					ld	a,000h 
019e 32 03 db					ld	(byte_count),a		;initialize byte count 
01a1 32 02 db					ld	(line_count),a		;initialize line count 
01a4 c3 d9 01					jp	dump_new_line 
01a7 2a 00 db		dump_next_byte:		ld	hl,(current_location)	;get byte address from storage, 
01aa 7e						ld	a,(hl)			;get byte to be converted to string 
01ab 23						inc	hl			;increment address and 
01ac 22 00 db					ld	(current_location),hl	;store back 
01af 21 08 db					ld	hl,buffer		;location to store string 
01b2 cd 88 00					call	byte_to_hex_string	;convert 
01b5 21 08 db					ld	hl,buffer		;display string 
01b8 cd 18 00					call	write_string 
01bb 3a 03 db					ld	a,(byte_count)		;next byte 
01be 3c						inc	a 
01bf ca 09 02					jp	z,dump_done		;stop when 256 bytes displayed 
01c2 32 03 db					ld	(byte_count),a		;not finished yet, store 
01c5 3a 02 db					ld	a,(line_count)		;end of line (16 characters)? 
01c8 fe 0f					cp	00fh			;yes, start new line 
01ca ca d9 01					jp	z,dump_new_line 
01cd 3c						inc	a			;no, increment line count 
01ce 32 02 db					ld	(line_count),a 
01d1 3e 20					ld	a,020h			;print space 
01d3 cd 0c 00					call	write_char 
01d6 c3 a7 01					jp	dump_next_byte		;continue 
01d9 3e 00		dump_new_line:		ld	a,000h			;reset line count to zero 
01db 32 02 db					ld	(line_count),a			 
01de cd 89 02					call	write_newline 
01e1 2a 00 db					ld	hl,(current_location)	;location of start of line 
01e4 7c						ld	a,h			;high byte of address 
01e5 21 08 db					ld	hl, buffer 
01e8 cd 88 00					call	byte_to_hex_string	;convert 
01eb 21 08 db					ld	hl,buffer 
01ee cd 18 00					call	write_string		;write high byte 
01f1 2a 00 db					ld	hl,(current_location) 
01f4 7d						ld	a,l			;low byte of address 
01f5 21 08 db					ld	hl, buffer 
01f8 cd 88 00					call	byte_to_hex_string	;convert 
01fb 21 08 db					ld	hl,buffer 
01fe cd 18 00					call	write_string		;write low byte 
0201 3e 20					ld	a,020h			;space 
0203 cd 0c 00					call	write_char 
0206 c3 a7 01					jp	dump_next_byte		;now write 16 bytes 
0209 3e 00		dump_done:		ld	a,000h 
020b 21 08 db					ld	hl,buffer 
020e 77						ld	(hl),a			;clear buffer of last string 
020f cd 89 02					call	write_newline 
0212 c9						ret 
0213			; 
0213			;Memory load 
0213			;Loads RAM memory with bytes entered as hex characters 
0213			;Called with address to start loading in HL 
0213			;Displays entered data in 16-byte rows. 
0213 22 00 db		memory_load:		ld	(current_location),hl 
0216 21 ee 03					ld	hl,data_entry_msg 
0219 cd 18 00					call	write_string 
021c c3 66 02					jp	load_new_line 
021f cd 7f 02		load_next_char:		call	get_char 
0222 fe 0d					cp	00dh			;return? 
0224 ca 7b 02					jp	z,load_done		;yes, quit 
0227 32 08 db					ld	(buffer),a 
022a cd 7f 02					call	get_char 
022d fe 0d					cp	00dh			;return? 
022f ca 7b 02					jp	z,load_done		;yes, quit 
0232 32 09 db					ld	(buffer+1),a 
0235 21 08 db					ld	hl,buffer 
0238 cd c7 00					call	hex_to_byte 
023b da 71 02					jp	c,load_data_entry_error	;non-hex character 
023e 2a 00 db					ld	hl,(current_location)	;get byte address from storage, 
0241 77						ld	(hl),a			;store byte 
0242 23						inc	hl			;increment address and 
0243 22 00 db					ld	(current_location),hl	;store back 
0246 3a 08 db					ld	a,(buffer) 
0249 cd 0c 00					call	write_char 
024c 3a 09 db					ld	a,(buffer+1) 
024f cd 0c 00					call	write_char 
0252 3a 02 db					ld	a,(line_count)		;end of line (16 characters)? 
0255 fe 0f					cp	00fh			;yes, start new line 
0257 ca 66 02					jp	z,load_new_line 
025a 3c						inc	a			;no, increment line count 
025b 32 02 db					ld	(line_count),a 
025e 3e 20					ld	a,020h			;print space 
0260 cd 0c 00					call	write_char 
0263 c3 1f 02					jp	load_next_char		;continue 
0266 3e 00		load_new_line:		ld	a,000h			;reset line count to zero 
0268 32 02 db					ld	(line_count),a 
026b cd 89 02					call	write_newline 
026e c3 1f 02					jp	load_next_char		;continue 
0271 cd 89 02		load_data_entry_error:	call	write_newline 
0274 21 1b 04					ld	hl,data_error_msg 
0277 cd 18 00					call	write_string 
027a c9						ret 
027b cd 89 02		load_done:		call	write_newline 
027e c9						ret 
027f			; 
027f			;Get one ASCII character from the serial port. 
027f			;Returns with char in A reg. No error checking. 
027f db 03		get_char:		in 	a,(3)			;get status 
0281 e6 02					and 	002h			;check RxRDY bit 
0283 ca 7f 02					jp 	z,get_char		;not ready, loop 
0286 db 02					in 	a,(2)			;get char 
0288 c9						ret 
0289			; 
0289			;Subroutine to start a new line 
0289 3e 0d		write_newline:		ld	a,00dh			;ASCII carriage return character 
028b cd 0c 00					call	write_char 
028e 3e 0a					ld	a,00ah			;new line (line feed) character 
0290 cd 0c 00					call	write_char 
0293 c9						ret 
0294			; 
0294			;Subroutine to read one disk sector (256 bytes) 
0294			;Address to place data passed in HL 
0294			;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B 
0294			;LBA bits 16 to 23 passed in E 
0294			disk_read: 
0294 db 0f		rd_status_loop_1:	in	a,(0fh)		;check status 
0296 e6 80					and	80h		;check BSY bit 
0298 c2 94 02					jp	nz,rd_status_loop_1	;loop until not busy 
029b db 0f		rd_status_loop_2:	in	a,(0fh)		;check	status 
029d e6 40					and	40h		;check DRDY bit 
029f ca 9b 02					jp	z,rd_status_loop_2	;loop until ready 
02a2 3e 01					ld	a,01h		;number of sectors = 1 
02a4 d3 0a					out	(0ah),a		;sector count register 
02a6 79						ld	a,c 
02a7 d3 0b					out	(0bh),a		;lba bits 0 - 7 
02a9 78						ld	a,b 
02aa d3 0c					out	(0ch),a		;lba bits 8 - 15 
02ac 7b						ld	a,e 
02ad d3 0d					out	(0dh),a		;lba bits 16 - 23 
02af 3e e0					ld	a,11100000b	;LBA mode, select drive 0 
02b1 d3 0e					out	(0eh),a		;drive/head register 
02b3 3e 20					ld	a,20h		;Read sector command 
02b5 d3 0f					out	(0fh),a 
02b7 db 0f		rd_wait_for_DRQ_set:	in	a,(0fh)		;read status 
02b9 e6 08					and	08h		;DRQ bit 
02bb ca b7 02					jp	z,rd_wait_for_DRQ_set	;loop until bit set 
02be db 0f		rd_wait_for_BSY_clear:	in	a,(0fh) 
02c0 e6 80					and	80h 
02c2 c2 be 02					jp	nz,rd_wait_for_BSY_clear 
02c5 db 0f					in	a,(0fh)		;clear INTRQ 
02c7 db 08		read_loop:		in	a,(08h)		;get data 
02c9 77						ld	(hl),a 
02ca 23						inc	hl 
02cb db 0f					in	a,(0fh)		;check status 
02cd e6 08					and	08h		;DRQ bit 
02cf c2 c7 02					jp	nz,read_loop	;loop until cleared 
02d2 c9						ret 
02d3			; 
02d3			;Subroutine to write one disk sector (256 bytes) 
02d3			;Address of data to write to disk passed in HL 
02d3			;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B 
02d3			;LBA bits 16 to 23 passed in E 
02d3			disk_write: 
02d3 db 0f		wr_status_loop_1:	in	a,(0fh)		;check status 
02d5 e6 80					and	80h		;check BSY bit 
02d7 c2 d3 02					jp	nz,wr_status_loop_1	;loop until not busy 
02da db 0f		wr_status_loop_2:	in	a,(0fh)		;check	status 
02dc e6 40					and	40h		;check DRDY bit 
02de ca da 02					jp	z,wr_status_loop_2	;loop until ready 
02e1 3e 01					ld	a,01h		;number of sectors = 1 
02e3 d3 0a					out	(0ah),a		;sector count register 
02e5 79						ld	a,c 
02e6 d3 0b					out	(0bh),a		;lba bits 0 - 7 
02e8 78						ld	a,b 
02e9 d3 0c					out	(0ch),a		;lba bits 8 - 15 
02eb 7b						ld	a,e 
02ec d3 0d					out	(0dh),a		;lba bits 16 - 23 
02ee 3e e0					ld	a,11100000b	;LBA mode, select drive 0 
02f0 d3 0e					out	(0eh),a		;drive/head register 
02f2 3e 30					ld	a,30h		;Write sector command 
02f4 d3 0f					out	(0fh),a 
02f6 db 0f		wr_wait_for_DRQ_set:	in	a,(0fh)		;read status 
02f8 e6 08					and	08h		;DRQ bit 
02fa ca f6 02					jp	z,wr_wait_for_DRQ_set	;loop until bit set			 
02fd 7e			write_loop:		ld	a,(hl) 
02fe d3 08					out	(08h),a		;write data 
0300 23						inc	hl 
0301 db 0f					in	a,(0fh)		;read status 
0303 e6 08					and	08h		;check DRQ bit 
0305 c2 fd 02					jp	nz,write_loop	;write until bit cleared 
0308 db 0f		wr_wait_for_BSY_clear:	in	a,(0fh) 
030a e6 80					and	80h 
030c c2 08 03					jp	nz,wr_wait_for_BSY_clear 
030f db 0f					in	a,(0fh)		;clear INTRQ 
0311 c9						ret 
0312			; 
0312			;Strings used in subroutines 
0312 .. 00		length_entry_string:	defm	"Enter length of file to load (decimal): ",0 
033b .. 00		dump_entry_string:	defm	"Enter no. of bytes to dump (decimal): ",0 
0362 .. 00		LBA_entry_string:	defm	"Enter LBA (decimal, 0 to 65535): ",0 
0384 08 1b .. 00	erase_char_string:	defm	008h,01bh,"[K",000h	;ANSI sequence for backspace, erase to end of line. 
0389 .. 00		address_entry_msg:	defm	"Enter 4-digit hex address (use upper-case A through F): ",0 
03c2 .. 00		address_error_msg:	defm	"\r\nError: invalid hex character, try again: ",0 
03ee .. 00		data_entry_msg:		defm	"Enter hex bytes, hit return when finished.\r\n",0 
041b .. 00		data_error_msg:		defm	"Error: invalid hex byte.\r\n",0 
0436 .. 00		decimal_error_msg:	defm	"\r\nError: invalid decimal number, try again: ",0 
0463			; 
0463			;Simple monitor program for CPUville Z80 computer with serial interface. 
0463 31 ff db		monitor_cold_start:	ld	sp,ROM_monitor_stack 
0466 cd 03 00					call	initialize_port 
0469 21 dc 05					ld	hl,monitor_message 
046c cd 18 00					call	write_string 
046f cd 89 02		monitor_warm_start:	call	write_newline		;routine program return here to avoid re-initialization of port 
0472 3e 3e					ld	a,03eh			;cursor symbol 
0474 cd 0c 00					call	write_char 
0477 21 08 db					ld	hl,buffer 
047a cd 4c 00					call	get_line		;get monitor input string (command) 
047d cd 89 02					call	write_newline 
0480 cd 84 04					call	parse			;interprets command, returns with address to jump to in HL 
0483 e9						jp	(hl) 
0484			; 
0484			;Parses an input line stored in buffer for available commands as described in parse table. 
0484			;Returns with address of jump to action for the command in HL 
0484 01 ba 07		parse:			ld	bc,parse_table		;bc is pointer to parse_table 
0487 0a			parse_start:		ld	a,(bc)			;get pointer to match string from parse table 
0488 5f						ld	e,a 
0489 03						inc	bc 
048a 0a						ld	a,(bc)			 
048b 57						ld	d,a			;de will is pointer to strings for matching 
048c 1a						ld	a,(de)			;get first char from match string 
048d f6 00					or	000h			;zero? 
048f ca aa 04					jp	z,parser_exit		;yes, exit no_match 
0492 21 08 db					ld	hl,buffer		;no, parse input string  
0495 be			match_loop:		cp	(hl)			;compare buffer char with match string char 
0496 c2 a4 04					jp	nz,no_match		;no match, go to next match string 
0499 f6 00					or	000h			;end of strings (zero)? 
049b ca aa 04					jp	z,parser_exit		;yes, matching string found 
049e 13						inc	de			;match so far, point to next char in match string 
049f 1a						ld	a,(de)			;get next character from match string 
04a0 23						inc	hl			;and point to next char in input string 
04a1 c3 95 04					jp	match_loop		;check for match 
04a4 03			no_match:		inc	bc			;skip over jump target to 
04a5 03						inc	bc 
04a6 03						inc	bc			;get address of next matching string 
04a7 c3 87 04					jp	parse_start 
04aa 03			parser_exit:		inc	bc			;skip to address of jump for match 
04ab 0a						ld	a,(bc) 
04ac 6f						ld	l,a 
04ad 03						inc	bc 
04ae 0a						ld	a,(bc) 
04af 67						ld	h,a			;returns with jump address in hl 
04b0 c9						ret 
04b1			; 
04b1			;Actions to be taken on match 
04b1			; 
04b1			;Memory dump program 
04b1			;Input 4-digit hexadecimal address 
04b1			;Calls memory_dump subroutine 
04b1 21 06 06		dump_jump:		ld	hl,dump_message		;Display greeting 
04b4 cd 18 00					call	write_string 
04b7 21 89 03					ld	hl,address_entry_msg	;get ready to get address 
04ba cd 18 00					call	write_string 
04bd cd fe 00					call	address_entry		;returns with address in HL 
04c0 cd 89 02					call	write_newline 
04c3 cd 99 01					call	memory_dump 
04c6 c3 6f 04					jp	monitor_warm_start 
04c9			; 
04c9			;Hex loader, displays formatted input 
04c9 21 2d 06		load_jump:		ld	hl,load_message		;Display greeting 
04cc cd 18 00					call	write_string		;get address to load 
04cf 21 89 03					ld	hl,address_entry_msg	;get ready to get address 
04d2 cd 18 00					call	write_string 
04d5 cd fe 00					call	address_entry 
04d8 cd 89 02					call	write_newline 
04db cd 13 02					call	memory_load 
04de c3 6f 04					jp	monitor_warm_start 
04e1			; 
04e1			;Jump and run do the same thing: get an address and jump to it. 
04e1 21 5c 06		run_jump:		ld	hl,run_message		;Display greeting 
04e4 cd 18 00					call	write_string 
04e7 21 89 03					ld	hl,address_entry_msg	;get ready to get address 
04ea cd 18 00					call	write_string 
04ed cd fe 00					call	address_entry 
04f0 e9						jp	(hl) 
04f1			; 
04f1			;Help and ? do the same thing, display the available commands 
04f1 21 ee 05		help_jump:		ld	hl,help_message 
04f4 cd 18 00					call	write_string 
04f7 01 ba 07					ld	bc,parse_table		;table with pointers to command strings 
04fa 0a			help_loop:		ld	a,(bc)			;displays the strings for matching commands, 
04fb 6f						ld	l,a			;getting the string addresses from the 
04fc 03						inc	bc			;parse table 
04fd 0a						ld	a,(bc)			;pass address of string to hl through a reg 
04fe 67						ld	h,a 
04ff 7e						ld	a,(hl)			;hl now points to start of match string 
0500 f6 00					or	000h			;exit if no_match string 
0502 ca 15 05					jp	z,help_done 
0505 c5						push	bc			;write_char uses b register 
0506 3e 20					ld	a,020h			;space char 
0508 cd 0c 00					call	write_char 
050b c1						pop	bc 
050c cd 18 00					call	write_string		;writes match string 
050f 03						inc	bc			;pass over jump address in table 
0510 03						inc	bc 
0511 03						inc	bc 
0512 c3 fa 04					jp	help_loop 
0515 c3 6f 04		help_done:		jp	monitor_warm_start 
0518			; 
0518			;Binary file load. Need both address to load and length of file 
0518 21 91 06		bload_jump:		ld	hl,bload_message 
051b cd 18 00					call	write_string 
051e 21 89 03					ld	hl,address_entry_msg 
0521 cd 18 00					call	write_string 
0524 cd fe 00					call	address_entry 
0527 cd 89 02					call	write_newline 
052a e5						push	hl 
052b 21 12 03					ld	hl,length_entry_string 
052e cd 18 00					call	write_string 
0531 cd 29 01					call	decimal_entry 
0534 44						ld	b,h 
0535 4d						ld	c,l 
0536 21 b4 06					ld	hl,bload_ready_message 
0539 cd 18 00					call	write_string 
053c e1						pop	hl 
053d cd 28 00					call	bload 
0540 c3 6f 04					jp	monitor_warm_start 
0543			; 
0543			;Binary memory dump. Need address of start of dump and no. bytes 
0543 21 d8 06		bdump_jump:		ld	hl,bdump_message 
0546 cd 18 00					call	write_string 
0549 21 89 03					ld	hl,address_entry_msg 
054c cd 18 00					call	write_string 
054f cd fe 00					call	address_entry 
0552 cd 89 02					call	write_newline 
0555 e5						push	hl 
0556 21 3b 03					ld	hl,dump_entry_string 
0559 cd 18 00					call	write_string 
055c cd 29 01					call	decimal_entry 
055f 44						ld	b,h 
0560 4d						ld	c,l 
0561 21 08 07					ld	hl,bdump_ready_message 
0564 cd 18 00					call	write_string 
0567 cd 7f 02					call	get_char 
056a e1						pop	hl 
056b cd 3a 00					call	bdump 
056e c3 6f 04					jp	monitor_warm_start 
0571			;Disk read. Need memory address to place data, LBA of sector to read 
0571 21 2f 07		diskrd_jump:		ld	hl,diskrd_message 
0574 cd 18 00					call	write_string 
0577 21 89 03					ld	hl,address_entry_msg 
057a cd 18 00					call	write_string 
057d cd fe 00					call	address_entry 
0580 cd 89 02					call	write_newline 
0583 e5						push	hl 
0584 21 62 03					ld	hl,LBA_entry_string 
0587 cd 18 00					call	write_string 
058a cd 29 01					call	decimal_entry 
058d 44						ld	b,h 
058e 4d						ld	c,l 
058f 1e 00					ld	e,00h 
0591 e1						pop	hl 
0592 cd 94 02					call	disk_read 
0595 c3 6f 04					jp	monitor_warm_start 
0598 21 57 07		diskwr_jump:		ld	hl,diskwr_message 
059b cd 18 00					call	write_string 
059e 21 89 03					ld	hl,address_entry_msg 
05a1 cd 18 00					call	write_string 
05a4 cd fe 00					call	address_entry 
05a7 cd 89 02					call	write_newline 
05aa e5						push	hl 
05ab 21 62 03					ld	hl,LBA_entry_string 
05ae cd 18 00					call	write_string 
05b1 cd 29 01					call	decimal_entry 
05b4 44						ld	b,h 
05b5 4d						ld	c,l 
05b6 1e 00					ld	e,00h 
05b8 e1						pop	hl 
05b9 cd d3 02					call	disk_write 
05bc c3 6f 04					jp	monitor_warm_start 
05bf 21 00 08		cpm_jump:		ld	hl,0800h 
05c2 01 00 00					ld	bc,0000h 
05c5 1e 00					ld	e,00h 
05c7 cd 94 02					call	disk_read 
05ca c3 00 08					jp	0800h 
05cd			;Prints message for no match to entered command 
05cd 21 eb 05		no_match_jump:		ld	hl,no_match_message 
05d0 cd 18 00					call	write_string 
05d3 21 08 db					ld	hl, buffer 
05d6 cd 18 00					call	write_string 
05d9 c3 6f 04					jp	monitor_warm_start 
05dc			; 
05dc			;Monitor data structures: 
05dc			; 
05dc .. 00		monitor_message: 	defm	"\r\nROM ver. 8\r\n",0 
05eb .. 00		no_match_message:	defm	"? ",0 
05ee .. 00		help_message:		defm	"Commands implemented:\r\n",0 
0606 .. 00		dump_message:		defm	"Displays a 256-byte block of memory.\r\n",0 
062d .. 00		load_message:		defm	"Enter hex bytes starting at memory location.\r\n",0 
065c .. 00		run_message:		defm	"Will jump to (execute) program at address entered.\r\n",0 
0691 .. 00		bload_message:		defm	"Loads a binary file into memory.\r\n",0 
06b4 .. 00		bload_ready_message:	defm	"\n\rReady to receive, start transfer.",0 
06d8 .. 00		bdump_message:		defm	"Dumps binary data from memory to serial port.\r\n",0 
0708 .. 00		bdump_ready_message:	defm	"\n\rReady to send, hit any key to start.",0 
072f .. 00		diskrd_message:		defm	"Reads one sector from disk to memory.\r\n",0 
0757 .. 00		diskwr_message:		defm	"Writes one sector from memory to disk.\r\n",0 
0780			;Strings for matching: 
0780 .. 00		dump_string:		defm	"dump",0 
0785 .. 00		load_string:		defm	"load",0 
078a .. 00		jump_string:		defm	"jump",0 
078f .. 00		run_string:		defm	"run",0 
0793 .. 00		question_string:	defm	"?",0 
0795 .. 00		help_string:		defm	"help",0 
079a .. 00		bload_string:		defm	"bload",0 
07a0 .. 00		bdump_string:		defm	"bdump",0 
07a6 .. 00		diskrd_string:		defm	"diskrd",0 
07ad .. 00		diskwr_string:		defm	"diskwr",0 
07b4 .. 00		cpm_string:		defm	"cpm",0 
07b8 00 00		no_match_string:	defm	0,0 
07ba			;Table for matching strings to jumps 
07ba 80 07 b1 04 85 07 c9 04	parse_table:		defw	dump_string,dump_jump,load_string,load_jump 
07c2 8a 07 e1 04 8f 07 e1 04				defw	jump_string,run_jump,run_string,run_jump 
07ca 93 07 f1 04 95 07 f1 04				defw	question_string,help_jump,help_string,help_jump 
07d2 9a 07 18 05 a0 07 43 05				defw	bload_string,bload_jump,bdump_string,bdump_jump 
07da a6 07 71 05 ad 07 98 05				defw	diskrd_string,diskrd_jump,diskwr_string,diskwr_jump 
07e2 b4 07 bf 05				defw	cpm_string,cpm_jump 
07e6 b8 07 cd 05				defw	no_match_string,no_match_jump 
07ea			 
# End of file 2K_ROM_8.asm
07ea