195 lines
6.0 KiB
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
|
|
}
|
|
}
|