t27.aiEnglish

Карта опкодов

Вы узнаете

Какие из 256 байтов опкода что-то значат и что делают остальные.

Из 256 значений байта 47 — опкоды TRI-27, от NOP и LD через ADD, JMP и HALT до SYSCALL. Остальные 209 для эмулятора не ошибка: decode_opcode() читает незнакомый байт как NOP, поэтому декодирование никогда не падает. Спека урока, t27a_mnemonics.t27, даёт ассемблеру t27a строгий декодер, который такие байты отвергает.

Попробовать

Найдите байты опкодов ADD и HALT; затем нажмите неподсвеченный байт и объясните, почему строгий декодер надёжнее для ассемблера.

Открыть интерактивный урок →

The opcode map: 47 of 256 bytes mean something
The opcode map: 47 of 256 bytes mean something ↗

Every byte from 0 to 255 as a cell; green ones are TRI-27 opcodes. Press a byte: an undefined one decodes as NOP, decoding never fails.

specs/isa/t27a_mnemonics.t27

// SPDX-License-Identifier: Apache-2.0
; specs/isa/t27a_mnemonics.t27 -- the t27a mnemonic table and a strict decoder for the 47 TRI-27
; opcodes (gHashTag/t27#6516, epic #6488; theorems T735-T736 in
; specs/compiler/theory/isa_round_trip.t27).
; One table, two directions. The assembler reads it as table index -> opcode byte (opcode_at), the
; disassembler as opcode byte -> table index (index_of). Both walk the same order as OPCODE_VALUES
; and OPCODE_NAMES in specs/isa/ternary_encoding.t27, so index i names OPCODE_NAMES[i] and encodes
; as OPCODE_VALUES[i]. The functions are written as if-chains over the OP_ constants of that spec,
; not as array indexing, so the C and Verilog backends lower them without a table in memory.
; Why a strict decoder. decode_opcode in specs/isa/ternary_encoding.t27 follows the pinned Trinity
; decoder: an undefined byte decodes as NOP and decoding never fails (UNKNOWN_OPCODE_RULE). That is
; right for the emulator and wrong for a disassembler: 209 of the 256 bytes are not opcodes, and a
; listing built on decode_opcode prints them as NOP (T735, last paragraph). strict_decode returns
; the byte for a defined opcode and INVALID_OPCODE (256, outside the byte range) for every other
; byte, so garbage cannot be mistaken for a NOP. decode_opcode is not changed; it is called below
; only as the negative control.
; ASCII only (L3).
; phi^2 + 1/phi^2 = 3 | TRINITY

module T27aMnemonics;

use isa::ternary_encoding;

pub const INVALID_OPCODE : u32 = 256;
pub const NO_INDEX : u32 = 47;
pub const BYTE_VALUES : u32 = 256;
pub const UNDEFINED_BYTES : u32 = 209;

