From 7fce58e4818d7b948ca7adb78669355f5bc3c712 Mon Sep 17 00:00:00 2001 From: "matthias@quintern.xyz" Date: Thu, 8 Aug 2024 20:15:50 +0200 Subject: [PATCH] improve documentation --- .doxygen_config | 20 +++--- .extra_docs.s65 | 67 ++++++++++++++++++ details.md | 19 ----- doxy-asm65.py | 74 +++++++++++++++----- programs/keypad_printer.s65 | 5 ++ programs/print_slow.s65 | 12 ++-- programs/ps2_keyboard_printer.s65 | 32 ++++++++- programs/ps2_keyboard_text_handler.s65 | 8 +-- programs/ps2_keyboard_util.s65 | 26 ++++++- programs/sleep.s65 | 18 ++--- programs/viu.s65 | 4 +- readme.md | 20 ++++-- spicode.s65 | 42 +---------- system/io_W65C22.h65 | 96 +++++++++++++++++--------- system/keypad.h65 | 23 +++--- system/lcd.h65 | 4 +- system/lcd.s65 | 2 +- system/ps2_keyboard.h65 | 70 +++++++++++++------ system/ps2_keyboard.s65 | 34 +++++---- system/spi.h65 | 10 ++- test.s65 | 22 ------ util/math.h65 | 25 ++++--- util/reverse.s65 | 4 ++ util/string.h65 | 5 +- util/string.s65 | 19 +++-- utility.h65 | 9 ++- 26 files changed, 426 insertions(+), 244 deletions(-) create mode 100644 .extra_docs.s65 delete mode 100644 details.md delete mode 100644 test.s65 diff --git a/.doxygen_config b/.doxygen_config index 00d0734..3df9fd4 100644 --- a/.doxygen_config +++ b/.doxygen_config @@ -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 diff --git a/.extra_docs.s65 b/.extra_docs.s65 new file mode 100644 index 0000000..f726379 --- /dev/null +++ b/.extra_docs.s65 @@ -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 +;; +;; - @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 +;; +;;******************************************************************************** + diff --git a/details.md b/details.md deleted file mode 100644 index b0f7bed..0000000 --- a/details.md +++ /dev/null @@ -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`) diff --git a/doxy-asm65.py b/doxy-asm65.py index 998a791..eda0fb8 100644 --- a/doxy-asm65.py +++ b/doxy-asm65.py @@ -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 = "";` +# - 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"(? 0xHEX except in comments r"(? 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"(? + - (Stylishly) print formated strings to the LCD + - Sleep + - Send various commands to the keyboard + - Ringbuffer > It's not much, but it's honest work. diff --git a/spicode.s65 b/spicode.s65 index 4ee4282..5fa6cda 100644 --- a/spicode.s65 +++ b/spicode.s65 @@ -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 diff --git a/system/io_W65C22.h65 b/system/io_W65C22.h65 index 1e607a0..31d3ba8 100644 --- a/system/io_W65C22.h65 +++ b/system/io_W65C22.h65 @@ -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 diff --git a/system/keypad.h65 b/system/keypad.h65 index 29bca1b..d5582d0 100644 --- a/system/keypad.h65 +++ b/system/keypad.h65 @@ -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 diff --git a/system/lcd.h65 b/system/lcd.h65 index 7cfd0c7..3b10363 100644 --- a/system/lcd.h65 +++ b/system/lcd.h65 @@ -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 diff --git a/system/lcd.s65 b/system/lcd.s65 index 11ec7f4..a419c28 100644 --- a/system/lcd.s65 +++ b/system/lcd.s65 @@ -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 diff --git a/system/ps2_keyboard.h65 b/system/ps2_keyboard.h65 index 450946a..7e365d5 100644 --- a/system/ps2_keyboard.h65 +++ b/system/ps2_keyboard.h65 @@ -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 @@ -16,33 +16,53 @@ ;; @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 -; 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 diff --git a/system/ps2_keyboard.s65 b/system/ps2_keyboard.s65 index cf90d26..841c197 100644 --- a/system/ps2_keyboard.s65 +++ b/system/ps2_keyboard.s65 @@ -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 " diff --git a/system/spi.h65 b/system/spi.h65 index 5c1507c..9687e16 100644 --- a/system/spi.h65 +++ b/system/spi.h65 @@ -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 diff --git a/test.s65 b/test.s65 deleted file mode 100644 index e863888..0000000 --- a/test.s65 +++ /dev/null @@ -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" diff --git a/util/math.h65 b/util/math.h65 index 03b26d1..721d3eb 100644 --- a/util/math.h65 +++ b/util/math.h65 @@ -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 diff --git a/util/reverse.s65 b/util/reverse.s65 index eab2484..da35f4c 100644 --- a/util/reverse.s65 +++ b/util/reverse.s65 @@ -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 diff --git a/util/string.h65 b/util/string.h65 index 54a79e0..b14bba0 100644 --- a/util/string.h65 +++ b/util/string.h65 @@ -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 diff --git a/util/string.s65 b/util/string.s65 index 6be6543..b427311 100644 --- a/util/string.s65 +++ b/util/string.s65 @@ -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 diff --git a/utility.h65 b/utility.h65 index 506c9ed..0b13a2a 100644 --- a/utility.h65 +++ b/utility.h65 @@ -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)