Split out project into serveral projects
This commit is contained in:
parent
59f802bc9f
commit
edddc5a690
11
Makefile
11
Makefile
|
@ -1,11 +1,15 @@
|
|||
BUILDDIR=.build
|
||||
_BIN= rom_monitor.bin ram_monitor.bin cpm22.bin bios.bin putsys.bin loader.bin MONITOR.COM
|
||||
_BIN= cpm22.bin bios.bin
|
||||
BIN = $(patsubst %,$(BUILDDIR)/%,$(_BIN))
|
||||
|
||||
$(BUILDDIR)/%.bin: src/%.z80
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(BUILDDIR) $(BIN)
|
||||
|
||||
$(BUILDDIR)/%.bin: src/%.z80 | $(BUILDDIR)
|
||||
@zasm -w -i $< -o $@
|
||||
|
||||
$(BUILDDIR)/%.COM: src/%.z80
|
||||
$(BUILDDIR)/%.COM: src/%.z80 | $(BUILDDIR)
|
||||
@zasm -w -i $< -o $@
|
||||
|
||||
$(BUILDDIR):
|
||||
|
@ -13,6 +17,5 @@ $(BUILDDIR):
|
|||
|
||||
all: $(BUILDDIR) $(BIN)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -df $(BUILDDIR)/*.bin $(BUILDDIR)/*.COM $(BUILDDIR)/*.lst .build
|
||||
|
|
BIN
bin/ASM.COM
BIN
bin/ASM.COM
Binary file not shown.
BIN
bin/DDT.COM
BIN
bin/DDT.COM
Binary file not shown.
251
bin/DISKDEF.LIB
251
bin/DISKDEF.LIB
|
@ -1,251 +0,0 @@
|
|||
; CP/M 2.0 disk re-definition library
|
||||
;
|
||||
; Copyright (c) 1979
|
||||
; Digital Research
|
||||
; Box 579
|
||||
; Pacific Grove, CA
|
||||
; 93950
|
||||
;
|
||||
; CP/M logical disk drives are defined using the
|
||||
; macros given below, where the sequence of calls
|
||||
; is:
|
||||
;
|
||||
; disks n
|
||||
; diskdef parameter-list-0
|
||||
; diskdef parameter-list-1
|
||||
; ...
|
||||
; diskdef parameter-list-n
|
||||
; endef
|
||||
;
|
||||
; where n is the number of logical disk drives attached
|
||||
; to the CP/M system, and parameter-list-i defines the
|
||||
; characteristics of the ith drive (i=0,1,...,n-1)
|
||||
;
|
||||
; each parameter-list-i takes the form
|
||||
; dn,fsc,lsc,[skf],bls,dks,dir,cks,ofs,[0]
|
||||
; where
|
||||
; dn is the disk number 0,1,...,n-1
|
||||
; fsc is the first sector number (usually 0 or 1)
|
||||
; lsc is the last sector number on a track
|
||||
; skf is optional "skew factor" for sector translate
|
||||
; bls is the data block size (1024,2048,...,16384)
|
||||
; dks is the disk size in bls increments (word)
|
||||
; dir is the number of directory elements (word)
|
||||
; cks is the number of dir elements to checksum
|
||||
; ofs is the number of tracks to skip (word)
|
||||
; [0] is an optional 0 which forces 16K/directory entry
|
||||
;
|
||||
; for convenience, the form
|
||||
; dn,dm
|
||||
; defines disk dn as having the same characteristics as
|
||||
; a previously defined disk dm.
|
||||
;
|
||||
; a standard four drive CP/M system is defined by
|
||||
; disks 4
|
||||
; diskdef 0,1,26,6,1024,243,64,64,2
|
||||
; dsk set 0
|
||||
; rept 3
|
||||
; dsk set dsk+1
|
||||
; diskdef %dsk,0
|
||||
; endm
|
||||
; endef
|
||||
;
|
||||
; the value of "begdat" at the end of assembly defines the
|
||||
; beginning of the uninitialize ram area above the bios,
|
||||
; while the value of "enddat" defines the next location
|
||||
; following the end of the data area. the size of this
|
||||
; area is given by the value of "datsiz" at the end of the
|
||||
; assembly. note that the allocation vector will be quite
|
||||
; large if a large disk size is defined with a small block
|
||||
; size.
|
||||
;
|
||||
dskhdr macro dn
|
||||
;; define a single disk header list
|
||||
dpe&dn: dw xlt&dn,0000h ;translate table
|
||||
dw 0000h,0000h ;scratch area
|
||||
dw dirbuf,dpb&dn ;dir buff,parm block
|
||||
dw csv&dn,alv&dn ;check, alloc vectors
|
||||
endm
|
||||
;
|
||||
disks macro nd
|
||||
;; define nd disks
|
||||
ndisks set nd ;;for later reference
|
||||
dpbase equ $ ;base of disk parameter blocks
|
||||
;; generate the nd elements
|
||||
dsknxt set 0
|
||||
rept nd
|
||||
dskhdr %dsknxt
|
||||
dsknxt set dsknxt+1
|
||||
endm
|
||||
endm
|
||||
;
|
||||
dpbhdr macro dn
|
||||
dpb&dn equ $ ;disk parm block
|
||||
endm
|
||||
;
|
||||
ddb macro data,comment
|
||||
;; define a db statement
|
||||
db data comment
|
||||
endm
|
||||
;
|
||||
ddw macro data,comment
|
||||
;; define a dw statement
|
||||
dw data comment
|
||||
endm
|
||||
;
|
||||
gcd macro m,n
|
||||
;; greatest common divisor of m,n
|
||||
;; produces value gcdn as result
|
||||
;; (used in sector translate table generation)
|
||||
gcdm set m ;;variable for m
|
||||
gcdn set n ;;variable for n
|
||||
gcdr set 0 ;;variable for r
|
||||
rept 65535
|
||||
gcdx set gcdm/gcdn
|
||||
gcdr set gcdm - gcdx*gcdn
|
||||
if gcdr = 0
|
||||
exitm
|
||||
endif
|
||||
gcdm set gcdn
|
||||
gcdn set gcdr
|
||||
endm
|
||||
endm
|
||||
;
|
||||
diskdef macro dn,fsc,lsc,skf,bls,dks,dir,cks,ofs,k16
|
||||
;; generate the set statements for later tables
|
||||
if nul lsc
|
||||
;; current disk dn same as previous fsc
|
||||
dpb&dn equ dpb&fsc ;equivalent parameters
|
||||
als&dn equ als&fsc ;same allocation vector size
|
||||
css&dn equ css&fsc ;same checksum vector size
|
||||
xlt&dn equ xlt&fsc ;same translate table
|
||||
else
|
||||
secmax set lsc-(fsc) ;;sectors 0...secmax
|
||||
sectors set secmax+1;;number of sectors
|
||||
als&dn set (dks)/8 ;;size of allocation vector
|
||||
if ((dks) mod 8) ne 0
|
||||
als&dn set als&dn+1
|
||||
endif
|
||||
css&dn set (cks)/4 ;;number of checksum elements
|
||||
;; generate the block shift value
|
||||
blkval set bls/128 ;;number of sectors/block
|
||||
blkshf set 0 ;;counts right 0's in blkval
|
||||
blkmsk set 0 ;;fills with 1's from right
|
||||
rept 16 ;;once for each bit position
|
||||
if blkval=1
|
||||
exitm
|
||||
endif
|
||||
;; otherwise, high order 1 not found yet
|
||||
blkshf set blkshf+1
|
||||
blkmsk set (blkmsk shl 1) or 1
|
||||
blkval set blkval/2
|
||||
endm
|
||||
;; generate the extent mask byte
|
||||
blkval set bls/1024 ;;number of kilobytes/block
|
||||
extmsk set 0 ;;fill from right with 1's
|
||||
rept 16
|
||||
if blkval=1
|
||||
exitm
|
||||
endif
|
||||
;; otherwise more to shift
|
||||
extmsk set (extmsk shl 1) or 1
|
||||
blkval set blkval/2
|
||||
endm
|
||||
;; may be double byte allocation
|
||||
if (dks) > 256
|
||||
extmsk set (extmsk shr 1)
|
||||
endif
|
||||
;; may be optional [0] in last position
|
||||
if not nul k16
|
||||
extmsk set k16
|
||||
endif
|
||||
;; now generate directory reservation bit vector
|
||||
dirrem set dir ;;# remaining to process
|
||||
dirbks set bls/32 ;;number of entries per block
|
||||
dirblk set 0 ;;fill with 1's on each loop
|
||||
rept 16
|
||||
if dirrem=0
|
||||
exitm
|
||||
endif
|
||||
;; not complete, iterate once again
|
||||
;; shift right and add 1 high order bit
|
||||
dirblk set (dirblk shr 1) or 8000h
|
||||
if dirrem > dirbks
|
||||
dirrem set dirrem-dirbks
|
||||
else
|
||||
dirrem set 0
|
||||
endif
|
||||
endm
|
||||
dpbhdr dn ;;generate equ $
|
||||
ddw %sectors,<;sec per track>
|
||||
ddb %blkshf,<;block shift>
|
||||
ddb %blkmsk,<;block mask>
|
||||
ddb %extmsk,<;extnt mask>
|
||||
ddw %(dks)-1,<;disk size-1>
|
||||
ddw %(dir)-1,<;directory max>
|
||||
ddb %dirblk shr 8,<;alloc0>
|
||||
ddb %dirblk and 0ffh,<;alloc1>
|
||||
ddw %(cks)/4,<;check size>
|
||||
ddw %ofs,<;offset>
|
||||
;; generate the translate table, if requested
|
||||
if nul skf
|
||||
xlt&dn equ 0 ;no xlate table
|
||||
else
|
||||
if skf = 0
|
||||
xlt&dn equ 0 ;no xlate table
|
||||
else
|
||||
;; generate the translate table
|
||||
nxtsec set 0 ;;next sector to fill
|
||||
nxtbas set 0 ;;moves by one on overflow
|
||||
gcd %sectors,skf
|
||||
;; gcdn = gcd(sectors,skew)
|
||||
neltst set sectors/gcdn
|
||||
;; neltst is number of elements to generate
|
||||
;; before we overlap previous elements
|
||||
nelts set neltst ;;counter
|
||||
xlt&dn equ $ ;translate table
|
||||
rept sectors ;;once for each sector
|
||||
if sectors < 256
|
||||
ddb %nxtsec+(fsc)
|
||||
else
|
||||
ddw %nxtsec+(fsc)
|
||||
endif
|
||||
nxtsec set nxtsec+(skf)
|
||||
if nxtsec >= sectors
|
||||
nxtsec set nxtsec-sectors
|
||||
endif
|
||||
nelts set nelts-1
|
||||
if nelts = 0
|
||||
nxtbas set nxtbas+1
|
||||
nxtsec set nxtbas
|
||||
nelts set neltst
|
||||
endif
|
||||
endm
|
||||
endif ;;end of nul fac test
|
||||
endif ;;end of nul bls test
|
||||
endm
|
||||
;
|
||||
defds macro lab,space
|
||||
lab: ds space
|
||||
endm
|
||||
;
|
||||
lds macro lb,dn,val
|
||||
defds lb&dn,%val&dn
|
||||
endm
|
||||
;
|
||||
endef macro
|
||||
;; generate the necessary ram data areas
|
||||
begdat equ $
|
||||
dirbuf: ds 128 ;directory access buffer
|
||||
dsknxt set 0
|
||||
rept ndisks ;;once for each disk
|
||||
lds alv,%dsknxt,als
|
||||
lds csv,%dsknxt,css
|
||||
dsknxt set dsknxt+1
|
||||
endm
|
||||
enddat equ $
|
||||
datsiz equ $-begdat
|
||||
;; db 0 at this point forces hex record
|
||||
endm
|
||||
;
|
||||
|
BIN
bin/DSKMAINT.COM
BIN
bin/DSKMAINT.COM
Binary file not shown.
BIN
bin/DUMP.COM
BIN
bin/DUMP.COM
Binary file not shown.
BIN
bin/ED.COM
BIN
bin/ED.COM
Binary file not shown.
BIN
bin/LOAD.COM
BIN
bin/LOAD.COM
Binary file not shown.
BIN
bin/MAC.COM
BIN
bin/MAC.COM
Binary file not shown.
BIN
bin/MBASIC.COM
BIN
bin/MBASIC.COM
Binary file not shown.
BIN
bin/PIP.COM
BIN
bin/PIP.COM
Binary file not shown.
BIN
bin/STAT.COM
BIN
bin/STAT.COM
Binary file not shown.
BIN
bin/SUBMIT.COM
BIN
bin/SUBMIT.COM
Binary file not shown.
BIN
bin/XSUB.COM
BIN
bin/XSUB.COM
Binary file not shown.
696
src/MONITOR.z80
696
src/MONITOR.z80
|
@ -1,696 +0,0 @@
|
|||
#target bin
|
||||
;RAM monitor for a system with serial interface and IDE disk and memory expansion board.
|
||||
;This program to be loaded by CP/M at 0100h, then it copies itself to memory at DC00h.
|
||||
;Assumes serial port has been initialized by ROM monitor.
|
||||
;Assumes the UART data port address is 02h and control/status address is 03h
|
||||
;Assumes memory configuration is all-RAM
|
||||
;
|
||||
;The subroutines use these variables in RAM, same area as ROM monitor:
|
||||
current_location: equ 0xE100 ;word variable in RAM
|
||||
line_count: equ 0xE102 ;byte variable in RAM
|
||||
byte_count: equ 0xE103 ;byte variable in RAM
|
||||
value_pointer: equ 0xE104 ;word variable in RAM
|
||||
current_value: equ 0xE106 ;word variable in RAM
|
||||
buffer: equ 0xE108 ;buffer in RAM -- up to stack area
|
||||
;Will use stack of calling program (CP/M) which is re-initialized at re-boot.
|
||||
;
|
||||
;
|
||||
;
|
||||
;Code to start program and move to higher memory
|
||||
;
|
||||
#code BOOT, 0x0100
|
||||
ld hl,code_origin ;start of code to transfer
|
||||
ld bc,code_end-code_start+1 ;length of code to transfer
|
||||
ld de,0E200h ;target of transfer
|
||||
ldir ;Z80 transfer instruction
|
||||
jp 0E200h
|
||||
code_origin: ;address of first byte of code before transfer
|
||||
;
|
||||
#code MAIN, 0xE200
|
||||
code_start: jp monitor_start
|
||||
;
|
||||
;Puts a single char (byte value) on serial output
|
||||
;Call with char to send in A register. Uses B register
|
||||
write_char: ld b,a ;store char
|
||||
write_char_loop: in a,(3) ;check if OK to send
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,write_char_loop ;loop if not set
|
||||
ld a,b ;get char back
|
||||
out (2),a ;send to output
|
||||
ret ;returns with char in a
|
||||
;
|
||||
;Subroutine to write a zero-terminated string to serial output
|
||||
;Pass address of string in HL register
|
||||
;No error checking
|
||||
write_string: in a,(3) ;read status
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,write_string ;loop if not set
|
||||
ld a,(hl) ;get char from string
|
||||
and a ;check if 0
|
||||
ret z ;yes, finished
|
||||
out (2),a ;no, write char to output
|
||||
inc hl ;next char in string
|
||||
jp write_string ;start over
|
||||
;
|
||||
;Binary loader. Receive a binary file, place in memory.
|
||||
;Address of load passed in HL, length of load (= file length) in BC
|
||||
bload: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,bload ;not ready, loop
|
||||
in a,(0x1E)
|
||||
ld (hl),a
|
||||
inc hl
|
||||
dec bc ;byte counter
|
||||
ld a,b ;need to test BC this way because
|
||||
or c ;dec rp instruction does not change flags
|
||||
jp nz,bload
|
||||
ret
|
||||
;
|
||||
;Binary dump to port. Send a stream of binary data from memory to serial output
|
||||
;Address of dump passed in HL, length of dump in BC
|
||||
bdump: in a,(3) ;get status
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,bdump ;not ready, loop
|
||||
ld a,(hl)
|
||||
out (2),a
|
||||
inc hl
|
||||
dec bc
|
||||
ld a,b ;need to test this way because
|
||||
or c ;dec rp instruction does not change flags
|
||||
jp nz,bdump
|
||||
ret
|
||||
;
|
||||
;Subroutine to get a string from serial input, place in buffer.
|
||||
;Buffer address passed in HL reg.
|
||||
;Uses A,BC,DE,HL registers (including calls to other subroutines).
|
||||
;Line entry ends by hitting return key. Return char not included in string (replaced by zero).
|
||||
;Backspace editing OK. No error checking.
|
||||
;
|
||||
get_line: ld c,000h ;line position
|
||||
ld a,h ;put original buffer address in de
|
||||
ld d,a ;after this don't need to preserve hl
|
||||
ld a,l ;subroutines called don't use de
|
||||
ld e,a
|
||||
get_line_next_char: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,get_line_next_char ;not ready, loop
|
||||
in a,(0x1E) ;get char
|
||||
cp 00dh ;check if return
|
||||
ret z ;yes, normal exit
|
||||
cp 07fh ;check if backspace (VT102 keys)
|
||||
jp z,get_line_backspace ;yes, jump to backspace routine
|
||||
cp 008h ;check if backspace (ANSI keys)
|
||||
jp z,get_line_backspace ;yes, jump to backspace
|
||||
call write_char ;put char on screen
|
||||
ld (de),a ;store char in buffer
|
||||
inc de ;point to next space in buffer
|
||||
inc c ;inc counter
|
||||
ld a,000h
|
||||
ld (de),a ;leaves a zero-terminated string in buffer
|
||||
jp get_line_next_char
|
||||
get_line_backspace: ld a,c ;check current position in line
|
||||
cp 000h ;at beginning of line?
|
||||
jp z,get_line_next_char ;yes, ignore backspace, get next char
|
||||
dec de ;no, erase char from buffer
|
||||
dec c ;back up one
|
||||
ld a,000h ;put a zero in buffer where the last char was
|
||||
ld (de),a
|
||||
ld hl,erase_char_string ;ANSI sequence to delete one char from line
|
||||
call write_string ;transmits sequence to backspace and erase char
|
||||
jp get_line_next_char
|
||||
;
|
||||
;Creates a two-char hex string from the byte value passed in register A
|
||||
;Location to place string passed in HL
|
||||
;String is zero-terminated, stored in 3 locations starting at HL
|
||||
;Also uses registers b,d, and e
|
||||
byte_to_hex_string: ld b,a ;store original byte
|
||||
srl a ;shift right 4 times, putting
|
||||
srl a ;high nybble in low-nybble spot
|
||||
srl a ;and zeros in high-nybble spot
|
||||
srl a
|
||||
ld d,000h ;prepare for 16-bit addition
|
||||
ld e,a ;de contains offset
|
||||
push hl ;temporarily store string target address
|
||||
ld hl,hex_char_table ;use char table to get high-nybble character
|
||||
add hl,de ;add offset to start of table
|
||||
ld a,(hl) ;get char
|
||||
pop hl ;get string target address
|
||||
ld (hl),a ;store first char of string
|
||||
inc hl ;point to next string target address
|
||||
ld a,b ;get original byte back from reg b
|
||||
and 00fh ;mask off high-nybble
|
||||
ld e,a ;d still has 000h, now de has offset
|
||||
push hl ;temp store string target address
|
||||
ld hl,hex_char_table ;start of table
|
||||
add hl,de ;add offset
|
||||
ld a,(hl) ;get char
|
||||
pop hl ;get string target address
|
||||
ld (hl),a ;store second char of string
|
||||
inc hl ;point to third location
|
||||
ld a,000h ;zero to terminate string
|
||||
ld (hl),a ;store the zero
|
||||
ret ;done
|
||||
;
|
||||
;Converts a single ASCII hex char to a nybble value
|
||||
;Pass char in reg A. Letter numerals must be upper case.
|
||||
;Return nybble value in low-order reg A with zeros in high-order nybble if no error.
|
||||
;Return 0ffh in reg A if error (char not a valid hex numeral).
|
||||
;Also uses b, c, and hl registers.
|
||||
hex_char_to_nybble: ld hl,hex_char_table
|
||||
ld b,00fh ;no. of valid characters in table - 1.
|
||||
ld c,000h ;will be nybble value
|
||||
hex_to_nybble_loop: cp (hl) ;character match here?
|
||||
jp z,hex_to_nybble_ok ;match found, exit
|
||||
dec b ;no match, check if at end of table
|
||||
jp m,hex_to_nybble_err ;table limit exceded, exit with error
|
||||
inc c ;still inside table, continue search
|
||||
inc hl
|
||||
jp hex_to_nybble_loop
|
||||
hex_to_nybble_ok: ld a,c ;put nybble value in a
|
||||
ret
|
||||
hex_to_nybble_err: ld a,0ffh ;error value
|
||||
ret
|
||||
;
|
||||
;Converts a hex character pair to a byte value
|
||||
;Called with location of high-order char in HL
|
||||
;If no error carry flag clear, returns with byte value in register A, and
|
||||
;HL pointing to next mem location after char pair.
|
||||
;If error (non-hex char) carry flag set, HL pointing to invalid char
|
||||
hex_to_byte: ld a,(hl) ;location of character pair
|
||||
push hl ;store hl (hex_char_to_nybble uses it)
|
||||
call hex_char_to_nybble
|
||||
pop hl ;returns with nybble value in a reg, or 0ffh if error
|
||||
cp 0ffh ;non-hex character?
|
||||
jp z,hex_to_byte_err ;yes, exit with error
|
||||
sla a ;no, move low order nybble to high side
|
||||
sla a
|
||||
sla a
|
||||
sla a
|
||||
ld d,a ;store high-nybble
|
||||
inc hl ;get next character of the pair
|
||||
ld a,(hl)
|
||||
push hl ;store hl
|
||||
call hex_char_to_nybble
|
||||
pop hl
|
||||
cp 0ffh ;non-hex character?
|
||||
jp z,hex_to_byte_err ;yes, exit with error
|
||||
or d ;no, combine with high-nybble
|
||||
inc hl ;point to next memory location after char pair
|
||||
scf
|
||||
ccf ;no-error exit (carry = 0)
|
||||
ret
|
||||
hex_to_byte_err: scf ;error, carry flag set
|
||||
ret
|
||||
hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table
|
||||
;
|
||||
;Subroutine to get a two-byte address from serial input.
|
||||
;Returns with address value in HL
|
||||
;Uses locations in RAM for buffer and variables
|
||||
address_entry: ld hl,buffer ;location for entered string
|
||||
call get_line ;returns with address string in buffer
|
||||
ld hl,buffer ;location of stored address entry string
|
||||
call hex_to_byte ;will get high-order byte first
|
||||
jp c, address_entry_error ;if error, jump
|
||||
ld (current_location+1),a ;store high-order byte, little-endian
|
||||
ld hl,buffer+2 ;point to low-order hex char pair
|
||||
call hex_to_byte ;get low-order byte
|
||||
jp c, address_entry_error ;jump if error
|
||||
ld (current_location),a ;store low-order byte in lower memory
|
||||
ld hl,(current_location) ;put memory address in hl
|
||||
ret
|
||||
address_entry_error: ld hl,address_error_msg
|
||||
call write_string
|
||||
jp address_entry
|
||||
;
|
||||
;Subroutine to get a decimal string, return a word value
|
||||
;Calls decimal_string_to_word subroutine
|
||||
decimal_entry: ld hl,buffer
|
||||
call get_line ;returns with DE pointing to terminating zero
|
||||
ld hl,buffer
|
||||
call decimal_string_to_word
|
||||
ret nc ;no error, return with word in hl
|
||||
ld hl,decimal_error_msg ;error, try again
|
||||
call write_string
|
||||
jp decimal_entry
|
||||
;
|
||||
;Subroutine to convert a decimal string to a word value
|
||||
;Call with address of string in HL, pointer to end of string in DE
|
||||
;Carry flag set if error (non-decimal char)
|
||||
;Carry flag clear, word value in HL if no error.
|
||||
decimal_string_to_word: ld b,d
|
||||
ld c,e ;use BC as string pointer
|
||||
ld (current_location),hl ;store addr. of start of buffer in RAM word variable
|
||||
ld hl,000h ;starting value zero
|
||||
ld (current_value),hl
|
||||
ld hl,decimal_place_value ;pointer to values
|
||||
ld (value_pointer),hl
|
||||
decimal_next_char: dec bc ;next char in string (moving right to left)
|
||||
ld hl,(current_location) ;check if at end of decimal string
|
||||
scf ;get ready to subtract de from buffer addr.
|
||||
ccf ;set carry to zero (clear)
|
||||
sbc hl,bc ;keep going if bc > or = hl (buffer address)
|
||||
jp c,decimal_continue ;borrow means bc > hl
|
||||
jp z,decimal_continue ;z means bc = hl
|
||||
ld hl,(current_value) ;return if de < buffer address (no borrow)
|
||||
scf ;get value back from RAM variable
|
||||
ccf
|
||||
ret ;return with carry clear, value in hl
|
||||
decimal_continue: ld a,(bc) ;next char in string (right to left)
|
||||
sub 030h ;ASCII value of zero char
|
||||
jp m,decimal_error ;error if char value less than 030h
|
||||
cp 00ah ;error if byte value > or = 10 decimal
|
||||
jp p,decimal_error ;a reg now has value of decimal numeral
|
||||
ld hl,(value_pointer) ;get value to add an put in de
|
||||
ld e,(hl) ;little-endian (low byte in low memory)
|
||||
inc hl
|
||||
ld d,(hl)
|
||||
inc hl ;hl now points to next value
|
||||
ld (value_pointer),hl
|
||||
ld hl,(current_value) ;get back current value
|
||||
decimal_add: dec a ;add loop to increase total value
|
||||
jp m,decimal_add_done ;end of multiplication
|
||||
add hl,de
|
||||
jp decimal_add
|
||||
decimal_add_done: ld (current_value),hl
|
||||
jp decimal_next_char
|
||||
decimal_error: scf
|
||||
ret
|
||||
jp decimal_add
|
||||
decimal_place_value: defw 1,10,100,1000,10000
|
||||
;
|
||||
;Memory dump
|
||||
;Displays a 256-byte block of memory in 16-byte rows.
|
||||
;Called with address of start of block in HL
|
||||
memory_dump: ld (current_location),hl ;store address of block to be displayed
|
||||
ld a,000h
|
||||
ld (byte_count),a ;initialize byte count
|
||||
ld (line_count),a ;initialize line count
|
||||
jp dump_new_line
|
||||
dump_next_byte: ld hl,(current_location) ;get byte address from storage,
|
||||
ld a,(hl) ;get byte to be converted to string
|
||||
inc hl ;increment address and
|
||||
ld (current_location),hl ;store back
|
||||
ld hl,buffer ;location to store string
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer ;display string
|
||||
call write_string
|
||||
ld a,(byte_count) ;next byte
|
||||
inc a
|
||||
jp z,dump_done ;stop when 256 bytes displayed
|
||||
ld (byte_count),a ;not finished yet, store
|
||||
ld a,(line_count) ;end of line (16 characters)?
|
||||
cp 00fh ;yes, start new line
|
||||
jp z,dump_new_line
|
||||
inc a ;no, increment line count
|
||||
ld (line_count),a
|
||||
ld a,020h ;print space
|
||||
call write_char
|
||||
jp dump_next_byte ;continue
|
||||
dump_new_line: ld a,000h ;reset line count to zero
|
||||
ld (line_count),a
|
||||
call write_newline
|
||||
ld hl,(current_location) ;location of start of line
|
||||
ld a,h ;high byte of address
|
||||
ld hl, buffer
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer
|
||||
call write_string ;write high byte
|
||||
ld hl,(current_location)
|
||||
ld a,l ;low byte of address
|
||||
ld hl, buffer
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer
|
||||
call write_string ;write low byte
|
||||
ld a,020h ;space
|
||||
call write_char
|
||||
jp dump_next_byte ;now write 16 bytes
|
||||
dump_done: ld a,000h
|
||||
ld hl,buffer
|
||||
ld (hl),a ;clear buffer of last string
|
||||
call write_newline
|
||||
ret
|
||||
;
|
||||
;Memory load
|
||||
;Loads RAM memory with bytes entered as hex characters
|
||||
;Called with address to start loading in HL
|
||||
;Displays entered data in 16-byte rows.
|
||||
memory_load: ld (current_location),hl
|
||||
ld hl,data_entry_msg
|
||||
call write_string
|
||||
jp load_new_line
|
||||
load_next_char: call get_char
|
||||
cp 00dh ;return?
|
||||
jp z,load_done ;yes, quit
|
||||
ld (buffer),a
|
||||
call get_char
|
||||
cp 00dh ;return?
|
||||
jp z,load_done ;yes, quit
|
||||
ld (buffer+1),a
|
||||
ld hl,buffer
|
||||
call hex_to_byte
|
||||
jp c,load_data_entry_error ;non-hex character
|
||||
ld hl,(current_location) ;get byte address from storage,
|
||||
ld (hl),a ;store byte
|
||||
inc hl ;increment address and
|
||||
ld (current_location),hl ;store back
|
||||
ld a,(buffer)
|
||||
call write_char
|
||||
ld a,(buffer+1)
|
||||
call write_char
|
||||
ld a,(line_count) ;end of line (16 characters)?
|
||||
cp 00fh ;yes, start new line
|
||||
jp z,load_new_line
|
||||
inc a ;no, increment line count
|
||||
ld (line_count),a
|
||||
ld a,020h ;print space
|
||||
call write_char
|
||||
jp load_next_char ;continue
|
||||
load_new_line: ld a,000h ;reset line count to zero
|
||||
ld (line_count),a
|
||||
call write_newline
|
||||
jp load_next_char ;continue
|
||||
load_data_entry_error: call write_newline
|
||||
ld hl,data_error_msg
|
||||
call write_string
|
||||
ret
|
||||
load_done: call write_newline
|
||||
ret
|
||||
;
|
||||
;Get one ASCII character from the serial port.
|
||||
;Returns with char in A reg. No error checking.
|
||||
get_char: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,get_char ;not ready, loop
|
||||
in a,(0x1E) ;get char
|
||||
ret
|
||||
;
|
||||
;Subroutine to start a new line
|
||||
write_newline: ld a,00dh ;ASCII carriage return character
|
||||
call write_char
|
||||
ld a,00ah ;new line (line feed) character
|
||||
call write_char
|
||||
ret
|
||||
;
|
||||
;Subroutine to read one disk sector (128 bytes)
|
||||
;Address to place data passed in HL
|
||||
;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B
|
||||
;LBA bits 16 to 23 passed in E
|
||||
disk_read:
|
||||
rd_status_loop_1: in a,(0fh) ;check status
|
||||
and 80h ;check BSY bit
|
||||
jp nz,rd_status_loop_1 ;loop until not busy
|
||||
rd_status_loop_2: in a,(0fh) ;check status
|
||||
and 40h ;check DRDY bit
|
||||
jp z,rd_status_loop_2 ;loop until ready
|
||||
ld a,01h ;number of sectors = 1
|
||||
out (0ah),a ;sector count register
|
||||
ld a,c
|
||||
out (0bh),a ;lba bits 0 - 7
|
||||
ld a,b
|
||||
out (0ch),a ;lba bits 8 - 15
|
||||
ld a,e
|
||||
out (0dh),a ;lba bits 16 - 23
|
||||
ld a,11100000b ;LBA mode, select drive 0
|
||||
out (0eh),a ;drive/head register
|
||||
ld a,20h ;Read sector command
|
||||
out (0fh),a
|
||||
rd_wait_for_DRQ_set: in a,(0fh) ;read status
|
||||
and 08h ;DRQ bit
|
||||
jp z,rd_wait_for_DRQ_set ;loop until bit set
|
||||
rd_wait_for_BSY_clear: in a,(0fh)
|
||||
and 80h
|
||||
jp nz,rd_wait_for_BSY_clear
|
||||
in a,(0fh) ;clear INTRQ
|
||||
read_loop: in a,(08h) ;get data
|
||||
ld (hl),a
|
||||
inc hl
|
||||
in a,(0fh) ;check status
|
||||
and 08h ;DRQ bit
|
||||
jp nz,read_loop ;loop until cleared
|
||||
ret
|
||||
;
|
||||
;Subroutine to write one disk sector (128 bytes)
|
||||
;Address of data to write to disk passed in HL
|
||||
;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B
|
||||
;LBA bits 16 to 23 passed in E
|
||||
disk_write:
|
||||
wr_status_loop_1: in a,(0fh) ;check status
|
||||
and 80h ;check BSY bit
|
||||
jp nz,wr_status_loop_1 ;loop until not busy
|
||||
wr_status_loop_2: in a,(0fh) ;check status
|
||||
and 40h ;check DRDY bit
|
||||
jp z,wr_status_loop_2 ;loop until ready
|
||||
ld a,01h ;number of sectors = 1
|
||||
out (0ah),a ;sector count register
|
||||
ld a,c
|
||||
out (0bh),a ;lba bits 0 - 7
|
||||
ld a,b
|
||||
out (0ch),a ;lba bits 8 - 15
|
||||
ld a,e
|
||||
out (0dh),a ;lba bits 16 - 23
|
||||
ld a,11100000b ;LBA mode, select drive 0
|
||||
out (0eh),a ;drive/head register
|
||||
ld a,30h ;Write sector command
|
||||
out (0fh),a
|
||||
wr_wait_for_DRQ_set: in a,(0fh) ;read status
|
||||
and 08h ;DRQ bit
|
||||
jp z,wr_wait_for_DRQ_set ;loop until bit set
|
||||
write_loop: ld a,(hl)
|
||||
out (08h),a ;write data
|
||||
inc hl
|
||||
in a,(0fh) ;read status
|
||||
and 08h ;check DRQ bit
|
||||
jp nz,write_loop ;write until bit cleared
|
||||
wr_wait_for_BSY_clear: in a,(0fh)
|
||||
and 80h
|
||||
jp nz,wr_wait_for_BSY_clear
|
||||
in a,(0fh) ;clear INTRQ
|
||||
ret
|
||||
;
|
||||
;Strings used in subroutines
|
||||
length_entry_string: defm "Enter length of file to load (decimal): ",0
|
||||
dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0
|
||||
LBA_entry_string: defm "Enter LBA (decimal, 0 to 65535): ",0
|
||||
erase_char_string: defm 008h,01bh,"[K",000h ;ANSI sequence for backspace, erase to end of line.
|
||||
address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0
|
||||
address_error_msg: defm 13,10,"Error: invalid hex character, try again: ",0
|
||||
data_entry_msg: defm "Enter hex bytes, hit return when finished.",13,10,0
|
||||
data_error_msg: defm "Error: invalid hex byte.",13,10,0
|
||||
decimal_error_msg: defm 13,10,"Error: invalid decimal number, try again: ",0
|
||||
;
|
||||
;Simple monitor program for CPUville Z80 computer with serial interface.
|
||||
|
||||
monitor_start: call write_newline ;routine program return here to avoid re-initialization of port
|
||||
ld a,03eh ;cursor symbol
|
||||
call write_char
|
||||
ld hl,buffer
|
||||
call get_line ;get monitor input string (command)
|
||||
call write_newline
|
||||
call parse ;interprets command, returns with address to jump to in HL
|
||||
jp (hl)
|
||||
;
|
||||
;Parses an input line stored in buffer for available commands as described in parse table.
|
||||
;Returns with address of jump to action for the command in HL
|
||||
parse: ld bc,parse_table ;bc is pointer to parse_table
|
||||
parse_start: ld a,(bc) ;get pointer to match string from parse table
|
||||
ld e,a
|
||||
inc bc
|
||||
ld a,(bc)
|
||||
ld d,a ;de will is pointer to strings for matching
|
||||
ld a,(de) ;get first char from match string
|
||||
or 000h ;zero?
|
||||
jp z,parser_exit ;yes, exit no_match
|
||||
ld hl,buffer ;no, parse input string
|
||||
match_loop: cp (hl) ;compare buffer char with match string char
|
||||
jp nz,no_match ;no match, go to next match string
|
||||
or 000h ;end of strings (zero)?
|
||||
jp z,parser_exit ;yes, matching string found
|
||||
inc de ;match so far, point to next char in match string
|
||||
ld a,(de) ;get next character from match string
|
||||
inc hl ;and point to next char in input string
|
||||
jp match_loop ;check for match
|
||||
no_match: inc bc ;skip over jump target to
|
||||
inc bc
|
||||
inc bc ;get address of next matching string
|
||||
jp parse_start
|
||||
parser_exit: inc bc ;skip to address of jump for match
|
||||
ld a,(bc)
|
||||
ld l,a
|
||||
inc bc
|
||||
ld a,(bc)
|
||||
ld h,a ;returns with jump address in hl
|
||||
ret
|
||||
;
|
||||
;Actions to be taken on match
|
||||
;
|
||||
;Memory dump program
|
||||
;Input 4-digit hexadecimal address
|
||||
;Calls memory_dump subroutine
|
||||
dump_jump: ld hl,dump_message ;Display greeting
|
||||
call write_string
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry ;returns with address in HL
|
||||
call write_newline
|
||||
call memory_dump
|
||||
jp monitor_start
|
||||
;
|
||||
;Hex loader, displays formatted input
|
||||
load_jump: ld hl,load_message ;Display greeting
|
||||
call write_string ;get address to load
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
call memory_load
|
||||
jp monitor_start
|
||||
;
|
||||
;Jump and run do the same thing: get an address and jump to it.
|
||||
run_jump: ld hl,run_message ;Display greeting
|
||||
call write_string
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry
|
||||
jp (hl)
|
||||
;
|
||||
;Help and ? do the same thing, display the available commands
|
||||
help_jump: ld hl,help_message
|
||||
call write_string
|
||||
ld bc,parse_table ;table with pointers to command strings
|
||||
help_loop: ld a,(bc) ;displays the strings for matching commands,
|
||||
ld l,a ;getting the string addresses from the
|
||||
inc bc ;parse table
|
||||
ld a,(bc) ;pass address of string to hl through a reg
|
||||
ld h,a
|
||||
ld a,(hl) ;hl now points to start of match string
|
||||
or 000h ;exit if no_match string
|
||||
jp z,help_done
|
||||
push bc ;write_char uses b register
|
||||
ld a,020h ;space char
|
||||
call write_char
|
||||
pop bc
|
||||
call write_string ;writes match string
|
||||
inc bc ;pass over jump address in table
|
||||
inc bc
|
||||
inc bc
|
||||
jp help_loop
|
||||
help_done: jp monitor_start
|
||||
;
|
||||
;Binary file load. Need both address to load and length of file
|
||||
bload_jump: ld hl,bload_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,length_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld hl,bload_ready_message
|
||||
call write_string
|
||||
pop hl
|
||||
call bload
|
||||
jp monitor_start
|
||||
;
|
||||
;Binary memory dump. Need address of start of dump and no. bytes
|
||||
bdump_jump: ld hl,bdump_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,dump_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld hl,bdump_ready_message
|
||||
call write_string
|
||||
call get_char
|
||||
pop hl
|
||||
call bdump
|
||||
jp monitor_start
|
||||
;Disk read. Need memory address to place data, LBA of sector to read
|
||||
diskrd_jump: ld hl,diskrd_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,LBA_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld e,00h
|
||||
pop hl
|
||||
call disk_read
|
||||
jp monitor_start
|
||||
diskwr_jump: ld hl,diskwr_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,LBA_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld e,00h
|
||||
pop hl
|
||||
call disk_write
|
||||
jp monitor_start
|
||||
exit_jump: jp 0000h ; Exit CP/M
|
||||
;Prints message for no match to entered command
|
||||
no_match_jump: ld hl,no_match_message
|
||||
call write_string
|
||||
ld hl, buffer
|
||||
call write_string
|
||||
jp monitor_start
|
||||
;
|
||||
;Monitor data structures:
|
||||
;
|
||||
monitor_message: defm 13,10,"ROM ver. 8",13,10,0
|
||||
no_match_message: defm "? ",0
|
||||
help_message: defm "Commands implemented:",13,10,0
|
||||
dump_message: defm "Displays a 256-byte block of memory.",13,10,0
|
||||
load_message: defm "Enter hex bytes starting at memory location.",13,10,0
|
||||
run_message: defm "Will jump to (execute) program at address entered.",13,10,0
|
||||
bload_message: defm "Loads a binary file into memory.",13,10,0
|
||||
bload_ready_message: defm 13,10,"Ready to receive, start transfer.",0
|
||||
bdump_message: defm "Dumps binary data from memory to serial port.",13,10,0
|
||||
bdump_ready_message: defm 13,10,"Ready to send, hit any key to start.",0
|
||||
diskrd_message: defm "Reads one sector from disk to memory.",13,10,0
|
||||
diskwr_message: defm "Writes one sector from memory to disk.",13,10,0
|
||||
;Strings for matching:
|
||||
dump_string: defm "dump",0
|
||||
load_string: defm "load",0
|
||||
jump_string: defm "jump",0
|
||||
run_string: defm "run",0
|
||||
question_string: defm "?",0
|
||||
help_string: defm "help",0
|
||||
bload_string: defm "bload",0
|
||||
bdump_string: defm "bdump",0
|
||||
diskrd_string: defm "diskrd",0
|
||||
diskwr_string: defm "diskwr",0
|
||||
exit_string: defm "exit",0
|
||||
no_match_string: defm 0,0
|
||||
;Table for matching strings to jumps
|
||||
parse_table: defw dump_string,dump_jump,load_string,load_jump
|
||||
defw jump_string,run_jump,run_string,run_jump
|
||||
defw question_string,help_jump,help_string,help_jump
|
||||
defw bload_string,bload_jump,bdump_string,bdump_jump
|
||||
defw diskrd_string,diskrd_jump,diskwr_string,diskwr_jump
|
||||
defw exit_string,exit_jump
|
||||
defw no_match_string,no_match_jump
|
||||
code_end:
|
||||
end
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
;Retrieves CP/M from disk and loads it in memory starting at E400h
|
||||
;Uses calls to ROM subroutine for disk read.
|
||||
;Reads track 0, sectors 2 to 26, then track 1, sectors 1 to 25
|
||||
;This program is loaded into LBA sector 0 of disk, read to loc. 0800h by ROM disk_read subroutine, and executed.
|
||||
|
||||
#target bin
|
||||
#code _HOME, 0x1100
|
||||
|
||||
hstbuf equ 0x1200 ;will put 256-byte raw sector here
|
||||
disk_read equ 0x0296 ;subroutine in 2K ROM
|
||||
cpm equ 0xFA00 ;CP/M cold start entry in BIOS
|
||||
|
||||
main:
|
||||
ld c,1 ;LBA bits 0 to 7
|
||||
ld b,0 ;LBA bits 8 to 15
|
||||
ld e,0 ;LBA bits 16 to 23
|
||||
ld hl,0xE400 ; Memory address -- start of CCP
|
||||
|
||||
loop:
|
||||
call disk_read ;subroutine in ROM
|
||||
ld a,c
|
||||
cp 50
|
||||
jp z,done
|
||||
inc a
|
||||
ld c,a
|
||||
jp loop
|
||||
|
||||
done:
|
||||
out (1),a ;switch memory config to all-RAM
|
||||
jp cpm ;to BIOS cold start entry
|
|
@ -1,56 +0,0 @@
|
|||
;Copies the memory image of CP/M loaded at E400h onto tracks 0 and 1 of the first CP/M disk
|
||||
;Load and run from ROM monitor
|
||||
;Uses calls to BIOS, in memory at FA00h
|
||||
;Writes track 0, sectors 2 to 26, then track 1, sectors 1 to 25
|
||||
|
||||
#target bin
|
||||
#code _HOME, 0x1400
|
||||
|
||||
_bios equ (0x4A00+0xB000)
|
||||
seldsk equ _bios+0x1b
|
||||
settrk equ _bios+0x1e
|
||||
setsec equ _bios+0x21
|
||||
setdma equ _bios+0x24
|
||||
write equ _bios+0x2a
|
||||
|
||||
monitor_warm_start equ 0x0433 ;Return to ROM monitor
|
||||
|
||||
main:
|
||||
ld c,00h ;CP/M disk a
|
||||
call seldsk
|
||||
|
||||
;Write track 0, sectors 2 to 51
|
||||
ld a,1 ;starting sector
|
||||
ld (sector),a
|
||||
ld hl, 0x6400 ;start of CCP
|
||||
ld (address),hl
|
||||
ld c,0 ;CP/M track
|
||||
call settrk
|
||||
|
||||
wr_trk_0_loop:
|
||||
ld a,(sector)
|
||||
ld c,a ;CP/M sector
|
||||
call setsec
|
||||
ld bc,(address) ;memory location
|
||||
call setdma
|
||||
call write
|
||||
ld a,(sector)
|
||||
cp 50 ;done:
|
||||
jp z,done ;yes
|
||||
inc a ;no, next sector
|
||||
ld (sector),a
|
||||
ld hl,(address)
|
||||
ld de,128
|
||||
add hl,de
|
||||
ld (address),hl
|
||||
jp wr_trk_0_loop
|
||||
|
||||
done:
|
||||
jp 0x0000
|
||||
|
||||
sector:
|
||||
db 00h
|
||||
|
||||
address:
|
||||
dw 0000h
|
||||
end
|
|
@ -1,687 +0,0 @@
|
|||
;RAM monitor for a system with serial interface and IDE disk and memory expansion board.
|
||||
;This program to be loaded at DC00h by ROM monitor, and run from there.
|
||||
;Assumes serial port has been initialized by ROM monitor.
|
||||
;Assumes the UART data port address is 02h and control/status address is 03h
|
||||
;
|
||||
;The subroutines use these variables in RAM, same area as ROM monitor:
|
||||
current_location: equ 0x1300 ;word variable in RAM
|
||||
line_count: equ 0x1302 ;byte variable in RAM
|
||||
byte_count: equ 0x1303 ;byte variable in RAM
|
||||
value_pointer: equ 0x1304 ;word variable in RAM
|
||||
current_value: equ 0x1306 ;word variable in RAM
|
||||
buffer: equ 0x1308 ;buffer in RAM -- up to stack area
|
||||
;Can use same stack as ROM monitor. Stack not re-initialized, listed here for information
|
||||
;ROM_monitor_stack: equ 0x13ff ;upper TPA in RAM, below RAM monitor
|
||||
;
|
||||
;
|
||||
org 01400h
|
||||
out (1),a ;change memory configuration to all-RAM
|
||||
jp monitor_start
|
||||
;
|
||||
;Puts a single char (byte value) on serial output
|
||||
;Call with char to send in A register. Uses B register
|
||||
write_char: ld b,a ;store char
|
||||
write_char_loop: in a,(3) ;check if OK to send
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,write_char_loop ;loop if not set
|
||||
ld a,b ;get char back
|
||||
out (2),a ;send to output
|
||||
ret ;returns with char in a
|
||||
;
|
||||
;Subroutine to write a zero-terminated string to serial output
|
||||
;Pass address of string in HL register
|
||||
;No error checking
|
||||
write_string: in a,(3) ;read status
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,write_string ;loop if not set
|
||||
ld a,(hl) ;get char from string
|
||||
and a ;check if 0
|
||||
ret z ;yes, finished
|
||||
out (2),a ;no, write char to output
|
||||
inc hl ;next char in string
|
||||
jp write_string ;start over
|
||||
;
|
||||
;Binary loader. Receive a binary file, place in memory.
|
||||
;Address of load passed in HL, length of load (= file length) in BC
|
||||
bload: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,bload ;not ready, loop
|
||||
in a,(0x1E)
|
||||
ld (hl),a
|
||||
inc hl
|
||||
dec bc ;byte counter
|
||||
ld a,b ;need to test BC this way because
|
||||
or c ;dec rp instruction does not change flags
|
||||
jp nz,bload
|
||||
ret
|
||||
;
|
||||
;Binary dump to port. Send a stream of binary data from memory to serial output
|
||||
;Address of dump passed in HL, length of dump in BC
|
||||
bdump: in a,(3) ;get status
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,bdump ;not ready, loop
|
||||
ld a,(hl)
|
||||
out (2),a
|
||||
inc hl
|
||||
dec bc
|
||||
ld a,b ;need to test this way because
|
||||
or c ;dec rp instruction does not change flags
|
||||
jp nz,bdump
|
||||
ret
|
||||
;
|
||||
;Subroutine to get a string from serial input, place in buffer.
|
||||
;Buffer address passed in HL reg.
|
||||
;Uses A,BC,DE,HL registers (including calls to other subroutines).
|
||||
;Line entry ends by hitting return key. Return char not included in string (replaced by zero).
|
||||
;Backspace editing OK. No error checking.
|
||||
;
|
||||
get_line: ld c,000h ;line position
|
||||
ld a,h ;put original buffer address in de
|
||||
ld d,a ;after this don't need to preserve hl
|
||||
ld a,l ;subroutines called don't use de
|
||||
ld e,a
|
||||
get_line_next_char: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,get_line_next_char ;not ready, loop
|
||||
in a,(0x1E) ;get char
|
||||
cp 00dh ;check if return
|
||||
ret z ;yes, normal exit
|
||||
cp 07fh ;check if backspace (VT102 keys)
|
||||
jp z,get_line_backspace ;yes, jump to backspace routine
|
||||
cp 008h ;check if backspace (ANSI keys)
|
||||
jp z,get_line_backspace ;yes, jump to backspace
|
||||
call write_char ;put char on screen
|
||||
ld (de),a ;store char in buffer
|
||||
inc de ;point to next space in buffer
|
||||
inc c ;inc counter
|
||||
ld a,000h
|
||||
ld (de),a ;leaves a zero-terminated string in buffer
|
||||
jp get_line_next_char
|
||||
get_line_backspace: ld a,c ;check current position in line
|
||||
cp 000h ;at beginning of line?
|
||||
jp z,get_line_next_char ;yes, ignore backspace, get next char
|
||||
dec de ;no, erase char from buffer
|
||||
dec c ;back up one
|
||||
ld a,000h ;put a zero in buffer where the last char was
|
||||
ld (de),a
|
||||
ld hl,erase_char_string ;ANSI sequence to delete one char from line
|
||||
call write_string ;transmits sequence to backspace and erase char
|
||||
jp get_line_next_char
|
||||
;
|
||||
;Creates a two-char hex string from the byte value passed in register A
|
||||
;Location to place string passed in HL
|
||||
;String is zero-terminated, stored in 3 locations starting at HL
|
||||
;Also uses registers b,d, and e
|
||||
byte_to_hex_string: ld b,a ;store original byte
|
||||
srl a ;shift right 4 times, putting
|
||||
srl a ;high nybble in low-nybble spot
|
||||
srl a ;and zeros in high-nybble spot
|
||||
srl a
|
||||
ld d,000h ;prepare for 16-bit addition
|
||||
ld e,a ;de contains offset
|
||||
push hl ;temporarily store string target address
|
||||
ld hl,hex_char_table ;use char table to get high-nybble character
|
||||
add hl,de ;add offset to start of table
|
||||
ld a,(hl) ;get char
|
||||
pop hl ;get string target address
|
||||
ld (hl),a ;store first char of string
|
||||
inc hl ;point to next string target address
|
||||
ld a,b ;get original byte back from reg b
|
||||
and 00fh ;mask off high-nybble
|
||||
ld e,a ;d still has 000h, now de has offset
|
||||
push hl ;temp store string target address
|
||||
ld hl,hex_char_table ;start of table
|
||||
add hl,de ;add offset
|
||||
ld a,(hl) ;get char
|
||||
pop hl ;get string target address
|
||||
ld (hl),a ;store second char of string
|
||||
inc hl ;point to third location
|
||||
ld a,000h ;zero to terminate string
|
||||
ld (hl),a ;store the zero
|
||||
ret ;done
|
||||
;
|
||||
;Converts a single ASCII hex char to a nybble value
|
||||
;Pass char in reg A. Letter numerals must be upper case.
|
||||
;Return nybble value in low-order reg A with zeros in high-order nybble if no error.
|
||||
;Return 0ffh in reg A if error (char not a valid hex numeral).
|
||||
;Also uses b, c, and hl registers.
|
||||
hex_char_to_nybble: ld hl,hex_char_table
|
||||
ld b,00fh ;no. of valid characters in table - 1.
|
||||
ld c,000h ;will be nybble value
|
||||
hex_to_nybble_loop: cp (hl) ;character match here?
|
||||
jp z,hex_to_nybble_ok ;match found, exit
|
||||
dec b ;no match, check if at end of table
|
||||
jp m,hex_to_nybble_err ;table limit exceded, exit with error
|
||||
inc c ;still inside table, continue search
|
||||
inc hl
|
||||
jp hex_to_nybble_loop
|
||||
hex_to_nybble_ok: ld a,c ;put nybble value in a
|
||||
ret
|
||||
hex_to_nybble_err: ld a,0ffh ;error value
|
||||
ret
|
||||
;
|
||||
;Converts a hex character pair to a byte value
|
||||
;Called with location of high-order char in HL
|
||||
;If no error carry flag clear, returns with byte value in register A, and
|
||||
;HL pointing to next mem location after char pair.
|
||||
;If error (non-hex char) carry flag set, HL pointing to invalid char
|
||||
hex_to_byte: ld a,(hl) ;location of character pair
|
||||
push hl ;store hl (hex_char_to_nybble uses it)
|
||||
call hex_char_to_nybble
|
||||
pop hl ;returns with nybble value in a reg, or 0ffh if error
|
||||
cp 0ffh ;non-hex character?
|
||||
jp z,hex_to_byte_err ;yes, exit with error
|
||||
sla a ;no, move low order nybble to high side
|
||||
sla a
|
||||
sla a
|
||||
sla a
|
||||
ld d,a ;store high-nybble
|
||||
inc hl ;get next character of the pair
|
||||
ld a,(hl)
|
||||
push hl ;store hl
|
||||
call hex_char_to_nybble
|
||||
pop hl
|
||||
cp 0ffh ;non-hex character?
|
||||
jp z,hex_to_byte_err ;yes, exit with error
|
||||
or d ;no, combine with high-nybble
|
||||
inc hl ;point to next memory location after char pair
|
||||
scf
|
||||
ccf ;no-error exit (carry = 0)
|
||||
ret
|
||||
hex_to_byte_err: scf ;error, carry flag set
|
||||
ret
|
||||
hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table
|
||||
;
|
||||
;Subroutine to get a two-byte address from serial input.
|
||||
;Returns with address value in HL
|
||||
;Uses locations in RAM for buffer and variables
|
||||
address_entry: ld hl,buffer ;location for entered string
|
||||
call get_line ;returns with address string in buffer
|
||||
ld hl,buffer ;location of stored address entry string
|
||||
call hex_to_byte ;will get high-order byte first
|
||||
jp c, address_entry_error ;if error, jump
|
||||
ld (current_location+1),a ;store high-order byte, little-endian
|
||||
ld hl,buffer+2 ;point to low-order hex char pair
|
||||
call hex_to_byte ;get low-order byte
|
||||
jp c, address_entry_error ;jump if error
|
||||
ld (current_location),a ;store low-order byte in lower memory
|
||||
ld hl,(current_location) ;put memory address in hl
|
||||
ret
|
||||
address_entry_error: ld hl,address_error_msg
|
||||
call write_string
|
||||
jp address_entry
|
||||
;
|
||||
;Subroutine to get a decimal string, return a word value
|
||||
;Calls decimal_string_to_word subroutine
|
||||
decimal_entry: ld hl,buffer
|
||||
call get_line ;returns with DE pointing to terminating zero
|
||||
ld hl,buffer
|
||||
call decimal_string_to_word
|
||||
ret nc ;no error, return with word in hl
|
||||
ld hl,decimal_error_msg ;error, try again
|
||||
call write_string
|
||||
jp decimal_entry
|
||||
;
|
||||
;Subroutine to convert a decimal string to a word value
|
||||
;Call with address of string in HL, pointer to end of string in DE
|
||||
;Carry flag set if error (non-decimal char)
|
||||
;Carry flag clear, word value in HL if no error.
|
||||
decimal_string_to_word: ld b,d
|
||||
ld c,e ;use BC as string pointer
|
||||
ld (current_location),hl ;store addr. of start of buffer in RAM word variable
|
||||
ld hl,000h ;starting value zero
|
||||
ld (current_value),hl
|
||||
ld hl,decimal_place_value ;pointer to values
|
||||
ld (value_pointer),hl
|
||||
decimal_next_char: dec bc ;next char in string (moving right to left)
|
||||
ld hl,(current_location) ;check if at end of decimal string
|
||||
scf ;get ready to subtract de from buffer addr.
|
||||
ccf ;set carry to zero (clear)
|
||||
sbc hl,bc ;keep going if bc > or = hl (buffer address)
|
||||
jp c,decimal_continue ;borrow means bc > hl
|
||||
jp z,decimal_continue ;z means bc = hl
|
||||
ld hl,(current_value) ;return if de < buffer address (no borrow)
|
||||
scf ;get value back from RAM variable
|
||||
ccf
|
||||
ret ;return with carry clear, value in hl
|
||||
decimal_continue: ld a,(bc) ;next char in string (right to left)
|
||||
sub 030h ;ASCII value of zero char
|
||||
jp m,decimal_error ;error if char value less than 030h
|
||||
cp 00ah ;error if byte value > or = 10 decimal
|
||||
jp p,decimal_error ;a reg now has value of decimal numeral
|
||||
ld hl,(value_pointer) ;get value to add an put in de
|
||||
ld e,(hl) ;little-endian (low byte in low memory)
|
||||
inc hl
|
||||
ld d,(hl)
|
||||
inc hl ;hl now points to next value
|
||||
ld (value_pointer),hl
|
||||
ld hl,(current_value) ;get back current value
|
||||
decimal_add: dec a ;add loop to increase total value
|
||||
jp m,decimal_add_done ;end of multiplication
|
||||
add hl,de
|
||||
jp decimal_add
|
||||
decimal_add_done: ld (current_value),hl
|
||||
jp decimal_next_char
|
||||
decimal_error: scf
|
||||
ret
|
||||
jp decimal_add
|
||||
decimal_place_value: defw 1,10,100,1000,10000
|
||||
;
|
||||
;Memory dump
|
||||
;Displays a 256-byte block of memory in 16-byte rows.
|
||||
;Called with address of start of block in HL
|
||||
memory_dump: ld (current_location),hl ;store address of block to be displayed
|
||||
ld a,000h
|
||||
ld (byte_count),a ;initialize byte count
|
||||
ld (line_count),a ;initialize line count
|
||||
jp dump_new_line
|
||||
dump_next_byte: ld hl,(current_location) ;get byte address from storage,
|
||||
ld a,(hl) ;get byte to be converted to string
|
||||
inc hl ;increment address and
|
||||
ld (current_location),hl ;store back
|
||||
ld hl,buffer ;location to store string
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer ;display string
|
||||
call write_string
|
||||
ld a,(byte_count) ;next byte
|
||||
inc a
|
||||
jp z,dump_done ;stop when 256 bytes displayed
|
||||
ld (byte_count),a ;not finished yet, store
|
||||
ld a,(line_count) ;end of line (16 characters)?
|
||||
cp 00fh ;yes, start new line
|
||||
jp z,dump_new_line
|
||||
inc a ;no, increment line count
|
||||
ld (line_count),a
|
||||
ld a,020h ;print space
|
||||
call write_char
|
||||
jp dump_next_byte ;continue
|
||||
dump_new_line: ld a,000h ;reset line count to zero
|
||||
ld (line_count),a
|
||||
call write_newline
|
||||
ld hl,(current_location) ;location of start of line
|
||||
ld a,h ;high byte of address
|
||||
ld hl, buffer
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer
|
||||
call write_string ;write high byte
|
||||
ld hl,(current_location)
|
||||
ld a,l ;low byte of address
|
||||
ld hl, buffer
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer
|
||||
call write_string ;write low byte
|
||||
ld a,020h ;space
|
||||
call write_char
|
||||
jp dump_next_byte ;now write 16 bytes
|
||||
dump_done: ld a,000h
|
||||
ld hl,buffer
|
||||
ld (hl),a ;clear buffer of last string
|
||||
call write_newline
|
||||
ret
|
||||
;
|
||||
;Memory load
|
||||
;Loads RAM memory with bytes entered as hex characters
|
||||
;Called with address to start loading in HL
|
||||
;Displays entered data in 16-byte rows.
|
||||
memory_load: ld (current_location),hl
|
||||
ld hl,data_entry_msg
|
||||
call write_string
|
||||
jp load_new_line
|
||||
load_next_char: call get_char
|
||||
cp 00dh ;return?
|
||||
jp z,load_done ;yes, quit
|
||||
ld (buffer),a
|
||||
call get_char
|
||||
cp 00dh ;return?
|
||||
jp z,load_done ;yes, quit
|
||||
ld (buffer+1),a
|
||||
ld hl,buffer
|
||||
call hex_to_byte
|
||||
jp c,load_data_entry_error ;non-hex character
|
||||
ld hl,(current_location) ;get byte address from storage,
|
||||
ld (hl),a ;store byte
|
||||
inc hl ;increment address and
|
||||
ld (current_location),hl ;store back
|
||||
ld a,(buffer)
|
||||
call write_char
|
||||
ld a,(buffer+1)
|
||||
call write_char
|
||||
ld a,(line_count) ;end of line (16 characters)?
|
||||
cp 00fh ;yes, start new line
|
||||
jp z,load_new_line
|
||||
inc a ;no, increment line count
|
||||
ld (line_count),a
|
||||
ld a,020h ;print space
|
||||
call write_char
|
||||
jp load_next_char ;continue
|
||||
load_new_line: ld a,000h ;reset line count to zero
|
||||
ld (line_count),a
|
||||
call write_newline
|
||||
jp load_next_char ;continue
|
||||
load_data_entry_error: call write_newline
|
||||
ld hl,data_error_msg
|
||||
call write_string
|
||||
ret
|
||||
load_done: call write_newline
|
||||
ret
|
||||
;
|
||||
;Get one ASCII character from the serial port.
|
||||
;Returns with char in A reg. No error checking.
|
||||
get_char: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,get_char ;not ready, loop
|
||||
in a,(0x1E) ;get char
|
||||
ret
|
||||
;
|
||||
;Subroutine to start a new line
|
||||
write_newline: ld a,00dh ;ASCII carriage return character
|
||||
call write_char
|
||||
ld a,00ah ;new line (line feed) character
|
||||
call write_char
|
||||
ret
|
||||
;
|
||||
;Subroutine to read one disk sector (128 bytes)
|
||||
;Address to place data passed in HL
|
||||
;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B
|
||||
;LBA bits 16 to 23 passed in E
|
||||
disk_read:
|
||||
rd_status_loop_1: in a,(0fh) ;check status
|
||||
and 80h ;check BSY bit
|
||||
jp nz,rd_status_loop_1 ;loop until not busy
|
||||
rd_status_loop_2: in a,(0fh) ;check status
|
||||
and 40h ;check DRDY bit
|
||||
jp z,rd_status_loop_2 ;loop until ready
|
||||
ld a,01h ;number of sectors = 1
|
||||
out (0ah),a ;sector count register
|
||||
ld a,c
|
||||
out (0bh),a ;lba bits 0 - 7
|
||||
ld a,b
|
||||
out (0ch),a ;lba bits 8 - 15
|
||||
ld a,e
|
||||
out (0dh),a ;lba bits 16 - 23
|
||||
ld a,11100000b ;LBA mode, select drive 0
|
||||
out (0eh),a ;drive/head register
|
||||
ld a,20h ;Read sector command
|
||||
out (0fh),a
|
||||
rd_wait_for_DRQ_set: in a,(0fh) ;read status
|
||||
and 08h ;DRQ bit
|
||||
jp z,rd_wait_for_DRQ_set ;loop until bit set
|
||||
rd_wait_for_BSY_clear: in a,(0fh)
|
||||
and 80h
|
||||
jp nz,rd_wait_for_BSY_clear
|
||||
in a,(0fh) ;clear INTRQ
|
||||
read_loop: in a,(08h) ;get data
|
||||
ld (hl),a
|
||||
inc hl
|
||||
in a,(0fh) ;check status
|
||||
and 08h ;DRQ bit
|
||||
jp nz,read_loop ;loop until cleared
|
||||
ret
|
||||
;
|
||||
;Subroutine to write one disk sector (128 bytes)
|
||||
;Address of data to write to disk passed in HL
|
||||
;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B
|
||||
;LBA bits 16 to 23 passed in E
|
||||
disk_write:
|
||||
wr_status_loop_1: in a,(0fh) ;check status
|
||||
and 80h ;check BSY bit
|
||||
jp nz,wr_status_loop_1 ;loop until not busy
|
||||
wr_status_loop_2: in a,(0fh) ;check status
|
||||
and 40h ;check DRDY bit
|
||||
jp z,wr_status_loop_2 ;loop until ready
|
||||
ld a,01h ;number of sectors = 1
|
||||
out (0ah),a ;sector count register
|
||||
ld a,c
|
||||
out (0bh),a ;lba bits 0 - 7
|
||||
ld a,b
|
||||
out (0ch),a ;lba bits 8 - 15
|
||||
ld a,e
|
||||
out (0dh),a ;lba bits 16 - 23
|
||||
ld a,11100000b ;LBA mode, select drive 0
|
||||
out (0eh),a ;drive/head register
|
||||
ld a,30h ;Write sector command
|
||||
out (0fh),a
|
||||
wr_wait_for_DRQ_set: in a,(0fh) ;read status
|
||||
and 08h ;DRQ bit
|
||||
jp z,wr_wait_for_DRQ_set ;loop until bit set
|
||||
write_loop: ld a,(hl)
|
||||
out (08h),a ;write data
|
||||
inc hl
|
||||
in a,(0fh) ;read status
|
||||
and 08h ;check DRQ bit
|
||||
jp nz,write_loop ;write until bit cleared
|
||||
wr_wait_for_BSY_clear: in a,(0fh)
|
||||
and 80h
|
||||
jp nz,wr_wait_for_BSY_clear
|
||||
in a,(0fh) ;clear INTRQ
|
||||
ret
|
||||
;
|
||||
;Strings used in subroutines
|
||||
length_entry_string: defm "Enter length of file to load (decimal): ",0
|
||||
dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0
|
||||
LBA_entry_string: defm "Enter LBA (decimal, 0 to 65535): ",0
|
||||
erase_char_string: defm 008h,01bh,"[K",000h ;ANSI sequence for backspace, erase to end of line.
|
||||
address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0
|
||||
address_error_msg: defm 13,10,"Error: invalid hex character, try again: ",0
|
||||
data_entry_msg: defm "Enter hex bytes, hit return when finished.",13,10,0
|
||||
data_error_msg: defm "Error: invalid hex byte.",13,10,0
|
||||
decimal_error_msg: defm 13,10,"Error: invalid decimal number, try again: ",0
|
||||
;
|
||||
;Simple monitor program for CPUville Z80 computer with serial interface.
|
||||
|
||||
monitor_start: call write_newline ;routine program return here to avoid re-initialization of port
|
||||
ld a,03eh ;cursor symbol
|
||||
call write_char
|
||||
ld hl,buffer
|
||||
call get_line ;get monitor input string (command)
|
||||
call write_newline
|
||||
call parse ;interprets command, returns with address to jump to in HL
|
||||
jp (hl)
|
||||
;
|
||||
;Parses an input line stored in buffer for available commands as described in parse table.
|
||||
;Returns with address of jump to action for the command in HL
|
||||
parse: ld bc,parse_table ;bc is pointer to parse_table
|
||||
parse_start: ld a,(bc) ;get pointer to match string from parse table
|
||||
ld e,a
|
||||
inc bc
|
||||
ld a,(bc)
|
||||
ld d,a ;de will is pointer to strings for matching
|
||||
ld a,(de) ;get first char from match string
|
||||
or 000h ;zero?
|
||||
jp z,parser_exit ;yes, exit no_match
|
||||
ld hl,buffer ;no, parse input string
|
||||
match_loop: cp (hl) ;compare buffer char with match string char
|
||||
jp nz,no_match ;no match, go to next match string
|
||||
or 000h ;end of strings (zero)?
|
||||
jp z,parser_exit ;yes, matching string found
|
||||
inc de ;match so far, point to next char in match string
|
||||
ld a,(de) ;get next character from match string
|
||||
inc hl ;and point to next char in input string
|
||||
jp match_loop ;check for match
|
||||
no_match: inc bc ;skip over jump target to
|
||||
inc bc
|
||||
inc bc ;get address of next matching string
|
||||
jp parse_start
|
||||
parser_exit: inc bc ;skip to address of jump for match
|
||||
ld a,(bc)
|
||||
ld l,a
|
||||
inc bc
|
||||
ld a,(bc)
|
||||
ld h,a ;returns with jump address in hl
|
||||
ret
|
||||
;
|
||||
;Actions to be taken on match
|
||||
;
|
||||
;Memory dump program
|
||||
;Input 4-digit hexadecimal address
|
||||
;Calls memory_dump subroutine
|
||||
dump_jump: ld hl,dump_message ;Display greeting
|
||||
call write_string
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry ;returns with address in HL
|
||||
call write_newline
|
||||
call memory_dump
|
||||
jp monitor_start
|
||||
;
|
||||
;Hex loader, displays formatted input
|
||||
load_jump: ld hl,load_message ;Display greeting
|
||||
call write_string ;get address to load
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
call memory_load
|
||||
jp monitor_start
|
||||
;
|
||||
;Jump and run do the same thing: get an address and jump to it.
|
||||
run_jump: ld hl,run_message ;Display greeting
|
||||
call write_string
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry
|
||||
jp (hl)
|
||||
;
|
||||
;Help and ? do the same thing, display the available commands
|
||||
help_jump: ld hl,help_message
|
||||
call write_string
|
||||
ld bc,parse_table ;table with pointers to command strings
|
||||
help_loop: ld a,(bc) ;displays the strings for matching commands,
|
||||
ld l,a ;getting the string addresses from the
|
||||
inc bc ;parse table
|
||||
ld a,(bc) ;pass address of string to hl through a reg
|
||||
ld h,a
|
||||
ld a,(hl) ;hl now points to start of match string
|
||||
or 000h ;exit if no_match string
|
||||
jp z,help_done
|
||||
push bc ;write_char uses b register
|
||||
ld a,020h ;space char
|
||||
call write_char
|
||||
pop bc
|
||||
call write_string ;writes match string
|
||||
inc bc ;pass over jump address in table
|
||||
inc bc
|
||||
inc bc
|
||||
jp help_loop
|
||||
help_done: jp monitor_start
|
||||
;
|
||||
;Binary file load. Need both address to load and length of file
|
||||
bload_jump: ld hl,bload_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,length_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld hl,bload_ready_message
|
||||
call write_string
|
||||
pop hl
|
||||
call bload
|
||||
jp monitor_start
|
||||
;
|
||||
;Binary memory dump. Need address of start of dump and no. bytes
|
||||
bdump_jump: ld hl,bdump_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,dump_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld hl,bdump_ready_message
|
||||
call write_string
|
||||
call get_char
|
||||
pop hl
|
||||
call bdump
|
||||
jp monitor_start
|
||||
;Disk read. Need memory address to place data, LBA of sector to read
|
||||
diskrd_jump: ld hl,diskrd_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,LBA_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld e,00h
|
||||
pop hl
|
||||
call disk_read
|
||||
jp monitor_start
|
||||
diskwr_jump: ld hl,diskwr_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,LBA_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld e,00h
|
||||
pop hl
|
||||
call disk_write
|
||||
jp monitor_start
|
||||
boot_jump: ld hl,1100h
|
||||
ld bc,0000h
|
||||
ld e,00h
|
||||
call disk_read
|
||||
out (0),a ;CP/M loader uses ROM routine to read disk
|
||||
jp 1100h
|
||||
;Prints message for no match to entered command
|
||||
no_match_jump: ld hl,no_match_message
|
||||
call write_string
|
||||
ld hl, buffer
|
||||
call write_string
|
||||
jp monitor_start
|
||||
;
|
||||
;Monitor data structures:
|
||||
;
|
||||
monitor_message: defm 13,10,"ROM ver. 8",13,10,0
|
||||
no_match_message: defm "? ",0
|
||||
help_message: defm "Commands implemented:",13,10,0
|
||||
dump_message: defm "Displays a 256-byte block of memory.",13,10,0
|
||||
load_message: defm "Enter hex bytes starting at memory location.",13,10,0
|
||||
run_message: defm "Will jump to (execute) program at address entered.",13,10,0
|
||||
bload_message: defm "Loads a binary file into memory.",13,10,0
|
||||
bload_ready_message: defm 13,10,"Ready to receive, start transfer.",0
|
||||
bdump_message: defm "Dumps binary data from memory to serial port.",13,10,0
|
||||
bdump_ready_message: defm 13,10,"Ready to send, hit any key to start.",0
|
||||
diskrd_message: defm "Reads one sector from disk to memory.",13,10,0
|
||||
diskwr_message: defm "Writes one sector from memory to disk.",13,10,0
|
||||
;Strings for matching:
|
||||
dump_string: defm "dump",0
|
||||
load_string: defm "load",0
|
||||
jump_string: defm "jump",0
|
||||
run_string: defm "run",0
|
||||
question_string: defm "?",0
|
||||
help_string: defm "help",0
|
||||
bload_string: defm "bload",0
|
||||
bdump_string: defm "bdump",0
|
||||
diskrd_string: defm "diskrd",0
|
||||
diskwr_string: defm "diskwr",0
|
||||
boot_string: defm "boot",0
|
||||
no_match_string: defm 0,0
|
||||
;Table for matching strings to jumps
|
||||
parse_table: defw dump_string,dump_jump,load_string,load_jump
|
||||
defw jump_string,run_jump,run_string,run_jump
|
||||
defw question_string,help_jump,help_string,help_jump
|
||||
defw bload_string,bload_jump,bdump_string,bdump_jump
|
||||
defw diskrd_string,diskrd_jump,diskwr_string,diskwr_jump
|
||||
defw boot_string,boot_jump
|
||||
defw no_match_string,no_match_jump
|
||||
|
|
@ -1,675 +0,0 @@
|
|||
;ROM monitor for a system with serial interface and IDE disk and memory expansion board.
|
||||
;The disk extension board has 64K RAM -- computer board memory decoder disabled (J2 off).
|
||||
;The disk extension board uses ports 2 and 3 for the serial interface, and 8 to 15 for the disk
|
||||
;Therefore the computer board I/O decoder is also disabled (J1 off)
|
||||
;Output to port 0 will cause memory configuration flip-flop to activate 2K ROM 0000-07FF with 62K RAM 0800-FFFF
|
||||
;Output to port 1 will cause memory configuration flip-flop to activate all RAM 0000-FFFF
|
||||
;
|
||||
org 00000h
|
||||
jp monitor_cold_start
|
||||
;
|
||||
;The following code is for a system with a serial port.
|
||||
;Assumes the UART data port address is 02h and control/status address is 03h
|
||||
;
|
||||
;The subroutines for the serial port use these variables in RAM:
|
||||
current_location: equ 0x1000 ;word variable in RAM
|
||||
line_count: equ 0x1002 ;byte variable in RAM
|
||||
byte_count: equ 0x1003 ;byte variable in RAM
|
||||
value_pointer: equ 0x1004 ;word variable in RAM
|
||||
current_value: equ 0x1006 ;word variable in RAM
|
||||
buffer: equ 0x1008 ;buffer in RAM -- up to stack area
|
||||
;Need to have stack in upper RAM, but not in area of CP/M or RAM monitor.
|
||||
ROM_monitor_stack: equ 0x10ff ;upper TPA in RAM, below RAM monitor
|
||||
;
|
||||
;Subroutine to initialize serial port UART
|
||||
;Needs to be called only once after computer comes out of reset.
|
||||
;If called while port is active will cause port to fail.
|
||||
;16x = 9600 baud
|
||||
initialize_port: ld a,04eh ;1 stop bit, no parity, 8-bit char, 16x baud
|
||||
out (3),a ;write to control port
|
||||
ld a,037h ;enable receive and transmit
|
||||
out (3),a ;write to control port
|
||||
out (3),a ;write to control port
|
||||
ret
|
||||
;
|
||||
;Puts a single char (byte value) on serial output
|
||||
;Call with char to send in A register. Uses B register
|
||||
write_char: ld b,a ;store char
|
||||
write_char_loop: in a,(3) ;check if OK to send
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,write_char_loop ;loop if not set
|
||||
ld a,b ;get char back
|
||||
out (2),a ;send to output
|
||||
ret ;returns with char in a
|
||||
;
|
||||
;Subroutine to write a zero-terminated string to serial output
|
||||
;Pass address of string in HL register
|
||||
;No error checking
|
||||
write_string: in a,(3) ;read status
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,write_string ;loop if not set
|
||||
ld a,(hl) ;get char from string
|
||||
and a ;check if 0
|
||||
ret z ;yes, finished
|
||||
out (2),a ;no, write char to output
|
||||
inc hl ;next char in string
|
||||
jp write_string ;start over
|
||||
;
|
||||
;Binary loader. Receive a binary file, place in memory.
|
||||
;Address of load passed in HL, length of load (= file length) in BC
|
||||
bload: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,bload ;not ready, loop
|
||||
in a,(0x1E)
|
||||
ld (hl),a
|
||||
inc hl
|
||||
dec bc ;byte counter
|
||||
ld a,b ;need to test BC this way because
|
||||
or c ;dec rp instruction does not change flags
|
||||
jp nz,bload
|
||||
ret
|
||||
;
|
||||
;Binary dump to port. Send a stream of binary data from memory to serial output
|
||||
;Address of dump passed in HL, length of dump in BC
|
||||
bdump: in a,(3) ;get status
|
||||
and 001h ;check TxRDY bit
|
||||
jp z,bdump ;not ready, loop
|
||||
ld a,(hl)
|
||||
out (2),a
|
||||
inc hl
|
||||
dec bc
|
||||
ld a,b ;need to test this way because
|
||||
or c ;dec rp instruction does not change flags
|
||||
jp nz,bdump
|
||||
ret
|
||||
;
|
||||
;Subroutine to get a string from serial input, place in buffer.
|
||||
;Buffer address passed in HL reg.
|
||||
;Uses A,BC,DE,HL registers (including calls to other subroutines).
|
||||
;Line entry ends by hitting return key. Return char not included in string (replaced by zero).
|
||||
;Backspace editing OK. No error checking.
|
||||
;
|
||||
get_line: ld c,000h ;line position
|
||||
ld a,h ;put original buffer address in de
|
||||
ld d,a ;after this don't need to preserve hl
|
||||
ld a,l ;subroutines called don't use de
|
||||
ld e,a
|
||||
get_line_next_char: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,get_line_next_char ;not ready, loop
|
||||
in a,(0x1E) ;get char
|
||||
cp 00dh ;check if return
|
||||
ret z ;yes, normal exit
|
||||
cp 07fh ;check if backspace (VT102 keys)
|
||||
jp z,get_line_backspace ;yes, jump to backspace routine
|
||||
cp 008h ;check if backspace (ANSI keys)
|
||||
jp z,get_line_backspace ;yes, jump to backspace
|
||||
call write_char ;put char on screen
|
||||
ld (de),a ;store char in buffer
|
||||
inc de ;point to next space in buffer
|
||||
inc c ;inc counter
|
||||
ld a,000h
|
||||
ld (de),a ;leaves a zero-terminated string in buffer
|
||||
jp get_line_next_char
|
||||
get_line_backspace: ld a,c ;check current position in line
|
||||
cp 000h ;at beginning of line?
|
||||
jp z,get_line_next_char ;yes, ignore backspace, get next char
|
||||
dec de ;no, erase char from buffer
|
||||
dec c ;back up one
|
||||
ld a,000h ;put a zero in buffer where the last char was
|
||||
ld (de),a
|
||||
ld hl,erase_char_string ;ANSI sequence to delete one char from line
|
||||
call write_string ;transmits sequence to backspace and erase char
|
||||
jp get_line_next_char
|
||||
;
|
||||
;Creates a two-char hex string from the byte value passed in register A
|
||||
;Location to place string passed in HL
|
||||
;String is zero-terminated, stored in 3 locations starting at HL
|
||||
;Also uses registers b,d, and e
|
||||
byte_to_hex_string: ld b,a ;store original byte
|
||||
srl a ;shift right 4 times, putting
|
||||
srl a ;high nybble in low-nybble spot
|
||||
srl a ;and zeros in high-nybble spot
|
||||
srl a
|
||||
ld d,000h ;prepare for 16-bit addition
|
||||
ld e,a ;de contains offset
|
||||
push hl ;temporarily store string target address
|
||||
ld hl,hex_char_table ;use char table to get high-nybble character
|
||||
add hl,de ;add offset to start of table
|
||||
ld a,(hl) ;get char
|
||||
pop hl ;get string target address
|
||||
ld (hl),a ;store first char of string
|
||||
inc hl ;point to next string target address
|
||||
ld a,b ;get original byte back from reg b
|
||||
and 00fh ;mask off high-nybble
|
||||
ld e,a ;d still has 000h, now de has offset
|
||||
push hl ;temp store string target address
|
||||
ld hl,hex_char_table ;start of table
|
||||
add hl,de ;add offset
|
||||
ld a,(hl) ;get char
|
||||
pop hl ;get string target address
|
||||
ld (hl),a ;store second char of string
|
||||
inc hl ;point to third location
|
||||
ld a,000h ;zero to terminate string
|
||||
ld (hl),a ;store the zero
|
||||
ret ;done
|
||||
;
|
||||
;Converts a single ASCII hex char to a nybble value
|
||||
;Pass char in reg A. Letter numerals must be upper case.
|
||||
;Return nybble value in low-order reg A with zeros in high-order nybble if no error.
|
||||
;Return 0ffh in reg A if error (char not a valid hex numeral).
|
||||
;Also uses b, c, and hl registers.
|
||||
hex_char_to_nybble: ld hl,hex_char_table
|
||||
ld b,00fh ;no. of valid characters in table - 1.
|
||||
ld c,000h ;will be nybble value
|
||||
hex_to_nybble_loop: cp (hl) ;character match here?
|
||||
jp z,hex_to_nybble_ok ;match found, exit
|
||||
dec b ;no match, check if at end of table
|
||||
jp m,hex_to_nybble_err ;table limit exceded, exit with error
|
||||
inc c ;still inside table, continue search
|
||||
inc hl
|
||||
jp hex_to_nybble_loop
|
||||
hex_to_nybble_ok: ld a,c ;put nybble value in a
|
||||
ret
|
||||
hex_to_nybble_err: ld a,0ffh ;error value
|
||||
ret
|
||||
;
|
||||
;Converts a hex character pair to a byte value
|
||||
;Called with location of high-order char in HL
|
||||
;If no error carry flag clear, returns with byte value in register A, and
|
||||
;HL pointing to next mem location after char pair.
|
||||
;If error (non-hex char) carry flag set, HL pointing to invalid char
|
||||
hex_to_byte: ld a,(hl) ;location of character pair
|
||||
push hl ;store hl (hex_char_to_nybble uses it)
|
||||
call hex_char_to_nybble
|
||||
pop hl ;returns with nybble value in a reg, or 0ffh if error
|
||||
cp 0ffh ;non-hex character?
|
||||
jp z,hex_to_byte_err ;yes, exit with error
|
||||
sla a ;no, move low order nybble to high side
|
||||
sla a
|
||||
sla a
|
||||
sla a
|
||||
ld d,a ;store high-nybble
|
||||
inc hl ;get next character of the pair
|
||||
ld a,(hl)
|
||||
push hl ;store hl
|
||||
call hex_char_to_nybble
|
||||
pop hl
|
||||
cp 0ffh ;non-hex character?
|
||||
jp z,hex_to_byte_err ;yes, exit with error
|
||||
or d ;no, combine with high-nybble
|
||||
inc hl ;point to next memory location after char pair
|
||||
scf
|
||||
ccf ;no-error exit (carry = 0)
|
||||
ret
|
||||
hex_to_byte_err: scf ;error, carry flag set
|
||||
ret
|
||||
hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table
|
||||
;
|
||||
;Subroutine to get a two-byte address from serial input.
|
||||
;Returns with address value in HL
|
||||
;Uses locations in RAM for buffer and variables
|
||||
address_entry: ld hl,buffer ;location for entered string
|
||||
call get_line ;returns with address string in buffer
|
||||
ld hl,buffer ;location of stored address entry string
|
||||
call hex_to_byte ;will get high-order byte first
|
||||
jp c, address_entry_error ;if error, jump
|
||||
ld (current_location+1),a ;store high-order byte, little-endian
|
||||
ld hl,buffer+2 ;point to low-order hex char pair
|
||||
call hex_to_byte ;get low-order byte
|
||||
jp c, address_entry_error ;jump if error
|
||||
ld (current_location),a ;store low-order byte in lower memory
|
||||
ld hl,(current_location) ;put memory address in hl
|
||||
ret
|
||||
address_entry_error: ld hl,address_error_msg
|
||||
call write_string
|
||||
jp address_entry
|
||||
;
|
||||
;Subroutine to get a decimal string, return a word value
|
||||
;Calls decimal_string_to_word subroutine
|
||||
decimal_entry: ld hl,buffer
|
||||
call get_line ;returns with DE pointing to terminating zero
|
||||
ld hl,buffer
|
||||
call decimal_string_to_word
|
||||
ret nc ;no error, return with word in hl
|
||||
ld hl,decimal_error_msg ;error, try again
|
||||
call write_string
|
||||
jp decimal_entry
|
||||
;
|
||||
;Subroutine to convert a decimal string to a word value
|
||||
;Call with address of string in HL, pointer to end of string in DE
|
||||
;Carry flag set if error (non-decimal char)
|
||||
;Carry flag clear, word value in HL if no error.
|
||||
decimal_string_to_word: ld b,d
|
||||
ld c,e ;use BC as string pointer
|
||||
ld (current_location),hl ;store addr. of start of buffer in RAM word variable
|
||||
ld hl,000h ;starting value zero
|
||||
ld (current_value),hl
|
||||
ld hl,decimal_place_value ;pointer to values
|
||||
ld (value_pointer),hl
|
||||
decimal_next_char: dec bc ;next char in string (moving right to left)
|
||||
ld hl,(current_location) ;check if at end of decimal string
|
||||
scf ;get ready to subtract de from buffer addr.
|
||||
ccf ;set carry to zero (clear)
|
||||
sbc hl,bc ;keep going if bc > or = hl (buffer address)
|
||||
jp c,decimal_continue ;borrow means bc > hl
|
||||
jp z,decimal_continue ;z means bc = hl
|
||||
ld hl,(current_value) ;return if de < buffer address (no borrow)
|
||||
scf ;get value back from RAM variable
|
||||
ccf
|
||||
ret ;return with carry clear, value in hl
|
||||
decimal_continue: ld a,(bc) ;next char in string (right to left)
|
||||
sub 030h ;ASCII value of zero char
|
||||
jp m,decimal_error ;error if char value less than 030h
|
||||
cp 00ah ;error if byte value > or = 10 decimal
|
||||
jp p,decimal_error ;a reg now has value of decimal numeral
|
||||
ld hl,(value_pointer) ;get value to add an put in de
|
||||
ld e,(hl) ;little-endian (low byte in low memory)
|
||||
inc hl
|
||||
ld d,(hl)
|
||||
inc hl ;hl now points to next value
|
||||
ld (value_pointer),hl
|
||||
ld hl,(current_value) ;get back current value
|
||||
decimal_add: dec a ;add loop to increase total value
|
||||
jp m,decimal_add_done ;end of multiplication
|
||||
add hl,de
|
||||
jp decimal_add
|
||||
decimal_add_done: ld (current_value),hl
|
||||
jp decimal_next_char
|
||||
decimal_error: scf
|
||||
ret
|
||||
jp decimal_add
|
||||
decimal_place_value: defw 1,10,100,1000,10000
|
||||
;
|
||||
;Memory dump
|
||||
;Displays a 256-byte block of memory in 16-byte rows.
|
||||
;Called with address of start of block in HL
|
||||
memory_dump: ld (current_location),hl ;store address of block to be displayed
|
||||
ld a,000h
|
||||
ld (byte_count),a ;initialize byte count
|
||||
ld (line_count),a ;initialize line count
|
||||
jp dump_new_line
|
||||
dump_next_byte: ld hl,(current_location) ;get byte address from storage,
|
||||
ld a,(hl) ;get byte to be converted to string
|
||||
inc hl ;increment address and
|
||||
ld (current_location),hl ;store back
|
||||
ld hl,buffer ;location to store string
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer ;display string
|
||||
call write_string
|
||||
ld a,(byte_count) ;next byte
|
||||
inc a
|
||||
jp z,dump_done ;stop when 256 bytes displayed
|
||||
ld (byte_count),a ;not finished yet, store
|
||||
ld a,(line_count) ;end of line (16 characters)?
|
||||
cp 00fh ;yes, start new line
|
||||
jp z,dump_new_line
|
||||
inc a ;no, increment line count
|
||||
ld (line_count),a
|
||||
ld a,020h ;print space
|
||||
call write_char
|
||||
jp dump_next_byte ;continue
|
||||
dump_new_line: ld a,000h ;reset line count to zero
|
||||
ld (line_count),a
|
||||
call write_newline
|
||||
ld hl,(current_location) ;location of start of line
|
||||
ld a,h ;high byte of address
|
||||
ld hl, buffer
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer
|
||||
call write_string ;write high byte
|
||||
ld hl,(current_location)
|
||||
ld a,l ;low byte of address
|
||||
ld hl, buffer
|
||||
call byte_to_hex_string ;convert
|
||||
ld hl,buffer
|
||||
call write_string ;write low byte
|
||||
ld a,020h ;space
|
||||
call write_char
|
||||
jp dump_next_byte ;now write 16 bytes
|
||||
dump_done: ld a,000h
|
||||
ld hl,buffer
|
||||
ld (hl),a ;clear buffer of last string
|
||||
call write_newline
|
||||
ret
|
||||
;
|
||||
;Memory load
|
||||
;Loads RAM memory with bytes entered as hex characters
|
||||
;Called with address to start loading in HL
|
||||
;Displays entered data in 16-byte rows.
|
||||
memory_load: ld (current_location),hl
|
||||
ld hl,data_entry_msg
|
||||
call write_string
|
||||
jp load_new_line
|
||||
load_next_char: call get_char
|
||||
cp 00dh ;return?
|
||||
jp z,load_done ;yes, quit
|
||||
ld (buffer),a
|
||||
call get_char
|
||||
cp 00dh ;return?
|
||||
jp z,load_done ;yes, quit
|
||||
ld (buffer+1),a
|
||||
ld hl,buffer
|
||||
call hex_to_byte
|
||||
jp c,load_data_entry_error ;non-hex character
|
||||
ld hl,(current_location) ;get byte address from storage,
|
||||
ld (hl),a ;store byte
|
||||
inc hl ;increment address and
|
||||
ld (current_location),hl ;store back
|
||||
ld a,(buffer)
|
||||
call write_char
|
||||
ld a,(buffer+1)
|
||||
call write_char
|
||||
ld a,(line_count) ;end of line (16 characters)?
|
||||
cp 00fh ;yes, start new line
|
||||
jp z,load_new_line
|
||||
inc a ;no, increment line count
|
||||
ld (line_count),a
|
||||
ld a,020h ;print space
|
||||
call write_char
|
||||
jp load_next_char ;continue
|
||||
load_new_line: ld a,000h ;reset line count to zero
|
||||
ld (line_count),a
|
||||
call write_newline
|
||||
jp load_next_char ;continue
|
||||
load_data_entry_error: call write_newline
|
||||
ld hl,data_error_msg
|
||||
call write_string
|
||||
ret
|
||||
load_done: call write_newline
|
||||
ret
|
||||
;
|
||||
;Get one ASCII character from the serial port.
|
||||
;Returns with char in A reg. No error checking.
|
||||
get_char: in a,(0x1F) ;get status
|
||||
and 001h ;check RxRDY bit
|
||||
jp z,get_char ;not ready, loop
|
||||
in a,(0x1E) ;get char
|
||||
ret
|
||||
;
|
||||
;Subroutine to start a new line
|
||||
write_newline: ld a,00dh ;ASCII carriage return character
|
||||
call write_char
|
||||
ld a,00ah ;new line (line feed) character
|
||||
call write_char
|
||||
ret
|
||||
;
|
||||
;Subroutine to read one disk sector (128 bytes)
|
||||
;Address to place data passed in HL
|
||||
;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B
|
||||
;LBA bits 16 to 23 passed in E
|
||||
disk_read:
|
||||
ld a,c
|
||||
out (0bh),a ;lba bits 0 - 7
|
||||
ld a,b
|
||||
out (0ch),a ;lba bits 8 - 15
|
||||
ld a,e
|
||||
out (0dh),a ;lba bits 16 - 23
|
||||
ld a,20h ;Read sector command
|
||||
out (0fh),a
|
||||
rd_wait_for_DRQ_set: in a,(0fh) ;read status
|
||||
and 08h ;DRQ bit
|
||||
jp z,rd_wait_for_DRQ_set ;loop until bit set
|
||||
read_loop: in a,(08h) ;get data
|
||||
ld (hl),a
|
||||
inc hl
|
||||
in a,(0fh) ;check status
|
||||
and 08h ;DRQ bit
|
||||
jp nz,read_loop ;loop until cleared
|
||||
ret
|
||||
;
|
||||
;Subroutine to write one disk sector (128 bytes)
|
||||
;Address of data to write to disk passed in HL
|
||||
;LBA bits 0 to 7 passed in C, bits 8 to 15 passed in B
|
||||
;LBA bits 16 to 23 passed in E
|
||||
disk_write:
|
||||
ld a,c
|
||||
out (0bh),a ;lba bits 0 - 7
|
||||
ld a,b
|
||||
out (0ch),a ;lba bits 8 - 15
|
||||
ld a,e
|
||||
out (0dh),a ;lba bits 16 - 23
|
||||
ld a,30h ;Write sector command
|
||||
out (0fh),a
|
||||
wr_wait_for_DRQ_set: in a,(0fh) ;read status
|
||||
and 08h ;DRQ bit
|
||||
jp z,wr_wait_for_DRQ_set ;loop until bit set
|
||||
write_loop: ld a,(hl)
|
||||
out (08h),a ;write data
|
||||
inc hl
|
||||
in a,(0fh) ;read status
|
||||
and 08h ;check DRQ bit
|
||||
jp nz,write_loop ;write until bit cleared
|
||||
ret
|
||||
;
|
||||
;Strings used in subroutines
|
||||
length_entry_string: defm "Enter length of file to load (decimal): ",0
|
||||
dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0
|
||||
LBA_entry_string: defm "Enter LBA (decimal, 0 to 65535): ",0
|
||||
erase_char_string: defm 008h,01bh,"[K",000h ;ANSI sequence for backspace, erase to end of line.
|
||||
address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0
|
||||
address_error_msg: defm 13,10,"Error: invalid hex character, try again: ",0
|
||||
data_entry_msg: defm "Enter hex bytes, hit return when finished.",13,10,0
|
||||
data_error_msg: defm "Error: invalid hex byte.",13,10,0
|
||||
decimal_error_msg: defm 13,10,"Error: invalid decimal number, try again: ",0
|
||||
;
|
||||
;Simple monitor program for CPUville Z80 computer with serial interface.
|
||||
monitor_cold_start: ld sp,ROM_monitor_stack
|
||||
call initialize_port
|
||||
ld hl,monitor_message
|
||||
call write_string
|
||||
monitor_warm_start: call write_newline ;routine program return here to avoid re-initialization of port
|
||||
ld a,03eh ;cursor symbol
|
||||
call write_char
|
||||
ld hl,buffer
|
||||
call get_line ;get monitor input string (command)
|
||||
call write_newline
|
||||
call parse ;interprets command, returns with address to jump to in HL
|
||||
jp (hl)
|
||||
;
|
||||
;Parses an input line stored in buffer for available commands as described in parse table.
|
||||
;Returns with address of jump to action for the command in HL
|
||||
parse: ld bc,parse_table ;bc is pointer to parse_table
|
||||
parse_start: ld a,(bc) ;get pointer to match string from parse table
|
||||
ld e,a
|
||||
inc bc
|
||||
ld a,(bc)
|
||||
ld d,a ;de will is pointer to strings for matching
|
||||
ld a,(de) ;get first char from match string
|
||||
or 000h ;zero?
|
||||
jp z,parser_exit ;yes, exit no_match
|
||||
ld hl,buffer ;no, parse input string
|
||||
match_loop: cp (hl) ;compare buffer char with match string char
|
||||
jp nz,no_match ;no match, go to next match string
|
||||
or 000h ;end of strings (zero)?
|
||||
jp z,parser_exit ;yes, matching string found
|
||||
inc de ;match so far, point to next char in match string
|
||||
ld a,(de) ;get next character from match string
|
||||
inc hl ;and point to next char in input string
|
||||
jp match_loop ;check for match
|
||||
no_match: inc bc ;skip over jump target to
|
||||
inc bc
|
||||
inc bc ;get address of next matching string
|
||||
jp parse_start
|
||||
parser_exit: inc bc ;skip to address of jump for match
|
||||
ld a,(bc)
|
||||
ld l,a
|
||||
inc bc
|
||||
ld a,(bc)
|
||||
ld h,a ;returns with jump address in hl
|
||||
ret
|
||||
;
|
||||
;Actions to be taken on match
|
||||
;
|
||||
;Memory dump program
|
||||
;Input 4-digit hexadecimal address
|
||||
;Calls memory_dump subroutine
|
||||
dump_jump: ld hl,dump_message ;Display greeting
|
||||
call write_string
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry ;returns with address in HL
|
||||
call write_newline
|
||||
call memory_dump
|
||||
jp monitor_warm_start
|
||||
;
|
||||
;Hex loader, displays formatted input
|
||||
load_jump: ld hl,load_message ;Display greeting
|
||||
call write_string ;get address to load
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
call memory_load
|
||||
jp monitor_warm_start
|
||||
;
|
||||
;Jump and run do the same thing: get an address and jump to it.
|
||||
run_jump: ld hl,run_message ;Display greeting
|
||||
call write_string
|
||||
ld hl,address_entry_msg ;get ready to get address
|
||||
call write_string
|
||||
call address_entry
|
||||
jp (hl)
|
||||
;
|
||||
;Help and ? do the same thing, display the available commands
|
||||
help_jump: ld hl,help_message
|
||||
call write_string
|
||||
ld bc,parse_table ;table with pointers to command strings
|
||||
help_loop: ld a,(bc) ;displays the strings for matching commands,
|
||||
ld l,a ;getting the string addresses from the
|
||||
inc bc ;parse table
|
||||
ld a,(bc) ;pass address of string to hl through a reg
|
||||
ld h,a
|
||||
ld a,(hl) ;hl now points to start of match string
|
||||
or 000h ;exit if no_match string
|
||||
jp z,help_done
|
||||
push bc ;write_char uses b register
|
||||
ld a,020h ;space char
|
||||
call write_char
|
||||
pop bc
|
||||
call write_string ;writes match string
|
||||
inc bc ;pass over jump address in table
|
||||
inc bc
|
||||
inc bc
|
||||
jp help_loop
|
||||
help_done: jp monitor_warm_start
|
||||
;
|
||||
;Binary file load. Need both address to load and length of file
|
||||
bload_jump: ld hl,bload_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,length_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld hl,bload_ready_message
|
||||
call write_string
|
||||
pop hl
|
||||
call bload
|
||||
jp monitor_warm_start
|
||||
;
|
||||
;Binary memory dump. Need address of start of dump and no. bytes
|
||||
bdump_jump: ld hl,bdump_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,dump_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld hl,bdump_ready_message
|
||||
call write_string
|
||||
call get_char
|
||||
pop hl
|
||||
call bdump
|
||||
jp monitor_warm_start
|
||||
;Disk read. Need memory address to place data, LBA of sector to read
|
||||
diskrd_jump: ld hl,diskrd_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,LBA_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld e,00h
|
||||
pop hl
|
||||
call disk_read
|
||||
jp monitor_warm_start
|
||||
diskwr_jump: ld hl,diskwr_message
|
||||
call write_string
|
||||
ld hl,address_entry_msg
|
||||
call write_string
|
||||
call address_entry
|
||||
call write_newline
|
||||
push hl
|
||||
ld hl,LBA_entry_string
|
||||
call write_string
|
||||
call decimal_entry
|
||||
ld b,h
|
||||
ld c,l
|
||||
ld e,00h
|
||||
pop hl
|
||||
call disk_write
|
||||
jp monitor_warm_start
|
||||
boot_jump: ld hl,1100h
|
||||
ld bc,0000h
|
||||
ld e,00h
|
||||
call disk_read
|
||||
jp 1100h
|
||||
;Prints message for no match to entered command
|
||||
no_match_jump: ld hl,no_match_message
|
||||
call write_string
|
||||
ld hl, buffer
|
||||
call write_string
|
||||
jp monitor_warm_start
|
||||
;
|
||||
;Monitor data structures:
|
||||
;
|
||||
monitor_message: defm 27,91,50,74,27,91,72,13,10,"ROM Ver. 9",13,10,0
|
||||
no_match_message: defm "? ",0
|
||||
help_message: defm "Commands implemented:",13,10,0
|
||||
dump_message: defm "Displays a 256-byte block of memory.",13,10,0
|
||||
load_message: defm "Enter hex bytes starting at memory location.",13,10,0
|
||||
run_message: defm "Will jump to (execute) program at address entered.",13,10,0
|
||||
bload_message: defm "Loads a binary file into memory.",13,10,0
|
||||
bload_ready_message: defm 13,10,"Ready to receive, start transfer.",0
|
||||
bdump_message: defm "Dumps binary data from memory to serial port.",13,10,0
|
||||
bdump_ready_message: defm 13,10,"Ready to send, hit any key to start.",0
|
||||
diskrd_message: defm "Reads one sector from disk to memory.",13,10,0
|
||||
diskwr_message: defm "Writes one sector from memory to disk.",13,10,0
|
||||
;Strings for matching:
|
||||
dump_string: defm "dump",0
|
||||
load_string: defm "load",0
|
||||
jump_string: defm "jump",0
|
||||
run_string: defm "run",0
|
||||
question_string: defm "?",0
|
||||
help_string: defm "help",0
|
||||
bload_string: defm "bload",0
|
||||
bdump_string: defm "bdump",0
|
||||
diskrd_string: defm "diskrd",0
|
||||
diskwr_string: defm "diskwr",0
|
||||
boot_string: defm "boot",0
|
||||
no_match_string: defm 0,0
|
||||
;Table for matching strings to jumps
|
||||
parse_table: defw dump_string,dump_jump,load_string,load_jump
|
||||
defw jump_string,run_jump,run_string,run_jump
|
||||
defw question_string,help_jump,help_string,help_jump
|
||||
defw bload_string,bload_jump,bdump_string,bdump_jump
|
||||
defw diskrd_string,diskrd_jump,diskwr_string,diskwr_jump
|
||||
defw boot_string,boot_jump
|
||||
defw no_match_string,no_match_jump
|
||||
|
Loading…
Reference in New Issue
Block a user