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, } RegMemMode :: enum { Memory00 = 0b00, Memory08 = 0b01, Memory16 = 0b10, Register = 0b11, }; OpCode :: 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}, } Instruction :: struct { mask: u8, encoding: u8, name: string, desc: string, } instructions := [?]Instruction { { mask = 0b11111100, encoding = 0b10001000, name = "mov", desc = "Register/memory to/from register" }, { mask = 0b11111110, encoding = 0b11000110, name = "mov", desc = "Immediate to register/memory" }, { mask = 0b11110000, encoding = 0b10110000, name = "mov", desc = "Immediate to register" }, { mask = 0b11111110, encoding = 0b10100000, name = "mov", desc = "Memory to accumulator" }, { mask = 0b11111110, encoding = 0b10100010, name = "mov", desc = "Accumulator to memory" }, { mask = 0b11111111, encoding = 0b10001110, name = "mov", desc = "Register/memory to segment register" }, { mask = 0b11111111, encoding = 0b10001100, name = "mov", desc = "Segment register to register/memory" }, } ParsedInstruction :: struct { code: OpCode, displacement: DisplacementMode, } inst_map := make(map[u8]Instruction) RIGHT_ALIGN_AMOUNT := 30 get_instruction :: proc(bytes: []u8) -> (Instruction, u8) { return {}, 0 } 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 } ModMemory :: struct {} Mod8BitDisp :: i8 Mod16BitDisp :: i16 ModRegister :: struct {} DisplacementMode :: union { ModMemory, Mod8BitDisp, Mod16BitDisp, ModRegister, } ModField :: struct { displacement: DisplacementMode } None :: struct {} Disp8 :: i8 Disp16 :: i16 Displacement :: union { None, Disp8, Disp16 } RegisterId :: u8 Immediate8 :: i8 Immediate16 :: i16 MemoryAddr :: struct { addr_id: u8, displacement: Displacement } 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 } MemoryType :: union { RegisterId, MemoryAddr } OperandType :: union { RegisterId, Immediate8, Immediate16, MemoryAddr } get_i16 :: proc(data: []u8) -> i16 { return (i16)(data[1]) << 8 | (i16)(data[0]) } parse_displacement :: proc(data: []u8) -> (displacement: DisplacementMode, disp_amount: int) { mod := (data[0] & 0b11000000) >> 6 disp: DisplacementMode amount: int switch mod { case 0: disp = ModMemory{} case 1: disp = (i8)(data[1]) amount = 1 case 2: disp = get_i16(data[1:]) amount = 2 case 3: disp = ModRegister{} } return disp, amount } get_displacement_string :: proc(displacement: DisplacementMode) -> 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) -> (Instruction, 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 Instruction{}, 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) } read_next := false src_dst := true fmt.println("bits 16\n") idx := 0 for idx < bytes_read { processed := 0 curr_byte := data[idx] inst_name: string if instruction, ok := try_find_instruction(curr_byte); ok { inst_name = instruction.name } else { txt := "unknown instruction" fmt.printfln("%s %*[1]s %8b", txt, RIGHT_ALIGN_AMOUNT - len(txt), ";;", curr_byte) idx += 1 continue } lhs2: OperandType rhs2: OperandType lhs: string rhs: string is_word: bool is_immediate := false flip_dst := false if curr_byte & 0b11110000 == 0b10110000 { is_word = curr_byte & 0b0000_1000 != 0 reg := registers[curr_byte & 0b00000111] lhs = is_word ? reg.fullname : reg.bytename processed += is_word ? 1 : 0 lhs2 := (RegisterId)(reg.code) rhs2 := (OperandType)(is_word ? ((Immediate16)(get_i16(data[idx+1:]))) : ((Immediate8)(data[idx+1]))) } else if curr_byte & 0b11111000 == 0b10001000 { mod_reg_rm := data[idx + 1] is_word = curr_byte & 1 == 1 flip_dst = curr_byte & 2 != 0 reg := (mod_reg_rm & 0b00111000) >> 3 rm := mod_reg_rm & 0b00000111 mod, disp_amount := parse_displacement(data[idx + 1:]) switch disp_val in mod { case ModMemory: lhs2 = (RegisterId)(reg) rhs2 = MemoryAddr{ addr_id = rm , displacement = None{} } processed += 1 case Mod8BitDisp: lhs2 = (RegisterId)(reg) rhs2 = MemoryAddr{ addr_id = rm , displacement = disp_val } processed += 1 case Mod16BitDisp: lhs2 = (RegisterId)(reg) rhs2 = MemoryAddr{ addr_id = rm , displacement = disp_val } processed += 2 case ModRegister: lhs2 = (RegisterId)(rm) rhs2 = (RegisterId)(reg) processed += 1 } dst_reg := registers[rm] } if flip_dst { lhs2, rhs2 = rhs2, lhs2 } switch val in lhs2 { case RegisterId: lhs = fmt.aprintf("%s", is_word ? registers[val].fullname : registers[val].bytename) case Immediate8: lhs = fmt.aprintf("%d", val) case Immediate16: lhs = fmt.aprintf("%d", val) case MemoryAddr: lhs = get_memory_string(val) } switch val in rhs2 { case RegisterId: rhs = is_word ? registers[val].fullname : registers[val].bytename case Immediate8: rhs = fmt.aprintf("%d", val) case Immediate16: rhs = fmt.aprintf("%d", val) case MemoryAddr: rhs = get_memory_string(val) } full_inst := fmt.aprintf("%s %s, %s", inst_name, lhs, rhs) processed += 1 fmt.printf("%s %*[1]s a %08b", full_inst, RIGHT_ALIGN_AMOUNT - len(full_inst), ";;", curr_byte) for i in 0..=processed { fmt.printf(" %08b", data[processed + 1 + i]) } fmt.println() idx += processed if true { continue } if curr_byte & 0b11111000 == 0b10001000 || curr_byte & 0b11111110 == 0b11000110 { is_imm_mode := curr_byte & 0b11111110 == 0b11000110 is_word := curr_byte & 1 == 1 flip_src := curr_byte & 2 != 0 next_byte := data[processed + 1] reg := (next_byte & 0b00111000) >> 3 rm := next_byte & 0b00000111 dst_reg := registers[rm] displacement, disp_amount := parse_displacement(data[processed + 1:]) src_name, dst_name: string // switch disp_val in displacement { // case DisplaceMemoryMode: // src_name = is_word ? registers[rm].fullname : registers[rm].bytename // if is_imm_mode { // if is_word { // dst_name = fmt.aprintf("word %d", get_i16(data[processed+2:])) // } else { // dst_name = fmt.aprintf("byte %d", (i8)(data[processed+2])) // } // } // disp_amount += is_word ? 2 : 1 // case Displace8Bits: // case Displace16Bits: // case DisplaceRegisterMode: // } // if disp_val, ok := displacement.(DisplaceRegisterMode); ok { // src_name = is_word ? registers[rm].fullname : registers[rm].bytename // } else { // src_name = fmt.aprintf("[%s%s]", calculate_effective_address(rm), get_displacement_string(displacement)) // } if flip_src && !is_imm_mode { src_name, dst_name = dst_name, src_name } inst_string := fmt.aprintf("mov %s, %s", src_name, dst_name) fmt.printf("%s %*[1]s a %08b", inst_string, RIGHT_ALIGN_AMOUNT - len(inst_string), ";;", curr_byte) for i in 0..=disp_amount { fmt.printf(" %08b", data[processed + 1 + i]) } fmt.println() processed += 1 + disp_amount } else if curr_byte & 0b11110000 == 0b10110000 { is_word := curr_byte & 0b0000_1000 != 0 reg := curr_byte & 0b00000111 dst_name: string imm: i16 if is_word { dst_name = registers[reg].fullname imm = (i16)(data[processed+2]) << 8 | (i16)(data[processed+1]) processed += 2 } else { dst_name = registers[reg].bytename imm = (i16)(data[processed+1]) processed += 1 } inst_string := fmt.aprintf("mov %s, %d", dst_name, imm) fmt.printfln("%s %*[1]s b %08b %08b", inst_string, RIGHT_ALIGN_AMOUNT - len(inst_string), ";; 2", curr_byte, data[processed + 1]) } else if curr_byte & 0b11111110 == 0b11000110 { is_word := curr_byte & 1 != 0 fmt.printfln("mov [%s], asdf ;; %08b %8b %8b", "", curr_byte, data[processed + 1], data[processed + 2]) } else { txt := "unknown instruction" fmt.printfln("%s %*[1]s %8b", txt, RIGHT_ALIGN_AMOUNT - len(txt), ";;", curr_byte) } processed += 1 } }