//! Programmer mode drawing for the TUI. const std = @import("std"); const vaxis = @import("vaxis"); const vxfw = vaxis.vxfw; const engine = @import("engine"); const draw = @import("draw.zig"); const tui = @import("../tui.zig"); const C = draw.C; pub fn drawProgrammerMode(app: *tui.App, surface: *vxfw.Surface, width: u16, height: u16) void { _ = width; const bw = app.prog_config.bit_width; const val = app.prog_value & bw.mask(); const focused = app.prog_field; // Config display var config_buf: [80]u8 = undefined; const config_str = std.fmt.bufPrint(&config_buf, "Bits: {d} Signed: {s} Endian: LE", .{ bw.bits(), if (app.prog_config.signedness == .signed) "yes" else "no", }) catch "Bits: ??"; draw.writeStr(surface, 2, 2, config_str, .{ .fg = C.muted }); // Bit grid const grid_start: u16 = 4; drawBitGrid(app, surface, grid_start, val, bw); // Multi-base display below bit grid const grid_rows: u16 = (@as(u16, bw.bits()) + 31) / 32; const base_start: u16 = grid_start + grid_rows + 1; const int = engine.types.Integer{ .raw = val, .bit_width = bw, .signedness = .signed }; // DEC(s) var sdec_buf: [256]u8 = undefined; const sdec = engine.formatter.formatDecimalSigned(&sdec_buf, int.signedValue()); const sdec_style: vaxis.Style = if (focused == .dec_signed) .{ .fg = C.bg, .bg = C.cyan, .bold = true } else .{ .fg = C.cyan }; draw.writeStr(surface, base_start, 2, "DEC(s):", sdec_style); draw.writeStr(surface, base_start, 11, sdec.display, if (focused == .dec_signed) .{ .fg = C.fg, .bold = true } else .{ .fg = C.fg }); // DEC(u) var udec_buf: [256]u8 = undefined; const udec = engine.formatter.formatDecimalUnsigned(&udec_buf, val); const udec_style: vaxis.Style = if (focused == .dec_unsigned) .{ .fg = C.bg, .bg = C.cyan, .bold = true } else .{ .fg = C.cyan }; draw.writeStr(surface, base_start + 1, 2, "DEC(u):", udec_style); draw.writeStr(surface, base_start + 1, 11, udec.display, if (focused == .dec_unsigned) .{ .fg = C.fg, .bold = true } else .{ .fg = C.fg }); // HEX var hex_buf: [256]u8 = undefined; const hex = engine.formatter.formatHex(&hex_buf, val, bw); const hex_style: vaxis.Style = if (focused == .hex) .{ .fg = C.bg, .bg = C.green, .bold = true } else .{ .fg = C.cyan }; draw.writeStr(surface, base_start + 2, 2, "HEX:", hex_style); if (focused == .hex) { drawFieldWithCursor(surface, base_start + 2, 11, hex.display, app.bit_cursor, 4, bw.bits(), C.green); } else { draw.writeStr(surface, base_start + 2, 11, hex.display, .{ .fg = C.green }); } // OCT var oct_buf: [256]u8 = undefined; const oct = engine.formatter.formatOctal(&oct_buf, val, bw); const oct_style: vaxis.Style = if (focused == .oct) .{ .fg = C.bg, .bg = C.purple, .bold = true } else .{ .fg = C.cyan }; draw.writeStr(surface, base_start + 3, 2, "OCT:", oct_style); if (focused == .oct) { drawFieldWithCursor(surface, base_start + 3, 11, oct.display, app.bit_cursor, 3, bw.bits(), C.purple); } else { draw.writeStr(surface, base_start + 3, 11, oct.display, .{ .fg = C.purple }); } // BIN var bin_buf: [512]u8 = undefined; const bin = engine.formatter.formatBinary(&bin_buf, val, bw); const bin_style: vaxis.Style = if (focused == .bin) .{ .fg = C.bg, .bg = C.yellow, .bold = true } else .{ .fg = C.cyan }; draw.writeStr(surface, base_start + 4, 2, "BIN:", bin_style); if (focused == .bin) { drawFieldWithCursor(surface, base_start + 4, 11, bin.display, app.bit_cursor, 1, bw.bits(), C.yellow); } else { draw.writeStr(surface, base_start + 4, 11, bin.display, .{ .fg = C.yellow }); } // History const hist_start = base_start + 6; const hist_end = height -| 4; if (hist_start < hist_end) { tui.drawHistory(app.history.items, surface, hist_start, hist_end); } // Separator + input + status draw.fillRow(surface, height -| 3, '-', .{ .fg = C.dim }); app.drawInput(surface, height -| 2); draw.fillRow(surface, height -| 1, ' ', .{ .fg = C.muted, .bg = C.bg }); const status = if (focused == .bits) "Arrows:nav | Space:toggle | Up/Down:field | Ctrl-W:width | Tab:mode | Ctrl-C:quit" else "Up/Down:field | Enter:set value | Ctrl-W:width | Tab:mode | ?:help | Ctrl-C:quit"; draw.writeStr(surface, height -| 1, 1, status, .{ .fg = C.muted, .bg = C.bg }); } /// Draw a field's display string with a cursor highlighting the digit at bit_cursor position. /// `bits_per_digit` is 4 for hex, 3 for oct, 1 for bin. fn drawFieldWithCursor(surface: *vxfw.Surface, row: u16, col: u16, text: []const u8, bit_cursor: u7, bits_per_digit: u8, total_bits: u8, color: vaxis.Cell.Color) void { _ = total_bits; // Count actual displayed digits (non-space, non-prefix characters) var start: usize = 0; if (text.len >= 2 and text[0] == '0' and (text[1] == 'o' or text[1] == 'x' or text[1] == 'b')) { start = 2; } var displayed_digits: u16 = 0; for (text[start..]) |ch| { if (ch != ' ') displayed_digits += 1; } // The cursor is at bit_cursor, which represents a digit position from LSB const cursor_digit_from_lsb: u16 = @as(u16, bit_cursor) / bits_per_digit; // Convert to position from MSB (0 = leftmost displayed digit) const cursor_digit_from_msb: u16 = if (cursor_digit_from_lsb < displayed_digits) displayed_digits - 1 - cursor_digit_from_lsb else 0; // Draw prefix var text_col: u16 = col; for (text[0..start]) |ch| { draw.writeChar(surface, row, text_col, ch, .{ .fg = color }); text_col += 1; } // Draw digits with cursor highlight var digit_idx: u16 = 0; for (text[start..]) |ch| { if (ch == ' ') { draw.writeChar(surface, row, text_col, ' ', .{ .fg = color }); } else { const style: vaxis.Style = if (digit_idx == cursor_digit_from_msb) .{ .fg = C.bg, .bg = color, .bold = true } else .{ .fg = color }; draw.writeChar(surface, row, text_col, ch, style); digit_idx += 1; } text_col += 1; } } fn drawBitGrid(app: *tui.App, surface: *vxfw.Surface, start_row: u16, val: u128, bw: engine.types.BitWidth) void { const width_bits = bw.bits(); const bits_per_row: u8 = if (width_bits > 32) 32 else width_bits; const num_rows: u16 = (@as(u16, width_bits) + bits_per_row - 1) / bits_per_row; const grid_focused = app.prog_field == .bits; var row = start_row; var bit_row: u16 = 0; while (bit_row < num_rows) : (bit_row += 1) { const row_start_bit: u8 = @intCast(width_bits - 1 - bit_row * bits_per_row); var col: u16 = 2; var b: u8 = 0; while (b < bits_per_row) : (b += 1) { const bit_idx = row_start_bit -| b; if (bit_idx >= width_bits) break; if (b > 0 and b % 4 == 0) col += 1; const bit_val: u8 = @intCast((val >> @intCast(bit_idx)) & 1); const is_cursor = grid_focused and (bit_idx == app.bit_cursor); const style: vaxis.Style = if (is_cursor) .{ .fg = C.bg, .bg = C.cyan, .bold = true, } else if (bit_val == 1) .{ .fg = C.green, .bold = true, } else if (grid_focused) .{ .fg = C.muted, } else .{ .fg = C.dim, }; draw.writeChar(surface, row, col, '0' + bit_val, style); col += 1; } row += 1; } }