Reorganized folders, added pal-adapter io device folder

This commit is contained in:
Daniele Verducci su MatissePenguin
2021-01-02 16:33:51 +01:00
parent 55f5d62082
commit 68752e85a7
35 changed files with 327 additions and 1 deletions

View File

@ -0,0 +1,323 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project source="2.7.1" version="1.0">
This file is intended to be loaded by Logisim (http://www.cburch.com/logisim/).
<lib desc="#Wiring" name="0">
<tool name="Splitter">
<a name="facing" val="west"/>
<a name="fanout" val="3"/>
<a name="incoming" val="3"/>
<a name="appear" val="right"/>
</tool>
<tool name="Tunnel">
<a name="facing" val="east"/>
</tool>
</lib>
<lib desc="#Gates" name="1"/>
<lib desc="#Plexers" name="2">
<tool name="Decoder">
<a name="select" val="3"/>
</tool>
</lib>
<lib desc="#Arithmetic" name="3"/>
<lib desc="#Memory" name="4">
<tool name="ROM">
<a name="contents">addr/data: 8 8
0
</a>
</tool>
</lib>
<lib desc="#I/O" name="5"/>
<lib desc="#Base" name="6">
<tool name="Text Tool">
<a name="text" val=""/>
<a name="font" val="SansSerif plain 12"/>
<a name="halign" val="center"/>
<a name="valign" val="base"/>
</tool>
</lib>
<main name="main"/>
<options>
<a name="gateUndefined" val="ignore"/>
<a name="simlimit" val="1000"/>
<a name="simrand" val="0"/>
</options>
<mappings>
<tool lib="6" map="Button2" name="Menu Tool"/>
<tool lib="6" map="Button3" name="Menu Tool"/>
<tool lib="6" map="Ctrl Button1" name="Menu Tool"/>
</mappings>
<toolbar>
<tool lib="6" name="Poke Tool"/>
<tool lib="6" name="Edit Tool"/>
<tool lib="6" name="Text Tool">
<a name="text" val=""/>
<a name="font" val="SansSerif plain 12"/>
<a name="halign" val="center"/>
<a name="valign" val="base"/>
</tool>
<sep/>
<tool lib="0" name="Pin">
<a name="tristate" val="false"/>
</tool>
<tool lib="0" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="labelloc" val="east"/>
</tool>
<tool lib="1" name="NOT Gate"/>
<tool lib="1" name="AND Gate"/>
<tool lib="1" name="OR Gate"/>
</toolbar>
<circuit name="main">
<a name="circuit" val="main"/>
<a name="clabel" val=""/>
<a name="clabelup" val="east"/>
<a name="clabelfont" val="SansSerif plain 12"/>
<wire from="(400,170)" to="(460,170)"/>
<wire from="(490,160)" to="(550,160)"/>
<wire from="(670,230)" to="(720,230)"/>
<wire from="(670,170)" to="(720,170)"/>
<wire from="(530,430)" to="(720,430)"/>
<wire from="(220,380)" to="(270,380)"/>
<wire from="(340,420)" to="(340,440)"/>
<wire from="(570,190)" to="(670,190)"/>
<wire from="(570,210)" to="(670,210)"/>
<wire from="(530,430)" to="(530,460)"/>
<wire from="(670,170)" to="(670,190)"/>
<wire from="(670,210)" to="(670,230)"/>
<wire from="(660,140)" to="(660,180)"/>
<wire from="(660,220)" to="(660,260)"/>
<wire from="(180,190)" to="(180,420)"/>
<wire from="(220,150)" to="(220,380)"/>
<wire from="(100,60)" to="(250,60)"/>
<wire from="(100,120)" to="(250,120)"/>
<wire from="(570,180)" to="(660,180)"/>
<wire from="(570,220)" to="(660,220)"/>
<wire from="(570,240)" to="(640,240)"/>
<wire from="(300,380)" to="(380,380)"/>
<wire from="(650,110)" to="(650,170)"/>
<wire from="(650,230)" to="(650,290)"/>
<wire from="(570,170)" to="(650,170)"/>
<wire from="(570,230)" to="(650,230)"/>
<wire from="(640,320)" to="(720,320)"/>
<wire from="(270,110)" to="(400,110)"/>
<wire from="(100,150)" to="(220,150)"/>
<wire from="(660,140)" to="(720,140)"/>
<wire from="(660,260)" to="(720,260)"/>
<wire from="(250,60)" to="(250,80)"/>
<wire from="(250,100)" to="(250,120)"/>
<wire from="(430,400)" to="(720,400)"/>
<wire from="(450,160)" to="(450,190)"/>
<wire from="(640,240)" to="(640,320)"/>
<wire from="(430,460)" to="(530,460)"/>
<wire from="(300,420)" to="(340,420)"/>
<wire from="(340,440)" to="(380,440)"/>
<wire from="(340,420)" to="(380,420)"/>
<wire from="(570,200)" to="(720,200)"/>
<wire from="(220,480)" to="(380,480)"/>
<wire from="(180,420)" to="(270,420)"/>
<wire from="(220,380)" to="(220,480)"/>
<wire from="(100,90)" to="(250,90)"/>
<wire from="(650,290)" to="(720,290)"/>
<wire from="(450,160)" to="(460,160)"/>
<wire from="(180,190)" to="(450,190)"/>
<wire from="(100,190)" to="(180,190)"/>
<wire from="(650,110)" to="(720,110)"/>
<wire from="(400,110)" to="(400,170)"/>
<comp lib="0" loc="(720,230)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 4"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="1" loc="(300,380)" name="NOT Gate"/>
<comp lib="0" loc="(100,60)" name="Pin">
<a name="tristate" val="false"/>
<a name="label" val="A5"/>
</comp>
<comp loc="(490,160)" name="SN74HC138N Active low 3-8 decoder"/>
<comp lib="0" loc="(720,400)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="RAM_EN"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(100,120)" name="Pin">
<a name="tristate" val="false"/>
<a name="label" val="A7"/>
</comp>
<comp lib="0" loc="(720,290)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 6"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(100,90)" name="Pin">
<a name="tristate" val="false"/>
<a name="label" val="A6"/>
</comp>
<comp lib="1" loc="(430,460)" name="OR Gate">
<a name="inputs" val="2"/>
</comp>
<comp lib="0" loc="(270,110)" name="Splitter">
<a name="facing" val="west"/>
<a name="fanout" val="3"/>
<a name="incoming" val="3"/>
<a name="appear" val="right"/>
</comp>
<comp lib="0" loc="(100,190)" name="Pin">
<a name="tristate" val="false"/>
<a name="label" val="IORQ"/>
</comp>
<comp lib="0" loc="(720,200)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 3"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(720,140)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 1"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(100,150)" name="Pin">
<a name="tristate" val="false"/>
<a name="label" val="A15"/>
</comp>
<comp lib="0" loc="(720,110)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 0"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(720,260)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 5"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="1" loc="(430,400)" name="OR Gate">
<a name="inputs" val="2"/>
</comp>
<comp lib="0" loc="(720,320)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 7"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(550,160)" name="Splitter">
<a name="fanout" val="8"/>
<a name="incoming" val="8"/>
<a name="appear" val="right"/>
</comp>
<comp lib="1" loc="(300,420)" name="NOT Gate"/>
<comp lib="0" loc="(720,170)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="I/O Port 2"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="0" loc="(720,430)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="label" val="ROM_EN"/>
<a name="labelloc" val="east"/>
</comp>
</circuit>
<circuit name="SN74HC138N Active low 3-8 decoder">
<a name="circuit" val="SN74HC138N Active low 3-8 decoder"/>
<a name="clabel" val=""/>
<a name="clabelup" val="east"/>
<a name="clabelfont" val="SansSerif plain 12"/>
<wire from="(320,250)" to="(380,250)"/>
<wire from="(580,180)" to="(580,190)"/>
<wire from="(650,250)" to="(700,250)"/>
<wire from="(450,200)" to="(630,200)"/>
<wire from="(590,180)" to="(630,180)"/>
<wire from="(590,220)" to="(630,220)"/>
<wire from="(400,180)" to="(400,200)"/>
<wire from="(600,140)" to="(600,170)"/>
<wire from="(380,290)" to="(420,290)"/>
<wire from="(320,210)" to="(410,210)"/>
<wire from="(390,270)" to="(420,270)"/>
<wire from="(80,320)" to="(300,320)"/>
<wire from="(610,240)" to="(630,240)"/>
<wire from="(400,250)" to="(420,250)"/>
<wire from="(450,270)" to="(600,270)"/>
<wire from="(380,140)" to="(380,180)"/>
<wire from="(410,230)" to="(420,230)"/>
<wire from="(80,260)" to="(160,260)"/>
<wire from="(450,160)" to="(590,160)"/>
<wire from="(320,200)" to="(400,200)"/>
<wire from="(300,260)" to="(300,320)"/>
<wire from="(450,230)" to="(580,230)"/>
<wire from="(320,190)" to="(390,190)"/>
<wire from="(320,180)" to="(380,180)"/>
<wire from="(580,210)" to="(630,210)"/>
<wire from="(580,190)" to="(630,190)"/>
<wire from="(410,200)" to="(410,210)"/>
<wire from="(410,220)" to="(410,230)"/>
<wire from="(400,230)" to="(400,250)"/>
<wire from="(590,220)" to="(590,250)"/>
<wire from="(390,240)" to="(390,270)"/>
<wire from="(390,160)" to="(390,190)"/>
<wire from="(580,210)" to="(580,230)"/>
<wire from="(590,160)" to="(590,180)"/>
<wire from="(380,140)" to="(420,140)"/>
<wire from="(190,260)" to="(290,260)"/>
<wire from="(600,230)" to="(600,270)"/>
<wire from="(320,220)" to="(410,220)"/>
<wire from="(450,290)" to="(610,290)"/>
<wire from="(390,160)" to="(420,160)"/>
<wire from="(400,180)" to="(420,180)"/>
<wire from="(600,170)" to="(630,170)"/>
<wire from="(600,230)" to="(630,230)"/>
<wire from="(450,140)" to="(600,140)"/>
<wire from="(380,250)" to="(380,290)"/>
<wire from="(410,200)" to="(420,200)"/>
<wire from="(450,250)" to="(590,250)"/>
<wire from="(320,230)" to="(400,230)"/>
<wire from="(450,180)" to="(580,180)"/>
<wire from="(610,240)" to="(610,290)"/>
<wire from="(320,240)" to="(390,240)"/>
<comp lib="2" loc="(300,260)" name="Decoder">
<a name="select" val="3"/>
<a name="disabled" val="0"/>
</comp>
<comp lib="1" loc="(450,250)" name="NOT Gate"/>
<comp lib="0" loc="(700,250)" name="Pin">
<a name="facing" val="west"/>
<a name="output" val="true"/>
<a name="width" val="8"/>
<a name="labelloc" val="east"/>
</comp>
<comp lib="1" loc="(450,140)" name="NOT Gate"/>
<comp lib="0" loc="(650,250)" name="Splitter">
<a name="facing" val="west"/>
<a name="fanout" val="8"/>
<a name="incoming" val="8"/>
<a name="appear" val="right"/>
</comp>
<comp lib="1" loc="(450,270)" name="NOT Gate"/>
<comp lib="1" loc="(450,180)" name="NOT Gate"/>
<comp lib="0" loc="(80,260)" name="Pin">
<a name="tristate" val="false"/>
<a name="label" val="Enable"/>
</comp>
<comp lib="6" loc="(174,292)" name="Text">
<a name="text" val="Enable is active-low"/>
</comp>
<comp lib="1" loc="(190,260)" name="NOT Gate"/>
<comp lib="1" loc="(450,290)" name="NOT Gate"/>
<comp lib="0" loc="(80,320)" name="Pin">
<a name="width" val="3"/>
<a name="tristate" val="false"/>
<a name="label" val="Input"/>
</comp>
<comp lib="1" loc="(450,230)" name="NOT Gate"/>
<comp lib="1" loc="(450,160)" name="NOT Gate"/>
<comp lib="1" loc="(450,200)" name="NOT Gate"/>
</circuit>
</project>

View File

@ -0,0 +1,12 @@
# Pat80 Applications
## Intro
This folder contains some example applications.
The folder `brief` contains little applications that can be entered directly via keyboard in the memory monitor.
The folder `big` contains complete applications to be loaded via sdcard or tape.
## How to write an application
When the Pat80 operating system is built, a `abi-generated.asm` file is built along with the rom binary. This file contains the description of the OS available API.
An application targeting the last version of the OS should include this file, to make the system calls labels available inside the application code.
The application can obtain the operating system ABI version (ABI -> https://en.wikipedia.org/wiki/Application_binary_interface) via the Sys_ABI call (it is a 16 bits integer returned in BC).
The application's first command should be an ABI check: if the OS version is not compatible with the app, the app should exit displaying an error message.

View File

@ -0,0 +1,7 @@
org 0xA000
include '../../os/abi-generated.asm'
STRING: DB "Hello",0
ld bc, STRING
call Sys_Print
jp 0

View File

@ -0,0 +1,38 @@
;hd44780 lcd test procedure
LCD_INSTR_REG: EQU %00000000
LCD_DATA_REG: EQU %00000001
;reset procedure
ld a,%00111000
out (LCD_INSTR_REG),a
ld a,%00001000
out (LCD_INSTR_REG),a
ld a,%00000001
out (LCD_INSTR_REG),a
;init procedure
ld a,%00111000
out (LCD_INSTR_REG),a
ld a,%00001110
out (LCD_INSTR_REG),a
;write characters to display
ld a,%01000100
out (LCD_DATA_REG),a
ld a,%01100001
out (LCD_DATA_REG),a
ld a,%01101110
out (LCD_DATA_REG),a
ld a,%01101001
out (LCD_DATA_REG),a
ld a,%01100101
out (LCD_DATA_REG),a
ld a,%01101100
out (LCD_DATA_REG),a
ld a,%01100101
out (LCD_DATA_REG),a
ld a,%00100001
out (LCD_DATA_REG),a
halt

View File

@ -0,0 +1,4 @@
# Dev-headers
This directory contains the ASM files to be used with ADB.
ADB (Assembly Debug Bridge) is the system to hot load code into PAT80 without needing to flash a rom chip. It works by selecting ADB command in the Memory Monitor running from ROM and sending the binary file via the Python Terminal Emulator (CTRL+A).
The binary file must be compiled using files in this folder because these files contain an instruction to set the starting memory address to the address used by the Monitor to load the application data into RAM.

View File

@ -0,0 +1,2 @@
org 0xA000 ; Set starting position to ram
include '../os/main.asm'

View 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"

View 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.

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -0,0 +1,2 @@
org 0xA000 ; Set starting position to ram
include 'main.asm'

View 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

View 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

View 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