/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

#ifdef DEBUGGER

#include <stdarg.h>
#include "snes9x.h"
#include "memmap.h"
#include "cpuops.h"
#include "dma.h"
#include "apu/apu.h"
#include "display.h"
#include "debug.h"
#include "missing.h"

#include "apu/bapu/snes/snes.hpp"

extern SDMA	DMA[8];
extern FILE	*apu_trace;
FILE		*trace = NULL, *trace2 = NULL;

struct SBreakPoint	S9xBreakpoint[6];

struct SDebug
{
	struct
	{
		uint8	Bank;
		uint16	Address;
	}	Dump;

	struct
	{
		uint8	Bank;
		uint16	Address;
	}	Unassemble;
};

static struct SDebug	Debug = { { 0, 0 }, { 0, 0 } };

static const char	*HelpMessage[] =
{
	"Command Help:",
	"?, help                - Shows this command help",
	"r                      - Shows the registers",
	"i                      - Shows the interrupt vectors",
	"t                      - Trace current instruction   [step-into]",
	"p                      - Proceed to next instruction [step-over]",
	"s                      - Skip to next instruction    [skip]",
	"T                      - Toggle CPU instruction tracing to trace.log",
	"TS                     - Toggle SA-1 instruction tracing to trace_sa1.log",
	"E                      - Toggle HC-based event tracing to trace.log",
	"V                      - Toggle non-DMA V-RAM read/write tracing to stdout",
	"D                      - Toggle on-screen DMA tracing",
	"H                      - Toggle on-screen HDMA tracing",
	"U                      - Toggle on-screen unknown register read/write tracing",
	"P                      - Toggle on-screen DSP tracing",
	"S                      - Dump sprite (OBJ) status",
	"g [Address]            - Go or go to [Address]",
	"u [Address]            - Disassemble from PC or [Address]",
	"d [Address]            - Dump from PC or [Address]",
	"bv [Number]            - View breakpoints or view breakpoint [Number]",
	"bs [Number] [Address]  - Enable/disable breakpoint",
	"                         [enable example: bs #2 $02:8002]",
	"                         [disable example: bs #2]",
	"c                      - Dump SNES colour palette",
	"W                      - Show what SNES hardware features the ROM is using",
	"                         which might not be implemented yet",
	"w                      - Show some SNES hardware features used so far in this frame",
	"R                      - Reset SNES",
	"q                      - Quit emulation",
//	"ai                     - Shou APU vectors",
//	"a                      - Show APU status",
//	"x                      - Show Sound DSP status",
	"A                      - Toggle APU instruction tracing to trace.log",
//	"B                      - Toggle sound DSP register tracing to aputrace.log",
//	"C                      - Dump sound sample addresses",
//	"ad [Address]           - Dump APU RAM from PC or [Address]",
	"",
	"[Address]              - $Bank:Address or $Address",
	"                         [for example: $01:8123]",
	"[Number]               - #Number",
	"                         [for example: #1]",
	"z                      - ",
	"f                      - ",
	"dump                   - ",
	"",
	NULL
};

static const char	*S9xMnemonics[256] =
{
	"BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA",
	"PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA",
	"BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA",
	"CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA",
	"JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND",
	"PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND",
	"BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND",
	"SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND",
	"RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR",
	"PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR",
	"BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR",
	"CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR",
	"RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC",
	"PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC",
	"BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC",
	"SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC",
	"BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA",
	"DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA",
	"BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA",
	"TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA",
	"LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA",
	"TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA",
	"BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA",
	"CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA",
	"CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP",
	"INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP",
	"BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP",
	"CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP",
	"CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC",
	"INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC",
	"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC",
	"SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC"
};

static int	AddrModes[256] =
{
  // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
	 3, 10,  3, 19,  6,  6,  6, 12,  0,  1, 24,  0, 14, 14, 14, 17, // 0
	 4, 11,  9, 20,  6,  7,  7, 13,  0, 16, 24,  0, 14, 15, 15, 18, // 1
	14, 10, 17, 19,  6,  6,  6, 12,  0,  1, 24,  0, 14, 14, 14, 17, // 2
	 4, 11,  9, 20,  7,  7,  7, 13,  0, 16, 24,  0, 15, 15, 15, 18, // 3
	 0, 10,  3, 19, 25,  6,  6, 12,  0,  1, 24,  0, 14, 14, 14, 17, // 4
	 4, 11,  9, 20, 25,  7,  7, 13,  0, 16,  0,  0, 17, 15, 15, 18, // 5
	 0, 10,  5, 19,  6,  6,  6, 12,  0,  1, 24,  0, 21, 14, 14, 17, // 6
	 4, 11,  9, 20,  7,  7,  7, 13,  0, 16,  0,  0, 23, 15, 15, 18, // 7
	 4, 10,  5, 19,  6,  6,  6, 12,  0,  1,  0,  0, 14, 14, 14, 17, // 8
	 4, 11,  9, 20,  7,  7,  8, 13,  0, 16,  0,  0, 14, 15, 15, 18, // 9
	 2, 10,  2, 19,  6,  6,  6, 12,  0,  1,  0,  0, 14, 14, 14, 17, // A
	 4, 11,  9, 20,  7,  7,  8, 13,  0, 16,  0,  0, 15, 15, 16, 18, // B
	 2, 10,  3, 19,  6,  6,  6, 12,  0,  1,  0,  0, 14, 14, 14, 17, // C
	 4, 11,  9,  9, 27,  7,  7, 13,  0, 16,  0,  0, 22, 15, 15, 18, // D
	 2, 10,  3, 19,  6,  6,  6, 12,  0,  1,  0,  0, 14, 14, 14, 17, // E
	 4, 11,  9, 20, 26,  7,  7, 13,  0, 16,  0,  0, 23, 15, 15, 18  // F
};

static uint8 S9xDebugGetByte (uint32);
static uint16 S9xDebugGetWord (uint32);
static uint8 S9xDebugSA1GetByte (uint32);
static uint16 S9xDebugSA1GetWord (uint32);
static uint8 debug_cpu_op_print (char *, uint8, uint16);
static uint8 debug_sa1_op_print (char *, uint8, uint16);
static void debug_line_print (const char *);
static int debug_get_number (char *, uint16 *);
static short debug_get_start_address (char *, uint8 *, uint32 *);
static void debug_print_window (uint8 *);
static const char * debug_clip_fn (int);
static void debug_whats_used (void);
static void debug_whats_missing (void);


static uint8 S9xDebugGetByte (uint32 Address)
{
	int		block = (Address & 0xffffff) >> MEMMAP_SHIFT;
	uint8	*GetAddress = Memory.Map[block];
	uint8	byte = 0;

	if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
	{
		byte = *(GetAddress + (Address & 0xffff));
		return (byte);
	}

	switch ((pint) GetAddress)
	{
		case CMemory::MAP_LOROM_SRAM:
		case CMemory::MAP_SA1RAM:
			byte = *(Memory.SRAM + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Memory.SRAMMask));
			return (byte);

		case CMemory::MAP_LOROM_SRAM_B:
			byte = *(Multi.sramB + ((((Address & 0xff0000) >> 1) | (Address & 0x7fff)) & Multi.sramMaskB));
			return (byte);

		case CMemory::MAP_HIROM_SRAM:
		case CMemory::MAP_RONLY_SRAM:
			byte = *(Memory.SRAM + (((Address & 0x7fff) - 0x6000 + ((Address & 0xf0000) >> 3)) & Memory.SRAMMask));
			return (byte);

		case CMemory::MAP_BWRAM:
			byte = *(Memory.BWRAM + ((Address & 0x7fff) - 0x6000));
			return (byte);

		default:
			return (byte);
	}
}

static uint16 S9xDebugGetWord (uint32 Address)
{
	uint16	word;

	word  = S9xDebugGetByte(Address);
	word |= S9xDebugGetByte(Address + 1) << 8;

	return (word);
}

