package sim_8086 import "core:fmt" import "core:math" import "core:strings" SEGMENT_REGISTER_START :: 8 get_i16 :: proc(data: []u8) -> i16 { return (i16)(data[1]) << 8 | (i16)(data[0]) } get_op :: proc(inst: Instruction) -> (Op, bool) { op: Op interseg: bool if inst.opname == .TBD2 { switch inst.raw_data[1] & 0b00111000 >> 3 { case 0b000: op = .INC case 0b001: op = .DEC case 0b010: op = .CALL case 0b011: op = .CALL; interseg = true case 0b100: op = .JMP case 0b101: op = .JMP; interseg = true case 0b110: op = .PUSH } } else if inst.opname == .TBD5 { switch inst.raw_data[1] & 0b00111000 >> 3 { case 0b000: op = .TEST case 0b001: op = .DEC case 0b010: op = .NOT case 0b011: op = .NEG case 0b100: op = .MUL case 0b101: op = .IMUL case 0b110: op = .DIV case 0b111: op = .IDIV } } else if inst.opname == .TBD6 { switch inst.raw_data[1] & 0b00111000 >> 3 { case 0b000: op = .ROL case 0b001: op = .ROR case 0b010: op = .RCL case 0b011: op = .RCR case 0b100: op = .SHL case 0b101: op = .SHR case 0b111: op = .SAR } } else if inst.opname == .TBD1 || inst.opname == .TBD3 || inst.opname == .TBD4 { bits: u8 if inst.opname == .TBD1 || inst.opname == .TBD3 { bits = inst.raw_data[0] & 0b00111000 >> 3 } else { bits = inst.raw_data[1] & 0b00111000 >> 3 } switch bits { case 0b000: op = .ADD case 0b001: op = .OR case 0b010: op = .ADC case 0b011: op = .SBB case 0b100: op = .AND case 0b101: op = .SUB case 0b110: op = .XOR case 0b111: op = .CMP } } else { op = inst.opname } return op, interseg } parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, total_bytes_processed: int, word: bool, has_segreg: Maybe(RegisterId)) -> Operand { operand: Operand = None{} switch opinfo { case .None: case .Register, .SegmentRegister: reg: u8 switch inst.reg_info { case .None: panic("Register is required but the encoded location is not provided") case .FirstByteLast3: reg = data[0] & 0b111 case .FirstByteMiddle3: reg = (data[0] >> 3) & 0b111 case .SecondByteMiddle3: reg = (data[1] >> 3) & 0b111 case .SecondByteLast3: reg = data[1] & 0b111 } if opinfo == .SegmentRegister { operand = (RegisterId){name = Register(SEGMENT_REGISTER_START + (int)(reg)), access = .Full} } else if word { operand = RegisterId { name = Register(reg), access = .Full } } else { operand = RegisterId { name = Register(reg % 4), access = reg < 4 ? .Low : .High } } case .RegisterMemory: mod := data[1] >> 6 rm := data[1] & 0b111 processed^ += 1 op: Operand if mod == 0 { if rm == 0b110 { // op = DirectAddress { value = get_i16(data[2:]) } op = (DirectAddress)(get_i16(data[2:])) processed^ += 2 } else { op = MemoryAddr{ addr_id = rm , displacement = None{} } } } else if mod == 1 { op = MemoryAddr{ addr_id = rm , displacement = (i8)(data[2]) } processed^ += 1 } else if mod == 2 { op = MemoryAddr{ addr_id = rm , displacement = get_i16(data[2:]) } processed^ += 2 } else if mod == 3 { if word { op = RegisterId { name = Register(rm), access = .Full } } else { op = RegisterId { name = Register(rm % 4), access = rm < 4 ? .Low : .High } } } operand = op case .Immediate: data_idx := processed^ word_signed := word if inst.has_sign_extension { word_signed &&= data[0] & 0b0000_0010 == 0 } value: i16 = word_signed ? get_i16(data[data_idx:]) : i16(i8(data[data_idx])) operand = Immediate { value = value, size = word_signed ? .Signed16 : .Signed8 } processed^ += word_signed ? 2 : 1 case .ImmediateUnsigned: operand = Immediate { value = i16(data[processed^]), size = .Unsigned8 } processed^ += 1 case .Accumulator: operand = RegisterId { name = Register(0), access = word ? .Full : .Low } case .DirectAddress: // operand = DirectAddress { value = get_i16(data[1:]) } operand = (DirectAddress)(get_i16(data[1:])) processed^ += 2 case .Jump: processed^ += 1 // NOTE: In order to mimic the label offset, you have to take the value you got and add two operand = (Jump)((i8)(data[1]) + 2) case .VariablePort: operand = RegisterId { name = Register.dx, access = .Full } case .ShiftRotate: v_flag := data[0] & 0b10 != 0 operand = v_flag ? RegisterId { name = Register(1), access = .Low } : Immediate { value = 1 } case .Repeat: bits := (data[1] & 0b1110) >> 1 w := (data[1] & 0b1) == 1 ? "w" : "b" rep: string switch bits { case 0b010: rep = "movs" case 0b011: rep = "cmps" case 0b101: rep = "stos" case 0b110: rep = "lods" case 0b111: rep = "scas" } operand = Repeat(fmt.aprintf("%s%s", rep, w)) processed^ += 1 case .DirectWithinSegment: value := (int)(get_i16(data[1:])) + total_bytes_processed + 3 operand = Immediate { value = i16(value), size = .Signed16 } processed^ += 2 case .Intersegment: operand = Intersegment { ip = get_i16(data[1:]), cs = get_i16(data[3:]), } processed^ += 4 } return operand } decode_data :: proc(inst_list: ^[dynamic]Instruction, data: []u8, bytes_to_read: int) -> int { idx := 0 has_segment: Maybe(RegisterId) has_lock: bool total_bytes_processed: int for idx < bytes_to_read { instruction: Instruction processed := 1 curr_byte := data[idx] found_inst: bool inst: InstructionInfo for i in instructions { if i.encoding == (curr_byte & i.mask) { found_inst = true inst = i break } } if !found_inst { instruction = { opname = .UNKNOWN, bytes_read = 1, raw_data = data[idx:idx+1], } append(inst_list, instruction) idx += 1 continue } // Here we check if the instruction affects the next instruction if inst.opname == .LOCK { has_lock = true idx += 1 continue } else if inst.opname == .SEGMENT { reg := (curr_byte & 0b11000) >> 3 has_segment = RegisterId { name = Register(SEGMENT_REGISTER_START+reg) } idx += 1 continue } else if inst.opname == .AAM { processed += 1 } debug_str: string // NOTE: This is a special case because it matches the bit pattern of .TBD5, // but the instruction itself is different if inst.opname == .TBD5 && (data[idx] & 0xFF) == 0b11110110 && (data[idx+1] & 0b00111000) == 0 { inst = test_inst } src_opr: Operand dst_opr: Operand word: bool flip: bool op: Operand if inst.has_flip { flip = curr_byte & 2 != 0 } #partial switch inst.word_size { case .LastBit: word = curr_byte & 1 == 1 case .FourthBit: word = curr_byte & 0b0000_1000 != 0 case .Always16: word = true } dst_opr = parse_operand(inst, inst.dst, data[idx:], &processed, total_bytes_processed, word, has_segment) src_opr = parse_operand(inst, inst.src, data[idx:], &processed, total_bytes_processed, word, has_segment) if flip { src_opr, dst_opr = dst_opr, src_opr } processed += inst.consume_extra_bytes instruction.src = src_opr instruction.dst = dst_opr instruction.is_word = word instruction.bytes_read = processed instruction.raw_data = data[idx:idx+processed] instruction.debug_msg = debug_str instruction.info = inst instruction.has_lock = has_lock instruction.has_segment = has_segment instruction.segment_offset += total_bytes_processed instruction.opname = inst.opname instruction.opname,instruction.indirect_intersegment = get_op(instruction) // fmt.println(parsed_inst) append(inst_list, instruction) idx += processed has_lock = false has_segment = nil total_bytes_processed = idx } return total_bytes_processed }