Compare commits

...

2 Commits

Author SHA1 Message Date
7fce58e481 improve documentation 2024-08-08 20:15:50 +02:00
d86ac8d52d move kb util to main 2024-08-08 20:15:28 +02:00
28 changed files with 452 additions and 258 deletions

View File

@ -289,11 +289,12 @@ TAB_SIZE = 4
# with the commands \{ and \} for these it is advised to use the version @{ and
# @} or use a double escape (\\{ and \\})
ALIASES = "modifies=\xrefitem modifies \"Modifies Registers\" \"Modified Registers\"" \
ALIASES = "modifies=\par Modifies Registers^^" \
"type=\xrefitem type \"Code Type\" \"Code Type\"" \
"macro=\qualifier macro \brief" \
"function=\qualifier subroutine \brief" \
"module=\file ^^ \brief"
"module=\file ^^ \brief" \
"clock_dependant=\xrefitem clock_dependant \"Clock Speed Dependance\" \"Clock frequency dependant code\""
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
@ -529,7 +530,8 @@ TIMESTAMP = NO
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = NO
# TODO Document everyhing
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
@ -954,7 +956,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = . build programs system util
INPUT = . build programs system util .extra_docs.s65 doxy-asm65.py
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -994,10 +996,8 @@ INPUT_FILE_ENCODING =
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.cpp \
*.s65 \
*.h65 \
*.md
FILE_PATTERNS = *.s65 \
*.h65
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
@ -1124,7 +1124,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = #../README.md
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
@ -1364,7 +1364,7 @@ HTML_STYLESHEET =
# documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = #../docs/doxygen-awesome-css/doxygen-awesome.css
HTML_EXTRA_STYLESHEET = ../docs/custom/doxygen-awesome.css
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note

67
.extra_docs.s65 Normal file
View File

@ -0,0 +1,67 @@
;;********************************************************************************
;; @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 utility
;; @brief Code that does something useful and is intended to be used in other code
;;
;;********************************************************************************

View File

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

View File

@ -2,6 +2,37 @@ 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 (@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):
@ -16,7 +47,7 @@ def parse_custom_language(file_content: str):
scope = m.groups()[1]
functions = m.groups()[2].replace(" ", "").strip(",")
for f in functions.split(","):
pdebug(f"Add Exported function: '{f}' in '{scope}'")
# pdebug(f"Add Exported function: '{f}' in '{scope}'")
exported_names[f] = scope
return ""
@ -50,12 +81,11 @@ def parse_custom_language(file_content: str):
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(','))}'")
# pdebug(f"Processing macro '{p_name}' with args '{'TXT'.join(p_args.replace(' ', '').split(','))}'")
s += f"macro {p_name}("
p_args = "".join("Param " + param + "," for param in p_args.replace(" ", "").split(',')).strip(",")
s += p_args
s += ");\n"
pdebug("Found macro", p_name, s)
elif p_type == "enum":
p_code = re.sub(r"( *(?:;;.*)?\n)", r",\1", p_code)
s += f"enum {p_name}" + "{\n" + p_code + "};"
@ -69,10 +99,12 @@ def parse_custom_language(file_content: str):
return s
def handle_storage_label(m):
l_docs = m.groups()[0].strip('\n')
l_docs = m.groups()[0]
l_name = m.groups()[1]
l_allocs = m.groups()[2]
storage_alloc = r"(?:\.(byte|res|dbyte|word|addr|faraddr|dword|ascii|asciiz)([, ]+(?:0x[a-fA-F0-9]+|0b[01]+|\d+|\w+|\"[^\n]*?[^\\\n]\")[ \n]*)*)"
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 = []
@ -83,36 +115,42 @@ def parse_custom_language(file_content: str):
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
# pdebug("ldocs for", l_name, l_docs)
# pdebug(m.groups())
# 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}" + " {" + l_docs
s += "\n"
else:
s += l_docs + "\n"
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 += "bytes[] = {"
s += f"bytes {l_name}[{len(args)}] = " + "{"
for arg in args:
s += arg + ","
s += "}"
s += s.strip(",") + "}"
s = s.strip(",") + "}"
else:
# alloc_type label =
l_type = allocs[0].groups()[0]
if len(args) == 0:
l_arg = None
else:
l_arg = args[0]
# if 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:
@ -134,8 +172,9 @@ def parse_custom_language(file_content: str):
r"^(Import(?:Zp)?) (\w+)((?: *, *\w+)+)": "",
r"(?<!^;)\$([A-Fa-f0-9_]+)": r"0x\1", # $HEX -> 0xHEX except in comments
r"(?<!^;)%([01_]+)": r"0b\1", # %BIN -> 0bBIN except in comments
r"^((?:;;.*\n)*)^\.(proc|enum|macro) (\w+)(.*?)\n((?:.|\n)*?)\.end(proc|enum|macro).*": handle_procedure,
r"^((?:;;.*\n)*) *(\w+):((?:\s*\.(?:byte|res|dbyte|word|addr|faraddr|dword|ascii|asciiz)(?:[, ]+(?:0x[a-fA-F0-9]+|0b[01]+|\d+|\w+|\"[^\n]*[^\\\n]\")[ \n]*)*)+)": handle_storage_label,
r"^((?:;;.*\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
@ -157,7 +196,6 @@ def parse_custom_language(file_content: str):
for pat, subst in resub_patterns.items():
file_content = re.sub(pat, subst, file_content, 0, re.MULTILINE)
for pat,subst in patterns.items():
pdebug(f"Now doing pattern: {pat}")
(file_content, n_subst) = re.subn(pat, subst, file_content, 0, re.MULTILINE)
return file_content

