add docs with doxygen using custom filter

This commit is contained in:
matthias@quintern.xyz 2024-08-08 00:11:15 +02:00
parent 5ee9610616
commit 8b2e7c2752
15 changed files with 3243 additions and 106 deletions

2861
.doxygen_config Normal file

File diff suppressed because it is too large Load Diff

View File

@ -57,3 +57,7 @@ clean:
rm -r $(OBJ_DIR)
rm $(ROM)
rm $(SPI)
docs:
doxygen .doxygen_config

181
doxy-asm65.py Normal file
View File

@ -0,0 +1,181 @@
import sys
import re
from typing import Callable
filename = "unknown"
def pdebug(*args, **k):
print(f"DEBUG ({filename}):", *args, file=sys.stderr, **k)
def parse_custom_language(file_content: str):
# procedure_name: scope_name
exported_names = {}
def handle_export(m):
export_type = m.groups()[0]
scope = m.groups()[1]
functions = m.groups()[2].replace(" ", "").strip(",")
for f in functions.split(","):
pdebug(f"Add Exported function: '{f}' in '{scope}'")
exported_names[f] = scope
return ""
def handle_procedure(m):
# print("handle procedure:", m.groups())
p_docs = m.groups()[0].strip("\n")
p_type = m.groups()[1]
p_name = m.groups()[2]
p_args = m.groups()[3].strip(" ")
p_code = m.groups()[4]
s = ""
in_namespace = False
if p_name in exported_names:
# print(f"{p_name} is exported")
in_namespace = True
# wrap function in namespace {}, which consumes the first line of the docstring, which must be ;;***...
namespace = exported_names[p_name]
# assert p_docs.startswith(";;*"), f"Documentation of an exported procedure must start with ';;***...' ({p_name})"
# assert p_docs[p_docs.rfind('\n'):].startswith("\n;;*"), f"Documentation of an exported procedure must end with ';;***...' ({p_name})"
s += f"namespace {namespace}" + " {" + p_docs
# s += p_docs[p_docs.find('\n'):p_docs.rfind('\n')]
s += "\n"
# s += f"@ingroup {namespace}\n"
else:
s += p_docs + "\n" #re.sub(r";;\*+", ";;", p_docs, 0, re.MULTILINE) + "\n"
if p_type == "proc":
s += f"proc {p_name}("
for match in re.finditer(r"[\@\\]param +(.+?) *:", p_docs):
s += f"Param {match.groups()[0]},"
if s[-1] == ",": s = s[:-1]
s += ");\n"
elif p_type == "macro":
pdebug(f"Processing macro '{p_name}' with args '{'TXT'.join(p_args.replace(' ', '').split(','))}'")
s += f"macro {p_name}("
p_args = "".join("Param " + param + "," for param in p_args.replace(" ", "").split(',')).strip(",")
s += p_args
s += ");\n"
pdebug("Found macro", p_name, s)
elif p_type == "enum":
p_code = re.sub(r"( *(?:;;.*)?\n)", r",\1", p_code)
s += f"enum {p_name}" + "{\n" + p_code + "};"
else:
raise NotImplementedError(f"handle_procedure not implemented for procedure type {p_type}")
s += re.sub(".*", "", p_code)
if in_namespace:
s += "} // namespace"
else:
s += "\n"
return s
def handle_storage_label(m):
l_docs = m.groups()[0].strip('\n')
l_name = m.groups()[1]
l_allocs = m.groups()[2]
storage_alloc = r"(?:\.(byte|res|dbyte|word|addr|faraddr|dword|ascii|asciiz)([, ]+(?:0x[a-fA-F0-9]+|0b[01]+|\d+|\w+|\"[^\n]*?[^\\\n]\")[ \n]*)*)"
storage_alloc_arg = r"(0x[a-fA-F0-9]+|0b[01]+|\d+|\w+|\"[^\n]*[^\\\n]\")"
args = []
allocs = []
for alloc_match in re.finditer(storage_alloc, l_allocs):
allocs.append(alloc_match)
alloc_args = alloc_match.groups()[1]
if alloc_args:
args += re.findall(storage_alloc_arg, alloc_args)
s = ""
in_namespace = False
# pdebug("ldocs for", l_name, l_docs)
# pdebug(m.groups())
if l_name in exported_names:
in_namespace = True
namespace = exported_names[l_name]
s += f"namespace {namespace}" + " {" + l_docs
s += "\n"
else:
s += l_docs + "\n"
if len(args) > 1:
if all(arg.startswith("\"") for arg in args):
s += f'char* {l_name} = "' + "".join(map(lambda x: x.strip('"'), args)) + '"'
else:
s += "bytes[] = {"
for arg in args:
s += arg + ","
s += "}"
s += s.strip(",") + "}"
else:
# alloc_type label =
l_type = allocs[0].groups()[0]
if len(args) == 0:
l_arg = None
else:
l_arg = args[0]
if l_type == "res":
l_type = f"bytes[{l_arg}]"
l_arg = None
else: l_type += "*"
s += f"{l_type} {l_name}"
if l_arg:
s += f" = {l_arg}"
s += ";"
if in_namespace: s += "} // namespace"
s += m.group().count('\n') * '\n' # make sure the #lines is the same
# pdebug(args, "\n---\n", s)
return s
patterns: dict[str, str|Callable[[re.Match], str]] = {
r"\@(?:macro|function)": "@brief",
r"^\.scope ([a-zA-Z0-9_]+)": r"namespace \1 {",
# r"^\.macro ([a-zA-Z0-9_]+)(.*)?": r"macro \1(\2 \2); ",
# r"^\.end(?:macro)": "",
r"^\.end(?:scope)": "}",
r"^\.(include)": r"#\1",
r"^(Export(?:Zp)?) (\w+)((?: *, *\w+)+)": handle_export,
r"^(Import(?:Zp)?) (\w+)((?: *, *\w+)+)": "",
r"(?<!^;)\$([A-Fa-f0-9_]+)": r"0x\1", # $HEX -> 0xHEX except in comments
r"(?<!^;)%([01_]+)": r"0b\1", # %BIN -> 0bBIN except in comments
r"^((?:;;.*\n)*)^\.(proc|enum|macro) (\w+)(.*?)\n((?:.|\n)*?)\.end(proc|enum|macro).*": handle_procedure,
r"^((?:;;.*\n)*) *(\w+):((?:\s*\.(?:byte|res|dbyte|word|addr|faraddr|dword|ascii|asciiz)(?:[, ]+(?:0x[a-fA-F0-9]+|0b[01]+|\d+|\w+|\"[^\n]*[^\\\n]\")[ \n]*)*)+)": handle_storage_label,
r";;": "//!", # C++ comments
# TODO this is currently case sensitive
r"(?<!^;)( *\w+ *= *[^;,\n]+?) *(//.*)?$": r"\1;\2", # semicolons after assignments, except in comments and when they already end with a comma or semicolon. Also preserve comments after the assignment
r"^([^\n;]*)(?<!\w)\.(\w+)": r"\1// #\2", # all .preprocessor commands
}
compiled_patterns = []
for k,v in patterns.items():
compiled_patterns.append((re.compile(k), v))
resub_patterns: dict[str, str|Callable[[re.Match], str]] = {
r"(?<!^;;)[ \t\r\f\v]+": " ", # turn all spaces into single whitespace except if in doxygen comment
r"^((?:[^\"\n;]||[^\"\n;]*\"(?:[^\"\n]|\\\")+\")+);(?!;).*": r"\1", # remove normal comments, detect strings
r"^;;\*+": ";;", # remove ;;*** comments
r"[ \t\r\f\v]+$": "", # remove trailing spaces print(file_content)
}
for pat, subst in resub_patterns.items():
file_content = re.sub(pat, subst, file_content, 0, re.MULTILINE)
for pat,subst in patterns.items():
pdebug(f"Now doing pattern: {pat}")
(file_content, n_subst) = re.subn(pat, subst, file_content, 0, re.MULTILINE)
return file_content
def main():
global filename
if len(sys.argv) != 2:
print("Usage: python doxy-asm65.py <input_file>")
sys.exit(1)
filename = sys.argv[1]
with open(filename, 'r') as file:
file_content = file.read()
transformed_content = parse_custom_language(file_content)
print(transformed_content)
if __name__ == "__main__":
main()

