snes9x/debug.cpp

2454 lines
56 KiB
C++

/*****************************************************************************\
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