View File

@ -3,6 +3,8 @@
.export home,homeloop
.import keypad_printer:absolute
.import spi_menu:absolute
.import ps2_keyboard_printer:absolute
.import ps2_keyboard_util:absolute
.code
@ -143,9 +145,11 @@ reset:
cmp #'D'
jeq print_2
cmp #'1'
beq @debug0_on
; beq @debug0_on
beq @ps2_util
cmp #'2'
beq @debug0_off
beq @ps2_printer
; beq @debug0_off
cmp #'4'
beq @debug1_on
cmp #'5'
@ -161,6 +165,10 @@ reset:
wai
jmp @loop
@ps2_util:
jmp ps2_keyboard_util
@ps2_printer:
jmp ps2_keyboard_printer
@debug0_off:
DEBUG_LED_OFF 0
jmp @loop
@ -209,18 +217,18 @@ message_1:
message_2:
.byte "0123456789=!?#+*"
.byte "ABCDEFGHIJKLMNOP"
.byte "QRSTUVWXYZÖÄÜß-_"
.byte "QRSTUVWXYZöäüß-_"
.asciiz "{[(<>)]}$%&/,;.:"
message_menu:
.byte "<A> Printer "
.byte "A/2: KP/KB Print"
; .byte "<B> Temperatur"
.byte "<B> SPI-Menu "
.byte "<C> Text 1 "
.asciiz "<D> Text 2 "
str_spi_begin:
.asciiz "---BEGIN SPI---"
str_spi_start:
.asciiz "---START SPI---"
.byte "1: KB Util "
.byte "B: SPI-Menu "
.asciiz "C/D: Text 1/2 "
; str_spi_begin:
; .asciiz "---BEGIN SPI---"
; str_spi_start:
; .asciiz "---START SPI---"
str_io2:
.asciiz "IO2::RB='"

View File

@ -3,6 +3,11 @@
.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:

View File

@ -5,11 +5,13 @@
.export print_slow
.code
;********************************************************************************
; @function Print a null-terminated string
; @param ARG0-1: Address of the string to print
; @param x: time to sleep in centiseconds
;********************************************************************************
;;********************************************************************************
;; @function Print a null-terminated string with a delay between each character
;; @param ARG0-1: Address of the string to print
;; @param x: time to sleep in centiseconds
;; @ingroup applications
;; @clock_dependant
;;********************************************************************************
.proc print_slow
ldy #$00
@print_loop:

View File

@ -1,4 +1,5 @@
.include "lcd.h65"
.include "string.h65"
.include "ps2_keyboard_text_handler.h65"
.export ps2_keyboard_printer
@ -6,10 +7,36 @@
.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
@ -18,11 +45,13 @@ 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
@ -30,4 +59,3 @@ loop:
jsr lcd::clear
bra loop
.endproc

View File

