mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-19 16:42:33 +00:00
Each ISA's hand-written ENCODING_TABLE (the single source of truth) now lives in a per-arch tablegen/ metaprogram that flattens it and serializes committed binary blobs; the library #loads those into @(rodata) at compile time rather than compiling a table body. No arch keeps encoding_table.odin or decoding_tables.odin -- only a generated tables.odin loader and tables/*.bin. * Two-stage, type-checked pipeline: tablegen Stage A emits human-readable generated Odin, which compiles and serializes the blobs in Stage B. * encode() goes through encoding_forms(m); decoders are unchanged apart from x86's flattened 2-D index. Decode tables are byte-identical to the old ones. * build.lua: a LuaJIT driver for the metaprograms, validations, and tests, with cross-platform gating and a clear report. * Docs refreshed; the obsolete forward-looking plan in cross_arch_design.md trimmed to what was actually built. * Attribution headers added to all rexcode source files; the generators emit them so generated files keep them.
140 lines
4.1 KiB
Odin
140 lines
4.1 KiB
Odin
// rexcode · Brendan Punsky (dotbmp@github), original author
|
|
|
|
package rexcode_isa
|
|
|
|
// =============================================================================
|
|
// PRINTER FRAMEWORK (shared scaffolding for all architectures)
|
|
// =============================================================================
|
|
//
|
|
// Owns the universal pieces of disassembly printing: token kinds (used
|
|
// for syntax highlighting), print options, the result type, and pure
|
|
// number-formatting helpers. Per-arch printers own the formatting of
|
|
// register names, memory syntax, mnemonics, and the actual output-sink
|
|
// procedures (sbprint/print/tprint/...) -- those call into the helpers
|
|
// here for hex/decimal output.
|
|
|
|
import "core:strings"
|
|
import "core:reflect"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Tokens (syntax-highlighting metadata)
|
|
// -----------------------------------------------------------------------------
|
|
|
|
Token_Kind :: enum u8 {
|
|
WHITESPACE, // spaces, tabs, indentation
|
|
NEWLINE, // line breaks
|
|
LABEL_DEF, // label definition (e.g., ".L1:")
|
|
LABEL_REF, // label reference in operand
|
|
OFFSET, // byte offset prefix (e.g., "0x10:")
|
|
MNEMONIC, // instruction mnemonic
|
|
REGISTER, // register name
|
|
IMMEDIATE, // immediate value
|
|
MEMORY_BRACKET, // '[' or ']'
|
|
MEMORY_OPERATOR, // '+', '-', '*' in memory operands
|
|
MEMORY_DISP, // displacement in memory operand
|
|
MEMORY_SCALE, // scale factor in memory operand
|
|
PUNCTUATION, // comma separator, colon
|
|
COMMENT,
|
|
}
|
|
|
|
Token :: struct {
|
|
offset: u32, // byte offset in output string
|
|
length: u16, // length in bytes
|
|
kind: Token_Kind,
|
|
instruction_index: u16, // which instruction (0xFFFF for labels/whitespace)
|
|
}
|
|
|
|
@(require_results)
|
|
token_kind_to_string :: proc(k: Token_Kind) -> string {
|
|
if name, ok := reflect.enum_name_from_value(k); ok {
|
|
return name
|
|
}
|
|
return "???"
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Print options & result
|
|
// -----------------------------------------------------------------------------
|
|
|
|
Print_Options :: struct {
|
|
uppercase: bool, // uppercase mnemonics/registers
|
|
hex_prefix: string, // hex prefix (default "0x")
|
|
hex_lowercase: bool,
|
|
label_prefix: string, // default ".L"
|
|
show_offsets: bool, // show byte offsets before each instruction
|
|
indent: string, // default " "
|
|
separator: string, // default "\n"
|
|
space_after_comma: bool,
|
|
}
|
|
|
|
DEFAULT_PRINT_OPTIONS :: Print_Options{
|
|
uppercase = false,
|
|
hex_prefix = "0x",
|
|
hex_lowercase = true,
|
|
label_prefix = ".L",
|
|
show_offsets = false,
|
|
indent = " ",
|
|
separator = "\n",
|
|
space_after_comma = true,
|
|
}
|
|
|
|
Print_Result :: struct {
|
|
text: string, // formatted disassembly text
|
|
tokens: []Token, // optional syntax-highlight metadata (nil if not requested)
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Number formatting helpers (arch-independent, used by per-arch printers)
|
|
// -----------------------------------------------------------------------------
|
|
|
|
print_hex :: proc(sb: ^strings.Builder, value: u64, options: ^Print_Options) {
|
|
strings.write_string(sb, options.hex_prefix)
|
|
print_hex_digits(sb, value, options)
|
|
}
|
|
|
|
print_hex_digits :: proc(sb: ^strings.Builder, value: u64, options: ^Print_Options) {
|
|
if value == 0 {
|
|
strings.write_byte(sb, '0')
|
|
return
|
|
}
|
|
|
|
buf: [16]u8
|
|
i := 0
|
|
v := value
|
|
for v > 0 {
|
|
digit := u8(v & 0xF)
|
|
buf[i] = digit < 10 ? '0' + digit : 'a' + digit - 10
|
|
v >>= 4
|
|
i += 1
|
|
}
|
|
|
|
for j := i - 1; j >= 0; j -= 1 {
|
|
c := buf[j]
|
|
if options.uppercase && c >= 'a' && c <= 'f' {
|
|
c -= 32
|
|
}
|
|
strings.write_byte(sb, c)
|
|
}
|
|
}
|
|
|
|
// Print a decimal number (used for label IDs, scale factors, etc).
|
|
print_decimal :: proc(sb: ^strings.Builder, value: u32) {
|
|
if value == 0 {
|
|
strings.write_byte(sb, '0')
|
|
return
|
|
}
|
|
|
|
buf: [10]u8
|
|
i := 0
|
|
v := value
|
|
for v > 0 {
|
|
buf[i] = '0' + u8(v % 10)
|
|
v /= 10
|
|
i += 1
|
|
}
|
|
|
|
for j := i - 1; j >= 0; j -= 1 {
|
|
strings.write_byte(sb, buf[j])
|
|
}
|
|
}
|