202 lines
6.1 KiB
Odin
202 lines
6.1 KiB
Odin
package sim_8086
|
|
|
|
import "core:fmt"
|
|
import "core:math"
|
|
import "core:strings"
|
|
import "core:reflect"
|
|
|
|
instruction_builder := strings.builder_make()
|
|
|
|
RIGHT_ALIGN_AMOUNT := 35
|
|
|
|
operand_is :: proc($T: typeid, opr: Operand) -> bool {
|
|
_, ok := opr.(T)
|
|
return ok
|
|
}
|
|
|
|
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_register_name :: proc(reg_id: RegisterId) -> string {
|
|
low_names := [?]string{"al", "cl", "dl", "bl"}
|
|
high_names := [?]string{"ah", "ch", "dh", "bh"}
|
|
switch reg_id.access {
|
|
case .Full: return registers[reg_id.id].fullname
|
|
case .Low: return low_names[reg_id.id]
|
|
case .High: return high_names[reg_id.id % 4]
|
|
}
|
|
return ""
|
|
}
|
|
|
|
get_operand_string :: proc(operand: Operand, has_segment: Maybe(Register)) -> string {
|
|
string_val: string
|
|
switch val in operand {
|
|
case None:
|
|
string_val = ""
|
|
case RegisterId:
|
|
string_val = get_register_name(val)
|
|
case Immediate:
|
|
switch val.size {
|
|
case .Signed8:
|
|
string_val = fmt.aprintf("%d", i8(val.value))
|
|
case .Unsigned8:
|
|
string_val = fmt.aprintf("%d", u8(val.value))
|
|
case .Signed16:
|
|
string_val = fmt.aprintf("%d", i16(val.value))
|
|
}
|
|
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 Jump:
|
|
string_val = fmt.aprintf("$%s%d", val >= 0 ? "+" : "", val)
|
|
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(Immediate, 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 := inst_info.opname == .TBD6
|
|
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.has_segment)
|
|
src_str := get_operand_string(inst.src, inst.has_segment)
|
|
opname: string
|
|
is_interseg: bool
|
|
|
|
opname = strings.to_lower(reflect.enum_string(inst.opname))
|
|
|
|
if dst_str == "" {
|
|
interseg_string: string
|
|
if instruction.indirect_intersegment {
|
|
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)
|
|
}
|
|
}
|