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