Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
ed279e4776 | |||
bf6a5efd7e | |||
7fce58e481 | |||
d86ac8d52d | |||
8b2e7c2752 | |||
|
5ee9610616 | ||
|
c61de15048 | ||
eb2fb061a0 | |||
64492a5f6b | |||
9867e4a119 | |||
4e76be17ad | |||
5c02ff6585 | |||
9060dfcbbf | |||
aa55d5fb37 | |||
bd083150c5 | |||
e61871ed0f | |||
326b2f6247 | |||
e8696cc312 | |||
23e5e3cd35 | |||
b778b4dcd1 | |||
2a13ce0ca4 | |||
108e5af0b9 | |||
e7bd5096cd | |||
aebd813a55 | |||
808900602e | |||
|
e1f78b2927 | ||
|
7f909b4f75 | ||
b60ff7be12 | |||
2bfae04f50 | |||
ff4e284f26 | |||
5767133ae3 | |||
dce5a43804 | |||
b65a0249a5 | |||
de098dd9c6 | |||
498b9803f1 | |||
45dce42475 | |||
|
302e49a9f7 | ||
|
3d773390bf | ||
|
27a901c7c0 | ||
|
4d5edeecfe | ||
|
fc0dba6c9c | ||
|
25d0fcfb90 | ||
|
eb02613837 | ||
|
9e9978669a | ||
|
7b97ca096d | ||
|
2d06c9700b |
2861
.doxygen_config
Normal file
2861
.doxygen_config
Normal file
File diff suppressed because it is too large
Load Diff
72
.extra_docs.s65
Normal file
72
.extra_docs.s65
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
;;********************************************************************************
|
||||||
|
;; @mainpage 8-bit Breadboard Computer with W65C02S Processor
|
||||||
|
;; This repo contains the assembly code for my [6502-project](https://quintern.xyz/de/posts/2021_6502).
|
||||||
|
;;
|
||||||
|
;; The assembler used for this project is the excellent [ca65](https://github.com/cc65/cc65).
|
||||||
|
;; After assembling it, the binary is loaded onto the EEPROM using [my *eeprom.py* script](https://git.quintern.xyz/MatthiasQuintern/AT28C256-rpi-util) on a Raspberry Pi 4B.
|
||||||
|
;;
|
||||||
|
;; @section main_os Operating System
|
||||||
|
;; ... is probably a far stretch, since it is just the programs I wrote pieced together. My "OS" consists of these functionalities:
|
||||||
|
;; - Supported Hardware:
|
||||||
|
;; - 4x4 Matrix Keypad
|
||||||
|
;; - 4x16 Characters LCD Display
|
||||||
|
;; - PS/2 Keyboard
|
||||||
|
;; - SPI Connection to an Arduino
|
||||||
|
;; - Software
|
||||||
|
;; - @ref ps2_keyboard_printer "Keyboard" and @ref keypad_printer "keypad printer": Prints the characters you press on to the lcd
|
||||||
|
;; <!-- - Temperature: Shows the temperature using a dht sensor. *Work in progress, this does not work yet* -->
|
||||||
|
;; - @ref print_slow "(Stylishly)" print @ref "str::strf" "formated strings" to the LCD
|
||||||
|
;; - @ref sleep "Sleep"
|
||||||
|
;; - Send @ref ps2_keyboard_util "various commands" to the keyboard
|
||||||
|
;; - @ref ringbuffer "Ringbuffer"
|
||||||
|
;;
|
||||||
|
;; > It's not much, but it's honest work.
|
||||||
|
;;
|
||||||
|
;; @section main_hardware Hardware details
|
||||||
|
;; @subsection main_hw_addr Address Space
|
||||||
|
;; |Name|From|To|r/w|
|
||||||
|
;; |---|---:|---:|:---:|
|
||||||
|
;; |ZEROPAGE |$00 |$ff |`rw`|
|
||||||
|
;; |STACK |$100 |$1ff |`rw`|
|
||||||
|
;; |RAM |$200 |$4fff |`rw`|
|
||||||
|
;; |SPI |$5000 |$5fff |`rw`|
|
||||||
|
;; |VIA1 |$6000 |$600f |`rw`|
|
||||||
|
;; |VIA2 |$7000 |$700f |`rw`|
|
||||||
|
;; |ROM |$8000 |$ffff |`r `|
|
||||||
|
;;
|
||||||
|
;;
|
||||||
|
;; @section main_software Software details
|
||||||
|
;; @subsection main_sw_naming Naming conventions
|
||||||
|
;; leading underscors `_` indicate a "private" label/variable, that is meant for internal use within the module only.
|
||||||
|
;;
|
||||||
|
;; **Labels**:
|
||||||
|
;; - **scopes**: snake case
|
||||||
|
;; - **subroutines** and **variables**: snake case (`scope::(_)fname_snake_case` or `scope::(_)varname_2`)
|
||||||
|
;; - **macros**: camel case (`(_)GoodMacroname` or `scope_GoodMacroname`)
|
||||||
|
;; - **constants** (eg. in ROM): upper case (`scope::(_)NICE_SYMBOLNAME`)
|
||||||
|
;; - **enums**: upper case (`scope::(_)ENUM_NAME::ENUM_MEMBER`)
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @defgroup applications
|
||||||
|
;; @brief Applications that do ... *something*
|
||||||
|
;;
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @defgroup drivers
|
||||||
|
;; @brief Code that handles a physical device
|
||||||
|
;;
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @defgroup libs Libraries
|
||||||
|
;; @brief Code that does something useful and is intended to be used in other code
|
||||||
|
;;
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @defgroup system
|
||||||
|
;; @brief Core code
|
||||||
|
;;
|
||||||
|
;;********************************************************************************
|
4
Makefile
4
Makefile
@ -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
|
||||||
|
|
||||||
|
19
details.md
19
details.md
@ -1,19 +0,0 @@
|
|||||||
# Project details
|
|
||||||
## Address Space
|
|
||||||
|Name|From|To|r/w|
|
|
||||||
|---|---:|---:|:---:|
|
|
||||||
|ZEROPAGE |$00 |$ff |`rw`|
|
|
||||||
|STACK |$100 |$1ff |`rw`|
|
|
||||||
|RAM |$200 |$4fff |`rw`|
|
|
||||||
|SPI |$5000 |$5fff |`rw`|
|
|
||||||
|VIA1 |$6000 |$600f |`rw`|
|
|
||||||
|VIA2 |$7000 |$700f |`rw`|
|
|
||||||
|ROM |$8000 |$ffff |`r `|
|
|
||||||
|
|
||||||
## Naming conventions
|
|
||||||
leading underscors `_` indicate a "private" label/variable, that is meant for internal use within the module only.
|
|
||||||
### Labels
|
|
||||||
- **scopes**: snake case
|
|
||||||
- **subroutines** and **variables**: snake case (`scope::(_)fname_snake_case` or `scope::(_)varname_2`)
|
|
||||||
- **macros**: camel case (`(_)GoodMacroname` or `scope_GoodMacroname`)
|
|
||||||
- **constants** (eg. in ROM): upper case (`scope::(_)NICE_SYMBOLNAME`)
|
|
219
doxy-asm65.py
Normal file
219
doxy-asm65.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import sys
|
||||||
|
import re
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
##
|
||||||
|
# @defgroup documentation Documentation
|
||||||
|
# @brief
|
||||||
|
#
|
||||||
|
|
||||||
|
##
|
||||||
|
# @file
|
||||||
|
# @brief Doxygen filter for ca65 assembler files
|
||||||
|
# @details
|
||||||
|
# This filter converts ca65 to C++ statements that doxygen can parse.
|
||||||
|
#
|
||||||
|
# Doxygen comments are double semicolons `;;`
|
||||||
|
# - turns procedures `.proc` into function statements with `proc` as return type
|
||||||
|
# Parameters documented with the @param command are put into the paranthesis (function arguments) with `Param` as type (@ref handle_procedure)
|
||||||
|
# - turns macros `.macro` into function statements with `macro` as return type with the parameter macros
|
||||||
|
# as function arguments with `Param` type (@ref handle_procedure)
|
||||||
|
# - enums become ... enums, documentation of enum members after their name is also handled when using ;;< (@ref handle_procedure)
|
||||||
|
# - labeled storage allocations with `.byte`, `.res`, `.ascii` etc. are turned into variable declarations with the label as variable name (@ref handle_procedure)
|
||||||
|
# - if allocations are strings, they are concatenated together to `char * LABEL_NAME = "<string(s)>";`
|
||||||
|
# - if there are multiple non-string allocations: `bytes LABEL_NAME[] = {alloc1, alloc2, ...};`
|
||||||
|
# - if there is one non-string allocation: `alloc_type * LABEL_NAME = alloc;`
|
||||||
|
# - if the allocation is not initilized: `alloc_type * LABEL_NAME;`
|
||||||
|
# - if the allocation type is `res SIZE`: `bytes LABEL_NAME[SIZE];`
|
||||||
|
# - the sizes of the arrays may be wrong!
|
||||||
|
# - include statements are kept
|
||||||
|
# - all other preprocessor macros are removed
|
||||||
|
#
|
||||||
|
# @todo Handle structs
|
||||||
|
# @todo for storage allocators, check in which segment they are in and apply `const` where necessary
|
||||||
|
# @ingroup documentation
|
||||||
|
|
||||||
|
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"
|
||||||
|
elif p_type == "enum":
|
||||||
|
p_code = re.sub(r"(.*=.*?)( *(?:;;.*)?\n)", r"\1,\2", 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]
|
||||||
|
l_name = m.groups()[1]
|
||||||
|
l_allocs = m.groups()[2]
|
||||||
|
l_docs2 = m.groups()[3] # if doc was in the same line as the label
|
||||||
|
|
||||||
|
storage_alloc = r"\.(byte|res|dbyte|word|addr|faraddr|dword|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)
|
||||||
|
|
||||||
|
# pdebug(f"Storage label {l_name} with allocs '{[ma.group() for ma in allocs]}' and args '{args}'\n\t{m.groups()}")
|
||||||
|
s = ""
|
||||||
|
in_namespace = False
|
||||||
|
# if the label is exported, put it in a namespace
|
||||||
|
if l_name in exported_names:
|
||||||
|
in_namespace = True
|
||||||
|
namespace = exported_names[l_name]
|
||||||
|
s += f"namespace {namespace}" + " {"
|
||||||
|
# docs after the namespace, otherwise they document the namespace
|
||||||
|
if l_docs:
|
||||||
|
s += l_docs
|
||||||
|
# put the single line comment into a /** */ comment in front of the declaration
|
||||||
|
if l_docs2:
|
||||||
|
s += "/** "
|
||||||
|
if not "brief" in l_docs2: s += "@brief "
|
||||||
|
s += f"{l_docs2.strip(';')} */ "
|
||||||
|
# completely ignoring the type of the storage allocation here
|
||||||
|
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 += f"bytes {l_name}[{len(args)}] = " + "{"
|
||||||
|
for arg in args:
|
||||||
|
s += arg + ","
|
||||||
|
s = s.strip(",") + "}"
|
||||||
|
else:
|
||||||
|
l_type = allocs[0].groups()[0]
|
||||||
|
if len(args) == 0:
|
||||||
|
l_arg = None
|
||||||
|
else:
|
||||||
|
l_arg = args[0]
|
||||||
|
# if res: use bytes[length] as type
|
||||||
|
if l_type == "res":
|
||||||
|
l_type = f"bytes[{l_arg}]"
|
||||||
|
l_arg = None
|
||||||
|
# else use type* as type
|
||||||
|
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|asciiz?)(?:[, ]+(?:0x[a-fA-F0-9]+|0b[01]+|\d+|\w+|\"[^\n]*[^\\\n]\")[ \n]*)*)+)(;;.*)?": handle_storage_label,
|
||||||
|
r"^INCLUDE_[A-Z0-9_]+ *= *1$": r"", # Include guard variables
|
||||||
|
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():
|
||||||
|
(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()
|
||||||
|
|
@ -3,10 +3,11 @@ MEMORY {
|
|||||||
# RAM: start = $0100, size = $5eff, type = rw, file = "", fill = yes;
|
# RAM: start = $0100, size = $5eff, type = rw, file = "", fill = yes;
|
||||||
STACK: start = $0100, size = $0100, type = rw, file = "", fill = yes;
|
STACK: start = $0100, size = $0100, type = rw, file = "", fill = yes;
|
||||||
RAM: start = $0200, size = $4e00, type = rw, file = "", fill = yes;
|
RAM: start = $0200, size = $4e00, type = rw, file = "", fill = yes;
|
||||||
SPI: start = $5000, size = $1000, type = rw, file = "../spi.bin", fill = no;
|
SPI: start = $5000, size = $1000, type = rw, file = "../spi.bin", fill = no, define = yes;
|
||||||
VIA1: start = $6000, size = $000f, type = rw, file = "", fill = yes;
|
VIA1: start = $6000, size = $000f, type = rw, file = "", fill = yes;
|
||||||
VIA2: start = $7000, size = $000f, type = rw, file = "", fill = yes;
|
VIA2: start = $7000, size = $000f, type = rw, file = "", fill = yes;
|
||||||
ROM: start = $8000, size = $8000, type = ro, file = %O, fill = yes;
|
ROM: start = $8000, size = $8000, type = ro, file = %O, fill = yes;
|
||||||
|
TEST: start = $9000, size = $1000, type = rw, file = "../test.bin";
|
||||||
}
|
}
|
||||||
SEGMENTS {
|
SEGMENTS {
|
||||||
VIA1: load = VIA1, type = bss;
|
VIA1: load = VIA1, type = bss;
|
||||||
@ -14,7 +15,8 @@ SEGMENTS {
|
|||||||
ZEROPAGE: load = RAM_ZP, type = bss, start = $0;
|
ZEROPAGE: load = RAM_ZP, type = bss, start = $0;
|
||||||
BSS: load = RAM, type = bss;
|
BSS: load = RAM, type = bss;
|
||||||
CODE: load = ROM, type = ro;
|
CODE: load = ROM, type = ro;
|
||||||
RODATA: load = ROM, type = ro;
|
RODATA: load = ROM, type = ro, align = $800;
|
||||||
RESET_VECTOR: load = ROM, type = ro, start = $FFFA;
|
RESET_VECTOR: load = ROM, type = ro, start = $FFFA;
|
||||||
SPI: load = SPI, type = rw;
|
SPI: load = SPI, type = rw;
|
||||||
|
TEST: load = TEST,type = rw;
|
||||||
}
|
}
|
||||||
|
136
main.s65
136
main.s65
@ -1,34 +1,13 @@
|
|||||||
.include "system/system.h65"
|
.include "system/system.h65"
|
||||||
|
.include "string.h65"
|
||||||
.export home,homeloop
|
.export home,homeloop
|
||||||
.import printer:absolute
|
.import keypad_printer:absolute
|
||||||
.import spi_menu:absolute
|
.import spi_menu:absolute
|
||||||
|
.import ps2_keyboard_printer:absolute
|
||||||
|
.import ps2_keyboard_util:absolute
|
||||||
|
|
||||||
.code
|
.code
|
||||||
|
|
||||||
.macro DEBUG_LED_OFF nr
|
|
||||||
pha
|
|
||||||
lda IO1 + IO::RA
|
|
||||||
.if nr = 0
|
|
||||||
and #%11111110
|
|
||||||
.else
|
|
||||||
and #%11111101
|
|
||||||
.endif
|
|
||||||
sta IO1 + IO::RA
|
|
||||||
pla
|
|
||||||
.endmacro
|
|
||||||
|
|
||||||
.macro DEBUG_LED_ON nr
|
|
||||||
pha
|
|
||||||
lda IO1 + IO::RA
|
|
||||||
.if nr = 0
|
|
||||||
ora #%00000001
|
|
||||||
.else
|
|
||||||
ora #%00000010
|
|
||||||
.endif
|
|
||||||
sta IO1 + IO::RA
|
|
||||||
pla
|
|
||||||
.endmacro
|
|
||||||
|
|
||||||
;********************************************************************************
|
;********************************************************************************
|
||||||
; Modules
|
; Modules
|
||||||
;********************************************************************************
|
;********************************************************************************
|
||||||
@ -62,12 +41,17 @@ nmi:
|
|||||||
rti
|
rti
|
||||||
irq:
|
irq:
|
||||||
; read IRFs, while bit 7 ist set handle interrupts
|
; read IRFs, while bit 7 ist set handle interrupts
|
||||||
|
; DEBUG_LED_ON 2
|
||||||
@irq_io1:
|
@irq_io1:
|
||||||
lda IO1 + IO::IFR
|
lda IO1 + IO::IFR
|
||||||
|
and IO1 + IO::IER ; sometimes CB1/2 IFR set even though not enabled in IER
|
||||||
sta irq_via_ifr
|
sta irq_via_ifr
|
||||||
bbr7 irq_via_ifr,@irq_io2 ; skip
|
bbr7 irq_via_ifr,@irq_io2 ; skip
|
||||||
bbs2 irq_via_ifr,@irq_kb1 ; shit reg -> first 8 bits
|
bbs2 irq_via_ifr,@irq_kb_sr
|
||||||
bbs5 irq_via_ifr,@irq_kb2 ; timer -> last 3 bits
|
bbs3 irq_via_ifr,@irq_kb_cb2
|
||||||
|
bbs4 irq_via_ifr,@irq_kb_cb1
|
||||||
|
bbs5 irq_via_ifr,@irq_kb_t2
|
||||||
|
bra @irq_unknown
|
||||||
@irq_io2:
|
@irq_io2:
|
||||||
lda IO2 + IO::IFR
|
lda IO2 + IO::IFR
|
||||||
sta irq_via_ifr
|
sta irq_via_ifr
|
||||||
@ -75,29 +59,31 @@ irq:
|
|||||||
bbs2 irq_via_ifr,@irq_spi_p ; check SR
|
bbs2 irq_via_ifr,@irq_spi_p ; check SR
|
||||||
bbs1 irq_via_ifr,@irq_keypad ; check CA1
|
bbs1 irq_via_ifr,@irq_keypad ; check CA1
|
||||||
|
|
||||||
|
@irq_unknown:
|
||||||
; this SHOULD never be reached
|
; this SHOULD never be reached
|
||||||
jsr lcd::clear
|
|
||||||
Print "Unknown IRQ"
|
Print "Unknown IRQ"
|
||||||
; force reset interrupt flags
|
; force reset interrupt flags
|
||||||
lda #$ff
|
lda #$ff
|
||||||
sta IO1 + IO::IFR
|
sta IO1 + IO::IFR
|
||||||
sta IO2 + IO::IFR
|
sta IO2 + IO::IFR
|
||||||
rti
|
bra @irq_return
|
||||||
; bra @irq_return
|
|
||||||
@irq_keypad:
|
@irq_keypad:
|
||||||
jsr kp::read_irq
|
jsr kp::read_irq
|
||||||
bra @irq_return
|
bra @irq_return
|
||||||
@irq_spi_p:
|
@irq_spi_p:
|
||||||
jsr spi_p::read
|
jsr spi_p::irq_read_byte
|
||||||
bra @irq_return
|
|
||||||
@irq_kb1:
|
|
||||||
; PrintNC "<30>"
|
|
||||||
jsr $3000
|
|
||||||
bra @irq_return
|
|
||||||
@irq_kb2:
|
|
||||||
; PrintNC "<31>"
|
|
||||||
jsr $3100
|
|
||||||
bra @irq_return
|
bra @irq_return
|
||||||
|
; jmp (spi_p::irq_handler)
|
||||||
|
; JsrIndirect (spi_p::irq_handler), @irq_return
|
||||||
|
@irq_kb_sr:
|
||||||
|
; Print "$3000"
|
||||||
|
JsrIndirect ($3000), @irq_return
|
||||||
|
@irq_kb_cb2:
|
||||||
|
JsrIndirect ($3020), @irq_return
|
||||||
|
@irq_kb_cb1:
|
||||||
|
JsrIndirect ($3040), @irq_return
|
||||||
|
@irq_kb_t2:
|
||||||
|
JsrIndirect ($3060), @irq_return
|
||||||
; @irq_dht:
|
; @irq_dht:
|
||||||
; lda IO1 + IO::T1CL ;T1L2 ; clear interrupt flag
|
; lda IO1 + IO::T1CL ;T1L2 ; clear interrupt flag
|
||||||
; bra @irq_return
|
; bra @irq_return
|
||||||
@ -116,25 +102,20 @@ reset:
|
|||||||
sta IO1 + IO::DDRA
|
sta IO1 + IO::DDRA
|
||||||
DEBUG_LED_OFF 0
|
DEBUG_LED_OFF 0
|
||||||
DEBUG_LED_OFF 1
|
DEBUG_LED_OFF 1
|
||||||
|
DEBUG_LED_OFF 1
|
||||||
|
|
||||||
jsr kp::init
|
jsr kp::init
|
||||||
|
|
||||||
SetCustomChar chars::CAT,0
|
DEBUG_LED_ON 0
|
||||||
SetCustomChar chars::SMILEY,1
|
|
||||||
SetCustomChar chars::SMILEY_XD,2
|
|
||||||
|
|
||||||
|
jsr spi_p::init
|
||||||
|
DEBUG_LED_ON 1
|
||||||
|
|
||||||
; ; INIT DHT
|
; SetCustomChar chars::CAT,0
|
||||||
; lda #%11000010 ; enable interrupt for Timer 1 and CA1 on IO2
|
; SetCustomChar chars::SMILEY,1
|
||||||
; sta IER2
|
; SetCustomChar chars::SMILEY_XD,2
|
||||||
; lda #%00111111 ; set Timer 1 to interrupt when loaded
|
|
||||||
; and ACR2
|
; DEBUG_LED_ON 2
|
||||||
; sta ACR2
|
|
||||||
; lda #%00000001 ; set PCR2 bit 0 CA1 pos edge interrupt
|
|
||||||
; ora PCR2
|
|
||||||
; sta PCR2
|
|
||||||
; stz DHT_STATUS
|
|
||||||
|
|
||||||
; enable interrupts
|
; enable interrupts
|
||||||
cli
|
cli
|
||||||
@ -156,7 +137,7 @@ reset:
|
|||||||
lda 0
|
lda 0
|
||||||
; beq home
|
; beq home
|
||||||
cmp #'A'
|
cmp #'A'
|
||||||
jeq printer
|
jeq keypad_printer
|
||||||
cmp #'B'
|
cmp #'B'
|
||||||
jeq spi_menu
|
jeq spi_menu
|
||||||
cmp #'C'
|
cmp #'C'
|
||||||
@ -164,19 +145,30 @@ reset:
|
|||||||
cmp #'D'
|
cmp #'D'
|
||||||
jeq print_2
|
jeq print_2
|
||||||
cmp #'1'
|
cmp #'1'
|
||||||
beq @debug0_on
|
; beq @debug0_on
|
||||||
|
beq @ps2_util
|
||||||
cmp #'2'
|
cmp #'2'
|
||||||
beq @debug0_off
|
beq @ps2_printer
|
||||||
|
; beq @debug0_off
|
||||||
cmp #'4'
|
cmp #'4'
|
||||||
beq @debug1_on
|
beq @debug1_on
|
||||||
cmp #'5'
|
cmp #'5'
|
||||||
beq @debug1_off
|
beq @debug1_off
|
||||||
cmp #'7'
|
cmp #'7'
|
||||||
|
beq @debug2_on
|
||||||
|
cmp #'8'
|
||||||
|
beq @debug2_off
|
||||||
|
cmp #'9'
|
||||||
beq @print_rb
|
beq @print_rb
|
||||||
cmp #'*' ; print home menu again if not visible (message 1 and 2 jmp to home)
|
cmp #'*' ; print home menu again if not visible (message 1 and 2 jmp to home)
|
||||||
beq home
|
beq home
|
||||||
|
wai
|
||||||
jmp @loop
|
jmp @loop
|
||||||
|
|
||||||
|
@ps2_util:
|
||||||
|
jmp ps2_keyboard_util
|
||||||
|
@ps2_printer:
|
||||||
|
jmp ps2_keyboard_printer
|
||||||
@debug0_off:
|
@debug0_off:
|
||||||
DEBUG_LED_OFF 0
|
DEBUG_LED_OFF 0
|
||||||
jmp @loop
|
jmp @loop
|
||||||
@ -189,6 +181,12 @@ reset:
|
|||||||
@debug1_on:
|
@debug1_on:
|
||||||
DEBUG_LED_ON 1
|
DEBUG_LED_ON 1
|
||||||
jmp @loop
|
jmp @loop
|
||||||
|
@debug2_off:
|
||||||
|
DEBUG_LED_OFF 2
|
||||||
|
jmp @loop
|
||||||
|
@debug2_on:
|
||||||
|
DEBUG_LED_ON 2
|
||||||
|
jmp @loop
|
||||||
@print_rb:
|
@print_rb:
|
||||||
jsr lcd::clear
|
jsr lcd::clear
|
||||||
Print str_io2
|
Print str_io2
|
||||||
@ -217,20 +215,20 @@ message_1:
|
|||||||
.byte " **** "
|
.byte " **** "
|
||||||
.asciiz "www.quintern.xyz"
|
.asciiz "www.quintern.xyz"
|
||||||
message_2:
|
message_2:
|
||||||
.byte " Hello "
|
.byte "0123456789=!?#+*"
|
||||||
.byte " there "
|
.byte "ABCDEFGHIJKLMNOP"
|
||||||
.byte " <3 "
|
.byte "QRSTUVWXYZöäüß-_"
|
||||||
.asciiz "================"
|
.asciiz "{[(<>)]}$%&/,;.:"
|
||||||
message_menu:
|
message_menu:
|
||||||
.byte "<A> Printer "
|
.byte "A/2: KP/KB Print"
|
||||||
; .byte "<B> Temperatur "
|
; .byte "<B> Temperatur"
|
||||||
.byte "<B> SPI-Menu "
|
.byte "1: KB Util "
|
||||||
.byte "<C> Text 1 "
|
.byte "B: SPI-Menu "
|
||||||
.asciiz "<D> Text 2 "
|
.asciiz "C/D: Text 1/2 "
|
||||||
str_spi_begin:
|
; str_spi_begin:
|
||||||
.asciiz "---BEGIN SPI---"
|
; .asciiz "---BEGIN SPI---"
|
||||||
str_spi_start:
|
; str_spi_start:
|
||||||
.asciiz "---START SPI---"
|
; .asciiz "---START SPI---"
|
||||||
str_io2:
|
str_io2:
|
||||||
.asciiz "IO2::RB='"
|
.asciiz "IO2::RB='"
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @module SPI
|
;; @file
|
||||||
;; @type driver
|
;; @brief DHT22 driver (WIP)
|
||||||
|
;; @ingroup driver
|
||||||
;; @details
|
;; @details
|
||||||
;; @depends IO-W65C22N
|
;; @depends IO-W65C22N
|
||||||
|
;;
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
|
|
||||||
;;TODO EVERYTHING
|
;;TODO EVERYTHING
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
.ifndef INCLUDE_PRINTER
|
.ifndef INCLUDE_PRINTER
|
||||||
INCLUDE_PRINTER = 1
|
INCLUDE_PRINTER = 1
|
||||||
|
|
||||||
.global printer
|
.global keypad_printer
|
||||||
.import home:absolute
|
.import home:absolute
|
||||||
|
|
||||||
.endif ; guard
|
.endif ; guard
|
20
programs/keypad_printer.s65
Normal file
20
programs/keypad_printer.s65
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.include "keypad_printer.h65"
|
||||||
|
.include "lcd.h65"
|
||||||
|
.include "keypad.h65"
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Print characters to the display when a button was pressed
|
||||||
|
;; @ingroup applications
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc keypad_printer
|
||||||
|
jsr lcd::clear
|
||||||
|
@printer_loop:
|
||||||
|
jsr kp::read
|
||||||
|
beq @printer_loop
|
||||||
|
cmp #'*'
|
||||||
|
jeq home
|
||||||
|
jsr lcd::print_char
|
||||||
|
bra @printer_loop
|
||||||
|
.endproc
|
@ -3,15 +3,16 @@ INCLUDE_MEMCOPY = 1
|
|||||||
|
|
||||||
.include "system.h65"
|
.include "system.h65"
|
||||||
|
|
||||||
.export memcopy
|
.export memcopy,memcopy16
|
||||||
|
|
||||||
.code
|
.code
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Copy a block of memory to a different address
|
;; @function Copy a block of memory to a different address
|
||||||
; @param ARG0-1: Source address
|
;; @param ARG0-1: Source address
|
||||||
; @param ARG2-3: Target address
|
;; @param ARG2-3: Target address
|
||||||
; @param y: Number of bytes to copy
|
;; @param Y: Number of bytes to copy
|
||||||
;********************************************************************************
|
;; @modifies A,Y
|
||||||
|
;;********************************************************************************
|
||||||
.proc memcopy
|
.proc memcopy
|
||||||
cpy #0
|
cpy #0
|
||||||
beq @rts
|
beq @rts
|
||||||
@ -24,5 +25,41 @@ INCLUDE_MEMCOPY = 1
|
|||||||
@rts:
|
@rts:
|
||||||
rts
|
rts
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Copy a block of memory to a different address
|
||||||
|
;; @param ARG0-1: Source address
|
||||||
|
;; @param ARG2-3: Target address
|
||||||
|
;; @param ARG5-6: Number of bytes to copy (LE)
|
||||||
|
;; @modifies A,Y
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc memcopy16
|
||||||
|
lda ARG6
|
||||||
|
beq @last_page ; no full page
|
||||||
|
ldy #$ff
|
||||||
|
bra @copy_byte
|
||||||
|
@next_page: ; y is 0
|
||||||
|
lda ARG6
|
||||||
|
beq @rts ; done
|
||||||
|
inc ARG1
|
||||||
|
inc ARG3
|
||||||
|
dec ARG6
|
||||||
|
beq @last_page
|
||||||
|
ldy #$ff
|
||||||
|
@copy_byte:
|
||||||
|
lda (ARG0),y
|
||||||
|
sta (ARG2),y
|
||||||
|
@dec_y:
|
||||||
|
dey
|
||||||
|
beq @next_page
|
||||||
|
bra @copy_byte
|
||||||
|
@last_page:
|
||||||
|
ldy ARG5
|
||||||
|
dey
|
||||||
|
bra @copy_byte
|
||||||
|
@rts:
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
.endif ; guard
|
.endif ; guard
|
||||||
|
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
.export print_slow
|
.export print_slow
|
||||||
|
|
||||||
.code
|
.code
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Print a null-terminated string
|
;; @function Print a null-terminated string with a delay between each character
|
||||||
; @param ARG0-1: Address of the string to print
|
;; @param ARG0-1: Address of the string to print
|
||||||
; @param x: time to sleep in centiseconds
|
;; @param x: time to sleep in centiseconds
|
||||||
;********************************************************************************
|
;; @ingroup applications
|
||||||
|
;; @clock_dependent
|
||||||
|
;;********************************************************************************
|
||||||
.proc print_slow
|
.proc print_slow
|
||||||
ldy #$00
|
ldy #$00
|
||||||
@print_loop:
|
@print_loop:
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
.include "printer.h65"
|
|
||||||
.include "lcd.h65"
|
|
||||||
.include "keypad.h65"
|
|
||||||
|
|
||||||
.code
|
|
||||||
.proc printer
|
|
||||||
jsr lcd::clear
|
|
||||||
@printer_loop:
|
|
||||||
jsr kp::read
|
|
||||||
beq @printer_loop
|
|
||||||
cmp #'*'
|
|
||||||
jeq home
|
|
||||||
jsr lcd::print_char
|
|
||||||
bra @printer_loop
|
|
||||||
.endproc
|
|
61
programs/ps2_keyboard_printer.s65
Normal file
61
programs/ps2_keyboard_printer.s65
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
.include "lcd.h65"
|
||||||
|
.include "string.h65"
|
||||||
|
.include "ps2_keyboard_text_handler.h65"
|
||||||
|
|
||||||
|
.export ps2_keyboard_printer
|
||||||
|
|
||||||
|
.import home:absolute
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Print characters to the display when a key was pressed
|
||||||
|
;; @details
|
||||||
|
;; Print the character that corresponds to a key.
|
||||||
|
;; This uses the @ref kb "text keyboard handler" to correctly handle modifier keys
|
||||||
|
;;
|
||||||
|
;; Controls:
|
||||||
|
;; - `ESC`: Jump home
|
||||||
|
;; - `Print`: Clear the display
|
||||||
|
;; - `F12`: Restart the program
|
||||||
|
;; @ingroup applications keyboard
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc ps2_keyboard_printer
|
||||||
|
jsr lcd::clear
|
||||||
|
jsr kb::init
|
||||||
|
jeq loop
|
||||||
|
ps2kb_PrintCmdFailed
|
||||||
|
; jmp homeloop
|
||||||
|
ps2kb_CmdEnable
|
||||||
|
jsr ps2kb::begin_receive
|
||||||
|
|
||||||
|
loop:
|
||||||
|
wai
|
||||||
|
; ; put shift, ralt and lalt in debug leds
|
||||||
|
; lda kb::modifier
|
||||||
|
; Reverse A
|
||||||
|
; and #%00000111
|
||||||
|
; ora IO1 + IO::RANH
|
||||||
|
; sta IO1 + IO::RANH
|
||||||
|
lda kb::char
|
||||||
|
beq @no_char
|
||||||
|
stz kb::char
|
||||||
|
jsr lcd::print_char
|
||||||
|
bra loop
|
||||||
|
@no_char:
|
||||||
|
lda kb::keycode
|
||||||
|
beq loop
|
||||||
|
stz kb::keycode
|
||||||
|
cmp #kb::K::ESCAPE
|
||||||
|
beq @esacpe
|
||||||
|
cmp #kb::K::PRINT
|
||||||
|
beq @clear_display
|
||||||
|
cmp #kb::K::F12
|
||||||
|
jeq ps2_keyboard_printer
|
||||||
|
bra loop
|
||||||
|
@esacpe:
|
||||||
|
jmp home
|
||||||
|
@clear_display:
|
||||||
|
jsr lcd::clear
|
||||||
|
bra loop
|
||||||
|
.endproc
|
38
programs/ps2_keyboard_simple_handler.h65
Normal file
38
programs/ps2_keyboard_simple_handler.h65
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief Simple PS/2 keyboard handler
|
||||||
|
;; @ingroup libs
|
||||||
|
;; @details:
|
||||||
|
;; This module processes keyboard scancodes from the ps2_keyboard driver.
|
||||||
|
;; It requires a PS/2 keyboard that supports scancode set 3.
|
||||||
|
;;
|
||||||
|
;; @section what What it does
|
||||||
|
;; - swallow break (release) scancodes
|
||||||
|
;;
|
||||||
|
;; This handler is designed for debug purposes.
|
||||||
|
;;
|
||||||
|
;; @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 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.
|
||||||
|
;;********************************************************************************
|
||||||
|
.ifndef INCLUDE_KEYBOARD_SIMPLE
|
||||||
|
INCLUDE_KEYBOARD_SIMPLE = 1
|
||||||
|
|
||||||
|
.include "ps2_keyboard.h65"
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Simple Keyboard Handler
|
||||||
|
;;********************************************************************************
|
||||||
|
.scope skb
|
||||||
|
Import skb, init, scancode
|
||||||
|
|
||||||
|
K_BREAK = $F0
|
||||||
|
.endscope
|
||||||
|
.endif ; guard
|
77
programs/ps2_keyboard_simple_handler.s65
Normal file
77
programs/ps2_keyboard_simple_handler.s65
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
.include "ps2_keyboard_simple_handler.h65"
|
||||||
|
Export skb, init, scancode
|
||||||
|
|
||||||
|
.bss
|
||||||
|
previous_scancode: .res 1 ; all 1 if previous_scancode is break
|
||||||
|
scancode: .res 1
|
||||||
|
|
||||||
|
.code
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Initialize the keyboard handler
|
||||||
|
;; @returns Z: Z = 1 => success, Z = 0 => failure
|
||||||
|
;; @details
|
||||||
|
;; Initializes the PS/2 keyboard driver and the handler.
|
||||||
|
;; If any PS/2 command fails, init returns with Z = 0. The failed command (and parameter) can be loaded from `ps2kb::send_cmd` and `ps2kb::send_data`
|
||||||
|
;; After init, the keyboard will be able to send scancodes.
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc init
|
||||||
|
stz scancode
|
||||||
|
stz previous_scancode
|
||||||
|
|
||||||
|
jsr ps2kb::init
|
||||||
|
|
||||||
|
; init & reset
|
||||||
|
ps2kb_CmdSelfTest
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response+1
|
||||||
|
cmp #$aa
|
||||||
|
bne fail
|
||||||
|
; set set 3
|
||||||
|
ps2kb_CmdSetScancodeSet 3
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response+1
|
||||||
|
cmp #ps2kb::ACK
|
||||||
|
bne fail
|
||||||
|
|
||||||
|
ps2kb_CmdEnable
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response
|
||||||
|
cmp #ps2kb::ACK
|
||||||
|
bne fail
|
||||||
|
|
||||||
|
StoreDByte process_scancode, ps2kb::scancode_handler
|
||||||
|
jsr ps2kb::begin_receive
|
||||||
|
lda #0 ; set Z = success
|
||||||
|
fail:
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Process a scancode
|
||||||
|
;; @details
|
||||||
|
;; If it is a 'make' scancode, store it in skb::scancode
|
||||||
|
;; 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
|
||||||
|
;; @param X: The scancode
|
||||||
|
;; @modifies A, X, Y
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc process_scancode
|
||||||
|
; check how this scancode needs to be interpreted
|
||||||
|
bit previous_scancode
|
||||||
|
jmi break_key ; bit 7 set = BREAK
|
||||||
|
|
||||||
|
lda ps2kb::scancode
|
||||||
|
cmp #skb::K_BREAK
|
||||||
|
beq @break_scancode
|
||||||
|
|
||||||
|
sta scancode
|
||||||
|
rts
|
||||||
|
@break_scancode:
|
||||||
|
lda #$ff
|
||||||
|
sta previous_scancode
|
||||||
|
rts
|
||||||
|
break_key:
|
||||||
|
stz previous_scancode
|
||||||
|
rts
|
||||||
|
.endproc
|
201
programs/ps2_keyboard_text_handler.h65
Normal file
201
programs/ps2_keyboard_text_handler.h65
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
; normal
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief PS/2 Keyboard handler for text input
|
||||||
|
;; @ingroup libs
|
||||||
|
;; @details
|
||||||
|
;; This module processes keyboard scancodes from the ps2_keyboard driver.
|
||||||
|
;; It requires a PS/2 keyboard that supports scancode set 3.
|
||||||
|
;;
|
||||||
|
;; @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 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 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 sent from the keyboard are processed immediately, as the handling functions is called
|
||||||
|
;; from the keyboard's interrupt handler.
|
||||||
|
;;
|
||||||
|
;; @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 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 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
|
||||||
|
;; using the `bbs` and `bbr` instructions, as each modifier is represented by a bit of the `kb::modifier` variable
|
||||||
|
;; in the zeropage.
|
||||||
|
;;
|
||||||
|
;; @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.
|
||||||
|
;;********************************************************************************
|
||||||
|
.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
|
||||||
|
LALT = %00100000 ; set while leftalt is pressed
|
||||||
|
META = %00010000 ; set while left or right meta is pressed
|
||||||
|
CTRL = %00001000 ; set while left or right ctrl is pressed
|
||||||
|
CAPSLOCK = %00000100 ; ps/2 LED
|
||||||
|
NUMLOCK = %00000010 ; ps/2 LED
|
||||||
|
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
|
||||||
|
TAB = $0D
|
||||||
|
GRAVE = $0E
|
||||||
|
F2 = $0F
|
||||||
|
LEFTCTRL = $11
|
||||||
|
LEFTSHIFT = $12
|
||||||
|
CAPSLOCK = $14
|
||||||
|
K_Q = $15
|
||||||
|
K_1 = $16
|
||||||
|
F3 = $17
|
||||||
|
LEFTALT = $19
|
||||||
|
K_Y = $1A
|
||||||
|
K_S = $1B
|
||||||
|
K_A = $1C
|
||||||
|
K_W = $1D
|
||||||
|
K_2 = $1E
|
||||||
|
F4 = $1F
|
||||||
|
K_C = $21
|
||||||
|
K_X = $22
|
||||||
|
K_D = $23
|
||||||
|
K_E = $24
|
||||||
|
K_4 = $25
|
||||||
|
K_3 = $26
|
||||||
|
F5 = $27
|
||||||
|
SPACE = $29
|
||||||
|
K_V = $2A
|
||||||
|
K_F = $2B
|
||||||
|
K_T = $2C
|
||||||
|
K_R = $2D
|
||||||
|
K_5 = $2E
|
||||||
|
F6 = $2F
|
||||||
|
K_N = $31
|
||||||
|
K_B = $32
|
||||||
|
K_H = $33
|
||||||
|
K_G = $34
|
||||||
|
K_Z = $35
|
||||||
|
K_6 = $36
|
||||||
|
F7 = $37
|
||||||
|
RIGHTALT = $39
|
||||||
|
K_M = $3A
|
||||||
|
K_J = $3B
|
||||||
|
K_U = $3C
|
||||||
|
K_7 = $3D
|
||||||
|
K_8 = $3E
|
||||||
|
F8 = $3F
|
||||||
|
COMMA = $41
|
||||||
|
K_K = $42
|
||||||
|
K_I = $43
|
||||||
|
K_O = $44
|
||||||
|
K_0 = $45
|
||||||
|
K_9 = $46
|
||||||
|
F9 = $47
|
||||||
|
DOT = $49
|
||||||
|
DASH = $4A
|
||||||
|
KPDIVIDE = $4A
|
||||||
|
K_L = $4B
|
||||||
|
OUML = $4C
|
||||||
|
K_P = $4D
|
||||||
|
SSHARP = $4E
|
||||||
|
KPMINUS = $4E
|
||||||
|
F10 = $4F
|
||||||
|
AUML = $52
|
||||||
|
UUML = $54
|
||||||
|
BACKTICK = $55
|
||||||
|
F11 = $56
|
||||||
|
PRINT = $57
|
||||||
|
RIGHTCTRL = $58
|
||||||
|
RIGHTSHIFT = $59
|
||||||
|
ENTER = $5A
|
||||||
|
PLUS = $5B
|
||||||
|
LESSTHAN = $5C
|
||||||
|
HASH = $5D
|
||||||
|
F12 = $5E
|
||||||
|
SCROLLLOCK = $5F
|
||||||
|
RIGHT = $60
|
||||||
|
LEFT = $61
|
||||||
|
PAUSE = $62
|
||||||
|
UP = $63
|
||||||
|
DELETE = $64
|
||||||
|
END = $65
|
||||||
|
BACKSPACE = $66
|
||||||
|
INSERT = $67
|
||||||
|
KP1 = $69
|
||||||
|
DOWN = $6A
|
||||||
|
KP4 = $6B
|
||||||
|
KP7 = $6C
|
||||||
|
PAGEDOWN = $6D
|
||||||
|
POS1 = $6E
|
||||||
|
PAGEUP = $6F
|
||||||
|
KP0 = $70
|
||||||
|
KPDOT = $71
|
||||||
|
KP2 = $72
|
||||||
|
KP5 = $73
|
||||||
|
KP6 = $74
|
||||||
|
KP8 = $75
|
||||||
|
NUMLOCK = $76
|
||||||
|
KPENTER = $79
|
||||||
|
KP3 = $7A
|
||||||
|
KPPLUS = $7C
|
||||||
|
KP9 = $7D
|
||||||
|
KPASTERISK = $7E
|
||||||
|
LEFTMETA = $8B
|
||||||
|
RIGHTMETA = $8C
|
||||||
|
MENU = $8D
|
||||||
|
.endenum
|
||||||
|
|
||||||
|
.endscope
|
||||||
|
.endif ; guard
|
310
programs/ps2_keyboard_text_handler.s65
Normal file
310
programs/ps2_keyboard_text_handler.s65
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
.include "ps2_keyboard_text_handler.h65"
|
||||||
|
Export kb, init, char, keycode
|
||||||
|
Export kb, CHARS_NOMOD, CHARS_MODRALT, CHARS_MODSHIFT, CHARS_NUMLOCK_OFF, CHARS_NUMLOCK_ON
|
||||||
|
ExportZp kb, modifier
|
||||||
|
|
||||||
|
.zeropage
|
||||||
|
;; @ref kb::MOD "status of the modifier keys"
|
||||||
|
modifier: .res 1
|
||||||
|
.bss
|
||||||
|
;; 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: .res 1
|
||||||
|
;; 0 when no key or non-character key was pressed, otherwise character of the key (with modifiers applied)
|
||||||
|
char: .res 1
|
||||||
|
;; @ref kb::K "keycode" of the last key that was pressed or 0 if none was pressed
|
||||||
|
keycode: .res 1
|
||||||
|
|
||||||
|
.code
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Initialize the keyboard handler
|
||||||
|
;; @returns Z: Z = 1 => success, Z = 0 => failure
|
||||||
|
;; @details
|
||||||
|
;; Initializes the PS/2 keyboard driver and the handler.
|
||||||
|
;; If any PS/2 command fails, init returns with Z = 0. The failed command (and parameter) can be loaded from `ps2kb::send_cmd` and `ps2kb::send_data`
|
||||||
|
;; After init, the keyboard will be able to send scancodes.
|
||||||
|
;; @modifies A, X, Y
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc init
|
||||||
|
stz modifier
|
||||||
|
stz char
|
||||||
|
stz keycode
|
||||||
|
stz previous_scancode
|
||||||
|
|
||||||
|
jsr ps2kb::init
|
||||||
|
|
||||||
|
; init & reset
|
||||||
|
ps2kb_CmdSelfTest
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response+1
|
||||||
|
cmp #$aa
|
||||||
|
bne fail
|
||||||
|
; set set 3
|
||||||
|
ps2kb_CmdSetScancodeSet 3
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response+1
|
||||||
|
cmp #ps2kb::ACK
|
||||||
|
bne fail
|
||||||
|
|
||||||
|
; toggle-behaving keys
|
||||||
|
ldx #kb::K::CAPSLOCK
|
||||||
|
jsr set_make
|
||||||
|
ldx #kb::K::NUMLOCK
|
||||||
|
jsr set_make
|
||||||
|
ldx #kb::K::SCROLLLOCK
|
||||||
|
jsr set_make
|
||||||
|
|
||||||
|
; modifiers
|
||||||
|
ldx #kb::K::LEFTSHIFT
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::RIGHTSHIFT
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::LEFTCTRL
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::RIGHTCTRL
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::LEFTALT
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::RIGHTALT
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::LEFTMETA
|
||||||
|
jsr set_make_release
|
||||||
|
ldx #kb::K::RIGHTMETA
|
||||||
|
jsr set_make_release
|
||||||
|
|
||||||
|
ps2kb_CmdEnable
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response
|
||||||
|
cmp #ps2kb::ACK
|
||||||
|
bne fail
|
||||||
|
|
||||||
|
StoreDByte process_scancode, ps2kb::scancode_handler
|
||||||
|
jsr ps2kb::begin_receive
|
||||||
|
lda #0 ; set Z = success
|
||||||
|
fail:
|
||||||
|
rts
|
||||||
|
|
||||||
|
set_make:
|
||||||
|
; x must be set to scancode
|
||||||
|
lda #$fd
|
||||||
|
bra set
|
||||||
|
set_make_release:
|
||||||
|
; x must be set to scancode
|
||||||
|
lda #$fc
|
||||||
|
set:
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
lda ps2kb::cmd_response+1
|
||||||
|
cmp #ps2kb::ACK
|
||||||
|
bne fail
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @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 @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
|
||||||
|
; check how this scancode needs to be interpreted
|
||||||
|
ldx ps2kb::scancode
|
||||||
|
bit previous_scancode
|
||||||
|
jmi break_key ; bit 7 set = BREAK
|
||||||
|
|
||||||
|
cpx #$60 ; codes >= 60 in separate table to reduce memory consumption
|
||||||
|
bcs x_ge_60
|
||||||
|
bbs kb::MOD::SHIFT, modifier, modshift ; if shift active
|
||||||
|
bbs kb::MOD::RALT, modifier, modralt ; if ralt active
|
||||||
|
|
||||||
|
; load from the correct table, if 0 => non-char, if 1 => non-char but mod, else char
|
||||||
|
nomod:
|
||||||
|
lda CHARS_NOMOD,x
|
||||||
|
bra check_char
|
||||||
|
modralt:
|
||||||
|
lda CHARS_MODRALT,x
|
||||||
|
bra check_char
|
||||||
|
modshift:
|
||||||
|
lda CHARS_MODSHIFT,x
|
||||||
|
check_char:
|
||||||
|
beq not_mod ; 0 => not mod and not char
|
||||||
|
cmp #1 ; 1 => mod
|
||||||
|
beq is_mod
|
||||||
|
@char: ; store char
|
||||||
|
sta char
|
||||||
|
not_mod: ; store keycode if not mod
|
||||||
|
stx keycode
|
||||||
|
_rts:
|
||||||
|
rts
|
||||||
|
|
||||||
|
x_ge_60: ; x >= $60
|
||||||
|
cpx #$90 ; only BREAK is higher than 8F, (also code table only has 8F bytes)
|
||||||
|
bcs check_release
|
||||||
|
; CHARS_NUMLOCK_OFF - CHARS_NOMOD = 60 -> no need to subtract $60 first
|
||||||
|
; CHARS_NUMLOCK_ON - CHARS_MODSHIFT = 60 -> no need to subtract $60 first
|
||||||
|
bbs kb::MOD::NUMLOCK, modifier, modshift ; if numlock active
|
||||||
|
lda CHARS_NOMOD,x
|
||||||
|
bra check_char
|
||||||
|
check_release: ; x >= $90
|
||||||
|
cpx #kb::K_BREAK
|
||||||
|
bne _rts
|
||||||
|
lda #$ff
|
||||||
|
sta previous_scancode
|
||||||
|
rts
|
||||||
|
|
||||||
|
is_mod:
|
||||||
|
; check which mod
|
||||||
|
cpx #kb::K::LEFTSHIFT
|
||||||
|
beq set_shift
|
||||||
|
cpx #kb::K::RIGHTSHIFT
|
||||||
|
beq set_shift
|
||||||
|
cpx #kb::K::RIGHTALT
|
||||||
|
beq set_rightalt
|
||||||
|
cpx #kb::K::LEFTALT
|
||||||
|
beq set_leftalt
|
||||||
|
cpx #kb::K::RIGHTMETA
|
||||||
|
beq set_meta
|
||||||
|
cpx #kb::K::LEFTMETA
|
||||||
|
beq set_meta
|
||||||
|
cpx #kb::K::RIGHTCTRL
|
||||||
|
beq set_ctrl
|
||||||
|
cpx #kb::K::LEFTCTRL
|
||||||
|
beq set_ctrl
|
||||||
|
cpx #kb::K::CAPSLOCK
|
||||||
|
beq toggle_capslock
|
||||||
|
cpx #kb::K::NUMLOCK
|
||||||
|
beq toggle_numlock
|
||||||
|
cpx #kb::K::SCROLLLOCK
|
||||||
|
beq toggle_scrolllock
|
||||||
|
|
||||||
|
; a real keycode and not a released keycode, twobyte or release scancode
|
||||||
|
|
||||||
|
set_ctrl:
|
||||||
|
smb kb::MOD::CTRL, modifier
|
||||||
|
rts
|
||||||
|
set_meta:
|
||||||
|
smb kb::MOD::META, modifier
|
||||||
|
rts
|
||||||
|
set_leftalt:
|
||||||
|
smb kb::MOD::LALT, modifier
|
||||||
|
rts
|
||||||
|
set_rightalt:
|
||||||
|
smb kb::MOD::RALT, modifier
|
||||||
|
rts
|
||||||
|
set_shift: ; process shift
|
||||||
|
bbs kb::MOD::CAPSLOCK, modifier, @unset_shift ; CAPSLOCK unset shift when capslock is on
|
||||||
|
@set_shift:
|
||||||
|
smb kb::MOD::SHIFT, modifier
|
||||||
|
rts
|
||||||
|
@unset_shift:
|
||||||
|
rmb kb::MOD::SHIFT, modifier
|
||||||
|
rts
|
||||||
|
|
||||||
|
toggle_numlock: ; toggle numlock
|
||||||
|
lda modifier
|
||||||
|
eor #kb::MOD::NUMLOCK
|
||||||
|
sta modifier
|
||||||
|
bra update_leds
|
||||||
|
toggle_scrolllock: ; toggle numlock
|
||||||
|
lda modifier
|
||||||
|
eor #kb::MOD::SCROLLLOCK
|
||||||
|
sta modifier
|
||||||
|
bra update_leds
|
||||||
|
toggle_capslock: ; toggle capslock & shift
|
||||||
|
lda modifier
|
||||||
|
eor #(kb::MOD::CAPSLOCK | kb::MOD::SHIFT)
|
||||||
|
sta modifier
|
||||||
|
; fallthrough
|
||||||
|
load_update_leds:
|
||||||
|
lda modifier
|
||||||
|
update_leds:
|
||||||
|
and #%00000111 ; only bottom 3 bits allowed - otherwise command fails
|
||||||
|
tax
|
||||||
|
ps2kb_CmdSetLeds
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @details
|
||||||
|
;; Unset modifier bits if a mod key is released.
|
||||||
|
;; @param X: The scancode
|
||||||
|
;; @modifies
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc break_key
|
||||||
|
; rmb kb::PREVIOUS::BREAK, kb::previous_scancode
|
||||||
|
stz previous_scancode
|
||||||
|
|
||||||
|
; check mod keys
|
||||||
|
cpx #kb::K::LEFTSHIFT
|
||||||
|
beq unset_shift
|
||||||
|
cpx #kb::K::RIGHTSHIFT
|
||||||
|
beq unset_shift
|
||||||
|
cpx #kb::K::RIGHTALT
|
||||||
|
beq unset_rightalt
|
||||||
|
cpx #kb::K::LEFTALT
|
||||||
|
beq unset_leftalt
|
||||||
|
cpx #kb::K::RIGHTMETA
|
||||||
|
beq unset_meta
|
||||||
|
cpx #kb::K::LEFTMETA
|
||||||
|
beq unset_meta
|
||||||
|
cpx #kb::K::RIGHTCTRL
|
||||||
|
beq unset_ctrl
|
||||||
|
cpx #kb::K::LEFTCTRL
|
||||||
|
rts
|
||||||
|
unset_shift: ; process shift
|
||||||
|
bbs kb::MOD::CAPSLOCK, modifier, @set_shift ; set shift when capslock is on
|
||||||
|
rmb kb::MOD::SHIFT, modifier ; unset if capslock is off
|
||||||
|
rts
|
||||||
|
@set_shift:
|
||||||
|
smb kb::MOD::SHIFT, modifier
|
||||||
|
rts
|
||||||
|
unset_ctrl:
|
||||||
|
rmb kb::MOD::CTRL, modifier
|
||||||
|
rts
|
||||||
|
unset_meta:
|
||||||
|
rmb kb::MOD::META, modifier
|
||||||
|
rts
|
||||||
|
unset_leftalt:
|
||||||
|
rmb kb::MOD::LALT, modifier
|
||||||
|
rts
|
||||||
|
unset_rightalt:
|
||||||
|
rmb kb::MOD::RALT, modifier
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
.rodata
|
||||||
|
.align 256
|
||||||
|
CHARS_NOMOD:
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00^\x00"
|
||||||
|
.byte "\x00\x01\x01<\x01q1\x00\x00\x01ysaw2\x00"
|
||||||
|
.byte "\x00cxde43\x00\x00 vftr5\x00"
|
||||||
|
.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"
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00-\x00"
|
||||||
|
CHARS_MODSHIFT:
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDF\x00"
|
||||||
|
.byte "\x00\x01\x01>\x01Q!\x00\x00\x01YSAW\"\x00"
|
||||||
|
.byte "\x00CXDE$\xED\x00\x00 VFTR%\x00"
|
||||||
|
.byte "\x00NBHGZ&\x00\x00\x01MJU/(\x00"
|
||||||
|
.byte "\x00;KIO=)\x00\x00:_L\xEFP\xE2\x00"
|
||||||
|
.byte "\x00\x00\xE1'\xF5`\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01"
|
||||||
|
CHARS_NUMLOCK_ON:
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x001\x0047\x00\x00\x00"
|
||||||
|
.byte "0.2568\x01\x00\x00\x003\x00\x009\x00\x00"
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00"
|
||||||
|
CHARS_MODRALT:
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\x00"
|
||||||
|
.byte "\x00\x01\x01|\x01\x00\x00\x00\x00\x01\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\x00\x00\x00\x01\x00\x00\x00{[\x00"
|
||||||
|
.byte "\x00\x00\x00\x00\x00}]\x00\x00\xA5\xB0\x00\x00\x00\x00\x00"
|
||||||
|
.byte "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x01"
|
159
programs/ps2_keyboard_util.s65
Normal file
159
programs/ps2_keyboard_util.s65
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
.include "system.h65"
|
||||||
|
.include "string.h65"
|
||||||
|
.include "keypad.h65"
|
||||||
|
.include "lcd.h65"
|
||||||
|
.include "ps2_keyboard_simple_handler.h65"
|
||||||
|
.include "ps2_keyboard_text_handler.h65"
|
||||||
|
|
||||||
|
.export ps2_keyboard_util
|
||||||
|
|
||||||
|
.import home:absolute
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Send various commands to the keyboard and print the response bytes
|
||||||
|
;; @details
|
||||||
|
;; - `0`: `0xf0` query scancode set
|
||||||
|
;; - `1`: `0xee` echo
|
||||||
|
;; - `2`: `0xf2` identify
|
||||||
|
;; - `3`: `0xf3` set typematic min speed and delay (`0b0111111`)
|
||||||
|
;; - `4`: `0xf4` enable
|
||||||
|
;; - `5`: `0xf5` disable
|
||||||
|
;; - `6`: `0xf3` set typematic max speed and min delay (`0b00000000`)
|
||||||
|
;; - `7`: `0xf7` set typematic all
|
||||||
|
;; - `8`: `0xf8` set make/release all
|
||||||
|
;; - `9`: `0xed` set CAPSLOCK led
|
||||||
|
;; - `#`: `0xed` set SCROLLLOCK led
|
||||||
|
;; - `C`: `0xed` set NUMLOCK led
|
||||||
|
;; - `D`: `0xed` turn all leds off
|
||||||
|
;; - `A`: reprint menu
|
||||||
|
;; - `B`: reprint menu
|
||||||
|
;; - `*`: jump home
|
||||||
|
;; For more details, see https://wiki.osdev.org/PS/2_Keyboard
|
||||||
|
;; @ingroup applications
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc ps2_keyboard_util
|
||||||
|
stz kp::_DEBUG_VAL
|
||||||
|
jsr skb::init
|
||||||
|
beq print_menu
|
||||||
|
ps2kb_PrintCmdFailed
|
||||||
|
bra loop
|
||||||
|
print_menu:
|
||||||
|
Print MENU
|
||||||
|
loop:
|
||||||
|
wai
|
||||||
|
lda skb::scancode
|
||||||
|
beq @read_keypad
|
||||||
|
Printf FMT_CODE,skb::scancode
|
||||||
|
stz skb::scancode
|
||||||
|
@read_keypad:
|
||||||
|
lda kp::_DEBUG_VAL
|
||||||
|
jeq loop
|
||||||
|
stz kp::_DEBUG_VAL
|
||||||
|
cmp #'*'
|
||||||
|
jeq home
|
||||||
|
pha
|
||||||
|
jsr lcd::clear
|
||||||
|
pla
|
||||||
|
ldy #0 ; arg for commands
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
cmp #'0'
|
||||||
|
beq @l0
|
||||||
|
cmp #'1'
|
||||||
|
beq @l1
|
||||||
|
cmp #'2'
|
||||||
|
beq @l2
|
||||||
|
cmp #'3'
|
||||||
|
beq @l3
|
||||||
|
cmp #'4'
|
||||||
|
beq @l4
|
||||||
|
cmp #'5'
|
||||||
|
beq @l5
|
||||||
|
cmp #'6'
|
||||||
|
beq @l6
|
||||||
|
cmp #'7'
|
||||||
|
beq @l7
|
||||||
|
cmp #'8'
|
||||||
|
beq @l8
|
||||||
|
cmp #'9'
|
||||||
|
beq @l9
|
||||||
|
cmp #'#'
|
||||||
|
beq @lhash
|
||||||
|
cmp #'A'
|
||||||
|
beq @lA
|
||||||
|
cmp #'B'
|
||||||
|
beq @lB
|
||||||
|
cmp #'C'
|
||||||
|
beq @lC
|
||||||
|
cmp #'D'
|
||||||
|
beq @lD
|
||||||
|
jsr lcd::print_char
|
||||||
|
jmp loop
|
||||||
|
|
||||||
|
@l0: ; get scancode set
|
||||||
|
lda #$f0
|
||||||
|
ldx #0
|
||||||
|
ldy #1
|
||||||
|
bra @send_cmd
|
||||||
|
@l1: ; echo
|
||||||
|
lda #$ee
|
||||||
|
bra @send_cmd
|
||||||
|
@l2: ; identify
|
||||||
|
lda #$f2
|
||||||
|
ldy #2
|
||||||
|
bra @send_cmd
|
||||||
|
@l3: ; set typematic min speed and delay
|
||||||
|
lda #$f3
|
||||||
|
ldx #%01111111
|
||||||
|
bra @send_cmd
|
||||||
|
@l4: ; enable
|
||||||
|
lda #$f4
|
||||||
|
bra @send_cmd
|
||||||
|
@l5: ; disable
|
||||||
|
lda #$f5
|
||||||
|
bra @send_cmd
|
||||||
|
@l6: ; set typematic max speed and min delay
|
||||||
|
lda #$f3
|
||||||
|
ldx #0
|
||||||
|
bra @send_cmd
|
||||||
|
@l7: ; set typematic all
|
||||||
|
lda #$f7
|
||||||
|
bra @send_cmd
|
||||||
|
@l8: ; set make/release all
|
||||||
|
lda #$f8
|
||||||
|
bra @send_cmd
|
||||||
|
@l9: ; set CAPSLOCK led
|
||||||
|
lda #$ed
|
||||||
|
ldx #kb::MOD::CAPSLOCK
|
||||||
|
bra @send_cmd
|
||||||
|
@lhash: ; set SCROLLLOCK led
|
||||||
|
lda #$ed
|
||||||
|
ldx #kb::MOD::SCROLLLOCK
|
||||||
|
bra @send_cmd
|
||||||
|
@lC: ; set NUMLOCK led
|
||||||
|
lda #$ed
|
||||||
|
ldx #kb::MOD::NUMLOCK
|
||||||
|
bra @send_cmd
|
||||||
|
@lD: ; turn all leds off
|
||||||
|
lda #$ed
|
||||||
|
ldx #0
|
||||||
|
bra @send_cmd
|
||||||
|
@lA:
|
||||||
|
; TODO
|
||||||
|
@lB: ; reprint menu
|
||||||
|
jmp print_menu
|
||||||
|
@send_cmd:
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
ps2kb_WaitFinishCmd
|
||||||
|
Printf FMT_CMD_DATA, ps2kb::send_cmd, ps2kb::send_data, ps2kb::cmd_response, ps2kb::cmd_response+1, ps2kb::cmd_response+2
|
||||||
|
jmp loop
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
.rodata
|
||||||
|
FMT_CODE: .asciiz "K %x "
|
||||||
|
FMT_CMD_DATA: .asciiz "C %x-%x > %x%x%x"
|
||||||
|
MENU: .byte " EE F2 F3 "
|
||||||
|
.byte " F4 F5 F3' MEN"
|
||||||
|
.byte " T1 T0 LC LN "
|
||||||
|
.asciiz " RET F0 LS L0 "
|
@ -3,14 +3,16 @@
|
|||||||
.export sleep
|
.export sleep
|
||||||
|
|
||||||
.code
|
.code
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function sleep
|
;; @function sleep
|
||||||
; @param x: Time to sleep in centiseconds (10^-2s = 10ms)
|
;; @param x: Time to sleep in centiseconds (10^-2s = 10ms) @ 1MHz
|
||||||
; @details
|
;; @details
|
||||||
; Interrupts might change the actual time to finish
|
;; Interrupts might change the actual time to finish
|
||||||
; To be exact, time_cs is in units of 0.010244s
|
;; To be exact, time_cs is in units of 0.010244s
|
||||||
; @modifies: ARG15
|
;; @modifies ARG15
|
||||||
;********************************************************************************
|
;; @ingroup applications
|
||||||
|
;; @clock_dependent
|
||||||
|
;;********************************************************************************
|
||||||
.proc sleep
|
.proc sleep
|
||||||
_VAR_1 = ARG15
|
_VAR_1 = ARG15
|
||||||
stz _VAR_1
|
stz _VAR_1
|
||||||
|
@ -4,44 +4,76 @@
|
|||||||
.include "lcd.h65"
|
.include "lcd.h65"
|
||||||
.include "chars.h65"
|
.include "chars.h65"
|
||||||
.import home:absolute
|
.import home:absolute
|
||||||
.import SPI_IO
|
.import CODE_START: absolute
|
||||||
|
.import __SPI_SIZE__
|
||||||
|
|
||||||
.export spi_menu
|
.export spi_menu
|
||||||
.bss
|
.bss
|
||||||
trans_bytes: .res 1
|
trans_bytes: .res 2 ;; used to check if screen needs to be updated
|
||||||
trans_pages: .res 1
|
status_char: .res 1
|
||||||
status: .res 1
|
status_str: .res 17 ;; space for 16 + null
|
||||||
status_str: .res 17 ; 16 + null
|
|
||||||
|
|
||||||
.code
|
.code
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Receive code via SPI and execute it
|
||||||
|
;; @ingroup applications
|
||||||
|
;;********************************************************************************
|
||||||
.proc spi_menu
|
.proc spi_menu
|
||||||
stz trans_bytes
|
stz trans_bytes
|
||||||
stz trans_pages
|
stz trans_bytes+1
|
||||||
lda #'X'
|
|
||||||
sta status
|
|
||||||
@print_menu:
|
@print_menu:
|
||||||
jsr lcd::clear
|
jsr lcd::clear
|
||||||
Print MENU
|
Print MENU
|
||||||
|
|
||||||
|
@update_status:
|
||||||
|
lda spi_p::status
|
||||||
|
cmp #spi_p::STATUS::ERROR
|
||||||
|
beq @status_ERROR
|
||||||
|
cmp #spi_p::STATUS::DONE
|
||||||
|
beq @status_DONE
|
||||||
|
cmp #spi_p::STATUS::XFER
|
||||||
|
beq @status_XFER
|
||||||
|
cmp #spi_p::STATUS::XFER_SIZEL
|
||||||
|
beq @status_XFER_SIZEL
|
||||||
|
cmp #spi_p::STATUS::XFER_SIZEH
|
||||||
|
beq @status_XFER_SIZEH
|
||||||
|
@status_UNKNOWN:
|
||||||
|
lda #'?'
|
||||||
|
bra @update_status_end
|
||||||
|
@status_ERROR:
|
||||||
|
lda #'E'
|
||||||
|
bra @update_status_end
|
||||||
|
@status_DONE:
|
||||||
|
lda #'O'
|
||||||
|
bra @update_status_end
|
||||||
|
@status_XFER_SIZEL:
|
||||||
|
lda #'1'
|
||||||
|
bra @update_status_end
|
||||||
|
@status_XFER_SIZEH:
|
||||||
|
lda #'2'
|
||||||
|
bra @update_status_end
|
||||||
|
@status_XFER:
|
||||||
|
lda #'X'
|
||||||
|
@update_status_end:
|
||||||
|
sta status_char
|
||||||
|
|
||||||
@print_status:
|
@print_status:
|
||||||
lda #lcd::LINE4
|
lda #lcd::LINE4
|
||||||
jsr lcd::set_position
|
jsr lcd::set_position
|
||||||
Strf FMT_STATUS,status_str,trans_pages,trans_bytes,status
|
Strf "%x%x/%x%x|%x%x|%c",status_str,trans_bytes+1,trans_bytes,spi_p::recv_size+1,spi_p::recv_size,spi_p::buffer_size+1,spi_p::buffer_size,status_char
|
||||||
PrintNC status_str
|
PrintNC status_str
|
||||||
@loop:
|
@loop:
|
||||||
|
wai
|
||||||
; check if a byte has been transferred
|
; check if a byte has been transferred
|
||||||
@check_byte:
|
@check_received:
|
||||||
lda spi_p::bytes_written
|
lda spi_p::recv_bytes
|
||||||
cmp trans_bytes
|
cmp trans_bytes
|
||||||
beq @read_keypad
|
beq @read_keypad
|
||||||
@byte_written:
|
@byte_received:
|
||||||
sta trans_bytes
|
sta trans_bytes
|
||||||
@check_page:
|
lda spi_p::recv_bytes+1
|
||||||
lda spi_p::pages_written
|
sta trans_bytes+1
|
||||||
cmp trans_pages
|
jmp @update_status
|
||||||
beq @read_keypad
|
|
||||||
@page_written:
|
|
||||||
sta trans_pages
|
|
||||||
bra @print_status
|
|
||||||
@read_keypad:
|
@read_keypad:
|
||||||
lda kp::_DEBUG_VAL
|
lda kp::_DEBUG_VAL
|
||||||
beq @loop
|
beq @loop
|
||||||
@ -57,24 +89,35 @@ status_str: .res 17 ; 16 + null
|
|||||||
beq @spi_end
|
beq @spi_end
|
||||||
cmp #'C'
|
cmp #'C'
|
||||||
beq @spi_jump
|
beq @spi_jump
|
||||||
bra @loop
|
cmp #'D'
|
||||||
|
beq @spi_jump2
|
||||||
|
jmp @update_status ; any other key
|
||||||
@spi_begin:
|
@spi_begin:
|
||||||
lda #'@'
|
lda #<CODE_START
|
||||||
sta status
|
sta ARG0
|
||||||
jsr spi_p::begin
|
lda #>CODE_START
|
||||||
|
sta ARG1
|
||||||
|
lda #<__SPI_SIZE__
|
||||||
|
sta ARG2
|
||||||
|
lda #>__SPI_SIZE__
|
||||||
|
sta ARG3
|
||||||
|
jsr spi_p::begin_read
|
||||||
jmp @print_menu
|
jmp @print_menu
|
||||||
@spi_end:
|
@spi_end:
|
||||||
lda #'X'
|
jsr spi_p::end_read
|
||||||
sta status
|
|
||||||
jsr spi_p::end
|
|
||||||
jmp @print_menu
|
jmp @print_menu
|
||||||
@spi_jump:
|
@spi_jump:
|
||||||
jsr spi_p::end
|
jsr spi_p::end_read
|
||||||
jsr lcd::clear
|
jsr lcd::clear
|
||||||
Print START
|
Print "---START SPI---"
|
||||||
jmp spi_p::CODE_START
|
jmp CODE_START
|
||||||
|
@spi_jump2:
|
||||||
|
jsr spi_p::end_read
|
||||||
|
jsr lcd::clear
|
||||||
|
Printf " >>> %x%x >>> ", spi_p::buffer_ptr+1, spi_p::buffer_ptr
|
||||||
|
jmp (spi_p::buffer_ptr)
|
||||||
@return_home:
|
@return_home:
|
||||||
jsr spi_p::end
|
jsr spi_p::end_read
|
||||||
jmp home
|
jmp home
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
@ -84,6 +127,5 @@ MENU:
|
|||||||
.byte "B> Stop Transfer"
|
.byte "B> Stop Transfer"
|
||||||
.asciiz "C> Jump Home <*"
|
.asciiz "C> Jump Home <*"
|
||||||
; .asciiz "0b0p Status: X"
|
; .asciiz "0b0p Status: X"
|
||||||
FMT_STATUS: .asciiz "%x%xb Status:%x"
|
|
||||||
BEGIN: .asciiz "---BEGIN SPI---"
|
BEGIN: .asciiz "---BEGIN SPI---"
|
||||||
START: .asciiz "---START SPI---"
|
START: .asciiz "---START SPI---"
|
||||||
|
54
programs/viu.s65
Normal file
54
programs/viu.s65
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief VIU - VI Unimproved
|
||||||
|
;;********************************************************************************
|
||||||
|
.include "lcd.h65"
|
||||||
|
.include "ps2_keyboard_text_handler.h65"
|
||||||
|
|
||||||
|
.import home:absolute
|
||||||
|
|
||||||
|
.zeropage
|
||||||
|
ptr: .res 2
|
||||||
|
.bss
|
||||||
|
buffer: .res 256
|
||||||
|
|
||||||
|
.code
|
||||||
|
.proc init
|
||||||
|
stz ptr
|
||||||
|
stz ptr+1
|
||||||
|
stz buffer
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
.proc insert_mode
|
||||||
|
jsr lcd::clear
|
||||||
|
jsr kb::init
|
||||||
|
loop:
|
||||||
|
lda kb::char
|
||||||
|
ldx ptr
|
||||||
|
beq @no_char
|
||||||
|
stz kb::char
|
||||||
|
jsr lcd::print_char
|
||||||
|
bra loop
|
||||||
|
@no_char:
|
||||||
|
lda kb::keycode
|
||||||
|
beq loop
|
||||||
|
|
||||||
|
cmp #kb::K::ESCAPE
|
||||||
|
beq k_esacpe
|
||||||
|
cmp #kb::K::PRINT
|
||||||
|
beq clear_display
|
||||||
|
cmp #kb::K::BACKSPACE
|
||||||
|
beq k_backspace
|
||||||
|
bra loop
|
||||||
|
k_esacpe:
|
||||||
|
jmp home
|
||||||
|
clear_display:
|
||||||
|
jsr lcd::clear
|
||||||
|
bra loop
|
||||||
|
k_backspace:
|
||||||
|
lcd_DecrementCursor
|
||||||
|
lda #' '
|
||||||
|
jsr lcd::print_char
|
||||||
|
lcd_DecrementCursor
|
||||||
|
bra loop
|
||||||
|
.endproc
|
22
readme.md
22
readme.md
@ -1,16 +1,22 @@
|
|||||||
# 8-bit Breadboard Computer with W65C02S Processor
|
# 8-bit Breadboard Computer with W65C02S Processor
|
||||||
This repo contains the assembly code for my [6502-project](https://quintern.xyz/de/6502.html).
|
This repo contains the assembly code for my [6502-project](https://quintern.xyz/de/posts/2021_6502).
|
||||||
|
|
||||||
The assembler used for this project is the excellent [ca65](https://github.com/cc65/cc65).
|
The assembler used for this project is the excellent [ca65](https://github.com/cc65/cc65).
|
||||||
After assembling it, the binary is loaded onto the EEPROM using [my *eeprom.py* script](https://git.quintern.xyz/MatthiasQuintern/AT28C256-rpi-util) on a Raspberry Pi 4B.
|
After assembling it, the binary is loaded onto the EEPROM using [my *eeprom.py* script](https://git.quintern.xyz/MatthiasQuintern/AT28C256-rpi-util) on a Raspberry Pi 4B.
|
||||||
|
|
||||||
## Operating System
|
## Operating System
|
||||||
... is probably a far stretch, since it is just the programs I wrote pieced together. My "os" consists of these functionalities:
|
... is probably a far stretch, since it is just the programs I wrote pieced together. My "OS" consists of these functionalities:
|
||||||
- Main Menu:
|
- Supported Hardware:
|
||||||
- Printer: Prints the characters you press on the keypad to the lcd.
|
- 4x4 Matrix Keypad
|
||||||
- Temperature: Shows the temperature using a dht sensor. *Work in progress, this does not work yet*
|
- 4x16 Characters LCD Display
|
||||||
- Text 1: Show a 4x16 character text (defined at compile time)
|
- PS/2 Keyboard
|
||||||
- Text 2: Show a 4x16 character text (defined at compile time)
|
- SPI Connection to an Arduino
|
||||||
- Ringbuffer for pressed keys.
|
- Software
|
||||||
|
- Keyboard and keypad printer: Prints the characters you press on to the lcd
|
||||||
|
<!-- - Temperature: Shows the temperature using a dht sensor. *Work in progress, this does not work yet* -->
|
||||||
|
- (Stylishly) print formated strings to the LCD
|
||||||
|
- Sleep
|
||||||
|
- Send various commands to the keyboard
|
||||||
|
- Ringbuffer
|
||||||
|
|
||||||
> It's not much, but it's honest work.
|
> It's not much, but it's honest work.
|
||||||
|
234
spicode.s65
234
spicode.s65
@ -1,227 +1,109 @@
|
|||||||
|
;;********************************************************************************
|
||||||
|
;; @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"
|
||||||
.include "math.h65"
|
.include "math.h65"
|
||||||
.include "keypad.h65"
|
.include "keypad.h65"
|
||||||
|
.include "ps2_keyboard_text_handler.h65"
|
||||||
.include "chars.h65"
|
.include "chars.h65"
|
||||||
|
.include "parity.h65"
|
||||||
|
.include "sleep.h65"
|
||||||
.import homeloop:absolute
|
.import homeloop:absolute
|
||||||
.import home:absolute
|
.import home:absolute
|
||||||
|
.import ps2_keyboard_util:absolute
|
||||||
|
.import ps2_keyboard_printer:absolute
|
||||||
|
|
||||||
.segment "SPI"
|
.segment "SPI"
|
||||||
.export CODE_START
|
.export CODE_START
|
||||||
|
|
||||||
.import memcopy
|
.import memcopy
|
||||||
|
|
||||||
.scope kb
|
|
||||||
KB_IO = IO1
|
|
||||||
.endscope
|
|
||||||
|
|
||||||
|
|
||||||
CODE_START:
|
CODE_START:
|
||||||
.assert * = $5000, error, "SPI Code not at $5000"
|
.assert * = $5000, error, "SPI Code not at $5000"
|
||||||
jsr lcd::clear
|
jsr lcd::clear
|
||||||
|
DEBUG_LED_OFF 0
|
||||||
|
DEBUG_LED_OFF 1
|
||||||
|
DEBUG_LED_OFF 2
|
||||||
|
|
||||||
|
stz kp::_DEBUG_VAL
|
||||||
lda #'$'
|
lda #'$'
|
||||||
jsr lcd::print_char
|
jsr lcd::print_char
|
||||||
|
|
||||||
; stz kp::_DEBUG_VAL
|
|
||||||
; @loop:
|
|
||||||
; lda kp::_DEBUG_VAL
|
|
||||||
; beq @loop
|
|
||||||
; stz kp::_DEBUG_VAL
|
|
||||||
; cmp #'*'
|
|
||||||
; jeq homeloop
|
|
||||||
; jsr lcd::print_char
|
|
||||||
; bra @loop
|
|
||||||
|
|
||||||
lda #<kb_irq1
|
|
||||||
sta ARG0
|
|
||||||
lda #>kb_irq1
|
|
||||||
sta ARG1
|
|
||||||
lda #<$3000
|
|
||||||
sta ARG2
|
|
||||||
lda #>$3000
|
|
||||||
sta ARG3
|
|
||||||
ldy #20
|
|
||||||
jsr memcopy
|
|
||||||
|
|
||||||
lda #<kb_irq2
|
|
||||||
sta ARG0
|
|
||||||
lda #>kb_irq2
|
|
||||||
sta ARG1
|
|
||||||
lda #<$3100
|
|
||||||
sta ARG2
|
|
||||||
lda #>$3100
|
|
||||||
sta ARG3
|
|
||||||
ldy #20
|
|
||||||
jsr memcopy
|
|
||||||
|
|
||||||
lda #'?'
|
|
||||||
jsr lcd::print_char
|
|
||||||
|
|
||||||
; PrintNC $3000
|
|
||||||
|
|
||||||
jsr kbinit
|
|
||||||
|
|
||||||
lda #'%'
|
|
||||||
jsr lcd::print_char
|
|
||||||
|
|
||||||
stz kp::_DEBUG_VAL
|
|
||||||
ldy #0
|
|
||||||
|
|
||||||
@loop:
|
@loop:
|
||||||
|
wai
|
||||||
|
@read_keypad:
|
||||||
lda kp::_DEBUG_VAL
|
lda kp::_DEBUG_VAL
|
||||||
beq @loop
|
jeq @loop
|
||||||
stz kp::_DEBUG_VAL
|
|
||||||
cmp #'*'
|
cmp #'*'
|
||||||
jeq homeloop
|
jeq homeloop
|
||||||
|
cmp #'0'
|
||||||
|
beq @l0
|
||||||
cmp #'1'
|
cmp #'1'
|
||||||
beq @l1
|
beq @l1
|
||||||
cmp #'2'
|
cmp #'2'
|
||||||
beq @l2
|
beq @l2
|
||||||
cmp #'3'
|
cmp #'3'
|
||||||
beq @l3
|
beq @l3
|
||||||
|
cmp #'4'
|
||||||
|
beq @l4
|
||||||
|
cmp #'5'
|
||||||
|
beq @l5
|
||||||
|
cmp #'6'
|
||||||
|
beq @l6
|
||||||
|
cmp #'7'
|
||||||
|
beq @l7
|
||||||
|
cmp #'8'
|
||||||
|
beq @l8
|
||||||
|
cmp #'9'
|
||||||
|
beq @l9
|
||||||
|
cmp #'#'
|
||||||
|
beq @lhash
|
||||||
cmp #'A'
|
cmp #'A'
|
||||||
beq @lA
|
beq @lA
|
||||||
cmp #'B'
|
cmp #'B'
|
||||||
beq @lB
|
beq @lB
|
||||||
cmp #'C'
|
cmp #'C'
|
||||||
beq @lC
|
beq @lC
|
||||||
|
cmp #'D'
|
||||||
|
beq @lD
|
||||||
jsr lcd::print_char
|
jsr lcd::print_char
|
||||||
bra @loop
|
jmp @loop
|
||||||
|
@l0:
|
||||||
|
jmp ps2_keyboard_util
|
||||||
@l1:
|
@l1:
|
||||||
; jsr irq_on_shift_reg
|
jmp ps2_keyboard_printer
|
||||||
jsr $3000
|
|
||||||
lda #'*'
|
|
||||||
jsr lcd::print_char
|
|
||||||
bra @loop
|
|
||||||
@l2:
|
@l2:
|
||||||
; jsr irq_on_timer
|
lcd_SetCursorPos $26
|
||||||
jsr $3100
|
lda #'6'
|
||||||
lda #'#'
|
jsr lcd::print_char
|
||||||
|
lcd_IncrementCursor
|
||||||
|
lda #'8'
|
||||||
jsr lcd::print_char
|
jsr lcd::print_char
|
||||||
bra @loop
|
|
||||||
@l3:
|
@l3:
|
||||||
lda $3000,y
|
@l4:
|
||||||
jsr lcd::print_char
|
@l5:
|
||||||
iny
|
@l6:
|
||||||
bra @loop
|
@l7:
|
||||||
|
@l8:
|
||||||
|
@l9:
|
||||||
|
@lhash:
|
||||||
@lA:
|
@lA:
|
||||||
lda kb::KB_IO + IO::SR
|
|
||||||
jsr lcd::print_char
|
|
||||||
bra @loop
|
|
||||||
@lB:
|
@lB:
|
||||||
Strf fmt_str, out_str, keycode
|
|
||||||
Print out_str
|
|
||||||
bra @loop
|
|
||||||
@lC:
|
@lC:
|
||||||
jsr lcd::clear
|
@lD:
|
||||||
bra @loop
|
jmp @loop
|
||||||
|
|
||||||
|
|
||||||
kbinit:
|
|
||||||
lda #'['
|
|
||||||
jsr lcd::print_char
|
|
||||||
; - use the shift register interrupts to read the first 8 bits
|
|
||||||
; set shift register to shift in under external clock on CB1
|
|
||||||
; - configure timer for timing the read of the last 3 bits
|
|
||||||
; timer 2 one shot mode is sufficient, leaves T1 available
|
|
||||||
lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD)
|
|
||||||
tsb kb::KB_IO + IO::ACR
|
|
||||||
; the 3 last bits take about 230us, at @1MHz => wait 230 cycles and then the shift register
|
|
||||||
lda #230
|
|
||||||
sta kb::KB_IO + IO::T2CL
|
|
||||||
stz key_read
|
|
||||||
stz key_read+1
|
|
||||||
|
|
||||||
; enable SR interrupts
|
|
||||||
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
|
|
||||||
sta kb::KB_IO + IO::IER
|
|
||||||
; load SR to reset
|
|
||||||
lda kb::KB_IO + IO::SR
|
|
||||||
lda #']'
|
|
||||||
jsr lcd::print_char
|
|
||||||
rts
|
|
||||||
|
|
||||||
|
|
||||||
;; @details
|
|
||||||
;; IO::SR has to be read before the next bit is shifted in, which happens ~75us after the irq
|
|
||||||
;; at 1MHz, handlings this interrupt takes about 50us (without any additional debug code), so it should work
|
|
||||||
irq_on_shift_reg:
|
|
||||||
; lda #'{'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
lda kb::KB_IO + IO::SR
|
|
||||||
sta key_read
|
|
||||||
stz kb::KB_IO + IO::SR
|
|
||||||
|
|
||||||
; disable SR interrupts
|
|
||||||
lda #IO::IRQ::SR
|
|
||||||
sta kb::KB_IO + IO::IER
|
|
||||||
; enable timer interrupts
|
|
||||||
lda #(IO::IRQ::IRQ | IO::IRQ::T2)
|
|
||||||
sta kb::KB_IO + IO::IER
|
|
||||||
; start timer
|
|
||||||
lda #1
|
|
||||||
sta kb::KB_IO + IO::T2CH
|
|
||||||
; lda #'}'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
rts
|
|
||||||
|
|
||||||
|
|
||||||
irq_on_timer:
|
|
||||||
; lda #'<'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
lda kb::KB_IO + IO::SR
|
|
||||||
sta key_read + 1
|
|
||||||
|
|
||||||
lda kb::KB_IO + IO::T2CL ; clear interrupt flag
|
|
||||||
|
|
||||||
; disable timer interrupts
|
|
||||||
lda #(IO::IRQ::T2)
|
|
||||||
sta kb::KB_IO + IO::IER
|
|
||||||
; enable shift register interrupts
|
|
||||||
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
|
|
||||||
sta kb::KB_IO + IO::IER
|
|
||||||
; reset SR
|
|
||||||
stz kb::KB_IO + IO::SR
|
|
||||||
|
|
||||||
; lda #'|'
|
|
||||||
; rotate bit 2 (last bit of keycode) into the carry
|
|
||||||
ror
|
|
||||||
ror
|
|
||||||
ror
|
|
||||||
lda key_read ; not affecting carry
|
|
||||||
rol ; rotate carry into byte, rotate startbit into carry
|
|
||||||
; TODO byte is inverted, maybe consider wasting 256 bytes for a bit reverse lookup table?
|
|
||||||
sta keycode
|
|
||||||
|
|
||||||
Strf fmt_str2, out_str, keycode, key_read, key_read+1
|
|
||||||
PrintNC out_str
|
|
||||||
|
|
||||||
stz key_read
|
|
||||||
stz key_read+1
|
|
||||||
|
|
||||||
rts
|
|
||||||
|
|
||||||
key_read: .res 2
|
|
||||||
keycode: .res 1
|
|
||||||
kb_irq1:
|
|
||||||
; lda #'!'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
jsr irq_on_shift_reg
|
|
||||||
; lda #':'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
rts
|
|
||||||
.byte '='
|
|
||||||
kb_irq2:
|
|
||||||
; lda #'?'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
jsr irq_on_timer
|
|
||||||
; lda #';'
|
|
||||||
; jsr lcd::print_char
|
|
||||||
rts
|
|
||||||
.byte '@'
|
.byte '@'
|
||||||
|
|
||||||
out_str: .res 40
|
out_str: .res 40
|
||||||
fmt_str: .asciiz "kc%x;"
|
FMT_INIT_FAIL: .asciiz "KB init failed: %x%x > %x%x%x"
|
||||||
fmt_str2: .asciiz "kc%x-%x-%x; "
|
|
||||||
|
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module ringbuffer
|
;; @file
|
||||||
; @type utility
|
;; @brief Ringbuffer
|
||||||
; @details
|
;; @details
|
||||||
; Size of the ringbuffer is RBUF_MEM_END - RBUF_MEM_START - 2, since two bytes
|
;; Size of the ringbuffer is RBUF_MEM_END - RBUF_MEM_START - 2, since two bytes
|
||||||
; are used by the read and write pointer
|
;; are used by the read and write pointer
|
||||||
; The RBUF_NAME variable must be defined, the functions will then be exported
|
;; The `RBUF_NAME` variable must be defined, the functions will then be exported
|
||||||
; as rb_<RBUF_NAME>_<function> where <function> = init, read or write
|
;; as `rb_<RBUF_NAME>_<function>` where `<function>` = `init`, `read` or `write`
|
||||||
; @requires
|
;;
|
||||||
; RBUF_MEM_START: First address of ringbuffer memory space
|
;; @note
|
||||||
; RBUF_MEM_END: Last address of ringbuffer memory space
|
;; Doxygen can not generate proper documentation for this module, because
|
||||||
; RBUF_NAME: Name of the ringbuffer
|
;; all names are defined using macros
|
||||||
;********************************************************************************
|
;; @requires
|
||||||
|
;; RBUF_MEM_START: First address of ringbuffer memory space
|
||||||
|
;; RBUF_MEM_END: Last address of ringbuffer memory space
|
||||||
|
;; RBUF_NAME: Name of the ringbuffer
|
||||||
|
;; @ingroup libs
|
||||||
|
;;********************************************************************************
|
||||||
.code
|
.code
|
||||||
|
|
||||||
.ifndef RBUF_MEM_START
|
.ifndef RBUF_MEM_START
|
||||||
@ -36,9 +41,9 @@ RB_LENGTH = RBUF_MEM_END - RBUF_MEM_START - 2
|
|||||||
.endif
|
.endif
|
||||||
|
|
||||||
|
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Initialize the buffer
|
;; @function Initialize the buffer
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.ident(.concat(_RBUF_NAME, "_init")):
|
.ident(.concat(_RBUF_NAME, "_init")):
|
||||||
.scope
|
.scope
|
||||||
stz RB_WRITE
|
stz RB_WRITE
|
||||||
@ -46,13 +51,13 @@ RB_LENGTH = RBUF_MEM_END - RBUF_MEM_START - 2
|
|||||||
rts
|
rts
|
||||||
.endscope
|
.endscope
|
||||||
|
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Read a value from the buffer
|
;; @function Read a value from the buffer
|
||||||
; @details
|
;; @details
|
||||||
; If there is no value to be read, the Pz will be set
|
;; If there is no value to be read, the Pz will be set
|
||||||
; @returns A: value
|
;; @returns A: value
|
||||||
; @modifies: A, X
|
;; @modifies A, X
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.ident(.concat(_RBUF_NAME, "_read")):
|
.ident(.concat(_RBUF_NAME, "_read")):
|
||||||
.scope
|
.scope
|
||||||
ldx RB_READ
|
ldx RB_READ
|
||||||
@ -72,11 +77,11 @@ RB_LENGTH = RBUF_MEM_END - RBUF_MEM_START - 2
|
|||||||
rts
|
rts
|
||||||
.endscope
|
.endscope
|
||||||
|
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Write a value to the buffer
|
;; @function Write a value to the buffer
|
||||||
; @param A: value to store
|
;; @param A: value to store
|
||||||
; @modifies: X
|
;; @modifies X
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.ident(.concat(_RBUF_NAME, "_write")):
|
.ident(.concat(_RBUF_NAME, "_write")):
|
||||||
.scope
|
.scope
|
||||||
; lda kp_VALUES, x ; load the char in a
|
; lda kp_VALUES, x ; load the char in a
|
||||||
|
@ -1,35 +1,51 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module IO-W65C22
|
;; @file
|
||||||
; @type utility
|
;; @brief VIA W65C22 header
|
||||||
; @device Western Design - W65C22N Versatile Interface Adapter
|
;; @ingroup system
|
||||||
; @details
|
;; @details
|
||||||
;********************************************************************************
|
;; @device Western Design - W65C22N Versatile Interface Adapter
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
.ifndef INCLUDE_IOW65C22
|
.ifndef INCLUDE_IOW65C22
|
||||||
INCLUDE_IOW65C22 = 1
|
INCLUDE_IOW65C22 = 1
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Versatile Interface Adapter (VIA) W65C22
|
||||||
|
;; @ingroup system
|
||||||
|
;; @device Western Design - W65C22N Versatile Interface Adapter
|
||||||
|
;; @todo rename to VIA
|
||||||
|
;; @warning @anchor via_hardware_bug
|
||||||
|
;; The 6522 and 65C22 have a hardware bug, where a bit is not read in when the
|
||||||
|
;; external shift register clock transitions close to @f$ \phi_2 @f$.
|
||||||
|
;; To resolve this, you should use a @f$ \phi_2 @f$ controlled flip flop to ensure
|
||||||
|
;; the external clock transitions after the system clock.
|
||||||
|
;;********************************************************************************
|
||||||
.scope IO
|
.scope IO
|
||||||
|
|
||||||
; not using a struct for this since the syntax for access would be the same,
|
; not using a struct for this since the syntax for access would be the same,
|
||||||
; ie label+IO::RA
|
; ie label+IO::RA
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief VIA register offsets from the base address
|
||||||
|
;; @details
|
||||||
|
;; Use like this: `VIA_ADDRESS + IO::RB`
|
||||||
|
;;********************************************************************************
|
||||||
.enum
|
.enum
|
||||||
; IO-CHIPS OFFSETS FOR PINS FROM BASE ADDRESS
|
RB = $0 ;;< Register B (ORB/IRB)
|
||||||
RB = $0 ; Register B (ORB/IRB)
|
RA = $1 ;;< Register A (ORA/IRA)
|
||||||
RA = $1 ; Register A (ORA/IRA)
|
DDRB = $2 ;;< Data Direction Register B
|
||||||
DDRB = $2 ; Data Direction Register B
|
DDRA = $3 ;;< Data Direction Register A
|
||||||
DDRA = $3 ; Data Direction Register A
|
T1CL = $4 ;;< Timer 1 Counter Low/High
|
||||||
T1CL = $4 ; Timer 1 Counter Low/High
|
|
||||||
T1CH = $5
|
T1CH = $5
|
||||||
T1LL = $6 ; Timer 1 Latch Low/High
|
T1LL = $6 ;;< Timer 1 Latch Low/High
|
||||||
T1LH = $7
|
T1LH = $7
|
||||||
T2CL = $8 ; Timer 2 Counter Low/High
|
T2CL = $8 ;;< Timer 2 Counter Low/High
|
||||||
T2CH = $9
|
T2CH = $9
|
||||||
SR = $a ; Shift Register
|
SR = $a ;;< Shift Register
|
||||||
ACR = $b ; Auxiliary Control Register
|
ACR = $b ;;< Auxiliary Control Register
|
||||||
PCR = $c ; Peripheral Control Register
|
PCR = $c ;;< Peripheral Control Register
|
||||||
IFR = $d ; Interrupt Flag Register
|
IFR = $d ;;< Interrupt Flag Register
|
||||||
IER = $e ; Interrupt Enable Register
|
IER = $e ;;< Interrupt Enable Register
|
||||||
RANH = $f ; RA without handshake
|
RANH = $f ;;< RA without handshake
|
||||||
.endenum
|
.endenum
|
||||||
|
|
||||||
.enum ACR_MASK ; ACR Masks
|
.enum ACR_MASK ; ACR Masks
|
||||||
@ -40,27 +56,32 @@ INCLUDE_IOW65C22 = 1
|
|||||||
T1 = %11000000
|
T1 = %11000000
|
||||||
.endenum
|
.endenum
|
||||||
|
|
||||||
.enum ACR ; ACR Settings
|
;;********************************************************************************
|
||||||
|
;; @brief Settings for Auxiliary Control Register
|
||||||
|
;; @details
|
||||||
|
;; `OR` the values with the appropriate IO::ACR_MASK to not target only specific settings
|
||||||
|
;;********************************************************************************
|
||||||
|
.enum ACR
|
||||||
; SR Modes
|
; SR Modes
|
||||||
SR_DISABLE = %00000000 ; Disabled
|
SR_DISABLE = %00000000 ;;< Disabled
|
||||||
SR_SIN_T2 = %00000100 ; Shift in under control of T2
|
SR_SIN_T2 = %00000100 ;;< Shift in under control of T2
|
||||||
SR_SIN_PHI2 = %00001000 ; Shift in under control of PHI2
|
SR_SIN_PHI2 = %00001000 ;;< Shift in under control of PHI2
|
||||||
SR_SIN_PHIE = %00001100 ; Shift in under control of external clock
|
SR_SIN_PHIE = %00001100 ;;< Shift in under control of external clock
|
||||||
SR_SOUT_FREE_T2 = %00010000 ; Shift out free running at T2 rate
|
SR_SOUT_FREE_T2 = %00010000 ;;< Shift out free running at T2 rate
|
||||||
SR_SOUT_T2 = %00010100 ; Shift out under control of T2
|
SR_SOUT_T2 = %00010100 ;;< Shift out under control of T2
|
||||||
SR_SOUT_PHI2 = %00011000 ; Shift out under control of PHI2
|
SR_SOUT_PHI2 = %00011000 ;;< Shift out under control of PHI2
|
||||||
SR_SOUT_PHIE = %00011100 ; Shift out under control of external clock
|
SR_SOUT_PHIE = %00011100 ;;< Shift out under control of external clock
|
||||||
; T1 Modes
|
; T1 Modes
|
||||||
T1_IRQ_LOAD = %00000000 ; Timed interrupt each time T1 is loaded
|
T1_IRQ_LOAD = %00000000 ;;< Timed interrupt each time T1 is loaded
|
||||||
T1_IRQ_CONT = %01000000 ; Continuous interrupts
|
T1_IRQ_CONT = %01000000 ;;< Continuous interrupts
|
||||||
T1_IRQ_LOAD_PB7 = %10000000 ; Timed interrupt each time T1 is loaded - PB7 One Shot output
|
T1_IRQ_LOAD_PB7 = %10000000 ;;< Timed interrupt each time T1 is loaded - PB7 One Shot output
|
||||||
T1_IRQ_CONT_PB7 = %11000000 ; Continuous interrupts - PB7 Square wave output
|
T1_IRQ_CONT_PB7 = %11000000 ;;< Continuous interrupts - PB7 Square wave output
|
||||||
; T2 Modes
|
; T2 Modes
|
||||||
T2_IRQ_LOAD = %00000000 ; Timed interrupt each time T2 is loaded
|
T2_IRQ_LOAD = %00000000 ;;< Timed interrupt each time T2 is loaded
|
||||||
T2_COUNT_PB6 = %00100000 ; Count down with pulsen on PB6
|
T2_COUNT_PB6 = %00100000 ;;< Count down with pulsen on PB6
|
||||||
; Latch
|
; Latch
|
||||||
LATCH_DISABLE = %00000000
|
LATCH_DISABLE = %00000000 ;;< `OR` this with IO::ACR_MASK::PA or IO::ACR_MASK::PB
|
||||||
LATCH_ENBLE = %00000000
|
LATCH_ENBLE = %00000011 ;;< `OR` this with IO::ACR_MASK::PA or IO::ACR_MASK::PB
|
||||||
.endenum
|
.endenum
|
||||||
|
|
||||||
.enum PCR_MASK ; PCR Masks
|
.enum PCR_MASK ; PCR Masks
|
||||||
@ -70,34 +91,42 @@ INCLUDE_IOW65C22 = 1
|
|||||||
CB2 = %11100000 ;
|
CB2 = %11100000 ;
|
||||||
.endenum
|
.endenum
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Settings for Peripheral Control Register
|
||||||
|
;; @details
|
||||||
|
;; `OR` the values with the appropriate IO::PCR_MASK to not target only specific settings
|
||||||
|
;;********************************************************************************
|
||||||
.enum PCR
|
.enum PCR
|
||||||
; CA1 Modes
|
; CA1 Modes
|
||||||
CA1_IN_AE = %00000000 ; Input-negative active edge
|
CA1_IN_AE = %00000000 ;;< Input-negative active edge
|
||||||
CA1_IP_AE = %00000001 ; Input-positive active edge
|
CA1_IP_AE = %00000001 ;;< Input-positive active edge
|
||||||
; CA2 Modes
|
; CA2 Modes
|
||||||
CA2_IN_AE = %00000000 ; Input-negative active edge
|
CA2_IN_AE = %00000000 ;;< Input-negative active edge
|
||||||
CA2_IN_AE_IRQ_IND= %00000010 ; Independent interrupt input-negative edge
|
CA2_IN_AE_IRQ_IND= %00000010 ;;< Independent interrupt input-negative edge
|
||||||
CA2_IP_AE = %00000100 ; Input-positive active edge
|
CA2_IP_AE = %00000100 ;;< Input-positive active edge
|
||||||
CA2_IP_AE_IRQ_IND= %00000110 ; Independent interrupt input-positive edge
|
CA2_IP_AE_IRQ_IND= %00000110 ;;< Independent interrupt input-positive edge
|
||||||
CA2_IN_HANDSHAKE = %00001000 ; Handshake output
|
CA2_OUT_HANDSHAKE= %00001000 ;;< Handshake output
|
||||||
CA2_IN_PULSE_OUT = %00001010 ; Pulse output
|
CA2_OUT_PULSE = %00001010 ;;< Pulse output
|
||||||
CA2_IN_LOW_OUT = %00001100 ; Low output
|
CA2_OUT_LOW = %00001100 ;;< Low output
|
||||||
CA2_IN_HIGH_OUT = %00001110 ; High output
|
CA2_OUT_HIGH = %00001110 ;;< High output
|
||||||
; CB1 Modes
|
; CB1 Modes
|
||||||
CB1_IN_AE = %00000000 ; Input-negative active edge
|
CB1_IN_AE = %00000000 ;;< Input-negative active edge
|
||||||
CB1_IP_AE = %00010000 ; Input-positive active edge
|
CB1_IP_AE = %00010000 ;;< Input-positive active edge
|
||||||
; CB2 Modes
|
; CB2 Modes
|
||||||
CB2_IN_AE = %00000000 ; Input-negative active edge
|
CB2_IN_AE = %00000000 ;;< Input-negative active edge
|
||||||
CB2_IN_AE_IRQ_IND= %00100000 ; Independent interrupt input-negative edge
|
CB2_IN_AE_IRQ_IND= %00100000 ;;< Independent interrupt input-negative edge
|
||||||
CB2_IP_AE = %01000000 ; Input-positive active edge
|
CB2_IP_AE = %01000000 ;;< Input-positive active edge
|
||||||
CB2_IP_AE_IRQ_IND= %01100000 ; Independent interrupt input-positive edge
|
CB2_IP_AE_IRQ_IND= %01100000 ;;< Independent interrupt input-positive edge
|
||||||
CB2_IN_HANDSHAKE = %10000000 ; Handshake output
|
CB2_OUT_HANDSHAKE= %10000000 ;;< Handshake output
|
||||||
CB2_IN_PULSE_OUT = %10100000 ; Pulse output
|
CB2_OUT_PULSE = %10100000 ;;< Pulse output
|
||||||
CB2_IN_LOW_OUT = %11000000 ; Low output
|
CB2_OUT_LOW = %11000000 ;;< Low output
|
||||||
CB2_IN_HIGH_OUT = %11100000 ; High output
|
CB2_OUT_HIGH = %11100000 ;;< High output
|
||||||
.endenum
|
.endenum
|
||||||
|
|
||||||
; IFR/IER bits
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Interrupt Flag/Enable Register bits
|
||||||
|
;;********************************************************************************
|
||||||
.enum IRQ
|
.enum IRQ
|
||||||
CA2 = %00000001
|
CA2 = %00000001
|
||||||
CA1 = %00000010
|
CA1 = %00000010
|
||||||
@ -109,5 +138,24 @@ INCLUDE_IOW65C22 = 1
|
|||||||
IRQ = %10000000
|
IRQ = %10000000
|
||||||
.endenum
|
.endenum
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Enable an interrupt source
|
||||||
|
;; @modifies A
|
||||||
|
;; @param flag: A flag of the interrupt flag register (IO::IRQ)
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro IO_EnableIRQ ioaddr, flag
|
||||||
|
lda #(IO::IRQ::IRQ | flag)
|
||||||
|
sta ioaddr + IO::IER
|
||||||
|
.endmacro
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Disable an interrupt source
|
||||||
|
;; @modifies A
|
||||||
|
;; @param flag: A flag of the interrupt flag register (IO::IRQ)
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro IO_DisableIRQ ioaddr, flag
|
||||||
|
lda #flag
|
||||||
|
sta ioaddr + IO::IER
|
||||||
|
.endmacro
|
||||||
|
|
||||||
.endscope ; IO
|
.endscope ; IO
|
||||||
.endif ; guard
|
.endif ; guard
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module irq_handler
|
;; @file
|
||||||
; @type system
|
;; @brief WIP irq handler with jump tables and stuff
|
||||||
;********************************************************************************
|
;; @ingroup system
|
||||||
|
;;********************************************************************************
|
||||||
.ifndef INCLUDE_IRQ_HANDLER
|
.ifndef INCLUDE_IRQ_HANDLER
|
||||||
INCLUDE_IRQ_HANDLER = 1
|
INCLUDE_IRQ_HANDLER = 1
|
||||||
|
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module keypad4x4
|
;; @file
|
||||||
; @type driver
|
;; @brief 4x4 Matrix Keypad driver
|
||||||
; @device 4x4 Matrix Keypad
|
;; @details
|
||||||
; @details
|
;; The LCD must be connected to a W65C22N Interface Chip:
|
||||||
; The LCD must be connected to a W65C22N Interface Chip:
|
;; - IO.RA0-7 ->
|
||||||
; - IO.RA0-7 ->
|
;; @requires KP_IO: Base Address of IO Chip
|
||||||
; @requires KP_IO: Base Address of IO Chip
|
;; @depends IO-W65C22N
|
||||||
; @depends IO-W65C22N
|
;; @ingroup drivers
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.ifndef INCLUDE_KEYPAD
|
.ifndef INCLUDE_KEYPAD
|
||||||
INCLUDE_KEYPAD = 1
|
INCLUDE_KEYPAD = 1
|
||||||
.include "system.h65"
|
.include "system.h65"
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief 4x4 Matrix Keypad
|
||||||
|
;;********************************************************************************
|
||||||
.scope kp
|
.scope kp
|
||||||
KP_IO = IO2
|
KP_IO = IO2
|
||||||
; .import KP_IO
|
; .import KP_IO
|
||||||
|
137
system/lcd.h65
137
system/lcd.h65
@ -1,26 +1,63 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module LCD-W164B
|
;; @file
|
||||||
; @type driver
|
;; @brief LCD-W164B driver
|
||||||
; @device ELECTRONIC ASSEMBLY - W164B-NLW
|
;; @ingroup drivers
|
||||||
; @details
|
;; @device ELECTRONIC ASSEMBLY - W164B-NLW
|
||||||
; The LCD must be connected to a W65C22N Interface Chip:
|
;; @details
|
||||||
; - IO.RB0-7 -> LCD.D0-7 data lines
|
;; The LCD must be connected to a W65C22N Interface Chip:
|
||||||
; - IO.RA5 -> LCD.RS register select
|
;; - IO.RB0-7 -> LCD.D0-7 data lines
|
||||||
; - IO.RA6 -> LCD.R/W read/write
|
;; - IO.RA5 -> LCD.RS register select
|
||||||
; - IO.RA7 -> LCD.E enable
|
;; - IO.RA6 -> LCD.R/W read/write
|
||||||
;
|
;; - IO.RA7 -> LCD.E enable
|
||||||
; @requires IO: Base Address of IO Chip
|
;; @requires IO: Base Address of IO Chip
|
||||||
; @depends IO-W65C22N
|
;; @depends IO-W65C22N
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.ifndef INCLUDE_LCD
|
.ifndef INCLUDE_LCD
|
||||||
INCLUDE_LCD = 1
|
INCLUDE_LCD = 1
|
||||||
|
|
||||||
.include "system/system.h65"
|
.include "system/system.h65"
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief LCD character display
|
||||||
|
;; @ingroup drivers
|
||||||
|
;;********************************************************************************
|
||||||
.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
|
Import lcd,_cmd,_wait_nbusy,_write_ram,_read_ram,_set_dd_ram_addr_from_charcount
|
||||||
|
Import lcd,_charcount
|
||||||
|
|
||||||
|
; PIN MASKS
|
||||||
|
E = %10000000
|
||||||
|
RW = %01000000
|
||||||
|
RS = %00100000
|
||||||
|
|
||||||
|
RA_MASK = %11100000
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; 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
|
||||||
|
LINE3 = $10
|
||||||
|
LINE4 = $50
|
||||||
|
|
||||||
|
.endscope
|
||||||
|
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @macro Clear the screen and print a null-terminated string
|
;; @macro Clear the screen and print a null-terminated string
|
||||||
@ -73,29 +110,55 @@ Import lcd,_cmd,_wait_nbusy,_write_ram,_read_ram
|
|||||||
jsr lcd::print
|
jsr lcd::print
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
; PIN MASKS
|
|
||||||
E = %10000000
|
|
||||||
RW = %01000000
|
|
||||||
RS = %00100000
|
|
||||||
|
|
||||||
RA_MASK = %11100000
|
;;********************************************************************************
|
||||||
|
;; @macro Set the cursor to the n-th position
|
||||||
|
;; @param n: Position [0,$40) or a register
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro lcd_SetCursorPos n
|
||||||
|
.if .match (n, a)
|
||||||
|
.elseif .match (n, x)
|
||||||
|
txa
|
||||||
|
.elseif .match (n, y)
|
||||||
|
tya
|
||||||
|
.else
|
||||||
|
lda #n
|
||||||
|
.endif
|
||||||
|
sta lcd::_charcount
|
||||||
|
jsr lcd::_set_dd_ram_addr_from_charcount
|
||||||
|
.endmacro
|
||||||
|
|
||||||
; LCD Instructions (see datasheet for more)
|
;;********************************************************************************
|
||||||
CMD_CLEAR = %00000001 ; clear display
|
;; @macro Move the cursor to the right
|
||||||
CMD_ENTRY_MODE = %00000110 ; auto-shift cursor
|
;; @param n: Number of characters to move n. Defaults to 1 if not provided
|
||||||
CMD_DISPLAY_ON = %00001111 ; everything on, with blinking cursor
|
;;********************************************************************************
|
||||||
CMD_SHIFT_CUR_LEFT = %00101000 ; shift the display to the left
|
.macro lcd_IncrementCursor n
|
||||||
CMD_SHIFT_DIS_LEFT = %00111000 ; shift the display to the left
|
.ifblank n
|
||||||
CMD_SHIFT_DIS_RIGHT = %00110000 ; shift the display to the right
|
inc lcd::_charcount
|
||||||
CMD_SHIFT_CUR_RIGHT = %00100000 ; shift the display to the right
|
.elseif n = 1
|
||||||
CMD_FUNCTION_SET = %00111000 ; 8-bit, 4 lines, 5x7 font
|
inc lcd::_charcount
|
||||||
CMD_SET_CG_ADDRESS = %01000000 ; or with the address
|
.else
|
||||||
CMD_SET_ADDRESS = %10000000 ; or with the address
|
lda lcd::_charcount
|
||||||
; LCD Constants
|
add #n
|
||||||
LINE1 = $00
|
sta lcd::_charcount
|
||||||
LINE2 = $40
|
.endif
|
||||||
LINE3 = $10
|
jsr lcd::_set_dd_ram_addr_from_charcount
|
||||||
LINE4 = $50
|
.endmacro
|
||||||
|
|
||||||
.endscope
|
;;********************************************************************************
|
||||||
|
;; @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
|
||||||
|
139
system/lcd.s65
139
system/lcd.s65
@ -5,12 +5,13 @@
|
|||||||
.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
|
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
|
||||||
|
|
||||||
; TODO when clockspeeds are more than 1MHz, _cmd, _write_ram and _read_ram might need to be adjusted
|
; @TODO when clockspeeds are more than 1MHz, _cmd, _write_ram and _read_ram might need to be adjusted
|
||||||
.code
|
.code
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @function Initialize the lcd module
|
;; @function Initialize the lcd module
|
||||||
@ -22,21 +23,21 @@ charcount: .res 1
|
|||||||
lda #$ff ; RB 0-7 output
|
lda #$ff ; RB 0-7 output
|
||||||
sta lcd::LCD_IO+IO::DDRB
|
sta lcd::LCD_IO+IO::DDRB
|
||||||
|
|
||||||
MaskedWrite lcd::LCD_IO+IO::DDRA, (lcd::RS | lcd::RW | lcd::E), lcd::RA_MASK
|
lda lcd::LCD_IO+IO::DDRA
|
||||||
; lda #(lcd::RS | lcd::RW | lcd::E) ; RA 5-7 output
|
ora #(lcd::RS | lcd::RW | lcd::E)
|
||||||
; 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
|
||||||
rts
|
rts
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ charcount: .res 1
|
|||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @function Set the cursor to a position
|
;; @function Set the cursor to a position
|
||||||
;; @param A: cursor position: `(lcd::LINEX + offset)`, where offset € [$0, $f]
|
;; @param A: cursor position: `(lcd::LINEX + offset)`, where offset € [$0, $f]
|
||||||
;; @details:
|
;; @details
|
||||||
;; If the position is too large, it will be set to char 4 in line 2
|
;; If the position is too large, it will be set to char 4 in line 2
|
||||||
;; @returns A: the cursor position
|
;; @returns A: the cursor position
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
@ -61,37 +62,37 @@ charcount: .res 1
|
|||||||
cmp #$10
|
cmp #$10
|
||||||
bge @line3
|
bge @line3
|
||||||
bra @set
|
bra @set
|
||||||
; @line1: ; starts at $00, charcount at $00
|
; @line1: ; starts at $00, _charcount at $00
|
||||||
@line2: ; starts at $40, charcount at $10
|
@line2: ; starts at $40, _charcount at $10
|
||||||
sbc #$30 ; carry is already set
|
sbc #$30 ; carry is already set
|
||||||
bra @set
|
bra @set
|
||||||
@line3: ; starts at $10, charcount at $20
|
@line3: ; starts at $10, _charcount at $20
|
||||||
add #$10
|
add #$10
|
||||||
bra @set
|
bra @set
|
||||||
@line4: ; starts at $50, charcount at $30
|
@line4: ; starts at $50, _charcount at $30
|
||||||
sbc #$20
|
sbc #$20
|
||||||
@set:
|
@set:
|
||||||
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
|
||||||
@ -99,8 +100,8 @@ charcount: .res 1
|
|||||||
;; @modifies A
|
;; @modifies A
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
.proc clear
|
.proc clear
|
||||||
stz charcount
|
stz _charcount
|
||||||
lda #lcd::CMD_CLEAR
|
lda #lcd::CMD::CLEAR
|
||||||
jsr _cmd
|
jsr _cmd
|
||||||
rts
|
rts
|
||||||
.endproc
|
.endproc
|
||||||
@ -108,7 +109,7 @@ charcount: .res 1
|
|||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @function Print a null-terminated string
|
;; @function Print a null-terminated string
|
||||||
;; @param ARG0-1 Address of the string to print
|
;; @param ARG0-1 Address of the string to print
|
||||||
;; @modifies: A,Y
|
;; @modifies A,Y
|
||||||
;; @returns Y: Length of the string
|
;; @returns Y: Length of the string
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
.proc print
|
.proc print
|
||||||
@ -131,7 +132,7 @@ charcount: .res 1
|
|||||||
.proc print_char
|
.proc print_char
|
||||||
pha
|
pha
|
||||||
jsr _write_ram
|
jsr _write_ram
|
||||||
inc charcount
|
inc _charcount
|
||||||
jsr _break_line
|
jsr _break_line
|
||||||
pla ; put char back in a
|
pla ; put char back in a
|
||||||
.endproc
|
.endproc
|
||||||
@ -142,7 +143,7 @@ charcount: .res 1
|
|||||||
;; @param A: The ASCII code: 0-7
|
;; @param A: The ASCII code: 0-7
|
||||||
;; @param ARG0-1: Start address of the 8 bytes describing the character
|
;; @param ARG0-1: Start address of the 8 bytes describing the character
|
||||||
;; @returns: C: 0 => success, 1 => invalid argument
|
;; @returns: C: 0 => success, 1 => invalid argument
|
||||||
;; @modifies: A,Y
|
;; @modifies A,Y
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
.proc set_custom_char
|
.proc set_custom_char
|
||||||
cmp #8
|
cmp #8
|
||||||
@ -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:
|
||||||
@ -176,19 +177,24 @@ charcount: .res 1
|
|||||||
.proc _wait_nbusy
|
.proc _wait_nbusy
|
||||||
pha
|
pha
|
||||||
stz lcd::LCD_IO + IO::DDRB ; set IO1-IO + IO::RB to input
|
stz lcd::LCD_IO + IO::DDRB ; set IO1-IO + IO::RB to input
|
||||||
@lcd_wait_nbusy_loop: ; read the busy flag
|
lda lcd::LCD_IO + IO::RA
|
||||||
; TODO use update_with_mask
|
and #<~lcd::RA_MASK
|
||||||
lda #lcd::RW
|
ora #lcd::RW
|
||||||
sta lcd::LCD_IO + IO::RA
|
sta lcd::LCD_IO + IO::RA
|
||||||
lda #(lcd::RW | lcd::E)
|
|
||||||
|
@lcd_wait_nbusy_loop: ; read the busy flag
|
||||||
|
; set E low, then high
|
||||||
|
lda lcd::LCD_IO + IO::RA
|
||||||
|
and #<~lcd::E
|
||||||
|
sta lcd::LCD_IO + IO::RA
|
||||||
|
ora #lcd::E
|
||||||
sta lcd::LCD_IO + IO::RA
|
sta lcd::LCD_IO + IO::RA
|
||||||
|
|
||||||
lda lcd::LCD_IO + IO::RB
|
lda lcd::LCD_IO + IO::RB
|
||||||
bmi @lcd_wait_nbusy_loop ; msb set
|
bmi @lcd_wait_nbusy_loop ; msb set
|
||||||
; and #%10000000 ; and updates zero flag, if not set retry
|
|
||||||
; bne @lcd_wait_nbusy_loop
|
|
||||||
|
|
||||||
lda #%00000000 ; TODO dont overwrite 0-4
|
lda lcd::LCD_IO + IO::RA
|
||||||
|
and #<~lcd::RA_MASK
|
||||||
sta lcd::LCD_IO + IO::RA
|
sta lcd::LCD_IO + IO::RA
|
||||||
lda #%11111111 ; set IO1-IO + IO::RB to output
|
lda #%11111111 ; set IO1-IO + IO::RB to output
|
||||||
sta lcd::LCD_IO + IO::DDRB
|
sta lcd::LCD_IO + IO::DDRB
|
||||||
@ -198,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
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
@ -281,11 +287,54 @@ charcount: .res 1
|
|||||||
|
|
||||||
|
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @function Set the LCD DD-RAM Address so that text linebreaks after 16 chars
|
;; @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 _charcount % $40
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _set_dd_ram_addr_from_charcount
|
||||||
|
cmp #$40 ; set to line1 when full
|
||||||
|
bcs @wrap_to_line1 ; a € [$40, $ff]
|
||||||
|
cmp #$10
|
||||||
|
bcc @line1 ; a € [$00, $0f]
|
||||||
|
cmp #$20
|
||||||
|
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:
|
||||||
|
and #%00111111
|
||||||
|
; now every _charcount is mapped between 0 and $3f
|
||||||
|
bra @set_address
|
||||||
|
@line2:
|
||||||
|
adc #$30 ; _charcount $10-$1f -> $40 - $4f
|
||||||
|
bra @set_address
|
||||||
|
@line3:
|
||||||
|
sec
|
||||||
|
sbc #$10 ; _charcount $20-$2f -> $10 - $1f
|
||||||
|
bra @set_address
|
||||||
|
@line1: ; _charcount $00-$1f -> $00 - $0f - nothing to do
|
||||||
|
@set_address:
|
||||||
|
ora #lcd::CMD::SET_ADDRESS
|
||||||
|
jsr _cmd
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Set the LCD DD-RAM Address to te next line if the end of the line was reached
|
||||||
|
;; @details
|
||||||
|
;; If the cursor is past the end of a line, the DD-RAM Address is set to the next line.
|
||||||
|
;; If _charcount is more than $40, the position will be set to line 1.
|
||||||
|
;; This function is intended to be used with to set the correct address when
|
||||||
|
;; auto-shift is enabled
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
.proc _break_line
|
.proc _break_line
|
||||||
; check if checks are necessary
|
; check if checks are necessary
|
||||||
lda charcount
|
lda _charcount
|
||||||
beq @line1
|
beq @line1
|
||||||
bit #%10001111 ; ($10 | $20 | $30 | $40) = %01110000
|
bit #%10001111 ; ($10 | $20 | $30 | $40) = %01110000
|
||||||
beq @check
|
beq @check
|
||||||
@ -302,20 +351,20 @@ charcount: .res 1
|
|||||||
bge @line1
|
bge @line1
|
||||||
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
|
||||||
|
219
system/ps2_keyboard.h65
Normal file
219
system/ps2_keyboard.h65
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief PS/2 Keyboard driver
|
||||||
|
;; @ingroup drivers
|
||||||
|
;; @details
|
||||||
|
;; Support for a PS2 Keyboard using the shift register of a 6522 VIA
|
||||||
|
;; @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
|
||||||
|
;; address the hardware bug of the VIA, where bit get lost when the external clock
|
||||||
|
;; transition happens during falling edge of PHI2). After reading the shift register,
|
||||||
|
;; 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 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`.
|
||||||
|
;;
|
||||||
|
;; To use a different layout, change the enum K and the CHARS_NOMOD and CHARS_MODSHIFT
|
||||||
|
;; @warning
|
||||||
|
;; The value with which the timer is loaded must depends on the clock frequency
|
||||||
|
;; of the keyboard and the computer
|
||||||
|
;;********************************************************************************
|
||||||
|
.ifndef INCLUDE_PS2_KEYBOARD
|
||||||
|
INCLUDE_PS2_KEYBOARD = 1
|
||||||
|
.include "system.h65"
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief PS/2 Keyboard
|
||||||
|
;; This requires the data line to be hooked up to `CB2` and the clock to `CB1`.
|
||||||
|
;; The VIA will be set to shift in the data into the shift register under the external clock.
|
||||||
|
;; @see @ref via_hardware_bug "VIA external clock bug"
|
||||||
|
;; @include keyboard
|
||||||
|
;; @ingroup drivers
|
||||||
|
;;********************************************************************************
|
||||||
|
.scope ps2kb
|
||||||
|
Import ps2kb, init, begin_receive, scancode, status, scancode_handler
|
||||||
|
Import ps2kb, _receive_irq_shift_reg_handler, _receive_irq_timer_handler, _send_byte, _send_irq_shift_reg_handler, _send_irq_timer_handler,
|
||||||
|
Import ps2kb, send_command, send_cmd, send_data, cmd_response, response_length, FMT_CMD_FAIL
|
||||||
|
|
||||||
|
;; Base address of the VIA the keyboard is conencted to
|
||||||
|
VIA = IO1
|
||||||
|
|
||||||
|
;; @brief #clock cycles to to wait for the last 3 bits after the first 8 have been shifted in
|
||||||
|
;; @details
|
||||||
|
;; The last 3 bits take about 230 ms.
|
||||||
|
;; Calculate the appropriate value using: @f$ N_\text{cycles} = 230 \times f_\text{in MHZ} @f$
|
||||||
|
;; - 230 \@ 1MHz
|
||||||
|
;; - 400 \@ 1.84 MHz
|
||||||
|
;; @clock_dependent
|
||||||
|
TIMER_RECV = 400
|
||||||
|
;; @brief #clock cycles to wait after loading the SR a second time while sending a command to the keyboard
|
||||||
|
;; @details
|
||||||
|
;; Enough time must pass for one bit must be shifted out (parity),
|
||||||
|
;; but at most two (parity+stop).
|
||||||
|
;; After that, the interrupt must have happened because the keyboard will pull data low.
|
||||||
|
;; At that point, the shift register needs to be set to input again
|
||||||
|
;;
|
||||||
|
;; Values that seem to work:
|
||||||
|
;; - 230 \@ 1MHz
|
||||||
|
;; - 400 \@ 1.84 MHz
|
||||||
|
;; @clock_dependent
|
||||||
|
TIMER_SEND = 400
|
||||||
|
|
||||||
|
PULL_REG = IO::RANH
|
||||||
|
PULL_DDR = IO::DDRA
|
||||||
|
; use RA4 to pull the clock low
|
||||||
|
PULL_MASK_CLK = %00010000
|
||||||
|
|
||||||
|
; using ff because 0 is a possible data byte (set led)
|
||||||
|
NO_DATA = $ff ;; indicates that no data byte should be send
|
||||||
|
NO_RESPONSE = $ff ;; indicates a command did not receive a response (yet)
|
||||||
|
ACK = $fa ;; successful transmission
|
||||||
|
RESEND = $fe ;; unsuccessful transmission
|
||||||
|
|
||||||
|
|
||||||
|
.enum STATUS
|
||||||
|
RECEIVE_KEYS = %10000000 ;;< keyboard sends scancodes
|
||||||
|
RECEIVE_ANSWER = %01000000 ;;< keyboard replies to a command
|
||||||
|
SEND_CMD = %00100000 ;;< host sends/sent the command byte
|
||||||
|
SEND_DATA = %00010000 ;;< host sends/sent the data byte
|
||||||
|
SEND_RECV = %00001000 ;;< keyboard sends additional data byte
|
||||||
|
SEND = SEND_CMD | SEND_DATA | SEND_RECV ;;< host is sending something, only used for checking status
|
||||||
|
NONE = %00000000
|
||||||
|
.endenum
|
||||||
|
|
||||||
|
.enum TYPEMATIC
|
||||||
|
REPEAT_MASK = %00011111 ;;< 00000 = 30Hz, ..., 11111 = 2Hz
|
||||||
|
DELAY250 = %00000000
|
||||||
|
DELAY500 = %00100000
|
||||||
|
DELAY750 = %01000000
|
||||||
|
DELAY1000 = %01100000
|
||||||
|
.endenum
|
||||||
|
.endscope
|
||||||
|
|
||||||
|
; see https://wiki.osdev.org/PS/2_Keyboard
|
||||||
|
.macro ps2kb_CmdEcho
|
||||||
|
lda #$ee
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdSetLeds mask
|
||||||
|
lda #$ed
|
||||||
|
.ifnblank mask
|
||||||
|
ldx #mask
|
||||||
|
.endif
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdGetScancodeSet
|
||||||
|
lda #$f0
|
||||||
|
ldx #0
|
||||||
|
ldy #1
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdSetScancodeSet set
|
||||||
|
.assert set = 1 .or set = 2 .or set = 3, warning, "ps2kb_CmdSetScancodeSet: invalid scancode set"
|
||||||
|
lda #$f0
|
||||||
|
ldx #set
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdIdentify
|
||||||
|
lda #$f2
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
ldy #2
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdTypematicSettings settings
|
||||||
|
lda #$f3
|
||||||
|
ldx #settings
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdEnable
|
||||||
|
lda #$f4
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdDisable
|
||||||
|
lda #$f5
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
ldy #0
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdSetTypematic scancode
|
||||||
|
ldy #0
|
||||||
|
.ifblank scancode
|
||||||
|
lda #$f7
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
.else
|
||||||
|
lda #$fb
|
||||||
|
ldx #scancode
|
||||||
|
.endif
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdSetMakeRelease scancode
|
||||||
|
ldy #0
|
||||||
|
.ifblank scancode
|
||||||
|
lda #$f8
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
.else
|
||||||
|
lda #$fc
|
||||||
|
ldx #scancode
|
||||||
|
.endif
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdSetMake scancode
|
||||||
|
ldy #0
|
||||||
|
.ifblank scancode
|
||||||
|
lda #$f9
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
.else
|
||||||
|
lda #$fd
|
||||||
|
ldx #scancode
|
||||||
|
.endif
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_CmdSelfTest
|
||||||
|
lda #$ff
|
||||||
|
ldx #ps2kb::NO_DATA
|
||||||
|
ldy #1
|
||||||
|
jsr ps2kb::send_command
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro ps2kb_WaitFinishCmd
|
||||||
|
:
|
||||||
|
wai
|
||||||
|
lda ps2kb::status
|
||||||
|
and #ps2kb::STATUS::SEND
|
||||||
|
bne :-
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @note You need to manually include `string.h65` when using this macro!
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro ps2kb_PrintCmdFailed
|
||||||
|
Printf ps2kb::FMT_CMD_FAIL,ps2kb::send_cmd,ps2kb::send_data,ps2kb::cmd_response,ps2kb::cmd_response+1,ps2kb::cmd_response+2
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
.endif ; guard
|
482
system/ps2_keyboard.s65
Normal file
482
system/ps2_keyboard.s65
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
.include "ps2_keyboard.h65"
|
||||||
|
.include "string.h65"
|
||||||
|
.include "lcd.h65"
|
||||||
|
.include "parity.h65"
|
||||||
|
Export ps2kb, init, begin_receive, scancode, status, scancode_handler
|
||||||
|
Export ps2kb, _receive_irq_shift_reg_handler, _receive_irq_timer_handler, _send_byte, _send_irq_shift_reg_handler, _send_irq_timer_handler,
|
||||||
|
Export ps2kb, send_command, send_cmd, send_data, cmd_response, response_length, FMT_CMD_FAIL
|
||||||
|
|
||||||
|
.bss
|
||||||
|
status: .res 1 ;; current status
|
||||||
|
prev_status: .res 1 ;; status before sending command
|
||||||
|
send_last_bits: .res 1 ;; last bits to load after 8 bits were shifted out
|
||||||
|
send_cmd: .res 1 ;; command to send/last sent
|
||||||
|
send_data: .res 1 ;; data byte to send/last sent or NO_DATA
|
||||||
|
expect_data_length: .res 1 ;; number of data bytes to expect from keyboard
|
||||||
|
response_length: .res 1 ;; number of response bytes from keyboard
|
||||||
|
cmd_response: .res 3 ;; responses from keyboard
|
||||||
|
key_read: .res 2 ;; first 8 bits, last 3 bits from scancode
|
||||||
|
scancode: .res 1 ;; last received scancode
|
||||||
|
scancode_handler: .res 2 ;; pointer to a function that handles new scancodes
|
||||||
|
|
||||||
|
.code
|
||||||
|
|
||||||
|
; spi-transferred code will be placed here
|
||||||
|
; 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
|
||||||
|
;; @details
|
||||||
|
;; Stop pulling the keyboards clock low
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro _EnableClock
|
||||||
|
; set pin to input
|
||||||
|
lda ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
and #<~ps2kb::PULL_MASK_CLK
|
||||||
|
sta ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Disable the clock signal from the keyboard
|
||||||
|
;; @modifies A
|
||||||
|
;; @details
|
||||||
|
;; Pulls the keyboards clock low
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro _DisableClock
|
||||||
|
; set pin to output
|
||||||
|
lda ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
ora #ps2kb::PULL_MASK_CLK
|
||||||
|
sta ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
; set pin low
|
||||||
|
lda ps2kb::VIA + ps2kb::PULL_REG
|
||||||
|
and #<~ps2kb::PULL_MASK_CLK
|
||||||
|
sta ps2kb::VIA + ps2kb::PULL_REG
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Stop pulling the keyboard data pin low
|
||||||
|
;; @modifies A
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro _StopPullDataLow
|
||||||
|
; set pin to input
|
||||||
|
lda ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
and #<~ps2kb::PULL_MASK_DAT
|
||||||
|
sta ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Pull the keyboard data pin low
|
||||||
|
;; @modifies A
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro _PullDataLow
|
||||||
|
; set pin to output
|
||||||
|
lda ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
ora #ps2kb::PULL_MASK_DAT
|
||||||
|
sta ps2kb::VIA + ps2kb::PULL_DDR
|
||||||
|
; set pin low
|
||||||
|
lda ps2kb::VIA + ps2kb::PULL_REG
|
||||||
|
and #<~ps2kb::PULL_MASK_DAT
|
||||||
|
sta ps2kb::VIA + ps2kb::PULL_REG
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Initialize the keyboard
|
||||||
|
;; @details
|
||||||
|
;; - @ref _DisableClock "Disable the clock"
|
||||||
|
;; - Initialize variables to 0
|
||||||
|
;; - Clear the VIAs shift register and set T2 to oneshote mode
|
||||||
|
;; - Set scancode_handler to immediately return (dont handle scancodes)
|
||||||
|
;; @modifies A
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc init
|
||||||
|
_DisableClock
|
||||||
|
stz key_read
|
||||||
|
stz key_read+1
|
||||||
|
stz scancode
|
||||||
|
stz status
|
||||||
|
; T2 oneshot mode, clear shift reg
|
||||||
|
lda ps2kb::VIA + IO::ACR
|
||||||
|
and #<~IO::ACR_MASK::SR
|
||||||
|
ora #IO::ACR::T2_IRQ_LOAD
|
||||||
|
sta ps2kb::VIA + IO::ACR
|
||||||
|
; load this rts as scancode_handler
|
||||||
|
StoreDByte @rts, scancode_handler
|
||||||
|
@rts:
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Start receiving data from the keyboard
|
||||||
|
;; @details
|
||||||
|
;; - use the shift register interrupts to read the first 8 bits
|
||||||
|
;; - configure timer for timing the read of the last 3 bits
|
||||||
|
;; @modifies A
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc begin_receive
|
||||||
|
; disable timer interrupts (this might be called while waiting on the last 3 bits)
|
||||||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||||||
|
; (re)set shift register to shift in under external clock on CB1
|
||||||
|
lda ps2kb::VIA + IO::ACR
|
||||||
|
and #<~IO::ACR_MASK::SR
|
||||||
|
sta ps2kb::VIA + IO::ACR ; important, disabling SR resets the count
|
||||||
|
ora #IO::ACR::SR_SIN_PHIE
|
||||||
|
sta ps2kb::VIA + IO::ACR
|
||||||
|
stz key_read
|
||||||
|
stz key_read+1
|
||||||
|
stz scancode
|
||||||
|
|
||||||
|
; the 3 last bits take about 230us, at @1MHz => wait 230 cycles and then the shift register
|
||||||
|
; (this could be shorter since the it takes a few cycles after the interrupt)
|
||||||
|
lda #<ps2kb::TIMER_RECV
|
||||||
|
sta ps2kb::VIA + IO::T2CL
|
||||||
|
|
||||||
|
; set to RECEIVE_KEYS only if RECEIVE_ANSWER is not set
|
||||||
|
bit status
|
||||||
|
bvs @status_set
|
||||||
|
lda #ps2kb::STATUS::RECEIVE_KEYS
|
||||||
|
sta status
|
||||||
|
@status_set:
|
||||||
|
; todo setup irq handlers
|
||||||
|
StoreDByte _receive_irq_shift_reg_handler, $3000
|
||||||
|
StoreDByte _receive_irq_timer_handler, $3060
|
||||||
|
|
||||||
|
_EnableClock
|
||||||
|
|
||||||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
|
||||||
|
; reset and start SR
|
||||||
|
stz ps2kb::VIA + IO::SR
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Read the first 8 bits an
|
||||||
|
;; @modifies A
|
||||||
|
;; @details
|
||||||
|
;; - read shift register
|
||||||
|
;; - disable shift register interrupts
|
||||||
|
;; - reset shift register
|
||||||
|
;; - enable timer 2 interrupts
|
||||||
|
;; - start timer 2
|
||||||
|
;; IO::SR has to be read before the next bit is shifted in, which happens ~75us after the irq
|
||||||
|
;; at 1MHz, handling this interrupt takes about 50us (without any additional debug code),
|
||||||
|
;; so it should work
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _receive_irq_shift_reg_handler
|
||||||
|
lda ps2kb::VIA + IO::SR
|
||||||
|
sta key_read
|
||||||
|
stz ps2kb::VIA + IO::SR
|
||||||
|
|
||||||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::SR
|
||||||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::T2
|
||||||
|
; start timer, low order count already in latch after begin_receive
|
||||||
|
lda #>ps2kb::TIMER_RECV
|
||||||
|
sta ps2kb::VIA + IO::T2CH
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Read the last 3 bits after after timer 2 is up
|
||||||
|
;; @modifies A
|
||||||
|
;; @details
|
||||||
|
;; - read shift register
|
||||||
|
;; - disable timer 2 interrupts
|
||||||
|
;; - enable shift register interrupts
|
||||||
|
;; - reset shift register
|
||||||
|
;; - jump to the subroutine pointed to by scancode_handler
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _receive_irq_timer_handler
|
||||||
|
lda ps2kb::VIA + IO::SR
|
||||||
|
sta key_read+1
|
||||||
|
|
||||||
|
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
|
||||||
|
|
||||||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||||||
|
; reset SR
|
||||||
|
; disabling shifting in acr seems necessary to reset - otherwise
|
||||||
|
; it continues counting and interrupts after the first 5 bits of the next keypress
|
||||||
|
lda #(IO::ACR::SR_SIN_PHIE | IO::ACR::T2_IRQ_LOAD)
|
||||||
|
trb ps2kb::VIA + IO::ACR
|
||||||
|
tsb ps2kb::VIA + IO::ACR
|
||||||
|
stz ps2kb::VIA + IO::SR
|
||||||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
|
||||||
|
|
||||||
|
lda key_read+1
|
||||||
|
ror ; stop bit -> C
|
||||||
|
sta key_read+1 ; parity in bit 0, D7 in bit 1
|
||||||
|
ror ; parity -> C
|
||||||
|
ror ; D7 -> C
|
||||||
|
lda key_read ; not affecting carry
|
||||||
|
rol ; C -> bit 0, startbit -> C
|
||||||
|
Reverse A
|
||||||
|
sta scancode
|
||||||
|
|
||||||
|
; check parity
|
||||||
|
CalculateOddParity
|
||||||
|
eor key_read+1 ; if bit 0 is 1 - parity error
|
||||||
|
and #1 ; bit 1 is still D7
|
||||||
|
bne @parity_error
|
||||||
|
@handle_scancode:
|
||||||
|
|
||||||
|
; check what to do with the scancode
|
||||||
|
bit status
|
||||||
|
bpl @status_not_receive_keys ; RECEIVE_KEYS
|
||||||
|
jmp (scancode_handler)
|
||||||
|
@status_not_receive_keys:
|
||||||
|
bvc @status_dont_handle ; RECEIVE_ANSWER
|
||||||
|
jmp _process_cmd_answer
|
||||||
|
@status_dont_handle:
|
||||||
|
rts
|
||||||
|
@parity_error: ; TODO handle somehow
|
||||||
|
lda #$fe ; fe means error/resend for commands
|
||||||
|
sta scancode
|
||||||
|
DEBUG_LED_ON 2
|
||||||
|
bra @handle_scancode
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Send a command to the keyboard
|
||||||
|
;; @details
|
||||||
|
;; No checks are done on the validity of the command and data byte.
|
||||||
|
;; You can send anything you want to the keyboard.
|
||||||
|
;; `send_command` will return immediately, as sending the command is asynchronous.
|
||||||
|
;; The command is done if `ps2kb::status & ps2kb::STATUS::SEND == 0`.
|
||||||
|
;; @section Response
|
||||||
|
;; The answers will be in the byte array ps2kb::cmd_response.
|
||||||
|
;; If the command did not have a data byte, cmd_response is:
|
||||||
|
;; - 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)
|
||||||
|
;; - 2: NO_RESPONSE or additional response byte
|
||||||
|
;;
|
||||||
|
;; If ANY of the bytes in cmd_response is 0xFE, the command failed.
|
||||||
|
;;
|
||||||
|
;; @modifies A,X,Y
|
||||||
|
;; @param A: The command byte
|
||||||
|
;; @param X: The data byte or NO_DATA if only the command byte should be sent
|
||||||
|
;; @param Y: The number of data bytes expected to receive. Must be one of {0, 1, 2}
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc send_command
|
||||||
|
sty expect_data_length
|
||||||
|
stx send_data
|
||||||
|
stz response_length
|
||||||
|
ldy #ps2kb::NO_RESPONSE
|
||||||
|
sty cmd_response
|
||||||
|
sty cmd_response+1
|
||||||
|
sty cmd_response+2
|
||||||
|
ldy status
|
||||||
|
sty prev_status
|
||||||
|
ldy #ps2kb::STATUS::SEND_CMD
|
||||||
|
sty status
|
||||||
|
sta send_cmd ; store if it needs to be resent
|
||||||
|
jmp _send_byte
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Send a byte to the keyboard
|
||||||
|
;; @modifies A,X,Y
|
||||||
|
;; @param A: The byte to send
|
||||||
|
;; @details
|
||||||
|
;; - pull clock low to stop keyboard transmissions
|
||||||
|
;; - setup SR to shift out
|
||||||
|
;; - pull data low (by shifting out a zero)
|
||||||
|
;; - reset SR
|
||||||
|
;; - put (reversed) byte in SR
|
||||||
|
;; - put parity + stopbit(s) in send_last_bits variable
|
||||||
|
;; - setup interrupt handlers
|
||||||
|
;; - enable clock
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _send_byte
|
||||||
|
pha
|
||||||
|
IO_DisableIRQ ps2kb::VIA, (IO::IRQ::T2 | IO::IRQ::SR)
|
||||||
|
|
||||||
|
_DisableClock
|
||||||
|
|
||||||
|
; set shift register to output
|
||||||
|
; shift out the startbit = data low
|
||||||
|
; set SR to shift out with PHI2 and shift out zeros
|
||||||
|
lda ps2kb::VIA + IO::ACR
|
||||||
|
and #<~IO::ACR_MASK::SR
|
||||||
|
ora #IO::ACR::SR_SOUT_PHI2
|
||||||
|
sta ps2kb::VIA + IO::ACR
|
||||||
|
stz ps2kb::VIA + IO::SR
|
||||||
|
|
||||||
|
; reset shift register to start the count at 0 again
|
||||||
|
and #<~IO::ACR_MASK::SR
|
||||||
|
sta ps2kb::VIA + IO::ACR
|
||||||
|
; set SR to shift out with external clock
|
||||||
|
ora #IO::ACR::SR_SOUT_PHIE
|
||||||
|
sta ps2kb::VIA + IO::ACR
|
||||||
|
|
||||||
|
; setup the low timer byte
|
||||||
|
lda #<ps2kb::TIMER_SEND
|
||||||
|
sta ps2kb::VIA + IO::T2CL
|
||||||
|
|
||||||
|
; setup interrupt handlers
|
||||||
|
StoreDByte _send_irq_shift_reg_handler, $3000
|
||||||
|
StoreDByte _send_irq_timer_handler, $3060
|
||||||
|
pla
|
||||||
|
Reverse A
|
||||||
|
pha
|
||||||
|
CalculateOddParity
|
||||||
|
ror ; Parity -> C
|
||||||
|
lda #$ff
|
||||||
|
ror ; Parity -> bit 7, rest = 1
|
||||||
|
sta send_last_bits ; loaded into SR by irq handler
|
||||||
|
pla
|
||||||
|
sta ps2kb::VIA + IO::SR
|
||||||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::SR
|
||||||
|
_EnableClock
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Send the lasts 3 bits
|
||||||
|
;; @modifies A
|
||||||
|
;; @details
|
||||||
|
;; - load the remaining 3 bits of the transmission into the shift register
|
||||||
|
;; - disable shift register interrupts
|
||||||
|
;; - enable timer 2 interrupts
|
||||||
|
;; - start timer 2
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _send_irq_shift_reg_handler
|
||||||
|
lda send_last_bits
|
||||||
|
sta ps2kb::VIA + IO::SR
|
||||||
|
|
||||||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::SR
|
||||||
|
IO_EnableIRQ ps2kb::VIA, IO::IRQ::T2
|
||||||
|
; start timer, low order count already in latch after init
|
||||||
|
lda #>ps2kb::TIMER_SEND
|
||||||
|
sta ps2kb::VIA + IO::T2CH
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Setup VIA to receive the keyboard's answer
|
||||||
|
;; @modifies A
|
||||||
|
;; @details
|
||||||
|
;; - disable timer 2 interrupts
|
||||||
|
;; - pull clock low
|
||||||
|
;; - or status with RECEIVE_ANSWER
|
||||||
|
;; - begin receive
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _send_irq_timer_handler
|
||||||
|
lda ps2kb::VIA + IO::T2CL ; clear interrupt flag
|
||||||
|
IO_DisableIRQ ps2kb::VIA, IO::IRQ::T2
|
||||||
|
|
||||||
|
; disable shift register
|
||||||
|
lda ps2kb::VIA + IO::ACR
|
||||||
|
and #<~IO::ACR_MASK::SR
|
||||||
|
sta ps2kb::VIA + IO::ACR
|
||||||
|
|
||||||
|
_DisableClock ; disable keyboard while setting up receive
|
||||||
|
lda status
|
||||||
|
ora #ps2kb::STATUS::RECEIVE_ANSWER
|
||||||
|
sta status
|
||||||
|
jmp ps2kb::begin_receive
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Process the response of a command
|
||||||
|
;; @details
|
||||||
|
;; Stores the answer in the `ps2kb::cmd_response` array.
|
||||||
|
;; If the response is $FE or command related transmissions are done (see below),
|
||||||
|
;; no further data will be sent or received and the `ps2kb::status` takes the previous value.
|
||||||
|
;;
|
||||||
|
;; - store response
|
||||||
|
;; - if response == 0xFE -> stop
|
||||||
|
;; - if status == SEND_CMD -> send databyte
|
||||||
|
;; - if statusk
|
||||||
|
;; You can send anything you want to the keyboard.
|
||||||
|
;; `send_command` will return immediately, as sending the command is asynchronous.
|
||||||
|
;; The command is done if `ps2kb::status & ps2kb::STATUS::SEND == 0`.
|
||||||
|
;; @section Response
|
||||||
|
;; The answers will be in the byte array ps2kb::cmd_response.
|
||||||
|
;; If the command did not have a data byte, cmd_response is:
|
||||||
|
;; - 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)
|
||||||
|
;; - 2: NO_RESPONSE or additional response byte
|
||||||
|
;;
|
||||||
|
;; If ANY of the bytes in cmd_response is 0xFE, the command failed.
|
||||||
|
;;
|
||||||
|
;; @modifies A,X,Y
|
||||||
|
;; @param A: The command byte
|
||||||
|
;; @param X: The data byte or NO_DATA if only the command byte should be sent
|
||||||
|
;; @param Y: The number of data bytes expected to receive. Must be one of {0, 1, 2}
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc _process_cmd_answer
|
||||||
|
@store_response:
|
||||||
|
ldx response_length
|
||||||
|
lda scancode
|
||||||
|
sta cmd_response,x
|
||||||
|
inx
|
||||||
|
stx response_length
|
||||||
|
stz scancode
|
||||||
|
; check for resend
|
||||||
|
cmp #$fe
|
||||||
|
beq @cmd_fail
|
||||||
|
; received something useful, check state
|
||||||
|
lda status
|
||||||
|
bit #ps2kb::STATUS::SEND_CMD
|
||||||
|
bne @cmd_sent
|
||||||
|
bit #ps2kb::STATUS::SEND_DATA
|
||||||
|
bne @everything_sent
|
||||||
|
; status must be SEND_RECV
|
||||||
|
|
||||||
|
@receive_data_response:
|
||||||
|
dec expect_data_length
|
||||||
|
bmi @cmd_done ; no more expected data
|
||||||
|
rts ; wait for another data byte
|
||||||
|
|
||||||
|
@cmd_sent:
|
||||||
|
; check if a data byte needs to be sent
|
||||||
|
lda send_data
|
||||||
|
beq @send_data ; if 0 (would fall through the cmp check TODO would it??)
|
||||||
|
cmp #ps2kb::NO_DATA
|
||||||
|
beq @everything_sent ; if not NO_DATA
|
||||||
|
@send_data:
|
||||||
|
lda #ps2kb::STATUS::SEND_DATA
|
||||||
|
sta status
|
||||||
|
lda send_data
|
||||||
|
jmp ps2kb::_send_byte ; send the data
|
||||||
|
|
||||||
|
@everything_sent: ; check if additonal bytes are expected
|
||||||
|
dec expect_data_length
|
||||||
|
bmi @cmd_done ; expect_data_length was 0
|
||||||
|
lda #(ps2kb::STATUS::SEND_RECV | ps2kb::STATUS::RECEIVE_ANSWER)
|
||||||
|
sta status
|
||||||
|
rts
|
||||||
|
|
||||||
|
@cmd_fail: ; keyboard wont expect data byte/send an additional response byte
|
||||||
|
@cmd_done:
|
||||||
|
; restore previous status
|
||||||
|
lda prev_status
|
||||||
|
sta status
|
||||||
|
; disable the clock if the previous was not RECEIVE_KEYS
|
||||||
|
cmp #ps2kb::STATUS::RECEIVE_KEYS
|
||||||
|
beq @rts
|
||||||
|
_DisableClock
|
||||||
|
@rts:
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
.rodata
|
||||||
|
;; Format string that can be used by other programs for keyboard command errors
|
||||||
|
FMT_CMD_FAIL: .asciiz "PS/2 Cmd fail: %x-%x > %x%x%x "
|
@ -1,18 +1,57 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module SPI
|
;; @file
|
||||||
; @type driver
|
;; @brief SPI Perpiheral
|
||||||
; @details
|
;; @details
|
||||||
; @depends IO-W65C22N
|
;; Support being a peripheral SPI device.
|
||||||
;********************************************************************************
|
;; @depends IO-W65C22N
|
||||||
|
;;********************************************************************************
|
||||||
.ifndef INCLUDE_SPI
|
.ifndef INCLUDE_SPI
|
||||||
INCLUDE_SPI = 1
|
INCLUDE_SPI = 1
|
||||||
|
|
||||||
.include "system/system.h65"
|
.include "system/system.h65"
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Support being a peripheral SPI device
|
||||||
|
;; @details
|
||||||
|
;; This requires the data line to be hooked up to `CB2` and the clock to `CB1`.
|
||||||
|
;; The VIA will be set to shift in the data into the shift register under the external clock.
|
||||||
|
;; @ingroup drivers
|
||||||
|
;; @see via_hardware_bug "VIA external clock bug"
|
||||||
|
;;********************************************************************************
|
||||||
.scope spi_p
|
.scope spi_p
|
||||||
|
|
||||||
|
;; Address of the VIA that is used for the spi connection
|
||||||
SPI_IO = IO2
|
SPI_IO = IO2
|
||||||
.import CODE_START:absolute
|
Import spi_p, init, irq_handler, status, buffer_size, recv_size
|
||||||
Import spi_p, begin, end, read, pages_written, bytes_written
|
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
|
||||||
|
XFER = %00000001
|
||||||
|
DONE = %00000000
|
||||||
|
ERROR = %00100000
|
||||||
|
.endenum
|
||||||
|
|
||||||
.endscope
|
.endscope
|
||||||
.endif ; guard
|
.endif ; guard
|
||||||
|
|
||||||
|
; .struct SPI_P_Pins
|
||||||
|
; ; VIA addresses
|
||||||
|
; DDR_a .word ; address of the data direction register
|
||||||
|
; R_a .word ; address of the register
|
||||||
|
; ; pin mask
|
||||||
|
; SCLK_p .byte ; Serial Clock
|
||||||
|
; POCI_p .byte ; Peripheral Out / Controller In
|
||||||
|
; PICO_p .byte ; Peripheral In / Controller Out
|
||||||
|
; CSB_p .byte ; Chip Select
|
||||||
|
; ; settings
|
||||||
|
; CPOL .byte ; Clock Polarity
|
||||||
|
; CPHA .byte ; Clock Phase
|
||||||
|
; .endstruct
|
||||||
|
292
system/spi.s65
292
system/spi.s65
@ -1,114 +1,256 @@
|
|||||||
.include "spi.h65"
|
.include "spi.h65"
|
||||||
|
.include "system.h65"
|
||||||
|
|
||||||
Export spi_p, begin, end, read, pages_written, bytes_written
|
Export spi_p, init, irq_handler, status, buffer_size, recv_size
|
||||||
|
ExportZp spi_p, buffer_ptr
|
||||||
|
Export spi_p, begin_read, end_read, irq_read_byte, recv_bytes
|
||||||
|
Export spi_p, begin_write, end_write, irq_write_byte, sent_bytes
|
||||||
|
|
||||||
.zeropage
|
.zeropage
|
||||||
code_page: .res 2 ; CODE_START + pages_written * 256
|
buffer_ptr: .res 2
|
||||||
.bss
|
.bss
|
||||||
bytes_written: .res 1
|
recv_bytes:
|
||||||
pages_written: .res 1
|
sent_bytes: .res 2
|
||||||
|
status: .res 1
|
||||||
|
buffer_size: .res 2
|
||||||
|
recv_size:
|
||||||
|
send_size: .res 2
|
||||||
|
irq_handler: .res 2
|
||||||
|
|
||||||
|
|
||||||
SPI_IO := spi_p::SPI_IO
|
SPI_IO := spi_p::SPI_IO
|
||||||
CODE_START := spi_p::CODE_START
|
|
||||||
|
|
||||||
; spi-transferred code will be placed here
|
; spi-transferred code will be placed here
|
||||||
; SPI_P = as peripheral
|
; SPI_P = as peripheral
|
||||||
.code
|
.code
|
||||||
.struct SPI_P_Pins
|
|
||||||
; VIA addresses
|
|
||||||
DDR_a .word ; address of the data direction register
|
|
||||||
R_a .word ; address of the register
|
|
||||||
; pin mask
|
|
||||||
SCLK_p .byte ; Serial Clock
|
|
||||||
POCI_p .byte ; Peripheral Out / Controller In
|
|
||||||
PICO_p .byte ; Peripheral In / Controller Out
|
|
||||||
CSB_p .byte ; Chip Select
|
|
||||||
; settings
|
|
||||||
CPOL .byte ; Clock Polarity
|
|
||||||
CPHA .byte ; Clock Phase
|
|
||||||
.endstruct
|
|
||||||
|
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Begin listening for SPI transfers
|
;; @function Begin listening for SPI transfers
|
||||||
; @details
|
;;********************************************************************************
|
||||||
; - initialize variables
|
.proc init
|
||||||
; - configure shift register to shift in under external clock
|
lda #spi_p::STATUS::DONE
|
||||||
; - enable shift register interrupts
|
sta status
|
||||||
;********************************************************************************
|
stz recv_bytes
|
||||||
.proc begin
|
stz recv_bytes+1
|
||||||
stz pages_written
|
stz buffer_size
|
||||||
stz bytes_written
|
stz buffer_size+1
|
||||||
|
stz recv_size
|
||||||
|
stz recv_size+1
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Begin listening for SPI transfers
|
||||||
|
;; @details
|
||||||
|
;; The transfer must be: <SIZE, >SIZE, data (SIZE bytes)
|
||||||
|
;;
|
||||||
|
;; - initialize variables
|
||||||
|
;; - configure shift register to shift in under external clock
|
||||||
|
;; - enable shift register interrupts
|
||||||
|
;; @param ARG0-1: Start address of the buffer
|
||||||
|
;; @param ARG2-3: Size of the buffer
|
||||||
|
;; @todo: Use irq register handler
|
||||||
|
;; @note: The buffer must be large enough for the received data.
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc begin_read
|
||||||
|
stz recv_bytes
|
||||||
|
stz recv_bytes+1
|
||||||
|
stz recv_size
|
||||||
|
stz recv_size+1
|
||||||
|
lda #spi_p::STATUS::XFER_SIZEL
|
||||||
|
sta status
|
||||||
|
|
||||||
; store address in zp
|
; store address in zp
|
||||||
lda #<CODE_START
|
lda ARG0
|
||||||
sta code_page
|
sta buffer_ptr
|
||||||
lda #>CODE_START
|
lda ARG1
|
||||||
sta code_page + 1
|
sta buffer_ptr+1
|
||||||
; todo USE MASKS
|
|
||||||
; set Shift register to shift in under external clock on CB1
|
|
||||||
lda #IO::ACR::SR_SIN_PHIE
|
|
||||||
sta SPI_IO + IO::ACR
|
|
||||||
|
|
||||||
|
; load irq handler
|
||||||
|
lda #<irq_read_byte
|
||||||
|
sta irq_handler
|
||||||
|
lda #>irq_read_byte
|
||||||
|
sta irq_handler+1
|
||||||
|
|
||||||
|
; temporarily store the low byte in buffer, will be checked later
|
||||||
|
lda ARG2
|
||||||
|
sta buffer_size
|
||||||
|
lda ARG3
|
||||||
|
sta buffer_size+1
|
||||||
|
; set Shift register to shift in under external clock on CB1
|
||||||
|
MaskedWrite SPI_IO+IO::ACR, #IO::ACR::SR_SIN_PHIE, #IO::ACR_MASK::SR
|
||||||
; enable SR interrupts
|
; enable SR interrupts
|
||||||
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
|
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
|
||||||
sta SPI_IO + IO::IER
|
sta SPI_IO + IO::IER
|
||||||
|
|
||||||
; load SR to reset
|
; load SR to reset
|
||||||
lda SPI_IO + IO::SR
|
lda SPI_IO + IO::SR
|
||||||
rts
|
rts
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Stop listening for SPI transfers
|
;; @function Stop reading/writing from/to SPI
|
||||||
; @details
|
;; @details
|
||||||
; Disables shift register interrupts
|
;; Disables shift register interrupts
|
||||||
; @modifies A
|
;; @modifies A
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.proc end
|
end_write:
|
||||||
|
.proc end_read
|
||||||
|
; disable SR interrupts
|
||||||
|
lda #IO::IRQ::SR
|
||||||
|
sta SPI_IO + IO::IER
|
||||||
|
lda #spi_p::STATUS::DONE
|
||||||
|
sta status
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Read a byte from SPI
|
||||||
|
;; @details
|
||||||
|
;; Reads a byte from the shift register and stores it in the SPI code buffer
|
||||||
|
;; The first two bytes must be the size of the incumong data (LE).
|
||||||
|
;; The spi_p:status is updated according to the current stage of the transfer.
|
||||||
|
;; If the buffer size given in begin_read is too small too fit the incoming data,
|
||||||
|
;; the status is set to ERROR and spi (shift register) interrupts are disabled before
|
||||||
|
;; the first real byte is read.
|
||||||
|
;; @modifies A,Y
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc irq_read_byte
|
||||||
|
lda SPI_IO + IO::SR
|
||||||
|
; Printf "%x",
|
||||||
|
bit status
|
||||||
|
bmi @read_size1 ; bit7 set => XFER_SIZEL
|
||||||
|
bvs @read_size2 ; bit6 set => XFER_SIZEH
|
||||||
|
ldy recv_bytes
|
||||||
|
sta (buffer_ptr),y
|
||||||
|
inc recv_bytes
|
||||||
|
beq @new_page
|
||||||
|
rts
|
||||||
|
@new_page: ; increment high bytes
|
||||||
|
inc recv_bytes+1
|
||||||
|
inc buffer_ptr+1
|
||||||
|
rts
|
||||||
|
|
||||||
|
@read_size1:
|
||||||
|
sta recv_size
|
||||||
|
lda #spi_p::STATUS::XFER_SIZEH
|
||||||
|
sta status
|
||||||
|
rts
|
||||||
|
|
||||||
|
@read_size2:
|
||||||
|
sta recv_size+1
|
||||||
|
; check if the buffer is large enough
|
||||||
|
cmp buffer_size+1
|
||||||
|
beq @hieq
|
||||||
|
bcc @enough ; recv_size+1 < buffer_size+1
|
||||||
|
bra @not_enough
|
||||||
|
@hieq: ; high bytes are equal, check lo
|
||||||
|
lda buffer_size
|
||||||
|
cmp recv_size
|
||||||
|
bcs @enough
|
||||||
|
@not_enough:
|
||||||
|
lda #spi_p::STATUS::ERROR
|
||||||
|
sta status
|
||||||
; disable SR interrupts
|
; disable SR interrupts
|
||||||
lda #IO::IRQ::SR
|
lda #IO::IRQ::SR
|
||||||
sta SPI_IO + IO::IER
|
sta SPI_IO + IO::IER
|
||||||
rts
|
rts
|
||||||
|
@enough:
|
||||||
|
lda #spi_p::STATUS::XFER
|
||||||
|
sta status
|
||||||
|
rts
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Begin writing to spi
|
||||||
|
;; @details
|
||||||
|
;; - initialize variables
|
||||||
|
;; - configure shift register to shift in under external clock
|
||||||
|
;; - enable shift register interrupts
|
||||||
|
;; @param ARG0-1: Start address of the buffer
|
||||||
|
;; @param ARG2-3: Size of the buffer
|
||||||
|
;; @todo: Use irq register handler
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc begin_write
|
||||||
|
stz recv_bytes
|
||||||
|
stz recv_bytes+1
|
||||||
|
; store address in zp
|
||||||
|
lda #<ARG0
|
||||||
|
sta buffer_ptr
|
||||||
|
lda #>ARG1
|
||||||
|
sta buffer_ptr+1
|
||||||
|
; store size
|
||||||
|
lda #<ARG2
|
||||||
|
sta buffer_size
|
||||||
|
lda #>ARG3
|
||||||
|
sta buffer_size+1
|
||||||
|
; load irq handler
|
||||||
|
lda #<irq_read_byte
|
||||||
|
sta irq_handler
|
||||||
|
lda #>irq_read_byte
|
||||||
|
sta irq_handler+1
|
||||||
|
; set Shift register to shift out under external clock on CB1
|
||||||
|
MaskedWrite SPI_IO+IO::ACR, #IO::ACR::SR_SOUT_PHIE, #IO::ACR_MASK::SR
|
||||||
|
; enable SR interrupts
|
||||||
|
lda #(IO::IRQ::IRQ | IO::IRQ::SR)
|
||||||
|
sta SPI_IO + IO::IER
|
||||||
|
; write the first byte
|
||||||
|
@write_size1:
|
||||||
|
lda buffer_size
|
||||||
|
sta SPI_IO+IO::SR
|
||||||
|
lda #spi_p::STATUS::XFER_SIZEH
|
||||||
|
sta status
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
;********************************************************************************
|
;********************************************************************************
|
||||||
; @function Read a byte from SPI
|
; @function Write a byte
|
||||||
; @details
|
; @details
|
||||||
; Reads a byte from the shift register and stores it in the SPI code buffer
|
; Write a byte to the spi shift reister
|
||||||
; @modifies A,Y
|
; @modifies A,Y
|
||||||
;********************************************************************************
|
;********************************************************************************
|
||||||
.proc read
|
;.proc irq_write_byte
|
||||||
ldy bytes_written
|
; ldy sent_bytes
|
||||||
lda SPI_IO + IO::SR
|
; lda (buffer_ptr),y
|
||||||
sta (code_page),y
|
; sta SPI_IO + IO::SR
|
||||||
inc bytes_written
|
; inc sent_bytes
|
||||||
|
; beq @new_page
|
||||||
|
; rts
|
||||||
|
;@new_page:
|
||||||
|
; inc sent_bytes+1
|
||||||
|
; inc buffer_ptr+1
|
||||||
|
; rts
|
||||||
|
;.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Write a byte to SPI
|
||||||
|
;; @details
|
||||||
|
;; Write a byte from the buffer to the (spi) shift register.
|
||||||
|
;; The first two bytes are the size of the data (LE).
|
||||||
|
;; The spi_p:status is updated according to the current stage of the transfer.
|
||||||
|
;; @modifies A,Y
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc irq_write_byte
|
||||||
|
bit status
|
||||||
|
bvs @write_size2 ; bit 6 set -> XFER_SIZEH
|
||||||
|
ldy sent_bytes
|
||||||
|
lda (buffer_ptr),y
|
||||||
|
sta SPI_IO + IO::SR
|
||||||
|
inc sent_bytes
|
||||||
beq @new_page
|
beq @new_page
|
||||||
rts
|
rts
|
||||||
@new_page:
|
@new_page:
|
||||||
inc pages_written
|
inc sent_bytes+1
|
||||||
inc code_page + 1
|
inc buffer_ptr+1
|
||||||
|
rts
|
||||||
|
@write_size2:
|
||||||
|
lda buffer_size+1
|
||||||
|
sta SPI_IO+IO::SR
|
||||||
|
lda #spi_p::STATUS::XFER
|
||||||
|
sta status
|
||||||
rts
|
rts
|
||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
;********************************************************************************
|
|
||||||
; @function Initialize the IO Adapter for SPI
|
|
||||||
; @param ARG0-1 Address of the SPI_Pins struct
|
|
||||||
;********************************************************************************
|
|
||||||
;********************************************************************************
|
|
||||||
; @function Read bytes
|
|
||||||
; @param X Number of bytes to send
|
|
||||||
; @param ARG0-1 Address of the SPI_Pins struct
|
|
||||||
; @param ARG2-3 Address of the first byte
|
|
||||||
;********************************************************************************
|
|
||||||
.proc recv_data
|
|
||||||
.endproc
|
|
||||||
;********************************************************************************
|
|
||||||
; @function Send bytes
|
|
||||||
; @param X Number of bytes to send
|
|
||||||
; @param ARG0-1 Address of the SPI_Pins struct
|
|
||||||
; @param ARG2-3 Address of the first byte
|
|
||||||
;********************************************************************************
|
|
||||||
.proc send_data
|
|
||||||
.endproc
|
|
||||||
|
@ -1,50 +1,69 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module system
|
;; @file
|
||||||
; @type header
|
;; @brief System header
|
||||||
; @details
|
;; @ingroup system
|
||||||
; Variable definitions for the current hardware setup
|
;; @details
|
||||||
;********************************************************************************
|
;; Variables for the current hardware setup
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
.ifndef INCLUDE_SYSTEM
|
.ifndef INCLUDE_SYSTEM
|
||||||
INCLUDE_SYSTEM = 1
|
INCLUDE_SYSTEM = 1
|
||||||
; reserved RAM addresses
|
|
||||||
; 00-0f - free
|
;;********************************************************************************
|
||||||
; 10-1f - arguments / return values
|
;; @page system_stuff More System stuff
|
||||||
; 20-ff - free
|
;; @subsubsection ram_reserved Reserved RAM addresses
|
||||||
; 0100 - 01FF Stack
|
;; - 00-0f - free use (Z0-Z15)
|
||||||
; 0200,0201 keybuffer write/read pointer
|
;; - 10-1f - arguments / return values (ARG0-ARG15)
|
||||||
; 0202-02ff keybuffer
|
;; - 20-ff - free
|
||||||
; 0300 lcd character counter
|
;; - 0100 - 01FF Stack
|
||||||
; 0400, 0401, 0402 dht status, dht bit, dht_bit_rot
|
;; - 0200,0201 keypad keybuffer write/read pointer
|
||||||
; 0403 value offset
|
;; - 0202-02ff keypad keybuffer
|
||||||
; 0405-04a0 rh high/low, temp high/low, checksum
|
;; - 0300 lcd character counter
|
||||||
|
;; @ingroup system
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
.include "io_W65C22.h65"
|
.include "io_W65C22.h65"
|
||||||
.include "utility.h65"
|
.include "utility.h65"
|
||||||
|
.importzp Z0,Z1,Z2,Z3,Z4,Z5,Z6,Z7,Z8,Z9,Z10,Z11,Z12,Z13,Z14,Z15
|
||||||
|
.importzp ARG0,ARG1,ARG2,ARG3,ARG4,ARG5,ARG6,ARG7,ARG8,ARG9,ARG10,ARG11,ARG12,ARG13,ARG14,ARG15
|
||||||
|
|
||||||
; ARGUMENTS
|
; Z0 = $0
|
||||||
; a,x,y can also be used
|
; Z1 = $1
|
||||||
ARG0 = $10
|
; Z2 = $2
|
||||||
ARG1 = $11
|
; Z3 = $3
|
||||||
ARG2 = $12
|
; Z4 = $4
|
||||||
ARG3 = $13
|
; Z5 = $5
|
||||||
ARG4 = $14
|
; Z6 = $6
|
||||||
ARG5 = $15
|
; Z7 = $7
|
||||||
ARG6 = $16
|
; Z8 = $8
|
||||||
ARG7 = $17
|
; Z9 = $9
|
||||||
ARG8 = $18
|
; Z10 = $a
|
||||||
ARG9 = $19
|
; Z11 = $b
|
||||||
ARG10 = $1a
|
; Z12 = $c
|
||||||
ARG11 = $1b
|
; Z13 = $d
|
||||||
ARG12 = $1c
|
; Z14 = $e
|
||||||
ARG13 = $1d
|
; Z15 = $f
|
||||||
ARG14 = $1e
|
; ; ARGUMENTS
|
||||||
ARG15 = $1f
|
; ; a,x,y can also be used
|
||||||
|
; ARG0 = $10
|
||||||
|
; ARG1 = $11
|
||||||
|
; ARG2 = $12
|
||||||
|
; ARG3 = $13
|
||||||
|
; ARG4 = $14
|
||||||
|
; ARG5 = $15
|
||||||
|
; ARG6 = $16
|
||||||
|
; ARG7 = $17
|
||||||
|
; ARG8 = $18
|
||||||
|
; ARG9 = $19
|
||||||
|
; ARG10 = $1a
|
||||||
|
; ARG11 = $1b
|
||||||
|
; ARG12 = $1c
|
||||||
|
; ARG13 = $1d
|
||||||
|
; ARG14 = $1e
|
||||||
|
; ARG15 = $1f
|
||||||
|
|
||||||
|
|
||||||
; RETURN VALUE
|
; RETURN VALUE
|
||||||
; in a
|
|
||||||
|
|
||||||
.segment "VIA1"
|
.segment "VIA1"
|
||||||
; IO1: .res 16
|
; IO1: .res 16
|
||||||
IO1 = $6000
|
IO1 = $6000
|
||||||
@ -53,4 +72,13 @@ IO1 = $6000
|
|||||||
; IO2: .res 16
|
; IO2: .res 16
|
||||||
IO2 = $7000
|
IO2 = $7000
|
||||||
|
|
||||||
|
; charmappings for the lcd
|
||||||
|
; .charmap 'ü', $F5
|
||||||
|
; .charmap 'Ü', $F5
|
||||||
|
; .charmap 'ö', $EF
|
||||||
|
; .charmap 'Ö', $EF
|
||||||
|
; .charmap 'ä', $E1
|
||||||
|
; .charmap 'Ä', $E1
|
||||||
|
; .charmap 'ß', $E2
|
||||||
|
|
||||||
.endif ; include guard
|
.endif ; include guard
|
||||||
|
@ -1,3 +1,42 @@
|
|||||||
|
; .include "system.h65"
|
||||||
|
.exportzp Z0,Z1,Z2,Z3,Z4,Z5,Z6,Z7,Z8,Z9,Z10,Z11,Z12,Z13,Z14,Z15
|
||||||
|
.exportzp ARG0,ARG1,ARG2,ARG3,ARG4,ARG5,ARG6,ARG7,ARG8,ARG9,ARG10,ARG11,ARG12,ARG13,ARG14,ARG15
|
||||||
|
|
||||||
.zeropage
|
.zeropage
|
||||||
.org $10
|
.org $0
|
||||||
.res 16
|
.assert * = $0, error, "Code not at 0"
|
||||||
|
Z0: .res 1
|
||||||
|
Z1: .res 1
|
||||||
|
Z2: .res 1
|
||||||
|
Z3: .res 1
|
||||||
|
Z4: .res 1
|
||||||
|
Z5: .res 1
|
||||||
|
Z6: .res 1
|
||||||
|
Z7: .res 1
|
||||||
|
Z8: .res 1
|
||||||
|
Z9: .res 1
|
||||||
|
Z10: .res 1
|
||||||
|
Z11: .res 1
|
||||||
|
Z12: .res 1
|
||||||
|
Z13: .res 1
|
||||||
|
Z14: .res 1
|
||||||
|
Z15: .res 1
|
||||||
|
; ARGUMENTS
|
||||||
|
; a,x,y can also be used
|
||||||
|
.assert * = $10, error, "Code not at 10"
|
||||||
|
ARG0: .res 1
|
||||||
|
ARG1: .res 1
|
||||||
|
ARG2: .res 1
|
||||||
|
ARG3: .res 1
|
||||||
|
ARG4: .res 1
|
||||||
|
ARG5: .res 1
|
||||||
|
ARG6: .res 1
|
||||||
|
ARG7: .res 1
|
||||||
|
ARG8: .res 1
|
||||||
|
ARG9: .res 1
|
||||||
|
ARG10: .res 1
|
||||||
|
ARG11: .res 1
|
||||||
|
ARG12: .res 1
|
||||||
|
ARG13: .res 1
|
||||||
|
ARG14: .res 1
|
||||||
|
ARG15: .res 1
|
||||||
|
55
test.s65
55
test.s65
@ -1,55 +0,0 @@
|
|||||||
;.include "system/system.h65"
|
|
||||||
;.segment "CODE"
|
|
||||||
|
|
||||||
;;********************************************************************************
|
|
||||||
;; Interrupts
|
|
||||||
;;********************************************************************************
|
|
||||||
;nmi:
|
|
||||||
; rti
|
|
||||||
;irq:
|
|
||||||
; .repeat 20
|
|
||||||
; .endrepeat
|
|
||||||
; rti
|
|
||||||
|
|
||||||
;;********************************************************************************
|
|
||||||
;; Reset sequence
|
|
||||||
;;********************************************************************************
|
|
||||||
;reset:
|
|
||||||
; sei
|
|
||||||
; ; setup io2 bank a 1-3
|
|
||||||
; lda #%11111111
|
|
||||||
; sta IO1 + IO_DDRA
|
|
||||||
; sta IO1 + IO_DDRB
|
|
||||||
|
|
||||||
;@loop:
|
|
||||||
; lda #%00000000
|
|
||||||
; sta IO1 + IO_RA
|
|
||||||
; .repeat 3
|
|
||||||
; nop
|
|
||||||
; .endrepeat
|
|
||||||
; lda #%11111111
|
|
||||||
; sta IO1 + IO_RA
|
|
||||||
; .repeat 15
|
|
||||||
; nop
|
|
||||||
; .endrepeat
|
|
||||||
|
|
||||||
; lda #%00000000
|
|
||||||
; sta IO1 + IO_RB
|
|
||||||
; .repeat 5
|
|
||||||
; nop
|
|
||||||
; .endrepeat
|
|
||||||
; lda #%11111111
|
|
||||||
; sta IO1 + IO_RB
|
|
||||||
; .repeat 10
|
|
||||||
; nop
|
|
||||||
; .endrepeat
|
|
||||||
; bra @loop
|
|
||||||
|
|
||||||
;;********************************************************************************
|
|
||||||
;; reset vector
|
|
||||||
;;********************************************************************************
|
|
||||||
;.segment "RESET_VECTOR"
|
|
||||||
; .word nmi
|
|
||||||
; .word reset
|
|
||||||
; .word irq
|
|
||||||
|
|
123
util/bit_macros.h65
Normal file
123
util/bit_macros.h65
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief Macros for bitwise operations
|
||||||
|
;; @ingroup libs
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Use the bbs instruction by providing the bit as a mask
|
||||||
|
;; @details
|
||||||
|
;; This is useful when having a enum with bitmasks, which might change later.
|
||||||
|
;; @param mask: A byte where a single bit is set
|
||||||
|
;; @param addr: The zero page address to test
|
||||||
|
;; @param label: The label to jump to when the bit is set
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro bbs mask,addr,label
|
||||||
|
.if mask = %00000001
|
||||||
|
bbs0 addr,label
|
||||||
|
.elseif mask = %00000010
|
||||||
|
bbs1 addr,label
|
||||||
|
.elseif mask = %00000100
|
||||||
|
bbs2 addr,label
|
||||||
|
.elseif mask = %00001000
|
||||||
|
bbs3 addr,label
|
||||||
|
.elseif mask = %00010000
|
||||||
|
bbs4 addr,label
|
||||||
|
.elseif mask = %00100000
|
||||||
|
bbs5 addr,label
|
||||||
|
.elseif mask = %01000000
|
||||||
|
bbs6 addr,label
|
||||||
|
.elseif mask = %10000000
|
||||||
|
bbs7 addr,label
|
||||||
|
.else
|
||||||
|
.fatal .sprintf("bbs macro got invalid mask: %x", mask)
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Use the bbr instruction by providing the bit as a mask
|
||||||
|
;; @details
|
||||||
|
;; This is useful when having a enum with bitmasks, which might change later.
|
||||||
|
;; @param mask: A byte where a single bit is set
|
||||||
|
;; @param addr: The zero page address to test
|
||||||
|
;; @param label: The label to jump to when the bit is clear
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro bbr mask,addr,label
|
||||||
|
.if mask = %00000001
|
||||||
|
bbr0 addr,label
|
||||||
|
.elseif mask = %00000010
|
||||||
|
bbr1 addr,label
|
||||||
|
.elseif mask = %00000100
|
||||||
|
bbr2 addr,label
|
||||||
|
.elseif mask = %00001000
|
||||||
|
bbr3 addr,label
|
||||||
|
.elseif mask = %00010000
|
||||||
|
bbr4 addr,label
|
||||||
|
.elseif mask = %00100000
|
||||||
|
bbr5 addr,label
|
||||||
|
.elseif mask = %01000000
|
||||||
|
bbr6 addr,label
|
||||||
|
.elseif mask = %10000000
|
||||||
|
bbr7 addr,label
|
||||||
|
.else
|
||||||
|
.fatal .sprintf("bbr macro got invalid mask: 0x%x", mask)
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Use the smb instruction by providing the bit as a mask
|
||||||
|
;; @details
|
||||||
|
;; This is useful when having a enum with bitmasks, which might change later.
|
||||||
|
;; @param mask: A byte where a single bit is set
|
||||||
|
;; @param addr: The zero page address to update
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro smb mask,addr
|
||||||
|
.if mask = %00000001
|
||||||
|
smb0 addr
|
||||||
|
.elseif mask = %00000010
|
||||||
|
smb1 addr
|
||||||
|
.elseif mask = %00000100
|
||||||
|
smb2 addr
|
||||||
|
.elseif mask = %00001000
|
||||||
|
smb3 addr
|
||||||
|
.elseif mask = %00010000
|
||||||
|
smb4 addr
|
||||||
|
.elseif mask = %00100000
|
||||||
|
smb5 addr
|
||||||
|
.elseif mask = %01000000
|
||||||
|
smb6 addr
|
||||||
|
.elseif mask = %10000000
|
||||||
|
smb7 addr
|
||||||
|
.else
|
||||||
|
.fatal .sprintf("smb macro got invalid mask: 0x%x", mask)
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Use the rmb instruction by providing the bit as a mask
|
||||||
|
;; @details
|
||||||
|
;; This is useful when having a enum with bitmasks, which might change later.
|
||||||
|
;; @param mask: A byte where a single bit is set
|
||||||
|
;; @param addr: The zero page address to update
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro rmb mask,addr
|
||||||
|
.if mask = %00000001
|
||||||
|
rmb0 addr
|
||||||
|
.elseif mask = %00000010
|
||||||
|
rmb1 addr
|
||||||
|
.elseif mask = %00000100
|
||||||
|
rmb2 addr
|
||||||
|
.elseif mask = %00001000
|
||||||
|
rmb3 addr
|
||||||
|
.elseif mask = %00010000
|
||||||
|
rmb4 addr
|
||||||
|
.elseif mask = %00100000
|
||||||
|
rmb5 addr
|
||||||
|
.elseif mask = %01000000
|
||||||
|
rmb6 addr
|
||||||
|
.elseif mask = %10000000
|
||||||
|
rmb7 addr
|
||||||
|
.else
|
||||||
|
.fatal .sprintf("rmb macro got invalid mask: 0x%x", mask)
|
||||||
|
.endif
|
||||||
|
.endmacro
|
@ -1,19 +1,21 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module math
|
;; @file
|
||||||
; @type utility
|
;; @brief Math library
|
||||||
; @details
|
;;********************************************************************************
|
||||||
; Math library
|
|
||||||
;********************************************************************************
|
|
||||||
.ifndef INCLUDE_MATH
|
.ifndef INCLUDE_MATH
|
||||||
INCLUDE_MATH = 1
|
INCLUDE_MATH = 1
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Math library
|
||||||
|
;; @ingroup libs
|
||||||
|
;;********************************************************************************
|
||||||
.scope math
|
.scope math
|
||||||
|
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @macro Divide a num (or A) by a factor
|
;; @macro Divide a num (or A) by a factor
|
||||||
; @param num: Memory or A
|
;; @param num: Memory or A
|
||||||
; @param divider: must be power of 2
|
;; @param divider: must be power of 2
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.macro div num,divider
|
.macro div num,divider
|
||||||
.if divider = 2
|
.if divider = 2
|
||||||
lsr num
|
lsr num
|
||||||
|
41
util/parity.h65
Normal file
41
util/parity.h65
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
.ifndef INCLUDE_PARITY
|
||||||
|
INCLUDE_PARITY = 1
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief Macros for calculating parity
|
||||||
|
;; @ingroup libs
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
|
.import count_set_bits
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Calculate even parity
|
||||||
|
;; @modifies A, X, Y
|
||||||
|
;; @returns A: 0 - even number of set bits, 1 - uneven number of set bits
|
||||||
|
;; @returns Y: The number of set bits in the byte
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro CalculateEvenParity
|
||||||
|
jsr count_set_bits
|
||||||
|
tya
|
||||||
|
and #1
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Calculate odd parity
|
||||||
|
;; @details
|
||||||
|
;; Count the number of set bits in Y.
|
||||||
|
;;
|
||||||
|
;; @modifies A, X, Y
|
||||||
|
;; @returns A: 1 - even number of set bits, 0 - uneven number of set bits
|
||||||
|
;; @returns Y: The number of set bits in the byte
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro CalculateOddParity
|
||||||
|
jsr count_set_bits
|
||||||
|
tya
|
||||||
|
and #1
|
||||||
|
eor #1
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.endif ; guard
|
22
util/parity.s65
Normal file
22
util/parity.s65
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.export count_set_bits
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Count the number of set bits in the `A` register
|
||||||
|
;; @modifies A, X, Y
|
||||||
|
;; @param A: Byte to count the set bits in
|
||||||
|
;; @returns Y: The number of set bits in the byte
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc count_set_bits
|
||||||
|
ldx #9
|
||||||
|
ldy #0 ; number of set bits
|
||||||
|
@loop:
|
||||||
|
dex
|
||||||
|
beq @done
|
||||||
|
ror
|
||||||
|
bcc @loop
|
||||||
|
@set:
|
||||||
|
iny
|
||||||
|
bra @loop
|
||||||
|
@done:
|
||||||
|
rts
|
||||||
|
.endproc
|
42
util/reverse.s65
Normal file
42
util/reverse.s65
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
.export REVERSE_TABLE
|
||||||
|
|
||||||
|
.rodata
|
||||||
|
.align 256
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief A 256 byte table where the *i*-th byte is the value *i* with bits reversed
|
||||||
|
;; @see Reverse
|
||||||
|
;;********************************************************************************
|
||||||
|
REVERSE_TABLE:
|
||||||
|
.byte $00, $80, $40, $c0, $20, $a0, $60, $e0
|
||||||
|
.byte $10, $90, $50, $d0, $30, $b0, $70, $f0
|
||||||
|
.byte $08, $88, $48, $c8, $28, $a8, $68, $e8
|
||||||
|
.byte $18, $98, $58, $d8, $38, $b8, $78, $f8
|
||||||
|
.byte $04, $84, $44, $c4, $24, $a4, $64, $e4
|
||||||
|
.byte $14, $94, $54, $d4, $34, $b4, $74, $f4
|
||||||
|
.byte $0c, $8c, $4c, $cc, $2c, $ac, $6c, $ec
|
||||||
|
.byte $1c, $9c, $5c, $dc, $3c, $bc, $7c, $fc
|
||||||
|
.byte $02, $82, $42, $c2, $22, $a2, $62, $e2
|
||||||
|
.byte $12, $92, $52, $d2, $32, $b2, $72, $f2
|
||||||
|
.byte $0a, $8a, $4a, $ca, $2a, $aa, $6a, $ea
|
||||||
|
.byte $1a, $9a, $5a, $da, $3a, $ba, $7a, $fa
|
||||||
|
.byte $06, $86, $46, $c6, $26, $a6, $66, $e6
|
||||||
|
.byte $16, $96, $56, $d6, $36, $b6, $76, $f6
|
||||||
|
.byte $0e, $8e, $4e, $ce, $2e, $ae, $6e, $ee
|
||||||
|
.byte $1e, $9e, $5e, $de, $3e, $be, $7e, $fe
|
||||||
|
.byte $01, $81, $41, $c1, $21, $a1, $61, $e1
|
||||||
|
.byte $11, $91, $51, $d1, $31, $b1, $71, $f1
|
||||||
|
.byte $09, $89, $49, $c9, $29, $a9, $69, $e9
|
||||||
|
.byte $19, $99, $59, $d9, $39, $b9, $79, $f9
|
||||||
|
.byte $05, $85, $45, $c5, $25, $a5, $65, $e5
|
||||||
|
.byte $15, $95, $55, $d5, $35, $b5, $75, $f5
|
||||||
|
.byte $0d, $8d, $4d, $cd, $2d, $ad, $6d, $ed
|
||||||
|
.byte $1d, $9d, $5d, $dd, $3d, $bd, $7d, $fd
|
||||||
|
.byte $03, $83, $43, $c3, $23, $a3, $63, $e3
|
||||||
|
.byte $13, $93, $53, $d3, $33, $b3, $73, $f3
|
||||||
|
.byte $0b, $8b, $4b, $cb, $2b, $ab, $6b, $eb
|
||||||
|
.byte $1b, $9b, $5b, $db, $3b, $bb, $7b, $fb
|
||||||
|
.byte $07, $87, $47, $c7, $27, $a7, $67, $e7
|
||||||
|
.byte $17, $97, $57, $d7, $37, $b7, $77, $f7
|
||||||
|
.byte $0f, $8f, $4f, $cf, $2f, $af, $6f, $ef
|
||||||
|
.byte $1f, $9f, $5f, $df, $3f, $bf, $7f, $ff
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
.include "string.h65"
|
.include "string.h65"
|
||||||
.include "math.h65"
|
.include "math.h65"
|
||||||
Export str,hex_char_to_uint8, hex_str_to_uint, uint8_to_hex_str
|
Export str,hex_char_to_uint8, hex_str_to_uint, uint8_to_hex_str, uint_to_hex_str
|
||||||
|
|
||||||
.code
|
.code
|
||||||
;
|
;
|
||||||
@ -130,6 +130,55 @@ Export str,hex_char_to_uint8, hex_str_to_uint, uint8_to_hex_str
|
|||||||
.endproc
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @function Convert a 1 byte number into two hex characters
|
||||||
|
;; @param A: Size of the number in bytes
|
||||||
|
;; @param X: Offset onto ARG4, so that the number starts at `ARG4+x` (BE)
|
||||||
|
;; @param ARG2-3: Pointer to output string
|
||||||
|
;; @param Y: Offset onto the string in ARG2-3
|
||||||
|
;; @returns A: 0
|
||||||
|
;; @returns X: Offset onto ARG4, past the end of the number
|
||||||
|
;; @returns Y: Offset onto the the string in ARG2-3, past the end of the number
|
||||||
|
;; @returns N: Clear if success, else set
|
||||||
|
;; @modifies A,X,Y
|
||||||
|
;;********************************************************************************
|
||||||
|
.proc uint_to_hex_str
|
||||||
|
@loop:
|
||||||
|
cmp #0
|
||||||
|
beq @rts
|
||||||
|
pha
|
||||||
|
lda ARG4,x
|
||||||
|
phx
|
||||||
|
pha
|
||||||
|
and #%00001111
|
||||||
|
tax
|
||||||
|
lda HEX_CHARS_UPPER,x
|
||||||
|
sta (ARG2),y
|
||||||
|
pla
|
||||||
|
iny
|
||||||
|
beq @overflow2
|
||||||
|
div A,16
|
||||||
|
and #%00001111
|
||||||
|
tax
|
||||||
|
lda HEX_CHARS_UPPER,x
|
||||||
|
sta (ARG2),y
|
||||||
|
iny
|
||||||
|
beq @overflow2
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
inx
|
||||||
|
beq @overflow
|
||||||
|
bra @loop
|
||||||
|
@overflow2:
|
||||||
|
plx
|
||||||
|
pla
|
||||||
|
@overflow:
|
||||||
|
lda #$ff ; set n
|
||||||
|
@rts:
|
||||||
|
rts
|
||||||
|
.endproc
|
||||||
|
|
||||||
|
|
||||||
.rodata
|
.rodata
|
||||||
HEX_CHARS_UPPER: .byte "0123456789ABCDEF"
|
HEX_CHARS_UPPER: .byte "0123456789ABCDEF"
|
||||||
HEX_CHARS_LOWER: .byte "0123456789abcdef"
|
HEX_CHARS_LOWER: .byte "0123456789abcdef"
|
||||||
|
@ -1,20 +1,31 @@
|
|||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @module string
|
;; @file
|
||||||
; @type utility
|
;; @brief String utility library
|
||||||
; @details
|
;; @details
|
||||||
; String utility
|
;; @see str
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
.ifndef INCLUDE_STRING
|
.ifndef INCLUDE_STRING
|
||||||
INCLUDE_STRING = 1
|
INCLUDE_STRING = 1
|
||||||
|
|
||||||
.include "system/system.h65"
|
.include "system/system.h65"
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief String utility
|
||||||
|
;; @ingroup libs
|
||||||
|
;;********************************************************************************
|
||||||
.scope str
|
.scope str
|
||||||
Import str, strf
|
Import str, strf, printf_buffer
|
||||||
Import str, hex_char_to_uint8, hex_str_to_uint, uint8_to_hex_str
|
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` by 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)
|
||||||
@ -29,15 +40,15 @@ Import str, hex_char_to_uint8, hex_str_to_uint, uint8_to_hex_str
|
|||||||
.endif
|
.endif
|
||||||
.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
|
;; @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
|
||||||
.if .match(fmt, "") ; fmt is a string literal -> store in rodata
|
.if .match(fmt, "") ; fmt is a string literal -> store in rodata
|
||||||
@ -73,8 +84,16 @@ Import str, hex_char_to_uint8, hex_str_to_uint, uint8_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
|
||||||
|
|
||||||
; TODO allocate zp memory
|
;;********************************************************************************
|
||||||
fmt_idx = $30
|
;; @function Macro for formated printing
|
||||||
out_idx = $31
|
;; @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
|
||||||
|
.endmacro
|
||||||
|
|
||||||
.endscope
|
.endscope
|
||||||
.endif ; guard
|
.endif ; guard
|
||||||
|
123
util/string.s65
123
util/string.s65
@ -1,24 +1,38 @@
|
|||||||
.include "string.h65"
|
.include "string.h65"
|
||||||
Export str, strf
|
Export str, strf, printf_buffer
|
||||||
|
|
||||||
|
.bss
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @brief Used to store output string of Printf macro
|
||||||
|
;; @todo Use dynamically allocated buffer when a memory allocator is implemented
|
||||||
|
;;********************************************************************************
|
||||||
|
printf_buffer: .res $41
|
||||||
|
|
||||||
|
.zeropage
|
||||||
|
fmt_idx: .res 1
|
||||||
|
out_idx: .res 1
|
||||||
|
pad_zero:.res 1
|
||||||
|
digits: .res 1
|
||||||
|
; addr: .res 2
|
||||||
.code
|
.code
|
||||||
;********************************************************************************
|
;;********************************************************************************
|
||||||
; @function Format a string
|
;; @function Format a string
|
||||||
; @details
|
;; @details
|
||||||
; If there is no value to be read, the Pz will be set
|
;; If there is no value to be read, the `Pz` will be set
|
||||||
; Formats:
|
;; Formats:
|
||||||
; - x: unsigned hex integer (1 byte) -> 2 chars
|
;; - `x`: unsigned hex integer (1 byte) -> 2 chars
|
||||||
; - X: unsigned hex integer (2 byte) -> 4 chars TODO
|
;; - `X`: unsigned hex integer (2 byte) -> 4 chars TODO
|
||||||
; - u: unsigned decimal integer (1 byte) TODO
|
;; - `u`: unsigned decimal integer (1 byte) TODO
|
||||||
; - U: unsigned decimal integer (2 bytes) TODO
|
;; - `U`: unsigned decimal integer (2 bytes) TODO
|
||||||
; @param ARG0-1: Format string address
|
;; - `s`: null-terminated string (2 bytes ptr)
|
||||||
; @param ARG2-3: Output string address
|
;; - `c`: char
|
||||||
; @param ARG4+: Additional parameters
|
;; @todo Implement decimal, test X
|
||||||
; @returns
|
;; @param ARG0-1: Format string address
|
||||||
; @modifies: A, X, Y
|
;; @param ARG2-3: Output string address
|
||||||
;********************************************************************************
|
;; @param ARG4+: Additional parameters
|
||||||
out_idx := str::out_idx
|
;; @returns
|
||||||
fmt_idx := str::fmt_idx
|
;; @modifies A, X, Y, ARG4, ARG5
|
||||||
|
;;********************************************************************************
|
||||||
.proc strf
|
.proc strf
|
||||||
stz out_idx
|
stz out_idx
|
||||||
stz fmt_idx
|
stz fmt_idx
|
||||||
@ -27,7 +41,7 @@ fmt_idx := str::fmt_idx
|
|||||||
@loop:
|
@loop:
|
||||||
ldy fmt_idx
|
ldy fmt_idx
|
||||||
lda (ARG0),y
|
lda (ARG0),y
|
||||||
beq @null
|
jeq @null
|
||||||
cmp #'%'
|
cmp #'%'
|
||||||
beq @percent
|
beq @percent
|
||||||
@normal_char: ; store A in output string
|
@normal_char: ; store A in output string
|
||||||
@ -42,29 +56,88 @@ fmt_idx := str::fmt_idx
|
|||||||
sty fmt_idx
|
sty fmt_idx
|
||||||
lda (ARG0),y
|
lda (ARG0),y
|
||||||
beq @null
|
beq @null
|
||||||
|
; padding
|
||||||
|
cmp #'0'
|
||||||
|
beq @percent_zero
|
||||||
|
stz pad_zero
|
||||||
; formats
|
; formats
|
||||||
cmp #'x'
|
cmp #'x'
|
||||||
beq @format_hex1
|
beq @format_hex1
|
||||||
|
cmp #'X'
|
||||||
|
beq @format_hex2
|
||||||
|
cmp #'c'
|
||||||
|
beq @format_char
|
||||||
|
|
||||||
bra @normal_char
|
bra @normal_char
|
||||||
|
@percent_zero:
|
||||||
|
sta pad_zero
|
||||||
|
bra @percent
|
||||||
|
; @percent_num:
|
||||||
|
; sta pad_zero
|
||||||
|
; bra @percent
|
||||||
|
|
||||||
@format_hex1: ; 1 byte hex -> 2 chars
|
@format_hex1: ; 1 byte hex -> 2 chars
|
||||||
lda ARG4,x
|
lda ARG4,x
|
||||||
phx
|
phx
|
||||||
jsr str::uint8_to_hex_str
|
jsr str::uint8_to_hex_str
|
||||||
ldy out_idx
|
ldy out_idx
|
||||||
sta (ARG2),y ; most sig digit
|
sta (ARG2),y ; most sig digit
|
||||||
|
txa
|
||||||
|
plx
|
||||||
iny
|
iny
|
||||||
beq @out_overflow
|
beq @out_overflow
|
||||||
txa
|
|
||||||
sta (ARG2),y ; least sig digit
|
sta (ARG2),y ; least sig digit
|
||||||
iny
|
iny
|
||||||
beq @out_overflow
|
beq @out_overflow
|
||||||
sty out_idx
|
sty out_idx
|
||||||
plx
|
|
||||||
inx ; 1 byte of args handeled
|
inx ; 1 byte of args handeled
|
||||||
; bra @format_return
|
bra @format_return
|
||||||
@format_return: ; increment fmt_idx to swallow the formating char
|
|
||||||
inc fmt_idx
|
@format_hex2: ; 2 byte hex -> 4 chars
|
||||||
bra @loop
|
lda #2
|
||||||
|
ldy out_idx
|
||||||
|
jsr str::uint_to_hex_str
|
||||||
|
bmi @out_overflow ; might also be x overflow
|
||||||
|
sty out_idx
|
||||||
|
bra @format_return
|
||||||
|
|
||||||
|
@format_char: ; 1 char
|
||||||
|
lda ARG4,x
|
||||||
|
ldy out_idx
|
||||||
|
sta (ARG2),y
|
||||||
|
inc out_idx
|
||||||
|
beq @out_overflow
|
||||||
|
inx
|
||||||
|
bra @format_return
|
||||||
|
|
||||||
|
@format_string: ; string
|
||||||
|
ldy #0
|
||||||
|
cpx #0
|
||||||
|
beq @format_string_loop
|
||||||
|
; store the pointer to the string at arg4-5
|
||||||
|
@format_string_move_ptr:
|
||||||
|
lda ARG4,x
|
||||||
|
sta ARG4
|
||||||
|
lda ARG5,x
|
||||||
|
sta ARG5
|
||||||
|
@format_string_loop:
|
||||||
|
lda (ARG4),y
|
||||||
|
beq @format_string_end ; if null
|
||||||
|
phy
|
||||||
|
ldy out_idx
|
||||||
|
sta (ARG2),y
|
||||||
|
inc out_idx
|
||||||
|
beq @out_overflow
|
||||||
|
ply
|
||||||
|
iny
|
||||||
|
@format_string_end:
|
||||||
|
inx
|
||||||
|
inx
|
||||||
|
bra @format_return
|
||||||
|
|
||||||
|
@format_return: ; increment fmt_idx to account for the formating char
|
||||||
|
inc fmt_idx ; at this point, out_idx will always overflow first
|
||||||
|
jmp @loop
|
||||||
@out_overflow: ; store 0 in last position
|
@out_overflow: ; store 0 in last position
|
||||||
ldy #$ff
|
ldy #$ff
|
||||||
sty out_idx
|
sty out_idx
|
||||||
|
151
utility.h65
151
utility.h65
@ -1,32 +1,133 @@
|
|||||||
.ifndef INCLUDE_UTILITY
|
.ifndef INCLUDE_UTILITY
|
||||||
INCLUDE_UTILITY = 1
|
INCLUDE_UTILITY = 1
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @file
|
||||||
|
;; @brief Various useful macros
|
||||||
|
;; @ingroup libs
|
||||||
|
;;********************************************************************************
|
||||||
|
|
||||||
.macpack longbranch ; jeq, jge...
|
.macpack longbranch ; jeq, jge...
|
||||||
.macpack generic ; bge, add, sub
|
.macpack generic ; bge, add, sub
|
||||||
|
|
||||||
.feature string_escapes
|
.feature string_escapes
|
||||||
.feature underline_in_numbers
|
.feature underline_in_numbers
|
||||||
|
|
||||||
|
.include "bit_macros.h65"
|
||||||
|
|
||||||
;********************************************************************************
|
.macro DEBUG_LED_OFF nr
|
||||||
; @macro Update a byte in memory using a mask
|
pha
|
||||||
; @param addr Address of the byte to update
|
lda IO1 + IO::RA
|
||||||
; @param value New value
|
.if nr = 0
|
||||||
; @param mask Mask of the bits to affect by the new value
|
and #%11111110
|
||||||
; @details
|
.elseif nr = 1
|
||||||
; xor #value with addr -> only bits that need to flip are 1
|
and #%11111101
|
||||||
; and result with #mask -> only selected bits that need to flip stay 1
|
.else
|
||||||
; xor result with addr -> flips selected bits
|
and #%11111011
|
||||||
;********************************************************************************
|
.endif
|
||||||
|
sta IO1 + IO::RA
|
||||||
|
pla
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro DEBUG_LED_ON nr
|
||||||
|
pha
|
||||||
|
lda IO1 + IO::RA
|
||||||
|
.if nr = 0
|
||||||
|
ora #%00000001
|
||||||
|
.elseif nr = 1
|
||||||
|
ora #%00000010
|
||||||
|
.else
|
||||||
|
ora #%00000100
|
||||||
|
.endif
|
||||||
|
sta IO1 + IO::RA
|
||||||
|
pla
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Store a double byte (immediate) at an address
|
||||||
|
;; @param addr Address, double byte will be loaded in LE to addr, addr+1
|
||||||
|
;; @param dbyte Location or value
|
||||||
|
;; @param reg Register, defaults to A
|
||||||
|
;; @modifies
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro StoreDByte dbyte,addr,reg
|
||||||
|
; .out .sprintf("%x, %x", dbyte, addr)
|
||||||
|
.if .blank(reg) .or .match(reg, A)
|
||||||
|
lda #<dbyte
|
||||||
|
sta addr
|
||||||
|
lda #>dbyte
|
||||||
|
sta addr+1
|
||||||
|
.elseif .match(reg, X)
|
||||||
|
ldx #<dbyte
|
||||||
|
stx addr
|
||||||
|
ldx #>dbyte
|
||||||
|
stx addr+1
|
||||||
|
.elseif .match(reg, Y)
|
||||||
|
ldy #<dbyte
|
||||||
|
sty addr
|
||||||
|
ldy #>dbyte
|
||||||
|
sty addr+1
|
||||||
|
.else
|
||||||
|
.fatal "Invalid reg given to StoreDByte"
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Update a byte in memory using a mask
|
||||||
|
;; @param addr Address of the byte to update
|
||||||
|
;; @param value New value
|
||||||
|
;; @param mask Mask of the bits to affect by the new value
|
||||||
|
;; @details
|
||||||
|
;; xor #value with addr -> only bits that need to flip are 1
|
||||||
|
;; and result with #mask -> only selected bits that need to flip stay 1
|
||||||
|
;; xor result with addr -> flips selected bits
|
||||||
|
;; @TODO optimize when immediate is used
|
||||||
|
;;********************************************************************************
|
||||||
.macro MaskedWrite addr,value,mask
|
.macro MaskedWrite addr,value,mask
|
||||||
lda #value
|
.if .not .match(value, A)
|
||||||
|
lda value
|
||||||
|
.endif
|
||||||
eor addr
|
eor addr
|
||||||
and #mask
|
and mask
|
||||||
eor addr
|
eor addr
|
||||||
sta addr
|
sta addr
|
||||||
.endmacro
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Jump to the subroutine and let the routine return at another location
|
||||||
|
;; @details
|
||||||
|
;; By using a indirect address `(addr)` and no ret_val, this macro behaves
|
||||||
|
;; like an indirect version of jsr.
|
||||||
|
;; @param addr Target of the jump (can be: `addr`, `(addr)` or `{(addr,x)}`
|
||||||
|
;; @param ret_addr Return address (optional)
|
||||||
|
;; @details
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro JsrIndirect addr,ret_addr
|
||||||
|
; -1 because rts increments it
|
||||||
|
.if .blank(ret_addr)
|
||||||
|
lda #>(:+ - 1)
|
||||||
|
pha
|
||||||
|
lda #<(:+ - 1)
|
||||||
|
pha
|
||||||
|
.else
|
||||||
|
lda #>(ret_addr -1)
|
||||||
|
pha
|
||||||
|
lda #<(ret_addr -1)
|
||||||
|
pha
|
||||||
|
.endif
|
||||||
|
jmp addr
|
||||||
|
.if .blank(ret_addr)
|
||||||
|
:
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_n_genlabel .set 0
|
_n_genlabel .set 0
|
||||||
;;********************************************************************************
|
;;********************************************************************************
|
||||||
;; @macro Generate a unique label
|
;; @macro Generate a unique label
|
||||||
@ -91,4 +192,28 @@ _n_genlabel .set 0
|
|||||||
.ident(.sprintf("%s", .string(l1))) = .ident(.sprintf("%s_%s", .string(prefix), .string(l1)))
|
.ident(.sprintf("%s", .string(l1))) = .ident(.sprintf("%s_%s", .string(prefix), .string(l1)))
|
||||||
ImportZp prefix,l2,l3,l4,l5,l6,l7,l8
|
ImportZp prefix,l2,l3,l4,l5,l6,l7,l8
|
||||||
.endmacro
|
.endmacro
|
||||||
.endif
|
|
||||||
|
|
||||||
|
.import REVERSE_TABLE
|
||||||
|
;;********************************************************************************
|
||||||
|
;; @macro Reverse a byte using a @ref REVERSE_TABLE "table"
|
||||||
|
;; @param reg: The byte to reverse. May be in X, Y or A
|
||||||
|
;; @returns A: The reversed byte
|
||||||
|
;; @modifies A, X (only when reg = A)
|
||||||
|
;;********************************************************************************
|
||||||
|
.macro Reverse reg
|
||||||
|
.if .match(reg, A)
|
||||||
|
tax
|
||||||
|
lda REVERSE_TABLE,x
|
||||||
|
.elseif .match(reg, x)
|
||||||
|
lda REVERSE_TABLE,x
|
||||||
|
.elseif .match(reg, y)
|
||||||
|
lda REVERSE_TABLE,y
|
||||||
|
.else
|
||||||
|
.fatal ("Reverse macro got invalid argument. This syntax error is intentional to show the line number")
|
||||||
|
.endif
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.endif ; guard
|
||||||
|
Loading…
Reference in New Issue
Block a user