static uint8 S9xDebugSA1GetByte (uint32 Address)
{
	int		block = (Address & 0xffffff) >> MEMMAP_SHIFT;
	uint8	*GetAddress = SA1.Map[block];
	uint8	byte = 0;

	if (GetAddress >= (uint8 *) CMemory::MAP_LAST)
	{
		byte = *(GetAddress + (Address & 0xffff));
		return (byte);
	}

	switch ((pint) GetAddress)
	{
		case CMemory::MAP_LOROM_SRAM:
		case CMemory::MAP_SA1RAM:
			byte = *(Memory.SRAM + (Address & 0xffff));
			return (byte);

		case CMemory::MAP_BWRAM:
			byte = *(SA1.BWRAM + ((Address & 0x7fff) - 0x6000));
			return (byte);

		case CMemory::MAP_BWRAM_BITMAP:
			Address -= 0x600000;
			if (SA1.VirtualBitmapFormat == 2)
				byte = (Memory.SRAM[(Address >> 2) & 0xffff] >> ((Address & 3) << 1)) &  3;
			else
				byte = (Memory.SRAM[(Address >> 1) & 0xffff] >> ((Address & 1) << 2)) & 15;
			return (byte);

		case CMemory::MAP_BWRAM_BITMAP2:
			Address = (Address & 0xffff) - 0x6000;
			if (SA1.VirtualBitmapFormat == 2)
				byte = (SA1.BWRAM[(Address >> 2) & 0xffff] >> ((Address & 3) << 1)) &  3;
			else
				byte = (SA1.BWRAM[(Address >> 1) & 0xffff] >> ((Address & 1) << 2)) & 15;
			return (byte);

		default:
			return (byte);
	}
}

static uint16 S9xDebugSA1GetWord (uint32 Address)
{
	uint16	word;

	word  = S9xDebugSA1GetByte(Address);
	word |= S9xDebugSA1GetByte(Address + 1) << 8;

	return (word);
}

