Skip to content

Instantly share code, notes, and snippets.

@nzrsky
Created June 28, 2024 20:27
Show Gist options
  • Select an option

  • Save nzrsky/0c605c1451a1fe6bfddab2b10e47a446 to your computer and use it in GitHub Desktop.

Select an option

Save nzrsky/0c605c1451a1fe6bfddab2b10e47a446 to your computer and use it in GitHub Desktop.
import Foundation
let args = ProcessInfo.processInfo.arguments
guard let filename = args.dropFirst().last.map ({
if let url = URL(string: $0), url.scheme != nil { url }
else { URL(fileURLWithPath: $0) }
}) else {
printe("usage: \(args[0]) FILE")
exit(EXIT_FAILURE)
}
do {
try disasm(filename)
} catch {
printe("error: \(filename.path): \(error)")
exit(EXIT_FAILURE)
}
exit(EXIT_SUCCESS)
func disasm(_ filename: URL) throws {
print("; \(filename.path)\n")
print("bits 16\n")
try Data(contentsOf: filename).withUnsafeBytes { (ptr: UnsafeRawBufferPointer) in
var offset = 0
while offset < ptr.count {
let inst = ptr.load(fromByteOffset: offset, as: RawInst.self)
print(try decode(inst))
offset += RawInst.bitWidth / 8
}
}
}
typealias RawInst = UInt16
enum Inst {
enum Opcode: UInt8 {
case mov = 0b100010
}
enum Mod {
typealias RawValue = UInt8
static let mem0: RawValue = 0b00
static let mem8: RawValue = 0b01
static let mem16: RawValue = 0b10
static let reg: RawValue = 0b11
}
enum Width {
typealias RawValue = UInt8
static let byte: RawValue = 0b0
static let word: RawValue = 0b1
}
enum Dir {
typealias RawValue = UInt8
static let toReg: RawValue = 0b0
static let fromReg: RawValue = 0b1
}
enum Reg {
static let table: [[String]] = [
["al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"], // w = 0
["ax", "cx", "dx", "bx", "sp", "bp", "si", "di"] // w = 1
]
static func decode(uint8: UInt8, width: Width.RawValue) -> String {
table[Int(width)][Int(uint8)]
}
}
enum DecodingError: Error {
case unknownInstruction(String)
case unimplemented(String)
}
}
func decode(_ inst: RawInst) throws -> String {
let b0 = UInt8(inst & 0xff), b1 = UInt8(inst >> 8)
let w = b0 & 0b01
let d = (b0 & 0b10) >> 1
let mod = b1 >> 6
let reg = (b1 >> 3) & 0b00_111
let rm = b1 & 0b00_000_111
let binInst = "\(String(b0, radix: 2)) \(String(b1, radix: 2))"
let mnemo = try { () throws -> String in
switch b0 >> 2 {
case Inst.Opcode.mov.rawValue:
let op1 = Inst.Reg.decode(uint8: reg, width: w)
switch mod {
case Inst.Mod.reg:
let op2 = Inst.Reg.decode(uint8: rm, width: w)
let ops = d == Inst.Dir.fromReg ? (op1, op2) : (op2, op1)
return "mov \(ops.0), \(ops.1)"
default:
throw Inst.DecodingError.unimplemented("inst(\(binInst), mod = \(mod)")
}
default:
throw Inst.DecodingError.unknownInstruction(binInst)
}
}()
return "\(mnemo.padded(to: 30)); \(binInst)"
}
func printe(_ msg: String) {
FileHandle.standardError.write((msg + "\n").data(using: .utf8) ?? .init())
}
extension String {
func padded(to length: Int, pad: String = " ") -> String {
self + String(repeating: pad, count: max(0, length - count))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment