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 -r $(OBJ_DIR)
rm $(ROM) rm $(ROM)
rm $(SPI) 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. ;; This module processes keyboard scancodes from the ps2_keyboard driver.
;; It requires a PS/2 keyboard that supports scancode set 3. ;; It requires a PS/2 keyboard that supports scancode set 3.
;; ;;
;; @section What it does ;; @section what What it does
;; - swallow break (release) scancodes ;; - swallow break (release) scancodes
;; ;;
;; This handler is designed for debug purposes. ;; This handler is designed for debug purposes.
;; ;;
;; @section How to use it ;; @section usage How to use it
;; Call `kb::init` and check if it was successful (Z = 1). ;; 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. ;; In your program loop, check if kb::scancode is 0. If it is not, a key has been pressed.
;; ;;
;; @subsection Changing the keyboard behaviour ;; @subsection change_behaviour Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3) ;; 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. ;; 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 .ifndef INCLUDE_KEYBOARD_SIMPLE
INCLUDE_KEYBOARD_SIMPLE = 1 INCLUDE_KEYBOARD_SIMPLE = 1
.include "ps2_keyboard.h65" .include "ps2_keyboard.h65"
;;********************************************************************************
;; @brief Simple Keyboard Handler
;;********************************************************************************
.scope skb .scope skb
Import skb, init, scancode Import skb, init, scancode

View File