View File

@ -5,26 +5,30 @@
;; This module processes keyboard scancodes from the ps2_keyboard driver.
;; It requires a PS/2 keyboard that supports scancode set 3.
;;
;; @section What it does
;; @section what What it does
;; - swallow break (release) scancodes
;;
;; This handler is designed for debug purposes.
;;
;; @section How to use it
;; Call `kb::init` and check if it was successful (Z = 1).
;; @section usage How to use it
;; Call `kb::init` and check if it was successful (`Z = 1`).
;; In your program loop, check if kb::scancode is 0. If it is not, a key has been pressed.
;;
;; @subsection Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3)
;; @subsection change_behaviour Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command `0xF3`)
;; and the typematic behavior (make, make/release, typematic) for all keys.
;;
;; You may also send the echo (0xEE) and identify (0xF2) commands to the keyboard.
;; You may also send the echo (`0xEE`) and identify (`0xF2`) commands to the keyboard.
;;********************************************************************************
.ifndef INCLUDE_KEYBOARD_SIMPLE
INCLUDE_KEYBOARD_SIMPLE = 1
.include "ps2_keyboard.h65"
;;********************************************************************************
;; @brief Simple Keyboard Handler
;;********************************************************************************
.scope skb
Import skb, init, scancode

View File

@ -1,69 +1,76 @@
; normal
;;********************************************************************************
;; @module ps2_keyboard_text_handler
;; @type system
;; @details:
;; @details
;; This module processes keyboard scancodes from the ps2_keyboard driver.
;; It requires a PS/2 keyboard that supports scancode set 3.
;;
;; @section What it does
;; @section what What it does
;; - keeps track of modifier keys: shift, left/right alt, left right meta (super),
;; control, capslock, numlock, scrollock
;; - convert valid keycodes to chars, dependening on the state of shift and right alt
;; - update the keyboard LEDs (numlock, capslock,, scrolllock)
;;
;; This handler is designed for efficient text input.
;; It does not track the status of the keys (except modifiers) and is therefore not suitable for games.
;;
;; @section How it works
;; In the init function, the module disables the typematic behaviour for the modifier keys by
;; sending commands to the keyboard. The 3 lock keys will be set to make only mode and
;; the others to make release.
;; @section working_principle How it works
;; In the init function, the module disables the *typematic* behaviour for the modifier keys by
;; sending commands to the keyboard. The 3 lock keys will be set to *make only* mode and
;; the others to *make release*.
;; This makes tracking the modifier keys much easier and is the reason why scancode set 3
;; is required, as these commands are only available with that.
;; is required, as these commands are only available with this set.
;;
;; Using scancode set 3 also adds the benefit that all scancodes are only 1 byte large,
;; making the recognition of scancodes trivial.
;;
;; Scancodes from the keyboard are processed immediately, as the handling functions is called
;; Scancodes sent from the keyboard are processed immediately, as the handling functions is called
;; from the keyboard's interrupt handler.
;;
;; @subsection Mapping keycodes to chars
;; @subsection mapping Mapping keycodes to chars
;; The mapping of keycodes to chars is done using an array of chars for each state:
;; default, shift and right alt. The tables contains the ascii characters, 0 where the keycode does not
;; generate a character and 1 where the keycode is modifier key.
;; default, shift and right alt. The tables contains the ascii characters, `0` where the keycode does not
;; generate a character and `1` where the keycode is a modifier key.
;; This approach consumes a relatively large amount of ROM (3 * 144 bytes), but is super fast,
;; as loading a character is merely one indexed load instruction.
;; Checking for mod keys is now also fast, beceause we only have to do 1 comparison to see IF it is a modifier key.
;;
;; @section How to use it
;; Call `kb::init` and check if it was successful (Z = 1).
;; In your program loop, check if kb::keycode is 0. If it is not, a key has been pressed.
;; @section usage How to use it
;; Call `kb::init` and check if it was successful (`Z = 1`).
;; In your program loop, check if `kb::keycode` is 0. If it is not, a key has been pressed.
;;
;; If the keypress generated a character `kb::char` will be non-zero and contain the character.
;;
;; Note that if you will probably want to write a 0 to `kb::keycode` and `kb::char`
;; after processing the keycode. This will not be done by the handler.
;;
;; Conditional branches based on the status of a modifier (SHIFT, RALT, LALT, CTRL, META, NUMLOCK, SCROLLLOCK) can be done
;; Conditional branches based on the status of a modifier (`SHIFT`, `RALT`, `LALT`, `CTRL`, `META`, `NUMLOCK`, `SCROLLLOCK`) can be done
;; using the `bbs` and `bbr` instructions, as each modifier is represented by a bit of the `kb::modifier` variable
;; in the zeropage.
;;
;; @subsection Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3)
;; @subsection change_behaviour Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command `0xF3`)
;; and the typematic behavior (make, make/release, typematic) for all keys except the modifier keys.
;;
;; You may also send the echo (0xEE) and identify (0xF2) commands to the keyboard.
;; You may also send the echo (`0xEE`) and identify (`0xF2`) commands to the keyboard.
;;********************************************************************************
.ifndef INCLUDE_KEYBOARD_TEXT
INCLUDE_KEYBOARD_TEXT = 1
.include "ps2_keyboard.h65"
;;********************************************************************************
;; @brief Keyboard Handler
;;********************************************************************************
.scope kb
Import kb, init, char, keycode
Import kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON
ImportZp kb, modifier
;;********************************************************************************
;; @brief Bitmask for checking modifier keys
;;********************************************************************************
.enum MOD
SHIFT = %10000000 ; set if either capslock is on or shift is pressed
RALT = %01000000 ; set while rightalt is pressed
@ -75,8 +82,12 @@ ImportZp kb, modifier
SCROLLLOCK = %00000001 ; ps/2 LED
.endenum
;; @brief Scancode that is sent by the keyboard when a key is released
K_BREAK = $F0
;;********************************************************************************
;; @brief S3 German Keycodes
;;********************************************************************************
.enum K
F1 = $07
ESCAPE = $08