@ -5,15 +5,15 @@ ExportZp kb, modifier
.zeropage
;; @ref kb::MOD "status of the modifier keys"
modifier: .byte
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: .byte
previous_scancode: .res 1
;; 0 when no key or non-character key was pressed, otherwise character of the key (with modifiers applied)
char: .byte
char: .res 1
;; @ref kb::K "keycode" of the last key that was pressed or 0 if none was pressed
keycode: .byte
keycode: .res 1
.code
;;********************************************************************************

View File

@ -10,6 +10,29 @@
.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 keyboard
;;********************************************************************************
.proc ps2_keyboard_util
stz kp::_DEBUG_VAL
jsr skb::init
@ -67,6 +90,7 @@ loop:
beq @lD
jsr lcd::print_char
jmp loop
@l0: ; get scancode set
lda #$f0
ldx #0
@ -99,7 +123,7 @@ loop:
@l8: ; set make/release all
lda #$f8
bra @send_cmd
@l9: ; set CAPSLOCK
@l9: ; set CAPSLOCK led
lda #$ed
ldx #kb::MOD::CAPSLOCK
bra @send_cmd

View File

@ -3,14 +3,16 @@
.export sleep
.code
;********************************************************************************
; @function sleep
; @param x: Time to sleep in centiseconds (10^-2s = 10ms)
; @details
; Interrupts might change the actual time to finish
; To be exact, time_cs is in units of 0.010244s
; @modifies: ARG15
;********************************************************************************
;;********************************************************************************
;; @function sleep
;; @param x: Time to sleep in centiseconds (10^-2s = 10ms) @ 1MHz
;; @details
;; Interrupts might change the actual time to finish
;; To be exact, time_cs is in units of 0.010244s
;; @modifies: ARG15
;; @ingroup applications
;; @clock_dependant
;;********************************************************************************
.proc sleep
_VAR_1 = ARG15
stz _VAR_1

View File

@ -9,11 +9,15 @@
.export spi_menu
.bss
trans_bytes: .res 2 ; used to check if screen needs to be updated
trans_bytes: .res 2 ;; used to check if screen needs to be updated
status_char: .res 1
status_str: .res 17 ; 16 + null
status_str: .res 17 ;; space for 16 + null
.code
;;********************************************************************************
;; @brief Receive code via SPI and execute it
;; @ingroup applications
;;********************************************************************************
.proc spi_menu
stz trans_bytes
stz trans_bytes+1

View File

@ -1,3 +1,6 @@
;;********************************************************************************
;; @module VIU - VI Unimproved
;;********************************************************************************
.include "lcd.h65"
.include "ps2_keyboard_text_handler.h65"
@ -13,7 +16,6 @@ buffer: .res 256
stz ptr
stz ptr+1
stz buffer
.endproc
.proc insert_mode

View File

