243 lines
7.7 KiB
Odin
243 lines
7.7 KiB
Odin
package sim_8086
|
|
|
|
import "core:fmt"
|
|
import "core:math"
|
|
import "core:strings"
|
|
|
|
instruction_builder := strings.builder_make()
|
|
|
|
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
|
|
}
|
|
|
|
get_memory_string :: proc(memoryAddr: MemoryAddr, has_segment: Maybe(Register)) -> 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))
|
|
}
|
|
}
|
|
seg_string: string
|
|
if segreg, ok := has_segment.?; ok {
|
|
seg_string = fmt.aprintf("%s:", segreg.fullname)
|
|
}
|
|
text := fmt.aprintf("%s[%s%s]", seg_string, calculate_effective_address(memoryAddr.addr_id), disp)
|
|
return text
|
|
}
|
|
|
|
get_displacement_string :: proc(displacement: Displacement) -> 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
|
|
}
|
|
|
|
get_opname :: proc(inst: Instruction) -> (string, bool) {
|
|
name: string
|
|
interseg: bool
|
|
if inst.opname == .TBD2 {
|
|
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
|
case 0b000: name = "inc"
|
|
case 0b001: name = "dec"
|
|
case 0b010: name = "call"
|
|
// TODO: We really have to fix this because we shouldn't be figuring out if this
|
|
// is an intersegment here
|
|
case 0b011: name = "call"; interseg = true
|
|
case 0b100: name = "jmp"
|
|
case 0b101: name = "jmp"; interseg = true
|
|
case 0b110: name = "push"
|
|
}
|
|
} else if inst.opname == .TBD5 {
|
|
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
|
case 0b000: name = "test"
|
|
case 0b001: name = "dec"
|
|
case 0b010: name = "not"
|
|
case 0b011: name = "neg"
|
|
case 0b100: name = "mul"
|
|
case 0b101: name = "imul"
|
|
case 0b110: name = "div"
|
|
case 0b111: name = "idiv"
|
|
}
|
|
} else if inst.opname == .TBD6 {
|
|
switch inst.raw_data[1] & 0b00111000 >> 3 {
|
|
case 0b000: name = "rol"
|
|
case 0b001: name = "ror"
|
|
case 0b010: name = "rcl"
|
|
case 0b011: name = "rcr"
|
|
case 0b100: name = "shl"
|
|
case 0b101: name = "shr"
|
|
case 0b111: name = "sar"
|
|
}
|
|
} else {
|
|
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: name = "add"
|
|
case 0b001: name = "or"
|
|
case 0b010: name = "adc"
|
|
case 0b011: name = "sbb"
|
|
case 0b100: name = "and"
|
|
case 0b101: name = "sub"
|
|
case 0b110: name = "xor"
|
|
case 0b111: name = "cmp"
|
|
}
|
|
}
|
|
return name, interseg
|
|
}
|
|
|
|
get_operand_string :: proc(operand: Operand, is_word: bool, has_segment: Maybe(Register)) -> string {
|
|
string_val: string
|
|
switch val in operand {
|
|
case None:
|
|
string_val = ""
|
|
case RegisterId:
|
|
string_val = is_word ? registers[val].fullname : registers[val].bytename
|
|
case Immediate8, ImmediateU8, Immediate16, DirectWithinSegment:
|
|
string_val = fmt.aprintf("%d", val)
|
|
case MemoryAddr:
|
|
string_val = get_memory_string(val, has_segment)
|
|
case DirectAddress:
|
|
seg_string: string
|
|
if segreg, ok := has_segment.?; ok {
|
|
seg_string = fmt.aprintf("%s:", segreg.fullname)
|
|
}
|
|
string_val = fmt.aprintf("%s[%d]", seg_string, val)
|
|
case SegmentRegister:
|
|
string_val = segment_registers[val].fullname
|
|
case Jump:
|
|
string_val = fmt.aprintf("$%s%d", val >= 0 ? "+" : "", val)
|
|
case VariablePort:
|
|
string_val = variable_port.fullname
|
|
case ShiftRotate:
|
|
string_val = val ? registers[1].bytename : "1"
|
|
case Repeat:
|
|
string_val = (string)(val)
|
|
case Intersegment:
|
|
string_val = fmt.aprintf("%d:%d", val.cs, val.ip)
|
|
}
|
|
return string_val
|
|
}
|
|
|
|
get_unknown_inst_string :: proc(inst: Instruction) -> string {
|
|
print_at_end := false
|
|
txt := "unknown instruction"
|
|
line := fmt.aprintf("%s %*[1]s %8b", txt, RIGHT_ALIGN_AMOUNT - len(txt), ";;", inst.raw_data[0])
|
|
return line
|
|
}
|
|
|
|
get_instruction_string :: proc(inst_info: InstructionInfo, instruction: Instruction) {
|
|
inst := instruction
|
|
src_is_imm := operand_is(Immediate8, inst.src) || operand_is(Immediate16, inst.src)
|
|
dst_is_bracketed := operand_is(MemoryAddr, inst.dst) || operand_is(DirectAddress, inst.dst)
|
|
src_is_bracketed := operand_is(MemoryAddr, inst.src) || operand_is(DirectAddress, inst.src)
|
|
shiftrot := operand_is(ShiftRotate, inst.src)
|
|
size_string := ""
|
|
if ((src_is_imm && dst_is_bracketed) || (dst_is_bracketed && shiftrot)) || (src_is_bracketed && operand_is(None, inst.dst)) {
|
|
size_string = inst.is_word ? "word " : "byte "
|
|
}
|
|
|
|
if inst.has_lock {
|
|
fmt.sbprint(&instruction_builder, "lock ")
|
|
}
|
|
|
|
dst_str := get_operand_string(inst.dst, inst.is_word, inst.has_segment)
|
|
src_str := get_operand_string(inst.src, inst.is_word, inst.has_segment)
|
|
opname: string
|
|
is_interseg: bool
|
|
if inst_info.check_second_encoding {
|
|
opname,is_interseg = get_opname(inst)
|
|
} else {
|
|
// TODO: Do the RTTI thing here with reflection
|
|
opname = strings.to_lower(fmt.aprintf("%s", inst.opname))
|
|
}
|
|
|
|
if dst_str == "" {
|
|
interseg_string: string
|
|
if is_interseg {
|
|
interseg_string = " far"
|
|
}
|
|
fmt.sbprintf(&instruction_builder, "%s%s %s%s", opname, interseg_string, size_string, src_str)
|
|
} else {
|
|
// note: i don't know why this is the case, but only the move has the word/byte
|
|
// keyword next to the immediate, but other instructions have it on the memory address
|
|
if opname == "mov" {
|
|
fmt.sbprintf(&instruction_builder, "%s %s, %s%s", opname, dst_str, size_string, src_str)
|
|
} else {
|
|
fmt.sbprintf(&instruction_builder, "%s %s%s, %s", opname, size_string, dst_str, src_str)
|
|
}
|
|
}
|
|
|
|
// Prepare padding and comment to add debug info
|
|
b_len := strings.builder_len(instruction_builder)
|
|
fmt.sbprintf(&instruction_builder, "%*[0]s", RIGHT_ALIGN_AMOUNT - b_len, ";;")
|
|
|
|
if inst.has_lock {
|
|
fmt.sbprintf(&instruction_builder, " lock")
|
|
}
|
|
if _,ok := inst.has_segment.?; ok {
|
|
fmt.sbprintf(&instruction_builder, " segment")
|
|
}
|
|
for i in 0..<inst.bytes_read {
|
|
fmt.sbprintf(&instruction_builder, " %08b", inst.raw_data[i])
|
|
}
|
|
}
|
|
|
|
print_instructions_stdout :: proc(instructions: []Instruction) {
|
|
last_opname: [3]byte
|
|
repeating_op_count := 0
|
|
fmt.println("bits 16\n")
|
|
for inst in instructions {
|
|
strings.builder_reset(&instruction_builder)
|
|
get_instruction_string(inst.info, inst)
|
|
op2 := strings.to_string(instruction_builder)
|
|
if op2[0:3] != string(last_opname[:]) {
|
|
if repeating_op_count > 0 {
|
|
fmt.println()
|
|
}
|
|
repeating_op_count = 0
|
|
} else {
|
|
repeating_op_count += 1
|
|
}
|
|
copy(last_opname[:], op2[0:3])
|
|
fmt.println(op2)
|
|
}
|
|
}
|