Split out project into serveral projects
This commit is contained in:
@@ -1,11 +1,15 @@
|
|||||||
BUILDDIR=.build
|
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))
|
BIN = $(patsubst %,$(BUILDDIR)/%,$(_BIN))
|
||||||
|
|
||||||
$(BUILDDIR)/%.bin: src/%.z80
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(BUILDDIR) $(BIN)
|
||||||
|
|
||||||
|
$(BUILDDIR)/%.bin: src/%.z80 | $(BUILDDIR)
|
||||||
@zasm -w -i $< -o $@
|
@zasm -w -i $< -o $@
|
||||||
|
|
||||||
$(BUILDDIR)/%.COM: src/%.z80
|
$(BUILDDIR)/%.COM: src/%.z80 | $(BUILDDIR)
|
||||||
@zasm -w -i $< -o $@
|
@zasm -w -i $< -o $@
|
||||||
|
|
||||||
$(BUILDDIR):
|
$(BUILDDIR):
|
||||||
@@ -13,6 +17,5 @@ $(BUILDDIR):
|
|||||||
|
|
||||||
all: $(BUILDDIR) $(BIN)
|
all: $(BUILDDIR) $(BIN)
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
clean:
|
||||||
@rm -df $(BUILDDIR)/*.bin $(BUILDDIR)/*.COM $(BUILDDIR)/*.lst .build
|
@rm -df $(BUILDDIR)/*.bin $(BUILDDIR)/*.COM $(BUILDDIR)/*.lst .build
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
-251
@@ -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
|
|
||||||
;
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-696
@@ -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
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user