View File

@ -4,11 +4,16 @@ Export kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_
ExportZp kb, modifier
.zeropage
modifier: .res 1
;; @ref kb::MOD "status of the modifier keys"
modifier: .byte
.bss
previous_scancode: .res 1 ; all 1 if previous_scancode is break
char: .res 1
keycode: .res 1
;; previous scancode is used to determine if a key was released (BREAK sent previously)
;; will be $ff if the previous scancode was K_BREAK
previous_scancode: .byte
;; 0 when no key or non-character key was pressed, otherwise character of the key (with modifiers applied)
char: .byte
;; @ref kb::K "keycode" of the last key that was pressed or 0 if none was pressed
keycode: .byte
.code
;;********************************************************************************
@ -101,8 +106,8 @@ set:
;; @function Process a scancode
;; @details
;; Update modifier bits if a mod key is pressed.
;; If the key is not a mod key, the keycode is stored in kb::keycode
;; If the key is an ascii character, the corresponding ascii character is stored in kb::char
;; If the key is not a mod key, the keycode is stored in @ref kb::keycode
;; If the key is an ascii character, the corresponding ascii character is stored in @ref kb::char
;; @modifies A, X
;;********************************************************************************
.proc process_scancode
@ -280,6 +285,7 @@ CHARS_NOMOD:
.byte "\x00nbhgz6\x00\x00\x01mju78\x00"
.byte "\x00,kio09\x00\x00.-l\xEFp\xE2\x00"
.byte "\x00\x00\xE1#\xF5`\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01"
;; numlock range characters where numlock is off
CHARS_NUMLOCK_OFF:
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x01/\x00\x00\x00\x00+\x00*\x00"

