diff --git a/.github/workflows/build-common.yml b/.github/workflows/build-common.yml new file mode 100644 index 0000000..7fcd0c0 --- /dev/null +++ b/.github/workflows/build-common.yml @@ -0,0 +1,201 @@ +name: Common Build Steps + +on: + workflow_call: + inputs: + build-firmware: + description: 'Whether to build firmware' + required: false + type: boolean + default: true + build-configurator: + description: 'Whether to build configurator' + required: false + type: boolean + default: false + artifact-name-suffix: + description: 'Suffix for artifact names' + required: true + type: string + run-windows: + description: 'Whether to run Windows job' + required: false + type: boolean + default: true + run-linux: + description: 'Whether to run Linux job' + required: false + type: boolean + default: true + run-macos: + description: 'Whether to run macOS job' + required: false + type: boolean + default: true + +jobs: + windows: + runs-on: windows-latest + if: ${{ inputs.run-windows }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install pillow + + - name: Install ARM GNU Toolchain + run: | + $url = "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-win32-x64.zip" + $output = "arm-toolchain.zip" + Invoke-WebRequest -Uri $url -OutFile $output + Expand-Archive -Path $output -DestinationPath "C:\arm-toolchain" + echo "C:\arm-toolchain\xpack-arm-none-eabi-gcc-13.2.1-1.1\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Configure CMake (with automatic SDK fetch and patch) + run: | + mkdir build + cd build + cmake -S .. -B . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=ON "-DPICO_SDK_FETCH_FROM_GIT_TAG=2.1.1" "-DPICO_BOARD_HEADER_DIRS=../src/boards" -DPICO_BOARD=pico9918 -DBUILD_TOOLS_FROM_SOURCE=ON + + - name: Build Firmware + if: ${{ inputs.build-firmware }} + run: | + cd build + cmake --build . --target firmware + + - name: Build Configurator + if: ${{ inputs.build-configurator }} + run: | + cd build + cmake --build . --target configurator_all + + - name: Upload Firmware Artifacts + if: ${{ inputs.build-firmware }} + uses: actions/upload-artifact@v4 + with: + name: firmware-${{ inputs.artifact-name-suffix }} + path: build/dist/*.uf2 + + - name: Upload Configurator Artifacts + if: ${{ inputs.build-configurator }} + uses: actions/upload-artifact@v4 + with: + name: configurator-${{ inputs.artifact-name-suffix }} + path: | + build/dist/*.rom + build/dist/*.bin + build/dist/*.nabu + build/dist/*.npz + + linux: + runs-on: ubuntu-latest + if: ${{ inputs.run-linux }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential cmake python3 python3-pip git gcc-arm-none-eabi + pip3 install pillow + + - name: Configure CMake (with automatic SDK fetch and patch) + run: | + mkdir build + cd build + cmake -S .. -B . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=ON "-DPICO_SDK_FETCH_FROM_GIT_TAG=2.1.1" "-DPICO_BOARD_HEADER_DIRS=../src/boards" -DPICO_BOARD=pico9918 -DBUILD_TOOLS_FROM_SOURCE=ON + + - name: Build Firmware + if: ${{ inputs.build-firmware }} + run: | + cd build + cmake --build . --target firmware + + - name: Build Configurator + if: ${{ inputs.build-configurator }} + run: | + cd build + cmake --build . --target configurator_all + + - name: Upload Firmware Artifacts + if: ${{ inputs.build-firmware }} + uses: actions/upload-artifact@v4 + with: + name: firmware-${{ inputs.artifact-name-suffix }} + path: build/dist/*.uf2 + + - name: Upload Configurator Artifacts + if: ${{ inputs.build-configurator }} + uses: actions/upload-artifact@v4 + with: + name: configurator-${{ inputs.artifact-name-suffix }} + path: | + build/dist/*.rom + build/dist/*.bin + build/dist/*.nabu + build/dist/*.npz + + macos: + runs-on: macos-latest + if: ${{ inputs.run-macos }} + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install dependencies + run: | + brew install cmake ninja python3 git + pip3 install pillow --break-system-packages + + - name: Install ARM GNU Toolchain + run: | + # Use the same version as other platforms for consistency + curl -L "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-darwin-arm64.tar.gz" -o arm-toolchain.tar.gz + sudo tar -xzf arm-toolchain.tar.gz -C /opt + echo "/opt/xpack-arm-none-eabi-gcc-13.2.1-1.1/bin" >> $GITHUB_PATH + + - name: Configure CMake (with automatic SDK fetch and patch) + run: | + mkdir build + cd build + cmake -S .. -B . -G Ninja -DPICO_SDK_FETCH_FROM_GIT=ON "-DPICO_SDK_FETCH_FROM_GIT_TAG=2.1.1" "-DPICO_BOARD_HEADER_DIRS=../src/boards" -DPICO_BOARD=pico9918 -DBUILD_TOOLS_FROM_SOURCE=ON + + - name: Build Firmware + if: ${{ inputs.build-firmware }} + run: | + cd build + cmake --build . --target firmware + + - name: Build Configurator + if: ${{ inputs.build-configurator }} + run: | + cd build + cmake --build . --target configurator_all + + - name: Upload Firmware Artifacts + if: ${{ inputs.build-firmware }} + uses: actions/upload-artifact@v4 + with: + name: firmware-${{ inputs.artifact-name-suffix }} + path: build/dist/*.uf2 + + - name: Upload Configurator Artifacts + if: ${{ inputs.build-configurator }} + uses: actions/upload-artifact@v4 + with: + name: configurator-${{ inputs.artifact-name-suffix }} + path: | + build/dist/*.rom + build/dist/*.bin + build/dist/*.nabu + build/dist/*.npz \ No newline at end of file diff --git a/.github/workflows/configurator-linux.yml b/.github/workflows/configurator-linux.yml new file mode 100644 index 0000000..6115494 --- /dev/null +++ b/.github/workflows/configurator-linux.yml @@ -0,0 +1,19 @@ +name: Configurator Linux + +on: + push: + branches: [ '**' ] + pull_request: + branches: [ '**' ] + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build-common.yml + with: + build-firmware: true + build-configurator: true + artifact-name-suffix: linux + run-windows: false + run-linux: true + run-macos: false \ No newline at end of file diff --git a/.github/workflows/configurator-macos.yml b/.github/workflows/configurator-macos.yml new file mode 100644 index 0000000..871b8c3 --- /dev/null +++ b/.github/workflows/configurator-macos.yml @@ -0,0 +1,19 @@ +name: Configurator macOS + +on: + push: + branches: [ '**' ] + pull_request: + branches: [ '**' ] + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build-common.yml + with: + build-firmware: true + build-configurator: true + artifact-name-suffix: macos + run-windows: false + run-linux: false + run-macos: true \ No newline at end of file diff --git a/.github/workflows/configurator-windows.yml b/.github/workflows/configurator-windows.yml new file mode 100644 index 0000000..de3f15b --- /dev/null +++ b/.github/workflows/configurator-windows.yml @@ -0,0 +1,19 @@ +name: Configurator Windows + +on: + push: + branches: [ '**' ] + pull_request: + branches: [ '**' ] + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build-common.yml + with: + build-firmware: true + build-configurator: true + artifact-name-suffix: windows + run-windows: true + run-linux: false + run-macos: false \ No newline at end of file diff --git a/.github/workflows/configurator.yml b/.github/workflows/configurator.yml deleted file mode 100644 index 0c580a9..0000000 --- a/.github/workflows/configurator.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Configurator - -on: - workflow_run: - workflows: ["Firmware"] - types: - - completed - branches: - - '**' - -jobs: - windows: - runs-on: windows-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_branch }} - submodules: recursive - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install pillow - - - name: Download firmware artifact from workflow - uses: actions/github-script@v7 - with: - script: | - let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); - let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { - return artifact.name == "firmware-windows" - })[0]; - let download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - let fs = require('fs'); - fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/firmware-windows.zip`, Buffer.from(download.data)); - - - name: Extract firmware artifact - run: | - Expand-Archive -Path firmware-windows.zip -DestinationPath build/dist - Remove-Item firmware-windows.zip - - - name: Setup build environment and build configurator - run: | - cd build - cmake -S .. -B . -G Ninja -DBUILD_TOOLS_FROM_SOURCE=ON -DCONFIGURATOR_ONLY=ON - cmake --build . --target configurator_all - - - name: Upload Configurator Artifacts - uses: actions/upload-artifact@v4 - with: - name: configurator-windows - path: | - build/dist/*.rom - build/dist/*.bin - build/dist/*.nabu - build/dist/*.npz - - linux: - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' }} - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_branch }} - submodules: recursive - - - name: Download firmware artifact from workflow - uses: actions/github-script@v7 - with: - script: | - let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: context.payload.workflow_run.id, - }); - let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { - return artifact.name == "firmware-linux" - })[0]; - let download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: matchArtifact.id, - archive_format: 'zip', - }); - let fs = require('fs'); - fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/firmware-linux.zip`, Buffer.from(download.data)); - - - name: Extract firmware artifact - run: | - mkdir -p build/dist - unzip firmware-linux.zip -d build/dist - rm firmware-linux.zip - - - name: Setup build environment and build configurator - run: | - cd build - cmake -S .. -B . -G Ninja -DBUILD_TOOLS_FROM_SOURCE=ON -DCONFIGURATOR_ONLY=ON - cmake --build . --target configurator_all - - - name: Upload Configurator Artifacts - uses: actions/upload-artifact@v4 - with: - name: configurator-linux - path: | - build/dist/*.rom - build/dist/*.bin - build/dist/*.nabu - build/dist/*.npz diff --git a/.github/workflows/firmware-linux.yml b/.github/workflows/firmware-linux.yml new file mode 100644 index 0000000..bc4c0b9 --- /dev/null +++ b/.github/workflows/firmware-linux.yml @@ -0,0 +1,19 @@ +name: Firmware Linux + +on: + push: + branches: [ '**' ] + pull_request: + branches: [ '**' ] + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build-common.yml + with: + build-firmware: true + build-configurator: false + artifact-name-suffix: linux + run-windows: false + run-linux: true + run-macos: false \ No newline at end of file diff --git a/.github/workflows/firmware-macos.yml b/.github/workflows/firmware-macos.yml new file mode 100644 index 0000000..7b2583f --- /dev/null +++ b/.github/workflows/firmware-macos.yml @@ -0,0 +1,19 @@ +name: Firmware macOS + +on: + push: + branches: [ '**' ] + pull_request: + branches: [ '**' ] + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build-common.yml + with: + build-firmware: true + build-configurator: false + artifact-name-suffix: macos + run-windows: false + run-linux: false + run-macos: true \ No newline at end of file diff --git a/.github/workflows/firmware-windows.yml b/.github/workflows/firmware-windows.yml new file mode 100644 index 0000000..c5718bf --- /dev/null +++ b/.github/workflows/firmware-windows.yml @@ -0,0 +1,19 @@ +name: Firmware Windows + +on: + push: + branches: [ '**' ] + pull_request: + branches: [ '**' ] + workflow_dispatch: + +jobs: + build: + uses: ./.github/workflows/build-common.yml + with: + build-firmware: true + build-configurator: false + artifact-name-suffix: windows + run-windows: true + run-linux: false + run-macos: false \ No newline at end of file diff --git a/.github/workflows/firmware.yml b/.github/workflows/firmware.yml deleted file mode 100644 index 4e0a560..0000000 --- a/.github/workflows/firmware.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Firmware - -on: - push: - branches: [ '**' ] - pull_request: - branches: [ '**' ] - -jobs: - windows: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install pillow - - - name: Install ARM GNU Toolchain - run: | - $url = "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-win32-x64.zip" - $output = "arm-toolchain.zip" - Invoke-WebRequest -Uri $url -OutFile $output - Expand-Archive -Path $output -DestinationPath "C:\arm-toolchain" - echo "C:\arm-toolchain\xpack-arm-none-eabi-gcc-13.2.1-1.1\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - - name: Install Pico SDK 2.1.1 manually - run: | - git clone -b 2.1.1 --depth 1 https://github.com/raspberrypi/pico-sdk.git pico-sdk - cd pico-sdk - git submodule update --init - git apply --ignore-whitespace --ignore-space-change --3way ../picosdk-2.0.0-visrealm-fastboot.patch || echo "Patch failed or already applied" - cd .. - - - name: Configure CMake - run: | - mkdir build - cd build - cmake -S .. -B . -G Ninja -DPICO_SDK_PATH="$PWD/../pico-sdk" -DPICO_BOARD_HEADER_DIRS="../src/boards" -DPICO_BOARD=pico9918 - - - name: Build Firmware - run: | - cd build - cmake --build . --target firmware - - - name: Upload Firmware Artifacts - uses: actions/upload-artifact@v4 - with: - name: firmware-windows - path: build/dist/*.uf2 - - linux: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential cmake python3 python3-pip git gcc-arm-none-eabi - pip3 install pillow - - - name: Install Pico SDK 2.1.1 manually - run: | - git clone -b 2.1.1 --depth 1 https://github.com/raspberrypi/pico-sdk.git pico-sdk - cd pico-sdk - git submodule update --init - git apply --ignore-whitespace --ignore-space-change --3way ../picosdk-2.0.0-visrealm-fastboot.patch || echo "Patch failed or already applied" - cd .. - - - name: Configure CMake - run: | - mkdir build - cd build - cmake -S .. -B . -G Ninja -DPICO_SDK_PATH="$PWD/../pico-sdk" -DPICO_BOARD_HEADER_DIRS="../src/boards" -DPICO_BOARD=pico9918 - - - name: Build Firmware - run: | - cd build - cmake --build . --target firmware - - - name: Upload Firmware Artifacts - uses: actions/upload-artifact@v4 - with: - name: firmware-linux - path: build/dist/*.uf2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8cece39..50da69e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ CMakeLists.txt.user CMakeCache.txt CMakeFiles CMakeScripts +Claude.md Testing Makefile cmake_install.cmake @@ -11,4 +12,5 @@ CTestTestfile.cmake _deps build/** testapps/** -configtool/build/** \ No newline at end of file +configtool/build/** +configtool/CLAUDE.md \ No newline at end of file diff --git a/BUILDING.md b/BUILDING.md index 5816153..b231e59 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -6,28 +6,96 @@ This document describes how to build the PICO9918 firmware and configurator ROMs ### Required Tools - **CMake 3.13+**: Build system generator +- **ARM GNU Toolchain**: Cross-compiler for ARM Cortex-M0+ (RP2040) - **Raspberry Pi Pico SDK**: Firmware compilation (v2.1.1 recommended for compatibility) - **Python 3**: Build scripts and asset conversion - **Git**: For submodules and dependencies -### Development Environment Setup +### Platform-Specific Setup -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. +#### Windows +```bash +# Install dependencies +# Download and install ARM GNU Toolchain 13.2.1-1.1 from: +# https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-win32-x64.zip +# Extract to C:\arm-toolchain\ and add to PATH -The latest PICO9918 source can be configured and built using the official [Raspberry Pi Pico VSCode plugin](https://github.com/raspberrypi/pico-vscode). +# Python dependencies +pip install pillow -### Python Dependencies +# Install Pico SDK 2.1.1 +git clone -b 2.1.1 --depth 1 https://github.com/raspberrypi/pico-sdk.git pico-sdk +cd pico-sdk +git submodule update --init +# Apply performance patches +git apply --ignore-whitespace --ignore-space-change --3way ../picosdk-2.0.0-visrealm-fastboot.patch +cd .. +``` + +#### Linux (Ubuntu/Debian) ```bash -pip install pillow # Image processing for splash screen and fonts +# Install system dependencies +sudo apt-get update +sudo apt-get install -y build-essential cmake python3 python3-pip git gcc-arm-none-eabi + +# Python dependencies +pip3 install pillow + +# Install Pico SDK 2.1.1 +git clone -b 2.1.1 --depth 1 https://github.com/raspberrypi/pico-sdk.git pico-sdk +cd pico-sdk +git submodule update --init +# Apply performance patches +git apply --ignore-whitespace --ignore-space-change --3way ../picosdk-2.0.0-visrealm-fastboot.patch +cd .. ``` +#### macOS +```bash +# Install dependencies via Homebrew +brew install cmake ninja python3 git + +# Install ARM GNU Toolchain (same version as other platforms for consistency) +curl -L "https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-darwin-arm64.tar.gz" -o arm-toolchain.tar.gz +sudo tar -xzf arm-toolchain.tar.gz -C /opt +echo 'export PATH="/opt/xpack-arm-none-eabi-gcc-13.2.1-1.1/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc + +# Python dependencies (may require --break-system-packages on newer macOS) +pip3 install pillow + +# Install Pico SDK 2.1.1 +git clone -b 2.1.1 --depth 1 https://github.com/raspberrypi/pico-sdk.git pico-sdk +cd pico-sdk +git submodule update --init +# Apply performance patches +git apply --ignore-whitespace --ignore-space-change --3way ../picosdk-2.0.0-visrealm-fastboot.patch +cd .. +``` + +### Development Environment Setup + +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). + ## Building Firmware The PICO9918 firmware is the primary component - a TMS9918A VDP emulator for Raspberry Pi Pico. ### Quick Start - Firmware Only + +**Option 1: Automatic SDK Download (Recommended)** ```bash mkdir build && cd build +cmake .. -DPICO_SDK_FETCH_FROM_GIT=ON -DPICO_SDK_FETCH_FROM_GIT_TAG=2.1.1 +cmake --build . --target firmware +``` + +**Option 2: Manual SDK Setup** +```bash +# First: Follow platform-specific setup above to install SDK +mkdir build && cd build cmake .. cmake --build . --target firmware ``` @@ -94,20 +162,34 @@ Set build options in `.vscode/settings.json`: - **DMA**: Memory transfers and sprite processing - **Flash**: Configuration storage in upper 1MB -### Automatic SDK Patches -The build system automatically applies performance patches to the Pico SDK: -- **Fast Boot**: Optimizes ROSC (Ring Oscillator) for faster boot times -- Patches are applied automatically when using `PICO_SDK_FETCH_FROM_GIT=ON` +### SDK Performance Patch + +> **✅ Automatic Patch Application** +> +> A performance patch is automatically applied for optimal boot times: +> - **Fast Boot**: Optimizes ROSC (Ring Oscillator) for faster startup +> - **Automatic**: Applied by CMake when using `PICO_SDK_FETCH_FROM_GIT=ON` +> - **Manual Setup**: Still required when manually installing SDK (see platform instructions above) + +#### How It Works +- **FetchContent builds**: CMake automatically applies `picosdk-2.0.0-visrealm-fastboot.patch` after downloading the SDK +- **Manual SDK installs**: You must run the `git apply` command shown in platform setup above +- **Safe Operation**: Patch command includes fallback - build continues even if patch fails ## Building Configurator The configurator creates ROM files for retro computers that can upload firmware to PICO9918. ### Prerequisites - Configurator -In addition to firmware requirements: -- **CVBasic**: Retro BASIC compiler (auto-built if missing) -- **GASM80**: Z80 assembler (auto-built if missing) -- **XDT99**: TI-99/4A tools (auto-built if missing) + +> **✅ No Manual Tool Installation Required!** +> +> The build system **automatically downloads and builds** all required tools: +> - **CVBasic** (Retro BASIC compiler) +> - **GASM80** (Z80 assembler) +> - **XDT99** (TI-99/4A development tools) +> +> Simply run the build commands below - all tools will be built from source automatically. ### Quick Start - Configurator ```bash @@ -148,15 +230,21 @@ cmake --build . --target creativision # CreatiVision All configurator tasks automatically depend on firmware build. ### Tool Auto-Building -By default, the build system automatically builds CVBasic, GASM80, and XDT99 from source. This ensures builds work on any platform without pre-installed tools. -The system will: -1. Clone tool repositories from GitHub -2. Build tools from source using CMake -3. Cache built tools for subsequent builds -4. Use locally-built tools for ROM generation +> **🚀 Zero-Configuration Tool Management** +> +> **By default**, the build system automatically handles all configurator tools: + +The system will automatically: +1. **Clone** tool repositories from GitHub +2. **Build** tools from source using CMake +3. **Cache** built tools for subsequent builds +4. **Use** locally-built tools for ROM generation -To use existing tools instead (if available): +**No manual tool installation needed!** Works on all platforms out-of-the-box. + +#### Advanced: Use Pre-installed Tools (Optional) +If you have CVBasic, GASM80, and XDT99 already installed in PATH: ```bash cmake .. -DBUILD_TOOLS_FROM_SOURCE=OFF ``` @@ -200,24 +288,35 @@ ninja ## Cross-Platform Support -### Windows -- **Native**: MSYS2, Visual Studio, or Clang -- **WSL**: Windows Subsystem for Linux +All major platforms are supported with consistent toolchains and build processes. See the **Platform-Specific Setup** section above for detailed installation instructions. -### Linux -- **Native**: GCC or Clang toolchain -- **Dependencies**: `build-essential cmake python3 python3-pip git` +### Platform Summary +- **Windows**: Native build with ARM GNU Toolchain 13.2.1-1.1 +- **Linux**: Native build with `gcc-arm-none-eabi` package +- **macOS**: Native build with ARM GNU Toolchain 13.2.1-1.1 (for consistency) +- **WSL**: Use Linux instructions within Windows Subsystem for Linux -### macOS -- **Native**: Xcode command line tools -- **Dependencies**: `brew install cmake python3 git` +### Important Notes +- **Toolchain Consistency**: All platforms use ARM GNU Toolchain 13.2.1-1.1 to ensure identical builds +- **macOS Python**: May require `--break-system-packages` flag for pip on newer macOS versions +- **SDK Version**: Use Pico SDK 2.1.1 specifically - newer versions may cause linker issues ### Continuous Integration -The project includes GitHub Actions workflows that automatically build: +The project includes GitHub Actions workflows that automatically build on every push: + +#### Individual Platform Workflows +- **Firmware Windows**: `firmware-windows.yml` +- **Firmware Linux**: `firmware-linux.yml` +- **Firmware macOS**: `firmware-macos.yml` +- **Configurator Windows**: `configurator-windows.yml` +- **Configurator Linux**: `configurator-linux.yml` +- **Configurator macOS**: `configurator-macos.yml` + +#### Build Outputs - **Firmware**: `.uf2` files for Raspberry Pi Pico - **Configurator ROMs**: All retro platform ROM files -- **Cross-platform**: Both Windows and Linux builds - **Artifacts**: Build outputs available for download from successful runs +- **Badges**: Individual build status badges for each OS and build type ## Output Structure @@ -259,6 +358,18 @@ cmake .. # Note: PICO_SDK_FETCH_FROM_GIT_TAG has known issues with tag resolution ``` +**SDK patch issues** +```bash +# If patch fails to apply: +git apply --ignore-whitespace --ignore-space-change --3way ../picosdk-2.0.0-visrealm-fastboot.patch + +# If patch was already applied or conflicts: +echo "Patch failed or already applied" # This is normal, firmware will still build + +# Patch is optional but improves boot performance +# Firmware works without it, just boots slower +``` + **Missing splash/font assets** ```bash # Install pillow for image conversion diff --git a/README.md b/README.md index 69552f4..92edd49 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,19 @@ The TMS9918A emulation is handled by my [vrEmuTms9918 library](https://github.co ## Build status -| Branch | Firmware | Configurator | -|--------|----------|--------------| -| main | [![Firmware](https://github.com/visrealm/pico9918/actions/workflows/firmware.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/firmware.yml) | [![Configurator](https://github.com/visrealm/pico9918/actions/workflows/configurator.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/configurator.yml) | -| dev | [![Firmware](https://github.com/visrealm/pico9918/actions/workflows/firmware.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/firmware.yml) | [![Configurator](https://github.com/visrealm/pico9918/actions/workflows/configurator.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/configurator.yml) | +### Main Branch + +| Build | Windows | Linux | macOS | +|-------|---------|-------|-------| +| Firmware | [![](https://github.com/visrealm/pico9918/actions/workflows/firmware-windows.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/firmware-windows.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/firmware-linux.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/firmware-linux.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/firmware-macos.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/firmware-macos.yml) | +| Configurator | [![](https://github.com/visrealm/pico9918/actions/workflows/configurator-windows.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/configurator-windows.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/configurator-linux.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/configurator-linux.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/configurator-macos.yml/badge.svg?branch=main)](https://github.com/visrealm/pico9918/actions/workflows/configurator-macos.yml) | + +### Dev Branch + +| Build | Windows | Linux | macOS | +|-------|---------|-------|-------| +| Firmware | [![](https://github.com/visrealm/pico9918/actions/workflows/firmware-windows.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/firmware-windows.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/firmware-linux.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/firmware-linux.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/firmware-macos.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/firmware-macos.yml) | +| Configurator | [![](https://github.com/visrealm/pico9918/actions/workflows/configurator-windows.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/configurator-windows.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/configurator-linux.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/configurator-linux.yml) | [![](https://github.com/visrealm/pico9918/actions/workflows/configurator-macos.yml/badge.svg?branch=dev)](https://github.com/visrealm/pico9918/actions/workflows/configurator-macos.yml) | ## Supported devices @@ -83,9 +92,9 @@ Also (more convenient for North America) There are two main variants of the hardware. -### v1.x (v1.2, v1.1, v1.0 and v0.4) +### v1.x (v1.3, v1.2, v1.1, v1.0 and v0.4) -PICO9918 v1.2 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. +PICO9918 v1.3 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.

PICO9918 v1.2

@@ -111,17 +120,27 @@ To install, just hold the 'BOOTSEL' (or 'BOOT') button while plugging the Pico i ## Building -Build both firmware and configurator ROMs with the unified CMake system: +Quick start - build both firmware and configurator ROMs: ```bash +# Automatic SDK download (recommended) mkdir build && cd build -cmake .. +cmake .. -DPICO_SDK_FETCH_FROM_GIT=ON -DPICO_SDK_FETCH_FROM_GIT_TAG=2.1.1 cmake --build . ``` Output in `build/dist/`: firmware `.uf2` file and configurator ROMs for all retro platforms. -📖 **[Complete Build Instructions](BUILDING.md)** - includes development environment setup, configuration options, platform-specific builds, and troubleshooting. +### Platform-Specific Setup Required + +Each platform requires specific toolchain installation: +- **Windows**: ARM GNU Toolchain 13.2.1-1.1, Python with pillow +- **Linux**: `build-essential cmake python3 python3-pip git gcc-arm-none-eabi` +- **macOS**: Homebrew + ARM GNU Toolchain 13.2.1-1.1, may need `--break-system-packages` + +All platforms use **Raspberry Pi Pico SDK 2.1.1** specifically (newer versions may cause issues). + +📖 **[Complete Build Instructions](BUILDING.md)** - includes detailed platform setup, development environment configuration, build options, individual platform builds, VSCode integration, and troubleshooting. ## Thanks diff --git a/configtool/CMakeLists.txt b/configtool/CMakeLists.txt index 3968273..06a9b29 100644 --- a/configtool/CMakeLists.txt +++ b/configtool/CMakeLists.txt @@ -19,6 +19,11 @@ setup_cvbasic_tools() # Firmware dependency (checked at build time, not configure time) set(FIRMWARE_FILE "${CMAKE_BINARY_DIR}/dist/pico9918-vga-build-${VERSION}.uf2") +if(NOT CONFIGURATOR_ONLY) + set(FIRMWARE_TARGET "firmware") # Target that builds the firmware +else() + set(FIRMWARE_TARGET "") # No target dependency when building configurator only +endif() # Custom function to convert UF2 to CVBasic data function(convert_uf2_to_cvbasic BANK_SIZE OUTPUT_PREFIX) @@ -51,6 +56,19 @@ function(add_cvbasic_target TARGET_NAME PLATFORM_FLAG DEFINES ASM_OUTPUT ROM_OUT file(MAKE_DIRECTORY "${ASM_DIR}") file(MAKE_DIRECTORY "${DIST_DIR}") + # Collect all CVBasic source files for dependency tracking + # This ensures any change to .bas files or .asm libraries triggers a rebuild + file(GLOB CVBASIC_SOURCE_FILES + "${CMAKE_SOURCE_DIR}/configtool/src/*.bas" + "${CMAKE_SOURCE_DIR}/configtool/src/lib/*.asm" + ) + + # Collect tools that affect the build + # Changes to these Python tools will trigger rebuilds + file(GLOB CVBASIC_TOOLS + "${CMAKE_SOURCE_DIR}/configtool/tools/*.py" + ) + # Copy source files and generated firmware data add_custom_command( OUTPUT "${SOURCE_DIR}/pico9918conf.bas" @@ -61,7 +79,7 @@ function(add_cvbasic_target TARGET_NAME PLATFORM_FLAG DEFINES ASM_OUTPUT ROM_OUT COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/intermediate/firmware_16k.bas" "${SOURCE_DIR}/" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/intermediate/firmware.h.bas" "${SOURCE_DIR}/" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_BINARY_DIR}/intermediate/firmware.bas" "${SOURCE_DIR}/" - DEPENDS ${CMAKE_SOURCE_DIR}/configtool/src/pico9918conf.bas + DEPENDS ${CVBASIC_SOURCE_FILES} ${CVBASIC_TOOLS} "${CMAKE_BINARY_DIR}/intermediate/firmware_8k.h.bas" "${CMAKE_BINARY_DIR}/intermediate/firmware_8k.bas" "${CMAKE_BINARY_DIR}/intermediate/firmware_16k.h.bas" "${CMAKE_BINARY_DIR}/intermediate/firmware_16k.bas" "${CMAKE_BINARY_DIR}/intermediate/firmware.h.bas" "${CMAKE_BINARY_DIR}/intermediate/firmware.bas" @@ -100,11 +118,11 @@ function(add_cvbasic_target TARGET_NAME PLATFORM_FLAG DEFINES ASM_OUTPUT ROM_OUT add_custom_command( OUTPUT "${DIST_DIR}/${ROM_OUTPUT}" COMMAND ${PYTHON} "${XAS99_SCRIPT}" -b -R "${ASM_DIR}/${ASM_OUTPUT}" - COMMAND ${PYTHON} "${CMAKE_SOURCE_DIR}/configtool/tools/cvbasic/linkticart.py" - "${ASM_DIR}/${ASM_NAME}_b00.bin" - "${DIST_DIR}/${ROM_OUTPUT}" + COMMAND ${PYTHON} "${LINKTICART_SCRIPT}" + "${ASM_DIR}/${ASM_NAME}_b00.bin" + "${DIST_DIR}/${ROM_OUTPUT}" "PICO9918 ${FRIENDLYVER}" - DEPENDS "${ASM_DIR}/${ASM_OUTPUT}" + DEPENDS "${ASM_DIR}/${ASM_OUTPUT}" ${TOOL_DEPENDENCIES} WORKING_DIRECTORY "${ASM_DIR}" COMMENT "Assembling ${DESCRIPTION}" VERBATIM @@ -130,7 +148,12 @@ function(add_cvbasic_target TARGET_NAME PLATFORM_FLAG DEFINES ASM_OUTPUT ROM_OUT ) endif() - add_custom_target(${TARGET_NAME} DEPENDS "${DIST_DIR}/${ROM_OUTPUT}") + # Add firmware target dependency if building full project + if(FIRMWARE_TARGET) + add_custom_target(${TARGET_NAME} DEPENDS "${DIST_DIR}/${ROM_OUTPUT}" ${FIRMWARE_TARGET}) + else() + add_custom_target(${TARGET_NAME} DEPENDS "${DIST_DIR}/${ROM_OUTPUT}") + endif() endfunction() # Define all platform targets @@ -167,6 +190,7 @@ message(STATUS "PICO9918 Configurator CMake Configuration") message(STATUS "Version: ${FRIENDLYVER}") message(STATUS "CVBasic: ${CVBASIC_EXE}") message(STATUS "GASM80: ${GASM80_EXE}") +message(STATUS "linkticart.py: ${LINKTICART_SCRIPT}") if(XAS99_SCRIPT) message(STATUS "XAS99: ${XAS99_SCRIPT}") else() diff --git a/configtool/src/lib/cvbasic_9900_prologue.asm b/configtool/src/lib/cvbasic_9900_prologue.asm index f25afc6..903321f 100644 --- a/configtool/src/lib/cvbasic_9900_prologue.asm +++ b/configtool/src/lib/cvbasic_9900_prologue.asm @@ -510,11 +510,26 @@ define_color update_sprite srl r4,8 ; make word sla r4,2 ; x4 for address + .ifeq CVBASIC_DIRECT_SPRITES ai r4,sprites ; sprite mirror address movb r5,*r4+ ; move bytes movb r6,*r4+ ; move bytes movb r7,*r4+ ; move bytes movb r0,*r4+ ; move bytes + .else + limi 0 + ai r4,>5b00 ; >1b00 with the write bit added + swpb r4 + movb r4,@VDPWADR ; SAL address + swpb r4 + movb r4,@VDPWADR ; going to copy the sprite table to VDP + nop + movb r5,@VDPWDATA + movb r6,@VDPWDATA + movb r7,@VDPWDATA + movb r0,@VDPWDATA + limi 2 + .endif b *r11 ; SGN R0 - return 1, -1 or 0 as 16 bit @@ -824,6 +839,7 @@ int_handler mov @>7ffe,@saved_bank ; save bank switch page .endif + .ifeq CVBASIC_DIRECT_SPRITES li r11,>005b ; >1b00 with the write bit added, and byte flipped movb r11,@VDPWADR ; SAL address swpb r11 @@ -861,6 +877,7 @@ int_handler dec r12 jne -!6 !5 + .endif ; !CVBASIC_DIRECT_SPRITES ; next read the joysticks - output needs to be 21xxLDRU - 1 and 2 are button and button2 respectively ; We don't have a button 2. We also need to read the keyboard and fill in key1_data. key2_data we diff --git a/configtool/src/menu-firmware.bas b/configtool/src/menu-firmware.bas index 55a47a5..176e098 100644 --- a/configtool/src/menu-firmware.bas +++ b/configtool/src/menu-firmware.bas @@ -170,7 +170,7 @@ firmwareWriteAndVerify: PROCEDURE R = R + 1 WEND - IF FWST AND $06 THEN + IF FWST AND $1c THEN PRINT AT BLOCKPOS(#FWBLOCK), CHR$(2) STATUS = 0 ELSE diff --git a/configtool/tools/cvbasic/cvbasic.exe b/configtool/tools/cvbasic/cvbasic.exe deleted file mode 100644 index abbe3bf..0000000 Binary files a/configtool/tools/cvbasic/cvbasic.exe and /dev/null differ diff --git a/configtool/tools/cvbasic/gasm80.exe b/configtool/tools/cvbasic/gasm80.exe deleted file mode 100644 index 92fdbb5..0000000 Binary files a/configtool/tools/cvbasic/gasm80.exe and /dev/null differ diff --git a/configtool/tools/cvbasic/linkticart.py b/configtool/tools/cvbasic/linkticart.py deleted file mode 100644 index 1e6a9c3..0000000 --- a/configtool/tools/cvbasic/linkticart.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python3 - -import os.path -import sys - -# take the first name, and return (base,zeros) -# where zeros is how many leading zeros are in the index -def parseFilename(fn): - zerocnt = 1 - namebase = fn[:-5] - while (namebase[-1] == '0'): - zerocnt += 1 - namebase = namebase[:-1] - return (namebase, zerocnt) - -# converts a binary from xas99 (xas99.py -b -R file.a99) with banks to a single non-inverted cart image -# note: minimal error checking - GIGO. -# pass the name of the first file (ie: file_b0.bin) -if (len(sys.argv) < 3): - print('Pass the first output file (ie: file_b0.bin), and the output file, and optionally a name for the cart') - print('ie: linkticart.py file_b0.bin file_8.bin "AWESOME GAME"') - exit(1) - -f = open(sys.argv[1], 'rb') -data = f.read() -f.close() - -# first 80 bytes are the cartridge header -hdr = data[0:80] - -if (len(sys.argv) > 3): - name = sys.argv[3].upper() - while (len(name)<20): - name += ' ' - p = hdr.find(b'CVBASIC GAME *') - if p == -1: - print('WARNING: Could not find cart name to set it') - else: - hdr = hdr[0:p] + bytearray(name, 'utf-8') + hdr[p+20:] - -# after 16k starts the RAM data -ram = data[16384:] - -# make sure we have 3 pages to pull from (especially if not banked, it won't be padded) -while len(ram) < 8192*3: - ram += b'\xff'*8192 - -fo = open(sys.argv[2], 'wb') - -# write the loader pages -fo.write(hdr) -fo.write(ram[0:8112]) -fo.write(hdr) -fo.write(ram[8112:16224]) -fo.write(hdr) -fo.write(ram[16224:24336]) -# any excess is discarded - -# now check if there are any pages to concatenate -# track pages written so we can square up the final size -sz = 3 - -if (sys.argv[1][-5:] != '0.bin'): - print('Banking not detected - finishing cart...') -else: - print('Banked cart detected...') - - (namebase,zerocnt) = parseFilename(sys.argv[1]) - - file = namebase + str(sz).zfill(zerocnt) + '.bin' - - while os.path.isfile(file): - f = open(file, 'rb') - data = f.read() - if len(data) < 8192: - while len(data)<8192: - data += b'\xff' - f.close() - fo.write(data) - sz+=1 - file = namebase + str(sz).zfill(zerocnt) + '.bin' - -# calculate number of files needed for power of two -desired=0 -if sz>64: - desired=128 -elif sz>32: - desired=64 -elif sz>16: - desired=32 -elif sz>8: - desired=16 -elif sz>4: - desired=8 -else: - desired=4 - -while sz int: header.write("\n' ===============================\n") header.write(" GOTO CONFTOOL_START\n") header.write("firmwareFilename:\n") - header.write(" DATA BYTE \"{0}\"\n".format(os.path.basename(filename.ljust(32)))) + header.write(" DATA BYTE \"{0}\"\n".format(os.path.basename(filename).ljust(32))) header.write("\n\n") header.write(" CONST #FIRMWARE_BLOCKS = {0}\n".format(w[6])) header.write(" CONST FIRMWARE_BANKS = {0}\n".format(int(w[6] / BLOCKS_PER_BANK) + 1)) diff --git a/pcb/README.md b/pcb/README.md index 7b43aea..a34d9d6 100644 --- a/pcb/README.md +++ b/pcb/README.md @@ -10,7 +10,7 @@ This version is relatively cheap and easy to build and is powered by a piggyback See [PICO9918 v0.3](v0.3) -### Fully integrated single board (v0.4 - v1.2+) +### Fully integrated single board (v0.4 - v1.3+) 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. @@ -19,6 +19,13 @@ alt="PICO9918 v1.0" width="240px">

# Revision history +## [v1.3 (2025-07-02)](v1.2) + +### Changelog +- Added LM66200 dual perfect diode to allow connecting USB while installed. +- Replaced 74HC08 with a 74ACT126 to make data and interrupt lines float on reset. +- Data lines remain active slightly longer after /CSR goes high. + ## [v1.2 (2024-11-26)](v1.2)

PICO9918 v1.2

diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake index cefd5bf..3a01783 100644 --- a/pico_sdk_import.cmake +++ b/pico_sdk_import.cmake @@ -31,11 +31,15 @@ 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") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE STRING "release tag for SDK") if (NOT PICO_SDK_PATH) if (PICO_SDK_FETCH_FROM_GIT) include(FetchContent) + # Handle FetchContent deprecation warning gracefully + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.0") + cmake_policy(SET CMP0169 NEW) + endif() 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}") @@ -47,20 +51,20 @@ if (NOT PICO_SDK_PATH) GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} GIT_SUBMODULES_RECURSE FALSE - PATCH_COMMAND "" + PATCH_COMMAND ${CMAKE_COMMAND} -E chdir git apply --ignore-whitespace --ignore-space-change --3way ${CMAKE_SOURCE_DIR}/picosdk-2.0.0-visrealm-fastboot.patch || ${CMAKE_COMMAND} -E echo "PICO9918: SDK patch failed or already applied (this is normal)" ) else () FetchContent_Declare( pico_sdk GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} - PATCH_COMMAND "" + PATCH_COMMAND ${CMAKE_COMMAND} -E chdir git apply --ignore-whitespace --ignore-space-change --3way ${CMAKE_SOURCE_DIR}/picosdk-2.0.0-visrealm-fastboot.patch || ${CMAKE_COMMAND} -E echo "PICO9918: SDK patch failed or already applied (this is normal)" ) endif () if (NOT pico_sdk) message("Downloading Raspberry Pi Pico SDK") - FetchContent_Populate(pico_sdk) + FetchContent_MakeAvailable(pico_sdk) set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) endif () set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 647c079..8808c3d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,6 +94,9 @@ target_link_libraries(${PROGRAM} PUBLIC pico9918-gpu vrEmuTms9918) +# Set the executable suffix to .elf for all platforms +set_target_properties(${PROGRAM} PROPERTIES SUFFIX .elf) + pico_add_extra_outputs(${PROGRAM}) # Copy final artifacts to dist directory for unified output diff --git a/src/diag.c b/src/diag.c index e562935..7f8170f 100644 --- a/src/diag.c +++ b/src/diag.c @@ -228,7 +228,7 @@ void updateDiagnostics(uint32_t frameCount) uint8_t mask = (vrEmuTms9918DisplayMode() == TMS_MODE_GRAPHICS_II) ? 0x80 : 0xff; uint2hexStr((TMS_REGISTER(tms9918, TMS_REG_COLOR_TABLE) & mask) << 6, 4, &colorTabStr); - mask = (vrEmuTms9918DisplayMode() == TMS_MODE_GRAPHICS_II) ? 0x80 : 0xff; + mask = (vrEmuTms9918DisplayMode() == TMS_MODE_GRAPHICS_II) ? 0x04 : 0x07; uint2hexStr(((TMS_REGISTER(tms9918, TMS_REG_PATTERN_TABLE) & mask) << 11) & 0xffff, 4, &pattTabStr); uint2hexStr((TMS_REGISTER(tms9918, TMS_REG_SPRITE_ATTR_TABLE) & 0x7f) << 7, 4, &sprAttTabStr); @@ -316,7 +316,7 @@ static const char *nibbleBinStr[] = // register numbers to render static const int extReg[] = { 10, 11, 15, 19, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 49, 50, 51, 54, 55, + 36, 37, 38, 48, 49, 50, 51, 54, 55, 56, 57, 58, 59, 63 }; const uint32_t leftXPos = 2; @@ -487,6 +487,8 @@ DiagPtr spriteDiags[] = { &diagSprite6, &diagSprite7}; +#if 0 + #define PROGDATA_FLASH_OFFSET (0x100000) // Top 1MB of flash #define PROGDATA_FLASH_ADDR (uint8_t*)(XIP_BASE + PROGDATA_FLASH_OFFSET) @@ -535,6 +537,7 @@ void vramAddressDiag(uint16_t row, uint16_t* pixels) } } +#endif void diagnosticsConfigUpdated() { @@ -542,9 +545,11 @@ void diagnosticsConfigUpdated() leftDiagRows= 0; +#if 0 leftDiags[leftDiagRows++] = flashAddressDiag; leftDiags[leftDiagRows++] = flashAddress2Diag; leftDiags[leftDiagRows++] = vramAddressDiag; +#endif if (tms9918->config[CONF_DIAG_PERFORMANCE]) { diff --git a/src/main.c b/src/main.c index 4687948..95d9a77 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ #include "clocks.pio.h" -#define PALCONV 1 +#define PALCONV 0 #if PALCONV #include "palconv.pio.h" @@ -86,9 +86,16 @@ static void updateTmsReadAhead() { uint32_t readAhead = 0xff; // pin direction readAhead |= nextValue << 8; - int vr = TMS_REGISTER(tms9918, 0x0F) & 0x0F; - readAhead |= (TMS_STATUS(tms9918, vr)) << 16; - readAhead |= vr << 24; + if (tms9918->isUnlocked) + { + int vr = TMS_REGISTER(tms9918, 0x0F) & 0x0F; + readAhead |= (TMS_STATUS(tms9918, vr)) << 16; + readAhead |= vr << 24; + } + else + { + readAhead |= (TMS_STATUS(tms9918, 0)) << 16; + } pio_sm_put(TMS_PIO, tmsReadSm, readAhead); } @@ -97,7 +104,52 @@ static void updateTmsReadAhead() */ void __not_in_flash_func(pio_irq_handler)() { - if ((TMS_PIO->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + tmsWriteSm))) == 0) // write? + if ((TMS_PIO->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + tmsReadSm))) == 0) // read? + { + uint32_t readVal = TMS_PIO->rxf[tmsReadSm]; + + if ((readVal & 0x01) == 0) // read data + { + nextValue = vrEmuTms9918ReadAheadDataImpl(); + } + else // read status + { + if (!tms9918->isUnlocked) + { + currentStatus = 0x1f; + vrEmuTms9918SetStatusImpl(currentStatus); + currentInt = false; + gpio_put(GPIO_INT, !currentInt); + } + else + { + readVal >>= (1 + 16); // What status was read? + int readReg = (readVal >> 8) & 0x0f; // What status register was read? + readVal &= 0xff; + tms9918->regWriteStage = 0; + switch (readReg) + { + case 0: + readVal &= (STATUS_INT | STATUS_5S | STATUS_COL); + currentStatus &= ~readVal; // Switch off any 3 high bits which have just been read + if (readVal & STATUS_5S) // Was 5th Sprite read? + currentStatus |= 0x1f; + vrEmuTms9918SetStatusImpl(currentStatus); + if (readVal & STATUS_INT) // Was Interrupt read? + { + currentInt = false; + gpio_put(GPIO_INT, !currentInt); + } + break; + case 1: + if (readVal << 31) + TMS_STATUS(tms9918, 0x01) &= ~0x01; + break; + } + } + } + } + else if ((TMS_PIO->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + tmsWriteSm))) == 0) // write? { uint32_t writeVal = TMS_PIO->rxf[tmsWriteSm]; uint8_t dataVal = writeVal & 0xff; @@ -115,45 +167,9 @@ void __not_in_flash_func(pio_irq_handler)() } nextValue = vrEmuTms9918ReadDataNoIncImpl(); - updateTmsReadAhead(); } - else if ((TMS_PIO->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + tmsReadSm))) == 0) // read? - { - uint32_t readVal = TMS_PIO->rxf[tmsReadSm]; - if ((readVal & 0x04) == 0) // read data - { - vrEmuTms9918ReadDataImpl(); - nextValue = vrEmuTms9918ReadDataNoIncImpl(); - } - else // read status - { - readVal >>= (3 + 16); // What status was read? - int readReg = (readVal >> 8) & 0x0f; // What status register was read? - readVal &= 0xff; - tms9918->regWriteStage = 0; - switch (readReg) - { - case 0: - readVal &= (STATUS_INT | STATUS_5S | STATUS_COL); - currentStatus &= ~readVal; // Switch off any 3 high bits which have just been read - if (readVal & STATUS_5S) // Was 5th Sprite read? - currentStatus |= 0x1f; - vrEmuTms9918SetStatusImpl(currentStatus); - if (readVal & STATUS_INT) // Was Interrupt read? - { - currentInt = false; - gpio_put(GPIO_INT, !currentInt); - } - break; - case 1: - if (readVal << 31) - TMS_STATUS(tms9918, 0x01) &= ~0x01; - break; - } - } - updateTmsReadAhead(); - } + updateTmsReadAhead(); } @@ -602,12 +618,12 @@ void tmsPioInit() } pio_sm_config readPioConfig = tmsRead_program_get_default_config(tmsReadProgram); - sm_config_set_in_pins(&readPioConfig, GPIO_CSR); - sm_config_set_jmp_pin(&readPioConfig, GPIO_MODE); + sm_config_set_jmp_pin(&readPioConfig, GPIO_CSR); + sm_config_set_in_pins(&readPioConfig, GPIO_MODE); sm_config_set_out_pins(&readPioConfig, GPIO_CD7, 8); sm_config_set_in_shift(&readPioConfig, false, false, 32); // L shift sm_config_set_out_shift(&readPioConfig, true, false, 32); // R shift - sm_config_set_clkdiv(&readPioConfig, 2.0f); + sm_config_set_clkdiv(&readPioConfig, 1.0f); pio_sm_init(TMS_PIO, tmsReadSm, tmsReadProgram, &readPioConfig); pio_sm_set_enabled(TMS_PIO, tmsReadSm, true); diff --git a/src/tms9918.pio b/src/tms9918.pio index bfd8e3b..bfc74dc 100644 --- a/src/tms9918.pio +++ b/src/tms9918.pio @@ -34,28 +34,28 @@ .wrap_target wait 1 gpio CSR_PIN ; wait for CSR to go high (inactive) - mov osr, null [4] ; change CD0-7 pindirs to inputs + mov osr, null ; change CD0-7 pindirs to inputs out pindirs, 8 + mov isr, null pullLoop: pull noblock ; continuously empty the tx fifo mov x, osr ; since we want the latest value - mov isr, null - in pins, 1 ; hacky way to jmp based on the CSR pin - mov y, isr ; since we can only have one jmp pin - jmp y--, pullLoop ; still 1? loop, otherwise, let's read + jmp pin pullLoop + + in pins, 1 ; hacky way to jmp based on the CSR pin + mov y, isr ; since we can only have one jmp pin out pindirs, 8 ; set up CD0-7 as outputs - jmp pin readStatus ; if 'mode' is high, read status bits -readData: out pins, 8 ; mode is low, send the data byte - jmp endPart + + jmp !y, endPart ; still 1? loop, otherwise, let's read + readStatus: - out null, 8 ; skip data bits out pins, 8 ; output the status byte mov isr, x ; make this status value available endPart: - in pins, 3 ; push CSR, CSW and MODE back through fifo + in pins, 1 ; push CSR, CSW and MODE back through fifo push ; push ^^^ back to cpu to process .wrap diff --git a/submodules/vrEmuTms9918 b/submodules/vrEmuTms9918 index afb54ce..fe091ce 160000 --- a/submodules/vrEmuTms9918 +++ b/submodules/vrEmuTms9918 @@ -1 +1 @@ -Subproject commit afb54cec89c2469178d7db3395e8525c52cb90f8 +Subproject commit fe091cee8e5e3008c4002a32d051207fccbc9d07 diff --git a/visrealm_cvbasic.cmake b/visrealm_cvbasic.cmake index b5bdf46..0505113 100644 --- a/visrealm_cvbasic.cmake +++ b/visrealm_cvbasic.cmake @@ -53,8 +53,24 @@ function(find_cvbasic_tools) endfunction() # Setup CVBasic toolchain - either by finding existing tools or building from source +# +# Version control: +# Use cmake cache variables to specify tool versions: +# -DCVBASIC_GIT_TAG=v1.2.3 (default: master) +# -DGASM80_GIT_TAG=v0.9.1 (default: master) +# -DXDT99_GIT_TAG=3.5.0 (default: master) +# +# Examples: +# cmake .. -DCVBASIC_GIT_TAG=v1.2.3 +# cmake .. -DGASM80_GIT_TAG=v0.9.1 -DXDT99_GIT_TAG=3.5.0 +# function(setup_cvbasic_tools) option(BUILD_TOOLS_FROM_SOURCE "Build CVBasic, gasm80 and XDT99 from source" ON) + + # Tool version/tag configuration + set(CVBASIC_GIT_TAG "master" CACHE STRING "CVBasic git tag/branch/commit") + set(GASM80_GIT_TAG "master" CACHE STRING "GASM80 git tag/branch/commit") + set(XDT99_GIT_TAG "master" CACHE STRING "XDT99 git tag/branch/commit") if(BUILD_TOOLS_FROM_SOURCE) # Use system default compilers for host builds @@ -72,23 +88,24 @@ function(setup_cvbasic_tools) # Build CVBasic from visrealm fork using separate process to avoid cross-compilation issues ExternalProject_Add(CVBasic_external GIT_REPOSITORY https://github.com/visrealm/CVBasic.git - GIT_TAG master + GIT_TAG ${CVBASIC_GIT_TAG} CONFIGURE_COMMAND "" BUILD_COMMAND "" - INSTALL_COMMAND + INSTALL_COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/external/CVBasic/bin && - ${CMAKE_COMMAND} -E chdir + ${CMAKE_COMMAND} -E chdir ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/external/CVBasic -B build && - ${CMAKE_COMMAND} -E chdir + ${CMAKE_COMMAND} -E chdir ${CMAKE_COMMAND} --build build --config Release && ${CMAKE_COMMAND} -E chdir - ${CMAKE_COMMAND} --install build --config Release + ${CMAKE_COMMAND} --install build --config Release && + ${CMAKE_COMMAND} -E copy_if_different /linkticart.py ${CMAKE_BINARY_DIR}/external/CVBasic/ ) - # Build gasm80 from visrealm fork using separate process to avoid cross-compilation issues + # Build gasm80 from visrealm fork using separate process to avoid cross-compilation issues ExternalProject_Add(gasm80_external GIT_REPOSITORY https://github.com/visrealm/gasm80.git - GIT_TAG master + GIT_TAG ${GASM80_GIT_TAG} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND @@ -104,7 +121,7 @@ function(setup_cvbasic_tools) # Build XDT99 tools (Python-based) ExternalProject_Add(XDT99_external GIT_REPOSITORY https://github.com/endlos99/xdt99.git - GIT_TAG master + GIT_TAG ${XDT99_GIT_TAG} CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND @@ -115,15 +132,30 @@ function(setup_cvbasic_tools) set(CVBASIC_EXE "${CMAKE_BINARY_DIR}/external/CVBasic/bin/cvbasic" PARENT_SCOPE) set(GASM80_EXE "${CMAKE_BINARY_DIR}/external/gasm80/bin/gasm80" PARENT_SCOPE) set(XAS99_SCRIPT "${CMAKE_BINARY_DIR}/external/xdt99/xas99.py" PARENT_SCOPE) + set(LINKTICART_SCRIPT "${CMAKE_BINARY_DIR}/external/CVBasic/linkticart.py" PARENT_SCOPE) # Add dependencies to all CVBasic targets set(TOOL_DEPENDENCIES CVBasic_external gasm80_external XDT99_external PARENT_SCOPE) - + message(STATUS "CVBasic tools will be built from source") + message(STATUS "CVBasic version/tag: ${CVBASIC_GIT_TAG}") + message(STATUS "GASM80 version/tag: ${GASM80_GIT_TAG}") + message(STATUS "XDT99 version/tag: ${XDT99_GIT_TAG}") else() # Find required tools (original behavior) find_program(CVBASIC_EXE cvbasic PATHS ${CMAKE_SOURCE_DIR}/configtool/tools/cvbasic ${CMAKE_SOURCE_DIR}/../CVBasic/build/Release REQUIRED) find_program(GASM80_EXE gasm80 PATHS ${CMAKE_SOURCE_DIR}/configtool/tools/cvbasic REQUIRED) + + # Find linkticart.py in local CVBasic installation or fallback to bundled version + find_file(LINKTICART_SCRIPT linkticart.py + PATHS + ${CMAKE_SOURCE_DIR}/../CVBasic + ${CMAKE_SOURCE_DIR}/configtool/tools/cvbasic + DOC "CVBasic linkticart.py script" + ) + if(NOT LINKTICART_SCRIPT) + set(LINKTICART_SCRIPT "${CMAKE_SOURCE_DIR}/configtool/tools/cvbasic/linkticart.py") + endif() # Platform-specific tool paths if(WIN32) @@ -143,6 +175,7 @@ function(setup_cvbasic_tools) message(STATUS "Using existing CVBasic tools") message(STATUS "CVBasic: ${CVBASIC_EXE}") message(STATUS "GASM80: ${GASM80_EXE}") + message(STATUS "linkticart.py: ${LINKTICART_SCRIPT}") if(XAS99_SCRIPT) message(STATUS "XAS99: ${XAS99_SCRIPT}") else() @@ -235,12 +268,11 @@ function(visrealm_xas99_assemble TARGET ASM_FILE BIN_OUTPUT CART_OUTPUT TITLE) VERBATIM ) - # Link to TI cartridge format - set(LINKTICART ${CMAKE_SOURCE_DIR}/configtool/tools/cvbasic/linkticart.py) + # Link to TI cartridge format using configured linkticart script add_custom_command( OUTPUT ${CART_OUTPUT} - COMMAND ${PYTHON} ${LINKTICART} ${BIN_FILE} ${CART_OUTPUT} ${TITLE} - DEPENDS ${BIN_FILE} ${LINKTICART} + COMMAND ${PYTHON} ${LINKTICART_SCRIPT} ${BIN_FILE} ${CART_OUTPUT} ${TITLE} + DEPENDS ${BIN_FILE} ${LINKTICART_SCRIPT} COMMENT "Creating TI cartridge: ${CART_OUTPUT}" VERBATIM )