add docs with doxygen using custom filter
This commit is contained in:
parent
5ee9610616
commit
8b2e7c2752
2861
.doxygen_config
Normal file
2861
.doxygen_config
Normal file
File diff suppressed because it is too large
Load Diff
4
Makefile
4
Makefile
@ -57,3 +57,7 @@ clean:
|
||||
rm -r $(OBJ_DIR)
|
||||
rm $(ROM)
|
||||
rm $(SPI)
|
||||
|
||||
docs:
|
||||
doxygen .doxygen_config
|
||||
|
||||
|
181
doxy-asm65.py
Normal file
181
doxy-asm65.py
Normal 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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
13
spicode.s65
13
spicode.s65
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user