namespace Z80 { public class Disasm { public byte[] Bytes { get; set; } private uint labelCounter = 0; private uint programCounter = 0; private byte currentOp = 0; private string currentInstrName; private string currentPredicate; public string Disassemble() { if (Bytes == null) { throw new System.NullReferenceException(); } string ret = ""; for (int i = 0; i < Bytes.Length; i++) { ret += DisassembleInstruction() + "\n"; } return ret; } public string DisassembleInstruction() { int err; if (programCounter + 1 > Bytes.Length) return "EOS"; err = DisasmInstructionName(); if (programCounter + 1 > Bytes.Length) return "EOS"; if (err == -2) DisasmBitInstructionNames(); DisasmPredicate(); return currentInstrName + " " + currentPredicate; } private int DisasmInstructionName() { currentOp = Bytes[programCounter++]; currentInstrName = ""; switch (currentOp) { case 0: currentInstrName = "NOP"; break; case 0xCB: currentInstrName = "BIT INSTRUCTIONS"; return -2; case 0xDD: currentInstrName = "IX INSTRUCTIONS"; return -3; case 0xED: currentInstrName = "EXTENDED INSTRUCTIONS"; return -4; default: break; } /* The load instruction */ if (currentOp == 0xF9) { currentInstrName = "LD"; } switch (currentOp & 0xF0) { case 0x40: case 0x50: case 0x60: case 0x70: if (currentOp != 0x76) currentInstrName = "LD"; else currentInstrName = "HALT"; break; default: break; } switch ((currentOp) & 0x0F) { case 0x01: case 0x02: case 0x06: case 0x0A: case 0x0E: if (currentOp < 0x80) currentInstrName = "LD"; break; default: break; } if (currentOp >= 0x80 && currentOp <= 0x8F || currentOp == 0xC6) currentInstrName = "ADD"; if (currentOp >= 0x90 && currentOp <= 0x9F || currentOp == 0xD6) currentInstrName = "SUB"; if (currentOp >= 0xA0 && currentOp <= 0xAF || currentOp == 0xE6) currentInstrName = "AND"; if (currentOp >= 0xB0 && currentOp <= 0xBF || currentOp == 0xF6) currentInstrName = "OR"; if (currentOp >= 0x88 && currentOp <= 0x8F || currentOp == 0xCE) currentInstrName = "ADC"; if (currentOp >= 0x98 && currentOp <= 0x9F || currentOp == 0xDE) currentInstrName = "SBC"; if (currentOp >= 0xA8 && currentOp <= 0xAF || currentOp == 0xEE) currentInstrName = "XOR"; if (currentOp >= 0xB8 && currentOp <= 0xBF || currentOp == 0xFE) currentInstrName = "CP"; return 0; } private int DisasmBitInstructionNames() { byte instr = Bytes[programCounter++]; if ((instr & 0x30) != 0) { if (instr <= 0x07) currentInstrName = "RLC"; else if (instr <= 0x0F) currentInstrName = "RRC"; else if (instr <= 0x17) currentInstrName = "RL"; else if (instr <= 0x1F) currentInstrName = "RR"; else if (instr <= 0x27) currentInstrName = "SLA"; else if (instr <= 0x2F) currentInstrName = "SRA"; else if (instr <= 0x37) currentInstrName = "SSL"; else if (instr <= 0x3F) currentInstrName = "SRL"; } switch (instr & 0xF0) { case 0x40: case 0x50: case 0x60: case 0x70: currentInstrName = "BIT"; break; case 0x80: case 0x90: case 0xA0: case 0xB0: currentInstrName = "RES"; break; case 0xC0: case 0xD0: case 0xE0: case 0xF0: currentInstrName = "SET"; break; default: break; } return 0; } public int DisasmPredicate() { currentPredicate = ""; if (currentOp >= 0x40 && currentOp <= 0x47) { currentPredicate = "b, "; } if (currentOp >= 0x50 && currentOp <= 0x57) { currentPredicate = "d, "; } if (currentOp >= 0x60 && currentOp <= 0x67) { currentPredicate = "h, "; } if (currentOp >= 0x70 && currentOp <= 0x77) { currentPredicate = "(hl), "; } if (currentOp >= 0x48 && currentOp <= 0x4F) { currentPredicate = "c, "; } if (currentOp >= 0x58 && currentOp <= 0x5F) { currentPredicate = "e, "; } if (currentOp >= 0x68 && currentOp <= 0x6F) { currentPredicate = "l, "; } if (currentOp >= 0x78 && currentOp <= 0x7F) { currentPredicate = "a, "; } if (currentOp >= 0x40 && currentOp <= 0xBF) { switch (currentOp & 0x0F) { case 0x7: case 0xF: currentPredicate += "a"; break; case 0x0: case 0x8: currentPredicate += "b"; break; case 0x1: case 0x9: currentPredicate += "c"; break; case 0x2: case 0xA: currentPredicate += "d"; break; case 0x3: case 0xB: currentPredicate += "e"; break; case 0x4: case 0xC: currentPredicate += "h"; break; case 0x5: case 0xD: currentPredicate += "l"; break; case 0x6: case 0xE: currentPredicate += "(hl)"; break; default: break; } } return 0; } } }