diff --git a/.gitignore b/.gitignore
index 55357e2..615be8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,13 @@
-CMakeLists.txt.user
-CMakeCache.txt
-CMakeFiles
-CMakeScripts
-Testing
-Makefile
-cmake_install.cmake
-install_manifest.txt
-compile_commands.json
-CTestTestfile.cmake
-_deps
-build/**
+CMakeLists.txt.user
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Testing
+Makefile
+cmake_install.cmake
+install_manifest.txt
+compile_commands.json
+CTestTestfile.cmake
+_deps
+build/**
testapps/**
\ No newline at end of file
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index a9157ca..f8169ec 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -1,22 +1,22 @@
-{
- "configurations": [
- {
- "name": "Pico",
- "includePath": [
- "${workspaceFolder}/**",
- "${userHome}/.pico-sdk/sdk/2.0.0/**"
- ],
- "forcedInclude": [
- "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h",
- "${userHome}/.pico-sdk/sdk/2.0.0/src/common/pico_base_headers/include/pico.h"
- ],
- "defines": [],
- "compilerPath": "${userHome}/.pico-sdk/toolchain/13_3_Rel1/bin/arm-none-eabi-gcc",
- "compileCommands": "${workspaceFolder}/build/compile_commands.json",
- "cStandard": "c17",
- "cppStandard": "c++14",
- "intelliSenseMode": "linux-gcc-arm"
- }
- ],
- "version": 4
+{
+ "configurations": [
+ {
+ "name": "Pico",
+ "includePath": [
+ "${workspaceFolder}/**",
+ "${userHome}/.pico-sdk/sdk/2.0.0/**"
+ ],
+ "forcedInclude": [
+ "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h",
+ "${userHome}/.pico-sdk/sdk/2.0.0/src/common/pico_base_headers/include/pico.h"
+ ],
+ "defines": [],
+ "compilerPath": "${userHome}/.pico-sdk/toolchain/13_3_Rel1/bin/arm-none-eabi-gcc",
+ "compileCommands": "${workspaceFolder}/build/compile_commands.json",
+ "cStandard": "c17",
+ "cppStandard": "c++14",
+ "intelliSenseMode": "linux-gcc-arm"
+ }
+ ],
+ "version": 4
}
\ No newline at end of file
diff --git a/.vscode/cmake-kits.json b/.vscode/cmake-kits.json
index 452050a..4d9001b 100644
--- a/.vscode/cmake-kits.json
+++ b/.vscode/cmake-kits.json
@@ -1,16 +1,16 @@
-[
- {
- "name": "Pico",
- "compilers": {
- "C": "${userHome}/.pico-sdk/toolchain/13_2_Rel1/bin/arm-none-eabi-gcc.exe",
- "CXX": "${userHome}/.pico-sdk/toolchain/13_2_Rel1/bin/arm-none-eabi-gcc.exe"
- },
- "toolchainFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.0.0/cmake/preload/toolchains/pico_arm_cortex_m0plus_gcc.cmake",
- "environmentVariables": {
- "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}"
- },
- "cmakeSettings": {
- "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}"
- }
- }
+[
+ {
+ "name": "Pico",
+ "compilers": {
+ "C": "${userHome}/.pico-sdk/toolchain/13_2_Rel1/bin/arm-none-eabi-gcc.exe",
+ "CXX": "${userHome}/.pico-sdk/toolchain/13_2_Rel1/bin/arm-none-eabi-gcc.exe"
+ },
+ "toolchainFile": "${env:USERPROFILE}/.pico-sdk/sdk/2.0.0/cmake/preload/toolchains/pico_arm_cortex_m0plus_gcc.cmake",
+ "environmentVariables": {
+ "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}"
+ },
+ "cmakeSettings": {
+ "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}"
+ }
+ }
]
\ No newline at end of file
diff --git a/.vscode/cmake_configurations.json b/.vscode/cmake_configurations.json
index 4b84d82..f82226e 100644
--- a/.vscode/cmake_configurations.json
+++ b/.vscode/cmake_configurations.json
@@ -1,20 +1,20 @@
-{
- "configurations": [
- {
- "name": "Debug",
- "buildType": "Debug"
- },
- {
- "name": "Release",
- "buildType": "Release"
- },
- {
- "name": "RelWithDebInfo",
- "buildType": "RelWithDebInfo"
- },
- {
- "name": "MinSizeRel",
- "buildType": "MinSizeRel"
- }
- ]
+{
+ "configurations": [
+ {
+ "name": "Debug",
+ "buildType": "Debug"
+ },
+ {
+ "name": "Release",
+ "buildType": "Release"
+ },
+ {
+ "name": "RelWithDebInfo",
+ "buildType": "RelWithDebInfo"
+ },
+ {
+ "name": "MinSizeRel",
+ "buildType": "MinSizeRel"
+ }
+ ]
}
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 2354cf2..f5aa30d 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,9 +1,9 @@
-{
- "recommendations": [
- "marus25.cortex-debug",
- "ms-vscode.cpptools",
- "ms-vscode.cpptools-extension-pack",
- "ms-vscode.vscode-serial-monitor",
- "raspberry-pi.raspberry-pi-pico",
- ]
-}
+{
+ "recommendations": [
+ "marus25.cortex-debug",
+ "ms-vscode.cpptools",
+ "ms-vscode.cpptools-extension-pack",
+ "ms-vscode.vscode-serial-monitor",
+ "raspberry-pi.raspberry-pi-pico",
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 3308d8d..2b2dd37 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,69 +1,69 @@
-{
- "version": "0.2.0",
- "configurations": [
- {
- "name": "Pico Debug (Cortex-Debug)",
- "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
- "executable": "${command:raspberry-pi-pico.launchTargetPath}",
- "request": "launch",
- "type": "cortex-debug",
- "servertype": "openocd",
- "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
- "gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
- "device": "${command:raspberry-pi-pico.getChipUppercase}",
- "configFiles": [
- "interface/cmsis-dap.cfg",
- "target/${command:raspberry-pi-pico.getTarget}.cfg"
- ],
- "svdFile": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
- "runToEntryPoint": "main",
- // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
- // Also works fine for flash binaries
- "overrideLaunchCommands": [
- "monitor reset init",
- "load \"${command:raspberry-pi-pico.launchTargetPath}\""
- ],
- "openOCDLaunchCommands": [
- "adapter speed 5000"
- ]
- },
- {
- "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
- "cwd": "${workspaceRoot}",
- "executable": "${command:raspberry-pi-pico.launchTargetPath}",
- "request": "launch",
- "type": "cortex-debug",
- "servertype": "external",
- "gdbTarget": "localhost:3333",
- "gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
- "device": "${command:raspberry-pi-pico.getChipUppercase}",
- "svdFile": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
- "runToEntryPoint": "main",
- // Give restart the same functionality as runToEntryPoint - main
- "postRestartCommands": [
- "break main",
- "continue"
- ]
- },
- {
- "name": "Pico Debug (C++ Debugger)",
- "type": "cppdbg",
- "request": "launch",
- "cwd": "${workspaceRoot}",
- "program": "${command:raspberry-pi-pico.launchTargetPath}",
- "MIMode": "gdb",
- "miDebuggerPath": "${command:raspberry-pi-pico.getGDBPath}",
- "miDebuggerServerAddress": "localhost:3333",
- "debugServerPath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
- "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/${command:raspberry-pi-pico.getTarget}.cfg -c \"adapter speed 5000\"",
- "serverStarted": "Listening on port .* for gdb connections",
- "filterStderr": true,
- "hardwareBreakpoints": {
- "require": true,
- "limit": 4
- },
- "preLaunchTask": "Flash",
- "svdPath": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd"
- },
- ]
-}
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Pico Debug (Cortex-Debug)",
+ "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
+ "executable": "${command:raspberry-pi-pico.launchTargetPath}",
+ "request": "launch",
+ "type": "cortex-debug",
+ "servertype": "openocd",
+ "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+ "gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
+ "device": "${command:raspberry-pi-pico.getChipUppercase}",
+ "configFiles": [
+ "interface/cmsis-dap.cfg",
+ "target/${command:raspberry-pi-pico.getTarget}.cfg"
+ ],
+ "svdFile": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
+ "runToEntryPoint": "main",
+ // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
+ // Also works fine for flash binaries
+ "overrideLaunchCommands": [
+ "monitor reset init",
+ "load \"${command:raspberry-pi-pico.launchTargetPath}\""
+ ],
+ "openOCDLaunchCommands": [
+ "adapter speed 5000"
+ ]
+ },
+ {
+ "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
+ "cwd": "${workspaceRoot}",
+ "executable": "${command:raspberry-pi-pico.launchTargetPath}",
+ "request": "launch",
+ "type": "cortex-debug",
+ "servertype": "external",
+ "gdbTarget": "localhost:3333",
+ "gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
+ "device": "${command:raspberry-pi-pico.getChipUppercase}",
+ "svdFile": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
+ "runToEntryPoint": "main",
+ // Give restart the same functionality as runToEntryPoint - main
+ "postRestartCommands": [
+ "break main",
+ "continue"
+ ]
+ },
+ {
+ "name": "Pico Debug (C++ Debugger)",
+ "type": "cppdbg",
+ "request": "launch",
+ "cwd": "${workspaceRoot}",
+ "program": "${command:raspberry-pi-pico.launchTargetPath}",
+ "MIMode": "gdb",
+ "miDebuggerPath": "${command:raspberry-pi-pico.getGDBPath}",
+ "miDebuggerServerAddress": "localhost:3333",
+ "debugServerPath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+ "debugServerArgs": "-f interface/cmsis-dap.cfg -f target/${command:raspberry-pi-pico.getTarget}.cfg -c \"adapter speed 5000\"",
+ "serverStarted": "Listening on port .* for gdb connections",
+ "filterStderr": true,
+ "hardwareBreakpoints": {
+ "require": true,
+ "limit": 4
+ },
+ "preLaunchTask": "Flash",
+ "svdPath": "${userHome}/.pico-sdk/sdk/2.0.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd"
+ },
+ ]
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 0af9a31..d9131e9 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,58 +1,58 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "label": "Compile Project",
- "type": "process",
- "isBuildCommand": true,
- "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja",
- "args": ["-C", "${workspaceFolder}/build"],
- "group": "build",
- "presentation": {
- "reveal": "always",
- "panel": "dedicated"
- },
- "problemMatcher": "$gcc",
- "windows": {
- "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe"
- }
- },
- {
- "label": "Run Project",
- "type": "process",
- "command": "${env:HOME}/.pico-sdk/picotool/2.0.0/picotool/picotool",
- "args": [
- "load",
- "${command:raspberry-pi-pico.launchTargetPath}",
- "-fx"
- ],
- "presentation": {
- "reveal": "always",
- "panel": "dedicated"
- },
- "problemMatcher": [],
- "windows": {
- "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.0.0/picotool/picotool.exe"
- }
- },
- {
- "label": "Flash",
- "type": "process",
- "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
- "args": [
- "-s",
- "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
- "-f",
- "interface/cmsis-dap.cfg",
- "-f",
- "target/${command:raspberry-pi-pico.getTarget}.cfg",
- "-c",
- "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit"
- ],
- "problemMatcher": [],
- "windows": {
- "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
- }
- }
- ]
-}
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Compile Project",
+ "type": "process",
+ "isBuildCommand": true,
+ "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja",
+ "args": ["-C", "${workspaceFolder}/build"],
+ "group": "build",
+ "presentation": {
+ "reveal": "always",
+ "panel": "dedicated"
+ },
+ "problemMatcher": "$gcc",
+ "windows": {
+ "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe"
+ }
+ },
+ {
+ "label": "Run Project",
+ "type": "process",
+ "command": "${env:HOME}/.pico-sdk/picotool/2.0.0/picotool/picotool",
+ "args": [
+ "load",
+ "${command:raspberry-pi-pico.launchTargetPath}",
+ "-fx"
+ ],
+ "presentation": {
+ "reveal": "always",
+ "panel": "dedicated"
+ },
+ "problemMatcher": [],
+ "windows": {
+ "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.0.0/picotool/picotool.exe"
+ }
+ },
+ {
+ "label": "Flash",
+ "type": "process",
+ "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+ "args": [
+ "-s",
+ "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
+ "-f",
+ "interface/cmsis-dap.cfg",
+ "-f",
+ "target/${command:raspberry-pi-pico.getTarget}.cfg",
+ "-c",
+ "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit"
+ ],
+ "problemMatcher": [],
+ "windows": {
+ "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+ }
+ }
+ ]
+}
diff --git a/LICENSE_FIRMWARE.md b/LICENSE_FIRMWARE.md
index f9b1031..dd7d477 100644
--- a/LICENSE_FIRMWARE.md
+++ b/LICENSE_FIRMWARE.md
@@ -1,21 +1,21 @@
-MIT License
-
-Copyright (c) 2024 Troy Schrapel
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+MIT License
+
+Copyright (c) 2024 Troy Schrapel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/LICENSE_HARDWARE.md b/LICENSE_HARDWARE.md
index ddc4823..43dc091 100644
--- a/LICENSE_HARDWARE.md
+++ b/LICENSE_HARDWARE.md
@@ -1,265 +1,265 @@
-CERN Open Hardware Licence Version 2 - Strongly Reciprocal
-
-
-Preamble
-
-CERN has developed this licence to promote collaboration among hardware
-designers and to provide a legal tool which supports the freedom to use,
-study, modify, share and distribute hardware designs and products based on
-those designs. Version 2 of the CERN Open Hardware Licence comes in three
-variants: CERN-OHL-P (permissive); and two reciprocal licences: CERN-OHL-W
-(weakly reciprocal) and this licence, CERN-OHL-S (strongly reciprocal).
-
-The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
-unmodified form only.
-
-Use of this Licence does not imply any endorsement by CERN of any Licensor or
-their designs nor does it imply any involvement by CERN in their development.
-
-
-1 Definitions
-
- 1.1 'Licence' means this CERN-OHL-S.
-
- 1.2 'Compatible Licence' means
-
- a) any earlier version of the CERN Open Hardware licence, or
-
- b) any version of the CERN-OHL-S, or
-
- c) any licence which permits You to treat the Source to which it
- applies as licensed under CERN-OHL-S provided that on Conveyance of
- any such Source, or any associated Product You treat the Source in
- question as being licensed under CERN-OHL-S.
-
- 1.3 'Source' means information such as design materials or digital code
- which can be applied to Make or test a Product or to prepare a Product
- for use, Conveyance or sale, regardless of its medium or how it is
- expressed. It may include Notices.
-
- 1.4 'Covered Source' means Source that is explicitly made available under
- this Licence.
-
- 1.5 'Product' means any device, component, work or physical object, whether
- in finished or intermediate form, arising from the use, application or
- processing of Covered Source.
-
- 1.6 'Make' means to create or configure something, whether by manufacture,
- assembly, compiling, loading or applying Covered Source or another
- Product or otherwise.
-
- 1.7 'Available Component' means any part, sub-assembly, library or code
- which:
-
- a) is licensed to You as Complete Source under a Compatible Licence; or
-
- b) is available, at the time a Product or the Source containing it is
- first Conveyed, to You and any other prospective licensees
-
- i) as a physical part with sufficient rights and information
- (including any configuration and programming files and
- information about its characteristics and interfaces) to enable
- it either to be Made itself, or to be sourced and used to Make
- the Product; or
- ii) as part of the normal distribution of a tool used to design or
- Make the Product.
-
- 1.8 'Complete Source' means the set of all Source necessary to Make a
- Product, in the preferred form for making modifications, including
- necessary installation and interfacing information both for the Product,
- and for any included Available Components. If the format is
- proprietary, it must also be made available in a format (if the
- proprietary tool can create it) which is viewable with a tool available
- to potential licensees and licensed under a licence approved by the Free
- Software Foundation or the Open Source Initiative. Complete Source need
- not include the Source of any Available Component, provided that You
- include in the Complete Source sufficient information to enable a
- recipient to Make or source and use the Available Component to Make the
- Product.
-
- 1.9 'Source Location' means a location where a Licensor has placed Covered
- Source, and which that Licensor reasonably believes will remain easily
- accessible for at least three years for anyone to obtain a digital copy.
-
- 1.10 'Notice' means copyright, acknowledgement and trademark notices, Source
- Location references, modification notices (subsection 3.3(b)) and all
- notices that refer to this Licence and to the disclaimer of warranties
- that are included in the Covered Source.
-
- 1.11 'Licensee' or 'You' means any person exercising rights under this
- Licence.
-
- 1.12 'Licensor' means a natural or legal person who creates or modifies
- Covered Source. A person may be a Licensee and a Licensor at the same
- time.
-
- 1.13 'Convey' means to communicate to the public or distribute.
-
-
-2 Applicability
-
- 2.1 This Licence governs the use, copying, modification, Conveying of
- Covered Source and Products, and the Making of Products. By exercising
- any right granted under this Licence, You irrevocably accept these terms
- and conditions.
-
- 2.2 This Licence is granted by the Licensor directly to You, and shall apply
- worldwide and without limitation in time.
-
- 2.3 You shall not attempt to restrict by contract or otherwise the rights
- granted under this Licence to other Licensees.
-
- 2.4 This Licence is not intended to restrict fair use, fair dealing, or any
- other similar right.
-
-
-3 Copying, Modifying and Conveying Covered Source
-
- 3.1 You may copy and Convey verbatim copies of Covered Source, in any
- medium, provided You retain all Notices.
-
- 3.2 You may modify Covered Source, other than Notices, provided that You
- irrevocably undertake to make that modified Covered Source available
- from a Source Location should You Convey a Product in circumstances
- where the recipient does not otherwise receive a copy of the modified
- Covered Source. In each case subsection 3.3 shall apply.
-
- You may only delete Notices if they are no longer applicable to the
- corresponding Covered Source as modified by You and You may add
- additional Notices applicable to Your modifications. Including Covered
- Source in a larger work is modifying the Covered Source, and the larger
- work becomes modified Covered Source.
-
- 3.3 You may Convey modified Covered Source (with the effect that You shall
- also become a Licensor) provided that You:
-
- a) retain Notices as required in subsection 3.2;
-
- b) add a Notice to the modified Covered Source stating that You have
- modified it, with the date and brief description of how You have
- modified it;
-
- c) add a Source Location Notice for the modified Covered Source if You
- Convey in circumstances where the recipient does not otherwise
- receive a copy of the modified Covered Source; and
-
- d) license the modified Covered Source under the terms and conditions
- of this Licence (or, as set out in subsection 8.3, a later version,
- if permitted by the licence of the original Covered Source). Such
- modified Covered Source must be licensed as a whole, but excluding
- Available Components contained in it, which remain licensed under
- their own applicable licences.
-
-
-4 Making and Conveying Products
-
-You may Make Products, and/or Convey them, provided that You either provide
-each recipient with a copy of the Complete Source or ensure that each
-recipient is notified of the Source Location of the Complete Source. That
-Complete Source is Covered Source, and You must accordingly satisfy Your
-obligations set out in subsection 3.3. If specified in a Notice, the Product
-must visibly and securely display the Source Location on it or its packaging
-or documentation in the manner specified in that Notice.
-
-
-5 Research and Development
-
-You may Convey Covered Source, modified Covered Source or Products to a legal
-entity carrying out development, testing or quality assurance work on Your
-behalf provided that the work is performed on terms which prevent the entity
-from both using the Source or Products for its own internal purposes and
-Conveying the Source or Products or any modifications to them to any person
-other than You. Any modifications made by the entity shall be deemed to be
-made by You pursuant to subsection 3.2.
-
-
-6 DISCLAIMER AND LIABILITY
-
- 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products are
- provided 'as is' and any express or implied warranties, including, but
- not limited to, implied warranties of merchantability, of satisfactory
- quality, non-infringement of third party rights, and fitness for a
- particular purpose or use are disclaimed in respect of any Source or
- Product to the maximum extent permitted by law. The Licensor makes no
- representation that any Source or Product does not or will not infringe
- any patent, copyright, trade secret or other proprietary right. The
- entire risk as to the use, quality, and performance of any Source or
- Product shall be with You and not the Licensor. This disclaimer of
- warranty is an essential part of this Licence and a condition for the
- grant of any rights granted under this Licence.
-
- 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to the
- maximum extent permitted by law, have no liability for direct, indirect,
- special, incidental, consequential, exemplary, punitive or other damages
- of any character including, without limitation, procurement of
- substitute goods or services, loss of use, data or profits, or business
- interruption, however caused and on any theory of contract, warranty,
- tort (including negligence), product liability or otherwise, arising in
- any way in relation to the Covered Source, modified Covered Source
- and/or the Making or Conveyance of a Product, even if advised of the
- possibility of such damages, and You shall hold the Licensor(s) free and
- harmless from any liability, costs, damages, fees and expenses,
- including claims by third parties, in relation to such use.
-
-
-7 Patents
-
- 7.1 Subject to the terms and conditions of this Licence, each Licensor
- hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
- royalty-free, irrevocable (except as stated in subsections 7.2 and 8.4)
- patent licence to Make, have Made, use, offer to sell, sell, import, and
- otherwise transfer the Covered Source and Products, where such licence
- applies only to those patent claims licensable by such Licensor that are
- necessarily infringed by exercising rights under the Covered Source as
- Conveyed by that Licensor.
-
- 7.2 If You institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Covered
- Source or a Product constitutes direct or contributory patent
- infringement, or You seek any declaration that a patent licensed to You
- under this Licence is invalid or unenforceable then any rights granted
- to You under this Licence shall terminate as of the date such process is
- initiated.
-
-
-8 General
-
- 8.1 If any provisions of this Licence are or subsequently become invalid or
- unenforceable for any reason, the remaining provisions shall remain
- effective.
-
- 8.2 You shall not use any of the name (including acronyms and
- abbreviations), image, or logo by which the Licensor or CERN is known,
- except where needed to comply with section 3, or where the use is
- otherwise allowed by law. Any such permitted use shall be factual and
- shall not be made so as to suggest any kind of endorsement or
- implication of involvement by the Licensor or its personnel.
-
- 8.3 CERN may publish updated versions and variants of this Licence which it
- considers to be in the spirit of this version, but may differ in detail
- to address new problems or concerns. New versions will be published with
- a unique version number and a variant identifier specifying the variant.
- If the Licensor has specified that a given variant applies to the
- Covered Source without specifying a version, You may treat that Covered
- Source as being released under any version of the CERN-OHL with that
- variant. If no variant is specified, the Covered Source shall be treated
- as being released under CERN-OHL-S. The Licensor may also specify that
- the Covered Source is subject to a specific version of the CERN-OHL or
- any later version in which case You may apply this or any later version
- of CERN-OHL with the same variant identifier published by CERN.
-
- 8.4 This Licence shall terminate with immediate effect if You fail to comply
- with any of its terms and conditions.
-
- 8.5 However, if You cease all breaches of this Licence, then Your Licence
- from any Licensor is reinstated unless such Licensor has terminated this
- Licence by giving You, while You remain in breach, a notice specifying
- the breach and requiring You to cure it within 30 days, and You have
- failed to come into compliance in all material respects by the end of
- the 30 day period. Should You repeat the breach after receipt of a cure
- notice and subsequent reinstatement, this Licence will terminate
- immediately and permanently. Section 6 shall continue to apply after any
- termination.
-
- 8.6 This Licence shall not be enforceable except by a Licensor acting as
- such, and third party beneficiary rights are specifically excluded.
+CERN Open Hardware Licence Version 2 - Strongly Reciprocal
+
+
+Preamble
+
+CERN has developed this licence to promote collaboration among hardware
+designers and to provide a legal tool which supports the freedom to use,
+study, modify, share and distribute hardware designs and products based on
+those designs. Version 2 of the CERN Open Hardware Licence comes in three
+variants: CERN-OHL-P (permissive); and two reciprocal licences: CERN-OHL-W
+(weakly reciprocal) and this licence, CERN-OHL-S (strongly reciprocal).
+
+The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
+unmodified form only.
+
+Use of this Licence does not imply any endorsement by CERN of any Licensor or
+their designs nor does it imply any involvement by CERN in their development.
+
+
+1 Definitions
+
+ 1.1 'Licence' means this CERN-OHL-S.
+
+ 1.2 'Compatible Licence' means
+
+ a) any earlier version of the CERN Open Hardware licence, or
+
+ b) any version of the CERN-OHL-S, or
+
+ c) any licence which permits You to treat the Source to which it
+ applies as licensed under CERN-OHL-S provided that on Conveyance of
+ any such Source, or any associated Product You treat the Source in
+ question as being licensed under CERN-OHL-S.
+
+ 1.3 'Source' means information such as design materials or digital code
+ which can be applied to Make or test a Product or to prepare a Product
+ for use, Conveyance or sale, regardless of its medium or how it is
+ expressed. It may include Notices.
+
+ 1.4 'Covered Source' means Source that is explicitly made available under
+ this Licence.
+
+ 1.5 'Product' means any device, component, work or physical object, whether
+ in finished or intermediate form, arising from the use, application or
+ processing of Covered Source.
+
+ 1.6 'Make' means to create or configure something, whether by manufacture,
+ assembly, compiling, loading or applying Covered Source or another
+ Product or otherwise.
+
+ 1.7 'Available Component' means any part, sub-assembly, library or code
+ which:
+
+ a) is licensed to You as Complete Source under a Compatible Licence; or
+
+ b) is available, at the time a Product or the Source containing it is
+ first Conveyed, to You and any other prospective licensees
+
+ i) as a physical part with sufficient rights and information
+ (including any configuration and programming files and
+ information about its characteristics and interfaces) to enable
+ it either to be Made itself, or to be sourced and used to Make
+ the Product; or
+ ii) as part of the normal distribution of a tool used to design or
+ Make the Product.
+
+ 1.8 'Complete Source' means the set of all Source necessary to Make a
+ Product, in the preferred form for making modifications, including
+ necessary installation and interfacing information both for the Product,
+ and for any included Available Components. If the format is
+ proprietary, it must also be made available in a format (if the
+ proprietary tool can create it) which is viewable with a tool available
+ to potential licensees and licensed under a licence approved by the Free
+ Software Foundation or the Open Source Initiative. Complete Source need
+ not include the Source of any Available Component, provided that You
+ include in the Complete Source sufficient information to enable a
+ recipient to Make or source and use the Available Component to Make the
+ Product.
+
+ 1.9 'Source Location' means a location where a Licensor has placed Covered
+ Source, and which that Licensor reasonably believes will remain easily
+ accessible for at least three years for anyone to obtain a digital copy.
+
+ 1.10 'Notice' means copyright, acknowledgement and trademark notices, Source
+ Location references, modification notices (subsection 3.3(b)) and all
+ notices that refer to this Licence and to the disclaimer of warranties
+ that are included in the Covered Source.
+
+ 1.11 'Licensee' or 'You' means any person exercising rights under this
+ Licence.
+
+ 1.12 'Licensor' means a natural or legal person who creates or modifies
+ Covered Source. A person may be a Licensee and a Licensor at the same
+ time.
+
+ 1.13 'Convey' means to communicate to the public or distribute.
+
+
+2 Applicability
+
+ 2.1 This Licence governs the use, copying, modification, Conveying of
+ Covered Source and Products, and the Making of Products. By exercising
+ any right granted under this Licence, You irrevocably accept these terms
+ and conditions.
+
+ 2.2 This Licence is granted by the Licensor directly to You, and shall apply
+ worldwide and without limitation in time.
+
+ 2.3 You shall not attempt to restrict by contract or otherwise the rights
+ granted under this Licence to other Licensees.
+
+ 2.4 This Licence is not intended to restrict fair use, fair dealing, or any
+ other similar right.
+
+
+3 Copying, Modifying and Conveying Covered Source
+
+ 3.1 You may copy and Convey verbatim copies of Covered Source, in any
+ medium, provided You retain all Notices.
+
+ 3.2 You may modify Covered Source, other than Notices, provided that You
+ irrevocably undertake to make that modified Covered Source available
+ from a Source Location should You Convey a Product in circumstances
+ where the recipient does not otherwise receive a copy of the modified
+ Covered Source. In each case subsection 3.3 shall apply.
+
+ You may only delete Notices if they are no longer applicable to the
+ corresponding Covered Source as modified by You and You may add
+ additional Notices applicable to Your modifications. Including Covered
+ Source in a larger work is modifying the Covered Source, and the larger
+ work becomes modified Covered Source.
+
+ 3.3 You may Convey modified Covered Source (with the effect that You shall
+ also become a Licensor) provided that You:
+
+ a) retain Notices as required in subsection 3.2;
+
+ b) add a Notice to the modified Covered Source stating that You have
+ modified it, with the date and brief description of how You have
+ modified it;
+
+ c) add a Source Location Notice for the modified Covered Source if You
+ Convey in circumstances where the recipient does not otherwise
+ receive a copy of the modified Covered Source; and
+
+ d) license the modified Covered Source under the terms and conditions
+ of this Licence (or, as set out in subsection 8.3, a later version,
+ if permitted by the licence of the original Covered Source). Such
+ modified Covered Source must be licensed as a whole, but excluding
+ Available Components contained in it, which remain licensed under
+ their own applicable licences.
+
+
+4 Making and Conveying Products
+
+You may Make Products, and/or Convey them, provided that You either provide
+each recipient with a copy of the Complete Source or ensure that each
+recipient is notified of the Source Location of the Complete Source. That
+Complete Source is Covered Source, and You must accordingly satisfy Your
+obligations set out in subsection 3.3. If specified in a Notice, the Product
+must visibly and securely display the Source Location on it or its packaging
+or documentation in the manner specified in that Notice.
+
+
+5 Research and Development
+
+You may Convey Covered Source, modified Covered Source or Products to a legal
+entity carrying out development, testing or quality assurance work on Your
+behalf provided that the work is performed on terms which prevent the entity
+from both using the Source or Products for its own internal purposes and
+Conveying the Source or Products or any modifications to them to any person
+other than You. Any modifications made by the entity shall be deemed to be
+made by You pursuant to subsection 3.2.
+
+
+6 DISCLAIMER AND LIABILITY
+
+ 6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products are
+ provided 'as is' and any express or implied warranties, including, but
+ not limited to, implied warranties of merchantability, of satisfactory
+ quality, non-infringement of third party rights, and fitness for a
+ particular purpose or use are disclaimed in respect of any Source or
+ Product to the maximum extent permitted by law. The Licensor makes no
+ representation that any Source or Product does not or will not infringe
+ any patent, copyright, trade secret or other proprietary right. The
+ entire risk as to the use, quality, and performance of any Source or
+ Product shall be with You and not the Licensor. This disclaimer of
+ warranty is an essential part of this Licence and a condition for the
+ grant of any rights granted under this Licence.
+
+ 6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to the
+ maximum extent permitted by law, have no liability for direct, indirect,
+ special, incidental, consequential, exemplary, punitive or other damages
+ of any character including, without limitation, procurement of
+ substitute goods or services, loss of use, data or profits, or business
+ interruption, however caused and on any theory of contract, warranty,
+ tort (including negligence), product liability or otherwise, arising in
+ any way in relation to the Covered Source, modified Covered Source
+ and/or the Making or Conveyance of a Product, even if advised of the
+ possibility of such damages, and You shall hold the Licensor(s) free and
+ harmless from any liability, costs, damages, fees and expenses,
+ including claims by third parties, in relation to such use.
+
+
+7 Patents
+
+ 7.1 Subject to the terms and conditions of this Licence, each Licensor
+ hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
+ royalty-free, irrevocable (except as stated in subsections 7.2 and 8.4)
+ patent licence to Make, have Made, use, offer to sell, sell, import, and
+ otherwise transfer the Covered Source and Products, where such licence
+ applies only to those patent claims licensable by such Licensor that are
+ necessarily infringed by exercising rights under the Covered Source as
+ Conveyed by that Licensor.
+
+ 7.2 If You institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Covered
+ Source or a Product constitutes direct or contributory patent
+ infringement, or You seek any declaration that a patent licensed to You
+ under this Licence is invalid or unenforceable then any rights granted
+ to You under this Licence shall terminate as of the date such process is
+ initiated.
+
+
+8 General
+
+ 8.1 If any provisions of this Licence are or subsequently become invalid or
+ unenforceable for any reason, the remaining provisions shall remain
+ effective.
+
+ 8.2 You shall not use any of the name (including acronyms and
+ abbreviations), image, or logo by which the Licensor or CERN is known,
+ except where needed to comply with section 3, or where the use is
+ otherwise allowed by law. Any such permitted use shall be factual and
+ shall not be made so as to suggest any kind of endorsement or
+ implication of involvement by the Licensor or its personnel.
+
+ 8.3 CERN may publish updated versions and variants of this Licence which it
+ considers to be in the spirit of this version, but may differ in detail
+ to address new problems or concerns. New versions will be published with
+ a unique version number and a variant identifier specifying the variant.
+ If the Licensor has specified that a given variant applies to the
+ Covered Source without specifying a version, You may treat that Covered
+ Source as being released under any version of the CERN-OHL with that
+ variant. If no variant is specified, the Covered Source shall be treated
+ as being released under CERN-OHL-S. The Licensor may also specify that
+ the Covered Source is subject to a specific version of the CERN-OHL or
+ any later version in which case You may apply this or any later version
+ of CERN-OHL with the same variant identifier published by CERN.
+
+ 8.4 This Licence shall terminate with immediate effect if You fail to comply
+ with any of its terms and conditions.
+
+ 8.5 However, if You cease all breaches of this Licence, then Your Licence
+ from any Licensor is reinstated unless such Licensor has terminated this
+ Licence by giving You, while You remain in breach, a notice specifying
+ the breach and requiring You to cure it within 30 days, and You have
+ failed to come into compliance in all material respects by the end of
+ the 30 day period. Should You repeat the breach after receipt of a cure
+ notice and subsequent reinstatement, this Licence will terminate
+ immediately and permanently. Section 6 shall continue to apply after any
+ termination.
+
+ 8.6 This Licence shall not be enforceable except by a Licensor acting as
+ such, and third party beneficiary rights are specifically excluded.
diff --git a/README.md b/README.md
index 239b505..bd1f6ff 100644
--- a/README.md
+++ b/README.md
@@ -1,138 +1,138 @@
-# PICO9918
-
-A drop-in replacement for a classic TMS9918A VDP using a Raspberry Pi Pico.
-
-The TMS9918A emulation is handled by my [vrEmuTms9918 library](https://github.com/visrealm/vrEmuTms9918) which is included as a submodule here
-
-## Supported devices
-
-This is a list of devices the PICO9918 has been tested and confirmed to work on.
-
-* [Texas Instruments TI-99/4A](https://en.wikipedia.org/wiki/TI-99/4A) (NTSC and PAL)
-* [Texas Instruments TI-99/4QI](http://www.mainbyte.com/ti99/computers/ti99qi.html)
-* [Dan Werner TI-99/22](https://github.com/danwerner21/TI99_22)
-* [ColecoVision](https://en.wikipedia.org/wiki/ColecoVision) (NTSC and PAL)
-* [Coleco ADAM](https://en.wikipedia.org/wiki/Coleco_Adam) (NTSC)
-* [AtariBits CV-NUC+](https://ataribits.weebly.com/cv-nuc.html)
-* [Memotech MTX500](https://en.wikipedia.org/wiki/Memotech_MTX)
-* [MSX](https://en.wikipedia.org/wiki/MSX)
- * [Casio MX-10](https://www.msx.org/wiki/Casio_MX-10)
- * [Casio PV-7](https://www.msx.org/wiki/Casio_PV-7)
- * [Casio PV-16](https://www.msx.org/wiki/Casio_PV-16)
- * [Gradiente Expert XP-800](https://www.msx.org/wiki/Gradiente_Expert_XP-800)
- * [Sharp HB-8000](https://www.msx.org/wiki/Sharp_HB-8000)
- * [Sony HB-75](https://www.msx.org/wiki/Sony_HB-75)
- * [Toshiba HX-21](https://www.msx.org/wiki/Toshiba_HX-21)
- * [Yamaha YIS-503](https://www.msx.org/wiki/Yamaha_YIS-503)
-* [NABU Personal Computer](https://en.wikipedia.org/wiki/NABU_Network)
-* [Powertran Cortex](http://powertrancortex.com/)
-* [Sega SG-1000 / SC-3000](https://en.wikipedia.org/wiki/SG-1000#SC-3000)
-* [Sord M5](https://en.wikipedia.org/wiki/Sord_M5)
-* [Tomy Pyūta / Tomy Tutor](https://en.wikipedia.org/wiki/Tomy_Tutor)
-* [Tomy Pyūta Jr](http://videogamekraken.com/pyuta-jr-by-tomy)
-
-Homebrews:
-
-* Troy Schrapel's [HBC-56](https://github.com/visrealm/hbc-56)
-* Stuart Connor's [TM990](http://www.stuartconner.me.uk/tm990/tm990.htm)
-* John Winans' [Z80-Retro](https://github.com/Z80-Retro)
-
-If you have tested the PICO9918 on any other device, please let me know and I'll happily update this list. :)
-
-### Unsupported devices
-
-So far, there aren't any.
-
-# F18A compatibility
-
-Work is being done to add F18A compatibility to the PICO9918. The video below was captured directly from the PICO9918 VGA output running various F18A demos on a TI-99/4A.
-
-[](https://youtu.be/TabTIPL1xQY)
-
-Pre-release firmware for F18A compatibility mode is available in [Releases](https://github.com/visrealm/pico9918/releases/tag/v0.4.1-f18a-preview1).
-
-## Purchasing options
-
-Fully assembled and tested PICO9918 v1.1s are available on my Tindie store:
-
-
-
-Also (more convenient for North America)
-
-
-
-
-## Hardware
-
-There are two main variants of the hardware.
-
-### v1.1 (formerly v1.0 and v0.4)
-
-PICO9918 v1.1 is the single board version which doesn't require a piggy-backed Pi Pico. This is the version you can currently buy pre-assembled from Tindie and ArcadeShopper.
-
-

-
-### v0.3
-
-v0.3 is relatively cheap and easy to build, schematic and gerbers are available. This version makes use of an external Pi Pico module piggy-backed onto the PICO9918 PCB.
-
-
-
-
-
-### Schematics
-
-Schematics and Gerbers are available in [/pcb](pcb)
-
-## Firmware
-
-If you're not interested in building the firmware yourself, you'll find the latest firmware in the [Releases](https://github.com/visrealm/pico9918/releases).
-
-To install, just hold the 'BOOTSEL' (or 'BOOT') button while plugging the Pico into a PC, then drag the pico9918.uf2 file on to the new USB drive which should have the volume label RPI-RP2. The Pico will restart (and disconnect) automatically.
-
-## Development environment
-
-To set up your development environment for the Raspberry Pi Pico, follow the [Raspberry Pi C/C++ SDK Setup](https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html) instructions. The latest PICO9918 source can be configured and built using the official [Raspberry Pi Pico VSCode plugin](https://github.com/raspberrypi/pico-vscode).
-
-#### Windows
-
-The build system expects `python3` to be available. If you have installed Python 3 outside of the Microsoft Store, you may need to alias your Python executable.
-
-You can do this from an elevated (Administator) command prompt in your python directory e.g. `C:\Program Files\Python310\` by creating a symlink with the command: `mklink python3.exe python.exe`.
-
-The custom python build tools are used to convert binary data (images) into code. These also require the [pillow](https://pypi.org/project/pillow/) library - ([Installation instructions for pillow](https://pillow.readthedocs.io/en/latest/installation/basic-installation.html))
-
-## Discussion
-
-For all the latest news and discussion on the PICO9918, you can follow [this AtariAge thread](https://forums.atariage.com/topic/367656-introducing-the-pico9918-a-tms9918a-drop-in-replacement-powered-by-a-pi-pico/)
-
-## Videos
-
-Initial "raw" videos recorded in the moments following the first boot on my TI-99/4A.
-
-These videos are showing the v0.2 hardware with an external Pi Pico providing the required GROMCLK signal to the TI-99. This signal has been added to v0.3. I'm still waiting on v0.3 boards to arrive.
-
-### It freaking works!
-[](https://youtu.be/Ri09dCjWxGE)
-
-### Don't mess with Texas!
-[](https://youtu.be/ljNRFKbOGJs)
-
-### 80 column mode
-[](https://youtu.be/qdCapu0CVJ8)
-
-And now v0.4 - the single board version:
-
-### v0.4 prototype working!
-[](https://youtu.be/KSbJnAwclQw)
-
-### F18A mode development preview
-[](https://youtu.be/TabTIPL1xQY)
-
-## Licensing
-
-### Hardware
-The hardware design files in this repository are licensed under the CERN-OHL-S. See [LICENSE_HARDWARE.md](LICENSE_HARDWARE.md) for details.
-
-### Firmware
-The firmware code in this repository is licensed under the MIT License. See [LICENSE_FIRMWARE.md](LICENSE_FIRMWARE.md) for details.
+# PICO9918
+
+A drop-in replacement for a classic TMS9918A VDP using a Raspberry Pi Pico.
+
+The TMS9918A emulation is handled by my [vrEmuTms9918 library](https://github.com/visrealm/vrEmuTms9918) which is included as a submodule here
+
+## Supported devices
+
+This is a list of devices the PICO9918 has been tested and confirmed to work on.
+
+* [Texas Instruments TI-99/4A](https://en.wikipedia.org/wiki/TI-99/4A) (NTSC and PAL)
+* [Texas Instruments TI-99/4QI](http://www.mainbyte.com/ti99/computers/ti99qi.html)
+* [Dan Werner TI-99/22](https://github.com/danwerner21/TI99_22)
+* [ColecoVision](https://en.wikipedia.org/wiki/ColecoVision) (NTSC and PAL)
+* [Coleco ADAM](https://en.wikipedia.org/wiki/Coleco_Adam) (NTSC)
+* [AtariBits CV-NUC+](https://ataribits.weebly.com/cv-nuc.html)
+* [Memotech MTX500](https://en.wikipedia.org/wiki/Memotech_MTX)
+* [MSX](https://en.wikipedia.org/wiki/MSX)
+ * [Casio MX-10](https://www.msx.org/wiki/Casio_MX-10)
+ * [Casio PV-7](https://www.msx.org/wiki/Casio_PV-7)
+ * [Casio PV-16](https://www.msx.org/wiki/Casio_PV-16)
+ * [Gradiente Expert XP-800](https://www.msx.org/wiki/Gradiente_Expert_XP-800)
+ * [Sharp HB-8000](https://www.msx.org/wiki/Sharp_HB-8000)
+ * [Sony HB-75](https://www.msx.org/wiki/Sony_HB-75)
+ * [Toshiba HX-21](https://www.msx.org/wiki/Toshiba_HX-21)
+ * [Yamaha YIS-503](https://www.msx.org/wiki/Yamaha_YIS-503)
+* [NABU Personal Computer](https://en.wikipedia.org/wiki/NABU_Network)
+* [Powertran Cortex](http://powertrancortex.com/)
+* [Sega SG-1000 / SC-3000](https://en.wikipedia.org/wiki/SG-1000#SC-3000)
+* [Sord M5](https://en.wikipedia.org/wiki/Sord_M5)
+* [Tomy Pyūta / Tomy Tutor](https://en.wikipedia.org/wiki/Tomy_Tutor)
+* [Tomy Pyūta Jr](http://videogamekraken.com/pyuta-jr-by-tomy)
+
+Homebrews:
+
+* Troy Schrapel's [HBC-56](https://github.com/visrealm/hbc-56)
+* Stuart Connor's [TM990](http://www.stuartconner.me.uk/tm990/tm990.htm)
+* John Winans' [Z80-Retro](https://github.com/Z80-Retro)
+
+If you have tested the PICO9918 on any other device, please let me know and I'll happily update this list. :)
+
+### Unsupported devices
+
+So far, there aren't any.
+
+# F18A compatibility
+
+Work is being done to add F18A compatibility to the PICO9918. The video below was captured directly from the PICO9918 VGA output running various F18A demos on a TI-99/4A.
+
+[](https://youtu.be/TabTIPL1xQY)
+
+Pre-release firmware for F18A compatibility mode is available in [Releases](https://github.com/visrealm/pico9918/releases/tag/v0.4.1-f18a-preview1).
+
+## Purchasing options
+
+Fully assembled and tested PICO9918 v1.1s are available on my Tindie store:
+
+
+
+Also (more convenient for North America)
+
+
+
+
+## Hardware
+
+There are two main variants of the hardware.
+
+### v1.1 (formerly v1.0 and v0.4)
+
+PICO9918 v1.1 is the single board version which doesn't require a piggy-backed Pi Pico. This is the version you can currently buy pre-assembled from Tindie and ArcadeShopper.
+
+
+
+### v0.3
+
+v0.3 is relatively cheap and easy to build, schematic and gerbers are available. This version makes use of an external Pi Pico module piggy-backed onto the PICO9918 PCB.
+
+
+
+
+
+### Schematics
+
+Schematics and Gerbers are available in [/pcb](pcb)
+
+## Firmware
+
+If you're not interested in building the firmware yourself, you'll find the latest firmware in the [Releases](https://github.com/visrealm/pico9918/releases).
+
+To install, just hold the 'BOOTSEL' (or 'BOOT') button while plugging the Pico into a PC, then drag the pico9918.uf2 file on to the new USB drive which should have the volume label RPI-RP2. The Pico will restart (and disconnect) automatically.
+
+## Development environment
+
+To set up your development environment for the Raspberry Pi Pico, follow the [Raspberry Pi C/C++ SDK Setup](https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html) instructions. The latest PICO9918 source can be configured and built using the official [Raspberry Pi Pico VSCode plugin](https://github.com/raspberrypi/pico-vscode).
+
+#### Windows
+
+The build system expects `python3` to be available. If you have installed Python 3 outside of the Microsoft Store, you may need to alias your Python executable.
+
+You can do this from an elevated (Administator) command prompt in your python directory e.g. `C:\Program Files\Python310\` by creating a symlink with the command: `mklink python3.exe python.exe`.
+
+The custom python build tools are used to convert binary data (images) into code. These also require the [pillow](https://pypi.org/project/pillow/) library - ([Installation instructions for pillow](https://pillow.readthedocs.io/en/latest/installation/basic-installation.html))
+
+## Discussion
+
+For all the latest news and discussion on the PICO9918, you can follow [this AtariAge thread](https://forums.atariage.com/topic/367656-introducing-the-pico9918-a-tms9918a-drop-in-replacement-powered-by-a-pi-pico/)
+
+## Videos
+
+Initial "raw" videos recorded in the moments following the first boot on my TI-99/4A.
+
+These videos are showing the v0.2 hardware with an external Pi Pico providing the required GROMCLK signal to the TI-99. This signal has been added to v0.3. I'm still waiting on v0.3 boards to arrive.
+
+### It freaking works!
+[](https://youtu.be/Ri09dCjWxGE)
+
+### Don't mess with Texas!
+[](https://youtu.be/ljNRFKbOGJs)
+
+### 80 column mode
+[](https://youtu.be/qdCapu0CVJ8)
+
+And now v0.4 - the single board version:
+
+### v0.4 prototype working!
+[](https://youtu.be/KSbJnAwclQw)
+
+### F18A mode development preview
+[](https://youtu.be/TabTIPL1xQY)
+
+## Licensing
+
+### Hardware
+The hardware design files in this repository are licensed under the CERN-OHL-S. See [LICENSE_HARDWARE.md](LICENSE_HARDWARE.md) for details.
+
+### Firmware
+The firmware code in this repository is licensed under the MIT License. See [LICENSE_FIRMWARE.md](LICENSE_FIRMWARE.md) for details.
diff --git a/img/logo.png b/img/logo.png
index 6abddf8..6259693 100644
Binary files a/img/logo.png and b/img/logo.png differ
diff --git a/nocut/README.md b/nocut/README.md
new file mode 100644
index 0000000..61bd795
--- /dev/null
+++ b/nocut/README.md
@@ -0,0 +1,29 @@
+## PICO9918 STLs
+
+Any 3D print or CAD files related to the PICO9918.
+
+## TI-99/4A no-cut mod
+
+The no-cut mod for the TI-99/4A consists of a custom PCB which replaces the original A/V DIN socket:
+
+
+
+The PCB is the supported by the printed enclosure:
+
+
+
+### PCB
+
+There are two versions of the PCB. One for v0.4 - v1.1 boards with JST connectors and another for v1.2+ boards with an FFC connector.
+
+See [pcb/](pcb/)
+
+To install the PCB, first remove the A/V connector from your TI-99/4A. Then install using the 3D printed spacer: [stl/pico9918-nocut-ti99-pcb-spacer.stl](stl/pico9918-nocut-ti99-pcb-spacer.stl)
+
+### Enclosure
+
+The vent holes on the black versus beige TI-99/4As are slightly different. For that reason, find either the beige or black version of the enclosure top and the generic enclosure bottom. They should be printed like this:
+
+
+
+See [stl/](stl/)
\ No newline at end of file
diff --git a/nocut/ti99/img/nocut-ti99-enclosure-1.jpg b/nocut/ti99/img/nocut-ti99-enclosure-1.jpg
new file mode 100644
index 0000000..8007321
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-enclosure-1.jpg differ
diff --git a/nocut/ti99/img/nocut-ti99-enclosure-2.jpg b/nocut/ti99/img/nocut-ti99-enclosure-2.jpg
new file mode 100644
index 0000000..33625ef
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-enclosure-2.jpg differ
diff --git a/nocut/ti99/img/nocut-ti99-enclosure-3.jpg b/nocut/ti99/img/nocut-ti99-enclosure-3.jpg
new file mode 100644
index 0000000..57f345b
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-enclosure-3.jpg differ
diff --git a/nocut/ti99/img/nocut-ti99-installed-bare.jpg b/nocut/ti99/img/nocut-ti99-installed-bare.jpg
new file mode 100644
index 0000000..0a3f9ab
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-installed-bare.jpg differ
diff --git a/nocut/ti99/img/nocut-ti99-installed-enclosure-2.jpeg b/nocut/ti99/img/nocut-ti99-installed-enclosure-2.jpeg
new file mode 100644
index 0000000..9039f16
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-installed-enclosure-2.jpeg differ
diff --git a/nocut/ti99/img/nocut-ti99-installed-enclosure.jpg b/nocut/ti99/img/nocut-ti99-installed-enclosure.jpg
new file mode 100644
index 0000000..5e2e3cc
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-installed-enclosure.jpg differ
diff --git a/nocut/ti99/img/nocut-ti99-installed-pcb.jpg b/nocut/ti99/img/nocut-ti99-installed-pcb.jpg
new file mode 100644
index 0000000..09e762f
Binary files /dev/null and b/nocut/ti99/img/nocut-ti99-installed-pcb.jpg differ
diff --git a/nocut/ti99/img/pico9918-nocut-ti99-build-plate.png b/nocut/ti99/img/pico9918-nocut-ti99-build-plate.png
new file mode 100644
index 0000000..68b1946
Binary files /dev/null and b/nocut/ti99/img/pico9918-nocut-ti99-build-plate.png differ
diff --git a/nocut/ti99/pcb/gerber-pico9918-nocut-ti994a-ffc-v1-2.zip b/nocut/ti99/pcb/gerber-pico9918-nocut-ti994a-ffc-v1-2.zip
new file mode 100644
index 0000000..15a5363
Binary files /dev/null and b/nocut/ti99/pcb/gerber-pico9918-nocut-ti994a-ffc-v1-2.zip differ
diff --git a/nocut/ti99/pcb/gerber-pico9918-nocut-ti994a-jst-v1-2.zip b/nocut/ti99/pcb/gerber-pico9918-nocut-ti994a-jst-v1-2.zip
new file mode 100644
index 0000000..6142b94
Binary files /dev/null and b/nocut/ti99/pcb/gerber-pico9918-nocut-ti994a-jst-v1-2.zip differ
diff --git a/nocut/ti99/stl/pico9918-nocut-ti99-beige-top.stl b/nocut/ti99/stl/pico9918-nocut-ti99-beige-top.stl
new file mode 100644
index 0000000..2f1b819
Binary files /dev/null and b/nocut/ti99/stl/pico9918-nocut-ti99-beige-top.stl differ
diff --git a/nocut/ti99/stl/pico9918-nocut-ti99-black-top.stl b/nocut/ti99/stl/pico9918-nocut-ti99-black-top.stl
new file mode 100644
index 0000000..f5806fd
Binary files /dev/null and b/nocut/ti99/stl/pico9918-nocut-ti99-black-top.stl differ
diff --git a/nocut/ti99/stl/pico9918-nocut-ti99-bottom.stl b/nocut/ti99/stl/pico9918-nocut-ti99-bottom.stl
new file mode 100644
index 0000000..b46e5d2
Binary files /dev/null and b/nocut/ti99/stl/pico9918-nocut-ti99-bottom.stl differ
diff --git a/nocut/ti99/stl/pico9918-nocut-ti99-pcb-spacer.stl b/nocut/ti99/stl/pico9918-nocut-ti99-pcb-spacer.stl
new file mode 100644
index 0000000..09f42e0
Binary files /dev/null and b/nocut/ti99/stl/pico9918-nocut-ti99-pcb-spacer.stl differ
diff --git a/pcb/README.md b/pcb/README.md
index 4e83cb8..e99abc3 100644
--- a/pcb/README.md
+++ b/pcb/README.md
@@ -1,83 +1,83 @@
-# PICO9918 PCBs
-
-Here you will find schematics and gerbers for all working revisions of the PICO9918. There are two main variants of the hardware. Due to a minor difference in RP2040 GPIO pinouts, firmware binaries aren't compatible between the two, however both are fully supported with latest firmware updates and separate firmware packages are provided for each.
-
-### DIY Piggybacked board (v0.3)
-
-This version is relatively cheap and easy to build and is powered by a piggybacked Pi Pico USB-C module. Recommended for a DIY project.
-
-
-
-See [PICO9918 v0.3](v0.3)
-
-### Fully integrated single board (v0.4 - v1.1+)
-
-From v0.4 the RP2040 has been integrated onto the PICO9918 PCB, making a much smaller small form factor. This revision has many small (0402) components and can be challenging (and more expensive) to build. They can be purchased too.
-
-
-
-# Revision history
-
-## [v1.1 (2024-09-12)](v1.1)
-
-### Changelog
-- Removed reset button.
-- Default CPUCLK and GROMCLK jumper pads to closed.
-- Minor positioning adjustments of bottom-side resistors to clear header pins.
-
-## [v1.0 (2024-08-01)](v1.0)
-
-First version available for sale on Tindie and ArcadeShopper
-
-
-
-### Changelog
-- Added reset button.
-- Moved CPUCLK and GROMCLK jumper pads to the top of the board.
-- Added SWD and SWC jumper pads to allow SWD (debugging)
-- Flipped top labels and graphics so it doesn't look upside-down.
-
-## [v0.4 (2024-07-16)](v0.4)
-
-This is the first fully-integrated single-board version and is powered by an RP2040 directly. This version was never released, however I hand-built 9 of them and sent most of the to various retro enthusiasts from AtariAge.
-
-
-
-### Changelog
-- Removed piggy backed Pi Pico module.
-- Added RP2040 and all of its dependencies.
-- Switched out resisitor networks for discrete resistors.
-- Shrinkified the package to something resembling the first version for sale (v1.0).
-- Updated VGA connector to 6p 1.25mm JST.
-
-## [v0.3 (2024-06-07)](v0.3)
-
-This is the first "public" version and is powered by a piggy-backed USB-C Pi Pico module. I have never produced or sold this version beyond the initial few prototypes.
-
-
-
-### Changelog
-- Switched to use USB-C Pi Pico module instead of genuine Pi Pico.
-- Added CPUCLK and GROMCLK.
-
-### PCB v0.3 Notes
-
-There are a number of 0 Ohm resistors (jumpers). You may need to omit the RST resistor. On some machines, the extra time is required to bootstrap the Pico. This will be changed to a soft reset on v0.4.
-
-### Raspberry Pi Pico Module
-
-Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29, a genuine Raspberry Pi Pico can't be used. v0.3 of the PCB is designed for the DWEII? RP2040 USB-C module which exposes these additional GPIOs. A future pico9918 revision will do without an external RP2040 board and use the RP2040 directly.
-
-Purchase links:
- * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X
- * https://www.aliexpress.com/item/1005007066733934.html
-
-I could reduce the VGA bit depth to 9-bit or 10-bit to allow the use of a genuine Raspberry Pi Pico board, but given the longer-term plan is to use the RP2040 directly, I've decided to go this way for the prototype.
-
-
-## What happened to v0.1 and v0.2?
-
-For the curious amongst you, v0.1 was the only version that wan't usable. The TMS9918A socket interface was 0.1" too narrow. Rookie error! Fortunately, I noticed within hours of ordering the PCBs, so ordered v0.2 long before v0.1 arrived.
-
+# PICO9918 PCBs
+
+Here you will find schematics and gerbers for all working revisions of the PICO9918. There are two main variants of the hardware. Due to a minor difference in RP2040 GPIO pinouts, firmware binaries aren't compatible between the two, however both are fully supported with latest firmware updates and separate firmware packages are provided for each.
+
+### DIY Piggybacked board (v0.3)
+
+This version is relatively cheap and easy to build and is powered by a piggybacked Pi Pico USB-C module. Recommended for a DIY project.
+
+
+
+See [PICO9918 v0.3](v0.3)
+
+### Fully integrated single board (v0.4 - v1.1+)
+
+From v0.4 the RP2040 has been integrated onto the PICO9918 PCB, making a much smaller small form factor. This revision has many small (0402) components and can be challenging (and more expensive) to build. They can be purchased too.
+
+
+
+# Revision history
+
+## [v1.1 (2024-09-12)](v1.1)
+
+### Changelog
+- Removed reset button.
+- Default CPUCLK and GROMCLK jumper pads to closed.
+- Minor positioning adjustments of bottom-side resistors to clear header pins.
+
+## [v1.0 (2024-08-01)](v1.0)
+
+First version available for sale on Tindie and ArcadeShopper
+
+
+
+### Changelog
+- Added reset button.
+- Moved CPUCLK and GROMCLK jumper pads to the top of the board.
+- Added SWD and SWC jumper pads to allow SWD (debugging)
+- Flipped top labels and graphics so it doesn't look upside-down.
+
+## [v0.4 (2024-07-16)](v0.4)
+
+This is the first fully-integrated single-board version and is powered by an RP2040 directly. This version was never released, however I hand-built 9 of them and sent most of the to various retro enthusiasts from AtariAge.
+
+
+
+### Changelog
+- Removed piggy backed Pi Pico module.
+- Added RP2040 and all of its dependencies.
+- Switched out resisitor networks for discrete resistors.
+- Shrinkified the package to something resembling the first version for sale (v1.0).
+- Updated VGA connector to 6p 1.25mm JST.
+
+## [v0.3 (2024-06-07)](v0.3)
+
+This is the first "public" version and is powered by a piggy-backed USB-C Pi Pico module. I have never produced or sold this version beyond the initial few prototypes.
+
+
+
+### Changelog
+- Switched to use USB-C Pi Pico module instead of genuine Pi Pico.
+- Added CPUCLK and GROMCLK.
+
+### PCB v0.3 Notes
+
+There are a number of 0 Ohm resistors (jumpers). You may need to omit the RST resistor. On some machines, the extra time is required to bootstrap the Pico. This will be changed to a soft reset on v0.4.
+
+### Raspberry Pi Pico Module
+
+Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29, a genuine Raspberry Pi Pico can't be used. v0.3 of the PCB is designed for the DWEII? RP2040 USB-C module which exposes these additional GPIOs. A future pico9918 revision will do without an external RP2040 board and use the RP2040 directly.
+
+Purchase links:
+ * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X
+ * https://www.aliexpress.com/item/1005007066733934.html
+
+I could reduce the VGA bit depth to 9-bit or 10-bit to allow the use of a genuine Raspberry Pi Pico board, but given the longer-term plan is to use the RP2040 directly, I've decided to go this way for the prototype.
+
+
+## What happened to v0.1 and v0.2?
+
+For the curious amongst you, v0.1 was the only version that wan't usable. The TMS9918A socket interface was 0.1" too narrow. Rookie error! Fortunately, I noticed within hours of ordering the PCBs, so ordered v0.2 long before v0.1 arrived.
+
v0.2 was very usable and is the version used in the initial "It freaking works!" video. The only reason it isn't published is because it lacked the GROMCLK and CPUCLK signals required for many systems. In the video, you can see the GROMCLK signal is being provided by a second Pi Pico.
\ No newline at end of file
diff --git a/pcb/v0.3/README.md b/pcb/v0.3/README.md
index 8b75741..326fa90 100644
--- a/pcb/v0.3/README.md
+++ b/pcb/v0.3/README.md
@@ -1,44 +1,44 @@
-## PICO9918 v0.3 (2024-06-07)
-
-This is the first "public" version and is powered by a piggy-backed USB-C Pi Pico module. I have never produced or sold this version beyond the initial few prototypes.
-
-
-
-
-### Schematic
-
-
-
-
-### PCB
-
-
-
-* [PICO9918 v0.3 Gerber](pico9918_v0_3_gerber.zip)
-* [PICO9918 v0.3 BOM](pico9918_v0_3_bom.xlsx)
-* [PICO9918 v0.3 Pick and place](pico9918_v0_3_picknplace.csv)
-
-#### VGA dongle PCB
-
-
-
-* [PICO9918 v0.3 VGA Gerber](pico9918_v0_3_vga_gerber.zip)
-* [VGA connector](https://www.lcsc.com/product-detail/D-Sub-DVI-HDMI-Connectors_TXGA-FDB1519-F0DB300U1KA_C2834384.html)
-
-### Changelog
-- Switched to use USB-C Pi Pico module instead of genuine Pi Pico.
-- Added CPUCLK and GROMCLK.
-
-### PCB v0.3 Notes
-
-There are a number of 0 Ohm resistors (jumpers). You may need to omit the RST resistor. On some machines, the extra time is required to bootstrap the Pico. This will be changed to a soft reset on v0.4.
-
-### Raspberry Pi Pico Module
-
-Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29, a genuine Raspberry Pi Pico can't be used. v0.3 of the PCB is designed for the DWEII? RP2040 USB-C module which exposes these additional GPIOs. A future pico9918 revision will do without an external RP2040 board and use the RP2040 directly.
-
-Purchase links:
- * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X
- * https://www.aliexpress.com/item/1005007066733934.html
-
-I could reduce the VGA bit depth to 9-bit or 10-bit to allow the use of a genuine Raspberry Pi Pico board, but given the longer-term plan is to use the RP2040 directly, I've decided to go this way for the prototype.
+## PICO9918 v0.3 (2024-06-07)
+
+This is the first "public" version and is powered by a piggy-backed USB-C Pi Pico module. I have never produced or sold this version beyond the initial few prototypes.
+
+
+
+
+### Schematic
+
+
+
+
+### PCB
+
+
+
+* [PICO9918 v0.3 Gerber](pico9918_v0_3_gerber.zip)
+* [PICO9918 v0.3 BOM](pico9918_v0_3_bom.xlsx)
+* [PICO9918 v0.3 Pick and place](pico9918_v0_3_picknplace.csv)
+
+#### VGA dongle PCB
+
+
+
+* [PICO9918 v0.3 VGA Gerber](pico9918_v0_3_vga_gerber.zip)
+* [VGA connector](https://www.lcsc.com/product-detail/D-Sub-DVI-HDMI-Connectors_TXGA-FDB1519-F0DB300U1KA_C2834384.html)
+
+### Changelog
+- Switched to use USB-C Pi Pico module instead of genuine Pi Pico.
+- Added CPUCLK and GROMCLK.
+
+### PCB v0.3 Notes
+
+There are a number of 0 Ohm resistors (jumpers). You may need to omit the RST resistor. On some machines, the extra time is required to bootstrap the Pico. This will be changed to a soft reset on v0.4.
+
+### Raspberry Pi Pico Module
+
+Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29, a genuine Raspberry Pi Pico can't be used. v0.3 of the PCB is designed for the DWEII? RP2040 USB-C module which exposes these additional GPIOs. A future pico9918 revision will do without an external RP2040 board and use the RP2040 directly.
+
+Purchase links:
+ * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X
+ * https://www.aliexpress.com/item/1005007066733934.html
+
+I could reduce the VGA bit depth to 9-bit or 10-bit to allow the use of a genuine Raspberry Pi Pico board, but given the longer-term plan is to use the RP2040 directly, I've decided to go this way for the prototype.
diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake
index a0721d0..8b66d30 100644
--- a/pico_sdk_import.cmake
+++ b/pico_sdk_import.cmake
@@ -1,84 +1,84 @@
-# This is a copy of /external/pico_sdk_import.cmake
-
-# This can be dropped into an external project to help locate this SDK
-# It should be include()ed prior to project()
-
-if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
- set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
- message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
-endif ()
-
-if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
- set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
- message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
-endif ()
-
-if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
- set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
- message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
-endif ()
-
-if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
- set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
- message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
-endif ()
-
-if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
- set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
- message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
-endif()
-
-set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
-set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
-set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
-set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
-
-if (NOT PICO_SDK_PATH)
- if (PICO_SDK_FETCH_FROM_GIT)
- include(FetchContent)
- set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
- if (PICO_SDK_FETCH_FROM_GIT_PATH)
- get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
- endif ()
- # GIT_SUBMODULES_RECURSE was added in 3.17
- if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
- FetchContent_Declare(
- pico_sdk
- GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
- GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
- GIT_SUBMODULES_RECURSE FALSE
- )
- else ()
- FetchContent_Declare(
- pico_sdk
- GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
- GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
- )
- endif ()
-
- if (NOT pico_sdk)
- message("Downloading Raspberry Pi Pico SDK")
- FetchContent_Populate(pico_sdk)
- set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
- endif ()
- set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
- else ()
- message(FATAL_ERROR
- "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
- )
- endif ()
-endif ()
-
-get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
-if (NOT EXISTS ${PICO_SDK_PATH})
- message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
-endif ()
-
-set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
-if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
- message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
-endif ()
-
-set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
-
-include(${PICO_SDK_INIT_CMAKE_FILE})
+# This is a copy of /external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+ set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+ message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+ set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+ message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+ set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+ message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
+ set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
+ message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
+endif ()
+
+if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
+ set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
+ message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
+endif()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
+
+if (NOT PICO_SDK_PATH)
+ if (PICO_SDK_FETCH_FROM_GIT)
+ include(FetchContent)
+ set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+ if (PICO_SDK_FETCH_FROM_GIT_PATH)
+ get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+ endif ()
+ # GIT_SUBMODULES_RECURSE was added in 3.17
+ if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+ GIT_SUBMODULES_RECURSE FALSE
+ )
+ else ()
+ FetchContent_Declare(
+ pico_sdk
+ GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+ GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+ )
+ endif ()
+
+ if (NOT pico_sdk)
+ message("Downloading Raspberry Pi Pico SDK")
+ FetchContent_Populate(pico_sdk)
+ set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+ endif ()
+ set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+ else ()
+ message(FATAL_ERROR
+ "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+ )
+ endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+ message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/src/boards/pico9918_v04.h b/src/boards/pico9918_v04.h
index 995babe..d6a0400 100644
--- a/src/boards/pico9918_v04.h
+++ b/src/boards/pico9918_v04.h
@@ -1,94 +1,94 @@
-/*
- * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
-// -----------------------------------------------------
-// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
-// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
-// -----------------------------------------------------
-
-// This header may be included by other board headers as "boards/pico.h"
-#pragma once
-// For board detection
-#define PICO9918
-
-#ifndef PICO_XOSC_STARTUP_DELAY_MULTIPLIER
-#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 32
-#endif
-
-// --- UART ---
-#ifndef PICO_DEFAULT_UART
-#define PICO_DEFAULT_UART 0
-#endif
-#ifndef PICO_DEFAULT_UART_TX_PIN
-#define PICO_DEFAULT_UART_TX_PIN 0
-#endif
-#ifndef PICO_DEFAULT_UART_RX_PIN
-#define PICO_DEFAULT_UART_RX_PIN 1
-#endif
-
-// --- LED ---
-#ifndef PICO_DEFAULT_LED_PIN
-#define PICO_DEFAULT_LED_PIN 25
-#endif
-// no PICO_DEFAULT_WS2812_PIN
-
-// --- I2C ---
-#ifndef PICO_DEFAULT_I2C
-#define PICO_DEFAULT_I2C 0
-#endif
-#ifndef PICO_DEFAULT_I2C_SDA_PIN
-#define PICO_DEFAULT_I2C_SDA_PIN 4
-#endif
-#ifndef PICO_DEFAULT_I2C_SCL_PIN
-#define PICO_DEFAULT_I2C_SCL_PIN 5
-#endif
-
-// --- SPI ---
-#ifndef PICO_DEFAULT_SPI
-#define PICO_DEFAULT_SPI 0
-#endif
-#ifndef PICO_DEFAULT_SPI_SCK_PIN
-#define PICO_DEFAULT_SPI_SCK_PIN 18
-#endif
-#ifndef PICO_DEFAULT_SPI_TX_PIN
-#define PICO_DEFAULT_SPI_TX_PIN 19
-#endif
-#ifndef PICO_DEFAULT_SPI_RX_PIN
-#define PICO_DEFAULT_SPI_RX_PIN 16
-#endif
-#ifndef PICO_DEFAULT_SPI_CSN_PIN
-#define PICO_DEFAULT_SPI_CSN_PIN 17
-#endif
-
-// --- FLASH ---
-
-#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1
-
-#ifndef PICO_FLASH_SPI_CLKDIV
-#define PICO_FLASH_SPI_CLKDIV 2
-#endif
-
-#ifndef PICO_FLASH_SIZE_BYTES
-#define PICO_FLASH_SIZE_BYTES (2 * 1024 * 1024)
-#endif
-
-// Drive high to force power supply into PWM mode (lower ripple on 3V3 at light loads)
-#define PICO_SMPS_MODE_PIN 23
-
-#ifndef PICO_RP2040_B0_SUPPORTED
-#define PICO_RP2040_B0_SUPPORTED 1
-#endif
-
-// The GPIO Pin used to read VBUS to determine if the device is battery powered.
-#ifndef PICO_VBUS_PIN
-#define PICO_VBUS_PIN 24
-#endif
-
-// The GPIO Pin used to monitor VSYS. Typically you would use this with ADC.
-// There is an example in adc/read_vsys in pico-examples.
-#ifndef PICO_VSYS_PIN
-#define PICO_VSYS_PIN 29
-#endif
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+// -----------------------------------------------------
+// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
+// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
+// -----------------------------------------------------
+
+// This header may be included by other board headers as "boards/pico.h"
+#pragma once
+// For board detection
+#define PICO9918
+
+#ifndef PICO_XOSC_STARTUP_DELAY_MULTIPLIER
+#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 32
+#endif
+
+// --- UART ---
+#ifndef PICO_DEFAULT_UART
+#define PICO_DEFAULT_UART 0
+#endif
+#ifndef PICO_DEFAULT_UART_TX_PIN
+#define PICO_DEFAULT_UART_TX_PIN 0
+#endif
+#ifndef PICO_DEFAULT_UART_RX_PIN
+#define PICO_DEFAULT_UART_RX_PIN 1
+#endif
+
+// --- LED ---
+#ifndef PICO_DEFAULT_LED_PIN
+#define PICO_DEFAULT_LED_PIN 25
+#endif
+// no PICO_DEFAULT_WS2812_PIN
+
+// --- I2C ---
+#ifndef PICO_DEFAULT_I2C
+#define PICO_DEFAULT_I2C 0
+#endif
+#ifndef PICO_DEFAULT_I2C_SDA_PIN
+#define PICO_DEFAULT_I2C_SDA_PIN 4
+#endif
+#ifndef PICO_DEFAULT_I2C_SCL_PIN
+#define PICO_DEFAULT_I2C_SCL_PIN 5
+#endif
+
+// --- SPI ---
+#ifndef PICO_DEFAULT_SPI
+#define PICO_DEFAULT_SPI 0
+#endif
+#ifndef PICO_DEFAULT_SPI_SCK_PIN
+#define PICO_DEFAULT_SPI_SCK_PIN 18
+#endif
+#ifndef PICO_DEFAULT_SPI_TX_PIN
+#define PICO_DEFAULT_SPI_TX_PIN 19
+#endif
+#ifndef PICO_DEFAULT_SPI_RX_PIN
+#define PICO_DEFAULT_SPI_RX_PIN 16
+#endif
+#ifndef PICO_DEFAULT_SPI_CSN_PIN
+#define PICO_DEFAULT_SPI_CSN_PIN 17
+#endif
+
+// --- FLASH ---
+
+#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1
+
+#ifndef PICO_FLASH_SPI_CLKDIV
+#define PICO_FLASH_SPI_CLKDIV 2
+#endif
+
+#ifndef PICO_FLASH_SIZE_BYTES
+#define PICO_FLASH_SIZE_BYTES (2 * 1024 * 1024)
+#endif
+
+// Drive high to force power supply into PWM mode (lower ripple on 3V3 at light loads)
+#define PICO_SMPS_MODE_PIN 23
+
+#ifndef PICO_RP2040_B0_SUPPORTED
+#define PICO_RP2040_B0_SUPPORTED 1
+#endif
+
+// The GPIO Pin used to read VBUS to determine if the device is battery powered.
+#ifndef PICO_VBUS_PIN
+#define PICO_VBUS_PIN 24
+#endif
+
+// The GPIO Pin used to monitor VSYS. Typically you would use this with ADC.
+// There is an example in adc/read_vsys in pico-examples.
+#ifndef PICO_VSYS_PIN
+#define PICO_VSYS_PIN 29
+#endif
diff --git a/submodules/vrEmuTms9918 b/submodules/vrEmuTms9918
index 0091eaa..30eb684 160000
--- a/submodules/vrEmuTms9918
+++ b/submodules/vrEmuTms9918
@@ -1 +1 @@
-Subproject commit 0091eaafe2423f8bb7d3737e2895b1159b2f62f9
+Subproject commit 30eb6841e91cdc3375716a66ce7aa978467a3ae1
diff --git a/test/host/CMakeLists.txt b/test/host/CMakeLists.txt
index 05a4655..5b6f72c 100644
--- a/test/host/CMakeLists.txt
+++ b/test/host/CMakeLists.txt
@@ -1,17 +1,17 @@
-set(PROGRAM pico9918test)
-
-add_executable(${PROGRAM})
-
-# generate image array source files from png images
-visrealm_generate_bindata_source(${PROGRAM} breakout res/BREAKOUT.* )
-
-pico_generate_pio_header(${PROGRAM} ${CMAKE_CURRENT_LIST_DIR}/clocks.pio)
-
-target_sources(${PROGRAM} PRIVATE test.c font.c)
-
-pico_add_extra_outputs(${PROGRAM})
-
-target_link_libraries(${PROGRAM} PUBLIC
- pico_stdlib
- hardware_pio)
-
+set(PROGRAM pico9918test)
+
+add_executable(${PROGRAM})
+
+# generate image array source files from png images
+visrealm_generate_bindata_source(${PROGRAM} breakout res/BREAKOUT.* )
+
+pico_generate_pio_header(${PROGRAM} ${CMAKE_CURRENT_LIST_DIR}/clocks.pio)
+
+target_sources(${PROGRAM} PRIVATE test.c font.c)
+
+pico_add_extra_outputs(${PROGRAM})
+
+target_link_libraries(${PROGRAM} PUBLIC
+ pico_stdlib
+ hardware_pio)
+
diff --git a/test/host/clocks.pio b/test/host/clocks.pio
index e214970..4eb31d8 100644
--- a/test/host/clocks.pio
+++ b/test/host/clocks.pio
@@ -1,35 +1,35 @@
-/*
- * Project: pico9918
- *
- * Copyright (c) 2024 Troy Schrapel
- *
- * This code is licensed under the MIT license
- *
- * https://github.com/visrealm/pico9918
- *
- */
-
-.program clock
- pull block
-.wrap_target
- set pins, 1
- mov x, osr
-onDelay:
- jmp x-- onDelay
- set pins, 0
- mov x, osr
-offDelay:
- jmp x-- offDelay
-.wrap
-
-
-% c-sdk {
-
-void clock_program_init(PIO pio, uint sm, uint offset, uint pin) {
- pio_gpio_init(pio, pin);
- pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
- pio_sm_config c = clock_program_get_default_config(offset);
- sm_config_set_set_pins(&c, pin, 1);
- pio_sm_init(pio, sm, offset, &c);
-}
-%}
+/*
+ * Project: pico9918
+ *
+ * Copyright (c) 2024 Troy Schrapel
+ *
+ * This code is licensed under the MIT license
+ *
+ * https://github.com/visrealm/pico9918
+ *
+ */
+
+.program clock
+ pull block
+.wrap_target
+ set pins, 1
+ mov x, osr
+onDelay:
+ jmp x-- onDelay
+ set pins, 0
+ mov x, osr
+offDelay:
+ jmp x-- offDelay
+.wrap
+
+
+% c-sdk {
+
+void clock_program_init(PIO pio, uint sm, uint offset, uint pin) {
+ pio_gpio_init(pio, pin);
+ pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
+ pio_sm_config c = clock_program_get_default_config(offset);
+ sm_config_set_set_pins(&c, pin, 1);
+ pio_sm_init(pio, sm, offset, &c);
+}
+%}
diff --git a/test/host/font.c b/test/host/font.c
index e61ca40..adb982d 100644
--- a/test/host/font.c
+++ b/test/host/font.c
@@ -1,103 +1,103 @@
-#include
-#include
-
-const uint8_t tmsFont[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // !
- 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, // "
- 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, // #
- 0x18, 0x7e, 0xc0, 0x7c, 0x06, 0xfc, 0x18, 0x00, // $
- 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, // %
- 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, // &
- 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // '
- 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, // (
- 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, // )
- 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, // *
- 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, // +
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // ,
- 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, // -
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // .
- 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, // /
- 0x7c, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0x7c, 0x00, // 0
- 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, // 1
- 0x7c, 0xc6, 0x06, 0x7c, 0xc0, 0xc0, 0xfe, 0x00, // 2
- 0xfc, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xfc, 0x00, // 3
- 0x0c, 0xcc, 0xcc, 0xcc, 0xfe, 0x0c, 0x0c, 0x00, // 4
- 0xfe, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, // 5
- 0x7c, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, // 6
- 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, // 7
- 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00, // 8
- 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x7c, 0x00, // 9
- 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, // :
- 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, // ;
- 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, // <
- 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, // =
- 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, // >
- 0x3c, 0x66, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x00, // ?
- 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x7e, 0x00, // @
- 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, // A
- 0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00, // B
- 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, // C
- 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, // D
- 0xfe, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xfe, 0x00, // E
- 0xfe, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, 0x00, // F
- 0x7c, 0xc6, 0xc0, 0xc0, 0xce, 0xc6, 0x7c, 0x00, // G
- 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, // H
- 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, // I
- 0x06, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, // J
- 0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, // K
- 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, // L
- 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, // M
- 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, // N
- 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, // O
- 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00, // P
- 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x06, // Q
- 0xfc, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xc6, 0x00, // R
- 0x7c, 0xc6, 0xc0, 0x7c, 0x06, 0xc6, 0x7c, 0x00, // S
- 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // T
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, // U
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x38, 0x00, // V
- 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00, // W
- 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, // X
- 0xc6, 0xc6, 0xc6, 0x7c, 0x18, 0x30, 0xe0, 0x00, // Y
- 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xfe, 0x00, // Z
- 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, // [
- 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, //
- 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, // ]
- 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, // ^
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, // _
- 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, // `
- 0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00, // a
- 0xc0, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xfc, 0x00, // b
- 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x00, // c
- 0x06, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0x7e, 0x00, // d
- 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, // e
- 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00, // f
- 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, // g
- 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, // h
- 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, // i
- 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, // j
- 0xc0, 0xc0, 0xcc, 0xd8, 0xf8, 0xcc, 0xc6, 0x00, // k
- 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, // l
- 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xd6, 0x00, // m
- 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, // n
- 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, // o
- 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, // p
- 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0x7e, 0x06, 0x06, // q
- 0x00, 0x00, 0xfc, 0xc6, 0xc0, 0xc0, 0xc0, 0x00, // r
- 0x00, 0x00, 0x7e, 0xc0, 0x7c, 0x06, 0xfc, 0x00, // s
- 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00, // t
- 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, // u
- 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7c, 0x38, 0x00, // v
- 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00, // w
- 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, // x
- 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, // y
- 0x00, 0x00, 0xfe, 0x0c, 0x38, 0x60, 0xfe, 0x00, // z
- 0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00, // {
- 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // |
- 0x70, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x70, 0x00, // }
- 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ~
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
-};
-
+#include
+#include
+
+const uint8_t tmsFont[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, // !
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, // "
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, // #
+ 0x18, 0x7e, 0xc0, 0x7c, 0x06, 0xfc, 0x18, 0x00, // $
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00, // %
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00, // &
+ 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // '
+ 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, // (
+ 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, // )
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, // *
+ 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, // +
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, // ,
+ 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, // -
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // .
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, // /
+ 0x7c, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0x7c, 0x00, // 0
+ 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, // 1
+ 0x7c, 0xc6, 0x06, 0x7c, 0xc0, 0xc0, 0xfe, 0x00, // 2
+ 0xfc, 0x06, 0x06, 0x3c, 0x06, 0x06, 0xfc, 0x00, // 3
+ 0x0c, 0xcc, 0xcc, 0xcc, 0xfe, 0x0c, 0x0c, 0x00, // 4
+ 0xfe, 0xc0, 0xfc, 0x06, 0x06, 0xc6, 0x7c, 0x00, // 5
+ 0x7c, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00, // 6
+ 0xfe, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x00, // 7
+ 0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00, // 8
+ 0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x7c, 0x00, // 9
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, // :
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, // ;
+ 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, // <
+ 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, // =
+ 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, // >
+ 0x3c, 0x66, 0x0c, 0x18, 0x18, 0x00, 0x18, 0x00, // ?
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x7e, 0x00, // @
+ 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, // A
+ 0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00, // B
+ 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, // C
+ 0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00, // D
+ 0xfe, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xfe, 0x00, // E
+ 0xfe, 0xc0, 0xc0, 0xf8, 0xc0, 0xc0, 0xc0, 0x00, // F
+ 0x7c, 0xc6, 0xc0, 0xc0, 0xce, 0xc6, 0x7c, 0x00, // G
+ 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, // H
+ 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, // I
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, // J
+ 0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00, // K
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, // L
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00, // M
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00, // N
+ 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, // O
+ 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00, // P
+ 0x7c, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x06, // Q
+ 0xfc, 0xc6, 0xc6, 0xfc, 0xd8, 0xcc, 0xc6, 0x00, // R
+ 0x7c, 0xc6, 0xc0, 0x7c, 0x06, 0xc6, 0x7c, 0x00, // S
+ 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // T
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, // U
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x38, 0x00, // V
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00, // W
+ 0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00, // X
+ 0xc6, 0xc6, 0xc6, 0x7c, 0x18, 0x30, 0xe0, 0x00, // Y
+ 0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xfe, 0x00, // Z
+ 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, // [
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00, //
+ 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, // ]
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, // ^
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, // _
+ 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, // `
+ 0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00, // a
+ 0xc0, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xfc, 0x00, // b
+ 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc6, 0x7c, 0x00, // c
+ 0x06, 0x06, 0x06, 0x7e, 0xc6, 0xc6, 0x7e, 0x00, // d
+ 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7c, 0x00, // e
+ 0x1c, 0x36, 0x30, 0x78, 0x30, 0x30, 0x78, 0x00, // f
+ 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, // g
+ 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, // h
+ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, // i
+ 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0xc6, 0x7c, // j
+ 0xc0, 0xc0, 0xcc, 0xd8, 0xf8, 0xcc, 0xc6, 0x00, // k
+ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, // l
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xd6, 0x00, // m
+ 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, // n
+ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, // o
+ 0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, // p
+ 0x00, 0x00, 0x7e, 0xc6, 0xc6, 0x7e, 0x06, 0x06, // q
+ 0x00, 0x00, 0xfc, 0xc6, 0xc0, 0xc0, 0xc0, 0x00, // r
+ 0x00, 0x00, 0x7e, 0xc0, 0x7c, 0x06, 0xfc, 0x00, // s
+ 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00, // t
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00, // u
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7c, 0x38, 0x00, // v
+ 0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00, // w
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00, // x
+ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0xfc, // y
+ 0x00, 0x00, 0xfe, 0x0c, 0x38, 0x60, 0xfe, 0x00, // z
+ 0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00, // {
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // |
+ 0x70, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x70, 0x00, // }
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ~
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //
+};
+
const size_t tmsFontBytes = sizeof(tmsFont);
\ No newline at end of file
diff --git a/test/host/test.c b/test/host/test.c
index 157cdea..2701783 100644
--- a/test/host/test.c
+++ b/test/host/test.c
@@ -1,573 +1,573 @@
-/*
- * Project: pico9918
- *
- * Copyright (c) 2024 Troy Schrapel
- *
- * This code is licensed under the MIT license
- *
- * https://github.com/visrealm/pico9918
- *
- */
-
- /*
- * External pins
- *
- * Pin | GPIO | Name | TMS9918A Pin
- * -----+------+--------+-------------
- * 19 | 14 | CD0 | 24
- * 20 | 15 | CD1 | 23
- * 21 | 16 | CD2 | 22
- * 22 | 17 | CD3 | 21
- * 24 | 18 | CD4 | 20
- * 25 | 19 | CD5 | 19
- * 26 | 20 | CD6 | 18
- * 27 | 21 | CD7 | 17
- * 29 | 22 | /INT | 16
- * 30 | RUN | RST | 34
- * 31 | 26 | /CSR | 15
- * 32 | 27 | /CSW | 14
- * 34 | 28 | MODE | 13
- */
-
-#include "pico/stdlib.h"
-#include "hardware/pio.h"
-#include "hardware/clocks.h"
-#include "clocks.pio.h"
-
-#include "breakout.h"
-
-
-
- //#include
-#include
-#include
-#include
-#include
-#include
-
-
-extern const uint8_t tmsFont[];
-extern size_t tmsFontBytes;
-
-#define GPIO_GROMCL 0
-#define GPIO_CPUCL 1
-
-#define GPIO_CD0 14
-#define GPIO_CSR 26
-#define GPIO_CSW 27
-#define GPIO_MODE 28
-#define GPIO_INT 22
-
-#define GPIO_CD_MASK (0xff << GPIO_CD0)
-#define GPIO_CSR_MASK (0x01 << GPIO_CSR)
-#define GPIO_CSW_MASK (0x01 << GPIO_CSW)
-#define GPIO_MODE_MASK (0x01 << GPIO_MODE)
-#define GPIO_INT_MASK (0x01 << GPIO_INT)
-
-#define TMS_CRYSTAL_FREQ_HZ 10738635.0f
-
-
-/* todo should I make this uint32_t and shift the bits too?*/
-static uint8_t __aligned(4) reversed[] =
-{
- 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
- 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
- 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
- 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
- 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
- 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
- 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
- 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
- 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
- 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
- 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
- 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
- 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
- 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
- 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
- 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
-};
-
-#define REVERSE(x) reversed[x]
-
-uint32_t buildGpioState(bool read, bool write, bool mode, uint8_t value)
-{
- return (read ? 0 : GPIO_CSR_MASK) |
- (write ? 0 : GPIO_CSW_MASK) |
- (mode ? GPIO_MODE_MASK : 0) |
- (REVERSE(value) << GPIO_CD0);
-}
-
-int del = 0;
-int del2 = 0;
-void doFn(uint8_t value)
-{
- --del;
- del2 += value;
- del2 *= value;
- del2 -= value & 0xff;
- del2 ^= value;
- del2 += value;
- del2 *= value;
- del2 -= value & 0xff;
- del2 ^= value;
-}
-
-void writeTo9918(bool mode, uint8_t value)
-{
- gpio_set_dir_out_masked(GPIO_CD_MASK);
- gpio_put_all(buildGpioState(false, true, mode, value));
- sleep_us(0);
- // del = 12;
- // while (del) doFn(value);
- //for (del = 0; del < 50; ++del);
- gpio_put_all(buildGpioState(false, false, mode, value));
-
- //del = 8;
- //while (del) doFn(value);
-
- sleep_us(0);
- //gpio_set_dir_in_masked(GPIO_CD_MASK);
-}
-
-uint8_t readFrom9918(bool mode)
-{
- gpio_set_dir_in_masked(GPIO_CD_MASK);
- gpio_put_all(buildGpioState(true, false, mode, 0));
- sleep_us(0);
- uint8_t value = REVERSE((gpio_get_all() >> GPIO_CD0) & 0xff);
- gpio_put_all(buildGpioState(false, false, mode, value));
- sleep_us(0);
- return value;
-}
-
-
-
-typedef enum
-{
- TMS_MODE_GRAPHICS_I,
- TMS_MODE_GRAPHICS_II,
- TMS_MODE_TEXT,
- TMS_MODE_MULTICOLOR,
-} vrEmuTms9918Mode;
-
-typedef enum
-{
- TMS_TRANSPARENT = 0,
- TMS_BLACK,
- TMS_MED_GREEN,
- TMS_LT_GREEN,
- TMS_DK_BLUE,
- TMS_LT_BLUE,
- TMS_DK_RED,
- TMS_CYAN,
- TMS_MED_RED,
- TMS_LT_RED,
- TMS_DK_YELLOW,
- TMS_LT_YELLOW,
- TMS_DK_GREEN,
- TMS_MAGENTA,
- TMS_GREY,
- TMS_WHITE,
-} vrEmuTms9918Color;
-
-typedef enum
-{
- TMS_REG_0 = 0,
- TMS_REG_1,
- TMS_REG_2,
- TMS_REG_3,
- TMS_REG_4,
- TMS_REG_5,
- TMS_REG_6,
- TMS_REG_7,
- TMS_NUM_REGISTERS,
- TMS_REG_NAME_TABLE = TMS_REG_2,
- TMS_REG_COLOR_TABLE = TMS_REG_3,
- TMS_REG_PATTERN_TABLE = TMS_REG_4,
- TMS_REG_SPRITE_ATTR_TABLE = TMS_REG_5,
- TMS_REG_SPRITE_PATT_TABLE = TMS_REG_6,
- TMS_REG_FG_BG_COLOR = TMS_REG_7,
-} vrEmuTms9918Register;
-
-#define TMS9918_PIXELS_X 256
-#define TMS9918_PIXELS_Y 192
-
-
-#define TMS_R0_MODE_GRAPHICS_I 0x00
-#define TMS_R0_MODE_GRAPHICS_II 0x02
-#define TMS_R0_MODE_MULTICOLOR 0x00
-#define TMS_R0_MODE_TEXT 0x00
-#define TMS_R0_EXT_VDP_ENABLE 0x01
-#define TMS_R0_EXT_VDP_DISABLE 0x00
-
-#define TMS_R1_RAM_16K 0x80
-#define TMS_R1_RAM_4K 0x00
-#define TMS_R1_DISP_BLANK 0x00
-#define TMS_R1_DISP_ACTIVE 0x40
-#define TMS_R1_INT_ENABLE 0x20
-#define TMS_R1_INT_DISABLE 0x00
-#define TMS_R1_MODE_GRAPHICS_I 0x00
-#define TMS_R1_MODE_GRAPHICS_II 0x00
-#define TMS_R1_MODE_MULTICOLOR 0x08
-#define TMS_R1_MODE_TEXT 0x10
-#define TMS_R1_SPRITE_8 0x00
-#define TMS_R1_SPRITE_16 0x02
-#define TMS_R1_SPRITE_MAG1 0x00
-#define TMS_R1_SPRITE_MAG2 0x01
-
-#define TMS_DEFAULT_VRAM_NAME_ADDRESS 0x3800
-#define TMS_DEFAULT_VRAM_COLOR_ADDRESS 0x0000
-#define TMS_DEFAULT_VRAM_PATT_ADDRESS 0x2000
-#define TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS 0x3B00
-#define TMS_DEFAULT_VRAM_SPRITE_PATT_ADDRESS 0x1800
-
-
-struct vrEmuTMS9918_s;
-typedef struct vrEmuTMS9918_s VrEmuTms9918;
-
-
-/* PUBLIC INTERFACE
- * ---------------------------------------- */
-
- /* Function: vrEmuTms9918New
- * --------------------
- * create a new TMS9918
- */
-VrEmuTms9918* vrEmuTms9918New()
-{
- gpio_init_mask(GPIO_CD_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK);
-
- gpio_set_pulls(GPIO_CSW, true, false);
- gpio_set_pulls(GPIO_CSR, true, false);
-
- gpio_put_all(GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK); // drive r, w, mode high
- gpio_set_dir_all_bits(GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK); // set r, w, mode to outputs
-
- return NULL;
-}
-
-/* Function: vrEmuTms9918Reset
- * --------------------
- * reset the new TMS9918
- */
-void vrEmuTms9918Reset(VrEmuTms9918* tms9918)
-{
-}
-
-/* Function: vrEmuTms9918Destroy
- * --------------------
- * destroy a TMS9918
- *
- * tms9918: tms9918 object to destroy / clean up
- */
-void vrEmuTms9918Destroy(VrEmuTms9918* tms9918)
-{
-
-}
-
-/* Function: vrEmuTms9918WriteAddr
- * --------------------
- * write an address (mode = 1) to the tms9918
- *
- * uint8_t: the data (DB0 -> DB7) to send
- */
-void vrEmuTms9918WriteAddr(VrEmuTms9918* tms9918, uint8_t data)
-{
- writeTo9918(true, data);
-}
-
-/* Function: vrEmuTms9918WriteData
- * --------------------
- * write data (mode = 0) to the tms9918
- *
- * uint8_t: the data (DB0 -> DB7) to send
- */
-void vrEmuTms9918WriteData(VrEmuTms9918* tms9918, uint8_t data)
-{
- writeTo9918(false, data);
-}
-
-/* Function: vrEmuTms9918ReadStatus
- * --------------------
- * read from the status register
- */
-uint8_t vrEmuTms9918ReadStatus(VrEmuTms9918* tms9918)
-{
- return readFrom9918(true);
-}
-
-/* Function: vrEmuTms9918ReadData
- * --------------------
- * read data (mode = 0) from the tms9918
- */
-uint8_t vrEmuTms9918ReadData(VrEmuTms9918* tms9918)
-{
- return readFrom9918(false);
-}
-
-
-/*
- * Write a register value
- */
-inline static void vrEmuTms9918WriteRegisterValue(VrEmuTms9918* tms9918, vrEmuTms9918Register reg, uint8_t value)
-{
- vrEmuTms9918WriteAddr(tms9918, value);
- vrEmuTms9918WriteAddr(tms9918, 0x80 | (uint8_t)reg);
-}
-
-
-/*
- * Write a series of bytes to the VRAM
- */
-inline static void vrEmuTms9918WriteBytes(VrEmuTms9918* tms9918, const uint8_t* bytes, size_t numBytes)
-{
- for (size_t i = 0; i < numBytes; ++i)
- {
- vrEmuTms9918WriteData(tms9918, bytes[i]);
- }
-}
-
-
-/*
- * Set current VRAM address for reading
- */
-inline static void vrEmuTms9918SetAddressRead(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918WriteAddr(tms9918, addr & 0x00ff);
- vrEmuTms9918WriteAddr(tms9918, ((addr & 0xff00) >> 8));
-}
-
-
-/*
- * Set current VRAM address for writing
- */
-inline static void vrEmuTms9918SetAddressWrite(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918SetAddressRead(tms9918, addr | 0x4000);
-}
-
-
-
-/*
- * Return a colur byte consisting of foreground and background colors
- */
-inline static uint8_t vrEmuTms9918FgBgColor(vrEmuTms9918Color fg, vrEmuTms9918Color bg)
-{
- return (uint8_t)((uint8_t)fg << 4) | (uint8_t)bg;
-}
-
-/*
- * Set name table address
- */
-inline static void vrEmuTms9918SetNameTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_NAME_TABLE, addr >> 10);
-}
-
-/*
- * Set color table address
- */
-inline static void vrEmuTms9918SetColorTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_COLOR_TABLE, (uint8_t)(addr >> 6));
-}
-
-/*
- * Set pattern table address
- */
-inline static void vrEmuTms9918SetPatternTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_PATTERN_TABLE, addr >> 11);
-}
-
-/*
- * Set sprite attribute table address
- */
-inline static void vrEmuTms9918SetSpriteAttrTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_SPRITE_ATTR_TABLE, (uint8_t)(addr >> 7));
-}
-
-/*
- * Set sprite pattern table address
- */
-inline static void vrEmuTms9918SetSpritePattTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_SPRITE_PATT_TABLE, addr >> 11);
-}
-
-/*
- * Set foreground (text mode) and background colors
- */
-inline static void vrEmuTms9918SetFgBgColor(VrEmuTms9918* tms9918, vrEmuTms9918Color fg, vrEmuTms9918Color bg)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_FG_BG_COLOR, vrEmuTms9918FgBgColor(fg, bg));
-}
-
-
-
-void vrEmuTms9918InitialiseGfxII(VrEmuTms9918* tms9918)
-{
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_0, TMS_R0_EXT_VDP_DISABLE | TMS_R0_MODE_GRAPHICS_II);
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_1, TMS_R1_RAM_16K | TMS_R1_MODE_GRAPHICS_II | TMS_R1_RAM_16K | TMS_R1_DISP_ACTIVE | TMS_R1_INT_ENABLE | TMS_R1_SPRITE_MAG2);
- vrEmuTms9918SetNameTableAddr(tms9918, TMS_DEFAULT_VRAM_NAME_ADDRESS);
-
- /* in Graphics II, Registers 3 and 4 work differently
- *
- * reg3 - Color table
- * 0x7f = 0x0000
- * 0xff = 0x2000
- *
- * reg4 - Pattern table
- * 0x03 = 0x0000
- * 0x07 = 0x2000
- */
-
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_COLOR_TABLE, 0x7f);
- vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_PATTERN_TABLE, 0x07);
-
- vrEmuTms9918SetSpriteAttrTableAddr(tms9918, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS);
- vrEmuTms9918SetSpritePattTableAddr(tms9918, TMS_DEFAULT_VRAM_SPRITE_PATT_ADDRESS);
- vrEmuTms9918SetFgBgColor(tms9918, TMS_BLACK, TMS_CYAN);
-
- vrEmuTms9918SetAddressWrite(tms9918, TMS_DEFAULT_VRAM_NAME_ADDRESS);
- for (int i = 0; i < 768; ++i)
- {
- vrEmuTms9918WriteData(tms9918, i & 0xff);
- }
-
-}
-
-
-
-VrEmuTms9918* tms = NULL;
-
-
-void animateSprites(uint64_t frameNumber)
-{
- for (int i = 0; i < 16; ++i)
- {
- float x = sin(frameNumber / 20.0f + i / 3.0f);
-
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i) + 4);
- uint8_t yPos = (frameNumber / 2 + i * 10 + 24);
- if (yPos == 0xd0) ++yPos;
- vrEmuTms9918WriteData(tms, yPos);
- vrEmuTms9918WriteData(tms, 128 - 8 + (x * 80.0f));
-
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i));
- if (yPos - 2 == 0xd0) ++yPos;
- vrEmuTms9918WriteData(tms, yPos - 2);
- vrEmuTms9918WriteData(tms, 128 - 8 + (x * 80.0f) - 2);
-
-
- vrEmuTms9918SetAddressRead(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i) + 2);
- char c = vrEmuTms9918ReadData(tms);
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i) + 2);
- vrEmuTms9918WriteData(tms, c);
-
- }
-}
-
-int i = 0;
-
-void onTms9918Interrupt()
-{
- vrEmuTms9918SetFgBgColor(tms, TMS_WHITE, TMS_CYAN);
- animateSprites(++i);
- vrEmuTms9918SetFgBgColor(tms, TMS_WHITE, TMS_BLACK);
-
- vrEmuTms9918ReadStatus(tms); // clear the interrupt
-}
-
-
-int main(void)
-{
- set_sys_clock_khz(252000, false);
-
- uint clocksPioOffset = pio_add_program(pio0, &clock_program);
-
- uint gromClkSm = pio_claim_unused_sm(pio0, true);
- uint cpuClkSm = pio_claim_unused_sm(pio0, true);
-
- clock_program_init(pio0, gromClkSm, clocksPioOffset, GPIO_GROMCL);
- clock_program_init(pio0, cpuClkSm, clocksPioOffset, GPIO_CPUCL);
-
- float clockDiv = (float)clock_get_hz(clk_sys) / (TMS_CRYSTAL_FREQ_HZ * 10.0f);
-
- pio_sm_set_clkdiv(pio0, gromClkSm, clockDiv);
- pio_sm_set_clkdiv(pio0, cpuClkSm, clockDiv);
-
- pio_sm_set_enabled(pio0, gromClkSm, true);
- pio_sm_set_enabled(pio0, cpuClkSm, true);
-
- const float gromClkFreq = TMS_CRYSTAL_FREQ_HZ / 24.0f;
- const float cpuClkFreq = TMS_CRYSTAL_FREQ_HZ / 3.0f;
-
- pio_sm_put(pio0, gromClkSm, (uint)(clock_get_hz(clk_sys) / clockDiv / (2.0f * gromClkFreq)) - 3.0f);
- pio_sm_put(pio0, cpuClkSm, (uint)(clock_get_hz(clk_sys) / clockDiv / (2.0f * cpuClkFreq)) - 3.0f);
-
- tms = vrEmuTms9918New();
-
- sleep_ms(50);
-
- vrEmuTms9918ReadStatus(tms);
-
- vrEmuTms9918InitialiseGfxII(tms);
-
- //while ((vrEmuTms9918ReadStatus(tms) & 0x80) == 0)
-// sleep_ms(10);
-
- vrEmuTms9918SetFgBgColor(tms, TMS_WHITE, TMS_BLACK);
-
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_PATT_ADDRESS + 32 * 8);
- vrEmuTms9918WriteBytes(tms, tmsFont, tmsFontBytes);
-
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_COLOR_ADDRESS);
- vrEmuTms9918WriteBytes(tms, BREAKOUT_TIAC, 6144);
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_PATT_ADDRESS);
- vrEmuTms9918WriteBytes(tms, BREAKOUT_TIAP, 6144);
-
- vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS);
- const char* str = "Hello, World!";
- const int strLen = strlen(str);
-
- for (int i = 0; i < strLen; ++i)
- {
- vrEmuTms9918WriteData(tms, i * 10 + 24 - 2);
- vrEmuTms9918WriteData(tms, i * 10 - 2);
- vrEmuTms9918WriteData(tms, str[strLen - (i + 1)]);
- vrEmuTms9918WriteData(tms, i + 2);
-
- vrEmuTms9918WriteData(tms, i * 10 + 24);
- vrEmuTms9918WriteData(tms, i * 10);
- vrEmuTms9918WriteData(tms, str[strLen - (i + 1)]);
- vrEmuTms9918WriteData(tms, 1);
- }
-
- for (int i = strLen; i < 16; ++i)
- {
- vrEmuTms9918WriteData(tms, 0xd0);
- vrEmuTms9918WriteData(tms, 0x0);
- vrEmuTms9918WriteData(tms, 0x0);
- vrEmuTms9918WriteData(tms, 0x0);
-
- vrEmuTms9918WriteData(tms, 0xd2);
- vrEmuTms9918WriteData(tms, 0x0);
- vrEmuTms9918WriteData(tms, 0x0);
- vrEmuTms9918WriteData(tms, 0x0);
- }
- // }
-
- gpio_set_irq_enabled_with_callback(GPIO_INT, GPIO_IRQ_EDGE_FALL, true, onTms9918Interrupt);
-
- while (1)
- {
- tight_loop_contents();
- }
-
- vrEmuTms9918Destroy(tms);
-
- return 0;
-}
+/*
+ * Project: pico9918
+ *
+ * Copyright (c) 2024 Troy Schrapel
+ *
+ * This code is licensed under the MIT license
+ *
+ * https://github.com/visrealm/pico9918
+ *
+ */
+
+ /*
+ * External pins
+ *
+ * Pin | GPIO | Name | TMS9918A Pin
+ * -----+------+--------+-------------
+ * 19 | 14 | CD0 | 24
+ * 20 | 15 | CD1 | 23
+ * 21 | 16 | CD2 | 22
+ * 22 | 17 | CD3 | 21
+ * 24 | 18 | CD4 | 20
+ * 25 | 19 | CD5 | 19
+ * 26 | 20 | CD6 | 18
+ * 27 | 21 | CD7 | 17
+ * 29 | 22 | /INT | 16
+ * 30 | RUN | RST | 34
+ * 31 | 26 | /CSR | 15
+ * 32 | 27 | /CSW | 14
+ * 34 | 28 | MODE | 13
+ */
+
+#include "pico/stdlib.h"
+#include "hardware/pio.h"
+#include "hardware/clocks.h"
+#include "clocks.pio.h"
+
+#include "breakout.h"
+
+
+
+ //#include
+#include
+#include
+#include
+#include
+#include
+
+
+extern const uint8_t tmsFont[];
+extern size_t tmsFontBytes;
+
+#define GPIO_GROMCL 0
+#define GPIO_CPUCL 1
+
+#define GPIO_CD0 14
+#define GPIO_CSR 26
+#define GPIO_CSW 27
+#define GPIO_MODE 28
+#define GPIO_INT 22
+
+#define GPIO_CD_MASK (0xff << GPIO_CD0)
+#define GPIO_CSR_MASK (0x01 << GPIO_CSR)
+#define GPIO_CSW_MASK (0x01 << GPIO_CSW)
+#define GPIO_MODE_MASK (0x01 << GPIO_MODE)
+#define GPIO_INT_MASK (0x01 << GPIO_INT)
+
+#define TMS_CRYSTAL_FREQ_HZ 10738635.0f
+
+
+/* todo should I make this uint32_t and shift the bits too?*/
+static uint8_t __aligned(4) reversed[] =
+{
+ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+#define REVERSE(x) reversed[x]
+
+uint32_t buildGpioState(bool read, bool write, bool mode, uint8_t value)
+{
+ return (read ? 0 : GPIO_CSR_MASK) |
+ (write ? 0 : GPIO_CSW_MASK) |
+ (mode ? GPIO_MODE_MASK : 0) |
+ (REVERSE(value) << GPIO_CD0);
+}
+
+int del = 0;
+int del2 = 0;
+void doFn(uint8_t value)
+{
+ --del;
+ del2 += value;
+ del2 *= value;
+ del2 -= value & 0xff;
+ del2 ^= value;
+ del2 += value;
+ del2 *= value;
+ del2 -= value & 0xff;
+ del2 ^= value;
+}
+
+void writeTo9918(bool mode, uint8_t value)
+{
+ gpio_set_dir_out_masked(GPIO_CD_MASK);
+ gpio_put_all(buildGpioState(false, true, mode, value));
+ sleep_us(0);
+ // del = 12;
+ // while (del) doFn(value);
+ //for (del = 0; del < 50; ++del);
+ gpio_put_all(buildGpioState(false, false, mode, value));
+
+ //del = 8;
+ //while (del) doFn(value);
+
+ sleep_us(0);
+ //gpio_set_dir_in_masked(GPIO_CD_MASK);
+}
+
+uint8_t readFrom9918(bool mode)
+{
+ gpio_set_dir_in_masked(GPIO_CD_MASK);
+ gpio_put_all(buildGpioState(true, false, mode, 0));
+ sleep_us(0);
+ uint8_t value = REVERSE((gpio_get_all() >> GPIO_CD0) & 0xff);
+ gpio_put_all(buildGpioState(false, false, mode, value));
+ sleep_us(0);
+ return value;
+}
+
+
+
+typedef enum
+{
+ TMS_MODE_GRAPHICS_I,
+ TMS_MODE_GRAPHICS_II,
+ TMS_MODE_TEXT,
+ TMS_MODE_MULTICOLOR,
+} vrEmuTms9918Mode;
+
+typedef enum
+{
+ TMS_TRANSPARENT = 0,
+ TMS_BLACK,
+ TMS_MED_GREEN,
+ TMS_LT_GREEN,
+ TMS_DK_BLUE,
+ TMS_LT_BLUE,
+ TMS_DK_RED,
+ TMS_CYAN,
+ TMS_MED_RED,
+ TMS_LT_RED,
+ TMS_DK_YELLOW,
+ TMS_LT_YELLOW,
+ TMS_DK_GREEN,
+ TMS_MAGENTA,
+ TMS_GREY,
+ TMS_WHITE,
+} vrEmuTms9918Color;
+
+typedef enum
+{
+ TMS_REG_0 = 0,
+ TMS_REG_1,
+ TMS_REG_2,
+ TMS_REG_3,
+ TMS_REG_4,
+ TMS_REG_5,
+ TMS_REG_6,
+ TMS_REG_7,
+ TMS_NUM_REGISTERS,
+ TMS_REG_NAME_TABLE = TMS_REG_2,
+ TMS_REG_COLOR_TABLE = TMS_REG_3,
+ TMS_REG_PATTERN_TABLE = TMS_REG_4,
+ TMS_REG_SPRITE_ATTR_TABLE = TMS_REG_5,
+ TMS_REG_SPRITE_PATT_TABLE = TMS_REG_6,
+ TMS_REG_FG_BG_COLOR = TMS_REG_7,
+} vrEmuTms9918Register;
+
+#define TMS9918_PIXELS_X 256
+#define TMS9918_PIXELS_Y 192
+
+
+#define TMS_R0_MODE_GRAPHICS_I 0x00
+#define TMS_R0_MODE_GRAPHICS_II 0x02
+#define TMS_R0_MODE_MULTICOLOR 0x00
+#define TMS_R0_MODE_TEXT 0x00
+#define TMS_R0_EXT_VDP_ENABLE 0x01
+#define TMS_R0_EXT_VDP_DISABLE 0x00
+
+#define TMS_R1_RAM_16K 0x80
+#define TMS_R1_RAM_4K 0x00
+#define TMS_R1_DISP_BLANK 0x00
+#define TMS_R1_DISP_ACTIVE 0x40
+#define TMS_R1_INT_ENABLE 0x20
+#define TMS_R1_INT_DISABLE 0x00
+#define TMS_R1_MODE_GRAPHICS_I 0x00
+#define TMS_R1_MODE_GRAPHICS_II 0x00
+#define TMS_R1_MODE_MULTICOLOR 0x08
+#define TMS_R1_MODE_TEXT 0x10
+#define TMS_R1_SPRITE_8 0x00
+#define TMS_R1_SPRITE_16 0x02
+#define TMS_R1_SPRITE_MAG1 0x00
+#define TMS_R1_SPRITE_MAG2 0x01
+
+#define TMS_DEFAULT_VRAM_NAME_ADDRESS 0x3800
+#define TMS_DEFAULT_VRAM_COLOR_ADDRESS 0x0000
+#define TMS_DEFAULT_VRAM_PATT_ADDRESS 0x2000
+#define TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS 0x3B00
+#define TMS_DEFAULT_VRAM_SPRITE_PATT_ADDRESS 0x1800
+
+
+struct vrEmuTMS9918_s;
+typedef struct vrEmuTMS9918_s VrEmuTms9918;
+
+
+/* PUBLIC INTERFACE
+ * ---------------------------------------- */
+
+ /* Function: vrEmuTms9918New
+ * --------------------
+ * create a new TMS9918
+ */
+VrEmuTms9918* vrEmuTms9918New()
+{
+ gpio_init_mask(GPIO_CD_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK);
+
+ gpio_set_pulls(GPIO_CSW, true, false);
+ gpio_set_pulls(GPIO_CSR, true, false);
+
+ gpio_put_all(GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK); // drive r, w, mode high
+ gpio_set_dir_all_bits(GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK); // set r, w, mode to outputs
+
+ return NULL;
+}
+
+/* Function: vrEmuTms9918Reset
+ * --------------------
+ * reset the new TMS9918
+ */
+void vrEmuTms9918Reset(VrEmuTms9918* tms9918)
+{
+}
+
+/* Function: vrEmuTms9918Destroy
+ * --------------------
+ * destroy a TMS9918
+ *
+ * tms9918: tms9918 object to destroy / clean up
+ */
+void vrEmuTms9918Destroy(VrEmuTms9918* tms9918)
+{
+
+}
+
+/* Function: vrEmuTms9918WriteAddr
+ * --------------------
+ * write an address (mode = 1) to the tms9918
+ *
+ * uint8_t: the data (DB0 -> DB7) to send
+ */
+void vrEmuTms9918WriteAddr(VrEmuTms9918* tms9918, uint8_t data)
+{
+ writeTo9918(true, data);
+}
+
+/* Function: vrEmuTms9918WriteData
+ * --------------------
+ * write data (mode = 0) to the tms9918
+ *
+ * uint8_t: the data (DB0 -> DB7) to send
+ */
+void vrEmuTms9918WriteData(VrEmuTms9918* tms9918, uint8_t data)
+{
+ writeTo9918(false, data);
+}
+
+/* Function: vrEmuTms9918ReadStatus
+ * --------------------
+ * read from the status register
+ */
+uint8_t vrEmuTms9918ReadStatus(VrEmuTms9918* tms9918)
+{
+ return readFrom9918(true);
+}
+
+/* Function: vrEmuTms9918ReadData
+ * --------------------
+ * read data (mode = 0) from the tms9918
+ */
+uint8_t vrEmuTms9918ReadData(VrEmuTms9918* tms9918)
+{
+ return readFrom9918(false);
+}
+
+
+/*
+ * Write a register value
+ */
+inline static void vrEmuTms9918WriteRegisterValue(VrEmuTms9918* tms9918, vrEmuTms9918Register reg, uint8_t value)
+{
+ vrEmuTms9918WriteAddr(tms9918, value);
+ vrEmuTms9918WriteAddr(tms9918, 0x80 | (uint8_t)reg);
+}
+
+
+/*
+ * Write a series of bytes to the VRAM
+ */
+inline static void vrEmuTms9918WriteBytes(VrEmuTms9918* tms9918, const uint8_t* bytes, size_t numBytes)
+{
+ for (size_t i = 0; i < numBytes; ++i)
+ {
+ vrEmuTms9918WriteData(tms9918, bytes[i]);
+ }
+}
+
+
+/*
+ * Set current VRAM address for reading
+ */
+inline static void vrEmuTms9918SetAddressRead(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918WriteAddr(tms9918, addr & 0x00ff);
+ vrEmuTms9918WriteAddr(tms9918, ((addr & 0xff00) >> 8));
+}
+
+
+/*
+ * Set current VRAM address for writing
+ */
+inline static void vrEmuTms9918SetAddressWrite(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918SetAddressRead(tms9918, addr | 0x4000);
+}
+
+
+
+/*
+ * Return a colur byte consisting of foreground and background colors
+ */
+inline static uint8_t vrEmuTms9918FgBgColor(vrEmuTms9918Color fg, vrEmuTms9918Color bg)
+{
+ return (uint8_t)((uint8_t)fg << 4) | (uint8_t)bg;
+}
+
+/*
+ * Set name table address
+ */
+inline static void vrEmuTms9918SetNameTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_NAME_TABLE, addr >> 10);
+}
+
+/*
+ * Set color table address
+ */
+inline static void vrEmuTms9918SetColorTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_COLOR_TABLE, (uint8_t)(addr >> 6));
+}
+
+/*
+ * Set pattern table address
+ */
+inline static void vrEmuTms9918SetPatternTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_PATTERN_TABLE, addr >> 11);
+}
+
+/*
+ * Set sprite attribute table address
+ */
+inline static void vrEmuTms9918SetSpriteAttrTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_SPRITE_ATTR_TABLE, (uint8_t)(addr >> 7));
+}
+
+/*
+ * Set sprite pattern table address
+ */
+inline static void vrEmuTms9918SetSpritePattTableAddr(VrEmuTms9918* tms9918, uint16_t addr)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_SPRITE_PATT_TABLE, addr >> 11);
+}
+
+/*
+ * Set foreground (text mode) and background colors
+ */
+inline static void vrEmuTms9918SetFgBgColor(VrEmuTms9918* tms9918, vrEmuTms9918Color fg, vrEmuTms9918Color bg)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_FG_BG_COLOR, vrEmuTms9918FgBgColor(fg, bg));
+}
+
+
+
+void vrEmuTms9918InitialiseGfxII(VrEmuTms9918* tms9918)
+{
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_0, TMS_R0_EXT_VDP_DISABLE | TMS_R0_MODE_GRAPHICS_II);
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_1, TMS_R1_RAM_16K | TMS_R1_MODE_GRAPHICS_II | TMS_R1_RAM_16K | TMS_R1_DISP_ACTIVE | TMS_R1_INT_ENABLE | TMS_R1_SPRITE_MAG2);
+ vrEmuTms9918SetNameTableAddr(tms9918, TMS_DEFAULT_VRAM_NAME_ADDRESS);
+
+ /* in Graphics II, Registers 3 and 4 work differently
+ *
+ * reg3 - Color table
+ * 0x7f = 0x0000
+ * 0xff = 0x2000
+ *
+ * reg4 - Pattern table
+ * 0x03 = 0x0000
+ * 0x07 = 0x2000
+ */
+
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_COLOR_TABLE, 0x7f);
+ vrEmuTms9918WriteRegisterValue(tms9918, TMS_REG_PATTERN_TABLE, 0x07);
+
+ vrEmuTms9918SetSpriteAttrTableAddr(tms9918, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS);
+ vrEmuTms9918SetSpritePattTableAddr(tms9918, TMS_DEFAULT_VRAM_SPRITE_PATT_ADDRESS);
+ vrEmuTms9918SetFgBgColor(tms9918, TMS_BLACK, TMS_CYAN);
+
+ vrEmuTms9918SetAddressWrite(tms9918, TMS_DEFAULT_VRAM_NAME_ADDRESS);
+ for (int i = 0; i < 768; ++i)
+ {
+ vrEmuTms9918WriteData(tms9918, i & 0xff);
+ }
+
+}
+
+
+
+VrEmuTms9918* tms = NULL;
+
+
+void animateSprites(uint64_t frameNumber)
+{
+ for (int i = 0; i < 16; ++i)
+ {
+ float x = sin(frameNumber / 20.0f + i / 3.0f);
+
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i) + 4);
+ uint8_t yPos = (frameNumber / 2 + i * 10 + 24);
+ if (yPos == 0xd0) ++yPos;
+ vrEmuTms9918WriteData(tms, yPos);
+ vrEmuTms9918WriteData(tms, 128 - 8 + (x * 80.0f));
+
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i));
+ if (yPos - 2 == 0xd0) ++yPos;
+ vrEmuTms9918WriteData(tms, yPos - 2);
+ vrEmuTms9918WriteData(tms, 128 - 8 + (x * 80.0f) - 2);
+
+
+ vrEmuTms9918SetAddressRead(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i) + 2);
+ char c = vrEmuTms9918ReadData(tms);
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS + (8 * i) + 2);
+ vrEmuTms9918WriteData(tms, c);
+
+ }
+}
+
+int i = 0;
+
+void onTms9918Interrupt()
+{
+ vrEmuTms9918SetFgBgColor(tms, TMS_WHITE, TMS_CYAN);
+ animateSprites(++i);
+ vrEmuTms9918SetFgBgColor(tms, TMS_WHITE, TMS_BLACK);
+
+ vrEmuTms9918ReadStatus(tms); // clear the interrupt
+}
+
+
+int main(void)
+{
+ set_sys_clock_khz(252000, false);
+
+ uint clocksPioOffset = pio_add_program(pio0, &clock_program);
+
+ uint gromClkSm = pio_claim_unused_sm(pio0, true);
+ uint cpuClkSm = pio_claim_unused_sm(pio0, true);
+
+ clock_program_init(pio0, gromClkSm, clocksPioOffset, GPIO_GROMCL);
+ clock_program_init(pio0, cpuClkSm, clocksPioOffset, GPIO_CPUCL);
+
+ float clockDiv = (float)clock_get_hz(clk_sys) / (TMS_CRYSTAL_FREQ_HZ * 10.0f);
+
+ pio_sm_set_clkdiv(pio0, gromClkSm, clockDiv);
+ pio_sm_set_clkdiv(pio0, cpuClkSm, clockDiv);
+
+ pio_sm_set_enabled(pio0, gromClkSm, true);
+ pio_sm_set_enabled(pio0, cpuClkSm, true);
+
+ const float gromClkFreq = TMS_CRYSTAL_FREQ_HZ / 24.0f;
+ const float cpuClkFreq = TMS_CRYSTAL_FREQ_HZ / 3.0f;
+
+ pio_sm_put(pio0, gromClkSm, (uint)(clock_get_hz(clk_sys) / clockDiv / (2.0f * gromClkFreq)) - 3.0f);
+ pio_sm_put(pio0, cpuClkSm, (uint)(clock_get_hz(clk_sys) / clockDiv / (2.0f * cpuClkFreq)) - 3.0f);
+
+ tms = vrEmuTms9918New();
+
+ sleep_ms(50);
+
+ vrEmuTms9918ReadStatus(tms);
+
+ vrEmuTms9918InitialiseGfxII(tms);
+
+ //while ((vrEmuTms9918ReadStatus(tms) & 0x80) == 0)
+// sleep_ms(10);
+
+ vrEmuTms9918SetFgBgColor(tms, TMS_WHITE, TMS_BLACK);
+
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_PATT_ADDRESS + 32 * 8);
+ vrEmuTms9918WriteBytes(tms, tmsFont, tmsFontBytes);
+
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_COLOR_ADDRESS);
+ vrEmuTms9918WriteBytes(tms, BREAKOUT_TIAC, 6144);
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_PATT_ADDRESS);
+ vrEmuTms9918WriteBytes(tms, BREAKOUT_TIAP, 6144);
+
+ vrEmuTms9918SetAddressWrite(tms, TMS_DEFAULT_VRAM_SPRITE_ATTR_ADDRESS);
+ const char* str = "Hello, World!";
+ const int strLen = strlen(str);
+
+ for (int i = 0; i < strLen; ++i)
+ {
+ vrEmuTms9918WriteData(tms, i * 10 + 24 - 2);
+ vrEmuTms9918WriteData(tms, i * 10 - 2);
+ vrEmuTms9918WriteData(tms, str[strLen - (i + 1)]);
+ vrEmuTms9918WriteData(tms, i + 2);
+
+ vrEmuTms9918WriteData(tms, i * 10 + 24);
+ vrEmuTms9918WriteData(tms, i * 10);
+ vrEmuTms9918WriteData(tms, str[strLen - (i + 1)]);
+ vrEmuTms9918WriteData(tms, 1);
+ }
+
+ for (int i = strLen; i < 16; ++i)
+ {
+ vrEmuTms9918WriteData(tms, 0xd0);
+ vrEmuTms9918WriteData(tms, 0x0);
+ vrEmuTms9918WriteData(tms, 0x0);
+ vrEmuTms9918WriteData(tms, 0x0);
+
+ vrEmuTms9918WriteData(tms, 0xd2);
+ vrEmuTms9918WriteData(tms, 0x0);
+ vrEmuTms9918WriteData(tms, 0x0);
+ vrEmuTms9918WriteData(tms, 0x0);
+ }
+ // }
+
+ gpio_set_irq_enabled_with_callback(GPIO_INT, GPIO_IRQ_EDGE_FALL, true, onTms9918Interrupt);
+
+ while (1)
+ {
+ tight_loop_contents();
+ }
+
+ vrEmuTms9918Destroy(tms);
+
+ return 0;
+}
diff --git a/test/qc/CMakeLists.txt b/test/qc/CMakeLists.txt
index b8632f4..7fa9421 100644
--- a/test/qc/CMakeLists.txt
+++ b/test/qc/CMakeLists.txt
@@ -1,13 +1,13 @@
-set(PROGRAM pico9918qc)
-
-add_executable(${PROGRAM})
-
-target_sources(${PROGRAM} PRIVATE qc.c)
-
-pico_add_extra_outputs(${PROGRAM})
-pico_enable_stdio_usb(${PROGRAM} 1)
-
-target_link_libraries(${PROGRAM} PUBLIC
- pico_stdlib
- pico_time)
-
+set(PROGRAM pico9918qc)
+
+add_executable(${PROGRAM})
+
+target_sources(${PROGRAM} PRIVATE qc.c)
+
+pico_add_extra_outputs(${PROGRAM})
+pico_enable_stdio_usb(${PROGRAM} 1)
+
+target_link_libraries(${PROGRAM} PUBLIC
+ pico_stdlib
+ pico_time)
+
diff --git a/test/qc/qc.c b/test/qc/qc.c
index 1b25de8..02256f5 100644
--- a/test/qc/qc.c
+++ b/test/qc/qc.c
@@ -1,240 +1,240 @@
-/*
- * Project: pico9918
- *
- * Copyright (c) 2024 Troy Schrapel
- *
- * This code is licensed under the MIT license
- *
- * https://github.com/visrealm/pico9918
- *
- */
-
- /*
- * External pins
- *
- * Pin | GPIO | Name | TMS9918A Pin
- * -----+------+--------+-------------
- * 19 | 14 | CD0 | 24
- * 20 | 15 | CD1 | 23
- * 21 | 16 | CD2 | 22
- * 22 | 17 | CD3 | 21
- * 24 | 18 | CD4 | 20
- * 25 | 19 | CD5 | 19
- * 26 | 20 | CD6 | 18
- * 27 | 21 | CD7 | 17
- * 29 | 22 | /INT | 16
- * 30 | RUN | RST | 34
- * 31 | 26 | /CSR | 15
- * 32 | 27 | /CSW | 14
- * 34 | 28 | MODE | 13
- */
-
-#include "pico/stdlib.h"
-
-#include
-#include
-#include
-
- /*
- * Pin mapping (PCB v0.3)
- *
- * Pin | GPIO | Name | TMS9918A Pin
- * -----+------+-----------+-------------
- * 19 | 14 | CD0 | 24
- * 20 | 15 | CD1 | 23
- * 21 | 16 | CD2 | 22
- * 22 | 17 | CD3 | 21
- * 24 | 18 | CD4 | 20
- * 25 | 19 | CD5 | 19
- * 26 | 20 | CD6 | 18
- * 27 | 21 | CD7 | 17
- * 29 | 22 | /INT | 16
- * 30 | RUN | RST | 34
- * 31 | 26 | /CSR | 15
- * 32 | 27 | /CSW | 14
- * 34 | 28 | MODE | 13
- * 35 | 29 | GROMCLK | 37
- * 37 | 23 | CPUCLK | 38
- *
- * Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29
- * a genuine Raspberry Pi Pico can't be used.
- * v0.3 of the PCB is designed for the DWEII?
- * RP2040 USB-C module which exposes these additional
- * GPIOs. A future pico9918 revision (v0.4+) will do without
- * an external RP2040 board and use the RP2040 directly.
- *
- * Purchase links:
- * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X
- * https://www.aliexpress.com/item/1005007066733934.html
- */
-
-#define PCB_MAJOR_VERSION 0
-#define PCB_MINOR_VERSION 4
-
-#define GPIO_CD0 14
-#define GPIO_CSR 26
-#define GPIO_CSW 27
-#define GPIO_MODE 28
-#define GPIO_INT 22
-
-#if PCB_MAJOR_VERSION != 0
-#error "Time traveller?"
-#endif
-
- // pin-mapping for gromclk and cpuclk changed in PCB v0.4
- // in order to have MODE and MODE1 sequential
-#if PCB_MINOR_VERSION < 4
-#error "Not for v0.3 yet"
-#define GPIO_GROMCL 29
-#define GPIO_CPUCL 23
-#else
-#define GPIO_GROMCL 25
-#define GPIO_CPUCL 24
-#define GPIO_RESET 23
-#define GPIO_MODE1 29
-#endif
-
-#define GPIO_CD_MASK (0xff << GPIO_CD0)
-#define GPIO_CSR_MASK (0x01 << GPIO_CSR)
-#define GPIO_CSW_MASK (0x01 << GPIO_CSW)
-#define GPIO_MODE_MASK (0x01 << GPIO_MODE)
-#define GPIO_INT_MASK (0x01 << GPIO_INT)
-#define GPIO_GROMCL_MASK (0x01 << GPIO_GROMCL)
-#define GPIO_CPUCL_MASK (0x01 << GPIO_CPUCL)
-
-// Quality control wiring:
-// CPUCLK to CSR
-// GROMCLK to CSW
-// INT to MODE
-
-int main(void)
-{
- stdio_init_all();
- gpio_init_mask(GPIO_CD_MASK | GPIO_GROMCL_MASK | GPIO_CPUCL_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK);
- gpio_set_dir_all_bits(GPIO_CD_MASK | GPIO_GROMCL_MASK | GPIO_CPUCL_MASK | GPIO_INT_MASK); // set r, w, mode to outputs
-
-start:
- gpio_put_all(0);
-
- printf("PICO9918 QC Tool\n");
-
- bool initialCheckOk;
-
- initialCheckOk = true;
-
-
- printf("Testing CPUCLK and CSR\n");
-
- printf("Set CPUCLK 1\n");
- gpio_put(GPIO_CPUCL, 1);
- sleep_ms(1);
- bool val = gpio_get(GPIO_CSR);
- if (!val) initialCheckOk = false;
- printf("Check CSR %d - %s\n", val, val ? "OK" : "FAILED!");
- sleep_ms(500);
-
- printf("Set CPUCLK 0\n");
- gpio_put(GPIO_CPUCL, 0);
- sleep_ms(1);
- val = gpio_get(GPIO_CSR);
- if (val) initialCheckOk = false;
- printf("Check CSR %d - %s\n", val, !val ? "OK" : "FAILED!");
- sleep_ms(500);
-
-
- printf("Testing GROMCLK and CSW\n");
-
- printf("Set GROMCLK 1\n");
- gpio_put(GPIO_GROMCL, 1);
- sleep_ms(1);
- val = gpio_get(GPIO_CSW);
- if (!val) initialCheckOk = false;
- printf("Check CSW %d - %s\n", val, val ? "OK" : "FAILED!");
- sleep_ms(500);
-
- printf("Set GROMCLK 0\n");
- gpio_put(GPIO_GROMCL, 0);
- sleep_ms(1);
- val = gpio_get(GPIO_CSW);
- if (val) initialCheckOk = false;
- printf("Check CSW %d - %s\n", val, !val ? "OK" : "FAILED!");
- sleep_ms(500);
-
-
-
- printf("Testing INT and MODE\n");
-
- printf("Set INT 1\n");
- gpio_put(GPIO_INT, 1);
- sleep_ms(1);
- val = gpio_get(GPIO_MODE);
- if (!val) initialCheckOk = false;
- printf("Check MODE %d - %s\n", val, val ? "OK" : "FAILED!");
- sleep_ms(500);
-
- printf("Set INT 0\n");
- gpio_put(GPIO_INT, 0);
- sleep_ms(1);
- val = gpio_get(GPIO_MODE);
- if (val) initialCheckOk = false;
- printf("Check MODE %d - %s\n", val, !val ? "OK" : "FAILED!");
- sleep_ms(500);
-
- if (!initialCheckOk)
- {
- printf("Initial check failed. Halting.");
- while (1)
- {
- tight_loop_contents();
- }
- }
- else
- {
- printf("Initial check passed. Continuing...");
- }
-
- uint8_t gpioOut = 1;
-
- printf("CD bit cycle start\n");
-
- for (int i = 0; i < 3 * 8; ++i)
- {
- uint32_t out = GPIO_GROMCL_MASK; // read low (active), write high (inactive)
- out |= (uint32_t)gpioOut << GPIO_CD0;
-
- gpio_put_all(out);
-
- printf("%02x\n", gpioOut);
-
- gpioOut <<= 1;
- if (gpioOut == 0) gpioOut= 1;
-
- sleep_ms(250);
- }
-
- printf("CD bit cycle end\n");
- printf("CD bit cycle OE disabled start\n");
-
- for (int i = 0; i < 3 * 8; ++i)
- {
- uint32_t out = GPIO_GROMCL_MASK | GPIO_CPUCL_MASK; // read low (active), write high (inactive)
- out |= (uint32_t)gpioOut << GPIO_CD0;
-
- gpio_put_all(out);
-
- printf("%02x\n", gpioOut);
-
- gpioOut <<= 1;
- if (gpioOut == 0) gpioOut= 1;
-
- sleep_ms(250);
- }
- printf("CD bit cycle OE disabled end\n");
-
- goto start;
-
-
-
-
- return 0;
-}
+/*
+ * Project: pico9918
+ *
+ * Copyright (c) 2024 Troy Schrapel
+ *
+ * This code is licensed under the MIT license
+ *
+ * https://github.com/visrealm/pico9918
+ *
+ */
+
+ /*
+ * External pins
+ *
+ * Pin | GPIO | Name | TMS9918A Pin
+ * -----+------+--------+-------------
+ * 19 | 14 | CD0 | 24
+ * 20 | 15 | CD1 | 23
+ * 21 | 16 | CD2 | 22
+ * 22 | 17 | CD3 | 21
+ * 24 | 18 | CD4 | 20
+ * 25 | 19 | CD5 | 19
+ * 26 | 20 | CD6 | 18
+ * 27 | 21 | CD7 | 17
+ * 29 | 22 | /INT | 16
+ * 30 | RUN | RST | 34
+ * 31 | 26 | /CSR | 15
+ * 32 | 27 | /CSW | 14
+ * 34 | 28 | MODE | 13
+ */
+
+#include "pico/stdlib.h"
+
+#include
+#include
+#include
+
+ /*
+ * Pin mapping (PCB v0.3)
+ *
+ * Pin | GPIO | Name | TMS9918A Pin
+ * -----+------+-----------+-------------
+ * 19 | 14 | CD0 | 24
+ * 20 | 15 | CD1 | 23
+ * 21 | 16 | CD2 | 22
+ * 22 | 17 | CD3 | 21
+ * 24 | 18 | CD4 | 20
+ * 25 | 19 | CD5 | 19
+ * 26 | 20 | CD6 | 18
+ * 27 | 21 | CD7 | 17
+ * 29 | 22 | /INT | 16
+ * 30 | RUN | RST | 34
+ * 31 | 26 | /CSR | 15
+ * 32 | 27 | /CSW | 14
+ * 34 | 28 | MODE | 13
+ * 35 | 29 | GROMCLK | 37
+ * 37 | 23 | CPUCLK | 38
+ *
+ * Note: Due to GROMCLK and CPUCLK using GPIO23 and GPIO29
+ * a genuine Raspberry Pi Pico can't be used.
+ * v0.3 of the PCB is designed for the DWEII?
+ * RP2040 USB-C module which exposes these additional
+ * GPIOs. A future pico9918 revision (v0.4+) will do without
+ * an external RP2040 board and use the RP2040 directly.
+ *
+ * Purchase links:
+ * https://www.amazon.com/RP2040-Board-Type-C-Raspberry-Micropython/dp/B0CG9BY48X
+ * https://www.aliexpress.com/item/1005007066733934.html
+ */
+
+#define PCB_MAJOR_VERSION 0
+#define PCB_MINOR_VERSION 4
+
+#define GPIO_CD0 14
+#define GPIO_CSR 26
+#define GPIO_CSW 27
+#define GPIO_MODE 28
+#define GPIO_INT 22
+
+#if PCB_MAJOR_VERSION != 0
+#error "Time traveller?"
+#endif
+
+ // pin-mapping for gromclk and cpuclk changed in PCB v0.4
+ // in order to have MODE and MODE1 sequential
+#if PCB_MINOR_VERSION < 4
+#error "Not for v0.3 yet"
+#define GPIO_GROMCL 29
+#define GPIO_CPUCL 23
+#else
+#define GPIO_GROMCL 25
+#define GPIO_CPUCL 24
+#define GPIO_RESET 23
+#define GPIO_MODE1 29
+#endif
+
+#define GPIO_CD_MASK (0xff << GPIO_CD0)
+#define GPIO_CSR_MASK (0x01 << GPIO_CSR)
+#define GPIO_CSW_MASK (0x01 << GPIO_CSW)
+#define GPIO_MODE_MASK (0x01 << GPIO_MODE)
+#define GPIO_INT_MASK (0x01 << GPIO_INT)
+#define GPIO_GROMCL_MASK (0x01 << GPIO_GROMCL)
+#define GPIO_CPUCL_MASK (0x01 << GPIO_CPUCL)
+
+// Quality control wiring:
+// CPUCLK to CSR
+// GROMCLK to CSW
+// INT to MODE
+
+int main(void)
+{
+ stdio_init_all();
+ gpio_init_mask(GPIO_CD_MASK | GPIO_GROMCL_MASK | GPIO_CPUCL_MASK | GPIO_CSR_MASK | GPIO_CSW_MASK | GPIO_MODE_MASK | GPIO_INT_MASK);
+ gpio_set_dir_all_bits(GPIO_CD_MASK | GPIO_GROMCL_MASK | GPIO_CPUCL_MASK | GPIO_INT_MASK); // set r, w, mode to outputs
+
+start:
+ gpio_put_all(0);
+
+ printf("PICO9918 QC Tool\n");
+
+ bool initialCheckOk;
+
+ initialCheckOk = true;
+
+
+ printf("Testing CPUCLK and CSR\n");
+
+ printf("Set CPUCLK 1\n");
+ gpio_put(GPIO_CPUCL, 1);
+ sleep_ms(1);
+ bool val = gpio_get(GPIO_CSR);
+ if (!val) initialCheckOk = false;
+ printf("Check CSR %d - %s\n", val, val ? "OK" : "FAILED!");
+ sleep_ms(500);
+
+ printf("Set CPUCLK 0\n");
+ gpio_put(GPIO_CPUCL, 0);
+ sleep_ms(1);
+ val = gpio_get(GPIO_CSR);
+ if (val) initialCheckOk = false;
+ printf("Check CSR %d - %s\n", val, !val ? "OK" : "FAILED!");
+ sleep_ms(500);
+
+
+ printf("Testing GROMCLK and CSW\n");
+
+ printf("Set GROMCLK 1\n");
+ gpio_put(GPIO_GROMCL, 1);
+ sleep_ms(1);
+ val = gpio_get(GPIO_CSW);
+ if (!val) initialCheckOk = false;
+ printf("Check CSW %d - %s\n", val, val ? "OK" : "FAILED!");
+ sleep_ms(500);
+
+ printf("Set GROMCLK 0\n");
+ gpio_put(GPIO_GROMCL, 0);
+ sleep_ms(1);
+ val = gpio_get(GPIO_CSW);
+ if (val) initialCheckOk = false;
+ printf("Check CSW %d - %s\n", val, !val ? "OK" : "FAILED!");
+ sleep_ms(500);
+
+
+
+ printf("Testing INT and MODE\n");
+
+ printf("Set INT 1\n");
+ gpio_put(GPIO_INT, 1);
+ sleep_ms(1);
+ val = gpio_get(GPIO_MODE);
+ if (!val) initialCheckOk = false;
+ printf("Check MODE %d - %s\n", val, val ? "OK" : "FAILED!");
+ sleep_ms(500);
+
+ printf("Set INT 0\n");
+ gpio_put(GPIO_INT, 0);
+ sleep_ms(1);
+ val = gpio_get(GPIO_MODE);
+ if (val) initialCheckOk = false;
+ printf("Check MODE %d - %s\n", val, !val ? "OK" : "FAILED!");
+ sleep_ms(500);
+
+ if (!initialCheckOk)
+ {
+ printf("Initial check failed. Halting.");
+ while (1)
+ {
+ tight_loop_contents();
+ }
+ }
+ else
+ {
+ printf("Initial check passed. Continuing...");
+ }
+
+ uint8_t gpioOut = 1;
+
+ printf("CD bit cycle start\n");
+
+ for (int i = 0; i < 3 * 8; ++i)
+ {
+ uint32_t out = GPIO_GROMCL_MASK; // read low (active), write high (inactive)
+ out |= (uint32_t)gpioOut << GPIO_CD0;
+
+ gpio_put_all(out);
+
+ printf("%02x\n", gpioOut);
+
+ gpioOut <<= 1;
+ if (gpioOut == 0) gpioOut= 1;
+
+ sleep_ms(250);
+ }
+
+ printf("CD bit cycle end\n");
+ printf("CD bit cycle OE disabled start\n");
+
+ for (int i = 0; i < 3 * 8; ++i)
+ {
+ uint32_t out = GPIO_GROMCL_MASK | GPIO_CPUCL_MASK; // read low (active), write high (inactive)
+ out |= (uint32_t)gpioOut << GPIO_CD0;
+
+ gpio_put_all(out);
+
+ printf("%02x\n", gpioOut);
+
+ gpioOut <<= 1;
+ if (gpioOut == 0) gpioOut= 1;
+
+ sleep_ms(250);
+ }
+ printf("CD bit cycle OE disabled end\n");
+
+ goto start;
+
+
+
+
+ return 0;
+}