bench5188f3865
Merge pull request #3 from krolyxon/release
Releases, install scripts, and v0.2.0
+594 −34
8 files changed
- M .github/workflows/ci.yml +25 −4
- A .github/workflows/release.yml +178 −0
- A CHANGELOG.md +44 −0
- M Cargo.lock +3 −3
- M Cargo.toml +1 −1
- M README.md +64 −26
- A install.ps1 +112 −0
- A install.sh +167 −0
| @@ -2,8 +2,14 @@ name: CI | |||
| 2 | 2 | ||
| 3 | 3 | on: | |
| 4 | 4 | push: | |
| 5 | + | branches: [master] | |
| 5 | 6 | pull_request: | |
| 6 | 7 | ||
| 8 | + | # A newer push to the same branch or PR supersedes the running check. | |
| 9 | + | concurrency: | |
| 10 | + | group: ci-${{ github.ref }} | |
| 11 | + | cancel-in-progress: true | |
| 12 | + | ||
| 7 | 13 | env: | |
| 8 | 14 | CARGO_TERM_COLOR: always | |
| 9 | 15 | ||
| @@ -16,7 +22,7 @@ jobs: | |||
| 16 | 22 | os: [ubuntu-latest, windows-latest, macos-latest] | |
| 17 | 23 | runs-on: ${{ matrix.os }} | |
| 18 | 24 | steps: | |
| 19 | − | - uses: actions/checkout@v4 | |
| 25 | + | - uses: actions/checkout@v7 | |
| 20 | 26 | - uses: dtolnay/rust-toolchain@stable | |
| 21 | 27 | with: | |
| 22 | 28 | components: clippy | |
| @@ -26,6 +32,21 @@ jobs: | |||
| 26 | 32 | sudo apt-get update | |
| 27 | 33 | sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev | |
| 28 | 34 | - uses: Swatinem/rust-cache@v2 | |
| 29 | − | - run: cargo build --workspace --release | |
| 30 | − | - run: cargo test --workspace | |
| 31 | − | - run: cargo clippy --workspace --all-targets -- -D warnings | |
| 35 | + | - run: cargo build --workspace --release --locked | |
| 36 | + | - run: cargo test --workspace --locked | |
| 37 | + | - run: cargo clippy --workspace --all-targets --locked -- -D warnings | |
| 38 | + | ||
| 39 | + | scripts: | |
| 40 | + | name: install scripts | |
| 41 | + | runs-on: ubuntu-latest | |
| 42 | + | steps: | |
| 43 | + | - uses: actions/checkout@v7 | |
| 44 | + | - name: shellcheck install.sh | |
| 45 | + | run: shellcheck install.sh | |
| 46 | + | - name: PSScriptAnalyzer install.ps1 | |
| 47 | + | shell: pwsh | |
| 48 | + | run: | | |
| 49 | + | # Write-Host is the right tool for an installer piped into iex. | |
| 50 | + | $issues = Invoke-ScriptAnalyzer -Path install.ps1 -Severity Warning,Error -ExcludeRule PSAvoidUsingWriteHost | |
| 51 | + | $issues | Format-Table -AutoSize | |
| 52 | + | if ($issues) { exit 1 } | |
| @@ -0,0 +1,178 @@ | |||
| 1 | + | name: Release | |
| 2 | + | ||
| 3 | + | # Pushing a tag vX.Y.Z (matching the version in Cargo.toml) builds bench51 | |
| 4 | + | # for every target, publishes a GitHub release with the archives, checksums | |
| 5 | + | # and the CHANGELOG.md section as notes, then installs it with the one-line | |
| 6 | + | # installers on each OS. Pull requests that touch the release machinery get | |
| 7 | + | # a dry run: everything except publishing. | |
| 8 | + | ||
| 9 | + | on: | |
| 10 | + | push: | |
| 11 | + | tags: ['v*'] | |
| 12 | + | pull_request: | |
| 13 | + | paths: | |
| 14 | + | - .github/workflows/release.yml | |
| 15 | + | - install.sh | |
| 16 | + | - install.ps1 | |
| 17 | + | workflow_dispatch: | |
| 18 | + | ||
| 19 | + | permissions: | |
| 20 | + | contents: read | |
| 21 | + | ||
| 22 | + | env: | |
| 23 | + | CARGO_TERM_COLOR: always | |
| 24 | + | ||
| 25 | + | jobs: | |
| 26 | + | build: | |
| 27 | + | name: ${{ matrix.target }} | |
| 28 | + | strategy: | |
| 29 | + | fail-fast: false | |
| 30 | + | matrix: | |
| 31 | + | include: | |
| 32 | + | # 22.04 keeps the glibc requirement low for older distributions. | |
| 33 | + | - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 } | |
| 34 | + | - { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm } | |
| 35 | + | - { target: x86_64-pc-windows-msvc, os: windows-latest } | |
| 36 | + | - { target: aarch64-apple-darwin, os: macos-latest } | |
| 37 | + | # Cross-compiled on Apple silicon, so it can't be run here. | |
| 38 | + | - { target: x86_64-apple-darwin, os: macos-latest, cross: true } | |
| 39 | + | runs-on: ${{ matrix.os }} | |
| 40 | + | steps: | |
| 41 | + | - uses: actions/checkout@v7 | |
| 42 | + | ||
| 43 | + | - name: Check the tag matches Cargo.toml | |
| 44 | + | if: startsWith(github.ref, 'refs/tags/v') | |
| 45 | + | shell: bash | |
| 46 | + | run: | | |
| 47 | + | version=$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n 1) | |
| 48 | + | if [ "$GITHUB_REF_NAME" != "v$version" ]; then | |
| 49 | + | echo "::error::tag $GITHUB_REF_NAME does not match version $version in Cargo.toml" | |
| 50 | + | exit 1 | |
| 51 | + | fi | |
| 52 | + | ||
| 53 | + | - uses: dtolnay/rust-toolchain@stable | |
| 54 | + | with: | |
| 55 | + | targets: ${{ matrix.target }} | |
| 56 | + | ||
| 57 | + | - name: Install Linux GUI dependencies | |
| 58 | + | if: runner.os == 'Linux' | |
| 59 | + | run: | | |
| 60 | + | sudo apt-get update | |
| 61 | + | sudo apt-get install -y libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev | |
| 62 | + | ||
| 63 | + | - uses: Swatinem/rust-cache@v2 | |
| 64 | + | with: | |
| 65 | + | key: ${{ matrix.target }} | |
| 66 | + | ||
| 67 | + | - name: Build | |
| 68 | + | run: cargo build --release --locked --target ${{ matrix.target }} -p bench51 -p bench51-gui | |
| 69 | + | ||
| 70 | + | - name: Package | |
| 71 | + | shell: bash | |
| 72 | + | run: | | |
| 73 | + | name=bench51-${{ matrix.target }} | |
| 74 | + | ext= | |
| 75 | + | if [ "$RUNNER_OS" = Windows ]; then ext=.exe; fi | |
| 76 | + | mkdir -p "dist/$name" | |
| 77 | + | cp "target/${{ matrix.target }}/release/bench51$ext" "target/${{ matrix.target }}/release/bench51-gui$ext" "dist/$name/" | |
| 78 | + | cp README.md LICENSE CHANGELOG.md "dist/$name/" | |
| 79 | + | if [ "$RUNNER_OS" = Linux ]; then | |
| 80 | + | mkdir -p "dist/$name/share/applications" "dist/$name/share/icons/hicolor/scalable/apps" | |
| 81 | + | cp crates/bench51-gui/assets/bench51-gui.desktop "dist/$name/share/applications/" | |
| 82 | + | cp crates/bench51-gui/assets/bench51-gui.svg "dist/$name/share/icons/hicolor/scalable/apps/" | |
| 83 | + | fi | |
| 84 | + | cd dist | |
| 85 | + | if [ "$RUNNER_OS" = Windows ]; then | |
| 86 | + | 7z a -tzip "$name.zip" "$name" > /dev/null | |
| 87 | + | else | |
| 88 | + | tar -czf "$name.tar.gz" "$name" | |
| 89 | + | fi | |
| 90 | + | rm -rf "$name" | |
| 91 | + | ls -l | |
| 92 | + | ||
| 93 | + | - name: Install the archive and run it | |
| 94 | + | if: runner.os != 'Windows' && !matrix.cross | |
| 95 | + | shell: bash | |
| 96 | + | run: | | |
| 97 | + | sh install.sh --archive "dist/bench51-${{ matrix.target }}.tar.gz" --dir "$RUNNER_TEMP/bin" --no-desktop | |
| 98 | + | "$RUNNER_TEMP/bin/bench51-gui" --version | |
| 99 | + | printf 'ORG 0\nloop: MOV A,#1\n SJMP loop\n END\n' > "$RUNNER_TEMP/t.asm" | |
| 100 | + | "$RUNNER_TEMP/bin/bench51" --assemble "$RUNNER_TEMP/t.asm" | |
| 101 | + | test -s "$RUNNER_TEMP/t.hex" | |
| 102 | + | ||
| 103 | + | - name: Install the archive and run it | |
| 104 | + | if: runner.os == 'Windows' | |
| 105 | + | shell: pwsh | |
| 106 | + | run: | | |
| 107 | + | ./install.ps1 -Archive "dist/bench51-${{ matrix.target }}.zip" -InstallDir "$env:RUNNER_TEMP\bench51" -NoShortcut | |
| 108 | + | if ($LASTEXITCODE) { exit $LASTEXITCODE } | |
| 109 | + | # The GUI has no console; check its exit code instead of its output. | |
| 110 | + | $gui = Start-Process -FilePath "$env:RUNNER_TEMP\bench51\bench51-gui.exe" -ArgumentList '--version' -Wait -PassThru | |
| 111 | + | if ($gui.ExitCode -ne 0) { exit 1 } | |
| 112 | + | ||
| 113 | + | - uses: actions/upload-artifact@v7 | |
| 114 | + | with: | |
| 115 | + | name: bench51-${{ matrix.target }} | |
| 116 | + | path: dist/* | |
| 117 | + | if-no-files-found: error | |
| 118 | + | ||
| 119 | + | publish: | |
| 120 | + | needs: build | |
| 121 | + | if: startsWith(github.ref, 'refs/tags/v') | |
| 122 | + | runs-on: ubuntu-latest | |
| 123 | + | permissions: | |
| 124 | + | contents: write | |
| 125 | + | steps: | |
| 126 | + | - uses: actions/checkout@v7 | |
| 127 | + | ||
| 128 | + | - uses: actions/download-artifact@v8 | |
| 129 | + | with: | |
| 130 | + | path: dist | |
| 131 | + | merge-multiple: true | |
| 132 | + | ||
| 133 | + | - name: Checksums | |
| 134 | + | run: | | |
| 135 | + | cd dist | |
| 136 | + | sha256sum -- * > SHA256SUMS | |
| 137 | + | cat SHA256SUMS | |
| 138 | + | ||
| 139 | + | - name: Release notes from CHANGELOG.md | |
| 140 | + | run: | | |
| 141 | + | version=${GITHUB_REF_NAME#v} | |
| 142 | + | awk -v v="$version" '/^## \[/ { if (on) exit; on = index($0, "[" v "]") } on' CHANGELOG.md | tail -n +2 > notes.md | |
| 143 | + | if [ ! -s notes.md ]; then | |
| 144 | + | echo "::error::CHANGELOG.md has no section for $version" | |
| 145 | + | exit 1 | |
| 146 | + | fi | |
| 147 | + | cat notes.md | |
| 148 | + | ||
| 149 | + | - name: Publish | |
| 150 | + | env: | |
| 151 | + | GH_TOKEN: ${{ github.token }} | |
| 152 | + | run: gh release create "$GITHUB_REF_NAME" dist/* --verify-tag --title "bench51 $GITHUB_REF_NAME" --notes-file notes.md | |
| 153 | + | ||
| 154 | + | # Installs the published release exactly as the README tells people to. | |
| 155 | + | verify: | |
| 156 | + | name: install on ${{ matrix.os }} | |
| 157 | + | needs: publish | |
| 158 | + | strategy: | |
| 159 | + | fail-fast: false | |
| 160 | + | matrix: | |
| 161 | + | os: [ubuntu-latest, macos-latest, windows-latest] | |
| 162 | + | runs-on: ${{ matrix.os }} | |
| 163 | + | steps: | |
| 164 | + | - name: One-line install | |
| 165 | + | if: runner.os != 'Windows' | |
| 166 | + | run: | | |
| 167 | + | curl -fsSL "https://raw.githubusercontent.com/$GITHUB_REPOSITORY/$GITHUB_REF_NAME/install.sh" | sh | |
| 168 | + | "$HOME/.local/bin/bench51" --version | grep -F "${GITHUB_REF_NAME#v}" | |
| 169 | + | "$HOME/.local/bin/bench51-gui" --version | grep -F "${GITHUB_REF_NAME#v}" | |
| 170 | + | ||
| 171 | + | - name: One-line install | |
| 172 | + | if: runner.os == 'Windows' | |
| 173 | + | shell: pwsh | |
| 174 | + | run: | | |
| 175 | + | irm "https://raw.githubusercontent.com/$env:GITHUB_REPOSITORY/$env:GITHUB_REF_NAME/install.ps1" | iex | |
| 176 | + | $version = & "$env:LOCALAPPDATA\Programs\bench51\bench51.exe" --version | |
| 177 | + | if ($version -notmatch [regex]::Escape($env:GITHUB_REF_NAME.TrimStart('v'))) { exit 1 } | |
| 178 | + | if (-not (Test-Path "$([Environment]::GetFolderPath('Programs'))\bench51.lnk")) { exit 1 } | |
| @@ -0,0 +1,44 @@ | |||
| 1 | + | # Changelog | |
| 2 | + | ||
| 3 | + | All notable changes to bench51. The format follows | |
| 4 | + | [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and versions follow | |
| 5 | + | [Semantic Versioning](https://semver.org/). | |
| 6 | + | ||
| 7 | + | ## [0.2.0] - 2026-11-01 | |
| 8 | + | ||
| 9 | + | ### Added | |
| 10 | + | ||
| 11 | + | - **bench51-gui**, a desktop IDE laid out like Keil µVision: menus, toolbar, | |
| 12 | + | Project window, editor with breakpoint and error gutter, Build Output, and | |
| 13 | + | in a debug session the Registers, Disassembly, Memory, Symbols, UART #1, | |
| 14 | + | Ports, Timers and Interrupts windows. Windows dock and tab anywhere; the | |
| 15 | + | edit and debug layouts are remembered separately. | |
| 16 | + | - Monochrome, dot-matrix design in dark and light themes (follows the | |
| 17 | + | system by default), with Space Grotesk, Space Mono and Doto bundled. | |
| 18 | + | - µVision's keys (F7, Ctrl-F5, F5, F10/F11, Ctrl-F10/F11, F9) with the | |
| 19 | + | TUI's as aliases; find (Ctrl-F); inline memory editing; clickable SFR | |
| 20 | + | bits and port pins; drag-and-drop to open files. | |
| 21 | + | - Prebuilt releases for Linux (x86_64, aarch64), Windows (x86_64) and macOS | |
| 22 | + | (Apple silicon, Intel), with one-line installers: `install.sh` for Linux | |
| 23 | + | and macOS, `install.ps1` for Windows. | |
| 24 | + | - CI on Linux, Windows and macOS. | |
| 25 | + | ||
| 26 | + | ### Changed | |
| 27 | + | ||
| 28 | + | - The assembler, simulator and debugger session moved into a shared | |
| 29 | + | `bench51-core` crate used by both front ends. The terminal app behaves | |
| 30 | + | as before. | |
| 31 | + | - Unsafe code is forbidden across the workspace. | |
| 32 | + | ||
| 33 | + | ## [0.1.0] - 2026-10-23 | |
| 34 | + | ||
| 35 | + | ### Added | |
| 36 | + | ||
| 37 | + | - `bench51`, a terminal (TUI) 8051 IDE: editor, two-pass assembler for the | |
| 38 | + | full instruction set with Keil-style listing and Intel HEX output, | |
| 39 | + | simulator with timers, interrupts, UART and ports, and a debugger with | |
| 40 | + | breakpoints, stepping and memory poke. | |
| 41 | + | - Headless assembly with `bench51 --assemble`. | |
| 42 | + | - Opening Intel HEX files as disassembled, re-assemblable source. | |
| 43 | + | ||
| 44 | + | [0.2.0]: https://github.com/krolyxon/bench51/releases/tag/v0.2.0 | |
| @@ -485,7 +485,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" | |||
| 485 | 485 | ||
| 486 | 486 | [[package]] | |
| 487 | 487 | name = "bench51" | |
| 488 | − | version = "0.1.0" | |
| 488 | + | version = "0.2.0" | |
| 489 | 489 | dependencies = [ | |
| 490 | 490 | "bench51-core", | |
| 491 | 491 | "clap", | |
| @@ -495,11 +495,11 @@ dependencies = [ | |||
| 495 | 495 | ||
| 496 | 496 | [[package]] | |
| 497 | 497 | name = "bench51-core" | |
| 498 | − | version = "0.1.0" | |
| 498 | + | version = "0.2.0" | |
| 499 | 499 | ||
| 500 | 500 | [[package]] | |
| 501 | 501 | name = "bench51-gui" | |
| 502 | − | version = "0.1.0" | |
| 502 | + | version = "0.2.0" | |
| 503 | 503 | dependencies = [ | |
| 504 | 504 | "bench51-core", | |
| 505 | 505 | "clap", | |
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | members = ["crates/*"] | |
| 3 | 3 | ||
| 4 | 4 | [workspace.package] | |
| 5 | − | version = "0.1.0" | |
| 5 | + | version = "0.2.0" | |
| 6 | 6 | edition = "2024" | |
| 7 | 7 | ||
| 8 | 8 | [workspace.lints.rust] | |
| @@ -1,12 +1,13 @@ | |||
| 1 | 1 | # bench51 — an 8051 assembler, simulator & debugger | |
| 2 | 2 | ||
| 3 | 3 | [](https://github.com/krolyxon/bench51/actions/workflows/ci.yml) | |
| 4 | − | [](Cargo.toml) | |
| 4 | + | [](https://github.com/krolyxon/bench51/releases/latest) | |
| 5 | + | [](https://github.com/krolyxon/bench51/releases) | |
| 5 | 6 | [](https://github.com/krolyxon/bench51) | |
| 6 | 7 | [](crates) | |
| 7 | 8 | [](Cargo.toml) | |
| 8 | 9 | [](LICENSE) | |
| 9 | − | [](#usage) | |
| 10 | + | [](#install) | |
| 10 | 11 | [](https://en.wikipedia.org/wiki/Intel_8051) | |
| 11 | 12 | [](https://github.com/emilk/egui) | |
| 12 | 13 | [](https://ratatui.rs) | |
| @@ -88,32 +89,57 @@ Linux, in a window or right in the terminal. | |||
| 88 | 89 | - **Dark and light themes** in a monochrome, dot-matrix design (Space | |
| 89 | 90 | Grotesk, Space Mono and Doto, bundled). | |
| 90 | 91 | ||
| 91 | − | ## Usage | |
| 92 | + | ## Install | |
| 93 | + | ||
| 94 | + | Linux and macOS: | |
| 92 | 95 | ||
| 93 | 96 | ```bash | |
| 94 | − | cargo install --path crates/bench51-gui # installs `bench51-gui` | |
| 97 | + | curl -fsSL https://raw.githubusercontent.com/krolyxon/bench51/master/install.sh | sh | |
| 98 | + | ``` | |
| 99 | + | ||
| 100 | + | Windows (PowerShell): | |
| 95 | 101 | ||
| 96 | − | cargo run --release -p bench51-gui # welcome screen | |
| 97 | − | cargo run --release -p bench51-gui -- examples/*.asm # open several files | |
| 98 | − | cargo run --release -p bench51-gui -- firmware.hex # disassemble & debug a HEX file | |
| 99 | − | cargo run --release -p bench51-gui -- --theme light | |
| 102 | + | ```powershell | |
| 103 | + | irm https://raw.githubusercontent.com/krolyxon/bench51/master/install.ps1 | iex | |
| 100 | 104 | ``` | |
| 101 | 105 | ||
| 102 | − | `--theme system|dark|light` overrides the theme chosen in View → Theme, and | |
| 103 | − | `--clock <MHz>` the Xtal set in Project → Options for Target (default | |
| 104 | − | 11.0592), which is used for the elapsed-time display. | |
| 106 | + | Both install `bench51-gui` and `bench51` from the | |
| 107 | + | [latest release](https://github.com/krolyxon/bench51/releases/latest) and | |
| 108 | + | verify the download against its SHA-256 checksum: | |
| 105 | 109 | ||
| 106 | − | On Linux it runs on Wayland and X11 and uses the XDG desktop portal for file | |
| 107 | − | dialogs. To add it to your application launcher: | |
| 110 | + | - **Linux and macOS:** the binaries go into `~/.local/bin`, and on Linux the | |
| 111 | + | app is added to your application menu. Options go after `sh -s --`: | |
| 112 | + | `--version v0.2.0` pins a release, `--dir /usr/local/bin` picks another | |
| 113 | + | directory, `--archive <file>` installs a downloaded archive offline | |
| 114 | + | (handy on lab PCs), and `--uninstall` removes it. | |
| 115 | + | - **Windows:** it installs to `%LOCALAPPDATA%\Programs\bench51`, adds that to | |
| 116 | + | your PATH and adds a Start menu entry. `-Uninstall` removes it. | |
| 117 | + | ||
| 118 | + | Prebuilt binaries cover Linux (x86_64, aarch64), Windows (x86_64) and macOS | |
| 119 | + | (Apple silicon, Intel). To build from source instead, with a Rust toolchain: | |
| 108 | 120 | ||
| 109 | 121 | ```bash | |
| 110 | − | install -Dm644 crates/bench51-gui/assets/bench51-gui.desktop ~/.local/share/applications/bench51-gui.desktop | |
| 111 | − | install -Dm644 crates/bench51-gui/assets/bench51-gui.svg ~/.local/share/icons/hicolor/scalable/apps/bench51-gui.svg | |
| 122 | + | cargo install --locked --git https://github.com/krolyxon/bench51 bench51-gui # the desktop app | |
| 123 | + | cargo install --locked --git https://github.com/krolyxon/bench51 bench51 # the terminal app | |
| 112 | 124 | ``` | |
| 113 | 125 | ||
| 114 | − | It also builds for Windows and macOS. On Debian or Ubuntu, first install the | |
| 115 | − | packages egui needs: `libxcb-render0-dev libxcb-shape0-dev | |
| 116 | − | libxcb-xfixes0-dev libxkbcommon-dev libssl-dev`. | |
| 126 | + | ## Usage | |
| 127 | + | ||
| 128 | + | ```bash | |
| 129 | + | bench51-gui # welcome screen | |
| 130 | + | bench51-gui examples/*.asm # open several files | |
| 131 | + | bench51-gui firmware.hex # disassemble & debug a HEX file | |
| 132 | + | bench51-gui --theme light | |
| 133 | + | ``` | |
| 134 | + | ||
| 135 | + | `--theme system|dark|light` overrides the theme chosen in View → Theme, and | |
| 136 | + | `--clock <MHz>` the Xtal set in Project → Options for Target (default | |
| 137 | + | 11.0592), which is used for the elapsed-time display. | |
| 138 | + | ||
| 139 | + | On Linux it runs on Wayland and X11 and uses the XDG desktop portal for file | |
| 140 | + | dialogs. Building it from source on Debian or Ubuntu needs the packages egui | |
| 141 | + | links against: `libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev | |
| 142 | + | libxkbcommon-dev libssl-dev`. | |
| 117 | 143 | ||
| 118 | 144 | ## Keys | |
| 119 | 145 | ||
| @@ -168,15 +194,13 @@ Makefiles. | |||
| 168 | 194 | </table> | |
| 169 | 195 | ||
| 170 | 196 | ```bash | |
| 171 | − | cargo install --path . # installs the `bench51` binary | |
| 172 | − | ||
| 173 | − | cargo run --release -- examples/serial_hello.asm # open in the TUI | |
| 174 | − | cargo run --release # start with a template | |
| 175 | − | cargo run --release -- firmware.hex # disassemble & debug a HEX file | |
| 197 | + | bench51 examples/serial_hello.asm # open in the TUI | |
| 198 | + | bench51 # start with a template | |
| 199 | + | bench51 firmware.hex # disassemble & debug a HEX file | |
| 176 | 200 | ||
| 177 | 201 | # Headless assembly: writes prog.hex and prog.lst | |
| 178 | − | cargo run --release -- --assemble prog.asm | |
| 179 | − | cargo run --release -- --assemble prog.asm -o out.hex --lst out.lst | |
| 202 | + | bench51 --assemble prog.asm | |
| 203 | + | bench51 --assemble prog.asm -o out.hex --lst out.lst | |
| 180 | 204 | ``` | |
| 181 | 205 | ||
| 182 | 206 | `--clock <MHz>` sets the oscillator frequency used for the elapsed-time display | |
| @@ -235,10 +259,24 @@ Letter shortcuts work too, for terminals that swallow function keys: | |||
| 235 | 259 | ## Development | |
| 236 | 260 | ||
| 237 | 261 | ```bash | |
| 238 | − | cargo test --workspace # assembler, CPU, peripheral, session and example-program tests | |
| 262 | + | cargo run --release -p bench51-gui # the desktop app | |
| 263 | + | cargo run --release -p bench51 # the terminal app | |
| 264 | + | cargo test --workspace # assembler, CPU, peripheral, session and example-program tests | |
| 239 | 265 | cargo clippy --workspace --all-targets | |
| 240 | 266 | ``` | |
| 241 | 267 | ||
| 268 | + | Releasing: bump `version` in `Cargo.toml`, add a section to | |
| 269 | + | [CHANGELOG.md](CHANGELOG.md), merge, then tag and push: | |
| 270 | + | ||
| 271 | + | ```bash | |
| 272 | + | git tag v0.3.0 && git push origin v0.3.0 | |
| 273 | + | ``` | |
| 274 | + | ||
| 275 | + | The release workflow checks that the tag matches `Cargo.toml` and builds | |
| 276 | + | every target. It publishes the archives and `SHA256SUMS` with that | |
| 277 | + | changelog section as the release notes, then installs the result with the | |
| 278 | + | one-liners on Linux, macOS and Windows. | |
| 279 | + | ||
| 242 | 280 | Code layout: | |
| 243 | 281 | ||
| 244 | 282 | - `crates/bench51-gui/` is the `bench51-gui` desktop app (egui). | |
| @@ -0,0 +1,112 @@ | |||
| 1 | + | # Installs bench51 (the bench51-gui desktop app and the bench51 terminal app) | |
| 2 | + | # from the GitHub releases, on Windows. | |
| 3 | + | # | |
| 4 | + | # irm https://raw.githubusercontent.com/krolyxon/bench51/master/install.ps1 | iex | |
| 5 | + | # | |
| 6 | + | # With options: | |
| 7 | + | # | |
| 8 | + | # & ([scriptblock]::Create((irm https://raw.githubusercontent.com/krolyxon/bench51/master/install.ps1))) -Version v0.2.0 | |
| 9 | + | # | |
| 10 | + | # Options: -Version <tag> (default latest), -InstallDir <path> (default | |
| 11 | + | # %LOCALAPPDATA%\Programs\bench51), -Archive <bench51-<target>.zip>, | |
| 12 | + | # -NoShortcut, -Uninstall. BENCH51_VERSION and BENCH51_INSTALL_DIR set the | |
| 13 | + | # defaults for -Version and -InstallDir when piping into iex. | |
| 14 | + | ||
| 15 | + | param( | |
| 16 | + | [string]$Version = $(if ($env:BENCH51_VERSION) { $env:BENCH51_VERSION } else { 'latest' }), | |
| 17 | + | [string]$InstallDir = $(if ($env:BENCH51_INSTALL_DIR) { $env:BENCH51_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'Programs\bench51' }), | |
| 18 | + | [string]$Archive, | |
| 19 | + | [switch]$NoShortcut, | |
| 20 | + | [switch]$Uninstall | |
| 21 | + | ) | |
| 22 | + | ||
| 23 | + | $ErrorActionPreference = 'Stop' | |
| 24 | + | # Invoke-WebRequest is much slower with the progress bar on. | |
| 25 | + | $ProgressPreference = 'SilentlyContinue' | |
| 26 | + | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 | |
| 27 | + | ||
| 28 | + | $Repo = 'krolyxon/bench51' | |
| 29 | + | $Asset = 'bench51-x86_64-pc-windows-msvc.zip' | |
| 30 | + | $Shortcut = Join-Path ([Environment]::GetFolderPath('Programs')) 'bench51.lnk' | |
| 31 | + | ||
| 32 | + | function Write-Step([string]$Message) { | |
| 33 | + | Write-Host "bench51: $Message" | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | # Adds or removes $Dir in the user's PATH. | |
| 37 | + | function Edit-UserPath([string]$Dir, [bool]$Add) { | |
| 38 | + | $current = [Environment]::GetEnvironmentVariable('Path', 'User') | |
| 39 | + | $parts = @(($current -split ';') | Where-Object { $_ -and ($_.TrimEnd('\') -ne $Dir.TrimEnd('\')) }) | |
| 40 | + | if ($Add) { | |
| 41 | + | $parts += $Dir | |
| 42 | + | } | |
| 43 | + | [Environment]::SetEnvironmentVariable('Path', ($parts -join ';'), 'User') | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | if ($Uninstall) { | |
| 47 | + | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $InstallDir | |
| 48 | + | Remove-Item -Force -ErrorAction SilentlyContinue $Shortcut | |
| 49 | + | Edit-UserPath $InstallDir $false | |
| 50 | + | Write-Step "removed bench51 from $InstallDir" | |
| 51 | + | return | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | $Temp = Join-Path ([IO.Path]::GetTempPath()) ("bench51-" + [guid]::NewGuid()) | |
| 55 | + | New-Item -ItemType Directory -Path $Temp | Out-Null | |
| 56 | + | try { | |
| 57 | + | if (-not $Archive) { | |
| 58 | + | if ($Version -eq 'latest') { | |
| 59 | + | $Base = "https://github.com/$Repo/releases/latest/download" | |
| 60 | + | } else { | |
| 61 | + | if (-not $Version.StartsWith('v')) { | |
| 62 | + | $Version = "v$Version" | |
| 63 | + | } | |
| 64 | + | $Base = "https://github.com/$Repo/releases/download/$Version" | |
| 65 | + | } | |
| 66 | + | Write-Step "downloading $Asset ($Version)" | |
| 67 | + | $Archive = Join-Path $Temp $Asset | |
| 68 | + | $Sums = Join-Path $Temp 'SHA256SUMS' | |
| 69 | + | Invoke-WebRequest -UseBasicParsing -Uri "$Base/$Asset" -OutFile $Archive | |
| 70 | + | Invoke-WebRequest -UseBasicParsing -Uri "$Base/SHA256SUMS" -OutFile $Sums | |
| 71 | + | $line = Get-Content $Sums | Where-Object { $_ -match ('\s\*?' + [regex]::Escape($Asset) + '$') } | Select-Object -First 1 | |
| 72 | + | if (-not $line) { | |
| 73 | + | throw "$Asset is not listed in SHA256SUMS" | |
| 74 | + | } | |
| 75 | + | $expected = ($line -split '\s+')[0].ToLowerInvariant() | |
| 76 | + | $actual = (Get-FileHash -Algorithm SHA256 -Path $Archive).Hash.ToLowerInvariant() | |
| 77 | + | if ($actual -ne $expected) { | |
| 78 | + | throw "checksum mismatch for $Asset" | |
| 79 | + | } | |
| 80 | + | } | |
| 81 | + | if (-not (Test-Path $Archive)) { | |
| 82 | + | throw "no such archive: $Archive" | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | $Extracted = Join-Path $Temp 'x' | |
| 86 | + | Expand-Archive -Path $Archive -DestinationPath $Extracted -Force | |
| 87 | + | $gui = Get-ChildItem -Path $Extracted -Recurse -Filter 'bench51-gui.exe' | Select-Object -First 1 | |
| 88 | + | if (-not $gui) { | |
| 89 | + | throw 'the archive has no bench51-gui.exe' | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null | |
| 93 | + | Copy-Item -Path (Join-Path $gui.DirectoryName '*') -Destination $InstallDir -Recurse -Force | |
| 94 | + | Edit-UserPath $InstallDir $true | |
| 95 | + | $env:Path = "$env:Path;$InstallDir" | |
| 96 | + | Write-Step "installed bench51 and bench51-gui to $InstallDir" | |
| 97 | + | ||
| 98 | + | if (-not $NoShortcut) { | |
| 99 | + | $shell = New-Object -ComObject WScript.Shell | |
| 100 | + | $link = $shell.CreateShortcut($Shortcut) | |
| 101 | + | $link.TargetPath = Join-Path $InstallDir 'bench51-gui.exe' | |
| 102 | + | $link.WorkingDirectory = $env:USERPROFILE | |
| 103 | + | $link.Description = 'Intel 8051 assembler, simulator and debugger' | |
| 104 | + | $link.Save() | |
| 105 | + | Write-Step 'added bench51 to the Start menu' | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | & (Join-Path $InstallDir 'bench51.exe') --version | |
| 109 | + | Write-Step 'open a new terminal to use bench51 and bench51-gui from PATH' | |
| 110 | + | } finally { | |
| 111 | + | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $Temp | |
| 112 | + | } | |
| @@ -0,0 +1,167 @@ | |||
| 1 | + | #!/bin/sh | |
| 2 | + | # Installs bench51 (the bench51-gui desktop app and the bench51 terminal app) | |
| 3 | + | # from the GitHub releases, on Linux and macOS. | |
| 4 | + | # | |
| 5 | + | # curl -fsSL https://raw.githubusercontent.com/krolyxon/bench51/master/install.sh | sh | |
| 6 | + | # curl -fsSL https://raw.githubusercontent.com/krolyxon/bench51/master/install.sh | sh -s -- --dir /usr/local/bin | |
| 7 | + | # | |
| 8 | + | # Run with --help for all options. On Windows, use install.ps1. | |
| 9 | + | ||
| 10 | + | set -eu | |
| 11 | + | ||
| 12 | + | REPO=krolyxon/bench51 | |
| 13 | + | VERSION=${BENCH51_VERSION:-latest} | |
| 14 | + | DIR=${BENCH51_INSTALL_DIR:-$HOME/.local/bin} | |
| 15 | + | ARCHIVE= | |
| 16 | + | DESKTOP=1 | |
| 17 | + | UNINSTALL=0 | |
| 18 | + | ||
| 19 | + | DATA=${XDG_DATA_HOME:-$HOME/.local/share} | |
| 20 | + | APPS=$DATA/applications | |
| 21 | + | ICONS=$DATA/icons/hicolor/scalable/apps | |
| 22 | + | ||
| 23 | + | usage() { | |
| 24 | + | cat <<EOF | |
| 25 | + | Install bench51 from https://github.com/$REPO/releases | |
| 26 | + | ||
| 27 | + | Usage: install.sh [options] | |
| 28 | + | ||
| 29 | + | --version <tag> release to install, e.g. v0.2.0 (default: latest) | |
| 30 | + | --dir <path> where to put the binaries (default: ~/.local/bin) | |
| 31 | + | --archive <file> install from a downloaded bench51-<target>.tar.gz | |
| 32 | + | --no-desktop on Linux, skip the application menu entry and icon | |
| 33 | + | --uninstall remove bench51 from --dir and the menu | |
| 34 | + | -h, --help show this help | |
| 35 | + | ||
| 36 | + | The environment variables BENCH51_VERSION and BENCH51_INSTALL_DIR set the | |
| 37 | + | defaults for --version and --dir. | |
| 38 | + | EOF | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | say() { printf 'bench51: %s\n' "$*"; } | |
| 42 | + | die() { | |
| 43 | + | printf 'bench51: error: %s\n' "$*" >&2 | |
| 44 | + | exit 1 | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | while [ $# -gt 0 ]; do | |
| 48 | + | case $1 in | |
| 49 | + | --version) [ $# -ge 2 ] || die "--version needs a value"; VERSION=$2; shift 2 ;; | |
| 50 | + | --version=*) VERSION=${1#*=}; shift ;; | |
| 51 | + | --dir) [ $# -ge 2 ] || die "--dir needs a value"; DIR=$2; shift 2 ;; | |
| 52 | + | --dir=*) DIR=${1#*=}; shift ;; | |
| 53 | + | --archive) [ $# -ge 2 ] || die "--archive needs a value"; ARCHIVE=$2; shift 2 ;; | |
| 54 | + | --archive=*) ARCHIVE=${1#*=}; shift ;; | |
| 55 | + | --no-desktop) DESKTOP=0; shift ;; | |
| 56 | + | --uninstall) UNINSTALL=1; shift ;; | |
| 57 | + | -h | --help) usage; exit 0 ;; | |
| 58 | + | *) die "unknown option: $1 (see --help)" ;; | |
| 59 | + | esac | |
| 60 | + | done | |
| 61 | + | ||
| 62 | + | refresh_menu() { | |
| 63 | + | if command -v update-desktop-database >/dev/null 2>&1; then | |
| 64 | + | update-desktop-database -q "$APPS" 2>/dev/null || true | |
| 65 | + | fi | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | if [ "$UNINSTALL" = 1 ]; then | |
| 69 | + | rm -f "$DIR/bench51" "$DIR/bench51-gui" "$APPS/bench51-gui.desktop" "$ICONS/bench51-gui.svg" | |
| 70 | + | refresh_menu | |
| 71 | + | say "removed bench51 from $DIR" | |
| 72 | + | exit 0 | |
| 73 | + | fi | |
| 74 | + | ||
| 75 | + | # ---- Which build fits this machine ---- | |
| 76 | + | ||
| 77 | + | os=$(uname -s) | |
| 78 | + | arch=$(uname -m) | |
| 79 | + | case $os in | |
| 80 | + | Linux) os=unknown-linux-gnu ;; | |
| 81 | + | Darwin) os=apple-darwin ;; | |
| 82 | + | *) die "no prebuilt binaries for $os; on Windows use install.ps1" ;; | |
| 83 | + | esac | |
| 84 | + | case $arch in | |
| 85 | + | x86_64 | amd64) arch=x86_64 ;; | |
| 86 | + | aarch64 | arm64) arch=aarch64 ;; | |
| 87 | + | *) die "no prebuilt binaries for $arch; build from source with: cargo install --git https://github.com/$REPO bench51-gui" ;; | |
| 88 | + | esac | |
| 89 | + | TARGET=$arch-$os | |
| 90 | + | NAME=bench51-$TARGET.tar.gz | |
| 91 | + | ||
| 92 | + | download() { | |
| 93 | + | if command -v curl >/dev/null 2>&1; then | |
| 94 | + | curl --proto '=https' --tlsv1.2 -fsSL -o "$2" "$1" | |
| 95 | + | elif command -v wget >/dev/null 2>&1; then | |
| 96 | + | wget -q --https-only -O "$2" "$1" | |
| 97 | + | else | |
| 98 | + | die "neither curl nor wget is installed" | |
| 99 | + | fi | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | sha256() { | |
| 103 | + | if command -v sha256sum >/dev/null 2>&1; then | |
| 104 | + | sha256sum "$1" | cut -d ' ' -f 1 | |
| 105 | + | elif command -v shasum >/dev/null 2>&1; then | |
| 106 | + | shasum -a 256 "$1" | cut -d ' ' -f 1 | |
| 107 | + | else | |
| 108 | + | die "neither sha256sum nor shasum is installed to verify the download" | |
| 109 | + | fi | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | TMP=$(mktemp -d 2>/dev/null || mktemp -d -t bench51) | |
| 113 | + | trap 'rm -rf "$TMP"' EXIT | |
| 114 | + | trap 'exit 130' INT TERM | |
| 115 | + | ||
| 116 | + | # ---- Download and verify ---- | |
| 117 | + | ||
| 118 | + | if [ -z "$ARCHIVE" ]; then | |
| 119 | + | if [ "$VERSION" = latest ]; then | |
| 120 | + | BASE=https://github.com/$REPO/releases/latest/download | |
| 121 | + | else | |
| 122 | + | case $VERSION in v*) ;; *) VERSION=v$VERSION ;; esac | |
| 123 | + | BASE=https://github.com/$REPO/releases/download/$VERSION | |
| 124 | + | fi | |
| 125 | + | say "downloading $NAME ($VERSION)" | |
| 126 | + | download "$BASE/$NAME" "$TMP/$NAME" || die "could not download $BASE/$NAME" | |
| 127 | + | download "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" || die "could not download the checksums" | |
| 128 | + | expected=$(grep " [* ]*$NAME\$" "$TMP/SHA256SUMS" | cut -d ' ' -f 1) | |
| 129 | + | [ -n "$expected" ] || die "$NAME is not listed in SHA256SUMS" | |
| 130 | + | [ "$(sha256 "$TMP/$NAME")" = "$expected" ] || die "checksum mismatch for $NAME" | |
| 131 | + | ARCHIVE=$TMP/$NAME | |
| 132 | + | fi | |
| 133 | + | [ -f "$ARCHIVE" ] || die "no such archive: $ARCHIVE" | |
| 134 | + | ||
| 135 | + | mkdir "$TMP/x" | |
| 136 | + | tar -xzf "$ARCHIVE" -C "$TMP/x" | |
| 137 | + | SRC=$(find "$TMP/x" -type f -name bench51-gui | head -n 1) | |
| 138 | + | [ -n "$SRC" ] || die "the archive has no bench51-gui" | |
| 139 | + | SRC=$(dirname "$SRC") | |
| 140 | + | [ -f "$SRC/bench51" ] || die "the archive has no bench51" | |
| 141 | + | ||
| 142 | + | # ---- Install ---- | |
| 143 | + | ||
| 144 | + | mkdir -p "$DIR" | |
| 145 | + | for bin in bench51 bench51-gui; do | |
| 146 | + | cp "$SRC/$bin" "$DIR/$bin.tmp" | |
| 147 | + | chmod 755 "$DIR/$bin.tmp" | |
| 148 | + | mv -f "$DIR/$bin.tmp" "$DIR/$bin" | |
| 149 | + | done | |
| 150 | + | say "installed bench51 and bench51-gui to $DIR" | |
| 151 | + | ||
| 152 | + | if [ "$DESKTOP" = 1 ] && [ "$(uname -s)" = Linux ] && [ -f "$SRC/share/applications/bench51-gui.desktop" ]; then | |
| 153 | + | mkdir -p "$APPS" "$ICONS" | |
| 154 | + | # Launchers don't always see ~/.local/bin on PATH: point at the binary. | |
| 155 | + | exec_path=$(printf '%s' "$DIR/bench51-gui" | sed 's/[\\&|]/\\&/g') | |
| 156 | + | sed "s|^Exec=bench51-gui|Exec=\"$exec_path\"|" \ | |
| 157 | + | "$SRC/share/applications/bench51-gui.desktop" >"$APPS/bench51-gui.desktop" | |
| 158 | + | cp "$SRC/share/icons/hicolor/scalable/apps/bench51-gui.svg" "$ICONS/bench51-gui.svg" | |
| 159 | + | refresh_menu | |
| 160 | + | say "added bench51 to the application menu" | |
| 161 | + | fi | |
| 162 | + | ||
| 163 | + | "$DIR/bench51" --version | |
| 164 | + | case ":$PATH:" in | |
| 165 | + | *":$DIR:"*) say "run bench51-gui for the desktop app, or bench51 in a terminal" ;; | |
| 166 | + | *) say "$DIR is not on your PATH; add it, e.g. in ~/.profile: export PATH=\"$DIR:\$PATH\"" ;; | |
| 167 | + | esac | |