View File

@ -21,6 +21,7 @@ buffer: .res 256
jsr kb::init
loop:
lda kb::char
ldx ptr
beq @no_char
stz kb::char
jsr lcd::print_char
@ -42,6 +43,9 @@ clear_display:
jsr lcd::clear
bra loop
k_backspace:
; go back
lda ' '
lcd_DecrementCursor
lda #' '
jsr lcd::print_char
lcd_DecrementCursor
bra loop
.endproc

View File

@ -1,3 +1,10 @@
;;********************************************************************************
;; @file
;; @details:
;; This code is loaded to the computer via a SPI interface
;; Currently, it must be loaded to $5000, where the main loop will begin
;;********************************************************************************
.include "system.h65"
.include "string.h65"
.include "lcd.h65"
@ -74,6 +81,12 @@ CODE_START:
@l1:
jmp _ps2_keyboard_printer
@l2:
lcd_SetCursorPos $26
lda #'6'
jsr lcd::print_char
lcd_IncrementCursor
lda #'8'
jsr lcd::print_char
@l3:
@l4:
@l5:

View File

@ -17,10 +17,15 @@ INCLUDE_LCD = 1
.include "system/system.h65"
;;********************************************************************************
;; @brief LCD character display
;;********************************************************************************
.scope lcd
LCD_IO = IO1
Import lcd,init,clear,print,print_char,set_position,set_custom_char
Import lcd,_cmd,_wait_nbusy,_write_ram,_read_ram,_charcount
Import lcd,_cmd,_wait_nbusy,_write_ram,_read_ram,_set_dd_ram_addr_from_charcount
Import lcd,_charcount
; PIN MASKS
E = %10000000
RW = %01000000
@ -28,17 +33,23 @@ RS = %00100000
RA_MASK = %11100000
; LCD Instructions (see datasheet for more)
CMD_CLEAR = %00000001 ; clear display
CMD_ENTRY_MODE = %00000110 ; auto-shift cursor
CMD_DISPLAY_ON = %00001111 ; everything on, with blinking cursor
CMD_SHIFT_CUR_LEFT = %00101000 ; shift the display to the left
CMD_SHIFT_DIS_LEFT = %00111000 ; shift the display to the left
CMD_SHIFT_DIS_RIGHT = %00110000 ; shift the display to the right
CMD_SHIFT_CUR_RIGHT = %00100000 ; shift the display to the right
CMD_FUNCTION_SET = %00111000 ; 8-bit, 4 lines, 5x7 font
CMD_SET_CG_ADDRESS = %01000000 ; or with the address
CMD_SET_ADDRESS = %10000000 ; or with the address
;;********************************************************************************
;; LCD Instructions
;; @details See LCD-W164B datasheet for a complete list
;;********************************************************************************
.enum CMD
CLEAR = %00000001 ;; clear display
ENTRY_MODE = %00000110 ;; auto-shift cursor
DISPLAY_ON = %00001111 ;; everything on, with blinking cursor
SHIFT_CUR_LEFT = %00101000 ;; shift the cursor to the left
SHIFT_DIS_LEFT = %00111000 ;; shift the display to the left
SHIFT_DIS_RIGHT = %00110000 ;; shift the display to the right
SHIFT_CUR_RIGHT = %00100000 ;; shift the cursor to the right
FUNCTION_SET = %00111000 ;; 8-bit, 4 lines, 5x7 font
SET_CG_ADDRESS = %01000000 ;; or this with the address
SET_ADDRESS = %10000000 ;; or this with the address
.endenum
; LCD Constants
LINE1 = $00
LINE2 = $40
@ -100,36 +111,53 @@ LINE4 = $50
;;********************************************************************************
;; @macro Print a null-terminated string
;; @param message: Address of the message or string literal
;; @macro Set the cursor to the n-th position
;; @param n: Position [0,$40) or a register
;;********************************************************************************
.macro lcd_SetCursor pos
.if .match (pos, a)
.elseif .match (pos, x)
txa
.elseif .match (pos, y)
tya
.macro lcd_SetCursorPos n
.if .match (n, a)
.elseif .match (n, x)
txa
.elseif .match (n, y)
tya
.else
lda #pos
lda #n
.endif
sta lcd::_charcount
jsr lcd::_set_dd_ram_addr_from__charcount
jsr lcd::_set_dd_ram_addr_from_charcount
.endmacro
;;********************************************************************************
;; @macro Print a null-terminated string
;; @param message: Address of the message or string literal
;; @macro Move the cursor to the right
;; @param n: Number of characters to move n. Defaults to 1 if not provided
;;********************************************************************************
.macro lcd_AdvanceCursor n
.macro lcd_IncrementCursor n
.ifblank n
inc lcd::_charcount
inc lcd::_charcount
.elseif n = 1
inc lcd::_charcount
.else
lda lcd::_charcount
add n
sta lcd::_charcount
lda lcd::_charcount
add #n
sta lcd::_charcount
.endif
jsr lcd::_set_dd_ram_addr_from__charcount
jsr lcd::_set_dd_ram_addr_from_charcount
.endmacro
;;********************************************************************************
;; @macro Move the cursor to the left
;; @param n: Number of characters to move n. Defaults to 1 if not provided
;;********************************************************************************
.macro lcd_DecrementCursor n
.ifblank n
dec lcd::_charcount
.elseif n = 1
dec lcd::_charcount
.else
lda lcd::_charcount
sub #n
sta lcd::_charcount
.endif
jsr lcd::_set_dd_ram_addr_from_charcount
.endmacro
.endif ; guard