; Table index 0..46 -> opcode byte, in the order of OPCODE_VALUES. Any other index -> 256.
pub fn opcode_at(i: u32) -> u32 {
    if (i == 0) { return OP_NOP; }
    if (i == 1) { return OP_LD; }
    if (i == 2) { return OP_ST; }
    if (i == 3) { return OP_LDI; }
    if (i == 4) { return OP_STI; }
    if (i == 5) { return OP_MOV; }
    if (i == 6) { return OP_ADD; }
    if (i == 7) { return OP_SUB; }
    if (i == 8) { return OP_MUL; }
    if (i == 9) { return OP_DIV; }
    if (i == 10) { return OP_INC; }
    if (i == 11) { return OP_DEC; }
    if (i == 12) { return OP_EXP; }
    if (i == 13) { return OP_SIN; }
    if (i == 14) { return OP_AND; }
    if (i == 15) { return OP_OR; }
    if (i == 16) { return OP_XOR; }
    if (i == 17) { return OP_NOT; }
    if (i == 18) { return OP_SHL; }
    if (i == 19) { return OP_SHR; }
    if (i == 20) { return OP_STR_LOAD; }
    if (i == 21) { return OP_STR_CONCAT; }
    if (i == 22) { return OP_STR_PRINT; }
    if (i == 23) { return OP_FILE_READ; }
    if (i == 24) { return OP_FILE_WRITE; }
    if (i == 25) { return OP_FILE_EXISTS; }
    if (i == 26) { return OP_JMP; }
    if (i == 27) { return OP_JZ; }
    if (i == 28) { return OP_JNZ; }
    if (i == 29) { return OP_CALL; }
    if (i == 30) { return OP_JGT; }
    if (i == 31) { return OP_JLT; }
    if (i == 32) { return OP_RET; }
    if (i == 33) { return OP_HALT; }
    if (i == 34) { return OP_DOT; }
    if (i == 35) { return OP_BIND; }
    if (i == 36) { return OP_BUNDLE2; }
    if (i == 37) { return OP_BUNDLE3; }
    if (i == 38) { return OP_PHI_CONST; }
    if (i == 39) { return OP_PI_CONST; }
    if (i == 40) { return OP_E_CONST; }
    if (i == 41) { return OP_SACR; }
    if (i == 42) { return OP_LD_IMM; }
    if (i == 43) { return OP_ADD3; }
    if (i == 44) { return OP_SUB3; }
    if (i == 45) { return OP_CMP3; }
    if (i == 46) { return OP_SYSCALL; }
    return INVALID_OPCODE;
}

; Opcode byte -> table index 0..46, the inverse of opcode_at. An undefined byte -> 47.
pub fn index_of(op: u32) -> u32 {
    if (op == OP_NOP) { return 0; }
    if (op == OP_LD) { return 1; }
    if (op == OP_ST) { return 2; }
    if (op == OP_LDI) { return 3; }
    if (op == OP_STI) { return 4; }
    if (op == OP_MOV) { return 5; }
    if (op == OP_ADD) { return 6; }
    if (op == OP_SUB) { return 7; }
    if (op == OP_MUL) { return 8; }
    if (op == OP_DIV) { return 9; }
    if (op == OP_INC) { return 10; }
    if (op == OP_DEC) { return 11; }
    if (op == OP_EXP) { return 12; }
    if (op == OP_SIN) { return 13; }
    if (op == OP_AND) { return 14; }
    if (op == OP_OR) { return 15; }
    if (op == OP_XOR) { return 16; }
    if (op == OP_NOT) { return 17; }
    if (op == OP_SHL) { return 18; }
    if (op == OP_SHR) { return 19; }
    if (op == OP_STR_LOAD) { return 20; }
    if (op == OP_STR_CONCAT) { return 21; }
    if (op == OP_STR_PRINT) { return 22; }
    if (op == OP_FILE_READ) { return 23; }
    if (op == OP_FILE_WRITE) { return 24; }
    if (op == OP_FILE_EXISTS) { return 25; }
    if (op == OP_JMP) { return 26; }
    if (op == OP_JZ) { return 27; }
    if (op == OP_JNZ) { return 28; }
    if (op == OP_CALL) { return 29; }
    if (op == OP_JGT) { return 30; }
    if (op == OP_JLT) { return 31; }
    if (op == OP_RET) { return 32; }
    if (op == OP_HALT) { return 33; }
    if (op == OP_DOT) { return 34; }
    if (op == OP_BIND) { return 35; }
    if (op == OP_BUNDLE2) { return 36; }
    if (op == OP_BUNDLE3) { return 37; }
    if (op == OP_PHI_CONST) { return 38; }
    if (op == OP_PI_CONST) { return 39; }
    if (op == OP_E_CONST) { return 40; }
    if (op == OP_SACR) { return 41; }
    if (op == OP_LD_IMM) { return 42; }
    if (op == OP_ADD3) { return 43; }
    if (op == OP_SUB3) { return 44; }
    if (op == OP_CMP3) { return 45; }
    if (op == OP_SYSCALL) { return 46; }
    return NO_INDEX;
}

