Reorganized folders, added pal-adapter io device folder
This commit is contained in:
11
pat80-computer/software/z80-assembly/os/Makefile
Normal file
11
pat80-computer/software/z80-assembly/os/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
os:
|
||||
@echo "Building PAT80 rom..."
|
||||
@z80asm -i main.asm -o rom.bin || (exit 1)
|
||||
@echo "Generating label lookup table..."
|
||||
@z80asm -i main.asm -o rom.bin -L 2>&1 | grep "Sys_" > abi-generated.asm
|
||||
@echo "PAT80 Rom size:"
|
||||
@du -h rom.bin
|
||||
@echo "Stretching rom to EEPROM size..."
|
||||
@dd if=/dev/zero of=rom.bin bs=1 count=0 seek=8192
|
||||
@echo "Writing to EEPROM..."
|
||||
@minipro -w rom.bin -p "AT28C64B"
|
15
pat80-computer/software/z80-assembly/os/README.md
Normal file
15
pat80-computer/software/z80-assembly/os/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Pat80 Operating System and Memory Monitor
|
||||
|
||||
## Intro
|
||||
This folder contains the Pat80 Operating System.
|
||||
It is a System Monitor that makes available also some system API to access hardware (monitor, sound, keyboard, parallel terminal...).
|
||||
|
||||
## Build
|
||||
### Requirements
|
||||
z80asm, minipro
|
||||
### Make
|
||||
The os can be build issuing command `make`.
|
||||
Two files will be generated:
|
||||
- `rom.bin` is the rom file to be flashed on the eeprom
|
||||
- `abi-generated.asm` is the file to be included in any Pat80 application to access system APIs (see README.md in ../applications/)
|
||||
The build routine will then try to write the rom to a MiniPRO.
|
@ -0,0 +1,6 @@
|
||||
Sys_ABI: equ $0003
|
||||
Sys_Beep: equ $0013
|
||||
Sys_Print: equ $0007
|
||||
Sys_Printc: equ $000a
|
||||
Sys_Readc: equ $000d
|
||||
Sys_Readline: equ $0010
|
@ -0,0 +1,74 @@
|
||||
; Arduino terminal driver
|
||||
; @author Daniele Verducci
|
||||
|
||||
; config (IO port 0)
|
||||
TERM_DATA_REG: EQU IO_0
|
||||
TERM_DATA_AVAIL_REG: EQU IO_0 + 1
|
||||
|
||||
; variables
|
||||
TERM_VAR_SPACE: EQU DRV_VAR_SPACE + 128
|
||||
incoming_string: EQU TERM_VAR_SPACE
|
||||
|
||||
; functions
|
||||
|
||||
; Sends string
|
||||
; @param BC Pointer to a null-terminated string first character
|
||||
Term_print:
|
||||
ld a, (bc) ; bc is the pointer to passed string's first char
|
||||
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
|
||||
ret z ; if prev compare is true (Z flag set), string is finished, return
|
||||
out (TERM_DATA_REG),a ; output char
|
||||
inc bc ; increment bc to move to next char
|
||||
jp Term_print
|
||||
|
||||
; Writes a single character
|
||||
; @param A Value of character to print
|
||||
Term_printc:
|
||||
out (TERM_DATA_REG),a
|
||||
ret
|
||||
|
||||
; Reads a single character. 0s are ignored (can be used with keyboard).
|
||||
; Doesn't check DATA_AVAILABLE register of parallel port, because a 0 byte
|
||||
; is ignored anyway (it represents the ASCII NUL control char).
|
||||
; @return A The read character
|
||||
Term_readc:
|
||||
in a, (TERM_DATA_REG) ; reads a character
|
||||
add a, 0
|
||||
jp z, Term_readc ; if char is 0 (NULL), ignores it and waits for another character
|
||||
ret ; if not NULL, returns it in the a register
|
||||
|
||||
; Reads a line. 0s are ignored (can be used with keyboard)
|
||||
; Doesn't check DATA_AVAILABLE register of parallel port, because a 0 byte
|
||||
; is ignored anyway (it represents the ASCII NUL control char).
|
||||
; @return BC The pointer to a null-terminated read string
|
||||
Term_readline:
|
||||
ld bc, incoming_string ; this array will contain read string
|
||||
in a, (TERM_DATA_REG) ; reads a character
|
||||
; if char is 0 (ascii NULL), ignore it
|
||||
add a, 0
|
||||
jp z, Term_readline ; if 0 (= ascii NULL), ignores it and waits for another character
|
||||
; if char is a newline (CR or LF), line is finished.
|
||||
cp 10 ; CR
|
||||
jp z, term_readline_foundcr ; Found newline. Jump to term_readline_foundcr
|
||||
cp 13 ; LF
|
||||
jp z, term_readline_foundcr ; Found newline. Jump to term_readline_foundcr
|
||||
; At this point the read character is a valid ascii character
|
||||
ld (bc), a ; adds char to the read string
|
||||
inc bc ; point to next array position
|
||||
jp Term_readline
|
||||
term_readline_foundcr: ; called when carriage return was found (end of line)
|
||||
;ld (bc), 0 ; Null-terminate string
|
||||
ld bc, incoming_string ; Returns read string pointer
|
||||
ret
|
||||
|
||||
; Reads the first available byte on the serial port using the DATA register.
|
||||
; Waits for the Terminal DATA_AVAILABLE register to be non-zero before reading.
|
||||
; 0s are not ignored (cannot be used with keyboard)
|
||||
; Affects NO condition bits!
|
||||
; @return the available byte, even if 0
|
||||
Term_readb:
|
||||
in a, (TERM_DATA_AVAIL_REG)
|
||||
cp 0
|
||||
jp z, Term_readb
|
||||
in a, (TERM_DATA_REG) ; reads a byte
|
||||
ret
|
112
pat80-computer/software/z80-assembly/os/drivers/hd44780.asm
Normal file
112
pat80-computer/software/z80-assembly/os/drivers/hd44780.asm
Normal file
@ -0,0 +1,112 @@
|
||||
; HD44780 20x4 characters LCD display driver
|
||||
; @author Daniele Verducci
|
||||
;
|
||||
; USAGE:
|
||||
; STR: DB "Hello world!",0 <-- null terminated string
|
||||
; call Lcd_init <-- inits hd44780 controller
|
||||
; ld bc, STR <-- load pointer to string's first char in reg BC
|
||||
; call Lcd_print <-- this function will print the string
|
||||
|
||||
; LCD config (IO port 0)
|
||||
LCD_INSTR_REG: EQU IO_0
|
||||
LCD_DATA_REG: EQU IO_0 + 1
|
||||
|
||||
; constants
|
||||
LCD_LINES_LEFT: DB 0x80, 0xA8, 0x94, 0xBC ;array defining lcd command codes for the first char of every line
|
||||
|
||||
; variables
|
||||
LCD_VAR_SPACE: EQU DRV_VAR_SPACE
|
||||
lcd_cur_x: EQU LCD_VAR_SPACE
|
||||
lcd_cur_y: EQU lcd_cur_x + 1
|
||||
|
||||
; functions
|
||||
|
||||
; Inits the lcd display
|
||||
Lcd_init:
|
||||
;reset procedure
|
||||
ld a,0x38
|
||||
out (LCD_INSTR_REG),a
|
||||
ld a,0x08
|
||||
out (LCD_INSTR_REG),a
|
||||
ld a,0x01
|
||||
out (LCD_INSTR_REG),a
|
||||
|
||||
;init procedure
|
||||
ld a,0x38
|
||||
out (LCD_INSTR_REG),a
|
||||
ld a,0x0F
|
||||
out (LCD_INSTR_REG),a
|
||||
|
||||
ret
|
||||
|
||||
; Writes text starting from current cursor position
|
||||
; @param BC Pointer to a null-terminated string first character
|
||||
Lcd_print:
|
||||
ld a, (bc) ; bc is the pointer to passed string's first char
|
||||
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
|
||||
ret z ; if prev compare is true (Z flag set), string is finished, return
|
||||
out (LCD_DATA_REG),a ; output char
|
||||
inc bc ; increment bc to move to next char
|
||||
; increment x position
|
||||
ld a, (lcd_cur_x)
|
||||
inc a
|
||||
ld (lcd_cur_x), a
|
||||
; if x position > 19, increment y position and set x to 0
|
||||
ld a, (lcd_cur_x)
|
||||
cp 20
|
||||
jp nz, Lcd_print ; if x position is not 20, continue cycle
|
||||
ld a, 0
|
||||
ld (lcd_cur_x), a ; else set x to 0
|
||||
; and increment y
|
||||
ld a, (lcd_cur_y)
|
||||
inc a
|
||||
ld (lcd_cur_y), a
|
||||
; if y > 3 set y to 0
|
||||
cp 4 ; a still contains lcd_cur_y: compare with 4
|
||||
jp nz, Lcd_print ; if y position is not 4, continue cycle
|
||||
ld a, 0
|
||||
ld (lcd_cur_x), a ; else set y pos to 0
|
||||
jp Lcd_print
|
||||
|
||||
; Writes a single character at current cursror position
|
||||
; @param A Value of character to print
|
||||
Lcd_printc:
|
||||
out (LCD_DATA_REG),a
|
||||
ret
|
||||
|
||||
|
||||
; Set cursor position
|
||||
; @param B X-axis position (0 to 19)
|
||||
; @param C Y-axis position (0 to 3)
|
||||
Lcd_locate:
|
||||
ld a, b
|
||||
ld (lcd_cur_x), a
|
||||
ld a, c
|
||||
ld (lcd_cur_y), a
|
||||
call lcd_locate
|
||||
ret
|
||||
|
||||
; private
|
||||
; The cursor position can seem like black magic but it makes much more sense once you know that the HD44780 is designed to control a 40 character 4-line display. So if you have a 16×2 then you will only see the first 16 characters of the top two lines. Simple enough once you get used to taking these character positions into account. For example, in a 16×2 display, the first line is position 0-15. So 0x80 is the first position, 0x80 + 12 = 0x8C is the 13th (remember, they are zero indexed). The second line is a little tricky since it shows positions 64-79. Just add the position number (in decimal) to 0x80 (in hex) to get the hex address of the cursor position. The address is the command to move the cursor to that location. So, to move to the 13th position of the top line in a 16×2 Sparkfun Display, I would send “0xFE 0x8C”. The first byte warns the onboard microcontroller that a command is coming, and the second byte is the command.
|
||||
lcd_locate:
|
||||
; warns the lcd microcontroller that a command is coming
|
||||
ld a, 0xFE
|
||||
out (LCD_INSTR_REG),a
|
||||
; get line start command code from array
|
||||
ld hl, LCD_LINES_LEFT ; load array pointer
|
||||
ld bc, lcd_cur_y ; load line number
|
||||
ld b, 0 ; since line number is only 8 bit, clean garbage on the upper bit
|
||||
add hl, bc ; sum first array element to line number to access correct array element. Now hl contains array pointer
|
||||
; now sum x offset to the start line code to obtain lcd controller complete position code
|
||||
ld a, (lcd_cur_x) ; load cursor x position
|
||||
add a, (hl) ; sum cursor x pos to line start code. Result is in a
|
||||
out (LCD_INSTR_REG),a ; send cursor position to lcd controller
|
||||
ret
|
||||
|
||||
; Clears the screen
|
||||
Lcd_cls:
|
||||
ld a,0x01
|
||||
out (LCD_INSTR_REG),a ; clear display
|
||||
ld a,0x02
|
||||
out (LCD_INSTR_REG),a ; cursor to home (top left)
|
||||
ret
|
56
pat80-computer/software/z80-assembly/os/drivers/keyboard.asm
Normal file
56
pat80-computer/software/z80-assembly/os/drivers/keyboard.asm
Normal file
@ -0,0 +1,56 @@
|
||||
; Keyboard driver
|
||||
; @author Daniele Verducci
|
||||
;
|
||||
; Requires declaration of following pointers, one for every column of the keys grid:
|
||||
; KEYB_A0_REG
|
||||
; KEYB_A1_REG
|
||||
; KEYB_A2_REG
|
||||
; KEYB_A3_REG
|
||||
; KEYB_A4_REG
|
||||
; These address must be exclusive if a decoder is not present.
|
||||
;
|
||||
; Example (no decoder):
|
||||
; KEYB_A0_REG = 0000001
|
||||
; KEYB_A1_REG = 0000010
|
||||
; KEYB_A2_REG = 0000100
|
||||
; KEYB_A3_REG = 0001000
|
||||
; etc...
|
||||
;
|
||||
; Example (with decoder):
|
||||
; KEYB_A0_REG = 0000000
|
||||
; KEYB_A1_REG = 0000001
|
||||
; KEYB_A2_REG = 0000010
|
||||
; KEYB_A3_REG = 0000011
|
||||
; etc...
|
||||
|
||||
; Keyboard config (IO port 1)
|
||||
KEYB_A0_REG: EQU IO_1 + %00000001
|
||||
KEYB_A1_REG: EQU IO_1 + %00000010
|
||||
KEYB_A2_REG: EQU IO_1 + %00000100
|
||||
KEYB_A3_REG: EQU IO_1 + %00001000
|
||||
KEYB_A4_REG: EQU IO_1 + %00010000
|
||||
|
||||
; Reads the keyboard
|
||||
; @return: a 0-terminated array of keycodes representing the pressed keys
|
||||
Keyb_read:
|
||||
in a, (KEYB_A0_REG)
|
||||
cp 0
|
||||
jp z, _keyb_read_a1
|
||||
add a, %01000000
|
||||
call Lcd_printc ; A already contains char to print
|
||||
_loop:
|
||||
in a, (KEYB_A0_REG)
|
||||
cp 0
|
||||
jp nz, _loop
|
||||
ret
|
||||
_keyb_read_a1:
|
||||
in a, (KEYB_A1_REG)
|
||||
cp 0
|
||||
ret z
|
||||
add a, %01010000
|
||||
call Lcd_printc ; A already contains char to print
|
||||
_loop2:
|
||||
in a, (KEYB_A1_REG)
|
||||
cp 0
|
||||
jp nz, _loop2
|
||||
ret
|
55
pat80-computer/software/z80-assembly/os/drivers/sn76489.asm
Normal file
55
pat80-computer/software/z80-assembly/os/drivers/sn76489.asm
Normal file
@ -0,0 +1,55 @@
|
||||
; TI SN76489 sound chip display driver
|
||||
; @author Daniele Verducci
|
||||
;
|
||||
; USAGE:
|
||||
; call Snd_init <-- inits sound (and silences default tone)
|
||||
; call Snd_beep <-- system beep
|
||||
|
||||
; Sound card is on port 1
|
||||
SND_DATA_REG: EQU IO_1
|
||||
|
||||
; Init device (silence all channels)
|
||||
; Bits meaning:
|
||||
; 1 R0 R1 R2 A0 A1 A2 A3
|
||||
; Bit0 is 1
|
||||
; Bit1,2,3 select the channel: 001, 011, 101, 111(noise)
|
||||
; Bit4,5,6,7 selecy the attenuation (0000=full volume, 1111=silent)
|
||||
Snd_init:
|
||||
; silence ch1
|
||||
ld a,%10011111
|
||||
out (SND_DATA_REG),a
|
||||
; silence ch2
|
||||
ld a,%10111111
|
||||
out (SND_DATA_REG),a
|
||||
; silence ch3
|
||||
ld a,%11011111
|
||||
out (SND_DATA_REG),a
|
||||
; silence noise ch
|
||||
ld a,%11111111
|
||||
out (SND_DATA_REG),a
|
||||
ret
|
||||
|
||||
; Plays the system beep.
|
||||
Snd_beep:
|
||||
; ch1 max volume
|
||||
ld a,%10010000
|
||||
out (SND_DATA_REG),a
|
||||
; play beep freq
|
||||
ld a,%10000000
|
||||
out (SND_DATA_REG),a
|
||||
ld a,%00001000
|
||||
out (SND_DATA_REG),a
|
||||
; wait
|
||||
ld bc, (TIME_DUR_MILLIS * 100)
|
||||
call Time_delay55
|
||||
; silence ch1
|
||||
ld a,%10011111
|
||||
out (SND_DATA_REG),a
|
||||
ret
|
||||
|
||||
; Sets the attenuation value for a channel
|
||||
; @param a Channel (0, 1, 2, 3(Noise))
|
||||
; @param c Attenuation (0 to 16)
|
||||
; Snd_setAtt:
|
||||
; cp a, 0
|
||||
|
103
pat80-computer/software/z80-assembly/os/drivers/vgax.asm
Normal file
103
pat80-computer/software/z80-assembly/os/drivers/vgax.asm
Normal file
@ -0,0 +1,103 @@
|
||||
; Vgax display driver
|
||||
; @author Daniele Verducci
|
||||
;
|
||||
; Requires declaration of following pointers:
|
||||
; VGAX_INSTR_REG
|
||||
; VGAX_DATA_REG
|
||||
|
||||
; variables
|
||||
lcd_cur_x: EQU DRV_VAR_SPACE
|
||||
lcd_cur_y: EQU lcd_cur_x + 1
|
||||
|
||||
; functions
|
||||
|
||||
; Inits the lcd display
|
||||
Lcd_init:
|
||||
;reset procedure
|
||||
ld a,0x38
|
||||
out (LCD_INSTR_REG),a
|
||||
ld a,0x08
|
||||
out (LCD_INSTR_REG),a
|
||||
ld a,0x01
|
||||
out (LCD_INSTR_REG),a
|
||||
|
||||
;init procedure
|
||||
ld a,0x38
|
||||
out (LCD_INSTR_REG),a
|
||||
ld a,0x0F
|
||||
out (LCD_INSTR_REG),a
|
||||
|
||||
ret
|
||||
|
||||
; Writes text starting from current cursor position
|
||||
; @param BC Pointer to a null-terminated string first character
|
||||
Lcd_print:
|
||||
ld a, (bc) ; bc is the pointer to passed string's first char
|
||||
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
|
||||
ret z ; if prev compare is true (Z flag set), string is finished, return
|
||||
out (LCD_DATA_REG),a ; output char
|
||||
inc bc ; increment bc to move to next char
|
||||
; increment x position
|
||||
ld a, (lcd_cur_x)
|
||||
inc a
|
||||
ld (lcd_cur_x), a
|
||||
; if x position > 19, increment y position and set x to 0
|
||||
ld a, (lcd_cur_x)
|
||||
cp 20
|
||||
jp nz, Lcd_print ; if x position is not 20, continue cycle
|
||||
ld a, 0
|
||||
ld (lcd_cur_x), a ; else set x to 0
|
||||
; and increment y
|
||||
ld a, (lcd_cur_y)
|
||||
inc a
|
||||
ld (lcd_cur_y), a
|
||||
; if y > 3 set y to 0
|
||||
cp 4 ; a still contains lcd_cur_y: compare with 4
|
||||
jp nz, Lcd_print ; if y position is not 4, continue cycle
|
||||
ld a, 0
|
||||
ld (lcd_cur_x), a ; else set y pos to 0
|
||||
jp Lcd_print
|
||||
ret
|
||||
|
||||
; Writes a single character at current cursror position
|
||||
; @param A Value of character to print
|
||||
Lcd_printc:
|
||||
out (LCD_DATA_REG),a
|
||||
ret
|
||||
|
||||
|
||||
; Set cursor position
|
||||
; @param B X-axis position (0 to 19)
|
||||
; @param C Y-axis position (0 to 3)
|
||||
Lcd_locate:
|
||||
ld a, b
|
||||
ld (lcd_cur_x), a
|
||||
ld a, c
|
||||
ld (lcd_cur_y), a
|
||||
call lcd_locate
|
||||
ret
|
||||
|
||||
; private
|
||||
; The cursor position can seem like black magic but it makes much more sense once you know that the HD44780 is designed to control a 40 character 4-line display. So if you have a 16×2 then you will only see the first 16 characters of the top two lines. Simple enough once you get used to taking these character positions into account. For example, in a 16×2 display, the first line is position 0-15. So 0x80 is the first position, 0x80 + 12 = 0x8C is the 13th (remember, they are zero indexed). The second line is a little tricky since it shows positions 64-79. Just add the position number (in decimal) to 0x80 (in hex) to get the hex address of the cursor position. The address is the command to move the cursor to that location. So, to move to the 13th position of the top line in a 16×2 Sparkfun Display, I would send “0xFE 0x8C”. The first byte warns the onboard microcontroller that a command is coming, and the second byte is the command.
|
||||
lcd_locate:
|
||||
; warns the lcd microcontroller that a command is coming
|
||||
ld a, 0xFE
|
||||
out (LCD_INSTR_REG),a
|
||||
; get line start command code from array
|
||||
ld hl, LCD_LINES_LEFT ; load array pointer
|
||||
ld bc, lcd_cur_y ; load line number
|
||||
ld b, 0 ; since line number is only 8 bit, clean garbage on the upper bit
|
||||
add hl, bc ; sum first array element to line number to access correct array element. Now hl contains array pointer
|
||||
; now sum x offset to the start line code to obtain lcd controller complete position code
|
||||
ld a, (lcd_cur_x) ; load cursor x position
|
||||
add a, (hl) ; sum cursor x pos to line start code. Result is in a
|
||||
out (LCD_INSTR_REG),a ; send cursor position to lcd controller
|
||||
ret
|
||||
|
||||
; Clears the screen
|
||||
Lcd_cls:
|
||||
ld a,0x01
|
||||
out (LCD_INSTR_REG),a ; clear display
|
||||
ld a,0x02
|
||||
out (LCD_INSTR_REG),a ; cursor to home (top left)
|
||||
ret
|
23
pat80-computer/software/z80-assembly/os/libs/strings.asm
Normal file
23
pat80-computer/software/z80-assembly/os/libs/strings.asm
Normal file
@ -0,0 +1,23 @@
|
||||
; Strings manipulation library
|
||||
; @author Daniele Verducci
|
||||
|
||||
; Transforms case to upper
|
||||
|
||||
Strings_strToUpper:
|
||||
; TODO
|
||||
ret
|
||||
|
||||
Strings_strToLower:
|
||||
; TODO
|
||||
ret
|
||||
|
||||
; Transforms character in A to uppercase. If is not a character, returns as is
|
||||
; @param A character to transform
|
||||
; @return A upper char
|
||||
Strings_charToUpper:
|
||||
; TODO
|
||||
ret
|
||||
|
||||
Strings_charToLower:
|
||||
; TODO
|
||||
ret
|
21
pat80-computer/software/z80-assembly/os/libs/time.asm
Normal file
21
pat80-computer/software/z80-assembly/os/libs/time.asm
Normal file
@ -0,0 +1,21 @@
|
||||
; Time library
|
||||
; @author Daniele Verducci
|
||||
|
||||
; Duration in cpu cycles / 55 (change these values based on CPU frequency)
|
||||
TIME_DUR_SECOND: EQU 2545
|
||||
TIME_DUR_MILLIS: EQU 3
|
||||
|
||||
; Wait bc * 55 states
|
||||
; Use 1 iteration as delay between I/O bus writes
|
||||
; @param bc The number of iterations. Each iteration is 55 states long.
|
||||
Time_delay55:
|
||||
ret
|
||||
bit 0,a ; 8
|
||||
bit 0,a ; 8
|
||||
bit 0,a ; 8
|
||||
and 255 ; 7
|
||||
dec bc ; 6
|
||||
ld a,c ; 4
|
||||
or b ; 4
|
||||
jp nz,Time_delay55 ; 10, total = 55 states/iteration
|
||||
ret
|
2
pat80-computer/software/z80-assembly/os/main-dev.asm
Normal file
2
pat80-computer/software/z80-assembly/os/main-dev.asm
Normal file
@ -0,0 +1,2 @@
|
||||
org 0xA000 ; Set starting position to ram
|
||||
include 'main.asm'
|
113
pat80-computer/software/z80-assembly/os/main.asm
Normal file
113
pat80-computer/software/z80-assembly/os/main.asm
Normal file
@ -0,0 +1,113 @@
|
||||
jp Sysinit ; Startup vector: DO NOT MOVE! Must be the first instruction
|
||||
|
||||
; Pat80 BIOS v0.01
|
||||
; @author: Daniele Verducci
|
||||
;
|
||||
; MEMORY MAP
|
||||
; ROM is at 0x0000
|
||||
; RAM is at 0x8000
|
||||
; SYSTEM VAR SPACE: 0x8000 - 0x8FFF (4kb)
|
||||
; DRIVERS VAR SPACE: 0x9000 - 0x9FFF (4kb)
|
||||
; APPLICATION VAR SPACE: 0xA000 - 0xFFFF (24kb)
|
||||
; I/O MAP
|
||||
; I/O 0 (0x00 - 0x1F) Parallel terminal (uses addr 0x00 only)
|
||||
; I/O 1 (0x20 - 0x3F) Sound card (uses addr 0x20 only)
|
||||
; I/O 2 (0x40 - 0x5F)
|
||||
; I/O 3 (0x60 - 0x7F)
|
||||
; I/O 4 (0x80 - 0x9F)
|
||||
; I/O 5 (0xA0 - 0xBF)
|
||||
; I/O 6 (0xC0 - 0xDF)
|
||||
; I/O 7 (0xE0 - 0xFF)
|
||||
|
||||
; **** SYSTEM CALLS ****
|
||||
; System calls provide access to low level functions (input from keyboard, output to screen etc).
|
||||
; The name starts always with Sys_
|
||||
|
||||
; Returns ABI version.
|
||||
; (ABI -> https://en.wikipedia.org/wiki/Application_binary_interface)
|
||||
; Any Pat80 application should check the ABI version on startup, and refuse to run if not compatible.
|
||||
; @return bc the ABI version
|
||||
Sys_ABI:
|
||||
ld bc, 0
|
||||
ret
|
||||
|
||||
; Prints string
|
||||
; @param BC Pointer to a null-terminated string first character
|
||||
Sys_Print:
|
||||
jp Term_print
|
||||
|
||||
; Writes a single character
|
||||
; @param A Value of character to print
|
||||
Sys_Printc:
|
||||
jp Term_printc
|
||||
|
||||
; Reads a single character
|
||||
; @return A The read character
|
||||
Sys_Readc:
|
||||
jp Term_readc
|
||||
|
||||
; Reads a line
|
||||
; @return BC The pointer to a null-terminated read string
|
||||
Sys_Readline:
|
||||
jp Term_readline
|
||||
|
||||
; Emits system beep
|
||||
Sys_Beep:
|
||||
jp Snd_beep
|
||||
|
||||
|
||||
|
||||
; MEMORY CONFIGURATION
|
||||
SYS_VAR_SPACE: EQU 0x8000
|
||||
DRV_VAR_SPACE: EQU 0x9000
|
||||
APP_SPACE: EQU 0xA000
|
||||
|
||||
; SYSTEM CONFIGURATION
|
||||
IO_0: EQU 0x00
|
||||
IO_1: EQU 0x20
|
||||
IO_2: EQU 0x40
|
||||
IO_3: EQU 0x60
|
||||
IO_4: EQU 0x80
|
||||
IO_5: EQU 0xA0
|
||||
IO_6: EQU 0xC0
|
||||
IO_7: EQU 0xE0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;include 'drivers/hd44780.asm'
|
||||
;include 'drivers/keyboard.asm'
|
||||
include 'drivers/arduino_terminal.asm'
|
||||
include 'drivers/sn76489.asm'
|
||||
include 'monitor.asm'
|
||||
include 'libs/time.asm'
|
||||
;include 'tests/sndtest.asm'
|
||||
|
||||
|
||||
; **** SYSTEM INITIALIZATION ****
|
||||
Sysinit:
|
||||
; Init snd driver
|
||||
call Snd_init
|
||||
|
||||
; Init video
|
||||
; TODO
|
||||
|
||||
; Wait for audio amp to unmute
|
||||
ld bc, TIME_DUR_SECOND
|
||||
call Time_delay55
|
||||
|
||||
; Play startup sound
|
||||
call Sys_Beep
|
||||
|
||||
; Run memory monitor
|
||||
call Monitor_main
|
||||
|
||||
; DEBUG: Echo chars
|
||||
; loop:
|
||||
; call Term_readc
|
||||
; call Term_printc
|
||||
; jp loop
|
||||
|
||||
halt
|
519
pat80-computer/software/z80-assembly/os/monitor.asm
Normal file
519
pat80-computer/software/z80-assembly/os/monitor.asm
Normal file
@ -0,0 +1,519 @@
|
||||
; Pat80 Memory Monitor
|
||||
; @author Daniele Verducci
|
||||
;
|
||||
; Monitor commands (CMD $arg):
|
||||
; H (HELP) Shows available commands
|
||||
; D (DUMP) $pos Dumps bytes of memory starting at $pos
|
||||
; S (SET) $pos $val Replaces byte at $pos with $val
|
||||
; L (LOAD) $pos $val
|
||||
; R (RUN) $pos Starts executing code from $pos
|
||||
; A (ADB) Enters in Assembly Depoy Bridge mode: loads all the incoming bytes in application memory and starts executing.
|
||||
; The commands are entered with a single letter and the program completes the command
|
||||
|
||||
include 'libs/strings.asm'
|
||||
|
||||
; CONSTANTS
|
||||
MON_WELCOME: DB 10,"PAT80 MEMORY MONITOR 0.2",10,0
|
||||
MON_COMMAND_HELP: DB "HELP",0 ; null terminated strings
|
||||
MON_COMMAND_DUMP: DB "DUMP",0
|
||||
MON_COMMAND_SET: DB "SET",0
|
||||
MON_COMMAND_ZERO: DB "ZERO",0
|
||||
MON_COMMAND_LOAD: DB "LOAD",0
|
||||
MON_COMMAND_RUN: DB "RUN",0
|
||||
MON_COMMAND_ADB: DB "ADB",0
|
||||
MON_ARG_HEX: DB " 0x",0
|
||||
MON_HELP: DB 10,"Available commands:\nHELP prints this message\nDUMP [ADDR] shows memory content\nSET [ADDR] sets memory content\nZERO [ADDR] [ADDR] sets all bytes to 0 in the specified range\nLOAD\nRUN [ADDR] executes code starting from ADDR\nADB starts Assembly Deploy Bridge",0
|
||||
MON_MSG_ADB: DB 10,"Waiting for data.",0
|
||||
MON_ERR_SYNTAX: DB " Syntax error",0
|
||||
;MON_ADB_TIMEOUT: EQU 0xFF // Number of cycles after an ADB binary transfer is considered completed
|
||||
MON_DUMP_BYTES_LINES: EQU 8
|
||||
MON_DUMP_BYTES_PER_LINE: EQU 8
|
||||
|
||||
Monitor_main:
|
||||
; Print welcome string
|
||||
ld bc, MON_WELCOME
|
||||
call Sys_Print
|
||||
monitor_main_loop:
|
||||
; Newline
|
||||
ld a, 10
|
||||
call Sys_Printc
|
||||
; Draw prompt char
|
||||
ld a, 62 ; >
|
||||
call Sys_Printc
|
||||
; Read char from command line
|
||||
call Sys_Readc ; blocking: returns when a character was read and placed in A reg
|
||||
call Strings_charToUpper ; user may enter lowercase char: transform to upper
|
||||
call Sys_Printc ; Print back the character to provide user feedback
|
||||
; Switch case
|
||||
ld hl, MON_COMMAND_HELP
|
||||
cp (hl) ; check incoming char is equal to command's first char
|
||||
jp z, monitor_help
|
||||
ld hl, MON_COMMAND_DUMP
|
||||
cp (hl)
|
||||
jp z, monitor_dump
|
||||
ld hl, MON_COMMAND_SET
|
||||
cp (hl)
|
||||
jp z, monitor_set
|
||||
ld hl, MON_COMMAND_ZERO
|
||||
cp (hl)
|
||||
jp z, monitor_zero
|
||||
ld hl, MON_COMMAND_LOAD
|
||||
cp (hl)
|
||||
jp z, monitor_load
|
||||
ld hl, MON_COMMAND_RUN
|
||||
cp (hl)
|
||||
jp z, monitor_run
|
||||
ld hl, MON_COMMAND_ADB
|
||||
cp (hl)
|
||||
jp z, monitor_adb
|
||||
; Unrecognized command: print error and beep
|
||||
ld bc, MON_ERR_SYNTAX
|
||||
call Sys_Print
|
||||
call Sys_Beep
|
||||
jp monitor_main_loop
|
||||
|
||||
monitor_help:
|
||||
ld bc, MON_COMMAND_HELP + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
|
||||
ld bc, MON_HELP
|
||||
call Sys_Print
|
||||
jp monitor_main_loop
|
||||
|
||||
; Asks the user for a memory position and shows the following 64 bytes of memory
|
||||
; @uses a, b, c, d, e, h, l
|
||||
monitor_dump:
|
||||
ld bc, MON_COMMAND_DUMP + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
; Now read the address from the user
|
||||
call monitor_arg_2byte ; returns the read bytes in hl
|
||||
ld a, 10 ; newline
|
||||
call Sys_Printc
|
||||
; now start displaying bytes from memory
|
||||
ld e, MON_DUMP_BYTES_LINES ; the number of lines to display
|
||||
monitor_dump_show_bytes_loop:
|
||||
ld d, MON_DUMP_BYTES_PER_LINE*2 ; the number of bytes per line to display (*2 as we display two times the same byte: once hex and once ascii)
|
||||
; Print current address
|
||||
ld a, h
|
||||
call monitor_printHexByte
|
||||
ld a, l
|
||||
call monitor_printHexByte
|
||||
; print four spaces
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
monitor_dump_show_bytes_line_loop: ; counts down from 15 to 0
|
||||
ld a, d
|
||||
sub MON_DUMP_BYTES_PER_LINE + 1
|
||||
jp m, monitor_dump_show_bytes_line_loop_ascii ; jp if
|
||||
; if position is 8 to 15, print hex value at mem position
|
||||
ld a, (hl)
|
||||
; print hex byte
|
||||
call monitor_printHexByte
|
||||
; print space
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
; if position is 4, print a second space (to group nibbles)
|
||||
ld a, d
|
||||
cp MON_DUMP_BYTES_PER_LINE / 2 + MON_DUMP_BYTES_PER_LINE + 1
|
||||
jp nz, no_second_space
|
||||
; print second space
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
no_second_space:
|
||||
; move to next mem position
|
||||
inc hl
|
||||
; decrement "nth byte on the line" counter
|
||||
dec d
|
||||
jp monitor_dump_show_bytes_line_loop
|
||||
; if position is 0 to 7, print ascii
|
||||
monitor_dump_show_bytes_line_loop_ascii:
|
||||
; is this the first ascii char printed in this line?
|
||||
ld a, d
|
||||
cp MON_DUMP_BYTES_PER_LINE
|
||||
jp nz, no_mempos_decr ; no need to decrement, already done
|
||||
; do this only once: printing hex values we advanced the counter by 8 positions. Bring it back.
|
||||
ld bc, MON_DUMP_BYTES_PER_LINE
|
||||
sbc hl, bc
|
||||
; print 3 spaces to separate hex from ascii
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
no_mempos_decr:
|
||||
; print ascii
|
||||
ld a, (hl)
|
||||
call monitor_printAsciiByte
|
||||
; print space
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
; if position is 12 (8+4), print a second space (to group nibbles)
|
||||
ld a, d
|
||||
cp MON_DUMP_BYTES_PER_LINE / 2 + 1
|
||||
jp nz, no_second_space2
|
||||
; print second space
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
no_second_space2:
|
||||
; move to next mem position
|
||||
inc hl
|
||||
; decrement counter: if non zero continue loop
|
||||
dec d
|
||||
jp nz, monitor_dump_show_bytes_line_loop
|
||||
; print newline
|
||||
ld a, 10
|
||||
call Sys_Printc
|
||||
; decrement line counter
|
||||
dec e
|
||||
jp nz, monitor_dump_show_bytes_loop ; if line counter is not 0, print another line
|
||||
; if line counter 0, finished
|
||||
jp monitor_main_loop
|
||||
|
||||
; Asks user for a memory position and a byte and puts the byte in memory
|
||||
; @uses a, b, c, h, l
|
||||
monitor_set:
|
||||
ld bc, MON_COMMAND_SET + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
; Now read the memory address to be changed from the user
|
||||
call monitor_arg_2byte ; returns the read bytes in hl
|
||||
; Start looping memory addresses
|
||||
monitor_set_byte_loop:
|
||||
ld a, 10 ; newline
|
||||
call Sys_Printc
|
||||
; Print current address
|
||||
ld a, h
|
||||
call monitor_printHexByte
|
||||
ld a, l
|
||||
call monitor_printHexByte
|
||||
; print two spaces
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
; print previous memory content (hex)
|
||||
ld a, (hl)
|
||||
call monitor_printHexByte
|
||||
; print two spaces
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
; print previous memory content (ascii)
|
||||
ld a, (hl)
|
||||
call monitor_printAsciiByte
|
||||
; print space
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
; ask the user the new memory content
|
||||
call monitor_arg_byte ; returns the read byte in a, exit code in b
|
||||
; find if user pressed Q/ENTER
|
||||
ld c, a ; save a
|
||||
ld a, b ; exit code in a
|
||||
cp 1 ; check if user pressed Q
|
||||
jp z, monitor_main_loop ; user wants to exit
|
||||
ld a, b
|
||||
cp 2 ; check if user pressed ENTER
|
||||
jp z, monitor_set_skipbyte ; user doesn't want to replace current byte: skip
|
||||
; user didn't press Q/ENTER: he inserted a valid byte
|
||||
; print two spaces
|
||||
ld a, 32
|
||||
call Sys_Printc
|
||||
call Sys_Printc
|
||||
ld a, c ; restore valid byte in a
|
||||
call monitor_printAsciiByte ; print user-inserted byte in ascii
|
||||
ld a, c ; restore valid byte in a
|
||||
ld (hl), a ; write new byte to memory
|
||||
monitor_set_skipbyte:
|
||||
inc hl ; next memory position
|
||||
jp monitor_set_byte_loop
|
||||
|
||||
; Asks user for a memory range and sets all bytes to zero in that range in memory
|
||||
; @uses a, b, c, h, l
|
||||
monitor_zero: ; TODO: bugged, doesn't exit cycle
|
||||
ld bc, MON_COMMAND_ZERO + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
; Now read the starting memory address
|
||||
call monitor_arg_2byte ; returns the read bytes in hl
|
||||
; starting addr in bc
|
||||
ld b, h
|
||||
ld c, l
|
||||
call monitor_arg_2byte ; ending addr in hl
|
||||
; Start looping memory addresses (from last to first)
|
||||
monitor_zero_loop:
|
||||
ld (hl), 0 ; set byte to 0 in memory
|
||||
dec hl ; next byte
|
||||
; check if we reached start addr (one byte at time)
|
||||
ld a, b
|
||||
cp h
|
||||
jp nz, monitor_zero_loop ; first byte is different, continue loop
|
||||
; first byte is equal, check second one
|
||||
ld a, c
|
||||
cp l
|
||||
jp nz, monitor_zero_loop ; second byte is different, continue loop
|
||||
; reached destination addr: zero the last byte and return
|
||||
ld (hl), 0 ; set byte to 0 in memory
|
||||
ret
|
||||
|
||||
monitor_load:
|
||||
ld bc, MON_COMMAND_LOAD + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
jp monitor_main_loop
|
||||
|
||||
monitor_run:
|
||||
ld bc, MON_COMMAND_RUN + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
; Now read the memory address to be changed from the user
|
||||
call monitor_arg_2byte ; returns the read bytes in hl
|
||||
ld a, 10 ; newline
|
||||
call Sys_Printc
|
||||
jp (hl) ; Start executing code
|
||||
|
||||
monitor_adb:
|
||||
ld bc, MON_COMMAND_ADB + 1 ; autocomplete command
|
||||
call Sys_Print
|
||||
; start copying incoming data to application space
|
||||
call monitor_copyTermToAppMem
|
||||
;jp APP_SPACE ; Start executing code
|
||||
; ld bc, APP_SPACE
|
||||
; call Sys_Print
|
||||
jp monitor_main_loop
|
||||
|
||||
; Prints "0x" and read 1 hex byte (2 hex digits, e.g. 0x8C)
|
||||
; Can be cancelled with Q/ENTER
|
||||
; @return a the read byte, b the exit code (0=valid byte in a, 1=Q, 2=ENTER)
|
||||
; @uses a, b, c
|
||||
monitor_arg_byte:
|
||||
; Print 0x... prompt
|
||||
ld bc, MON_ARG_HEX
|
||||
call Sys_Print
|
||||
; Read 2 digits
|
||||
call monitor_arg_byte_impl
|
||||
ret
|
||||
|
||||
; Prints "0x" and reads 2 hex bytes (4 hex digits e.g. 0x3F09)
|
||||
; Ignores Q/ENTER keys
|
||||
; @return hl the two read bytes
|
||||
; @uses a, b, c, h, l
|
||||
monitor_arg_2byte:
|
||||
; Print 0x... prompt
|
||||
ld bc, MON_ARG_HEX
|
||||
call Sys_Print
|
||||
; Read 2 digits
|
||||
call monitor_arg_byte_impl
|
||||
ld h, a ; move result to h
|
||||
; Read 2 digits
|
||||
call monitor_arg_byte_impl
|
||||
ld l, a ; move result to l
|
||||
ret
|
||||
|
||||
; Read 2 hex digits
|
||||
; @return a the read byte, b the exit code
|
||||
; (0 if no control key was, pressed, 1 for Q, 2 for RETURN)
|
||||
; @uses a, b, c
|
||||
monitor_arg_byte_impl:
|
||||
; Receive first hex digit. Value in a, exit code in b
|
||||
call monitor_readHexDigit
|
||||
; check exit code to find if user pressed esc or return
|
||||
ld c, a ; save a
|
||||
ld a, b ; load exit code in a
|
||||
cp 0
|
||||
jp nz, monitor_arg_byte_impl_exitcode ; user pressed Q/RETURN key
|
||||
; user didn't press Q/RETURN: returned nibble is valid
|
||||
ld a, c ; restore a and discard c
|
||||
; First hex digit is the most signif nibble, so rotate left by 4 bits
|
||||
rlca
|
||||
rlca
|
||||
rlca
|
||||
rlca
|
||||
; the lower nibble must now be discarded
|
||||
and %11110000
|
||||
ld c, a ; save shifted nibble in c
|
||||
; Read second hex digit
|
||||
call monitor_readHexDigit
|
||||
; Join the two nibbles in a single byte: second digit is already in a,
|
||||
; so we OR with the previously shifted c and obtain the complete byte in a.
|
||||
or c
|
||||
ret
|
||||
|
||||
monitor_arg_byte_impl_exitcode:
|
||||
; user pressed Q/RETURN key. Exit code is now in a.
|
||||
ld b, a ; move exit code in b
|
||||
ld a, 0 ; clear a
|
||||
ret
|
||||
|
||||
|
||||
|
||||
; Reads an hex digit (0 to 9, A to F)
|
||||
; @return a the read nibble (or 0 if Q/RETURN was pressed), b the exit code
|
||||
; (0 if no control key was, pressed, 1 for Q, 2 for RETURN)
|
||||
; @uses a, b
|
||||
monitor_readHexDigit:
|
||||
call Sys_Readc
|
||||
; check if user pressed Q
|
||||
; if user pressed Q, return exit code 1 in b and 0 in a
|
||||
cp 81
|
||||
jp z, monitor_readHexDigit_esc
|
||||
; check if user pressed RETURN
|
||||
; if user pressed RETURN, return exit code 2 in b and 0 in a
|
||||
cp 10
|
||||
jp z, monitor_readHexDigit_return
|
||||
|
||||
; check if is a valid hex digit (0-9 -> ascii codes 48 to 57; A-F -> ascii codes 65 to 70)
|
||||
; first check if is between 0 and F(ascii codes 48 to 70)
|
||||
ld b, a
|
||||
sub a, 48
|
||||
jp m, monitor_readHexDigit ; if negative (s), ascii code is under 48: ignore char
|
||||
ld a, b
|
||||
sub a, 71 ; 71 because we want to include 70 and the result must be negative
|
||||
jp p, monitor_readHexDigit ; if not negative (ns), ascii code is over 70: ignore it
|
||||
; check if is a valid int (<=57)
|
||||
ld a, b
|
||||
sub a, 58
|
||||
jp p, monitor_readHexDigit_char ; if not negative (ns), maybe is a char
|
||||
; otherwise is a number! First print for visive feedback
|
||||
ld a, b
|
||||
call Sys_Printc
|
||||
; then convert to its value subtracting 48
|
||||
sub a, 48
|
||||
ld b, 0 ; set b to exit code 0 to represent "valid value in a"
|
||||
ret
|
||||
monitor_readHexDigit_char:
|
||||
; check if is A, B, C, D, E, F (ascii codes 65 to 70). We already checked it is less than 70.
|
||||
ld a, b
|
||||
sub a, 65
|
||||
jp m, monitor_readHexDigit ; if negative (s), ascii code is under 65: ignore char
|
||||
; otherwise is a valid char (A-F). Print for visive feedback
|
||||
ld a, b
|
||||
call Sys_Printc
|
||||
; Its numeric value is 10 (A) to 15 (F). To obtain this, subtract 55.
|
||||
sub a, 55
|
||||
ld b, 0 ; set b to exit code 0 to represent "valid value in a"
|
||||
ret
|
||||
|
||||
monitor_readHexDigit_esc:
|
||||
ld a, 0
|
||||
ld b, 1
|
||||
ret
|
||||
monitor_readHexDigit_return:
|
||||
ld a, 0
|
||||
ld b, 2
|
||||
ret
|
||||
|
||||
; Prints a byte in hex format: splits it in two nibbles and prints the two hex digits
|
||||
; NOTE: The byte in a will be modified!
|
||||
; @param a the byte to print
|
||||
; @uses a, b, c
|
||||
monitor_printHexByte:
|
||||
ld c, a
|
||||
; rotate out the least significant nibble to obtain a byte with the most significant nibble
|
||||
; in the least significant nibble position
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
rrca
|
||||
; the upper nibble must now be discarded
|
||||
and %00001111
|
||||
call monitor_printHexDigit
|
||||
ld a, c
|
||||
and %00001111 ; bitwise and: set to 0 the most significant nibble and preserve the least
|
||||
call monitor_printHexDigit
|
||||
ret
|
||||
|
||||
; Prints an hex digit
|
||||
; @param a provides the byte containing, in the LSBs, the nibble to print
|
||||
; @uses a, b
|
||||
monitor_printHexDigit:
|
||||
; check the input is valid (0 to 15)
|
||||
ld b, a
|
||||
sub 16 ; subtract 16 instead of 15 cause 0 is positive
|
||||
; if positive, the input is invalid. Do not print anything.
|
||||
ret p
|
||||
; now check if the digit is a letter (10 to 15 -> A to F)
|
||||
ld a, b ; restore a
|
||||
sub 10
|
||||
; if a is positive, the digit is a letter
|
||||
jp p, monitor_printHexDigit_letter
|
||||
ld a, b ; restore a
|
||||
; add 48 (the ASCII number for 0) to obtain the corresponding number
|
||||
add 48
|
||||
call Sys_Printc
|
||||
ret
|
||||
monitor_printHexDigit_letter:
|
||||
ld a, b ; restore a
|
||||
; to obtain the corresponding letter we should subtract 10 (so we count from A)
|
||||
; and add 65 (the ASCII number for A). So -10+65=+55 we add only 55.
|
||||
add 55
|
||||
call Sys_Printc
|
||||
ret
|
||||
|
||||
; Prints an ASCII character. Similar to system Print function, but
|
||||
; ignores control characters and replaces any non-printable character with a dot.
|
||||
; NOTE: the a register is modified
|
||||
; @param a the byte to print
|
||||
; @uses a, b
|
||||
monitor_printAsciiByte:
|
||||
ld b, a ; save a (it will be modified)
|
||||
; if < 32 is a control char, non printable
|
||||
sub 32
|
||||
jp m, monitor_printAsciiByte_nonprintable
|
||||
ld a, b ; restore a
|
||||
; if >= 127 is an extended char, may not be printable
|
||||
sub 127
|
||||
jp p, monitor_printAsciiByte_nonprintable
|
||||
; otherwise is a printable ascii char
|
||||
ld a, b ; restore a
|
||||
call Sys_Printc
|
||||
ret
|
||||
monitor_printAsciiByte_nonprintable:
|
||||
ld a, 46 ; print dot
|
||||
call Sys_Printc
|
||||
ret
|
||||
|
||||
; Copy data from parallel terminal to application memory. This is tought to be used with the ADB function of the Pat80 Python Terminal.
|
||||
; Uses TERM_DATA_AVAIL_REG to check if a byte is available before reading it.
|
||||
; The first two received bytes (heading bytes) defines the stream length (MSB first), the rest of the bytes are copied to memory.
|
||||
; The copy is completed when the number of bytes defined in the heading bytes are received.
|
||||
; @uses a, b, c, d, h, l
|
||||
monitor_copyTermToAppMem:
|
||||
; d contains the current status.
|
||||
; 2 = waiting for first heading byte
|
||||
; 1 = waiting for second heading byte
|
||||
; 0 = heading bytes received, now receiving binary stream
|
||||
ld d, 2
|
||||
ld hl, APP_SPACE ; we will write in APP_SPACE
|
||||
monitor_copyTermToAppMem_loop:
|
||||
ld a, d
|
||||
cp 2 ; check if we are receiving first header byte
|
||||
jp z, monitor_copyTermToAppMem_loop_rec_head_byte_1
|
||||
ld a, d
|
||||
cp 1 ; check if we are receiving second header byte
|
||||
jp z, monitor_copyTermToAppMem_loop_rec_head_byte_2
|
||||
; we are receiving binary stream: read byte and save to memory
|
||||
call Term_readb ; reads a byte from terminal
|
||||
ld (hl), a ; copy byte to memory
|
||||
inc hl ; move to next memory position
|
||||
dec bc ; decrement remaining bytes counter
|
||||
; check if we reached the number of bytes to be transferred
|
||||
ld a, b
|
||||
cp 0
|
||||
jp nz, monitor_copyTermToAppMem_loop ; continue loop
|
||||
ld a, c
|
||||
cp 0
|
||||
jp nz, monitor_copyTermToAppMem_loop ; continue loop
|
||||
; all bytes received, return
|
||||
ret
|
||||
|
||||
monitor_copyTermToAppMem_loop_rec_head_byte_1:
|
||||
; we are receiving first header byte: read byte and save to b
|
||||
call Term_readb ; reads a byte from terminal
|
||||
ld b, a
|
||||
dec d
|
||||
jp monitor_copyTermToAppMem_loop ; continue loop
|
||||
monitor_copyTermToAppMem_loop_rec_head_byte_2:
|
||||
; we are receiving second header byte: read byte and save to c
|
||||
call Term_readb ; reads a byte from terminal
|
||||
ld c, a
|
||||
dec d
|
||||
jp monitor_copyTermToAppMem_loop ; continue loop
|
||||
|
||||
|
67
pat80-computer/software/z80-assembly/os/tests/sndtest.asm
Normal file
67
pat80-computer/software/z80-assembly/os/tests/sndtest.asm
Normal file
@ -0,0 +1,67 @@
|
||||
SndTest_test:
|
||||
; ch1 max volume
|
||||
ld a,%10010000
|
||||
out (SND_DATA_REG),a
|
||||
; play note ch1
|
||||
ld a,%10000000
|
||||
out (SND_DATA_REG),a
|
||||
ld a,%00100000
|
||||
out (SND_DATA_REG),a
|
||||
; wait
|
||||
ld bc, 1200
|
||||
call Time_delay55
|
||||
|
||||
; ch2 max volume
|
||||
ld a,%10110010
|
||||
out (SND_DATA_REG),a
|
||||
; play note ch2
|
||||
ld a,%10100000
|
||||
out (SND_DATA_REG),a
|
||||
ld a,%00010000
|
||||
out (SND_DATA_REG),a
|
||||
; wait
|
||||
ld bc, 1200
|
||||
call Time_delay55
|
||||
|
||||
; ch3 max volume
|
||||
ld a,%11010100
|
||||
out (SND_DATA_REG),a
|
||||
; play note ch3
|
||||
ld a,%11000000
|
||||
out (SND_DATA_REG),a
|
||||
ld a,%00001000
|
||||
out (SND_DATA_REG),a
|
||||
; wait
|
||||
ld bc, 2400
|
||||
call Time_delay55
|
||||
|
||||
; fade ch1,ch2,ch3
|
||||
ld d, 0 ; attenuation
|
||||
sndTest_fade: ; BROKEN!
|
||||
inc d
|
||||
; update ch1 atten
|
||||
ld a, d ; use A as attenuation
|
||||
and %10010000; place channel number in upper bits to compose attenuation byte
|
||||
out (SND_DATA_REG),a
|
||||
; update ch2 atten
|
||||
ld a, d ; use A as attenuation
|
||||
and %10110010; place channel number in upper bits to compose attenuation byte
|
||||
out (SND_DATA_REG),a
|
||||
; update ch3 atten
|
||||
ld a, d ; use A as attenuation
|
||||
and %11010100; place channel number in upper bits to compose attenuation byte
|
||||
out (SND_DATA_REG),a
|
||||
; wait
|
||||
ld bc, 100
|
||||
call Time_delay55
|
||||
; cycle until attenuation is 1111
|
||||
cp d %1111
|
||||
jp nz, sndTest_fade
|
||||
|
||||
; wait
|
||||
ld bc, 2400
|
||||
call Time_delay55
|
||||
|
||||
; play noise
|
||||
|
||||
ret
|
Reference in New Issue
Block a user