performance-aware/printing.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)
}
}