From e40776afc9981a0af5e7a3e6598df1744bd58966 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Tue, 6 Oct 2026 20:31:59 +0700 Subject: [PATCH] fix(tri27): decoder takes the T736 field table from the t27 spec (Refs gHashTag/t27#6507) Owner, 2026-10-06, on gHashTag/t27#6507: "add the label yourself, do the work, do not ask about labels" -- label owner-approved-foreign. decoder.zig read src2 for ADD..XOR (and BUNDLE3) only, so DOT, BIND and BUNDLE2 decoded with src2 = 0, against T736. It now imports src/tri27/emu/tri27_encoding.zig, which is `t27c gen` of gHashTag/t27 specs/isa/ternary_encoding.t27, byte for byte, instead of a second hand-copied table: - spec sha256 5e2c265a0972189ccf887d523abb86c13f9fa7ae6a1f35a59c20f1a1fc4796cd - generated sha256 da08bb8d44888dd747b11de4476d75395921f3fbc11754acb9532a218ffa3fad (equals gen_hash_zig in t27 .trinity/seals/isa_Tri27Encoding.json) decode uses t27a_src1 / t27a_src2 / t27a_imm / t27a_imm_form and reads BUNDLE3's v3 field into `cond`; encode is t27a_encode. New tests: DOT/BIND/BUNDLE2 keep src2, BUNDLE3 keeps its third register, and the six words t27 specs/isa/t27a.t27 pins decode to the same fields. zig 0.15.2: decoder 24/24, tri27_encoding 14/14, test_encoder_fuzz 31/31, test_comprehensive 66/66, executor 33/33, smoke_tests 37/37, encoder_simple 35/35, emit_verilog 2/2, emit_zig 2/2. Co-Authored-By: Claude Opus 5.5 --- src/tri27/emu/decoder.zig | 156 ++++---- src/tri27/emu/tri27_encoding.zig | 642 +++++++++++++++++++++++++++++++ 2 files changed, 720 insertions(+), 78 deletions(-) create mode 100644 src/tri27/emu/tri27_encoding.zig diff --git a/src/tri27/emu/decoder.zig b/src/tri27/emu/decoder.zig index 20859eac19..b7d39c90c9 100644 --- a/src/tri27/emu/decoder.zig +++ b/src/tri27/emu/decoder.zig @@ -8,6 +8,19 @@ const std = @import("std"); +// The field table (which opcodes carry an immediate, which a src2, where +// BUNDLE3 keeps its third register) is not written here. It is the T736 table +// of gHashTag/t27 specs/isa/ternary_encoding.t27 (t27a_imm_form, +// t27a_src2_form, t27a_src1, t27a_src2, t27a_imm, t27a_encode), and +// tri27_encoding.zig is that spec's `t27c gen` output, byte for byte, never +// hand-edited. Recorded at the vendoring (gHashTag/t27#6507, gHashTag/t27#6789): +// spec sha256 5e2c265a0972189ccf887d523abb86c13f9fa7ae6a1f35a59c20f1a1fc4796cd +// generated sha256 da08bb8d44888dd747b11de4476d75395921f3fbc11754acb9532a218ffa3fad +// (= gen_hash_zig of t27 .trinity/seals/isa_Tri27Encoding.json) +// To move it, regenerate from t27 (`t27c gen specs/isa/ternary_encoding.t27`) +// and replace the file; do not edit a field position in this one. +const fields = @import("tri27_encoding.zig"); + /// ═══════════════════════════════════════════════════════════════════════════════ // TRI-27 OPCODE ENUM // ═══════════════════════════════════════════════════════════════════════════════════ @@ -114,62 +127,32 @@ pub const Instruction = struct { // DECODER FUNCTIONS // ═══════════════════════════════════════════════════════════════════════════════════ /// Decode 32-bit instruction word into Instruction struct -/// Word format (hybrid): +/// Word format (the T736 table, see `fields` above): /// [7:0] = opcode (8 bits) /// [12:8] = dst (5 bits) -/// For immediate instructions: +/// Immediate form (t27a_imm_form, SACR included): /// [16:13] = src1 (4 bits) -/// [31:17] = immediate (15 bits) -/// For 3-operand instructions: +/// [31:17] = immediate (15 bits, two's complement) +/// Three-operand form (t27a_src2_form: ADD..XOR, DOT, BIND, BUNDLE2, BUNDLE3): /// [17:13] = src1 (5 bits) /// [22:18] = src2 (5 bits) +/// [27:23] = third register of BUNDLE3, carried in `cond` +/// Every other opcode: [17:13] = src1 (5 bits) +/// An undefined opcode byte still decodes as NOP, as before. pub fn decode(word: u32) Instruction { const opcode_val = @as(u8, @truncate(word & 0xFF)); const opcode = std.meta.intToEnum(Opcode, opcode_val) catch Opcode.NOP; - - const dst = @as(u8, @truncate((word >> 8) & 0x1F)); - - // Determine if instruction has immediate or src2 - const has_imm = switch (opcode) { - .LD, .ST, .LDI, .STI, .LD_IMM, .PHI_CONST, .PI_CONST, .E_CONST, .JMP, .JZ, .JNZ, .JGT, .JLT, .CALL, .RET, .SHL, .SHR, .SACR, .STR_LOAD, .STR_CONCAT, .STR_PRINT, .FILE_READ, .FILE_WRITE, .FILE_EXISTS => true, - else => false, - }; - - const has_src2 = switch (opcode) { - .ADD, .SUB, .MUL, .DIV, .AND, .OR, .XOR => true, - else => false, - }; - - // Decode src1 based on instruction type - const src1: u8 = if (has_imm) - @as(u8, @truncate((word >> 13) & 0x0F)) // 4 bits for immediate instructions - else - @as(u8, @truncate((word >> 13) & 0x1F)); // 5 bits for other instructions - - const src2: u8 = if (has_src2) @as(u8, @truncate((word >> 18) & 0x1F)) else 0; - - const immediate: i16 = blk: { - if (!has_imm) break :blk 0; - // Decode 15-bit immediate (bits 31-17) - const imm_raw = @as(u16, @truncate((word >> 17) & 0x7FFF)); - if (imm_raw & 0x4000 != 0) - break :blk @as(i16, @bitCast(imm_raw | 0x8000)) - else - break :blk @as(i16, @intCast(imm_raw)); - }; - - // For BUNDLE3: extract src2 (lower 5 bits) and v3_reg (upper 9 bits) from bits 18-31 - const src2_or_v3 = @as(u16, @truncate((word >> 18) & 0x3FFF)); - const v3_reg = @as(u8, @truncate((src2_or_v3 >> 5) & 0x1F)); + const op: u32 = @intFromEnum(opcode); return Instruction{ .opcode = opcode, - .dst = dst, - .src1 = src1, - .src2 = if (opcode == .BUNDLE3) @as(u8, @truncate(src2_or_v3 & 0x1F)) else src2, - .immediate = immediate, - .has_imm = has_imm, - .cond = if (opcode == .BUNDLE3) v3_reg else 0, + .dst = @truncate((word >> @intCast(fields.DST_SHIFT)) & fields.REG_MASK), + .src1 = @truncate(fields.t27a_src1(word, op)), + .src2 = @truncate(fields.t27a_src2(word, op)), + // 15-bit two's complement, so always within i16. + .immediate = @intCast(fields.t27a_imm(word, op)), + .has_imm = fields.t27a_imm_form(op), + .cond = if (opcode == .BUNDLE3) @truncate((word >> @intCast(fields.V3_SHIFT)) & fields.REG_MASK) else 0, }; } @@ -178,40 +161,18 @@ pub fn decodeInstruction(word: u32) Instruction { return decode(word); } -/// Encode Instruction to 32-bit word +/// Encode Instruction to 32-bit word: `t27a_encode` of the T736 table. The +/// immediate is clamped to [-16384, 16383]; the third register of BUNDLE3 is +/// `cond`, as `decode` returns it. pub fn encode(inst: Instruction) u32 { - var word: u32 = @intFromEnum(inst.opcode); - word |= @as(u32, inst.dst) << 8; - - // Determine if this instruction uses immediate or src2 - const has_src2 = switch (inst.opcode) { - .ADD, .SUB, .MUL, .DIV, .AND, .OR, .XOR => true, - else => false, - }; - - const has_imm = switch (inst.opcode) { - .LD, .ST, .LDI, .STI, .LD_IMM, .PHI_CONST, .PI_CONST, .E_CONST, .JMP, .JZ, .JNZ, .JGT, .JLT, .CALL, .RET, .SHL, .SHR, .SACR, .STR_LOAD, .STR_CONCAT, .STR_PRINT, .FILE_READ, .FILE_WRITE, .FILE_EXISTS => true, - else => false, - }; - - // For instructions with immediate, src1 is encoded in lower bits of immediate field (bits 13-16) - // For 3-operand instructions, src1 is at bits 13-17 and src2 at bits 18-22 - if (has_imm) { - // Encode src1 in bits 13-16 (4 bits), immediate in bits 17-31 (15 bits) - word |= @as(u32, inst.src1 & 0x0F) << 13; - const imm_clamped = std.math.clamp(inst.immediate, -16384, 16383); - const imm_u15: u16 = @bitCast(@as(i16, imm_clamped)); - word |= @as(u32, imm_u15 & 0x7FFF) << 17; - } else if (has_src2) { - // 3-operand instruction: src1 at bits 13-17, src2 at bits 18-22 - word |= @as(u32, inst.src1) << 13; - word |= @as(u32, inst.src2) << 18; - } else { - // 2-operand instruction (like MOV, NOT): src1 at bits 13-17 - word |= @as(u32, inst.src1) << 13; - } - - return word; + return fields.t27a_encode( + @intFromEnum(inst.opcode), + inst.dst, + inst.src1, + inst.src2, + inst.cond, + inst.immediate, + ); } /// Get opcode name for debugging @@ -379,3 +340,42 @@ test "decoder: getOpcodeName" { try std.testing.expectEqualStrings("ADD", getOpcodeName(Opcode.ADD)); try std.testing.expectEqualStrings("HALT", getOpcodeName(Opcode.HALT)); } + +test "decoder: DOT, BIND and BUNDLE2 keep src2 (T736)" { + // Before the T736 table these three read src2 as 0 and wrote none, while + // the executor reads inst.src2 for all of them. + for ([_]Opcode{ .DOT, .BIND, .BUNDLE2 }) |op| { + const inst = Instruction{ .opcode = op, .dst = 3, .src1 = 17, .src2 = 29 }; + const decoded = decode(encode(inst)); + try std.testing.expectEqual(op, decoded.opcode); + try std.testing.expectEqual(@as(u8, 3), decoded.dst); + try std.testing.expectEqual(@as(u8, 17), decoded.src1); + try std.testing.expectEqual(@as(u8, 29), decoded.src2); + try std.testing.expect(!decoded.has_imm); + } +} + +test "decoder: BUNDLE3 keeps its third register (T736)" { + const inst = Instruction{ .opcode = .BUNDLE3, .dst = 3, .src1 = 1, .src2 = 2, .cond = 31 }; + const decoded = decode(encode(inst)); + try std.testing.expectEqual(@as(u8, 1), decoded.src1); + try std.testing.expectEqual(@as(u8, 2), decoded.src2); + try std.testing.expectEqual(@as(u8, 31), decoded.cond); +} + +test "decoder: the words gHashTag/t27 specs/isa/t27a.t27 pins" { + // Same words `t27c asm` writes for the same text (gHashTag/t27#6507). + const Pin = struct { word: u32, inst: Instruction }; + const pins = [_]Pin{ + .{ .word = 533264, .inst = .{ .opcode = .ADD, .dst = 3, .src1 = 1, .src2 = 2 } }, + .{ .word = 4294312708, .inst = .{ .opcode = .LDI, .dst = 3, .src1 = 0, .immediate = -5, .has_imm = true } }, + .{ .word = 8975, .inst = .{ .opcode = .MOV, .dst = 3, .src1 = 1 } }, + .{ .word = 926276, .inst = .{ .opcode = .JGT, .dst = 2, .src1 = 1, .immediate = 7, .has_imm = true } }, + .{ .word = 34087779, .inst = .{ .opcode = .BUNDLE3, .dst = 3, .src1 = 1, .src2 = 2, .cond = 4 } }, + .{ .word = 0, .inst = .{ .opcode = .NOP } }, + }; + for (pins) |p| { + try std.testing.expectEqual(p.word, encode(p.inst)); + try std.testing.expectEqual(p.inst, decode(p.word)); + } +} diff --git a/src/tri27/emu/tri27_encoding.zig b/src/tri27/emu/tri27_encoding.zig new file mode 100644 index 0000000000..4de372bfed --- /dev/null +++ b/src/tri27/emu/tri27_encoding.zig @@ -0,0 +1,642 @@ +// Generated from t27 spec: Tri27Encoding (module name) +// DO NOT EDIT — generated by t27c +// phi^2 + 1/phi^2 = 3 | TRINITY + +const std = @import("std"); +fn __t27_assert_fail(comptime fmt: []const u8, args: anytype) noreturn { + if (@inComptime()) { + @compileError("assertion failed"); + } else { + std.debug.print(fmt, args); + @panic("assertion failed"); + } +} +fn assert_eq(a: anytype, b: anytype) void { + if (a != b) __t27_assert_fail("\n {any}\n != {any}\n", .{ a, b }); +} + +pub const KIND: []const u8 = "isa-encoding"; +pub const ID: []const u8 = "isa/ternary_encoding"; +pub const NAME: []const u8 = "The TRI-27 instruction word of the pinned Trinity emulator"; +pub const CONSUMER_REPO: []const u8 = "gHashTag/trinity"; +pub const PINNED_REVISION: []const u8 = "976df517419f918f05b78e73767e2fdf4aad14be"; +pub const OWNER_MODULE: []const u8 = "src/tri27/emu/decoder.zig"; +pub const OWNER_LINES: []const u8 = "Opcode enum :14-79; decode :126-174; encode :182-210"; +pub const TRIT_VALUES: [3]i32 = .{ -1, 0, 1 }; +pub const WORD_BITS: u32 = 32; +pub const INSTRUCTION_BYTES: u32 = 4; +pub const BYTE_ORDER: []const u8 = "little-endian: byte 0 of the instruction holds bits 7:0, the opcode"; +pub const OPCODE_SHIFT: u32 = 0; +pub const OPCODE_MASK: u32 = 255; +pub const DST_SHIFT: u32 = 8; +pub const REG_MASK: u32 = 31; +pub const SRC1_SHIFT: u32 = 13; +pub const SRC1_MASK_IMM_FORM: u32 = 15; +pub const SRC2_SHIFT: u32 = 18; +pub const V3_SHIFT: u32 = 23; +pub const IMM_SHIFT: u32 = 17; +pub const IMM_MASK: u32 = 32767; +pub const IMM_SIGN_BIT: u32 = 16384; +pub const IMM_BITS: u32 = 15; +pub const IMM_MIN: i32 = -16384; +pub const IMM_MAX: i32 = 16383; +pub const LAYOUT_IMM_FORM: []const u8 = "opcode 7:0, dst 12:8, src1 16:13 (four bits), immediate 31:17 (fifteen bits, two's complement)"; +pub const LAYOUT_THREE_OPERAND: []const u8 = "opcode 7:0, dst 12:8, src1 17:13, src2 22:18"; +pub const LAYOUT_TWO_OPERAND: []const u8 = "opcode 7:0, dst 12:8, src1 17:13; bits 31:18 unused"; +pub const LAYOUT_BUNDLE3: []const u8 = "opcode 7:0, dst 12:8, src1 17:13, src2 22:18, third register 27:23; the decoder reads this form, the owner's encoder never writes it"; +pub const OPCODE_COUNT: u32 = 47; +pub const OPCODE_NAMES: [47][]const u8 = .{ "NOP", "LD", "ST", "LDI", "STI", "MOV", "ADD", "SUB", "MUL", "DIV", "INC", "DEC", "EXP", "SIN", "AND", "OR", "XOR", "NOT", "SHL", "SHR", "STR_LOAD", "STR_CONCAT", "STR_PRINT", "FILE_READ", "FILE_WRITE", "FILE_EXISTS", "JMP", "JZ", "JNZ", "CALL", "JGT", "JLT", "RET", "HALT", "DOT", "BIND", "BUNDLE2", "BUNDLE3", "PHI_CONST", "PI_CONST", "E_CONST", "SACR", "LD_IMM", "ADD3", "SUB3", "CMP3", "SYSCALL" }; +pub const OPCODE_VALUES: [47]u32 = .{ 0, 2, 3, 4, 5, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 64, 65, 66, 67, 68, 69, 75, 77, 96, 97, 98, 99, 128, 129, 130, 131, 132, 133, 134, 135, 136 }; +pub const OP_NOP: u32 = 0; +pub const OP_LD: u32 = 2; +pub const OP_ST: u32 = 3; +pub const OP_LDI: u32 = 4; +pub const OP_STI: u32 = 5; +pub const OP_MOV: u32 = 15; +pub const OP_ADD: u32 = 16; +pub const OP_SUB: u32 = 17; +pub const OP_MUL: u32 = 18; +pub const OP_DIV: u32 = 19; +pub const OP_INC: u32 = 20; +pub const OP_DEC: u32 = 21; +pub const OP_EXP: u32 = 22; +pub const OP_SIN: u32 = 23; +pub const OP_AND: u32 = 24; +pub const OP_OR: u32 = 25; +pub const OP_XOR: u32 = 26; +pub const OP_NOT: u32 = 27; +pub const OP_SHL: u32 = 28; +pub const OP_SHR: u32 = 29; +pub const OP_STR_LOAD: u32 = 32; +pub const OP_STR_CONCAT: u32 = 33; +pub const OP_STR_PRINT: u32 = 34; +pub const OP_FILE_READ: u32 = 35; +pub const OP_FILE_WRITE: u32 = 36; +pub const OP_FILE_EXISTS: u32 = 37; +pub const OP_JMP: u32 = 64; +pub const OP_JZ: u32 = 65; +pub const OP_JNZ: u32 = 66; +pub const OP_CALL: u32 = 67; +pub const OP_JGT: u32 = 68; +pub const OP_JLT: u32 = 69; +pub const OP_RET: u32 = 75; +pub const OP_HALT: u32 = 77; +pub const OP_DOT: u32 = 96; +pub const OP_BIND: u32 = 97; +pub const OP_BUNDLE2: u32 = 98; +pub const OP_BUNDLE3: u32 = 99; +pub const OP_PHI_CONST: u32 = 128; +pub const OP_PI_CONST: u32 = 129; +pub const OP_E_CONST: u32 = 130; +pub const OP_SACR: u32 = 131; +pub const OP_LD_IMM: u32 = 132; +pub const OP_ADD3: u32 = 133; +pub const OP_SUB3: u32 = 134; +pub const OP_CMP3: u32 = 135; +pub const OP_SYSCALL: u32 = 136; +pub const IMM_FORM_COUNT: u32 = 23; +pub const IMM_FORM: [23]u32 = .{ 2, 3, 4, 5, 132, 128, 129, 130, 64, 65, 66, 68, 69, 67, 75, 28, 29, 32, 33, 34, 35, 36, 37 }; +pub const SRC2_FORM_COUNT: u32 = 7; +pub const SRC2_FORM: [7]u32 = .{ 16, 17, 18, 19, 24, 25, 26 }; +pub const UNKNOWN_OPCODE_RULE: []const u8 = "an opcode byte that is not one of the forty-seven decodes as NOP; decoding never fails"; +pub const REGISTER_FIELD_RULE: []const u8 = "register fields are five bits wide (0..31); the executor refuses 27..31 with InvalidRegister; in the immediate form src1 is four bits (0..15)"; +pub const SRC2_LOSS_RULE: []const u8 = "DOT, BIND, BUNDLE2 execute with src1 and src2 but are not in SRC2_FORM: encode writes only src1 and decode reads src2 as 0 (register t0)"; +pub const SACR_MODE_RULE: []const u8 = "SACR selects its mode by the immediate, but SACR is not in IMM_FORM: decode reads the immediate as 0 and every decoded SACR adds modulo 19683; modes 2, 3 and 4 are reachable only by constructing an Instruction directly, as the owner's tests do"; +pub const T27A_IMM_FORM_COUNT: u32 = 24; +pub const T27A_SRC2_FORM_COUNT: u32 = 11; +pub const T27A_TWO_OPERAND_COUNT: u32 = 12; +pub const T27A_FIELD_RULE: []const u8 = "t27a: the immediate form is IMM_FORM plus SACR (src1 four bits, its mode in the immediate); the three-operand form is SRC2_FORM plus DOT, BIND, BUNDLE2, BUNDLE3; BUNDLE3 also writes its third register at bits 27:23; every other opcode writes dst and a five-bit src1 only"; +pub const T27A_CONSUMER_GAP: []const u8 = "decoder.zig at PINNED_REVISION reads src2 of DOT, BIND, BUNDLE2 as 0 and the immediate of SACR as 0; the t27a table is not yet the emulator's"; +pub const FINDING_COUNT: u32 = 6; +pub const FINDINGS: [6][]const u8 = .{ "src/tri27/emu/encoder_simple.zig puts a sixteen-bit immediate at bit 16 (loads, stores) or bit 8 (jumps, calls); src/tri27/emu/asm_parser.zig, the assembler behind `tri tri27 assemble`, uses it, so its words are misread by decoder.zig", "src/tri27/emu/specs/tbin_format.md puts the opcode in bits 31:24 and a four-bit immediate in bits 3:0", "docs/tri27/t27_format.md puts a six-bit opcode in bits 31:26 and an eight-bit immediate in bits 7:0, with opcode numbers (ADD 0x60 ... DEC 0x65) that collide with DOT 0x60", "docs/tri27/README.md puts a six-bit opcode in bits 8:5", "src/tri27/emit_zig.zig emits a VM whose decoder reads the opcode from bits 31:24 and packs bytes big-endian; it also switches on LTI, PUSH, POP and RESET, which no enum declares", "the encoder_simple.zig enum lists 36 opcodes and misses MOV, EXP, SIN, JGT, JLT and every STR_ and FILE_ opcode; every document says 36, the decoder declares 47" }; +pub const ENABLED: bool = true; +pub fn is_defined_opcode(op: u32) bool { + if (op == OP_NOP) { + return true; + } + if (op == OP_LD) { + return true; + } + if (op == OP_ST) { + return true; + } + if (op == OP_LDI) { + return true; + } + if (op == OP_STI) { + return true; + } + if (op == OP_MOV) { + return true; + } + if (op == OP_ADD) { + return true; + } + if (op == OP_SUB) { + return true; + } + if (op == OP_MUL) { + return true; + } + if (op == OP_DIV) { + return true; + } + if (op == OP_INC) { + return true; + } + if (op == OP_DEC) { + return true; + } + if (op == OP_EXP) { + return true; + } + if (op == OP_SIN) { + return true; + } + if (op == OP_AND) { + return true; + } + if (op == OP_OR) { + return true; + } + if (op == OP_XOR) { + return true; + } + if (op == OP_NOT) { + return true; + } + if (op == OP_SHL) { + return true; + } + if (op == OP_SHR) { + return true; + } + if (op == OP_STR_LOAD) { + return true; + } + if (op == OP_STR_CONCAT) { + return true; + } + if (op == OP_STR_PRINT) { + return true; + } + if (op == OP_FILE_READ) { + return true; + } + if (op == OP_FILE_WRITE) { + return true; + } + if (op == OP_FILE_EXISTS) { + return true; + } + if (op == OP_JMP) { + return true; + } + if (op == OP_JZ) { + return true; + } + if (op == OP_JNZ) { + return true; + } + if (op == OP_CALL) { + return true; + } + if (op == OP_JGT) { + return true; + } + if (op == OP_JLT) { + return true; + } + if (op == OP_RET) { + return true; + } + if (op == OP_HALT) { + return true; + } + if (op == OP_DOT) { + return true; + } + if (op == OP_BIND) { + return true; + } + if (op == OP_BUNDLE2) { + return true; + } + if (op == OP_BUNDLE3) { + return true; + } + if (op == OP_PHI_CONST) { + return true; + } + if (op == OP_PI_CONST) { + return true; + } + if (op == OP_E_CONST) { + return true; + } + if (op == OP_SACR) { + return true; + } + if (op == OP_LD_IMM) { + return true; + } + if (op == OP_ADD3) { + return true; + } + if (op == OP_SUB3) { + return true; + } + if (op == OP_CMP3) { + return true; + } + if (op == OP_SYSCALL) { + return true; + } + return false; +} +pub fn decode_opcode(byte: u32) u32 { + if (is_defined_opcode(byte)) { + return byte; + } + return OP_NOP; +} +pub fn has_imm(op: u32) bool { + if (op == OP_LD) { + return true; + } + if (op == OP_ST) { + return true; + } + if (op == OP_LDI) { + return true; + } + if (op == OP_STI) { + return true; + } + if (op == OP_LD_IMM) { + return true; + } + if (op == OP_PHI_CONST) { + return true; + } + if (op == OP_PI_CONST) { + return true; + } + if (op == OP_E_CONST) { + return true; + } + if (op == OP_JMP) { + return true; + } + if (op == OP_JZ) { + return true; + } + if (op == OP_JNZ) { + return true; + } + if (op == OP_JGT) { + return true; + } + if (op == OP_JLT) { + return true; + } + if (op == OP_CALL) { + return true; + } + if (op == OP_RET) { + return true; + } + if (op == OP_SHL) { + return true; + } + if (op == OP_SHR) { + return true; + } + if (op == OP_STR_LOAD) { + return true; + } + if (op == OP_STR_CONCAT) { + return true; + } + if (op == OP_STR_PRINT) { + return true; + } + if (op == OP_FILE_READ) { + return true; + } + if (op == OP_FILE_WRITE) { + return true; + } + if (op == OP_FILE_EXISTS) { + return true; + } + return false; +} +pub fn has_src2(op: u32) bool { + if (op == OP_ADD) { + return true; + } + if (op == OP_SUB) { + return true; + } + if (op == OP_MUL) { + return true; + } + if (op == OP_DIV) { + return true; + } + if (op == OP_AND) { + return true; + } + if (op == OP_OR) { + return true; + } + if (op == OP_XOR) { + return true; + } + return false; +} +pub fn opcode_of(word: u32) u32 { + return word & OPCODE_MASK; +} +pub fn dst_of(word: u32) u32 { + return (word >> @intCast(DST_SHIFT)) & REG_MASK; +} +pub fn src1_of(word: u32, op: u32) u32 { + if (has_imm(op)) { + return (word >> @intCast(SRC1_SHIFT)) & SRC1_MASK_IMM_FORM; + } + return (word >> @intCast(SRC1_SHIFT)) & REG_MASK; +} +pub fn src2_of(word: u32, op: u32) u32 { + if (has_src2(op)) { + return (word >> @intCast(SRC2_SHIFT)) & REG_MASK; + } + if (op == OP_BUNDLE3) { + return (word >> @intCast(SRC2_SHIFT)) & REG_MASK; + } + return 0; +} +pub fn v3_of(word: u32, op: u32) u32 { + if (op == OP_BUNDLE3) { + return (word >> @intCast(V3_SHIFT)) & REG_MASK; + } + return 0; +} +pub fn imm_of(word: u32, op: u32) i32 { + if (has_imm(op) == false) { + return 0; + } + var raw: u32 = (word >> @intCast(IMM_SHIFT)) & IMM_MASK; + _ = &raw; + if ((raw & IMM_SIGN_BIT) != 0) { + return @as(i32, @intCast(raw)) - 32768; + } + return @as(i32, @intCast(raw)); +} +pub fn imm_clamped(imm: i32) i32 { + if (imm < IMM_MIN) { + return IMM_MIN; + } + if (imm > IMM_MAX) { + return IMM_MAX; + } + return imm; +} +pub fn imm_field(imm: i32) u32 { + var c: i32 = imm_clamped(imm); + _ = &c; + if (c < 0) { + return @as(u32, @intCast(c + 65536)) & IMM_MASK; + } + return @as(u32, @intCast(c)) & IMM_MASK; +} +pub fn encode(op: u32, dst: u32, src1: u32, src2: u32, imm: i32) u32 { + var word: u32 = (op & OPCODE_MASK) | ((dst & REG_MASK) << @intCast(DST_SHIFT)); + _ = &word; + if (has_imm(op)) { + return (word | ((src1 & SRC1_MASK_IMM_FORM) << @intCast(SRC1_SHIFT))) | (imm_field(imm) << @intCast(IMM_SHIFT)); + } + if (has_src2(op)) { + return (word | ((src1 & REG_MASK) << @intCast(SRC1_SHIFT))) | ((src2 & REG_MASK) << @intCast(SRC2_SHIFT)); + } + return word | ((src1 & REG_MASK) << @intCast(SRC1_SHIFT)); +} +pub fn bundle3_word(dst: u32, src1: u32, src2: u32, v3: u32) u32 { + return (((OP_BUNDLE3 | ((dst & REG_MASK) << @intCast(DST_SHIFT))) | ((src1 & REG_MASK) << @intCast(SRC1_SHIFT))) | ((src2 & REG_MASK) << @intCast(SRC2_SHIFT))) | ((v3 & REG_MASK) << @intCast(V3_SHIFT)); +} +pub fn t27a_imm_form(op: u32) bool { + if (op == OP_SACR) { + return true; + } + return has_imm(op); +} +pub fn t27a_src2_form(op: u32) bool { + if (has_src2(op)) { + return true; + } + if (op == OP_DOT) { + return true; + } + if (op == OP_BIND) { + return true; + } + if (op == OP_BUNDLE2) { + return true; + } + if (op == OP_BUNDLE3) { + return true; + } + return false; +} +pub fn t27a_encode(op: u32, dst: u32, s1: u32, s2: u32, v3: u32, imm: i32) u32 { + var w: u32 = (op & OPCODE_MASK) | ((dst & REG_MASK) << @intCast(DST_SHIFT)); + _ = &w; + if (t27a_imm_form(op)) { + return (w | ((s1 & SRC1_MASK_IMM_FORM) << @intCast(SRC1_SHIFT))) | (imm_field(imm) << @intCast(IMM_SHIFT)); + } + w = w | ((s1 & REG_MASK) << @intCast(SRC1_SHIFT)); + if (t27a_src2_form(op)) { + w = w | ((s2 & REG_MASK) << @intCast(SRC2_SHIFT)); + } + if (op == OP_BUNDLE3) { + w = w | ((v3 & REG_MASK) << @intCast(V3_SHIFT)); + } + return w; +} +pub fn t27a_src1(w: u32, op: u32) u32 { + if (t27a_imm_form(op)) { + return (w >> @intCast(SRC1_SHIFT)) & SRC1_MASK_IMM_FORM; + } + return (w >> @intCast(SRC1_SHIFT)) & REG_MASK; +} +pub fn t27a_src2(w: u32, op: u32) u32 { + if (t27a_src2_form(op)) { + return (w >> @intCast(SRC2_SHIFT)) & REG_MASK; + } + return 0; +} +pub fn t27a_imm(w: u32, op: u32) i32 { + if (t27a_imm_form(op) == false) { + return 0; + } + var raw: u32 = (w >> @intCast(IMM_SHIFT)) & IMM_MASK; + _ = &raw; + if ((raw & IMM_SIGN_BIT) != 0) { + return @as(i32, @intCast(raw)) - 32768; + } + return @as(i32, @intCast(raw)); +} +comptime { + // invariant: the_immediate_range_is_fifteen_bits + if (!((IMM_MAX - IMM_MIN) == 32767)) __t27_assert_fail("\n assertion failed:\n IMM_MAX - IMM_MIN = {any}\n", .{ IMM_MAX - IMM_MIN }); +} +comptime { + // invariant: the_forms_partition_the_opcodes + if (!((IMM_FORM_COUNT + SRC2_FORM_COUNT) == 30)) __t27_assert_fail("\n assertion failed:\n IMM_FORM_COUNT + SRC2_FORM_COUNT = {any}\n", .{ IMM_FORM_COUNT + SRC2_FORM_COUNT }); + if (!(((T27A_IMM_FORM_COUNT + T27A_SRC2_FORM_COUNT) + T27A_TWO_OPERAND_COUNT) == OPCODE_COUNT)) __t27_assert_fail("\n assertion failed:\n (T27A_IMM_FORM_COUNT + T27A_SRC2_FORM_COUNT) + T27A_TWO_OPERAND_COUNT = {any}\n OPCODE_COUNT = {any}\n", .{ (T27A_IMM_FORM_COUNT + T27A_SRC2_FORM_COUNT) + T27A_TWO_OPERAND_COUNT, OPCODE_COUNT }); +} +test "every_opcode_byte_of_the_table_is_defined" { + if (!(is_defined_opcode(OP_NOP) == true)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(OP_NOP) = {any}\n", .{ is_defined_opcode(OP_NOP) }); + if (!(is_defined_opcode(OP_SYSCALL) == true)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(OP_SYSCALL) = {any}\n", .{ is_defined_opcode(OP_SYSCALL) }); + if (!(is_defined_opcode(OP_RET) == true)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(OP_RET) = {any}\n", .{ is_defined_opcode(OP_RET) }); + if (!(is_defined_opcode(1) == false)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(1) = {any}\n", .{ is_defined_opcode(1) }); + if (!(is_defined_opcode(6) == false)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(6) = {any}\n", .{ is_defined_opcode(6) }); + if (!(is_defined_opcode(70) == false)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(70) = {any}\n", .{ is_defined_opcode(70) }); + if (!(is_defined_opcode(137) == false)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(137) = {any}\n", .{ is_defined_opcode(137) }); + if (!(is_defined_opcode(255) == false)) __t27_assert_fail("\n assertion failed:\n is_defined_opcode(255) = {any}\n", .{ is_defined_opcode(255) }); +} +test "an_unknown_opcode_decodes_as_nop" { + if (!(decode_opcode(1) == OP_NOP)) __t27_assert_fail("\n assertion failed:\n decode_opcode(1) = {any}\n OP_NOP = {any}\n", .{ decode_opcode(1), OP_NOP }); + if (!(decode_opcode(50) == OP_NOP)) __t27_assert_fail("\n assertion failed:\n decode_opcode(50) = {any}\n OP_NOP = {any}\n", .{ decode_opcode(50), OP_NOP }); + if (!(decode_opcode(OP_HALT) == OP_HALT)) __t27_assert_fail("\n assertion failed:\n decode_opcode(OP_HALT) = {any}\n OP_HALT = {any}\n", .{ decode_opcode(OP_HALT), OP_HALT }); +} +test "the_immediate_form_round_trips_and_sign_extends" { + var w: u32 = encode(OP_LDI, 3, 0, 0, -5); + _ = &w; + if (!(w == 4294312708)) __t27_assert_fail("\n assertion failed:\n w = {any}\n", .{ w }); + if (!(opcode_of(w) == OP_LDI)) __t27_assert_fail("\n assertion failed:\n opcode_of(w) = {any}\n OP_LDI = {any}\n", .{ opcode_of(w), OP_LDI }); + if (!(dst_of(w) == 3)) __t27_assert_fail("\n assertion failed:\n dst_of(w) = {any}\n", .{ dst_of(w) }); + if (!(imm_of(w, OP_LDI) == -5)) __t27_assert_fail("\n assertion failed:\n imm_of(w, OP_LDI) = {any}\n -5 = {any}\n", .{ imm_of(w, OP_LDI), -5 }); + if (!(imm_of(encode(OP_LDI, 3, 0, 0, 5), OP_LDI) == 5)) __t27_assert_fail("\n assertion failed:\n imm_of(encode(OP_LDI, 3, 0, 0, 5), OP_LDI) = {any}\n", .{ imm_of(encode(OP_LDI, 3, 0, 0, 5), OP_LDI) }); + if (!(imm_of(2147483652, OP_LDI) == -16384)) __t27_assert_fail("\n assertion failed:\n imm_of(2147483652, OP_LDI) = {any}\n -16384 = {any}\n", .{ imm_of(2147483652, OP_LDI), -16384 }); + if (!(imm_of(encode(OP_LDI, 0, 0, 0, 16383), OP_LDI) == 16383)) __t27_assert_fail("\n assertion failed:\n imm_of(encode(OP_LDI, 0, 0, 0, 16383), OP_LDI) = {any}\n", .{ imm_of(encode(OP_LDI, 0, 0, 0, 16383), OP_LDI) }); +} +test "the_immediate_is_clamped_by_the_encoder" { + if (!(imm_clamped(20000) == 16383)) __t27_assert_fail("\n assertion failed:\n imm_clamped(20000) = {any}\n", .{ imm_clamped(20000) }); + if (!(imm_clamped(-20000) == -16384)) __t27_assert_fail("\n assertion failed:\n imm_clamped(-20000) = {any}\n -16384 = {any}\n", .{ imm_clamped(-20000), -16384 }); + if (!(imm_of(encode(OP_JMP, 0, 0, 0, 20000), OP_JMP) == 16383)) __t27_assert_fail("\n assertion failed:\n imm_of(encode(OP_JMP, 0, 0, 0, 20000), OP_JMP) = {any}\n", .{ imm_of(encode(OP_JMP, 0, 0, 0, 20000), OP_JMP) }); +} +test "the_three_operand_form_round_trips" { + var w: u32 = encode(OP_ADD, 3, 1, 2, 0); + _ = &w; + if (!(w == 533264)) __t27_assert_fail("\n assertion failed:\n w = {any}\n", .{ w }); + if (!(opcode_of(w) == OP_ADD)) __t27_assert_fail("\n assertion failed:\n opcode_of(w) = {any}\n OP_ADD = {any}\n", .{ opcode_of(w), OP_ADD }); + if (!(dst_of(w) == 3)) __t27_assert_fail("\n assertion failed:\n dst_of(w) = {any}\n", .{ dst_of(w) }); + if (!(src1_of(w, OP_ADD) == 1)) __t27_assert_fail("\n assertion failed:\n src1_of(w, OP_ADD) = {any}\n", .{ src1_of(w, OP_ADD) }); + if (!(src2_of(w, OP_ADD) == 2)) __t27_assert_fail("\n assertion failed:\n src2_of(w, OP_ADD) = {any}\n", .{ src2_of(w, OP_ADD) }); + if (!(imm_of(w, OP_ADD) == 0)) __t27_assert_fail("\n assertion failed:\n imm_of(w, OP_ADD) = {any}\n", .{ imm_of(w, OP_ADD) }); +} +test "the_two_operand_form_carries_src1_only" { + var w: u32 = encode(OP_MOV, 3, 1, 0, 0); + _ = &w; + if (!(w == 8975)) __t27_assert_fail("\n assertion failed:\n w = {any}\n", .{ w }); + if (!(src1_of(w, OP_MOV) == 1)) __t27_assert_fail("\n assertion failed:\n src1_of(w, OP_MOV) = {any}\n", .{ src1_of(w, OP_MOV) }); + if (!(src2_of(w, OP_MOV) == 0)) __t27_assert_fail("\n assertion failed:\n src2_of(w, OP_MOV) = {any}\n", .{ src2_of(w, OP_MOV) }); +} +test "src1_is_four_bits_in_the_immediate_form" { + var w: u32 = encode(OP_JGT, 2, 17, 0, 7); + _ = &w; + if (!(w == 926276)) __t27_assert_fail("\n assertion failed:\n w = {any}\n", .{ w }); + if (!(src1_of(w, OP_JGT) == 1)) __t27_assert_fail("\n assertion failed:\n src1_of(w, OP_JGT) = {any}\n", .{ src1_of(w, OP_JGT) }); + if (!(imm_of(w, OP_JGT) == 7)) __t27_assert_fail("\n assertion failed:\n imm_of(w, OP_JGT) = {any}\n", .{ imm_of(w, OP_JGT) }); +} +test "dot_bind_and_bundle2_lose_src2_through_encode" { + var w: u32 = encode(OP_DOT, 3, 1, 2, 0); + _ = &w; + if (!(src1_of(w, OP_DOT) == 1)) __t27_assert_fail("\n assertion failed:\n src1_of(w, OP_DOT) = {any}\n", .{ src1_of(w, OP_DOT) }); + if (!(src2_of(w, OP_DOT) == 0)) __t27_assert_fail("\n assertion failed:\n src2_of(w, OP_DOT) = {any}\n", .{ src2_of(w, OP_DOT) }); + if (!(src2_of(encode(OP_BIND, 3, 1, 2, 0), OP_BIND) == 0)) __t27_assert_fail("\n assertion failed:\n src2_of(encode(OP_BIND, 3, 1, 2, 0), OP_BIND) = {any}\n", .{ src2_of(encode(OP_BIND, 3, 1, 2, 0), OP_BIND) }); + if (!(src2_of(encode(OP_BUNDLE2, 3, 1, 2, 0), OP_BUNDLE2) == 0)) __t27_assert_fail("\n assertion failed:\n src2_of(encode(OP_BUNDLE2, 3, 1, 2, 0), OP_BUNDLE2) = {any}\n", .{ src2_of(encode(OP_BUNDLE2, 3, 1, 2, 0), OP_BUNDLE2) }); +} +test "the_bundle3_raw_word_carries_three_sources" { + var w: u32 = bundle3_word(3, 1, 2, 4); + _ = &w; + if (!(w == 34087779)) __t27_assert_fail("\n assertion failed:\n w = {any}\n", .{ w }); + if (!(src1_of(w, OP_BUNDLE3) == 1)) __t27_assert_fail("\n assertion failed:\n src1_of(w, OP_BUNDLE3) = {any}\n", .{ src1_of(w, OP_BUNDLE3) }); + if (!(src2_of(w, OP_BUNDLE3) == 2)) __t27_assert_fail("\n assertion failed:\n src2_of(w, OP_BUNDLE3) = {any}\n", .{ src2_of(w, OP_BUNDLE3) }); + if (!(v3_of(w, OP_BUNDLE3) == 4)) __t27_assert_fail("\n assertion failed:\n v3_of(w, OP_BUNDLE3) = {any}\n", .{ v3_of(w, OP_BUNDLE3) }); + if (!(v3_of(w, OP_ADD) == 0)) __t27_assert_fail("\n assertion failed:\n v3_of(w, OP_ADD) = {any}\n", .{ v3_of(w, OP_ADD) }); +} +test "sacr_and_the_two_operand_opcodes_carry_no_immediate" { + if (!(imm_of(encode(OP_SACR, 4, 1, 0, 3), OP_SACR) == 0)) __t27_assert_fail("\n assertion failed:\n imm_of(encode(OP_SACR, 4, 1, 0, 3), OP_SACR) = {any}\n", .{ imm_of(encode(OP_SACR, 4, 1, 0, 3), OP_SACR) }); + if (!(imm_of(encode(OP_MOV, 4, 1, 0, 3), OP_MOV) == 0)) __t27_assert_fail("\n assertion failed:\n imm_of(encode(OP_MOV, 4, 1, 0, 3), OP_MOV) = {any}\n", .{ imm_of(encode(OP_MOV, 4, 1, 0, 3), OP_MOV) }); + if (!(has_imm(OP_SACR) == false)) __t27_assert_fail("\n assertion failed:\n has_imm(OP_SACR) = {any}\n", .{ has_imm(OP_SACR) }); +} +test "the_forms_are_disjoint" { + if (!(has_imm(OP_ADD) == false)) __t27_assert_fail("\n assertion failed:\n has_imm(OP_ADD) = {any}\n", .{ has_imm(OP_ADD) }); + if (!(has_src2(OP_ADD) == true)) __t27_assert_fail("\n assertion failed:\n has_src2(OP_ADD) = {any}\n", .{ has_src2(OP_ADD) }); + if (!(has_imm(OP_JMP) == true)) __t27_assert_fail("\n assertion failed:\n has_imm(OP_JMP) = {any}\n", .{ has_imm(OP_JMP) }); + if (!(has_src2(OP_JMP) == false)) __t27_assert_fail("\n assertion failed:\n has_src2(OP_JMP) = {any}\n", .{ has_src2(OP_JMP) }); + if (!(has_imm(OP_DOT) == false)) __t27_assert_fail("\n assertion failed:\n has_imm(OP_DOT) = {any}\n", .{ has_imm(OP_DOT) }); + if (!(has_src2(OP_DOT) == false)) __t27_assert_fail("\n assertion failed:\n has_src2(OP_DOT) = {any}\n", .{ has_src2(OP_DOT) }); + if (!(has_imm(OP_SYSCALL) == false)) __t27_assert_fail("\n assertion failed:\n has_imm(OP_SYSCALL) = {any}\n", .{ has_imm(OP_SYSCALL) }); +} +test "the_t27a_forms_partition_the_forty_seven_opcodes" { + var b: u32 = 0; + _ = &b; + var imm_form: u32 = 0; + _ = &imm_form; + var src2_form: u32 = 0; + _ = &src2_form; + var two_operand: u32 = 0; + _ = &two_operand; + var both: u32 = 0; + _ = &both; + while (b < 256) { + if (is_defined_opcode(b)) { + if (t27a_imm_form(b)) { + imm_form = imm_form + 1; + } + if (t27a_src2_form(b)) { + src2_form = src2_form + 1; + } + if (t27a_imm_form(b) == false) { + if (t27a_src2_form(b) == false) { + two_operand = two_operand + 1; + } + } + if (t27a_imm_form(b)) { + if (t27a_src2_form(b)) { + both = both + 1; + } + } + } + b = b + 1; + } + if (!(imm_form == T27A_IMM_FORM_COUNT)) __t27_assert_fail("\n assertion failed:\n imm_form = {any}\n T27A_IMM_FORM_COUNT = {any}\n", .{ imm_form, T27A_IMM_FORM_COUNT }); + if (!(src2_form == T27A_SRC2_FORM_COUNT)) __t27_assert_fail("\n assertion failed:\n src2_form = {any}\n T27A_SRC2_FORM_COUNT = {any}\n", .{ src2_form, T27A_SRC2_FORM_COUNT }); + if (!(two_operand == T27A_TWO_OPERAND_COUNT)) __t27_assert_fail("\n assertion failed:\n two_operand = {any}\n T27A_TWO_OPERAND_COUNT = {any}\n", .{ two_operand, T27A_TWO_OPERAND_COUNT }); + if (!(both == 0)) __t27_assert_fail("\n assertion failed:\n both = {any}\n", .{ both }); +} +test "the_t27a_table_keeps_what_the_pinned_one_loses" { + var w: u32 = t27a_encode(OP_DOT, 3, 1, 2, 0, 0); + _ = &w; + if (!(t27a_src1(w, OP_DOT) == 1)) __t27_assert_fail("\n assertion failed:\n t27a_src1(w, OP_DOT) = {any}\n", .{ t27a_src1(w, OP_DOT) }); + if (!(t27a_src2(w, OP_DOT) == 2)) __t27_assert_fail("\n assertion failed:\n t27a_src2(w, OP_DOT) = {any}\n", .{ t27a_src2(w, OP_DOT) }); + if (!(src2_of(encode(OP_DOT, 3, 1, 2, 0), OP_DOT) == 0)) __t27_assert_fail("\n assertion failed:\n src2_of(encode(OP_DOT, 3, 1, 2, 0), OP_DOT) = {any}\n", .{ src2_of(encode(OP_DOT, 3, 1, 2, 0), OP_DOT) }); + if (!(t27a_src2(t27a_encode(OP_BIND, 3, 1, 26, 0, 0), OP_BIND) == 26)) __t27_assert_fail("\n assertion failed:\n t27a_src2(t27a_encode(OP_BIND, 3, 1, 26, 0, 0), OP_BIND) = {any}\n", .{ t27a_src2(t27a_encode(OP_BIND, 3, 1, 26, 0, 0), OP_BIND) }); + if (!(t27a_src2(t27a_encode(OP_BUNDLE2, 3, 1, 9, 0, 0), OP_BUNDLE2) == 9)) __t27_assert_fail("\n assertion failed:\n t27a_src2(t27a_encode(OP_BUNDLE2, 3, 1, 9, 0, 0), OP_BUNDLE2) = {any}\n", .{ t27a_src2(t27a_encode(OP_BUNDLE2, 3, 1, 9, 0, 0), OP_BUNDLE2) }); + if (!(t27a_imm(t27a_encode(OP_SACR, 4, 1, 0, 0, 3), OP_SACR) == 3)) __t27_assert_fail("\n assertion failed:\n t27a_imm(t27a_encode(OP_SACR, 4, 1, 0, 0, 3), OP_SACR) = {any}\n", .{ t27a_imm(t27a_encode(OP_SACR, 4, 1, 0, 0, 3), OP_SACR) }); + if (!(t27a_imm(t27a_encode(OP_SACR, 4, 1, 0, 0, 0 - 7), OP_SACR) == (0 - 7))) __t27_assert_fail("\n assertion failed:\n t27a_imm(t27a_encode(OP_SACR, 4, 1, 0, 0, 0 - 7), OP_SACR) = {any}\n 0 - 7 = {any}\n", .{ t27a_imm(t27a_encode(OP_SACR, 4, 1, 0, 0, 0 - 7), OP_SACR), 0 - 7 }); + if (!(t27a_encode(OP_BUNDLE3, 3, 1, 2, 4, 0) == bundle3_word(3, 1, 2, 4))) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_BUNDLE3, 3, 1, 2, 4, 0) = {any}\n bundle3_word(3, 1, 2, 4) = {any}\n", .{ t27a_encode(OP_BUNDLE3, 3, 1, 2, 4, 0), bundle3_word(3, 1, 2, 4) }); + if (!(v3_of(t27a_encode(OP_BUNDLE3, 3, 1, 2, 4, 0), OP_BUNDLE3) == 4)) __t27_assert_fail("\n assertion failed:\n v3_of(t27a_encode(OP_BUNDLE3, 3, 1, 2, 4, 0), OP_BUNDLE3) = {any}\n", .{ v3_of(t27a_encode(OP_BUNDLE3, 3, 1, 2, 4, 0), OP_BUNDLE3) }); +} +test "the_t27a_table_moves_no_pinned_word" { + if (!(t27a_encode(OP_ADD, 3, 1, 2, 0, 0) == encode(OP_ADD, 3, 1, 2, 0))) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_ADD, 3, 1, 2, 0, 0) = {any}\n encode(OP_ADD, 3, 1, 2, 0) = {any}\n", .{ t27a_encode(OP_ADD, 3, 1, 2, 0, 0), encode(OP_ADD, 3, 1, 2, 0) }); + if (!(t27a_encode(OP_ADD, 3, 1, 2, 0, 0) == 533264)) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_ADD, 3, 1, 2, 0, 0) = {any}\n", .{ t27a_encode(OP_ADD, 3, 1, 2, 0, 0) }); + if (!(t27a_encode(OP_LDI, 3, 0, 0, 0, 0 - 5) == 4294312708)) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_LDI, 3, 0, 0, 0, 0 - 5) = {any}\n", .{ t27a_encode(OP_LDI, 3, 0, 0, 0, 0 - 5) }); + if (!(t27a_encode(OP_MOV, 3, 1, 0, 0, 0) == 8975)) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_MOV, 3, 1, 0, 0, 0) = {any}\n", .{ t27a_encode(OP_MOV, 3, 1, 0, 0, 0) }); + if (!(t27a_encode(OP_JGT, 2, 1, 0, 0, 7) == encode(OP_JGT, 2, 1, 0, 7))) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_JGT, 2, 1, 0, 0, 7) = {any}\n encode(OP_JGT, 2, 1, 0, 7) = {any}\n", .{ t27a_encode(OP_JGT, 2, 1, 0, 0, 7), encode(OP_JGT, 2, 1, 0, 7) }); + if (!(t27a_encode(OP_SYSCALL, 26, 26, 0, 0, 0) == encode(OP_SYSCALL, 26, 26, 0, 0))) __t27_assert_fail("\n assertion failed:\n t27a_encode(OP_SYSCALL, 26, 26, 0, 0, 0) = {any}\n encode(OP_SYSCALL, 26, 26, 0, 0) = {any}\n", .{ t27a_encode(OP_SYSCALL, 26, 26, 0, 0, 0), encode(OP_SYSCALL, 26, 26, 0, 0) }); +}