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 | [](https://github.com/visrealm/pico9918/actions/workflows/firmware.yml) | [](https://github.com/visrealm/pico9918/actions/workflows/configurator.yml) | -| dev | [](https://github.com/visrealm/pico9918/actions/workflows/firmware.yml) | [](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) | [](https://github.com/visrealm/pico9918/actions/workflows/firmware-linux.yml) | [](https://github.com/visrealm/pico9918/actions/workflows/firmware-macos.yml) | +| Configurator | [](https://github.com/visrealm/pico9918/actions/workflows/configurator-windows.yml) | [](https://github.com/visrealm/pico9918/actions/workflows/configurator-linux.yml) | [](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) | [](https://github.com/visrealm/pico9918/actions/workflows/firmware-linux.yml) | [](https://github.com/visrealm/pico9918/actions/workflows/firmware-macos.yml) | +| Configurator | [](https://github.com/visrealm/pico9918/actions/workflows/configurator-windows.yml) | [](https://github.com/visrealm/pico9918/actions/workflows/configurator-linux.yml) | [](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.
@@ -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