; The byte itself for one of the 47 opcodes; INVALID_OPCODE (256) for the 209 other bytes.
pub fn strict_decode(byte: u32) -> u32 {
    if (index_of(byte) < OPCODE_COUNT) { return byte; }
    return INVALID_OPCODE;
}

pub fn is_valid(decoded: u32) -> bool {
    return decoded != INVALID_OPCODE;
}

invariant the_bytes_split_into_opcodes_and_rejects {
    assert OPCODE_COUNT + UNDEFINED_BYTES == BYTE_VALUES;
    assert NO_INDEX == OPCODE_COUNT;
    assert INVALID_OPCODE == BYTE_VALUES;
}

; T736 for the table: index_of is a left inverse of opcode_at on every index 0..46.
test index_of_inverts_opcode_at_for_all_47 {
    var i : u32 = 0;
    var ok : u32 = 0;
    while (i < OPCODE_COUNT) {
        if (index_of(opcode_at(i)) == i) { ok = ok + 1; }
        i = i + 1;
    }
    assert ok == 47;
    assert opcode_at(47) == INVALID_OPCODE;
    assert opcode_at(1000) == INVALID_OPCODE;
}

; The if-chain walks the same order as OPCODE_VALUES, strictly increasing, so no byte is listed twice.
test opcode_at_follows_opcode_values {
    var i : u32 = 0;
    var same : u32 = 0;
    var rising : u32 = 0;
    while (i < OPCODE_COUNT) {
        if (opcode_at(i) == OPCODE_VALUES[i]) { same = same + 1; }
        if (i > 0) {
            if (opcode_at(i) > opcode_at(i - 1)) { rising = rising + 1; }
        }
        i = i + 1;
    }
    assert same == 47;
    assert rising == 46;
    assert opcode_at(0) == OP_NOP;
    assert opcode_at(46) == OP_SYSCALL;
}

; The other direction: opcode_at inverts index_of on every defined byte, and every undefined byte
; has no index.
test opcode_at_inverts_index_of_on_defined_bytes {
    var b : u32 = 0;
    var back : u32 = 0;
    var no_index : u32 = 0;
    while (b < BYTE_VALUES) {
        var k : u32 = index_of(b);
        if (k < OPCODE_COUNT) {
            if (opcode_at(k) == b) { back = back + 1; }
        } else {
            if (k == NO_INDEX) { no_index = no_index + 1; }
        }
        b = b + 1;
    }
    assert back == 47;
    assert no_index == 209;
}

; Exactly 47 bytes decode, exactly 209 are rejected, and the accepted set is is_defined_opcode.
test strict_decode_accepts_47_and_rejects_209 {
    var b : u32 = 0;
    var accepted : u32 = 0;
    var rejected : u32 = 0;
    var agree : u32 = 0;
    while (b < BYTE_VALUES) {
        var d : u32 = strict_decode(b);
        if (d != INVALID_OPCODE) {
            accepted = accepted + 1;
            if (d == b) { agree = agree + 1; }
        } else {
            rejected = rejected + 1;
        }
        if (is_valid(d) != is_defined_opcode(b)) { agree = 0; }
        b = b + 1;
    }
    assert accepted == 47;
    assert rejected == 209;
    assert agree == 47;
}

; Negative control: the emulator's decoder reads byte 1 as NOP; the strict decoder rejects it, and
; still accepts the real NOP byte 0.
test undefined_byte_is_rejected_not_nop {
    assert decode_opcode(1) == OP_NOP;
    assert strict_decode(1) == INVALID_OPCODE;
    assert is_valid(strict_decode(1)) == false;
    assert strict_decode(OP_NOP) == OP_NOP;
    assert is_valid(strict_decode(OP_NOP));
    assert strict_decode(70) == INVALID_OPCODE;
    assert strict_decode(137) == INVALID_OPCODE;
    assert strict_decode(255) == INVALID_OPCODE;
    assert strict_decode(OP_HALT) == OP_HALT;
    assert index_of(1) == NO_INDEX;
}

Открыть спеку урока в плеере ↗

Все уроки