View File

@ -5,7 +5,8 @@
.endif
Export lcd,init,clear,print,print_char,set_position,set_custom_char
Export lcd,_cmd,_wait_nbusy,_write_ram,_read_ram,_charcount
Export lcd,_cmd,_wait_nbusy,_write_ram,_read_ram,_set_dd_ram_addr_from_charcount
Export lcd,_charcount
; RAM VARIABLES
.bss
_charcount: .res 1
@ -27,13 +28,13 @@ _charcount: .res 1
sta lcd::LCD_IO+IO::DDRA
; init lcd
lda #lcd::CMD_FUNCTION_SET
lda #lcd::CMD::FUNCTION_SET
jsr _cmd
lda #lcd::CMD_DISPLAY_ON
lda #lcd::CMD::DISPLAY_ON
jsr _cmd
lda #lcd::CMD_CLEAR
lda #lcd::CMD::CLEAR
jsr _cmd
lda #lcd::CMD_ENTRY_MODE
lda #lcd::CMD::ENTRY_MODE
jsr _cmd
stz _charcount
@ -74,24 +75,24 @@ _charcount: .res 1
sta _charcount
pla
pha
ora #lcd::CMD_SET_ADDRESS
ora #lcd::CMD::SET_ADDRESS
jsr _cmd
; and #(<~lcd::CMD_SET_ADDRESS) ; return original argument
; and #(<~lcd::CMD::SET_ADDRESS) ; return original argument
pla
rts
@invalid:
pla ; otherwise stack corruption
lda #$13
sta _charcount
lda #(lcd::CMD_SET_ADDRESS | (lcd::LINE2 + 3))
lda #(lcd::CMD::SET_ADDRESS | (lcd::LINE2 + 3))
jsr _cmd
lda #(lcd::LINE2 + 3)
rts
.endproc
;;********************************************************************************
;; PRINTING TO LCD
;;********************************************************************************
;********************************************************************************
; PRINTING TO LCD
;********************************************************************************
;;********************************************************************************
;; @function Clear the display
@ -100,7 +101,7 @@ _charcount: .res 1
;;********************************************************************************
.proc clear
stz _charcount
lda #lcd::CMD_CLEAR
lda #lcd::CMD::CLEAR
jsr _cmd
rts
.endproc
@ -150,7 +151,7 @@ _charcount: .res 1
rol ; address is bytes 3-5
rol
rol
ora #lcd::CMD_SET_CG_ADDRESS
ora #lcd::CMD::SET_CG_ADDRESS
jsr lcd::_cmd
ldy #0
@loop:
@ -203,7 +204,7 @@ _charcount: .res 1
;;********************************************************************************
;; @function Send a command to the lcd
;; @function Send a @ref lcd::CMD "command" to the lcd
;; @param A: command
;; @modifies A
;;********************************************************************************
@ -289,23 +290,24 @@ _charcount: .res 1
;; @function Set the LCD DD-RAM Address from the character count
;; @details
;; Sets the DD-RAM Address so that a line break occurs after 16 characters
;; If _charcount is more than $40, the position will be set to line 1.
;; If _charcount is more than $40, the position will be set to _charcount % $40
;;********************************************************************************
.proc _set_dd_ram_addr_from__charcount
.proc _set_dd_ram_addr_from_charcount
cmp #$40 ; set to line1 when full
bcs @wrap_to_line1
bcs @wrap_to_line1 ; a € [$40, $ff]
cmp #$10
bcc @line1
bcc @line1 ; a € [$00, $0f]
cmp #$20
bcc @line3
; bra @line4
bcc @line2 ; a € [$10, $1f]
cmp #$30
bcc @line3 ; a € [$20, $2f]
; bra @line4; a € [$30, $3f]
@line4:
clc
adc #$20 ; _charcount $30-$3f -> $50 - $5f
bra @set_address
@wrap_to_line1:
sbc #$40
and #%00001111
and #%00111111
; now every _charcount is mapped between 0 and $3f
bra @set_address
@line2:
@ -317,7 +319,7 @@ _charcount: .res 1
bra @set_address
@line1: ; _charcount $00-$1f -> $00 - $0f - nothing to do
@set_address:
ora #lcd::CMD_SET_ADDRESS
ora #lcd::CMD::SET_ADDRESS
jsr _cmd
rts
.endproc
@ -350,19 +352,19 @@ _charcount: .res 1
rts
@line1:
stz _charcount
lda #(lcd::CMD_SET_ADDRESS | lcd::LINE1)
lda #(lcd::CMD::SET_ADDRESS | lcd::LINE1)
jsr _cmd
rts
@line2:
lda #(lcd::CMD_SET_ADDRESS | lcd::LINE2)
lda #(lcd::CMD::SET_ADDRESS | lcd::LINE2)
jsr _cmd
rts
@line3:
lda #(lcd::CMD_SET_ADDRESS | lcd::LINE3)
lda #(lcd::CMD::SET_ADDRESS | lcd::LINE3)
jsr _cmd
rts
@line4:
lda #(lcd::CMD_SET_ADDRESS | lcd::LINE4)
lda #(lcd::CMD::SET_ADDRESS | lcd::LINE4)
jsr _cmd
rts
.endproc

