///|
fn hex_digit(n : Int) -> String {
let d = n & 0xF
if d < 10 {
(48 + d).unsafe_to_char().to_string()
} else {
(87 + d).unsafe_to_char().to_string()
}
}
///|
fn hex_byte(n : Int) -> String {
hex_digit((n >> 4) & 0xF) + hex_digit(n & 0xF)
}
///|
fn int_to_hex(n : Int) -> String {
if n == 0 {
"0"
} else {
let digits : Array[String] = []
let mut value = n
while value > 0 {
digits.push(hex_digit(value & 0xF))
value = value >> 4
}
digits.rev_in_place()
digits.join("")
}
}
///|
fn hex_offset(n : Int, width : Int) -> String {
let hex = int_to_hex(n)
if hex.length() >= width {
hex
} else {
"0".repeat(width - hex.length()) + hex
}
}
///|
fn disasm_width(code_size : Int) -> Int {
let max_offset = if code_size > 0 { code_size - 1 } else { 0 }
if max_offset <= 0xFFFF {
4
} else if max_offset <= 0xFFFFFF {
6
} else {
8
}
}
///|
pub fn format_disassembly(
code : Array[Int],
label_offsets : Array[(Int, Int)],
annotations : Array[(Int, String)],
) -> String {
let label_map : Map[Int, Array[Int]] = Map([])
for entry in label_offsets {
let (block_id, offset) = entry
match label_map.get(offset) {
Some(ids) => ids.push(block_id)
None => label_map.set(offset, [block_id])
}
}
let width = disasm_width(code.length())
let sorted = annotations.copy()
sorted.sort_by(fn(a, b) { a.0.compare(b.0) })
let mut result = ""
for entry in sorted {
let (offset, text) = entry
if label_map.get(offset) is Some(block_ids) {
for block_id in block_ids {
result = result + "block\{block_id}:\n"
}
}
let b0 = if offset < code.length() { code[offset] } else { 0 }
let b1 = if offset + 1 < code.length() { code[offset + 1] } else { 0 }
let b2 = if offset + 2 < code.length() { code[offset + 2] } else { 0 }
let b3 = if offset + 3 < code.length() { code[offset + 3] } else { 0 }
let hex = hex_byte(b0) + hex_byte(b1) + hex_byte(b2) + hex_byte(b3)
result = result + " \{hex_offset(offset, width)}: \{hex} \{text}\n"
}
result
}