summaryrefslogtreecommitdiff
path: root/src/board_memory.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/board_memory.zig')
-rw-r--r--src/board_memory.zig69
1 files changed, 51 insertions, 18 deletions
diff --git a/src/board_memory.zig b/src/board_memory.zig
index 853ae5da..f99fc106 100644
--- a/src/board_memory.zig
+++ b/src/board_memory.zig
@@ -85,20 +85,41 @@ pub const Error = error{
ExtraArgument,
};
-/// hex (`0x4ff40000`), decimal (`1341718528`), and — for free, from base 0 —
-/// binary and octal. A bare `4ff40000` is deliberately NOT hex: it is a
-/// legal-looking decimal number, so guessing the base would make one typo
-/// silently address somewhere else entirely.
+/// EVERY literal these three words take is HEX, with or without an `0x`, and there is no way to
+/// write a decimal one.
+///
+/// This replaces base-0 parsing, which accepted `0x4ff40000` and `1341390848` and refused a bare
+/// `4ff40000` on the grounds that guessing between hex and decimal would make one typo address
+/// somewhere else entirely. That reasoning was sound and the conclusion was still wrong: the
+/// ambiguity it protected 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 guard a case that does not arise.
+///
+/// The COUNTS go with them, and that is the part worth stating 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 in the word is worth more than two rules that each fit their argument better,
+/// because the second kind is the sort of thing you have to remember at the moment you are already
+/// concentrating on something else. Everything these words PRINT is hex too, including the clamp
+/// notes, so a number can go back in where it came out.
+fn parseHex(comptime T: type, tok: []const u8, bad: Error) Error!T {
+ // `parseInt` only honours an `0x` when its base is 0, so with base 16 the prefix has to come off
+ // here. A bare `0x` leaves nothing behind and `parseInt` rejects the empty string, which is the
+ // answer that wants giving.
+ const body = if (tok.len > 2 and tok[0] == '0' and (tok[1] | 0x20) == 'x') tok[2..] else tok;
+ return std.fmt.parseInt(T, body, 16) catch bad;
+}
+
fn parseAddr(tok: []const u8) Error!u32 {
- return std.fmt.parseInt(u32, tok, 0) catch return Error.BadAddress;
+ return parseHex(u32, tok, Error.BadAddress);
}
fn parseCount(tok: []const u8) Error!u64 {
- return std.fmt.parseInt(u64, tok, 0) catch return Error.BadCount;
+ return parseHex(u64, tok, Error.BadCount);
}
fn parseValue(tok: []const u8) Error!u32 {
- return std.fmt.parseInt(u32, tok, 0) catch return Error.BadValue;
+ return parseHex(u32, tok, Error.BadValue);
}
/// A 32-bit peripheral or RAM read that the compiler may neither elide,
@@ -152,12 +173,14 @@ fn extent(addr: u32, requested: u64, unit: u32, cap: u32) Extent {
fn writeNote(w: *std.Io.Writer, e: Extent, requested: u64, unit_name: []const u8) !void {
switch (e.limit orelse return) {
+ // Hex, like everything else these words read and print, so the number in a clamp note can go
+ // straight back into the command that produced it.
.console => try w.print(
- "clamped: {d} {s} requested, {d} shown ({d}-byte cap, one 115200-baud console)\n",
+ "clamped: 0x{x} {s} requested, 0x{x} shown (0x{x}-byte cap, one 115200-baud console)\n",
.{ requested, unit_name, e.count, max_bytes },
),
.space => try w.print(
- "clamped: {d} {s} requested, {d} shown (the 32-bit address space ends at 0x100000000)\n",
+ "clamped: 0x{x} {s} requested, 0x{x} shown (the 32-bit address space ends at 0x100000000)\n",
.{ requested, unit_name, e.count },
),
}
@@ -192,24 +215,34 @@ test "a clamp note is written exactly when something was clamped" {
try writeNote(&w, extent(0x4ff40000, 99_999, 4, max_words), 99_999, "words");
try std.testing.expectEqualStrings(
- "clamped: 99999 words requested, 1024 shown (4096-byte cap, one 115200-baud console)\n",
+ "clamped: 0x1869f words requested, 0x400 shown (0x1000-byte cap, one 115200-baud console)\n",
w.buffered(),
);
w = .fixed(&buf);
try writeNote(&w, extent(0xfffffff0, 64, 1, max_bytes), 64, "bytes");
try std.testing.expectEqualStrings(
- "clamped: 64 bytes requested, 16 shown (the 32-bit address space ends at 0x100000000)\n",
+ "clamped: 0x40 bytes requested, 0x10 shown (the 32-bit address space ends at 0x100000000)\n",
w.buffered(),
);
}
-test "an address is hex or decimal, and a bare hex-looking token is decimal" {
- try std.testing.expectEqual(0x4ff40000, parseAddr("0x4ff40000"));
- try std.testing.expectEqual(0x4ff40000, parseAddr("1341390848"));
- // a bare hex-looking token is a decimal number, never a guess
- try std.testing.expectError(Error.BadAddress, parseAddr("4ff40000"));
+test "every literal is hex, with or without the prefix" {
+ const eq = std.testing.expectEqual;
+ // the prefix is optional, never required, and never changes the answer
+ try eq(0x4ff40000, parseAddr("0x4ff40000"));
+ try eq(0x4ff40000, parseAddr("4ff40000"));
+ try eq(0x4ff40000, parseAddr("0X4FF40000"));
+ try eq(0x4ff40000, parseAddr("4FF40000"));
+ // a token that looks decimal is hex too - the whole point, and the thing to remember
+ try eq(0x100, parseCount("100"));
+ try eq(0x256, parseCount("256"));
+ try eq(0xdeadbeef, parseValue("deadbeef"));
+ // and the refusals still refuse
try std.testing.expectError(Error.BadAddress, parseAddr("0x100000000"));
+ try std.testing.expectError(Error.BadAddress, parseAddr("0x"));
+ try std.testing.expectError(Error.BadAddress, parseAddr("nope"));
+ try std.testing.expectError(Error.BadAddress, parseAddr("12g4"));
try std.testing.expectError(Error.BadCount, parseCount("-1"));
try std.testing.expectError(Error.BadValue, parseValue("0x1_0000_0000"));
}
@@ -221,7 +254,7 @@ test "an address is hex or decimal, and a bare hex-looking token is decimal" {
pub fn peek(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);
- const requested = if (it.next()) |tok| try parseCount(tok) else 1;
+ const requested = if (it.next()) |tok| try parseCount(tok) else 0x1;
if (it.next() != null) return Error.ExtraArgument;
if (addr % 4 != 0) return Error.MisalignedAddress;
@@ -271,7 +304,7 @@ pub fn poke(p: *Pardes, id: usize, argument: []const u8) !void {
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);
- const requested = if (it.next()) |tok| try parseCount(tok) else 256;
+ const requested = if (it.next()) |tok| try parseCount(tok) else 0x100;
if (it.next() != null) return Error.ExtraArgument;
const e = extent(addr, requested, 1, max_bytes);