performance-aware/decoding.odin

195 lines
6.0 KiB
Odin

package sim_8086
import "core:fmt"
import "core:math"
import "core:strings"
parse_operand :: proc(inst: InstructionInfo, opinfo: OperandInfo, data: []u8, processed: ^int, word: bool, has_segreg: Maybe(Register)) -> Operand {
operand: Operand = None{}
switch opinfo {
case .None:
case .Register:
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
}
operand = (RegisterId)(registers[reg].code)
case .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
}
operand = (SegmentRegister)(segment_registers[reg].code)
case .RegisterMemory:
mod := data[1] >> 6
rm := data[1] & 0b111
processed^ += 1
op: Operand
if mod == 0 {
if rm == 0b110 {
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 {
op = (RegisterId)(registers[rm].code)
}
operand = op
case .Immediate:
data_idx := processed^
word_signed := word
if inst.has_sign_extension {
word_signed &&= data[0] & 0b0000_0010 == 0
}
operand = (Operand)(word_signed ? (Immediate16)(get_i16(data[data_idx:])) : (Immediate8)(data[data_idx]))
processed^ += word_signed ? 2 : 1
case .ImmediateUnsigned:
operand = (ImmediateU8)(data[processed^])
processed^ += 1
case .Accumulator:
operand = (RegisterId)(registers[0].code)
case .DirectAddress:
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 = VariablePort{}
case .ShiftRotate:
v_flag := data[0] & 0b10 != 0
operand = (ShiftRotate)(v_flag)
case .Repeat:
operand = get_repeat_op(data[1])
processed^ += 1
case .DirectWithinSegment:
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
operand = (DirectWithinSegment)(value)
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) {
idx := 0
has_segment: Maybe(Register)
has_lock: bool
for idx < bytes_to_read {
instruction: Instruction
processed := 1
curr_byte := data[idx]
inst, ok := try_find_instruction(curr_byte)
if !ok {
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 = segment_registers[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
indirect_intersegment: 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, word, has_segment)
src_opr = parse_operand(inst, inst.src, data[idx:], &processed, word, has_segment)
if flip {
src_opr, dst_opr = dst_opr, src_opr
}
processed += inst.consume_extra_bytes
instruction.opname = inst.opname
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
// fmt.println(parsed_inst)
append(inst_list, instruction)
idx += processed
has_lock = false
has_segment = nil
total_bytes_processed = idx
}
}