using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Essgee.Emulation.CPU { public partial class SM83 { static readonly string[] opcodeMnemonicNoPrefix = { /* +00 +01 +02 +03 +04 +05 +06 +07 */ "NOP", "LD BC, 0x{0:X4}", "LD (BC), A", "INC BC", "INC B", "DEC B", "LD B, 0x{0:X2}", "RLCA", /* 0x00 */ "LD (0x{0:X4}), SP", "ADD HL, BC", "LD A, (BC)", "DEC BC", "INC C", "DEC C", "LD C, 0x{0:X2}", "RRCA", /* 0x08 */ "STOP", "LD DE, 0x{0:X4}", "LD (DE), A", "INC DE", "INC D", "DEC D", "LD D, 0x{0:X2}", "RLA", /* 0x10 */ "JR 0x{0:X2}", "ADD HL, DE", "LD A, (DE)", "DEC DE", "INC E", "DEC E", "LD E, 0x{0:X2}", "RRA", /* 0x18 */ "JR NZ, 0x{0:X2}", "LD HL, 0x{0:X4}", "LDI (HL), A", "INC HL", "INC H", "DEC H", "LD H, 0x{0:X2}", "DAA", /* 0x20 */ "JR Z, 0x{0:X2}", "ADD HL, HL", "LDI A, (HL)", "DEC HL", "INC L", "DEC L", "LD L, 0x{0:X2}", "CPL", /* 0x28 */ "JR NC, 0x{0:X2}", "LD SP, 0x{0:X4}", "LDD (HL), A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL), 0x{0:X2}", "SCF", /* 0x30 */ "JR C, 0x{0:X2}", "ADD HL, SP", "LDD A, (HL)", "DEC SP", "INC A", "DEC A", "LD A, 0x{0:X2}", "CCF", /* 0x38 */ "LD B, B", "LD B, C", "LD B, D", "LD B, E", "LD B, H", "LD B, L", "LD B, (HL)", "LD B, A", /* 0x40 */ "LD C, B", "LD C, C", "LD C, D", "LD C, E", "LD C, H", "LD C, L", "LD C, (HL)", "LD C, A", /* 0x48 */ "LD D, B", "LD D, C", "LD D, D", "LD D, E", "LD D, H", "LD D, L", "LD D, (HL)", "LD D, A", /* 0x50 */ "LD E, B", "LD E, C", "LD E, D", "LD E, E", "LD E, H", "LD E, L", "LD E, (HL)", "LD E, A", /* 0x58 */ "LD H, B", "LD H, C", "LD H, D", "LD H, E", "LD H, H", "LD H, L", "LD H, (HL)", "LD H, A", /* 0x60 */ "LD L, B", "LD L, C", "LD L, D", "LD L, E", "LD L, H", "LD L, L", "LD L, (HL)", "LD L, A", /* 0x68 */ "LD (HL), B", "LD (HL), C", "LD (HL), D", "LD (HL), E", "LD (HL), H", "LD (HL), L", "HALT", "LD (HL), A", /* 0x70 */ "LD A, B", "LD A, C", "LD A, D", "LD A, E", "LD A, H", "LD A, L", "LD A, (HL)", "LD A, A", /* 0x78 */ "ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD (HL)", "ADD A", /* 0x80 */ "ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC (HL)", "ADC A", /* 0x88 */ "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", /* 0x90 */ "SBC B", "SBC C", "SBC D", "SBC E", "SBC H", "SBC L", "SBC (HL)", "SBC A", /* 0x98 */ "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", /* 0xA0 */ "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", /* 0xA8 */ "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", /* 0xB0 */ "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", /* 0xB8 */ "RET NZ", "POP BC", "JP NZ, 0x{0:X4}", "JP 0x{0:X4}", "CALL NZ, 0x{0:X4}", "PUSH BC", "ADD 0x{0:X2}", "RST 00", /* 0xC0 */ "RET Z", "RET", "JP Z, 0x{0:X4}", string.Empty, "CALL Z, 0x{0:X4}", "CALL 0x{0:X4}", "ADC 0x{0:X2}", "RST 08", /* 0xC8 */ "RET NC", "POP DE", "JP NC, 0x{0:X4}", ".DB 0xD3", "CALL NC, 0x{0:X4}", "PUSH DE", "SUB 0x{0:X2}", "RST 10", /* 0xD0 */ "RET C", "RETI", "JP C, 0x{0:X4}", ".DB 0xDB", "CALL C, 0x{0:X4}", ".DB 0xDD", "SBC 0x{0:X2}", "RST 18", /* 0xD8 */ "LD (FF00+0x{0:X2}), A", "POP HL", "LD (FF00+C), A", ".DB 0xE3", ".DB 0xE4", "PUSH HL", "AND 0x{0:X2}", "RST 20", /* 0xE0 */ "ADD SP, 0x{0:X2}", "LD PC, HL", "LD (0x{0:X4}), A", ".DB 0xEB", ".DB 0xEC", ".DB 0xED", "XOR 0x{0:X2}", "RST 28", /* 0xE8 */ "LD A, (FF00+0x{0:X2})", "POP AF", "LD A, (FF00+C)", "DI", ".DB 0xF4", "PUSH AF", "OR 0x{0:X2}", "RST 30", /* 0xF0 */ "LD HL, SP+0x{0:X2}", "LD SP, HL", "LD A, (0x{0:X4})", "EI", ".DB 0xFC", ".DB 0xFD", "CP 0x{0:X2}", "RST 38" /* 0xF8 */ }; static readonly int[] opcodeLengthNoPrefix = { 1, 3, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 2, 1, 1, 1, 3, 2, 3, 3, 2, 1, 1, 1, 3, 1, 3, 1, 2, 1, 1, 1, 3, 1, 3, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 1, 1, 2, 1 }; static readonly string[] opcodeMnemonicPrefixCB = new string[] { /* +00 +01 +02 +03 +04 +05 +06 +07 */ "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", /* 0x00 */ "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", /* 0x08 */ "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", /* 0x10 */ "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", /* 0x18 */ "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", /* 0x20 */ "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", /* 0x28 */ "SWAP B", "SWAP C", "SWAP D", "SWAP E", "SWAP H", "SWAP L", "SWAP (HL)", "SWAP A", /* 0x30 */ "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", /* 0x38 */ "BIT 0, B", "BIT 0, C", "BIT 0, D", "BIT 0, E", "BIT 0, H", "BIT 0, L", "BIT 0, (HL)", "BIT 0, A", /* 0x40 */ "BIT 1, B", "BIT 1, C", "BIT 1, D", "BIT 1, E", "BIT 1, H", "BIT 1, L", "BIT 1, (HL)", "BIT 1, A", /* 0x48 */ "BIT 2, B", "BIT 2, C", "BIT 2, D", "BIT 2, E", "BIT 2, H", "BIT 2, L", "BIT 2, (HL)", "BIT 2, A", /* 0x50 */ "BIT 3, B", "BIT 3, C", "BIT 3, D", "BIT 3, E", "BIT 3, H", "BIT 3, L", "BIT 3, (HL)", "BIT 3, A", /* 0x58 */ "BIT 4, B", "BIT 4, C", "BIT 4, D", "BIT 4, E", "BIT 4, H", "BIT 4, L", "BIT 4, (HL)", "BIT 4, A", /* 0x60 */ "BIT 5, B", "BIT 5, C", "BIT 5, D", "BIT 5, E", "BIT 5, H", "BIT 5, L", "BIT 5, (HL)", "BIT 5, A", /* 0x68 */ "BIT 6, B", "BIT 6, C", "BIT 6, D", "BIT 6, E", "BIT 6, H", "BIT 6, L", "BIT 6, (HL)", "BIT 6, A", /* 0x70 */ "BIT 7, B", "BIT 7, C", "BIT 7, D", "BIT 7, E", "BIT 7, H", "BIT 7, L", "BIT 7, (HL)", "BIT 7, A", /* 0x78 */ "RES 0, B", "RES 0, C", "RES 0, D", "RES 0, E", "RES 0, H", "RES 0, L", "RES 0, (HL)", "RES 0, A", /* 0x80 */ "RES 1, B", "RES 1, C", "RES 1, D", "RES 1, E", "RES 1, H", "RES 1, L", "RES 1, (HL)", "RES 1, A", /* 0x88 */ "RES 2, B", "RES 2, C", "RES 2, D", "RES 2, E", "RES 2, H", "RES 2, L", "RES 2, (HL)", "RES 2, A", /* 0x90 */ "RES 3, B", "RES 3, C", "RES 3, D", "RES 3, E", "RES 3, H", "RES 3, L", "RES 3, (HL)", "RES 3, A", /* 0x98 */ "RES 4, B", "RES 4, C", "RES 4, D", "RES 4, E", "RES 4, H", "RES 4, L", "RES 4, (HL)", "RES 4, A", /* 0xA0 */ "RES 5, B", "RES 5, C", "RES 5, D", "RES 5, E", "RES 5, H", "RES 5, L", "RES 5, (HL)", "RES 5, A", /* 0xA8 */ "RES 6, B", "RES 6, C", "RES 6, D", "RES 6, E", "RES 6, H", "RES 6, L", "RES 6, (HL)", "RES 6, A", /* 0xB0 */ "RES 7, B", "RES 7, C", "RES 7, D", "RES 7, E", "RES 7, H", "RES 7, L", "RES 7, (HL)", "RES 7, A", /* 0xB8 */ "SET 0, B", "SET 0, C", "SET 0, D", "SET 0, E", "SET 0, H", "SET 0, L", "SET 0, (HL)", "SET 0, A", /* 0xC0 */ "SET 1, B", "SET 1, C", "SET 1, D", "SET 1, E", "SET 1, H", "SET 1, L", "SET 1, (HL)", "SET 1, A", /* 0xC8 */ "SET 2, B", "SET 2, C", "SET 2, D", "SET 2, E", "SET 2, H", "SET 2, L", "SET 2, (HL)", "SET 2, A", /* 0xD0 */ "SET 3, B", "SET 3, C", "SET 3, D", "SET 3, E", "SET 3, H", "SET 3, L", "SET 3, (HL)", "SET 3, A", /* 0xD8 */ "SET 4, B", "SET 4, C", "SET 4, D", "SET 4, E", "SET 4, H", "SET 4, L", "SET 4, (HL)", "SET 4, A", /* 0xE0 */ "SET 5, B", "SET 5, C", "SET 5, D", "SET 5, E", "SET 5, H", "SET 5, L", "SET 5, (HL)", "SET 5, A", /* 0xE8 */ "SET 6, B", "SET 6, C", "SET 6, D", "SET 6, E", "SET 6, H", "SET 6, L", "SET 6, (HL)", "SET 6, A", /* 0xF0 */ "SET 7, B", "SET 7, C", "SET 7, D", "SET 7, E", "SET 7, H", "SET 7, L", "SET 7, (HL)", "SET 7, A" /* 0xF8 */ }; static readonly int[] opcodeLengthPrefixCB = new int[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; public static string PrintRegisters(SM83 cpu) { return $"AF:{cpu.af.Word:X4} BC:{cpu.bc.Word:X4} DE:{cpu.de.Word:X4} HL:{cpu.hl.Word:X4} SP:{cpu.sp:X4}"; } public static string PrintFlags(SM83 cpu) { return $"{(cpu.IsFlagSet(Flags.Zero) ? "Z" : "-")}{(cpu.IsFlagSet(Flags.Subtract) ? "N" : "-")}{(cpu.IsFlagSet(Flags.HalfCarry) ? "H" : "-")}{(cpu.IsFlagSet(Flags.Carry) ? "C" : "-")}"; } public static string PrintInterrupt(SM83 cpu) { var intFlags = (InterruptSource)cpu.memoryReadDelegate(0xFF0F); var intEnable = (InterruptSource)cpu.memoryReadDelegate(0xFFFF); var intFlagsString = $"{((intFlags & InterruptSource.VBlank) != 0 ? "V" : "-")}" + $"{((intFlags & InterruptSource.LCDCStatus) != 0 ? "L" : "-")}" + $"{((intFlags & InterruptSource.TimerOverflow) != 0 ? "T" : "-")}" + $"{((intFlags & InterruptSource.SerialIO) != 0 ? "S" : "-")}" + $"{((intFlags & InterruptSource.Keypad) != 0 ? "K" : "-")}"; var intEnableString = $"{((intEnable & InterruptSource.VBlank) != 0 ? "V" : "-")}" + $"{((intEnable & InterruptSource.LCDCStatus) != 0 ? "L" : "-")}" + $"{((intEnable & InterruptSource.TimerOverflow) != 0 ? "T" : "-")}" + $"{((intEnable & InterruptSource.SerialIO) != 0 ? "S" : "-")}" + $"{((intEnable & InterruptSource.Keypad) != 0 ? "K" : "-")}"; return $"IME:{(cpu.ime ? "1" : "0")} IF:{intFlagsString} IE:{intEnableString}"; } public static string DisassembleOpcode(SM83 cpu, ushort address) { var opcode = DisassembleGetOpcodeBytes(cpu, address); return $"0x{address:X4} | {DisassembleMakeByteString(cpu, opcode).PadRight(15)} | {DisassembleMakeMnemonicString(cpu, opcode)}"; } public static byte[] DisassembleGetOpcodeBytes(SM83 cpu, ushort address) { var opcode = new byte[3]; for (int i = 0; i < opcode.Length; i++) opcode[i] = address + i <= 0xFFFF ? cpu.memoryReadDelegate((ushort)(address + i)) : (byte)0; return opcode; } public static int DisassembleGetOpcodeLen(SM83 cpu, byte[] opcode) { if (opcode[0] == 0xCB) return opcodeLengthPrefixCB[opcode[1]]; else return opcodeLengthNoPrefix[opcode[0]]; } public static string DisassembleMakeByteString(SM83 cpu, byte[] opcode) { return string.Join(" ", opcode.Select(x => $"{x:X2}").Take(DisassembleGetOpcodeLen(cpu, opcode))); } public static string DisassembleMakeMnemonicString(SM83 cpu, byte[] opcode) { var len = DisassembleGetOpcodeLen(cpu, opcode); var start = opcode[0] == 0xCB ? 1 : 0; var mnemonics = opcode[0] == 0xCB ? opcodeMnemonicPrefixCB : opcodeMnemonicNoPrefix; switch (len - start) { case 1: return mnemonics[opcode[start]]; case 2: return string.Format(mnemonics[opcode[start]], opcode[start + 1]); case 3: return string.Format(mnemonics[opcode[start]], (opcode[start + 2] << 8 | opcode[start + 1])); default: return string.Empty; } } private string MakeUnimplementedOpcodeString(string prefix, ushort address) { var opcode = DisassembleGetOpcodeBytes(this, address); return $"Unimplemented {(prefix != string.Empty ? prefix + " " : prefix)}opcode {DisassembleMakeByteString(this, opcode)} ({DisassembleMakeMnemonicString(this, opcode)})"; } } }