187 lines
6.2 KiB
Odin
187 lines
6.2 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, .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){id = SEGMENT_REGISTER_START + (int)(reg), access = .Full}
|
|
} else if word {
|
|
operand = RegisterId { id = (int)(reg), access = .Full }
|
|
} else {
|
|
operand = RegisterId { id = (int)(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 { id = (int)(rm), access = .Full }
|
|
} else {
|
|
op = RegisterId { id = (int)(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: u16 = word_signed ? u16(get_i16(data[data_idx:])) : u16(data[data_idx])
|
|
operand = Immediate { value = value, size = word_signed ? .Signed16 : .Signed8 }
|
|
processed^ += word_signed ? 2 : 1
|
|
case .ImmediateUnsigned:
|
|
operand = Immediate { value = u16(data[processed^]), size = .Unsigned8 }
|
|
processed^ += 1
|
|
case .Accumulator:
|
|
operand = RegisterId { id = 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 { id = (int)(variable_port.code), access = .Full }
|
|
case .ShiftRotate:
|
|
v_flag := data[0] & 0b10 != 0
|
|
operand = v_flag ? RegisterId { id = 1, access = .Low } : Immediate { value = 1 }
|
|
case .Repeat:
|
|
operand = get_repeat_op(data[1])
|
|
processed^ += 1
|
|
case .DirectWithinSegment:
|
|
value := (int)(get_i16(data[1:])) + total_bytes_processed + 3
|
|
operand = Immediate { value = u16(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) {
|
|
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 = registers[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, 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
|
|
}
|
|
}
|