performance-aware/decoder8086.odin

374 lines
10 KiB
Odin

package decoder_8086
import "core:os"
import "core:fmt"
import "core:math"
Register :: struct {
fullname: string,
bytename: string,
value: struct #raw_union {
using _: struct {
low, high: byte,
},
full: u16,
},
code: u8,
}
OpName :: enum {
MOV,
ADD,
SUB,
CMP,
JMP,
}
registers := [8]Register {
{fullname = "ax", bytename = "al", code = 0b000},
{fullname = "cx", bytename = "cl", code = 0b001},
{fullname = "dx", bytename = "dl", code = 0b010},
{fullname = "bx", bytename = "bl", code = 0b011},
{fullname = "sp", bytename = "ah", code = 0b100},
{fullname = "bp", bytename = "ch", code = 0b101},
{fullname = "si", bytename = "dh", code = 0b110},
{fullname = "di", bytename = "bh", code = 0b111},
}
RegInfo :: struct {
in_first_byte: bool,
shift_offset: u8,
}
LastBit :: struct{}
FourthBit :: struct{}
WordSize :: union {
None,
LastBit,
FourthBit,
}
InstructionInfo :: struct {
mask: u8,
encoding: u8,
name: string,
desc: string,
has_mod_rm: bool,
word_size: WordSize,
reg_info: Maybe(RegInfo),
has_data: bool,
has_displacement: bool,
has_segreg: bool,
has_flip: bool,
has_explicit_size: bool,
has_accumulator: bool,
}
reg_first_last := RegInfo{ in_first_byte = true, shift_offset = 0 }
reg_second_middle := RegInfo{ in_first_byte = false, shift_offset = 3 }
instructions := [?]InstructionInfo {
{ name = "mov", desc = "Register/memory to/from register", mask = 0b11111100, encoding = 0b10001000,
has_mod_rm = true, reg_info = reg_second_middle, has_data = false, has_displacement = true,
word_size = LastBit{}, has_flip = true },
{ name = "mov", desc = "Immediate to register/memory", mask = 0b11111110, encoding = 0b11000110,
has_mod_rm = true, reg_info = nil, has_data = true, has_displacement = true,
word_size = LastBit{}, has_explicit_size = true },
{ name = "mov", desc = "Immediate to register", mask = 0b11110000, encoding = 0b10110000,
has_mod_rm = false, reg_info = reg_first_last, has_data = true, has_displacement = false,
word_size = FourthBit{} },
{ name = "mov", desc = "Memory to accumulator", mask = 0b11111110, encoding = 0b10100000,
has_mod_rm = false, reg_info = nil, has_data = true, has_displacement = false, has_flip = true,
word_size = LastBit{}, has_accumulator = true },
{ name = "mov", desc = "Accumulator to memory", mask = 0b11111110, encoding = 0b10100010,
has_mod_rm = false, reg_info = nil, has_data = true, has_displacement = false, has_flip = true,
word_size = LastBit{}, has_accumulator = true },
{ name = "mov", desc = "Register/memory to segment register", mask = 0b11111111, encoding = 0b10001110,
has_mod_rm = true, reg_info = nil, has_segreg = true, has_displacement = true,
word_size = None{} },
{ name = "mov", desc = "Segment register to register/memory", mask = 0b11111111, encoding = 0b10001100,
has_mod_rm = true, reg_info = nil, has_segreg = true, has_displacement = true,
word_size = None{} },
}
None :: struct {}
Disp8 :: i8
Disp16 :: i16
Displacement :: union {
None,
Disp8,
Disp16
}
Value8 :: i8
Value16 :: i16
Data :: union {
None,
Value8,
Value16
}
ModMemory :: struct {}
Mod8BitDisp :: i8
Mod16BitDisp :: i16
ModRegister :: struct {}
ModMode :: union {
ModMemory,
Mod8BitDisp,
Mod16BitDisp,
ModRegister,
}
RegisterId :: distinct u8
Immediate8 :: distinct i8
Immediate16 :: distinct i16
MemoryAddr :: struct {
addr_id: u8,
displacement: Displacement
}
DirectAddress :: distinct i16
Accumulator8 :: distinct i8
Accumulator16 :: distinct i16
OperandType :: union {
RegisterId,
Immediate8,
Immediate16,
MemoryAddr,
DirectAddress,
Accumulator8,
Accumulator16,
}
inst_map := make(map[u8]InstructionInfo)
RIGHT_ALIGN_AMOUNT := 30
calculate_effective_address :: proc(r_m: u8) -> string {
val: string
switch r_m {
case 0b000:
val = "bx + si"
case 0b001:
val = "bx + di"
case 0b010:
val = "bp + si"
case 0b011:
val = "bp + di"
case 0b100:
val = "si"
case 0b101:
val = "di"
case 0b110:
val = "bp"
case 0b111:
val = "bx"
}
return val
}
get_memory_string :: proc(memoryAddr: MemoryAddr) -> string {
disp: string
switch value in memoryAddr.displacement {
case None:
disp = ""
case Disp8:
if value != 0 {
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
}
case Disp16:
if value != 0 {
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
}
}
text := fmt.aprintf("[%s%s]", calculate_effective_address(memoryAddr.addr_id), disp)
return text
}
get_memory_type_string :: proc(mem_type: OperandType, is_word: bool) -> string {
switch val in mem_type {
case RegisterId:
return is_word ? registers[val].fullname : registers[val].bytename
case Immediate8:
return fmt.aprintf("%d", val)
case Immediate16:
return fmt.aprintf("%d", val)
case MemoryAddr:
return get_memory_string(val)
case DirectAddress:
return fmt.aprintf("[%d]", val)
case Accumulator8:
return fmt.aprintf("[%d]", val)
case Accumulator16:
return fmt.aprintf("[%d]", val)
}
return ""
}
get_i16 :: proc(data: []u8) -> i16 {
return (i16)(data[1]) << 8 | (i16)(data[0])
}
parse_displacement :: proc(data: []u8) -> (displacement: Displacement, disp_amount: int) {
mod := (data[0] & 0b11000000) >> 6
disp: Displacement = None{}
amount: int
switch mod {
case 1:
disp = (i8)(data[1])
amount = 1
case 2:
disp = get_i16(data[1:])
amount = 2
}
return disp, amount
}
get_displacement_string :: proc(displacement: Displacement) -> string {
disp := ""
#partial switch value in displacement {
case i8:
if value != 0 {
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
}
case i16:
if value != 0 {
disp = fmt.aprintf(" %s %d", value > 0 ? "+" : "-", math.abs(value))
}
}
return disp
}
try_find_instruction :: proc(b: u8) -> (InstructionInfo, bool) {
mask: u8 = 0xFF
for j in 0..=4 {
encoding := b & mask
if inst, ok := inst_map[encoding]; ok {
return inst, true
}
mask <<= 1
}
return InstructionInfo{}, false
}
main :: proc() {
f,err := os.open(len(os.args) > 1 ? os.args[1] : "./asm_files/01-02-39.bin")
// f,err := os.open(len(os.args) > 1 ? os.args[1] : "./asm_files/01-02-40.bin")
if err != os.ERROR_NONE {
os.exit(1)
}
defer os.close(f)
data := make([]u8, 512)
bytes_read, err2 := os.read(f, data)
if err2 != nil {
// ...
os.exit(1)
}
for inst in instructions {
inst_map[inst.encoding] = inst
}
if false {
os.exit(0)
}
// asdf :u16 = 0b1111_0000_1001_0100
// asdf2 :i16 = (i16)(asdf)
// fmt.printfln("%d", asdf2)
read_next := false
src_dst := true
fmt.println("bits 16\n")
idx := 0
for idx < bytes_read {
processed := 1
curr_byte := data[idx]
instruction, ok := try_find_instruction(curr_byte)
if !ok {
txt := "unknown instruction"
fmt.printfln("%s %*[1]s %8b", txt, RIGHT_ALIGN_AMOUNT - len(txt), ";;", curr_byte)
idx += 1
continue
}
lhs2: OperandType
rhs2: OperandType
is_word: bool
is_immediate := false
flip_dst := false
rm: u8
mod: u8
reg: u8
if instruction.has_flip {
flip_dst = curr_byte & 2 != 0
}
switch val in instruction.word_size {
case LastBit: is_word = curr_byte & 1 == 1
case FourthBit: is_word = curr_byte & 0b0000_1000 != 0
case None:
}
if reg_info, ok := instruction.reg_info.(RegInfo); ok {
b := reg_info.in_first_byte ? data[idx] : data[idx+1]
reg = (b >> reg_info.shift_offset) & 0b111
}
if instruction.has_mod_rm {
mod = data[idx+1] >> 6
rm = data[idx+1] & 0b00000111
processed += 1 + ((int)(mod) % 3)
if mod == 0 {
if rm == 0b110 {
lhs2 = (DirectAddress)(get_i16(data[idx+2:]))
processed += 2
} else {
lhs2 = MemoryAddr{ addr_id = rm , displacement = None{} }
}
} else if mod == 1 {
lhs2 = MemoryAddr{ addr_id = rm , displacement = (i8)(data[idx+2]) }
} else if mod == 2 {
lhs2 = MemoryAddr{ addr_id = rm , displacement = get_i16(data[idx+2:]) }
} else if mod == 3 {
lhs2 = (RegisterId)(registers[rm].code)
}
if instruction.has_explicit_size {
imm_idx := idx + 2 + ((int)(mod) % 3)
rhs2 = (OperandType)(is_word ? (Immediate16)(get_i16(data[imm_idx:])) : (Immediate8)(data[imm_idx]))
processed += is_word ? 2 : 1
} else {
rhs2 = (RegisterId)(reg)
}
} else {
lhs2 = (RegisterId)(registers[reg].code)
if instruction.has_accumulator {
rhs2 = (OperandType)(is_word ? ((Accumulator16)(get_i16(data[idx+1:]))) : ((Accumulator8)(data[idx+1])))
} else {
rhs2 = (OperandType)(is_word ? ((Immediate16)(get_i16(data[idx+1:]))) : ((Immediate8)(data[idx+1])))
}
processed += is_word ? 2 : 1
}
if flip_dst {
lhs2, rhs2 = rhs2, lhs2
}
lhs := get_memory_type_string(lhs2, is_word)
rhs := get_memory_type_string(rhs2, is_word)
size_string := instruction.has_explicit_size ? is_word ? "word " : "byte " : ""
full_inst := fmt.aprintf("%s %s, %s%s", instruction.name, lhs, size_string, rhs)
fmt.printf("%s %*[1]s a", full_inst, RIGHT_ALIGN_AMOUNT - len(full_inst), ";;")
for i in 0..<processed {
fmt.printf(" %08b", data[idx + i])
}
fmt.println()
idx += processed
}
}