Converted monitor to asz80 to allow a more modular design
This commit is contained in:
parent
ae998ec3dc
commit
789b07d893
70
Makefile
70
Makefile
|
@ -1,19 +1,67 @@
|
|||
BUILDDIR=.build
|
||||
_BIN= rom_monitor.bin ram_monitor.bin MONITOR.COM
|
||||
BIN = $(patsubst %,$(BUILDDIR)/%,$(_BIN))
|
||||
# BUILDDIR=.build
|
||||
# _BIN= rom_monitor.bin ram_monitor.bin MONITOR.COM
|
||||
# BIN = $(patsubst %,$(BUILDDIR)/%,$(_BIN))
|
||||
#
|
||||
# .PHONY: all clean
|
||||
#
|
||||
# all: $(BUILDDIR) $(BIN)
|
||||
#
|
||||
# $(BUILDDIR)/%.bin: src/%.z80 | $(BUILDDIR)
|
||||
# @zasm -w -i $< -o $@
|
||||
#
|
||||
# $(BUILDDIR)/%.COM: src/%.z80 | $(BUILDDIR)
|
||||
# @zasm -w -i $< -o $@
|
||||
#
|
||||
# $(BUILDDIR):
|
||||
# @mkdir $(BUILDDIR)
|
||||
#
|
||||
# clean:
|
||||
# @rm -df $(BUILDDIR)/*.bin $(BUILDDIR)/*.COM $(BUILDDIR)/*.lst .build
|
||||
|
||||
AS = sdasz80
|
||||
LD = sdldz80
|
||||
OBJCOPY = sdobjcopy
|
||||
|
||||
BUILD = .build
|
||||
|
||||
TARGET_ROM = rom_monitor
|
||||
TARGET_COM = MONITOR
|
||||
|
||||
ASFLAGS = -plosff
|
||||
|
||||
SRC_ROM = \
|
||||
src/monitor.s \
|
||||
src/rom.s
|
||||
SRC_COM = \
|
||||
src/monitor.s \
|
||||
src/cpm.s
|
||||
|
||||
OBJ_ROM = $(addprefix $(BUILD)/, $(notdir $(SRC_ROM:.s=.rel)))
|
||||
OBJ_COM = $(addprefix $(BUILD)/, $(notdir $(SRC_COM:.s=.rel)))
|
||||
vpath %.s $(sort $(dir $(SRC_ROM)))
|
||||
vpath %.s $(sort $(dir $(SRC_COM)))
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(BUILDDIR) $(BIN)
|
||||
all: $(BUILD) $(BUILD)/$(TARGET_ROM).bin $(BUILD)/$(TARGET_COM).COM
|
||||
|
||||
$(BUILDDIR)/%.bin: src/%.z80 | $(BUILDDIR)
|
||||
@zasm -w -i $< -o $@
|
||||
$(BUILD)/%.rel: %.s Makefile | $(BUILD)
|
||||
$(AS) $(ASFLAGS) $@ $<
|
||||
|
||||
$(BUILDDIR)/%.COM: src/%.z80 | $(BUILDDIR)
|
||||
@zasm -w -i $< -o $@
|
||||
$(BUILD)/$(TARGET_ROM).ihx: $(OBJ_ROM)
|
||||
$(LD) -im1 $@ $(OBJ_ROM) -b BOOT=0x0000
|
||||
|
||||
$(BUILDDIR):
|
||||
@mkdir $(BUILDDIR)
|
||||
$(BUILD)/$(TARGET_COM).ihx: $(OBJ_COM)
|
||||
$(LD) -im1 $@ $(OBJ_COM) -b BOOT=0x0100 -b MAIN=0xE200
|
||||
|
||||
%.bin: %.ihx
|
||||
$(OBJCOPY) -I ihex -O binary $< $@
|
||||
|
||||
%.COM: %.ihx
|
||||
$(OBJCOPY) -I ihex -O binary $< $@
|
||||
|
||||
$(BUILD):
|
||||
mkdir $@
|
||||
|
||||
clean:
|
||||
@rm -df $(BUILDDIR)/*.bin $(BUILDDIR)/*.COM $(BUILDDIR)/*.lst .build
|
||||
rm -fr $(BUILD)
|
||||
|
|
25
src/cpm.s
Normal file
25
src/cpm.s
Normal file
|
@ -0,0 +1,25 @@
|
|||
bios .equ 0x0000
|
||||
main .equ 0xE200
|
||||
|
||||
.globl monitor_start
|
||||
.globl s_MAIN
|
||||
.globl end
|
||||
|
||||
.area BOOT
|
||||
ld hl, #origin ; Start of code to transfer
|
||||
ld bc, #end-main+1 ; Size of code transfer
|
||||
ld de, #s_MAIN ; Target of transfer
|
||||
ldir ; Z80 transfer instruction
|
||||
jp monitor_start
|
||||
|
||||
origin:
|
||||
|
||||
.area MAIN
|
||||
system_jump:
|
||||
jp bios
|
||||
|
||||
system_string:
|
||||
.str /system\0/
|
||||
|
||||
.area PARSE_TABLE
|
||||
.dw system_string, system_jump
|
756
src/monitor.s
Normal file
756
src/monitor.s
Normal file
|
@ -0,0 +1,756 @@
|
|||
; Variables
|
||||
current_location .equ 0x1000
|
||||
line_count .equ 0x1002
|
||||
byte_count .equ 0x1003
|
||||
value_pointer .equ 0x1004
|
||||
current_value .equ 0x1006
|
||||
; Buffer up to stack
|
||||
buffer .equ 0x1008
|
||||
|
||||
.area BOOT
|
||||
.area MAIN
|
||||
; 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
|
||||
write_char_loop:
|
||||
in a, (0x03) ; Check if OK to send
|
||||
and #0x01
|
||||
jp z, write_char_loop
|
||||
|
||||
ld a, b
|
||||
out (0x02), a ; Send the char
|
||||
|
||||
ret
|
||||
|
||||
; Subroutine to write a zero-terminated string to serial output
|
||||
; Pass address of string in HL register
|
||||
; No error checking
|
||||
write_string::
|
||||
in a, (0x03) ; Check if OK to send
|
||||
and #0x01
|
||||
jp z, write_string
|
||||
|
||||
ld a, (hl) ; Get char from string
|
||||
and a ; Check if 0
|
||||
ret z ; Yes, finished
|
||||
out (0x02), a ; No, write char to output
|
||||
inc hl ; Next char in string
|
||||
jp write_string
|
||||
|
||||
; 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) ; Check if char is available
|
||||
and #0x01
|
||||
jp z,bload
|
||||
|
||||
in a, (0x1E) ; Read char
|
||||
ld (hl), a ; Write char to memory
|
||||
inc hl
|
||||
dec bc ; Decrement 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, (0x03) ; Check if OK to send
|
||||
and #0x01
|
||||
jp z, bdump
|
||||
|
||||
ld a, (hl) ; Load from memory
|
||||
out (0x02), a
|
||||
inc hl
|
||||
dec bc ; Decrement counter
|
||||
ld a, b ; Need to test BC 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, #0x00 ; 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) ; Check if char is available
|
||||
and #0x01
|
||||
jp z, get_line_next_char
|
||||
|
||||
in a, (0x1E) ; Read char
|
||||
cp #0x0D ; Check if return
|
||||
ret z
|
||||
|
||||
cp #0x7F ; Check if backspace (VT102 keys)
|
||||
jp z, get_line_backspace
|
||||
|
||||
cp #0x08 ; Check if backspace (ANSI keys)
|
||||
jp z, get_line_backspace
|
||||
|
||||
call write_char ; Display the char
|
||||
ld (de), a ; Store char in buffer
|
||||
inc de
|
||||
inc c
|
||||
ld a, #0x00 ; leaves a zero-terminated string in buffer
|
||||
ld (de), a
|
||||
jp get_line_next_char
|
||||
|
||||
get_line_backspace:
|
||||
ld a, c ; Check current position in line
|
||||
cp #0x00 ; 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, #0x00 ; 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, #0x00 ; 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 #0x0F ; 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, #0x00 ; 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, #0x0f ; No. of valid characters in table - 1.
|
||||
ld c, #0x00 ; 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, #0xFF ; 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 #0xFF ; 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 #0xFF ; 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:
|
||||
.ascii /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, #0x00 ; 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 #0x30 ; ASCII value of zero char
|
||||
jp m, decimal_error ; Error if char value less than 030h
|
||||
cp #0x0A ; 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:
|
||||
.dw 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, #0x00
|
||||
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 #0x0f ; Yes, start new line
|
||||
jp z, dump_new_line
|
||||
inc a ; No, increment line count
|
||||
ld (line_count), a
|
||||
ld a, #0x20 ; Print space
|
||||
call write_char
|
||||
jp dump_next_byte ; Continue
|
||||
dump_new_line:
|
||||
ld a, #0x00 ; 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, #0x20 ; Space
|
||||
call write_char
|
||||
jp dump_next_byte ; Now write 16 bytes
|
||||
dump_done:
|
||||
ld a, #0x00
|
||||
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 #0x0D ; Return?
|
||||
jp z, load_done ; Yes, quit
|
||||
ld (buffer), a
|
||||
call get_char
|
||||
cp #0x0D ; 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 #0x0F ; Yes, start new line
|
||||
jp z, load_new_line
|
||||
inc a ; No, increment line count
|
||||
ld (line_count), a
|
||||
ld a, #0x20 ; Print space
|
||||
call write_char
|
||||
jp load_next_char ; Continue
|
||||
load_new_line:
|
||||
ld a, #0x00 ; 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 #0x01 ; 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, #0x0D ; ASCII carriage return character
|
||||
call write_char
|
||||
ld a, #0x0A ; 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 (0x0B), a ; lba bits 0 - 7
|
||||
ld a, b
|
||||
out (0x0C), a ; lba bits 8 - 15
|
||||
ld a, e
|
||||
out (0x0D), a ; lba bits 16 - 23
|
||||
ld a, #0x20 ; Read sector command
|
||||
out (0x0F), a
|
||||
rd_wait_for_DRQ_set:
|
||||
in a, (0x0F) ; Read status
|
||||
and #0x08 ; DRQ bit
|
||||
jp z, rd_wait_for_DRQ_set ; Loop until bit set
|
||||
read_loop:
|
||||
in a, (0x08) ; Get data
|
||||
ld (hl), a
|
||||
inc hl
|
||||
in a, (0x0f) ; Check status
|
||||
and #0x08 ; 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 (0x0b), a ; lba bits 0 - 7
|
||||
ld a, b
|
||||
out (0x0c), a ; lba bits 8 - 15
|
||||
ld a, e
|
||||
out (0x0d), a ; lba bits 16 - 23
|
||||
ld a, #0x30 ; Write sector command
|
||||
out (0x0f), a
|
||||
wr_wait_for_DRQ_set:
|
||||
in a, (0x0f) ; Read status
|
||||
and #0x08 ; DRQ bit
|
||||
jp z, wr_wait_for_DRQ_set ; Loop until bit set
|
||||
write_loop:
|
||||
ld a, (hl)
|
||||
out (0x08), a ; Write data
|
||||
inc hl
|
||||
in a, (0x0f) ; Read status
|
||||
and #0x08 ; Check DRQ bit
|
||||
jp nz, write_loop ;write until bit cleared
|
||||
ret
|
||||
|
||||
; Strings used in subroutines
|
||||
length_entry_string:
|
||||
.str /Enter length of file to load (decimal): \0/
|
||||
dump_entry_string:
|
||||
.str /Enter no. of bytes to dump (decimal): \0/
|
||||
LBA_entry_string:
|
||||
.str /Enter LBA (decimal, 0 to 65535): \0/
|
||||
erase_char_string: ;ANSI sequence for backspace, erase to end of line.
|
||||
.db #0x08
|
||||
.db #0x1B
|
||||
.str /[K\0/
|
||||
address_entry_msg:
|
||||
.str /Enter 4-digit hex address (use upper-case A through F): \0/
|
||||
address_error_msg:
|
||||
.str /\r\nError: invalid hex character, try again: \0/
|
||||
data_entry_msg:
|
||||
.str /Enter hex bytes, hit return when finished.\r\n\0/
|
||||
data_error_msg:
|
||||
.str /Error: invalid hex byte.\r\n\0/
|
||||
decimal_error_msg:
|
||||
.str /\r\nError: 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, #0x03e ; 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 #0x00 ; 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 #0x00 ; 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 #0x00 ; Exit if no_match string
|
||||
jp z, help_done
|
||||
push bc ; Write_char uses b register
|
||||
ld a, #0x20 ; 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, #0x00
|
||||
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, #0x00
|
||||
pop hl
|
||||
call disk_write
|
||||
jp monitor_start
|
||||
|
||||
; 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:
|
||||
no_match_message:
|
||||
.str /? \0/
|
||||
help_message:
|
||||
.str /Commands implemented:\r\n\0/
|
||||
dump_message:
|
||||
.str /Displays a 256-byte block of memory.\r\n\0/
|
||||
load_message:
|
||||
.str /Enter hex bytes starting at memory location.\r\n\0/
|
||||
run_message:
|
||||
.str /Will jump to (execute) program at address entered.\r\n\0/
|
||||
bload_message:
|
||||
.str /Loads a binary file into memory.\r\n\0/
|
||||
bload_ready_message:
|
||||
.str /\r\nReady to receive, start transfer.\0/
|
||||
bdump_message:
|
||||
.str /Dumps binary data from memory to serial port.\r\n\0/
|
||||
bdump_ready_message:
|
||||
.str /\r\nReady to send, hit any key to start.\0/
|
||||
diskrd_message:
|
||||
.str /Reads one sector from disk to memory.\r\n\0/
|
||||
diskwr_message:
|
||||
.str /Writes one sector from memory to disk.\r\n\0/
|
||||
|
||||
; Strings for matching:
|
||||
dump_string:
|
||||
.str /dump\0/
|
||||
load_string:
|
||||
.str /load\0/
|
||||
jump_string:
|
||||
.str /jump\0/
|
||||
run_string:
|
||||
.str /run\0/
|
||||
question_string:
|
||||
.str /?\0/
|
||||
help_string:
|
||||
.str /help\0/
|
||||
bload_string:
|
||||
.str /bload\0/
|
||||
bdump_string:
|
||||
.str /bdump\0/
|
||||
diskrd_string:
|
||||
.str /diskrd\0/
|
||||
diskwr_string:
|
||||
.str /diskwr\0/
|
||||
no_match_string:
|
||||
.dw #0x00
|
||||
|
||||
;Table for matching strings to jumps
|
||||
.area PARSE_TABLE
|
||||
parse_table:
|
||||
.dw dump_string, dump_jump
|
||||
.dw load_string, load_jump
|
||||
.dw jump_string, run_jump
|
||||
.dw run_string, run_jump
|
||||
.dw question_string, help_jump
|
||||
.dw help_string, help_jump
|
||||
.dw bload_string, bload_jump
|
||||
.dw bdump_string, bdump_jump
|
||||
.dw diskrd_string, diskrd_jump
|
||||
.dw diskwr_string, diskwr_jump
|
||||
.area PARSE_TABLE_END
|
||||
.dw no_match_string, no_match_jump
|
||||
|
||||
end::
|
49
src/rom.s
Normal file
49
src/rom.s
Normal file
|
@ -0,0 +1,49 @@
|
|||
stack .equ 0x10ff
|
||||
bootloader .equ 0x1100
|
||||
|
||||
.globl disk_read
|
||||
.globl write_string
|
||||
.globl monitor_start
|
||||
|
||||
.area BOOT
|
||||
start:
|
||||
ld sp, #stack
|
||||
call initialize
|
||||
ld hl, #monitor_message
|
||||
call write_string
|
||||
jp monitor_start
|
||||
|
||||
monitor_message:
|
||||
.db #0x1B
|
||||
.str /[2J/
|
||||
.db #0x1B
|
||||
.str /[H\r\nROM Ver. 9\r\n\0/
|
||||
|
||||
; Clear out bufferend input
|
||||
initialize:
|
||||
initialize_loop_1:
|
||||
in a, (0x1E) ; Read char from keyboard
|
||||
in a, (0x1F) ; Check if there is another char
|
||||
and #0x01
|
||||
jp nz, initialize_loop_1
|
||||
; Wait for GPU be ready
|
||||
initialize_loop_2:
|
||||
in a, (0x03) ; If GPU is not ready value is 0xFF
|
||||
and #0x02
|
||||
jp nz, initialize_loop_2
|
||||
|
||||
ret
|
||||
|
||||
.area MAIN
|
||||
boot_jump:
|
||||
ld hl, #bootloader ; Set destination
|
||||
ld bc, #0x0000 ; Set lba of bootloader
|
||||
ld e, #0x00
|
||||
call disk_read
|
||||
jp bootloader
|
||||
|
||||
boot_string:
|
||||
.str /boot\0/
|
||||
|
||||
.area PARSE_TABLE
|
||||
.dw boot_string, boot_jump
|
Loading…
Reference in New Issue
Block a user