@ -5,12 +5,18 @@ The assembler used for this project is the excellent [ca65](https://github.com/c
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
... is probably a far stretch, since it is just the programs I wrote pieced together. My "os" consists of these functionalities:
- Main Menu:
- Printer: Prints the characters you press on the keypad to the lcd.
- Temperature: Shows the temperature using a dht sensor. *Work in progress, this does not work yet*
- Text 1: Show a 4x16 character text (defined at compile time)
- Text 2: Show a 4x16 character text (defined at compile time)
- Ringbuffer for pressed keys.
... 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
- 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.

View File

@ -79,7 +79,7 @@ CODE_START:
@l0:
jmp ps2_keyboard_util
@l1:
jmp _ps2_keyboard_printer
jmp ps2_keyboard_printer
@l2:
lcd_SetCursorPos $26
lda #'6'
@ -101,46 +101,6 @@ CODE_START:
@lD:
jmp @loop
.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

View File

@ -8,28 +8,43 @@
.ifndef INCLUDE_IOW65C22
INCLUDE_IOW65C22 = 1
;;********************************************************************************
;; @brief Versatile Interface Adapter (VIA) W65C22
;; @ingroup utility
;; @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
; not using a struct for this since the syntax for access would be the same,
; ie label+IO::RA
;;********************************************************************************
;; @brief VIA register offsets from the base address
;; @details
;; Use like this: `VIA_ADDRESS + IO::RB`
;;********************************************************************************
.enum
; IO-CHIPS OFFSETS FOR PINS FROM BASE ADDRESS
RB = $0 ; Register B (ORB/IRB)
RA = $1 ; Register A (ORA/IRA)
DDRB = $2 ; Data Direction Register B
DDRA = $3 ; Data Direction Register A
T1CL = $4 ; Timer 1 Counter Low/High
RB = $0 ;; Register B (ORB/IRB)
RA = $1 ;; Register A (ORA/IRA)
DDRB = $2 ;; Data Direction Register B
DDRA = $3 ;; Data Direction Register A
T1CL = $4 ;; Timer 1 Counter Low/High
T1CH = $5
T1LL = $6 ; Timer 1 Latch Low/High
T1LL = $6 ;; Timer 1 Latch Low/High
T1LH = $7
T2CL = $8 ; Timer 2 Counter Low/High
T2CL = $8 ;; Timer 2 Counter Low/High
T2CH = $9
SR = $a ; Shift Register
ACR = $b ; Auxiliary Control Register
PCR = $c ; Peripheral Control Register
IFR = $d ; Interrupt Flag Register
IER = $e ; Interrupt Enable Register
RANH = $f ; RA without handshake
SR = $a ;; Shift Register
ACR = $b ;; Auxiliary Control Register
PCR = $c ;; Peripheral Control Register
IFR = $d ;; Interrupt Flag Register
IER = $e ;; Interrupt Enable Register
RANH = $f ;; RA without handshake
.endenum
.enum ACR_MASK ; ACR Masks
@ -40,27 +55,32 @@ INCLUDE_IOW65C22 = 1
T1 = %11000000
.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_DISABLE = %00000000 ; Disabled
SR_SIN_T2 = %00000100 ; Shift in under control of T2
SR_SIN_PHI2 = %00001000 ; Shift in under control of PHI2
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_T2 = %00010100 ; Shift out under control of T2
SR_SOUT_PHI2 = %00011000 ; Shift out under control of PHI2
SR_SOUT_PHIE = %00011100 ; Shift out under control of external clock
SR_DISABLE = %00000000 ;; Disabled
SR_SIN_T2 = %00000100 ;; Shift in under control of T2
SR_SIN_PHI2 = %00001000 ;; Shift in under control of PHI2
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_T2 = %00010100 ;; Shift out under control of T2
SR_SOUT_PHI2 = %00011000 ;; Shift out under control of PHI2
SR_SOUT_PHIE = %00011100 ;; Shift out under control of external clock
; T1 Modes
T1_IRQ_LOAD = %00000000 ; Timed interrupt each time T1 is loaded
T1_IRQ_CONT = %01000000 ; Continuous interrupts
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_LOAD = %00000000 ;; Timed interrupt each time T1 is loaded
T1_IRQ_CONT = %01000000 ;; Continuous interrupts
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
; T2 Modes
T2_IRQ_LOAD = %00000000 ; Timed interrupt each time T2 is loaded
T2_COUNT_PB6 = %00100000 ; Count down with pulsen on PB6
T2_IRQ_LOAD = %00000000 ;; Timed interrupt each time T2 is loaded
T2_COUNT_PB6 = %00100000 ;; Count down with pulsen on PB6
; Latch
LATCH_DISABLE = %00000000
LATCH_ENBLE = %00000000
LATCH_DISABLE = %00000000 ;; `OR` this with IO::ACR_MASK::PA or IO::ACR_MASK::PB
LATCH_ENBLE = %00000011 ;; `OR` this with IO::ACR_MASK::PA or IO::ACR_MASK::PB
.endenum
.enum PCR_MASK ; PCR Masks
@ -70,6 +90,11 @@ INCLUDE_IOW65C22 = 1
CB2 = %11100000 ;
.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
; CA1 Modes
CA1_IN_AE = %00000000 ; Input-negative active edge
@ -97,7 +122,10 @@ INCLUDE_IOW65C22 = 1
CB2_OUT_HIGH = %11100000 ; High output
.endenum
; IFR/IER bits
;;********************************************************************************
;; @brief Interrupt Flag/Enable Register bits
;;********************************************************************************
.enum IRQ
CA2 = %00000001
CA1 = %00000010
@ -112,7 +140,7 @@ INCLUDE_IOW65C22 = 1
;;********************************************************************************
;; @macro Enable an interrupt source
;; @modifies: A
;; @param flag: A flag of the interrupt flag register (IO:IRQ)
;; @param flag: A flag of the interrupt flag register (IO::IRQ)
;;********************************************************************************
.macro IO_EnableIRQ ioaddr, flag
lda #(IO::IRQ::IRQ | flag)
@ -121,7 +149,7 @@ INCLUDE_IOW65C22 = 1
;;********************************************************************************
;; @macro Disable an interrupt source
;; @modifies: A
;; @param flag: A flag of the interrupt flag register (IO:IRQ)
;; @param flag: A flag of the interrupt flag register (IO::IRQ)
;;********************************************************************************
.macro IO_DisableIRQ ioaddr, flag
lda #flag

View File

@ -1,17 +1,20 @@
;********************************************************************************
; @module keypad4x4
; @type driver
; @device 4x4 Matrix Keypad
; @details
; The LCD must be connected to a W65C22N Interface Chip:
; - IO.RA0-7 ->
; @requires KP_IO: Base Address of IO Chip
; @depends IO-W65C22N
;********************************************************************************
;;********************************************************************************
;; @module keypad4x4
;; @type driver
;; @device 4x4 Matrix Keypad
;; @details
;; The LCD must be connected to a W65C22N Interface Chip:
;; - IO.RA0-7 ->
;; @requires KP_IO: Base Address of IO Chip
;; @depends IO-W65C22N
;;********************************************************************************
.ifndef INCLUDE_KEYPAD
INCLUDE_KEYPAD = 1
.include "system.h65"
;;********************************************************************************
;; @brief 4x4 Matrix Keypad
;;********************************************************************************
.scope kp
KP_IO = IO2
; .import KP_IO

View File

@ -1,6 +1,6 @@
;;********************************************************************************
;; @module LCD-W164B
;; @type driver
;; @ingroup driver
;; @device ELECTRONIC ASSEMBLY - W164B-NLW
;; @details
;; The LCD must be connected to a W65C22N Interface Chip:
@ -8,7 +8,6 @@
;; - IO.RA5 -> LCD.RS register select
;; - IO.RA6 -> LCD.R/W read/write
;; - IO.RA7 -> LCD.E enable
;;
;; @requires IO: Base Address of IO Chip
;; @depends IO-W65C22N
;;********************************************************************************
@ -19,6 +18,7 @@ INCLUDE_LCD = 1
;;********************************************************************************
;; @brief LCD character display
;; @ingroup driver
;;********************************************************************************
.scope lcd
LCD_IO = IO1

View File

@ -11,7 +11,7 @@ Export lcd,_charcount
.bss
_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
;;********************************************************************************
;; @function Initialize the lcd module

View File

@ -1,6 +1,6 @@
;;********************************************************************************
;; @module ps2_keyboard
;; @type driver
;; @ingroup drivers
;; @details:
;; Support for a PS2 Keyboard using the shift register of a 6522 VIA
;; @section reading Reading a scancode/command answer
@ -17,32 +17,52 @@
;; 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
; Enough time must pass for 3 bits to be shifted in.
; TIMER_RECV = 230 ; 230 ms (@1MHz)
TIMER_RECV = 400 ; 230 ms (@1MHz)
; 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 SR needs to be set to input again
; TIMER_SEND = 230 ; 180 ms (@1MHz)
TIMER_SEND = 400 ; 180 ms (@1MHz)
;; @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_dependant
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_dependant
TIMER_SEND = 400
PULL_REG = IO::RANH
PULL_DDR = IO::DDRA
@ -50,24 +70,24 @@ PULL_DDR = IO::DDRA
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
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
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
REPEAT_MASK = %00011111 ;; 00000 = 30Hz, ..., 11111 = 2Hz
DELAY250 = %00000000
DELAY500 = %00100000
DELAY750 = %01000000
@ -186,6 +206,10 @@ RESEND = $fe ; unsuccessful transmission
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

View File

@ -7,20 +7,21 @@ Export ps2kb, _receive_irq_shift_reg_handler, _receive_irq_timer_handler, _send_
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
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
@ -80,6 +81,15 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
.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
@ -468,5 +478,5 @@ scancode_handler: .res 2 ; pointer to a function that handles new scancod
.endproc
.rodata
;; Can be used by other programs
;; 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 "

View File

@ -1,6 +1,5 @@
;;********************************************************************************
;; @module SPI-P
;; @type driver
;; @details
;; Support being a peripheral SPI device.
;; @depends IO-W65C22N
@ -11,11 +10,16 @@ INCLUDE_SPI = 1
.include "system/system.h65"
;;********************************************************************************
;; @brief SPI-Peripheral
;; @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 driver
;; @see via_hardware_bug "VIA external clock bug"
;;********************************************************************************
.scope spi_p
;; Address of the VIA that is used for the spi connection
SPI_IO = IO2
Import spi_p, init, irq_handler, status, buffer_size, recv_size
ImportZp spi_p, buffer_ptr

View File

@ -1,22 +0,0 @@
; .include "other/file.h65"
.segment "TEST"
; .proc my_subroutine
; pha
; bbr4 var1,@bit_4_set
; bit var1
; ; macro packs are supported
; jmi other_routine
; lda #'a'
; sta char
; bra @end
; @bit_4_set:
; ; custom macros can be highlighted
; Printf STR_FMT,char,var1
; @end:
; pla
; rts
; .endproc
; STR_FMT: .asciiz "%c: num=%x"

View File

@ -1,19 +1,22 @@
;********************************************************************************
; @module math
; @type utility
; @details
; Math library
;********************************************************************************
;;********************************************************************************
;; @module math
;; @details
;; Math library
;;********************************************************************************
.ifndef INCLUDE_MATH
INCLUDE_MATH = 1
;;********************************************************************************
;; @brief Math library
;; @ingroup utility
;;********************************************************************************
.scope math
;********************************************************************************
; @macro Divide a num (or A) by a factor
; @param num: Memory or A
; @param divider: must be power of 2
;********************************************************************************
;;********************************************************************************
;; @macro Divide a num (or A) by a factor
;; @param num: Memory or A
;; @param divider: must be power of 2
;;********************************************************************************
.macro div num,divider
.if divider = 2
lsr num

View File

@ -2,6 +2,10 @@
.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

View File

@ -1,8 +1,8 @@
;;********************************************************************************
;; @module string
;; @type utility
;; @details
;; String utility
;; @see str
;;********************************************************************************
.ifndef INCLUDE_STRING
INCLUDE_STRING = 1
@ -12,6 +12,7 @@ INCLUDE_STRING = 1
;;********************************************************************************
;; @brief String utility
;; @ingroup utility
;;********************************************************************************
.scope str
Import str, strf, printf_buffer
@ -22,7 +23,7 @@ Import str, uint8_to_hex_str, uint_to_hex_str
;;********************************************************************************
;; @macro Helper for str::Strf macro
;; @details
;; Store arg in ARG4-ARG... and increment counter variable @N_ARGS one.
;; 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

View File

@ -2,6 +2,10 @@
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
@ -14,14 +18,15 @@ digits: .res 1
;;********************************************************************************
;; @function Format a string
;; @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:
;; - x: unsigned hex integer (1 byte) -> 2 chars
;; - X: unsigned hex integer (2 byte) -> 4 chars TODO
;; - u: unsigned decimal integer (1 byte) TODO
;; - U: unsigned decimal integer (2 bytes) TODO
;; - s: null-terminated string (2 bytes ptr)
;; - c: char
;; - `x`: unsigned hex integer (1 byte) -> 2 chars
;; - `X`: unsigned hex integer (2 byte) -> 4 chars TODO
;; - `u`: unsigned decimal integer (1 byte) TODO
;; - `U`: unsigned decimal integer (2 bytes) TODO
;; - `s`: null-terminated string (2 bytes ptr)
;; - `c`: char
;; @todo Implement decimal, test X
;; @param ARG0-1: Format string address
;; @param ARG2-3: Output string address
;; @param ARG4+: Additional parameters

View File

@ -1,6 +1,13 @@
.ifndef INCLUDE_UTILITY
INCLUDE_UTILITY = 1
;;********************************************************************************
;; @file
;; @brief Various useful macros
;; @ingroup utility
;;********************************************************************************
.macpack longbranch ; jeq, jge...
.macpack generic ; bge, add, sub
@ -190,7 +197,7 @@ _n_genlabel .set 0
.import REVERSE_TABLE
;;********************************************************************************
;; @macro Reverse a byte
;; @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)