static uint8 debug_cpu_op_print (char *Line, uint8 Bank, uint16 Address)
{
	uint8	S9xOpcode;
	uint8	Operant[3];
	uint16	Word;
	uint8	Byte;
	int16	SWord;
	int8	SByte;
	uint8	Size = 0;

	S9xOpcode = S9xDebugGetByte((Bank << 16) + Address);
	sprintf(Line, "$%02X:%04X %02X ", Bank, Address, S9xOpcode);

	Operant[0] = S9xDebugGetByte((Bank << 16) + Address + 1);
	Operant[1] = S9xDebugGetByte((Bank << 16) + Address + 2);
	Operant[2] = S9xDebugGetByte((Bank << 16) + Address + 3);

	switch (AddrModes[S9xOpcode])
	{
		case 0:
			// Implied
			sprintf(Line, "%s         %s",
					Line,
					S9xMnemonics[S9xOpcode]);
			Size = 1;
			break;

		case 1:
			// Immediate[MemoryFlag]
			if (!CheckFlag(MemoryFlag))
			{
				// Accumulator 16 - Bit
				sprintf(Line, "%s%02X %02X    %s #$%02X%02X",
						Line,
						Operant[0],
						Operant[1],
						S9xMnemonics[S9xOpcode],
						Operant[1],
						Operant[0]);
				Size = 3;
			}
			else
			{
				// Accumulator 8 - Bit
				sprintf(Line, "%s%02X       %s #$%02X",
						Line,
						Operant[0],
						S9xMnemonics[S9xOpcode],
						Operant[0]);
				Size = 2;
			}

			break;

		case 2:
			// Immediate[IndexFlag]
			if (!CheckFlag(IndexFlag))
			{
				// X / Y 16 - Bit
				sprintf(Line, "%s%02X %02X    %s #$%02X%02X",
				        Line,
				        Operant[0],
				        Operant[1],
				        S9xMnemonics[S9xOpcode],
				        Operant[1],
				        Operant[0]);
				Size = 3;
			}
			else
			{
				// X / Y 8 - Bit
				sprintf(Line, "%s%02X       %s #$%02X",
				        Line,
				        Operant[0],
				        S9xMnemonics[S9xOpcode],
				        Operant[0]);
				Size = 2;
			}

			break;

		case 3:
			// Immediate[Always 8 - Bit]
			sprintf(Line, "%s%02X       %s #$%02X",
					Line,
					Operant[0],
					S9xMnemonics[S9xOpcode],
					Operant[0]);
			Size = 2;
			break;

		case 4:
			// Relative
			sprintf(Line, "%s%02X       %s $%02X",
					Line,
					Operant[0],
					S9xMnemonics[S9xOpcode],
					Operant[0]);
			SByte = Operant[0];
			Word = Address;
			Word += SByte;
			Word += 2;
			sprintf(Line, "%-32s[$%04X]", Line, Word);
			Size = 2;
			break;

		case 5:
			// Relative Long
			sprintf(Line, "%s%02X %02X    %s $%02X%02X",
					Line,
					Operant[0],
					Operant[1],
					S9xMnemonics[S9xOpcode],
					Operant[1],
					Operant[0]);
			SWord = (Operant[1] << 8) | Operant[0];
			Word = Address;
			Word += SWord;
			Word += 3;
			sprintf(Line, "%-32s[$%04X]", Line, Word);
			Size = 3;
			break;

		case 6:
			// Direct
			sprintf(Line, "%s%02X       %s $%02X",
					Line,
					Operant[0],
					S9xMnemonics[S9xOpcode],
					Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 7:
			// Direct Indexed (with X)
			sprintf(Line, "%s%02X       %s $%02X,x",
					Line,
					Operant[0],
					S9xMnemonics[S9xOpcode],
					Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Word += Registers.X.W;
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 8:
			// Direct Indexed (with Y)
			sprintf(Line, "%s%02X       %s $%02X,y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Word += Registers.Y.W;
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 9:
			// Direct Indirect
			sprintf(Line, "%s%02X       %s ($%02X)",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Word = S9xDebugGetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 2;
			break;

		case 10:
			// Direct Indexed Indirect
			sprintf(Line, "%s%02X       %s ($%02X,x)",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Word += Registers.X.W;
			Word = S9xDebugGetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 2;
			break;

		case 11:
			// Direct Indirect Indexed
			sprintf(Line, "%s%02X       %s ($%02X),y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Word = S9xDebugGetWord(Word);
			Word += Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 2;
			break;

		case 12:
			// Direct Indirect Long
			sprintf(Line, "%s%02X       %s [$%02X]",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Byte = S9xDebugGetByte(Word + 2);
			Word = S9xDebugGetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Byte, Word);
			Size = 2;
			break;

		case 13:
			// Direct Indirect Indexed Long
			sprintf(Line, "%s%02X       %s [$%02X],y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Byte = S9xDebugGetByte(Word + 2);
			Word = S9xDebugGetWord(Word);
			Word += Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Byte, Word);
			Size = 2;
			break;

		case 14:
			// Absolute
			sprintf(Line, "%s%02X %02X    %s $%02X%02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 3;
			break;

		case 15:
			// Absolute Indexed (with X)
			sprintf(Line, "%s%02X %02X    %s $%02X%02X,x",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += Registers.X.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 3;
			break;

		case 16:
			// Absolute Indexed (with Y)
			sprintf(Line, "%s%02X %02X    %s $%02X%02X,y",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 3;
			break;

		case 17:
			// Absolute Long
			sprintf(Line, "%s%02X %02X %02X %s $%02X%02X%02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        Operant[2],
			        S9xMnemonics[S9xOpcode],
			        Operant[2],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Operant[2], Word);
			Size = 4;
			break;

		case 18:
			// Absolute Indexed Long
			sprintf(Line, "%s%02X %02X %02X %s $%02X%02X%02X,x",
			        Line,
			        Operant[0],
			        Operant[1],
			        Operant[2],
			        S9xMnemonics[S9xOpcode],
			        Operant[2],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += Registers.X.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Operant[2], Word);
			Size = 4;
			break;

		case 19:
			// Stack Relative
			sprintf(Line, "%s%02X       %s $%02X,s",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Registers.S.W;
			Word += Operant[0];
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 20:
			// Stack Relative Indirect Indexed
			sprintf(Line, "%s%02X       %s ($%02X,s),y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Registers.S.W;
			Word += Operant[0];
			Word = S9xDebugGetWord(Word);
			Word += Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.DB, Word);
			Size = 2;
			break;

		case 21:
			// Absolute Indirect
			sprintf(Line, "%s%02X %02X    %s ($%02X%02X)",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word = S9xDebugGetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.PB, Word);
			Size = 3;
			break;

		case 22:
			// Absolute Indirect Long
			sprintf(Line, "%s%02X %02X    %s [$%02X%02X]",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Byte = S9xDebugGetByte(Word + 2);
			Word = S9xDebugGetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Byte, Word);
			Size = 3;
			break;

		case 23:
			// Absolute Indexed Indirect
			sprintf(Line, "%s%02X %02X    %s ($%02X%02X,x)",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += Registers.X.W;
			Word = S9xDebugGetWord(ICPU.ShiftedPB + Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Registers.PB, Word);
			Size = 3;
			break;

		case 24:
			// Implied Accumulator
			sprintf(Line, "%s         %s A",
			        Line,
			        S9xMnemonics[S9xOpcode]);
			Size = 1;
			break;

		case 25:
			// MVN/MVP SRC DST
			sprintf(Line, "%s%02X %02X    %s %02X %02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Size = 3;
			break;

		case 26:
			// PEA
			sprintf(Line, "%s%02X %02X    %s $%02X%02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Size = 3;
			break;

		case 27:
			// PEI Direct Indirect
			sprintf(Line, "%s%02X       %s ($%02X)",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += Registers.D.W;
			Word = S9xDebugGetWord(Word);
			sprintf(Line, "%-32s[$%04X]", Line, Word);
			Size = 2;
			break;
	}

	sprintf(Line, "%-44s A:%04X X:%04X Y:%04X D:%04X DB:%02X S:%04X P:%c%c%c%c%c%c%c%c%c HC:%04ld VC:%03ld FC:%02d %c%c%c %c%c%c %c%c HT:%d VT:%d C:%d",
	        Line, Registers.A.W, Registers.X.W, Registers.Y.W,
	        Registers.D.W, Registers.DB, Registers.S.W,
	        CheckEmulation() ? 'E' : 'e',
	        CheckNegative() ? 'N' : 'n',
	        CheckOverflow() ? 'V' : 'v',
	        CheckMemory() ? 'M' : 'm',
	        CheckIndex() ? 'X' : 'x',
	        CheckDecimal() ? 'D' : 'd',
	        CheckIRQ() ? 'I' : 'i',
	        CheckZero() ? 'Z' : 'z',
	        CheckCarry() ? 'C' : 'c',
	        (long) CPU.Cycles,
	        (long) CPU.V_Counter,
	        IPPU.FrameCount,
	        CPU.IRQExternal ?  'E' : ' ', PPU.HTimerEnabled ? 'H' : ' ', PPU.VTimerEnabled ? 'V' : ' ',
	        CPU.NMIPending ? 'N' : '.',
	        Memory.FillRAM[0x4200] & 0x80 ? 'n' : '.',
	        Memory.FillRAM[0x4210] & 0x80 ? '+' : '.',
	        CPU.IRQTransition ? 'T' : ' ',
	        CPU.IRQLine ? 'L' : ' ',
	        PPU.HTimerPosition, PPU.VTimerPosition, Timings.NextIRQTimer);

	return (Size);
}

static uint8 debug_sa1_op_print (char *Line, uint8 Bank, uint16 Address)
{
	uint8	S9xOpcode;
	uint8	Operant[3];
	uint16	Word;
	uint8	Byte;
	int16	SWord;
	int8	SByte;
	uint8	Size = 0;

	S9xOpcode = S9xDebugSA1GetByte((Bank << 16) + Address);
	sprintf(Line, "$%02X:%04X %02X ", Bank, Address, S9xOpcode);

	Operant[0] = S9xDebugSA1GetByte((Bank << 16) + Address + 1);
	Operant[1] = S9xDebugSA1GetByte((Bank << 16) + Address + 2);
	Operant[2] = S9xDebugSA1GetByte((Bank << 16) + Address + 3);

	switch (AddrModes[S9xOpcode])
	{
		case 0:
			// Implied
			sprintf(Line, "%s         %s",
			        Line,
			        S9xMnemonics[S9xOpcode]);
			Size = 1;
			break;

		case 1:
			// Immediate[MemoryFlag]
			if (!SA1CheckFlag(MemoryFlag))
			{
				// Accumulator 16 - Bit
				sprintf(Line, "%s%02X %02X    %s #$%02X%02X",
				        Line,
				        Operant[0],
				        Operant[1],
				        S9xMnemonics[S9xOpcode],
				        Operant[1],
				        Operant[0]);
				Size = 3;
			}
			else
			{
				// Accumulator 8 - Bit
				sprintf(Line, "%s%02X       %s #$%02X",
				        Line,
				        Operant[0],
				        S9xMnemonics[S9xOpcode],
				        Operant[0]);
				Size = 2;
			}

			break;

		case 2:
			// Immediate[IndexFlag]
			if (!SA1CheckFlag(IndexFlag))
			{
				// X / Y 16 - Bit
				sprintf(Line, "%s%02X %02X    %s #$%02X%02X",
				        Line,
				        Operant[0],
				        Operant[1],
				        S9xMnemonics[S9xOpcode],
				        Operant[1],
				        Operant[0]);
				Size = 3;
			}
			else
			{
				// X / Y 8 - Bit
				sprintf(Line, "%s%02X       %s #$%02X",
				        Line,
				        Operant[0],
				        S9xMnemonics[S9xOpcode],
				        Operant[0]);
				Size = 2;
			}

			break;

		case 3:
			// Immediate[Always 8 - Bit]
			sprintf(Line, "%s%02X       %s #$%02X",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Size = 2;
			break;

		case 4:
			// Relative
			sprintf(Line, "%s%02X       %s $%02X",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			SByte = Operant[0];
			Word = Address;
			Word += SByte;
			Word += 2;
			sprintf(Line, "%-32s[$%04X]", Line, Word);
			Size = 2;
			break;

		case 5:
			// Relative Long
			sprintf(Line, "%s%02X %02X    %s $%02X%02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			SWord = (Operant[1] << 8) | Operant[0];
			Word = Address;
			Word += SWord;
			Word += 3;
			sprintf(Line, "%-32s[$%04X]", Line, Word);
			Size = 3;
			break;

		case 6:
			// Direct
			sprintf(Line, "%s%02X       %s $%02X",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 7:
			// Direct Indexed (with X)
			sprintf(Line, "%s%02X       %s $%02X,x",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Word += SA1Registers.X.W;
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 8:
			// Direct Indexed (with Y)
			sprintf(Line, "%s%02X       %s $%02X,y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Word += SA1Registers.Y.W;
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 9:
			// Direct Indirect
			sprintf(Line, "%s%02X       %s ($%02X)",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Word = S9xDebugSA1GetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 2;
			break;

		case 10:
			// Direct Indexed Indirect
			sprintf(Line, "%s%02X       %s ($%02X,x)",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Word += SA1Registers.X.W;
			Word = S9xDebugSA1GetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 2;
			break;

		case 11:
			// Direct Indirect Indexed
			sprintf(Line, "%s%02X       %s ($%02X),y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Word = S9xDebugSA1GetWord(Word);
			Word += SA1Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 2;
			break;

		case 12:
			// Direct Indirect Long
			sprintf(Line, "%s%02X       %s [$%02X]",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Byte = S9xDebugSA1GetByte(Word + 2);
			Word = S9xDebugSA1GetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Byte, Word);
			Size = 2;
			break;

		case 13:
			// Direct Indirect Indexed Long
			sprintf(Line, "%s%02X       %s [$%02X],y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = Operant[0];
			Word += SA1Registers.D.W;
			Byte = S9xDebugSA1GetByte(Word + 2);
			Word = S9xDebugSA1GetWord(Word);
			Word += SA1Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Byte, Word);
			Size = 2;
			break;

		case 14:
			// Absolute
			sprintf(Line, "%s%02X %02X    %s $%02X%02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 3;
			break;

		case 15:
			// Absolute Indexed (with X)
			sprintf(Line, "%s%02X %02X    %s $%02X%02X,x",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += SA1Registers.X.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 3;
			break;

		case 16:
			// Absolute Indexed (with Y)
			sprintf(Line, "%s%02X %02X    %s $%02X%02X,y",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += SA1Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 3;
			break;

		case 17:
			// Absolute Long
			sprintf(Line, "%s%02X %02X %02X %s $%02X%02X%02X",
			        Line,
			        Operant[0],
			        Operant[1],
			        Operant[2],
			        S9xMnemonics[S9xOpcode],
			        Operant[2],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Operant[2], Word);
			Size = 4;
			break;

		case 18:
			// Absolute Indexed Long
			sprintf(Line, "%s%02X %02X %02X %s $%02X%02X%02X,x",
			        Line,
			        Operant[0],
			        Operant[1],
			        Operant[2],
			        S9xMnemonics[S9xOpcode],
			        Operant[2],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += SA1Registers.X.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Operant[2], Word);
			Size = 4;
			break;

		case 19:
			// Stack Relative
			sprintf(Line, "%s%02X       %s $%02X,s",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = SA1Registers.S.W;
			Word += Operant[0];
			sprintf(Line, "%-32s[$00:%04X]", Line, Word);
			Size = 2;
			break;

		case 20:
			// Stack Relative Indirect Indexed
			sprintf(Line, "%s%02X       %s ($%02X,s),y",
			        Line,
			        Operant[0],
			        S9xMnemonics[S9xOpcode],
			        Operant[0]);
			Word = SA1Registers.S.W;
			Word += Operant[0];
			Word = S9xDebugSA1GetWord(Word);
			Word += SA1Registers.Y.W;
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.DB, Word);
			Size = 2;
			break;

		case 21:
			// Absolute Indirect
			sprintf(Line, "%s%02X %02X    %s ($%02X%02X)",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word = S9xDebugSA1GetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.PB, Word);
			Size = 3;
			break;

		case 22:
			// Absolute Indirect Long
			sprintf(Line, "%s%02X %02X    %s [$%02X%02X]",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Byte = S9xDebugSA1GetByte(Word + 2);
			Word = S9xDebugSA1GetWord(Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, Byte, Word);
			Size = 3;
			break;

		case 23:
			// Absolute Indexed Indirect
			sprintf(Line, "%s%02X %02X    %s ($%02X%02X,x)",
			        Line,
			        Operant[0],
			        Operant[1],
			        S9xMnemonics[S9xOpcode],
			        Operant[1],
			        Operant[0]);
			Word = (Operant[1] << 8) | Operant[0];
			Word += SA1Registers.X.W;
			Word = S9xDebugSA1GetWord(SA1.ShiftedPB + Word);
			sprintf(Line, "%-32s[$%02X:%04X]", Line, SA1Registers.PB, Word);
			Size = 3;
			break;

		case 24:
			// Implied Accumulator
			sprintf(Line, "%s         %s A",
			        Line,
			        S9xMnemonics[S9xOpcode]);
			Size = 1;
			break;

		case 25:
			// MVN/MVP SRC DST
			sprintf(Line, "%s         %s %02X %02X",
			        Line,
			        S9xMnemonics[S9xOpcode],
			        Operant[0],
			        Operant[1]);
			Size = 3;
			break;
	}

	sprintf(Line, "%-44s A:%04X X:%04X Y:%04X D:%04X DB:%02X S:%04X P:%c%c%c%c%c%c%c%c%c HC:%04ld VC:%03ld FC:%02d %c%c",
	        Line, SA1Registers.A.W, SA1Registers.X.W, SA1Registers.Y.W,
	        SA1Registers.D.W, SA1Registers.DB, SA1Registers.S.W,
	        SA1CheckEmulation() ? 'E' : 'e',
	        SA1CheckNegative() ? 'N' : 'n',
	        SA1CheckOverflow() ? 'V' : 'v',
	        SA1CheckMemory() ? 'M' : 'm',
	        SA1CheckIndex() ? 'X' : 'x',
	        SA1CheckDecimal() ? 'D' : 'd',
	        SA1CheckIRQ() ? 'I' : 'i',
	        SA1CheckZero() ? 'Z' : 'z',
	        SA1CheckCarry() ? 'C' : 'c',
	        (long) CPU.Cycles,
	        (long) CPU.V_Counter,
	        IPPU.FrameCount,
	        CPU.NMIPending ? 'P' : ' ',
	        Memory.FillRAM[0x4210] & 0x80 ? 'N' : ' ');

	return (Size);
}

static void debug_line_print (const char *Line)
{
	printf("%s\n", Line);
}

static int debug_get_number (char *Line, uint16 *Number)
{
	int	i;

	if (sscanf(Line, " #%d", &i) == 1)
	{
		*Number = i;
		return (1);
	}

	return (-1);
}

static short debug_get_start_address (char *Line, uint8 *Bank, uint32 *Address)
{
	uint32	a, b;

	if (sscanf(Line + 1, " $%x:%x", &b, &a) != 2)
		return (-1);

	*Bank = b;
	*Address = a;

	return (1);
}

void S9xDebugProcessCommand(char *Line)
{
	uint8	Bank = Registers.PB;
	uint32	Address = Registers.PCw;
	uint16	Hold = 0;
	uint16	Number;
	short	ErrorCode;
	char	string[512];

	if (strncasecmp(Line, "dump", 4) == 0)
	{
		int	Count;

		if (sscanf(&Line[4], "%x %d", &Address, &Count) == 2)
		{
			FILE	*fs;

			sprintf(string, "%06x%05d.sd2", Address, Count);
			fs = fopen(string, "wb");
			if (fs)
			{
				for (int i = 0; i < Count; i++)
					putc(S9xDebugGetByte(Address + i), fs);
				fclose(fs);
			}
			else
				printf("Can't open %s for writing\n", string);
		}
		else
			printf("Usage: dump start_address_in_hex count_in_decimal\n");

		return;
	}

	if (*Line == 'i')
	{
		printf("Vectors:\n");
		sprintf(string, "      8 Bit   16 Bit ");
		debug_line_print(string);
		sprintf(string, "ABT $00:%04X|$00:%04X", S9xDebugGetWord(0xFFF8), S9xDebugGetWord(0xFFE8));
		debug_line_print(string);
		sprintf(string, "BRK $00:%04X|$00:%04X", S9xDebugGetWord(0xFFFE), S9xDebugGetWord(0xFFE6));
		debug_line_print(string);
		sprintf(string, "COP $00:%04X|$00:%04X", S9xDebugGetWord(0xFFF4), S9xDebugGetWord(0xFFE4));
		debug_line_print(string);
		sprintf(string, "IRQ $00:%04X|$00:%04X", S9xDebugGetWord(0xFFFE), S9xDebugGetWord(0xFFEE));
		debug_line_print(string);
		sprintf(string, "NMI $00:%04X|$00:%04X", S9xDebugGetWord(0xFFFA), S9xDebugGetWord(0xFFEA));
		debug_line_print(string);
		sprintf(string, "RES     $00:%04X", S9xDebugGetWord(0xFFFC));
		debug_line_print(string);
	}

/*
	if (strncmp(Line, "ai", 2) == 0)
	{
		printf("APU vectors:");

		for (int i = 0; i < 0x40; i += 2)
		{
			if (i % 16 == 0)
				printf("\n%04x ", 0xffc0 + i);

			printf("%04x ", APU.ExtraRAM[i]);
		}

		printf("\n");
	}
*/

	if (*Line == 's')
	{
		Registers.PCw += debug_cpu_op_print(string, Bank, Address);
		Bank = Registers.PB;
		Address = Registers.PCw;
		*Line = 'r';
	}

	if (*Line == 'z')
	{
		uint16	*p = (uint16 *) &Memory.VRAM[PPU.BG[2].SCBase << 1];

		for (int l = 0; l < 32; l++)
		{
			for (int c = 0; c < 32; c++, p++)
				printf("%04x,", *p++);

			printf("\n");
		}
	}

	if (*Line == 'c')
	{
		printf("Colours:\n");

		for (int i = 0; i < 256; i++)
			printf("%02x%02x%02x  ", PPU.CGDATA[i] & 0x1f, (PPU.CGDATA[i] >> 5) & 0x1f, (PPU.CGDATA[i] >> 10) & 0x1f);

		printf("\n");
	}

	if (*Line == 'S')
	{
		int	SmallWidth, LargeWidth, SmallHeight, LargeHeight;

		switch ((Memory.FillRAM[0x2101] >> 5) & 7)
		{

			case 0:
				SmallWidth = SmallHeight = 8;
				LargeWidth = LargeHeight = 16;
				break;

			case 1:
				SmallWidth = SmallHeight = 8;
				LargeWidth = LargeHeight = 32;
				break;

			case 2:
				SmallWidth = SmallHeight = 8;
				LargeWidth = LargeHeight = 64;
				break;

			case 3:
				SmallWidth = SmallHeight = 16;
				LargeWidth = LargeHeight = 32;
				break;

			case 4:
				SmallWidth = SmallHeight = 16;
				LargeWidth = LargeHeight = 64;
				break;

			default:
			case 5:
				SmallWidth = SmallHeight = 32;
				LargeWidth = LargeHeight = 64;
				break;

			case 6:
				SmallWidth = 16;
				SmallHeight = 32;
				LargeWidth = 32;
				LargeHeight = 64;
				break;

			case 7:
				SmallWidth = 16;
				SmallHeight = 32;
				LargeWidth = LargeHeight = 32;
				break;
		}

		printf("Sprites: Small: %dx%d, Large: %dx%d, OAMAddr: 0x%04x, OBJNameBase: 0x%04x, OBJNameSelect: 0x%04x, First: %d\n",
		       SmallWidth, SmallHeight, LargeWidth, LargeHeight, PPU.OAMAddr, PPU.OBJNameBase, PPU.OBJNameSelect, PPU.FirstSprite);

		for (int i = 0; i < 128; i++)
		{
			printf("X:%3d Y:%3d %c%c%d%c ",
			       PPU.OBJ[i].HPos,
			       PPU.OBJ[i].VPos,
			       PPU.OBJ[i].VFlip ? 'V' : 'v',
			       PPU.OBJ[i].HFlip ? 'H' : 'h',
			       PPU.OBJ[i].Priority,
			       PPU.OBJ[i].Size ? 'S' : 's');

			if (i % 4 == 3)
				printf("\n");
		}
	}

	if (*Line == 'T')
	{
		if (Line[1] == 'S')
		{
			SA1.Flags ^= TRACE_FLAG;

			if (SA1.Flags & TRACE_FLAG)
			{
				printf("SA1 CPU instruction tracing enabled.\n");
				ENSURE_TRACE_OPEN(trace2, "trace_sa1.log", "wb")
			}
			else
			{
				printf("SA1 CPU instruction tracing disabled.\n");
				fclose(trace2);
				trace2 = NULL;
			}
		}
		else
		{
			CPU.Flags ^= TRACE_FLAG;

			if (CPU.Flags & TRACE_FLAG)
			{
				printf("CPU instruction tracing enabled.\n");
				ENSURE_TRACE_OPEN(trace, "trace.log", "wb")
			}
			else
			{
				printf("CPU instruction tracing disabled.\n");
				fclose(trace);
				trace = NULL;
			}
		}
	}

	if (*Line == 'E')
	{
		Settings.TraceHCEvent = !Settings.TraceHCEvent;
		printf("HC event tracing %s.\n", Settings.TraceHCEvent ? "enabled" : "disabled");
	}

	// TODO: reactivate once APU debugger works again
	if (*Line == 'A')
	{
		Settings.TraceSMP = !Settings.TraceSMP;
		printf("SMP tracing %s\n", Settings.TraceSMP ? "enabled" : "disabled");
	}

/*
	if (*Line == 'B')
	{
		Settings.TraceSoundDSP = !Settings.TraceSoundDSP;
		printf("Sound DSP register tracing %s.\n", Settings.TraceSoundDSP ? "enabled" : "disabled");
	}

	if (*Line == 'x')
		S9xPrintSoundDSPState();

	if (*Line == 'C')
	{
		printf("SPC700 sample addresses at 0x%04x:\n", APU.DSP[APU_DIR] << 8);

		for (int i = 0; i < 256; i++)
		{
			uint8	*dir = IAPU.RAM + (((APU.DSP[APU_DIR] << 8) + i * 4) & 0xffff);
			int		addr = *dir + (*(dir + 1) << 8);
			int		addr2 = *(dir + 2) + (*(dir + 3) << 8);
			printf("%04X %04X;", addr, addr2);

			if (i % 8 == 7)
				printf("\n");
		}
	}
*/

	if (*Line == 'R')
	{
		S9xReset();
		printf("SNES reset.\n");
		CPU.Flags |= DEBUG_MODE_FLAG;
	}

/*
	if (strncmp(Line, "ad", 2) == 0)
	{
		uint32	Count = 16;
		Address = 0;

		if (sscanf(Line + 2, "%x,%x", &Address, &Count) != 2)
		{
			if (sscanf(Line + 2, "%x", &Address) == 1)
				Count = 16;
		}

		printf("APU RAM dump:\n");

		for (uint32 l = 0; l < Count; l += 16)
		{
			printf("%04X ", Address);

			for (int i = 0; i < 16; i++)
				printf("%02X ", IAPU.RAM[Address++]);

			printf("\n");
		}

		*Line = 0;
	}*/



	if (*Line == 'a')
	{
		printf("S-CPU-side ports S-CPU writes these, S-SMP reads: %02X %02X %02X %02X\n", SNES::cpu.port_read(0), SNES::cpu.port_read(1), SNES::cpu.port_read(2), SNES::cpu.port_read(3));
		printf("S-SMP-side ports S-SMP writes these, S-CPU reads: %02X %02X %02X %02X\n", SNES::smp.port_read(0), SNES::smp.port_read(1), SNES::smp.port_read(2), SNES::smp.port_read(3));
	}
/*
	if (*Line == 'P')
	{
		Settings.TraceDSP = !Settings.TraceDSP;
		printf("DSP tracing %s.\n", Settings.TraceDSP ? "enabled" : "disabled");
	}
*/

	if (*Line == 'p')
	{
		S9xBreakpoint[5].Enabled = FALSE;
		Address += debug_cpu_op_print(string, Bank, Address);

		if (strncmp(&string[18], "JMP", 3) != 0 &&
		    strncmp(&string[18], "JML", 3) != 0 &&
		    strncmp(&string[18], "RT" , 2) != 0 &&
		    strncmp(&string[18], "BRA", 3))
		{
			S9xBreakpoint[5].Enabled = TRUE;
			S9xBreakpoint[5].Bank = Bank;
			S9xBreakpoint[5].Address = Address;
		}
		else
		{
			CPU.Flags |= SINGLE_STEP_FLAG;
			CPU.Flags &= ~DEBUG_MODE_FLAG;
		}
	}

	if (*Line == 'b')
	{
		if (Line[1] == 's')
		{
			debug_get_number(Line + 2, &Hold);

			if (Hold > 4)
				Hold = 0;

			if (Hold < 5)
			{
				if (debug_get_start_address(Line + 5, &Bank, &Address) == -1)
					S9xBreakpoint[Hold].Enabled = FALSE;
				else
				{
					S9xBreakpoint[Hold].Enabled = TRUE;
					S9xBreakpoint[Hold].Bank = Bank;
					S9xBreakpoint[Hold].Address = Address;
					CPU.Flags |= BREAK_FLAG;
				}
			}

			Line[1] = 'v';
		}

		if (Line[1] == 'v')
		{
			Number = 0;

			if (debug_get_number(Line + 2, &Number) == -1 && Number < 5)
			{
				debug_line_print("Breakpoints:");

				for (Number = 0; Number != 5; Number++)
				{
					if (S9xBreakpoint[Number].Enabled)
						sprintf(string, "%i @ $%02X:%04X", Number, S9xBreakpoint[Number].Bank, S9xBreakpoint[Number].Address);
					else
						sprintf(string, "%i @ Disabled", Number);

					debug_line_print(string);
				}
			}
			else
			{
				debug_line_print("Breakpoint:");

				if (S9xBreakpoint[Number].Enabled)
					sprintf(string, "%i @ $%02X:%04X", Number, S9xBreakpoint[Number].Bank, S9xBreakpoint[Number].Address);
				else
					sprintf(string, "%i @ Disabled", Number);

				debug_line_print(string);
			}
		}
	}

	if (*Line == '?' || strcasecmp(Line, "help") == 0)
	{
		for (int i = 0; HelpMessage[i] != NULL; i++)
			debug_line_print(HelpMessage[i]);
	}

	if (*Line == 't')
	{
		CPU.Flags |= SINGLE_STEP_FLAG;
		CPU.Flags &= ~DEBUG_MODE_FLAG;
	}

	if (*Line == 'f')
	{
		CPU.Flags |= FRAME_ADVANCE_FLAG;
		CPU.Flags &= ~DEBUG_MODE_FLAG;

		IPPU.RenderThisFrame = TRUE;
		IPPU.FrameSkip = 0;

		if (sscanf(&Line[1], "%u", &ICPU.FrameAdvanceCount) != 1)
			ICPU.Frame = 0;
	}

	if (*Line == 'g')
	{
		S9xBreakpoint[5].Enabled = FALSE;

		bool8	found = FALSE;

		for (int i = 0; i < 5; i++)
		{
			if (S9xBreakpoint[i].Enabled)
			{
				found = TRUE;

				if (S9xBreakpoint[i].Bank == Registers.PB && S9xBreakpoint[i].Address == Registers.PCw)
				{
					S9xBreakpoint[i].Enabled = 2;
					break;
				}
			}
		}

		if (!found)
			CPU.Flags &= ~BREAK_FLAG;

		ErrorCode = debug_get_start_address(Line, &Bank, &Address);

		if (ErrorCode == 1)
		{
			S9xBreakpoint[5].Enabled = TRUE;
			S9xBreakpoint[5].Bank = Bank;
			S9xBreakpoint[5].Address = Address;
			CPU.Flags |= BREAK_FLAG;
		}

		CPU.Flags &= ~DEBUG_MODE_FLAG;
	}

	if (*Line == 'D')
	{
		Settings.TraceDMA = !Settings.TraceDMA;
		printf("DMA tracing %s.\n", Settings.TraceDMA ? "enabled" : "disabled");
	}

	if (*Line == 'V')
	{
		Settings.TraceVRAM = !Settings.TraceVRAM;
		printf("Non-DMA VRAM write tracing %s.\n", Settings.TraceVRAM ? "enabled" : "disabled");
	}

	if (*Line == 'H')
	{
		Settings.TraceHDMA = !Settings.TraceHDMA;
		printf("HDMA tracing %s.\n", Settings.TraceHDMA ? "enabled" : "disabled");
	}

	if (*Line == 'U')
	{
		Settings.TraceUnknownRegisters = !Settings.TraceUnknownRegisters;
		printf("Unknown registers read/write tracing %s.\n", Settings.TraceUnknownRegisters ? "enabled" : "disabled");
	}

	if (*Line == 'd')
	{
		int		CLine;
		int		CByte;
		uint8	MemoryByte;

		if (Debug.Dump.Bank != 0 || Debug.Dump.Address != 0)
		{
			Bank = Debug.Dump.Bank;
			Address = Debug.Dump.Address;
		}

		ErrorCode = debug_get_start_address(Line, &Bank, &Address);

		for (CLine = 0; CLine != 10; CLine++)
		{
			sprintf(string, "$%02X:%04X", Bank, Address);

			for (CByte = 0; CByte != 16; CByte++)
			{
				if (Address + CByte == 0x2140 ||
				    Address + CByte == 0x2141 ||
				    Address + CByte == 0x2142 ||
				    Address + CByte == 0x2143 ||
				    Address + CByte == 0x4210)
					MemoryByte = 0;
				else
					MemoryByte = S9xDebugGetByte((Bank << 16) + Address + CByte);

				sprintf(string, "%s %02X", string, MemoryByte);
			}

			sprintf(string, "%s-", string);

			for (CByte = 0; CByte != 16; CByte++)
			{
				if (Address + CByte == 0x2140 ||
				    Address + CByte == 0x2141 ||
				    Address + CByte == 0x2142 ||
				    Address + CByte == 0x2143 ||
				    Address + CByte == 0x4210)
					MemoryByte = 0;
				else
					MemoryByte = S9xDebugGetByte((Bank << 16) + Address + CByte);

				if (MemoryByte < 32 || MemoryByte >= 127)
					MemoryByte = '?';

				sprintf(string, "%s%c", string, MemoryByte);
			}

			Address += 16;

			debug_line_print(string);
		}

		Debug.Dump.Bank = Bank;
		Debug.Dump.Address = Address;
	}

	if (*Line == 'q')
		S9xExit();

	if (*Line == 'W')
		debug_whats_missing();

	if (*Line == 'w')
		debug_whats_used();

	if (*Line == 'r')
	{
		debug_cpu_op_print(string, Bank, Address);
		debug_line_print(string);
	}

	if (*Line == 'u')
	{
		if (Debug.Unassemble.Bank != 0 || Debug.Unassemble.Address != 0)
		{
			Bank = Debug.Unassemble.Bank;
			Address = Debug.Unassemble.Address;
		}

		ErrorCode = debug_get_start_address(Line, &Bank, &Address);

		for (int i = 0; i != 10; i++)
		{
			Address += debug_cpu_op_print(string, Bank, Address);
			debug_line_print(string);
		}

		Debug.Unassemble.Bank = Bank;
		Debug.Unassemble.Address = Address;
	}

	debug_line_print("");

	return;
}

static void debug_print_window (uint8 *window)
{
	for (int i = 0; i < 6; i++)
	{
		if (window[i])
		{
			switch (i)
			{
				case 0:
					printf("Background 0, ");
					break;

				case 1:
					printf("Background 1, ");
					break;

				case 2:
					printf("Background 2, ");
					break;

				case 3:
					printf("Background 3, ");
					break;

				case 4:
					printf("Objects, ");
					break;

				case 5:
					printf("Color window, ");
					break;
			}
		}
	}
}

static const char * debug_clip_fn (int logic)
{
	switch (logic)
	{
		case CLIP_OR:
			return ("OR");

		case CLIP_AND:
			return ("AND");

		case CLIP_XOR:
			return ("XOR");

		case CLIP_XNOR:
			return ("XNOR");

		default:
			return ("???");
	}
}

static void debug_whats_used (void)
{
	printf("V-line: %ld, H-Pos: %ld, \n", (long) CPU.V_Counter, (long) CPU.Cycles);

	printf("Screen mode: %d, ", PPU.BGMode);

	if (PPU.BGMode <= 1 && (Memory.FillRAM[0x2105] & 8))
		printf("(BG#2 Priority), ");

	printf("Brightness: %d, ", PPU.Brightness);

	if (Memory.FillRAM[0x2100] & 0x80)
		printf("(screen blanked), ");

	printf("\n");

	if (Memory.FillRAM[0x2133] & 1)
		printf("Interlace, ");

	if (Memory.FillRAM[0x2133] & 4)
		printf("240 line visible, ");

	if (Memory.FillRAM[0x2133] & 8)
		printf("Pseudo 512 pixels horizontal resolution, ");

	if (Memory.FillRAM[0x2133] & 0x40)
		printf("Mode 7 priority per pixel, ");

	printf("\n");

	if (PPU.BGMode == 7 && (Memory.FillRAM[0x211a] & 3))
		printf("Mode 7 flipping, ");

	if (PPU.BGMode == 7)
		printf("Mode 7 screen repeat: %d, ", (Memory.FillRAM[0x211a] & 0xc0) >> 6);

	if (Memory.FillRAM[0x2130] & 1)
		printf("32K colour mode, ");

	printf("\n");

	if (PPU.BGMode == 7)
	{
		// Sign extend 13 bit values to 16 bit values...
		if (PPU.CentreX & (1 << 12))
			PPU.CentreX |= 0xe000;

		if (PPU.CentreY & (1 << 12))
			PPU.CentreY |= 0xe000;

		printf("Matrix A: %.3f, B: %.3f, C: %.3f, D: %.3f, Centre X: %d Y:%d, \n",
		       (double) PPU.MatrixA / 256, (double) PPU.MatrixB / 256,
		       (double) PPU.MatrixC / 256, (double) PPU.MatrixD / 256,
		       PPU.CentreX, PPU.CentreY);
	}

	if ((Memory.FillRAM[0x2106] & 0xf0) && (Memory.FillRAM[0x2106] & 0x0f))
	{
		printf("Mosaic effect(%d) on, ", PPU.Mosaic);

		for (int i = 0; i < 4; i++)
			if (Memory.FillRAM[0x2106] & (1 << i))
				printf("BG%d, ", i);
	}

	printf("\n");

	if (PPU.HVBeamCounterLatched)
		printf("V and H beam pos latched, \n");

	if (Memory.FillRAM[0x4200] & 0x20)
		printf("V-IRQ enabled at %d, \n", PPU.IRQVBeamPos);

	if (Memory.FillRAM[0x4200] & 0x10)
		printf("H-IRQ enabled at %d, \n", PPU.IRQHBeamPos);

	if (Memory.FillRAM[0x4200] & 0x80)
		printf("V-blank NMI enabled, \n");

	for (int i = 0; i < 8; i++)
	{
		if (missing.hdma_this_frame & (1 << i))
		{
			printf("H-DMA %d [%d] 0x%02X%04X->0x21%02X %s %s 0x%02X%04X %s addressing, \n",
			       i, DMA[i].TransferMode, DMA[i].ABank, DMA[i].AAddress, DMA[i].BAddress,
			       DMA[i].AAddressDecrement ? "dec" : "inc",
			       DMA[i].Repeat ? "repeat" : "continue",
			       DMA[i].IndirectBank, DMA[i].IndirectAddress,
			       DMA[i].HDMAIndirectAddressing ? "indirect" : "absolute");
		}
	}

	for (int i = 0; i < 8; i++)
	{
		if (missing.dma_this_frame & (1 << i))
		{
			printf("DMA %d [%d] 0x%02X%04X->0x21%02X Num: %d %s, \n",
			       i, DMA[i].TransferMode, DMA[i].ABank, DMA[i].AAddress, DMA[i].BAddress, DMA[i].TransferBytes,
			       DMA[i].AAddressFixed ? "fixed" : (DMA[i].AAddressDecrement ? "dec" : "inc"));
		}
	}

	printf("VRAM write address: 0x%04x(%s), Full Graphic: %d, Address inc: %d, \n",
	       PPU.VMA.Address,
	       PPU.VMA.High  ? "Byte" : "Word",
	       PPU.VMA.FullGraphicCount, PPU.VMA.Increment);

	for (int i = 0; i < 4; i++)
	{
		printf("BG%d: VOffset:%d, HOffset:%d, W:%d, H:%d, TS:%d, BA:0x%04x, TA:0x%04X, \n",
		       i, PPU.BG[i].VOffset, PPU.BG[i].HOffset,
		       (PPU.BG[i].SCSize & 1) * 32 + 32,
		       (PPU.BG[i].SCSize & 2) * 16 + 32,
		       PPU.BG[i].BGSize * 8 + 8,
		       PPU.BG[i].SCBase,
		       PPU.BG[i].NameBase);
	}

	const char	*s = "";

	switch ((Memory.FillRAM[0x2130] & 0xc0) >> 6)
	{
		case 0:
			s = "always on";
			break;

		case 1:
			s = "inside";
			break;

		case 2:
			s = "outside";
			break;

		case 3:
			s = "always off";
			break;
	}

	printf("Main screen (%s): ", s);

	for (int i = 0; i < 5; i++)
	{
		if (Memory.FillRAM[0x212c] & (1 << i))
		{
			switch (i)
			{
				case 0:
					printf("BG0, ");
					break;

				case 1:
					printf("BG1, ");
					break;

				case 2:
					printf("BG2, ");
					break;

				case 3:
					printf("BG3, ");
					break;

				case 4:
					printf("OBJ, ");
					break;
			}
		}
	}

	printf("\n");

	switch ((Memory.FillRAM[0x2130] & 0x30) >> 4)
	{
		case 0:
			s = "always on";
			break;

		case 1:
			s = "inside";
			break;

		case 2:
			s = "outside";
			break;

		case 3:
			s = "always off";
			break;
	}

	printf("Subscreen (%s): ", s);

	for (int i = 0; i < 5; i++)
	{
		if (Memory.FillRAM[0x212d] & (1 << i))
		{
			switch (i)
			{
				case 0:
					printf("BG0, ");
					break;

				case 1:
					printf("BG1, ");
					break;

				case 2:
					printf("BG2, ");
					break;

				case 3:
					printf("BG3, ");
					break;

				case 4:
					printf("OBJ, ");
					break;
			}
		}
	}

	printf("\n");

	if ((Memory.FillRAM[0x2131] & 0x3f))
	{
		if (Memory.FillRAM[0x2131] & 0x80)
		{
			if (Memory.FillRAM[0x2130] & 0x02)
				printf("Subscreen subtract");
			else
				printf("Fixed colour subtract");
		}
		else
		{
			if (Memory.FillRAM[0x2130] & 0x02)
				printf("Subscreen addition");
			else
				printf("Fixed colour addition");
		}

		if (Memory.FillRAM[0x2131] & 0x40)
			printf("(half): ");
		else
			printf(": ");

		for (int i = 0; i < 6; i++)
		{
			if (Memory.FillRAM[0x2131] & (1 << i))
			{
				switch (i)
				{
					case 0:
						printf("BG0, ");
						break;

					case 1:
						printf("BG1, ");
						break;

					case 2:
						printf("BG2, ");
						break;

					case 3:
						printf("BG3, ");
						break;

					case 4:
						printf("OBJ, ");
						break;

					case 5:
						printf("BACK, ");
						break;
				}
			}
		}

		printf("\n");
	}

	printf("Window 1 (%d, %d, %02x, %02x): ", PPU.Window1Left, PPU.Window1Right, Memory.FillRAM[0x212e], Memory.FillRAM[0x212f]);

	for (int i = 0; i < 6; i++)
	{
		if (PPU.ClipWindow1Enable[i])
		{
			switch (i)
			{
				case 0:
					printf("BG0(%s-%s), ", PPU.ClipWindow1Inside[0] ? "I" : "O", debug_clip_fn(PPU.ClipWindowOverlapLogic[0]));
					break;

				case 1:
					printf("BG1(%s-%s), ", PPU.ClipWindow1Inside[1] ? "I" : "O", debug_clip_fn(PPU.ClipWindowOverlapLogic[1]));
					break;

				case 2:
					printf("BG2(%s-%s), ", PPU.ClipWindow1Inside[2] ? "I" : "O", debug_clip_fn(PPU.ClipWindowOverlapLogic[2]));
					break;

				case 3:
					printf("BG3(%s-%s), ", PPU.ClipWindow1Inside[3] ? "I" : "O", debug_clip_fn(PPU.ClipWindowOverlapLogic[3]));
					break;

				case 4:
					printf("OBJ(%s-%s), ", PPU.ClipWindow1Inside[4] ? "I" : "O", debug_clip_fn(PPU.ClipWindowOverlapLogic[4]));
					break;

				case 5:
					printf("COL(%s-%s), ", PPU.ClipWindow1Inside[5] ? "I" : "O", debug_clip_fn(PPU.ClipWindowOverlapLogic[5]));
					break;
			}
		}
	}

	printf("\n");

	printf("Window 2 (%d, %d): ", PPU.Window2Left, PPU.Window2Right);

	for (int i = 0; i < 6; i++)
	{
		if (PPU.ClipWindow2Enable[i])
		{
			switch (i)
			{
				case 0:
					printf("BG0(%s), ", PPU.ClipWindow2Inside[0] ? "I" : "O");
					break;

				case 1:
					printf("BG1(%s), ", PPU.ClipWindow2Inside[1] ? "I" : "O");
					break;

				case 2:
					printf("BG2(%s), ", PPU.ClipWindow2Inside[2] ? "I" : "O");
					break;

				case 3:
					printf("BG3(%s), ", PPU.ClipWindow2Inside[3] ? "I" : "O");
					break;

				case 4:
					printf("OBJ(%s), ", PPU.ClipWindow2Inside[4] ? "I" : "O");
					break;

				case 5:
					printf("COL(%s), " , PPU.ClipWindow2Inside[5] ? "I" : "O");
					break;
			}
		}
	}

	printf("\n");

	printf("Fixed colour: %02x%02x%02x, \n", PPU.FixedColourRed, PPU.FixedColourGreen, PPU.FixedColourBlue);
}

static void debug_whats_missing (void)
{
	printf("Processor: ");

	if (missing.emulate6502)
		printf("emulation mode, ");

	if (missing.decimal_mode)
		printf("decimal mode, ");

	if (missing.mv_8bit_index)
		printf("MVP/MVN with 8bit index registers and XH or YH > 0, ");

	if (missing.mv_8bit_acc)
		printf("MVP/MVN with 8bit accumulator > 255, ");

	printf("\n");

	printf("Screen modes used: ");

	for (int i = 0; i < 8; i++)
		if (missing.modes[i])
			printf("%d, ", i);

	printf("\n");

	if (missing.interlace)
		printf("Interlace, ");

	if (missing.pseudo_512)
		printf("Pseudo 512 pixels horizontal resolution, ");

	if (missing.lines_239)
		printf("240 lines visible, ");

	if (missing.sprite_double_height)
		printf("double-hight sprites, ");

	printf("\n");

	if (missing.mode7_fx)
		printf("Mode 7 rotation/scaling, ");

	if (missing.matrix_read)
		printf("Mode 7 read matrix registers, ");

	if (missing.mode7_flip)
		printf("Mode 7 flipping, ");

	if (missing.mode7_bgmode)
		printf("Mode 7 priority per pixel, ");

	if (missing.direct)
		printf("Direct 32000 colour mode, ");

	printf("\n");

	if (missing.mosaic)
		printf("Mosaic effect, ");

	if (missing.subscreen)
		printf("Subscreen enabled, ");

	if (missing.subscreen_add)
		printf("Subscreen colour add, ");

	if (missing.subscreen_sub)
		printf("Subscreen colour subtract, ");

	if (missing.fixed_colour_add)
		printf("Fixed colour add, ");

	if (missing.fixed_colour_sub)
		printf("Fixed colour subtract, ");

	printf("\n");

	printf("Window 1 enabled on: ");
	debug_print_window(missing.window1);

	printf("\n");
	
	printf("Window 2 enabled on: ");
	debug_print_window(missing.window2);

	printf("\n");

	if (missing.bg_offset_read)
		printf("BG offset read, ");

	if (missing.oam_address_read)
		printf("OAM address read, ");

	if (missing.sprite_priority_rotation)
		printf("Sprite priority rotation, ");

	if (missing.fast_rom)
		printf("Fast 3.58MHz ROM access enabled, ");

	if (missing.matrix_multiply)
		printf("Matrix multiply 16bit by 8bit used, ");

	printf("\n");

	if (missing.virq)
		printf("V-IRQ used at line %d, ", missing.virq_pos);

	if (missing.hirq)
		printf("H-IRQ used at position %d, ", missing.hirq_pos);

	printf("\n");

	if (missing.h_v_latch)
		printf("H and V-Pos latched, ");

	if (missing.h_counter_read)
		printf("H-Pos read, ");

	if (missing.v_counter_read)
		printf("V-Pos read, ");

	printf("\n");

	if (missing.oam_read)
		printf("OAM read, ");

	if (missing.vram_read)
		printf("VRAM read, ");

	if (missing.cgram_read)
		printf("CG-RAM read, ");

	if (missing.wram_read)
		printf("WRAM read, ");

	if (missing.dma_read)
		printf("DMA read, ");

	if (missing.vram_inc)
		printf("VRAM inc: %d, ", missing.vram_inc);

	if (missing.vram_full_graphic_inc)
		printf("VRAM full graphic inc: %d, ", missing.vram_full_graphic_inc);

	printf("\n");

	for (int i = 0; i < 8; i++)
	{
		if (missing.hdma[i].used)
		{
			printf("HDMA %d 0x%02X%04X->0x21%02X %s, ",
			       i, missing.hdma[i].abus_bank, missing.hdma[i].abus_address, missing.hdma[i].bbus_address,
			       missing.hdma[i].indirect_address ? "indirect" : "absolute");

			if (missing.hdma[i].force_table_address_write)
				printf("Forced address write, ");

			if (missing.hdma[i].force_table_address_read)
				printf("Current address read, ");

			if (missing.hdma[i].line_count_write)
				printf("Line count write, ");

			if (missing.hdma[i].line_count_read)
				printf("Line count read, ");

			printf("\n");
		}
	}

	for (int i = 0; i < 8; i++)
	{
		if (missing.dma_channels & (1 << i))
		{
			printf("DMA %d [%d] 0x%02X%04X->0x21%02X Num: %d %s, \n",
			       i, DMA[i].TransferMode, DMA[i].ABank, DMA[i].AAddress, DMA[i].BAddress, DMA[i].TransferBytes,
			       DMA[i].AAddressFixed ? "fixed" : (DMA[i].AAddressDecrement ? "dec" : "inc"));
		}
	}

	if (missing.unknownppu_read)
		printf("Read from unknown PPU register: $%04X, \n", missing.unknownppu_read);

	if (missing.unknownppu_write)
		printf("Write to unknown PPU register: $%04X, \n", missing.unknownppu_write);

	if (missing.unknowncpu_read)
		printf("Read from unknown CPU register: $%04X, \n", missing.unknowncpu_read);

	if (missing.unknowncpu_write)
		printf("Write to unknown CPU register: $%04X, \n", missing.unknowncpu_write);

	if (missing.unknowndsp_read)
		printf("Read from unknown DSP register: $%04X, \n", missing.unknowndsp_read);

	if (missing.unknowndsp_write)
		printf("Write to unknown DSP register: $%04X, \n", missing.unknowndsp_write);
}

void S9xDoDebug (void)
{
	char	Line[513];

	Debug.Dump.Bank = 0;
	Debug.Dump.Address = 0;
	Debug.Unassemble.Bank = 0;
	Debug.Unassemble.Address = 0;

	S9xTextMode();

	strcpy(Line, "r");
	S9xDebugProcessCommand(Line);

	while (CPU.Flags & DEBUG_MODE_FLAG)
	{
		int32	Cycles;
		char	*p;

		printf("> ");
		fflush(stdout);

		p = fgets(Line, sizeof(Line) - 1, stdin);
		Line[strlen(Line) - 1] = 0;

		Cycles = CPU.Cycles;
		S9xDebugProcessCommand(Line);
		CPU.Cycles = Cycles;
	}

	if (!(CPU.Flags & SINGLE_STEP_FLAG))
		S9xGraphicsMode();
}

void S9xTrace (void)
{
	char	msg[512];

	ENSURE_TRACE_OPEN(trace, "trace.log", "a")

	debug_cpu_op_print(msg, Registers.PB, Registers.PCw);
	fprintf(trace, "%s\n", msg);
}

void S9xSA1Trace (void)
{
	char	msg[512];

	ENSURE_TRACE_OPEN(trace2, "trace_sa1.log", "a")

	debug_sa1_op_print(msg, SA1Registers.PB, SA1Registers.PCw);
	fprintf(trace2, "%s\n", msg);
}

void S9xTraceMessage (const char *s)
{
	if (s)
	{
		if (trace)
			fprintf(trace, "%s\n", s);
		else
		if (trace2)
			fprintf(trace2, "%s\n", s);
	}
}

void S9xTraceFormattedMessage (const char *s, ...)
{
	char	msg[512];

	if (s)
	{
		va_list	argptr;

		va_start(argptr, s);
		vsprintf(msg, s, argptr);
		va_end(argptr);

		S9xTraceMessage(msg);
	}
}

void S9xPrintHVPosition (char *s)
{
	sprintf(s, "HC:%04ld VC:%03ld FC:%02d", (long) CPU.Cycles, (long) CPU.V_Counter, IPPU.FrameCount);
}

#endif
