add ascii to programmer mode
This commit is contained in:
parent
51b63b4fb8
commit
6a5bdf1e5e
3 changed files with 113 additions and 11 deletions
|
|
@ -619,10 +619,14 @@ Fields (navigable with Up/Down):
|
||||||
2. DEC(s) - editable signed decimal
|
2. DEC(s) - editable signed decimal
|
||||||
3. DEC(u) - editable unsigned decimal
|
3. DEC(u) - editable unsigned decimal
|
||||||
4. HEX - editable hex (space-separated bytes: FF 00 AB CD)
|
4. HEX - editable hex (space-separated bytes: FF 00 AB CD)
|
||||||
5. OCT - editable octal
|
5. ASCII - read-only (derived) view of the bytes, one glyph per byte,
|
||||||
6. BIN - editable binary (nibble-separated: 1111 0000)
|
MSB first, aligned column-for-column under the HEX row directly above
|
||||||
7. ASCII - shows printable ASCII interpretation of the bytes
|
it. Printable bytes (0x20-0x7E) show as themselves; every other byte
|
||||||
(e.g., `.ELF` or `hell` - dots for non-printable)
|
shows as `.`, matching the `xxd` hex-dump convention (e.g. `.ELF`,
|
||||||
|
`...ascii`). Not part of the Up/Down focus cycle since it has nothing
|
||||||
|
to edit.
|
||||||
|
6. OCT - editable octal
|
||||||
|
7. BIN - editable binary (nibble-separated: 1111 0000)
|
||||||
8. Expression - full expression evaluator (& | ^ ~ << >> etc.)
|
8. Expression - full expression evaluator (& | ^ ~ << >> etc.)
|
||||||
|
|
||||||
Navigation:
|
Navigation:
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,49 @@ pub fn formatOctal(buf: []u8, value: u128, bit_width: BitWidth) FormattedValue {
|
||||||
|
|
||||||
return .{ .display = display, .raw = raw };
|
return .{ .display = display, .raw = raw };
|
||||||
}
|
}
|
||||||
|
/// Format an integer's bytes as an ASCII representation for programmer mode.
|
||||||
|
/// One glyph per byte, most-significant byte first (matching the hex byte view).
|
||||||
|
/// Printable bytes (0x20-0x7E) render as themselves; every other byte renders
|
||||||
|
/// as '.', matching the convention used by `xxd` and similar hex dumps.
|
||||||
|
///
|
||||||
|
/// Display: " . . . a s c i i" - each byte is a leading space + glyph,
|
||||||
|
/// space-separated, so the row lines up column-for-column under formatHex.
|
||||||
|
/// Raw: "...ascii" - contiguous glyphs, clipboard-friendly.
|
||||||
|
pub fn formatAscii(buf: []u8, value: u128, bit_width: BitWidth) FormattedValue {
|
||||||
|
const num_bytes: usize = @as(usize, bit_width.bits()) / 8;
|
||||||
|
|
||||||
|
// Raw: contiguous glyphs, MSB first.
|
||||||
|
var pos: usize = 0;
|
||||||
|
var k: usize = 0;
|
||||||
|
while (k < num_bytes) : (k += 1) {
|
||||||
|
const shift_amt: u7 = @intCast((num_bytes - 1 - k) * 8);
|
||||||
|
const byte: u8 = @intCast((value >> shift_amt) & 0xFF);
|
||||||
|
buf[pos] = asciiGlyph(byte);
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
const raw = buf[0..pos];
|
||||||
|
|
||||||
|
// Display: " c" per byte, space-separated, so glyph k sits under the
|
||||||
|
// right hex digit of byte k in the hex row.
|
||||||
|
const display_start = pos;
|
||||||
|
k = 0;
|
||||||
|
while (k < num_bytes) : (k += 1) {
|
||||||
|
if (k > 0) {
|
||||||
|
buf[pos] = ' ';
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
buf[pos] = ' ';
|
||||||
|
pos += 1;
|
||||||
|
const shift_amt: u7 = @intCast((num_bytes - 1 - k) * 8);
|
||||||
|
const byte: u8 = @intCast((value >> shift_amt) & 0xFF);
|
||||||
|
buf[pos] = asciiGlyph(byte);
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
const display = buf[display_start..pos];
|
||||||
|
|
||||||
|
return .{ .display = display, .raw = raw };
|
||||||
|
}
|
||||||
|
|
||||||
pub fn formatDecimalUnsigned(buf: []u8, value: u128) FormattedValue {
|
pub fn formatDecimalUnsigned(buf: []u8, value: u128) FormattedValue {
|
||||||
const raw_len = writeUnsignedInt(buf, value);
|
const raw_len = writeUnsignedInt(buf, value);
|
||||||
const raw = buf[0..raw_len];
|
const raw = buf[0..raw_len];
|
||||||
|
|
@ -201,6 +244,13 @@ fn hexDigit(nibble: u4) u8 {
|
||||||
return 'A' + @as(u8, nibble) - 10;
|
return 'A' + @as(u8, nibble) - 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Map a byte to its printable glyph, or '.' if outside the printable ASCII
|
||||||
|
/// range (0x20-0x7E), matching the `xxd` hex-dump convention.
|
||||||
|
fn asciiGlyph(byte: u8) u8 {
|
||||||
|
if (byte >= 0x20 and byte <= 0x7E) return byte;
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
fn writeUnsignedInt(buf: []u8, value: u128) usize {
|
fn writeUnsignedInt(buf: []u8, value: u128) usize {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
buf[0] = '0';
|
buf[0] = '0';
|
||||||
|
|
@ -437,3 +487,45 @@ test "formatFloat: regular float" {
|
||||||
const result = formatFloat(&buf, 3.14159);
|
const result = formatFloat(&buf, 3.14159);
|
||||||
try testing.expect(std.mem.indexOf(u8, result.display, "3.14") != null);
|
try testing.expect(std.mem.indexOf(u8, result.display, "3.14") != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "formatAscii: printable word 64-bit" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
// 0x0000006173636969 -> bytes 00 00 00 61 73 63 69 69 -> "...ascii"
|
||||||
|
const result = formatAscii(&buf, 0x0000006173636969, .bits64);
|
||||||
|
try testing.expectEqualStrings("...ascii", result.raw);
|
||||||
|
// Display aligns one glyph per byte, space-separated
|
||||||
|
try testing.expectEqualStrings(" . . . a s c i i", result.display);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatAscii: 8-bit printable" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
const result = formatAscii(&buf, 0x41, .bits8);
|
||||||
|
try testing.expectEqualStrings("A", result.raw);
|
||||||
|
try testing.expectEqualStrings(" A", result.display);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatAscii: 8-bit non-printable becomes dot" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
const result = formatAscii(&buf, 0x00, .bits8);
|
||||||
|
try testing.expectEqualStrings(".", result.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatAscii: control and high bytes become dots" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
// 0x1B (ESC) and 0xFF are both non-printable
|
||||||
|
const result = formatAscii(&buf, 0x1BFF, .bits16);
|
||||||
|
try testing.expectEqualStrings("..", result.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatAscii: boundary bytes 0x20 and 0x7E are printable" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
// 0x20 = space, 0x7E = '~'
|
||||||
|
const result = formatAscii(&buf, 0x207E, .bits16);
|
||||||
|
try testing.expectEqualStrings(" ~", result.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatAscii: 0x7F is non-printable" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
const result = formatAscii(&buf, 0x7F, .bits8);
|
||||||
|
try testing.expectEqualStrings(".", result.raw);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
||||||
draw.writeStr(surface, base_start + 2, 11, hex.display, .{ .fg = C.green });
|
draw.writeStr(surface, base_start + 2, 11, hex.display, .{ .fg = C.green });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ASCII (derived, read-only): one glyph per byte, aligned under HEX.
|
||||||
|
var ascii_buf: [128]u8 = undefined;
|
||||||
|
const ascii = engine.formatter.formatAscii(&ascii_buf, val, bw);
|
||||||
|
draw.writeStr(surface, base_start + 3, 2, "ASCII:", .{ .fg = C.cyan });
|
||||||
|
draw.writeStr(surface, base_start + 3, 11, ascii.display, .{ .fg = C.orange });
|
||||||
|
|
||||||
// OCT
|
// OCT
|
||||||
var oct_buf: [256]u8 = undefined;
|
var oct_buf: [256]u8 = undefined;
|
||||||
const oct = engine.formatter.formatOctal(&oct_buf, val, bw);
|
const oct = engine.formatter.formatOctal(&oct_buf, val, bw);
|
||||||
|
|
@ -72,11 +78,11 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
||||||
.{ .fg = C.bg, .bg = C.purple, .bold = true }
|
.{ .fg = C.bg, .bg = C.purple, .bold = true }
|
||||||
else
|
else
|
||||||
.{ .fg = C.cyan };
|
.{ .fg = C.cyan };
|
||||||
draw.writeStr(surface, base_start + 3, 2, "OCT:", oct_style);
|
draw.writeStr(surface, base_start + 4, 2, "OCT:", oct_style);
|
||||||
if (focused == .oct) {
|
if (focused == .oct) {
|
||||||
drawFieldWithCursor(surface, base_start + 3, 11, oct.display, app.bit_cursor, 3, bw.bits(), C.purple);
|
drawFieldWithCursor(surface, base_start + 4, 11, oct.display, app.bit_cursor, 3, bw.bits(), C.purple);
|
||||||
} else {
|
} else {
|
||||||
draw.writeStr(surface, base_start + 3, 11, oct.display, .{ .fg = C.purple });
|
draw.writeStr(surface, base_start + 4, 11, oct.display, .{ .fg = C.purple });
|
||||||
}
|
}
|
||||||
|
|
||||||
// BIN
|
// BIN
|
||||||
|
|
@ -86,15 +92,15 @@ pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, hei
|
||||||
.{ .fg = C.bg, .bg = C.yellow, .bold = true }
|
.{ .fg = C.bg, .bg = C.yellow, .bold = true }
|
||||||
else
|
else
|
||||||
.{ .fg = C.cyan };
|
.{ .fg = C.cyan };
|
||||||
draw.writeStr(surface, base_start + 4, 2, "BIN:", bin_style);
|
draw.writeStr(surface, base_start + 5, 2, "BIN:", bin_style);
|
||||||
if (focused == .bin) {
|
if (focused == .bin) {
|
||||||
drawFieldWithCursor(surface, base_start + 4, 11, bin.display, app.bit_cursor, 1, bw.bits(), C.yellow);
|
drawFieldWithCursor(surface, base_start + 5, 11, bin.display, app.bit_cursor, 1, bw.bits(), C.yellow);
|
||||||
} else {
|
} else {
|
||||||
draw.writeStr(surface, base_start + 4, 11, bin.display, .{ .fg = C.yellow });
|
draw.writeStr(surface, base_start + 5, 11, bin.display, .{ .fg = C.yellow });
|
||||||
}
|
}
|
||||||
|
|
||||||
// History
|
// History
|
||||||
const hist_start = base_start + 6;
|
const hist_start = base_start + 7;
|
||||||
const hist_end = height -| 4;
|
const hist_end = height -| 4;
|
||||||
if (hist_start < hist_end) {
|
if (hist_start < hist_end) {
|
||||||
tui.drawHistory(app.history.items, surface, hist_start, hist_end);
|
tui.drawHistory(app.history.items, surface, hist_start, hist_end);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue