summaryrefslogtreecommitdiff
path: root/src/board_memory.zig
Commit message (Collapse)AuthorAge
* Fit Hexdump to the board's width, and cut the boot buffer down to addressesGabriel Schneider2026-08-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ## Eight bytes a row on the P4 `hexdump -C`'s sixteen needs 79 columns: ten for the address, forty-eight of hex, a gap, and eighteen of ASCII gutter. The board drives 56 columns of which seven go to the line numbers, so every row wrapped onto a second display line and the columns stopped lining up - which is the entire value of the layout. Eight fits in 46 and keeps every property that matters, including a gap at the halfway mark, because the eye counts in fours and eights rather than in sixteens. Verified on the die: 0x40000020 32 54 cd ab 00 00 00 00 |2T......| 0x40000030 30 2e 31 00 00 00 00 00 |0.1.....| That is the app descriptor: 0xABCD5432 and the version string, read out of flash by a command typed with no 0x on either argument. ## The boot buffer is shorter, and its addresses are named The first draft opened with four lines of prose explaining that there is no operating system. True, unhelpful, and it cost a third of a fourteen-row window before the first command. One header line earns its place; the rest of the screen is addresses. The two LP registers at the end are now named, because they are named in ESP-IDF's own headers and the names are the interesting part: 0x5011002c is LP_SYSTEM_REG_LP_STORE0, a general-purpose retention register that holds what you put in it, and 0x501101a4 is LP_SYSTEM_REG_RNG_DATA, the hardware random generator. Between them they demonstrate the whole point of a volatile read - one address gives back what was written, the other never gives the same answer twice: Poke 5011002c deadbeef -> 0x5011002c: wrote 0xdeadbeef, reads 0xdeadbeef Peek 5011002c -> 0x5011002c: 0xdeadbeef Peek 501101a4 -> 0x501101a4: 0x0b099791 Peek 501101a4 -> 0x501101a4: 0xfc97f3b7 All four run on the die, all with bare hex. Peek and Poke had not been tested there before this - only Hexdump had, which I had let stand as though it covered all three. snap 95/95, hxdiff 481/0, hxparity 561/0, unit-test, tty/p4/gui.
* Every literal in Peek, Poke and Hexdump is hex; boot the P4 into a tour of ↵Gabriel Schneider2026-08-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | the bus ## Hex, always Base-0 parsing accepted `0x4ff40000` and `1341390848` and refused a bare `4ff40000`, on the grounds that guessing between hex and decimal would let one typo address somewhere else entirely. The reasoning was sound and the conclusion was still wrong: the ambiguity it guarded against is not a real one. Every address anybody has ever typed at these three words is hex - it came off a datasheet, a linker map, or a previous dump's own output, all of which print hex - so the base was never in doubt, and demanding `0x` on every one of them was a toll on the common case to protect a case that does not arise. The COUNTS go with them, and that is the part worth saying out loud rather than leaving as a surprise: `Hexdump 4ff40000 100` shows 0x100 bytes, which is 256, not one hundred. One rule for every literal beats two rules that each fit their own argument better, because the second kind has to be remembered at the moment you are concentrating on something else. What these words PRINT is hex too now, clamp notes included, so a number can go back in where it came out. ## And the board boots into somewhere worth looking The empty output buffer was honest and useless. The three words that make this port interesting all take an address, and a board's address space is precisely the thing you cannot guess - so the boot buffer is now a tour of it: the image's own rodata and code in flash, the firmware's data and the editor's heap in L2MEM, the mask ROM, UART0, the systimer, GPIO_OUT and an IO_MUX pad, and one harmless Poke. Every address comes from this repository rather than from memory, which is what makes them worth trusting: the flash and RAM figures are the linker script's own ORIGINs in `05-zig-p4/build.zig`, and the peripheral bases are the `DR_REG_*` values `05-zig-p4/src/hal` uses. Each command sits alone on its line because an argument list ends at the last argument - a trailing comment would be `ExtraArgument` - so the notes go above the lines they describe. Lines are kept inside 48 columns because the first draft wrapped every one of them at the 56-column grid, which reads like a bug. Verified on the die: the buffer renders one line per line, and putting the cursor on `Hexdump 40000020 60`, selecting with `x` and pressing Tab opens a dump whose first bytes are `32 54 cd ab` - 0xABCD5432, the ESP app-descriptor magic - with the version string right behind it. Bare hex, no prefix, reading real flash. snap 95/95, hxdiff 481/0, hxparity 561/0, unit-test, tty/p4/gui.
* A fourth platform: pardes as ESP32-P4 firmware, bytes in and bytes outGabriel Schneider2026-08-25
`-Dplatform=p4 -Dtarget=riscv32-freestanding` emits a single freestanding OBJECT exporting a seven-function C ABI, not an executable. The board's toolchain (../05-zig-p4) owns `_start`, the linker script and the UART driver and links this in. The seam is bytes rather than types, so neither side can accidentally depend on the other's internals, and a signature that drifts fails at link time. The serial line is the whole of the I/O. `src/p4.zig` drives vaxis unchanged over it: the renderer is a byte writer and `queryTerminalSend` is a byte writer, so the terminal emulator on the host answers the capability handshake and the firmware sees a real terminal. Measured going out over the wire on attach: alt screen, in-band resize, cursor report, kitty keyboard, kitty graphics, DA1. THREE WORDS EXIST ONLY HERE. `src/board_memory.zig` implements `Peek`, `Poke` and `Hexdump`, gated on `builtin.os.tag == .freestanding and !isWasm()` - derived from the TARGET, because they are a property of running with no OS under you rather than a product option, and because wasm is freestanding too and is exactly what must be excluded: in a browser an address is an offset into the linear memory this editor's own heap lives in. Every access goes through `*allowzero volatile`: a peripheral register is not memory, and address 0 is an ordinary unmapped address on this bus. One 4 KiB cap per command, set by the console rather than the memory - an unbounded dump would wedge the only console the board has for eleven hours. Measured on ESP32-P4 rev v1.3 silicon, driven from a host terminal: Peek 0x501101a4 0x0e63ce71, then 0xaeaa6919 on a second read - the RNG register, so the volatile loads are not folded Poke 0x5011002c 0xdeadbeef LP_STORE0; a later Peek returned 0xdeadbeef Hexdump 0x5011002c 32 16 bytes a row, hex columns and an ASCII gutter Peek 0x50110001 `peek: MisalignedAddress` on the message row That last line is the one that matters. A misaligned 32-bit access traps, and a trap in firmware is a watchdog reset that takes the session with it, so the check that turns it into a message is the reason the file is hand-written rather than a generic reader. BARE METAL BOOTS AN EMPTY OUTPUT BUFFER. Every other boot layout in `init` makes a shell, and on this platform that is not a preference but an impossibility: nothing to fork, no pty to give a terminal pane. Booting one anyway produced precisely what that describes - a pane whose tag ends in `Filter`, no gutter, no buffer, and every keystroke vanishing into the Fallback's silent pty. An output buffer is also what the platform's own words want, since Peek, Poke and Hexdump each fill one. Sized for the board rather than for a desktop: * `allocators.zig` gains a p4 tier that is ALL fallback - every capacity is zero, so each arena spills immediately to the 384 KiB heap the firmware hands over, and no megabyte-shaped static reservation lands in `.bss`. * `source_manifest.zig`'s allowlist is EMPTY on p4. The table is ~0.95 MiB of rodata against a 1.5 MiB flash partition; the firmware's filesystem is the serial host's, through the Host vtable. * The grid is clamped and the clamp is measured, not guessed: every cell is paid for four times (vaxis Screen + InternalScreen, pardes Surface + previous_cells), so 40x12 fits and 80x24 exhausts the heap during `Pardes.init`. * `Vaxis.resize` deinits both screens before allocating replacements, so a failed resize leaves vaxis rendering nothing. The p4 shell keeps the previous geometry on failure instead of leaving a half-applied one. Also here: `output_pane_integration_test.zig` had an exhaustive switch over `Platform` that adding `.p4` left unhandled, which broke `zig build unit-test` outright - the native test binary is the one consumer no platform build compiles. 346 tests pass again.