diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-26 11:11:46 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-26 11:11:46 -0300 |
| commit | fc7d3274c41ebc83911c5a6d1225cecea270cc86 (patch) | |
| tree | be6105f3b1600acb308fc346dc4884dff151c056 | |
| parent | 27a749d3a2155419fb72bab66b1e78bcd99882ae (diff) | |
| download | pardes-fc7d3274c41ebc83911c5a6d1225cecea270cc86.tar.gz pardes-fc7d3274c41ebc83911c5a6d1225cecea270cc86.zip | |
Fit Hexdump to the board's width, and cut the boot buffer down to addresses
## 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.
| -rw-r--r-- | src/board_memory.zig | 34 | ||||
| -rw-r--r-- | src/pardes.zig | 72 |
2 files changed, 54 insertions, 52 deletions
diff --git a/src/board_memory.zig b/src/board_memory.zig index f99fc106..702b130d 100644 --- a/src/board_memory.zig +++ b/src/board_memory.zig @@ -296,11 +296,22 @@ pub fn poke(p: *Pardes, id: usize, argument: []const u8) !void { ) catch unreachable); } -/// `Hexdump <addr> [len]` — len bytes, 16 to a row, hex columns and an ASCII -/// gutter, in `hexdump -C`'s layout because that is the one everyone can -/// already read. BYTE reads, so a partial row at the end of the space is a -/// short row rather than a refusal, and no alignment is required: this is the -/// word you reach for when you do not yet know what is there. +/// Bytes per dumped row, and it is a different number on the board. +/// +/// `hexdump -C`'s sixteen is the layout everyone can already read, and it needs 79 columns: ten for +/// the address, forty-eight for the hex, a gap, and the eighteen-column ASCII gutter. The P4 drives +/// a 56-column grid of which seven go to the line-number gutter, so a sixteen-byte row wraps onto a +/// second display line and the columns stop lining up - which is the entire value of the layout. +/// +/// Eight fits in 46 and keeps every property that matters: address on the left, fixed-width hex +/// columns, ASCII on the right, and a gap at the halfway mark because the eye counts in fours and +/// eights rather than in sixteens. +const row_bytes: u32 = if (pardes.platform == .p4) 8 else 16; + +/// `Hexdump <addr> [len]` — len bytes, `row_bytes` to a row, hex columns and an ASCII gutter, in +/// `hexdump -C`'s layout because that is the one everyone can already read. BYTE reads, so a partial +/// row at the end of the space is a short row rather than a refusal, and no alignment is required: +/// this is the word you reach for when you do not yet know what is there. pub fn hexdump(p: *Pardes, id: usize, argument: []const u8) !void { var it = std.mem.tokenizeAny(u8, argument, " \t\r\n"); const addr = try parseAddr(it.next() orelse return Error.MissingAddress); @@ -312,15 +323,14 @@ pub fn hexdump(p: *Pardes, id: usize, argument: []const u8) !void { errdefer out.deinit(); try writeNote(&out.writer, e, requested, "bytes"); var row: u32 = 0; - while (row < e.count) : (row += 16) { - const n = @min(@as(u32, 16), e.count - row); - var bytes: [16]u8 = undefined; + while (row < e.count) : (row += row_bytes) { + const n = @min(row_bytes, e.count - row); + var bytes: [row_bytes]u8 = undefined; for (0..n) |i| bytes[i] = readByte(addr + row + @as(u32, @intCast(i))); try out.writer.print("0x{x:0>8} ", .{addr + row}); - for (0..16) |i| { - // hexdump -C's gap after the eighth column: the eye counts to - // eight, not to sixteen - if (i == 8) try out.writer.writeByte(' '); + for (0..row_bytes) |i| { + // The gap at the halfway mark: the eye counts to four or eight, not to sixteen. + if (i == row_bytes / 2) try out.writer.writeByte(' '); if (i < n) try out.writer.print(" {x:0>2}", .{bytes[i]}) else diff --git a/src/pardes.zig b/src/pardes.zig index 8d68cf22..691ee345 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -5747,50 +5747,42 @@ pub const Pardes = struct { // the honest description of a buffer on a board with no filesystem. // AND IT BOOTS WITH SOMETHING IN IT. An empty buffer is honest and useless: the three // words that make this board interesting take an address, and a board's address space is - // precisely the thing you cannot guess. So the boot buffer is a tour of it - every - // address below comes from this repository rather than from memory, which is why they are - // worth trusting: the two flash figures and the two RAM ones are the linker script's own - // ORIGINs (`05-zig-p4/build.zig`'s MEMORY block), and the peripheral bases are - // `DR_REG_*` from ESP-IDF's headers as `05-zig-p4/src/hal` uses them. + // precisely the thing you cannot guess. // - // 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. Run one by putting the cursor on it, `x` to select the line, Tab to execute - - // the same two keystrokes as any other selection, because an output buffer is a file pane - // and these are ordinary words. + // SHORT, because the window is fourteen rows. The first draft opened with four lines of + // prose about there being no operating system, which is true, unhelpful, and cost a + // third of the screen before the first command. One header line earns its place; the + // rest of the screen is addresses. + // + // Each command sits alone on its line because an argument list ends at the last argument + // - a trailing comment would be `ExtraArgument` - so the labels go above. Every address + // is from this repository or from a session that read it on this die: the flash and RAM + // figures are the linker script's own ORIGINs (`05-zig-p4/build.zig`), the peripheral + // bases are the `DR_REG_*` values `05-zig-p4/src/hal` uses, and the two LP addresses at + // the end are named in ESP-IDF's own headers: 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 and the + // other never gives the same answer twice. Both verified on this die. const content = try p.gpa.dupe(u8, - \\pardes on an ESP32-P4. No OS, no filesystem: - \\this buffer is text with nothing behind it. - \\Peek, Poke and Hexdump read the bus itself. - \\Numbers are hex, with or without 0x. - \\ - \\Select a line with x, then Tab to run it. - \\ - \\-- this image in flash: rodata, then code - \\Hexdump 40000020 60 - \\Hexdump 40050000 60 - \\ - \\-- L2MEM: firmware data, then editor heap - \\Hexdump 4ff00000 60 - \\Hexdump 4ff40000 60 + \\x selects a line, Tab runs it. Hex, 0x optional. \\ - \\-- the mask ROM, where ets_printf lives - \\Hexdump 4fc00000 40 - \\ - \\-- UART0: the console you are reading on + \\-- flash: this image's rodata, then its code + \\Hexdump 40000020 40 + \\Hexdump 40050000 40 + \\-- L2MEM: firmware data, then the editor's heap + \\Hexdump 4ff00000 40 + \\Hexdump 4ff40000 40 + \\-- the mask ROM + \\Hexdump 4fc00000 20 + \\-- UART0, the console you are reading this on \\Peek 500ca000 4 - \\ - \\-- systimer. Run twice: a register is not - \\-- memory, and the two reads differ. - \\Peek 500e2000 4 - \\ - \\-- GPIO_OUT, and pad 0's IO_MUX entry - \\Peek 500e0004 - \\Peek 500e1004 - \\ - \\-- Poke needs a value. Harmless: a W1TC - \\-- register clears nothing given no bits. - \\Poke 500e000c 0 + \\-- LP_STORE0: write a word, then read it back + \\Poke 5011002c deadbeef + \\Peek 5011002c + \\-- RNG_DATA: a register is not memory. Run twice. + \\Peek 501101a4 + \\Peek 501101a4 ); errdefer p.gpa.free(content); _ = try output_pane.open(p, 0, "", .{ .cmd = .New }, "", content); |
