| 1 | # bench51 — an 8051 assembler, simulator & debugger |
| 2 | |
| 3 | [](https://github.com/krolyxon/bench51/actions/workflows/ci.yml) |
| 4 | [](https://github.com/krolyxon/bench51/releases/latest) |
| 5 | [](https://github.com/krolyxon/bench51/releases) |
| 6 | [](https://github.com/krolyxon/bench51) |
| 7 | [](crates) |
| 8 | [](Cargo.toml) |
| 9 | [](LICENSE) |
| 10 | [](#install) |
| 11 | [](https://en.wikipedia.org/wiki/Intel_8051) |
| 12 | [](https://github.com/emilk/egui) |
| 13 | [](https://ratatui.rs) |
| 14 | [](https://github.com/krolyxon/bench51/commits) |
| 15 | |
| 16 | A small, Keil µVision-style IDE for the Intel 8051. Write assembly in the |
| 17 | editor, build it, inspect the listing and Intel HEX output, then run, pause |
| 18 | and single-step the program while watching registers, SFRs, memory, ports, |
| 19 | timers, interrupts and the serial port, all in a window laid out like |
| 20 | µVision. |
| 21 | |
| 22 | Like Emacs, it also runs in a terminal: the same assembler and simulator |
| 23 | come with a TUI (`bench51`) for SSH sessions and terminal setups. |
| 24 | |
| 25 | Built in Rust with [egui](https://github.com/emilk/egui) and |
| 26 | [ratatui](https://ratatui.rs). It is a learning tool, not a cycle-exact |
| 27 | replacement for a vendor simulator. |
| 28 | |
| 29 |  |
| 30 | |
| 31 | <table> |
| 32 | <tr> |
| 33 | <td width="50%"><img src="docs/screenshots/gui_edit.png" alt="Editor and Keil-style build output"></td> |
| 34 | <td width="50%"><img src="docs/screenshots/gui_breakpoint_light.png" alt="Stopped at a breakpoint in a timer ISR, light theme"></td> |
| 35 | </tr> |
| 36 | <tr> |
| 37 | <td align="center"><sub>Editor + build output</sub></td> |
| 38 | <td align="center"><sub>Breakpoint in a timer ISR, light theme</sub></td> |
| 39 | </tr> |
| 40 | <tr> |
| 41 | <td width="50%"><img src="docs/screenshots/gui_peripherals.png" alt="Ports, timers and interrupts windows docked side by side"></td> |
| 42 | <td width="50%"><img src="docs/screenshots/gui_errors.png" alt="Assembler errors in the gutter and Build Output"></td> |
| 43 | </tr> |
| 44 | <tr> |
| 45 | <td align="center"><sub>Peripheral windows, rearranged</sub></td> |
| 46 | <td align="center"><sub>Assembler errors</sub></td> |
| 47 | </tr> |
| 48 | </table> |
| 49 | |
| 50 | ## Why this exists |
| 51 | |
| 52 | I had microcontroller practicals to get through, and the course expects you to |
| 53 | use Keil µVision. Keil µVision doesn't run on Linux. Rather than fight with |
| 54 | Wine or a Windows VM every lab session, I wrote my own: bench51 does the parts |
| 55 | of µVision those practicals actually need (write the code, assemble it, look |
| 56 | at the listing and HEX, step through it and watch the registers) natively on |
| 57 | Linux, in a window or right in the terminal. |
| 58 | |
| 59 | ----- |
| 60 | |
| 61 | ## Features |
| 62 | |
| 63 | - **µVision layout**: menus and toolbar, a Project window, the editor, Build |
| 64 | Output, and in a debug session the Registers, Disassembly, Memory, Symbols, |
| 65 | UART #1, Ports, Timers and Interrupts windows. Every window docks and tabs |
| 66 | wherever you drag it; the edit and debug layouts are remembered separately, |
| 67 | as in µVision. |
| 68 | - **Editor** with Vim keybindings (or standard ones, in Settings), syntax |
| 69 | highlighting, find, and breakpoint, error and PC markers in the gutter. |
| 70 | - **Two-pass assembler** for the full 8051 instruction set (all 255 opcodes): |
| 71 | - labels, expressions (`+ - * / MOD AND OR XOR NOT SHL SHR HIGH LOW`, `$`) |
| 72 | - number formats `42`, `2Ah`, `0x2A`, `101010b`, `52o`, `'*'` |
| 73 | - bit addressing (`P1.3`, `ACC.7`, `20h.0`) and all predefined SFR/bit names |
| 74 | - directives `ORG DB DW DS END EQU SET BIT DATA IDATA XDATA CODE CSEG AT` |
| 75 | - generic `JMP`/`CALL` (assembled as `LJMP`/`LCALL`) |
| 76 | - Keil-style `.lst` listing with symbol table, Intel HEX output |
| 77 | - **Simulator** |
| 78 | - all instructions with correct flags (CY, AC, OV, P) and machine-cycle counts |
| 79 | - 4 register banks, 256 bytes IRAM, SFRs, 64K code, 64K XRAM |
| 80 | - ports P0–P3 with latch vs. pin semantics (read-modify-write reads the latch) |
| 81 | - timers 0/1 in modes 0–3, gate and counter inputs |
| 82 | - interrupts: INT0, T0, INT1, T1, serial; two priority levels; edge/level triggers |
| 83 | - UART in modes 0–3 with baud rate timing from timer 1 |
| 84 | - idle and power-down modes (PCON) |
| 85 | - **Debugger**: run / pause, step into / over / out, run to cursor, |
| 86 | breakpoints, changed-value highlighting, memory poke, adjustable speed. |
| 87 | - **Open `.hex` files**: they're disassembled into editable source that |
| 88 | re-assembles to the identical image. |
| 89 | - **Dark and light themes** in a monochrome, dot-matrix design (Space |
| 90 | Grotesk, Space Mono and Doto, bundled). |
| 91 | |
| 92 | ## Install |
| 93 | |
| 94 | Linux and macOS: |
| 95 | |
| 96 | ```bash |
| 97 | curl -fsSL https://raw.githubusercontent.com/krolyxon/bench51/master/install.sh | sh |
| 98 | ``` |
| 99 | |
| 100 | Windows (PowerShell): |
| 101 | |
| 102 | ```powershell |
| 103 | irm https://raw.githubusercontent.com/krolyxon/bench51/master/install.ps1 | iex |
| 104 | ``` |
| 105 | |
| 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: |
| 109 | |
| 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: |
| 120 | |
| 121 | ```bash |
| 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 |
| 124 | ``` |
| 125 | |
| 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` and `--keymap vim|standard` override the choices |
| 136 | in the Settings menu for one run. `--clock <MHz>` overrides the Xtal set in |
| 137 | Project → Options for Target (default 11.0592), which is used for the |
| 138 | elapsed-time display. |
| 139 | |
| 140 | On Linux it runs on Wayland and X11 and uses the XDG desktop portal for file |
| 141 | dialogs. Building it from source on Debian or Ubuntu needs the packages egui |
| 142 | links against: `libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev |
| 143 | libxkbcommon-dev libssl-dev`. |
| 144 | |
| 145 | ## Keys |
| 146 | |
| 147 | The keys are µVision's, with the TUI's as aliases: |
| 148 | |
| 149 | | Key | Action | |
| 150 | | --- | --- | |
| 151 | | F7 | build target (also writes `.lst` and `.hex` next to a saved file) | |
| 152 | | Ctrl-F5 / F4 | start / stop the debug session | |
| 153 | | F5 | run / stop | |
| 154 | | F11 / F10 | step into / step over | |
| 155 | | Ctrl-F11 / F12 | step out | |
| 156 | | Ctrl-F10 / F8 | run to the cursor line | |
| 157 | | F9 / Ctrl-Shift-F9 | toggle breakpoint / remove all breakpoints | |
| 158 | | F6 | reset the CPU | |
| 159 | | Ctrl-N / Ctrl-O / Ctrl-S / Ctrl-W | new / open / save / close | |
| 160 | | Ctrl-F | find | |
| 161 | | F1 | all shortcuts | |
| 162 | |
| 163 | Click the gutter to toggle a breakpoint, double-click a byte in Memory to |
| 164 | change it, click a pin in Ports to hold it low from outside, and click the |
| 165 | bit chips in Registers, Timers and Interrupts to flip SFR bits. Files can be |
| 166 | dropped onto the window. |
| 167 | |
| 168 | ### Editing with Vim keys |
| 169 | |
| 170 | The editor starts in Vim's Normal mode, with a block cursor and a mode line |
| 171 | along its bottom edge: |
| 172 | |
| 173 | - **Modes:** `i a I A o O` to insert, `v V` for Visual and Visual Line, Esc |
| 174 | to go back. |
| 175 | - **Motions:** `h j k l w b e W B E 0 ^ $ gg G f t F T ; , % { } H M L`, |
| 176 | Ctrl-D/U/F/B. All take counts (`5j`, `3w`). |
| 177 | - **Operators:** `d c y > <` with any motion or text object (`iw aw i" a" |
| 178 | i( a( ip`), doubled for whole lines (`dd`, `yy`, `>>`). Also |
| 179 | `x X D C s S r J ~ p P`. |
| 180 | - **Repeat and undo:** `u` undoes, Ctrl-R redoes, `.` repeats the last |
| 181 | change, and `qa`…`q` / `@a` record and replay a macro. |
| 182 | - **Search:** `/ ? n N * #`; `:noh` clears the highlight. |
| 183 | - **Assembly helpers:** `gd` jumps to a label's definition, `gt`/`gT` |
| 184 | switch files, and new lines line up with the instruction column after a |
| 185 | label. |
| 186 | - **Commands:** `:w`, `:q`, `:wq`, `:e file`, `:42`, and `:make` to build. |
| 187 | - **Clipboard:** yanks and deletes also go to the system clipboard, and |
| 188 | Ctrl+V pastes from it. |
| 189 | - **Debug sessions:** the editor is read-only, but moving, searching and |
| 190 | yanking still work. |
| 191 | |
| 192 | Settings → Keybindings → Standard switches to ordinary editing: type to |
| 193 | insert, with Ctrl+Z to undo. F1 lists the keys for whichever is active. |
| 194 | |
| 195 | ## Terminal version |
| 196 | |
| 197 | `bench51` is the same IDE in a terminal, the way `emacs -nw` is Emacs |
| 198 | without a window. It has the editor, listing, HEX and error views, and a |
| 199 | debug screen with registers, peripherals, memory, ports and the UART. The |
| 200 | same binary also assembles headlessly, which is handy in scripts and |
| 201 | Makefiles. |
| 202 | |
| 203 |  |
| 204 | |
| 205 | <table> |
| 206 | <tr> |
| 207 | <td width="50%"><img src="docs/screenshots/listing.png" alt="Editor with the assembled Keil-style listing"></td> |
| 208 | <td width="50%"><img src="docs/screenshots/debug_breakpoint.png" alt="Stopped at a breakpoint inside the timer 0 ISR, SFR view"></td> |
| 209 | </tr> |
| 210 | <tr> |
| 211 | <td align="center"><sub>Editor + assembled listing</sub></td> |
| 212 | <td align="center"><sub>Breakpoint inside a timer ISR, SFR view</sub></td> |
| 213 | </tr> |
| 214 | <tr> |
| 215 | <td width="50%"><img src="docs/screenshots/errors.png" alt="Assembler errors marked in the gutter and listed in the Errors tab"></td> |
| 216 | <td width="50%"><img src="docs/screenshots/hex.png" alt="Intel HEX output tab"></td> |
| 217 | </tr> |
| 218 | <tr> |
| 219 | <td align="center"><sub>Assembler errors in the gutter and Errors tab</sub></td> |
| 220 | <td align="center"><sub>Intel HEX output</sub></td> |
| 221 | </tr> |
| 222 | </table> |
| 223 | |
| 224 | ```bash |
| 225 | bench51 examples/serial_hello.asm # open in the TUI |
| 226 | bench51 # start with a template |
| 227 | bench51 firmware.hex # disassemble & debug a HEX file |
| 228 | |
| 229 | # Headless assembly: writes prog.hex and prog.lst |
| 230 | bench51 --assemble prog.asm |
| 231 | bench51 --assemble prog.asm -o out.hex --lst out.lst |
| 232 | ``` |
| 233 | |
| 234 | `--clock <MHz>` sets the oscillator frequency used for the elapsed-time display |
| 235 | (default 11.0592). |
| 236 | |
| 237 | ### Terminal keys |
| 238 | |
| 239 | | Key | Action | |
| 240 | | --- | --- | |
| 241 | | F1 | help | |
| 242 | | F2 / Ctrl-S | save (assembling a saved file also writes `.lst` and `.hex` next to it) | |
| 243 | | F3 / Ctrl-O | open `.asm` or `.hex` | |
| 244 | | Ctrl-G | edit in `$EDITOR`; on save & exit the buffer reloads and reassembles | |
| 245 | | F4 | switch Edit ⇄ Debug | |
| 246 | | F7 | assemble, reset and load | |
| 247 | | F5 | run / pause (assembles first if the source changed) | |
| 248 | | F11 / F10 / F12 | step into / step over / step out | |
| 249 | | F8 | run to cursor | |
| 250 | | F9 | toggle breakpoint on the cursor line | |
| 251 | | F6 | reset | |
| 252 | | Ctrl-Q | quit | |
| 253 | |
| 254 | Edit mode: `Alt-1/2/3` switch between Listing, HEX and Errors; `Alt-↑/↓`, |
| 255 | `Alt-PgUp/PgDn` scroll them. The editor uses Emacs-style keys |
| 256 | (`Ctrl-U` undo, `Ctrl-R` redo, `Ctrl-Y` paste), or press Ctrl-G to edit in |
| 257 | your own `$EDITOR` (vim, nvim, nano, …). |
| 258 | |
| 259 | Debug mode: `Tab` cycles focus between Source, Memory, Ports and Serial. |
| 260 | Letter shortcuts work too, for terminals that swallow function keys: |
| 261 | `r` run/pause, `s` step, `n` step over, `o` step out, `g` run to cursor, |
| 262 | `b` breakpoint, `x` reset, `i`/`f`/`d`/`c` show IRAM/SFR/XRAM/CODE, |
| 263 | `p` poke memory, `+`/`-` speed, `.` jump to PC, `Esc` pause / back to editor. |
| 264 | |
| 265 | - **Ports**: select a pin and press Space to pull it low from outside (a |
| 266 | button press); the program sees the pin as `latch AND external level`. |
| 267 | - **Serial**: type a line and press Enter to send it (followed by CR) to the |
| 268 | 8051's receiver; everything the 8051 transmits appears above. |
| 269 | |
| 270 | ## Examples |
| 271 | |
| 272 | | File | Shows | |
| 273 | | --- | --- | |
| 274 | | `examples/blink.asm` | port toggling, delay loops, `ACALL`/`RET` | |
| 275 | | `examples/timer_irq.asm` | timer 0 mode 1 interrupt | |
| 276 | | `examples/serial_hello.asm` | UART setup, string output with `MOVC`, echo | |
| 277 | | `examples/button_int0.asm` | edge-triggered INT0, idle mode | |
| 278 | | `examples/bcd_add.asm` | `ADDC` and `DA A` with packed BCD | |
| 279 | | `examples/lookup_movc.asm` | 7-segment table lookup | |
| 280 | |
| 281 | ## Notes and limitations |
| 282 | |
| 283 | - Indirect addressing reaches all 256 bytes of IRAM (8052-style upper 128). |
| 284 | - Serial input has no 9th data bit; in modes 2/3 `RB8` reads 0. |
| 285 | - No 8052 timer 2, external code memory or on-chip peripherals of derivatives. |
| 286 | |
| 287 | ## Development |
| 288 | |
| 289 | ```bash |
| 290 | cargo run --release -p bench51-gui # the desktop app |
| 291 | cargo run --release -p bench51 # the terminal app |
| 292 | cargo test --workspace # assembler, CPU, peripheral, session and example-program tests |
| 293 | cargo clippy --workspace --all-targets |
| 294 | ``` |
| 295 | |
| 296 | Releasing: bump `version` in `Cargo.toml`, add a section to |
| 297 | [CHANGELOG.md](CHANGELOG.md), merge, then tag and push: |
| 298 | |
| 299 | ```bash |
| 300 | git tag v0.3.0 && git push origin v0.3.0 |
| 301 | ``` |
| 302 | |
| 303 | The release workflow checks that the tag matches `Cargo.toml` and builds |
| 304 | every target. It publishes the archives and `SHA256SUMS` with that |
| 305 | changelog section as the release notes, then installs the result with the |
| 306 | one-liners on Linux, macOS and Windows. |
| 307 | |
| 308 | Code layout: |
| 309 | |
| 310 | - `crates/bench51-gui/` is the `bench51-gui` desktop app (egui). |
| 311 | - `src/` is the `bench51` terminal app (ratatui), including headless |
| 312 | assembly. |
| 313 | - `crates/bench51-core/`, shared by both: `isa.rs` (opcode table), `asm/` |
| 314 | (assembler, listing, Intel HEX), `sim/` (CPU, peripherals, disassembler), |
| 315 | `session.rs` (build, run, step and breakpoints), `syntax.rs` |
| 316 | (highlighting tokens), `files.rs` (HEX loading) |