View File

@ -3,7 +3,7 @@
;; @type driver
;; @details:
;; Support for a PS2 Keyboard using the shift register of a 6522 VIA
;; @section Reading a scancode/command answer
;; @section reading Reading a scancode/command answer
;; Pressing a key causes 11 bits to be sent: 1 start - 8 scancode - 1 parity - 1 stop
;; The VIA is set up to interrupt after 8 bits have been shifted into the shift register
;; from the external clock pulses of the keyboard (additional hardware required to
@ -12,10 +12,10 @@
;; the VIAs T2 timer is set to interrupt after the last 3 bits have been shifted in,
;; which takes about ~230ms.
;; T2 is used because it leaves to more versatile T1 available for something else
;; @section Processing a scancode
;;
;; @section processing Processing a scancode
;; The scancode may be processed by storing the address of a handler subroutine in
;; ps2kb::scancode_handler. This handler can load the scancode byte from ps2kb::scancode.
;; `ps2kb::scancode_handler`. This handler can load the scancode byte from `ps2kb::scancode`.
;;
;;
;; To use a different layout, change the enum K and the CHARS_NOMOD and CHARS_MODSHIFT

View File

@ -21,7 +21,7 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
.code
; these are macros the save time during the interrupt handler (no jsr, rts)
; these are macros and not subroutines to save time during the interrupt handler (no jsr, rts)
;;********************************************************************************
;; @macro Enable the clock signal from the keyboard
;; @modifies: A
@ -242,7 +242,7 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
;; - 0: command response
;; - 1: NO_RESPONSE or additional response byte (identify keyboard command only)
;; - 2: NO_RESPONSE or additional response byte (identify keyboard command only)
;;
;; If the command did have a data byte, cmd_response is:
;; - 0: 0xFA (ACK) or 0xFE (Resend/Error)
;; - 1: 0xFA (ACK) or 0xFE (Resend/Error)
@ -399,7 +399,6 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
;; - 0: command response
;; - 1: NO_RESPONSE or additional response byte (identify keyboard command only)
;; - 2: NO_RESPONSE or additional response byte (identify keyboard command only)
;; If the command did have a data byte, cmd_response is:
;; - 0: 0xFA (ACK) or 0xFE (Resend/Error)
;; - 1: 0xFA (ACK) or 0xFE (Resend/Error)

View File

@ -10,13 +10,22 @@ INCLUDE_SPI = 1
.include "system/system.h65"
;;********************************************************************************
;; @brief SPI-Peripheral
;;********************************************************************************
.scope spi_p
SPI_IO = IO2
Import spi_p, init, irq_handler, status, buffer_size, recv_size
ImportZp spi_p, buffer_ptr
Import spi_p, begin_read, end_read, irq_read_byte, recv_bytes
Import spi_p, begin_write, end_write, irq_write_byte, sent_bytes
;;********************************************************************************
;; @brief Status of a SPI transfer
;;********************************************************************************
.enum STATUS
XFER_SIZEL = %10000000
XFER_SIZEH = %01000000

View File

@ -206,12 +206,12 @@ end_write:
rts
.endproc
;;;********************************************************************************
;;; @function Write a byte
;;; @details
;;; Write a byte to the spi shift reister
;;; @modifies A,Y
;;;********************************************************************************
;********************************************************************************
; @function Write a byte
; @details
; Write a byte to the spi shift reister
; @modifies A,Y
;********************************************************************************
;.proc irq_write_byte
; ldy sent_bytes
; lda (buffer_ptr),y

View File

@ -10,12 +10,21 @@ INCLUDE_STRING = 1
.include "system/system.h65"
;;********************************************************************************
;; @brief String utility
;;********************************************************************************
.scope str
Import str, strf, printf_buffer
Import str, hex_char_to_uint8, hex_str_to_uint
Import str, uint8_to_hex_str, uint_to_hex_str
;;********************************************************************************
;; @macro Helper for str::Strf macro
;; @details
;; Store arg in ARG4-ARG... and increment counter variable @N_ARGS one.
;; Arg can be x, y, immediate or an address
;;********************************************************************************
.macro _StrfStoreArg arg
.if (.not .blank(arg))
.if .match(arg, x)
@ -31,13 +40,13 @@ Import str, uint8_to_hex_str, uint_to_hex_str
.endmacro
;;********************************************************************************
;; @function Macro for passing arguments to strf
;; @macro Macro for passing arguments to strf
;; @param fmt: Format string address or string literal
;; @param out: Output string address
;; @param x0-x9: Additional parameters
;; @warning Addresses as additional paramters must be passed like this `#<addr,#>addr`
;; @modifies: A, X, Y, ARG4, ARG5
;; @see strf
;; @see str::strf
;;********************************************************************************
.macro Strf fmt,out,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9
@N_ARGS .set 0 ; @ so that it doesnt break cheap labels
@ -74,6 +83,12 @@ Import str, uint8_to_hex_str, uint_to_hex_str
; .out .sprintf("info: Strf: called with %d arguments", @N_ARGS)
.endmacro
;;********************************************************************************
;; @function Macro for formated printing
;; @details
;; Pass all arguments to str::Strf macro, then call lcd::PrintNC macro
;; @see str::strf
;;********************************************************************************
.macro Printf fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9
Strf fmt,str::printf_buffer,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9
PrintNC str::printf_buffer