@ -1,69 +1,76 @@
; normal
;;******************************************************************************** ;;********************************************************************************
;; @module ps2_keyboard_text_handler ;; @module ps2_keyboard_text_handler
;; @type system ;; @type system
;; @details: ;; @details
;; This module processes keyboard scancodes from the ps2_keyboard driver. ;; This module processes keyboard scancodes from the ps2_keyboard driver.
;; It requires a PS/2 keyboard that supports scancode set 3. ;; 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), ;; - keeps track of modifier keys: shift, left/right alt, left right meta (super),
;; control, capslock, numlock, scrollock ;; control, capslock, numlock, scrollock
;; - convert valid keycodes to chars, dependening on the state of shift and right alt ;; - convert valid keycodes to chars, dependening on the state of shift and right alt
;; - update the keyboard LEDs (numlock, capslock,, scrolllock) ;; - update the keyboard LEDs (numlock, capslock,, scrolllock)
;;
;; This handler is designed for efficient text input. ;; 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. ;; It does not track the status of the keys (except modifiers) and is therefore not suitable for games.
;; ;;
;; @section How it works ;; @section working_principle How it works
;; In the init function, the module disables the typematic behaviour for the modifier keys by ;; 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 ;; sending commands to the keyboard. The 3 lock keys will be set to *make only* mode and
;; the others to make release. ;; the others to *make release*.
;; This makes tracking the modifier keys much easier and is the reason why scancode set 3 ;; 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, ;; Using scancode set 3 also adds the benefit that all scancodes are only 1 byte large,
;; making the recognition of scancodes trivial. ;; 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. ;; 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: ;; 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 ;; 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. ;; 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, ;; 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. ;; 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. ;; 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 ;; @section usage How to use it
;; Call `kb::init` and check if it was successful (Z = 1). ;; 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. ;; 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. ;; 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` ;; 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. ;; 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 ;; using the `bbs` and `bbr` instructions, as each modifier is represented by a bit of the `kb::modifier` variable
;; in the zeropage. ;; in the zeropage.
;; ;;
;; @subsection Changing the keyboard behaviour ;; @subsection change_behaviour Changing the keyboard behaviour
;; You can change the typematic rate and delay (see PS/2 keyboard command 0xF3) ;; 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. ;; 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 .ifndef INCLUDE_KEYBOARD_TEXT
INCLUDE_KEYBOARD_TEXT = 1 INCLUDE_KEYBOARD_TEXT = 1
.include "ps2_keyboard.h65" .include "ps2_keyboard.h65"
;;********************************************************************************
;; @brief Keyboard Handler
;;********************************************************************************
.scope kb .scope kb
Import kb, init, char, keycode Import kb, init, char, keycode
Import kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON Import kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON
ImportZp kb, modifier ImportZp kb, modifier
;;********************************************************************************
;; @brief Bitmask for checking modifier keys
;;********************************************************************************
.enum MOD .enum MOD
SHIFT = %10000000 ; set if either capslock is on or shift is pressed SHIFT = %10000000 ; set if either capslock is on or shift is pressed
RALT = %01000000 ; set while rightalt is pressed RALT = %01000000 ; set while rightalt is pressed
@ -75,8 +82,12 @@ ImportZp kb, modifier
SCROLLLOCK = %00000001 ; ps/2 LED SCROLLLOCK = %00000001 ; ps/2 LED
.endenum .endenum
;; @brief Scancode that is sent by the keyboard when a key is released
K_BREAK = $F0 K_BREAK = $F0
;;********************************************************************************
;; @brief S3 German Keycodes
;;********************************************************************************
.enum K .enum K
F1 = $07 F1 = $07
ESCAPE = $08 ESCAPE = $08

View File

@ -4,11 +4,16 @@ Export kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_
ExportZp kb, modifier ExportZp kb, modifier
.zeropage .zeropage
modifier: .res 1 ;; @ref kb::MOD "status of the modifier keys"
modifier: .byte
.bss .bss
previous_scancode: .res 1 ; all 1 if previous_scancode is break ;; previous scancode is used to determine if a key was released (BREAK sent previously)
char: .res 1 ;; will be $ff if the previous scancode was K_BREAK
keycode: .res 1 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 .code
;;******************************************************************************** ;;********************************************************************************
@ -101,8 +106,8 @@ set:
;; @function Process a scancode ;; @function Process a scancode
;; @details ;; @details
;; Update modifier bits if a mod key is pressed. ;; 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 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 kb::char ;; If the key is an ascii character, the corresponding ascii character is stored in @ref kb::char
;; @modifies A, X ;; @modifies A, X
;;******************************************************************************** ;;********************************************************************************
.proc process_scancode .proc process_scancode
@ -280,6 +285,7 @@ CHARS_NOMOD:
.byte "\x00nbhgz6\x00\x00\x01mju78\x00" .byte "\x00nbhgz6\x00\x00\x01mju78\x00"
.byte "\x00,kio09\x00\x00.-l\xEFp\xE2\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" .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: 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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
.byte "\x00\x00\x00\x00\x00\x00\x01/\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 jsr kb::init
loop: loop:
lda kb::char lda kb::char
ldx ptr
beq @no_char beq @no_char
stz kb::char stz kb::char
jsr lcd::print_char jsr lcd::print_char
@ -42,6 +43,9 @@ clear_display:
jsr lcd::clear jsr lcd::clear
bra loop bra loop
k_backspace: k_backspace:
; go back lcd_DecrementCursor
lda ' ' lda #' '
jsr lcd::print_char
lcd_DecrementCursor
bra loop
.endproc .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 "system.h65"
.include "string.h65" .include "string.h65"
.include "lcd.h65" .include "lcd.h65"
@ -74,6 +81,12 @@ CODE_START:
@l1: @l1:
jmp _ps2_keyboard_printer jmp _ps2_keyboard_printer
@l2: @l2:
lcd_SetCursorPos $26
lda #'6'
jsr lcd::print_char
lcd_IncrementCursor
lda #'8'
jsr lcd::print_char
@l3: @l3:
@l4: @l4:
@l5: @l5:

View File

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

View File

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

View File

@ -3,7 +3,7 @@
;; @type driver ;; @type driver
;; @details: ;; @details:
;; Support for a PS2 Keyboard using the shift register of a 6522 VIA ;; 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 ;; 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 ;; 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 ;; 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, ;; the VIAs T2 timer is set to interrupt after the last 3 bits have been shifted in,
;; which takes about ~230ms. ;; which takes about ~230ms.
;; T2 is used because it leaves to more versatile T1 available for something else ;; 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 ;; 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 ;; 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 .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 ;; @macro Enable the clock signal from the keyboard
;; @modifies: A ;; @modifies: A
@ -242,7 +242,7 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
;; - 0: command response ;; - 0: command response
;; - 1: NO_RESPONSE or additional response byte (identify keyboard command only) ;; - 1: NO_RESPONSE or additional response byte (identify keyboard command only)
;; - 2: 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: ;; If the command did have a data byte, cmd_response is:
;; - 0: 0xFA (ACK) or 0xFE (Resend/Error) ;; - 0: 0xFA (ACK) or 0xFE (Resend/Error)
;; - 1: 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 ;; - 0: command response
;; - 1: NO_RESPONSE or additional response byte (identify keyboard command only) ;; - 1: NO_RESPONSE or additional response byte (identify keyboard command only)
;; - 2: 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: ;; If the command did have a data byte, cmd_response is:
;; - 0: 0xFA (ACK) or 0xFE (Resend/Error) ;; - 0: 0xFA (ACK) or 0xFE (Resend/Error)
;; - 1: 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" .include "system/system.h65"
;;********************************************************************************
;; @brief SPI-Peripheral
;;********************************************************************************
.scope spi_p .scope spi_p
SPI_IO = IO2 SPI_IO = IO2
Import spi_p, init, irq_handler, status, buffer_size, recv_size Import spi_p, init, irq_handler, status, buffer_size, recv_size
ImportZp spi_p, buffer_ptr ImportZp spi_p, buffer_ptr
Import spi_p, begin_read, end_read, irq_read_byte, recv_bytes Import spi_p, begin_read, end_read, irq_read_byte, recv_bytes
Import spi_p, begin_write, end_write, irq_write_byte, sent_bytes Import spi_p, begin_write, end_write, irq_write_byte, sent_bytes
;;********************************************************************************
;; @brief Status of a SPI transfer
;;********************************************************************************
.enum STATUS .enum STATUS
XFER_SIZEL = %10000000 XFER_SIZEL = %10000000
XFER_SIZEH = %01000000 XFER_SIZEH = %01000000

View File

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

View File

@ -10,12 +10,21 @@ INCLUDE_STRING = 1
.include "system/system.h65" .include "system/system.h65"
;;********************************************************************************
;; @brief String utility
;;********************************************************************************
.scope str .scope str
Import str, strf, printf_buffer Import str, strf, printf_buffer
Import str, hex_char_to_uint8, hex_str_to_uint Import str, hex_char_to_uint8, hex_str_to_uint
Import str, uint8_to_hex_str, uint_to_hex_str 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 .macro _StrfStoreArg arg
.if (.not .blank(arg)) .if (.not .blank(arg))
.if .match(arg, x) .if .match(arg, x)
@ -31,13 +40,13 @@ Import str, uint8_to_hex_str, uint_to_hex_str
.endmacro .endmacro
;;******************************************************************************** ;;********************************************************************************
;; @function Macro for passing arguments to strf ;; @macro Macro for passing arguments to strf
;; @param fmt: Format string address or string literal ;; @param fmt: Format string address or string literal
;; @param out: Output string address ;; @param out: Output string address
;; @param x0-x9: Additional parameters ;; @param x0-x9: Additional parameters
;; @warning Addresses as additional paramters must be passed like this `#<addr,#>addr` ;; @warning Addresses as additional paramters must be passed like this `#<addr,#>addr`
;; @modifies: A, X, Y, ARG4, ARG5 ;; @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 .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 @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) ; .out .sprintf("info: Strf: called with %d arguments", @N_ARGS)
.endmacro .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 .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 Strf fmt,str::printf_buffer,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9
PrintNC str::printf_buffer PrintNC str::printf_buffer