Files
ci-libretro-fceumm/src/debug.c

498 lines
13 KiB
C
Raw Normal View History

/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2003 Xodnizel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "fceu-types.h"
#include "x6502.h"
#include "fceu.h"
#include "debug.h"
#include "cart.h"
void FCEUI_DumpVid(const char *fname, uint32 start, uint32 end) {
FILE *fp = FCEUD_UTF8fopen(fname, "wb");
fceuindbg = 1;
start &= 0x3FFF;
end &= 0x3FFF;
for (; start <= end; start++)
fputc(VPage[start >> 10][start], fp);
fclose(fp);
fceuindbg = 0;
}
void FCEUI_DumpMem(const char *fname, uint32 start, uint32 end) {
FILE *fp = FCEUD_UTF8fopen(fname, "wb");
fceuindbg = 1;
for (; start <= end; start++)
fputc(ARead[start](start), fp);
fclose(fp);
fceuindbg = 0;
}
void FCEUI_LoadMem(const char *fname, uint32 start, int hl) {
int t;
FILE *fp = FCEUD_UTF8fopen(fname, "rb");
while ((t = fgetc(fp)) >= 0) {
if (start > 0xFFFF) break;
if (hl) {
extern uint8 *Page[32];
if (Page[start / 2048])
Page[start / 2048][start] = t;
} else
BWrite[start](start, t);
start++;
}
fclose(fp);
}
#ifdef FCEUDEF_DEBUGGER
static char *fstrings[12] =
{
"#$%02X", // immediate
"$%04X", // RELATIVE(jump)
"$%02X", // Z
"$%02X,X", // Z,x
"$%02X,Y", // Z,y
"$%04X", //ABS
"$%04X,X", // ABS,x
"$%04X,Y", // ABS,y
"($%04X)", // IND
"($%02X,X)", // INX
"($%02X),Y", // INY
""
};
static int flengths[12] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 0 };
#define IMD(x) ((0 << 16) | x)
#define REL(x) ((1 << 16) | x)
#define ZP(x) ((2 << 16) | x)
#define ZPX(x) ((3 << 16) | x)
#define ZPY(x) ((4 << 16) | x)
#define ABS(x) ((5 << 16) | x)
#define ABX(x) ((6 << 16) | x)
#define ABY(x) ((7 << 16) | x)
#define IND(x) ((8 << 16) | x)
#define INX(x) ((9 << 16) | x)
#define INY(x) ((10 << 16) | x)
#define IMP(x) ((11 << 16) | x)
typedef struct {
char *name;
int type; /* 1 for read, 2 for write, 3 for r then write. */
int32 modes[10];
} OPS;
#define NUMOPS 56
static OPS optable[NUMOPS] =
{
{ "BRK", 0, { IMP(0x00), -1 } },
{ "RTI", 0, { IMP(0x40), -1 } },
{ "RTS", 0, { IMP(0x60), -1 } },
{ "PHA", 2, { IMP(0x48), -1 } },
{ "PHP", 2, { IMP(0x08), -1 } },
{ "PLA", 1, { IMP(0x68), -1 } },
{ "PLP", 1, { IMP(0x28), -1 } },
{ "JMP", 0, { ABS(0x4C), IND(0x6C), -1 } },
{ "JSR", 0, { ABS(0x20), -1 } },
{ "TAX", 0, { IMP(0xAA), -1 } },
{ "TXA", 0, { IMP(0x8A), -1 } },
{ "TAY", 0, { IMP(0xA8), -1 } },
{ "TYA", 0, { IMP(0x98), -1 } },
{ "TSX", 0, { IMP(0xBA), -1 } },
{ "TXS", 0, { IMP(0x9A), -1 } },
{ "DEX", 0, { IMP(0xCA), -1 } },
{ "DEY", 0, { IMP(0x88), -1 } },
{ "INX", 0, { IMP(0xE8), -1 } },
{ "INY", 0, { IMP(0xC8), -1 } },
{ "CLC", 0, { IMP(0x18), -1 } },
{ "CLD", 0, { IMP(0xD8), -1 } },
{ "CLI", 0, { IMP(0x58), -1 } },
{ "CLV", 0, { IMP(0xB8), -1 } },
{ "SEC", 0, { IMP(0x38), -1 } },
{ "SED", 0, { IMP(0xF8), -1 } },
{ "SEI", 0, { IMP(0x78), -1 } },
{ "NOP", 0, { IMP(0xEA), -1 } },
{ "ASL", 1, { IMP(0x0a), ZP(0x06), ZPX(0x16), ABS(0x0E), ABX(0x1E), -1 } },
{ "DEC", 3, { ZP(0xc6), ZPX(0xd6), ABS(0xcE), ABX(0xdE), -1 } },
{ "INC", 3, { ZP(0xe6), ZPX(0xf6), ABS(0xeE), ABX(0xfE), -1 } },
{ "LSR", 3, { IMP(0x4a), ZP(0x46), ZPX(0x56), ABS(0x4E), ABX(0x5E), -1 } },
{ "ROL", 3, { IMP(0x2a), ZP(0x26), ZPX(0x36), ABS(0x2E), ABX(0x3E), -1 } },
{ "ROR", 3, { IMP(0x6a), ZP(0x66), ZPX(0x76), ABS(0x6E), ABX(0x7E), -1 } },
{ "ADC", 1, { IMD(0x69), ZP(0x65), ZPX(0x75), ABS(0x6D), ABX(0x7d), ABY(0x79),
INX(0x61), INY(0x71), -1 } },
{ "AND", 1, { IMD(0x29), ZP(0x25), ZPX(0x35), ABS(0x2D), ABX(0x3d), ABY(0x39),
INX(0x21), INY(0x31), -1 } },
{ "BIT", 1, { ZP(0x24), ABS(0x2c), -1 } },
{ "CMP", 1, { IMD(0xc9), ZP(0xc5), ZPX(0xd5), ABS(0xcD), ABX(0xdd), ABY(0xd9),
INX(0xc1), INY(0xd1), -1 } },
{ "CPX", 1, { IMD(0xe0), ZP(0xe4), ABS(0xec), -1 } },
{ "CPY", 1, { IMD(0xc0), ZP(0xc4), ABS(0xcc), -1 } },
{ "EOR", 1, { IMD(0x49), ZP(0x45), ZPX(0x55), ABS(0x4D), ABX(0x5d), ABY(0x59),
INX(0x41), INY(0x51), -1 } },
{ "LDA", 1, { IMD(0xa9), ZP(0xa5), ZPX(0xb5), ABS(0xaD), ABX(0xbd), ABY(0xb9),
INX(0xa1), INY(0xb1), -1 } },
{ "LDX", 1, { IMD(0xa2), ZP(0xa6), ZPY(0xB6), ABS(0xae), ABY(0xbe), -1 } },
{ "LDY", 1, { IMD(0xa0), ZP(0xa4), ZPX(0xB4), ABS(0xac), ABX(0xbc), -1 } },
{ "ORA", 1, { IMD(0x09), ZP(0x05), ZPX(0x15), ABS(0x0D), ABX(0x1d), ABY(0x19),
INX(0x01), INY(0x11), -1 } },
{ "SBC", 1, { IMD(0xEB), IMD(0xe9), ZP(0xe5), ZPX(0xf5), ABS(0xeD), ABX(0xfd), ABY(0xf9),
INX(0xe1), INY(0xf1), -1 } },
{ "STA", 2, { ZP(0x85), ZPX(0x95), ABS(0x8D), ABX(0x9d), ABY(0x99),
INX(0x81), INY(0x91), -1 } },
{ "STX", 2, { ZP(0x86), ZPY(0x96), ABS(0x8E), -1 } },
{ "STY", 2, { ZP(0x84), ZPX(0x94), ABS(0x8C), -1 } },
{ "BCC", 1, { REL(0x90), -1 } },
{ "BCS", 1, { REL(0xb0), -1 } },
{ "BEQ", 1, { REL(0xf0), -1 } },
{ "BNE", 1, { REL(0xd0), -1 } },
{ "BMI", 1, { REL(0x30), -1 } },
{ "BPL", 1, { REL(0x10), -1 } },
{ "BVC", 1, { REL(0x50), -1 } },
{ "BVS", 1, { REL(0x70), -1 } },
};
uint16 FCEUI_Disassemble(void *XA, uint16 a, char *stringo) {
X6502 *X = XA;
uint8 buf;
uint32 arg;
int32 info;
int x;
int y;
info = -1;
fceuindbg = 1;
buf = ARead[a](a);
a++;
for (x = 0; x < NUMOPS; x++) {
y = 0;
while (optable[x].modes[y] >= 0) {
if ((optable[x].modes[y] & 0xFF) == buf) {
info = optable[x].modes[y];
goto endy;
}
y++;
}
}
endy:
sprintf(stringo, "%02X ", buf);
if (info >= 0) {
int z = flengths[(info >> 16)];
if (z) {
arg = ARead[a](a);
sprintf(stringo + strlen(stringo), "%02X ", arg);
a++;
if (z == 2) {
arg |= ARead[a](a) << 8; sprintf(stringo + strlen(stringo), "%02X ", arg >> 8); a++;
} else
strcat(stringo, " ");
if ((info >> 16) == 1) /* Relative branch */
arg = a + (char)arg;
sprintf(stringo + strlen(stringo), "%s ", optable[x].name);
sprintf(stringo + strlen(stringo), fstrings[info >> 16], arg);
/*
0 "#$%02X", // immediate
1 "$%04X", // RELATIVE(jump)
2 "$%02X", // Z
3 "$%02X,X", // Z,x
4 "$%02X,Y", // Z,y
5 "$%04X", //ABS
6 "$%04X,X", // ABS,x
7 "$%04X,Y", // ABS,y
8 "($%04X)", // IND
9 "($%02X,X)", // INX
10 "($%02X),Y", // INY
11 #define IMP(x) ((11<<16)|x)
*/
{
uint32 tmp;
switch (info >> 16) {
case 2: tmp = arg;
if (optable[x].type & 1) {
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
}
break;
case 3: tmp = (arg + X->X) & 0xff;
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
if (optable[x].type & 1)
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
break;
case 4: tmp = (arg + X->Y) & 0xff;
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
if (optable[x].type & 1)
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
break;
case 5: tmp = arg;
if (optable[x].type & 1) {
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
}
break;
case 6: tmp = (arg + X->X) & 0xffff;
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
if (optable[x].type & 1)
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
break;
case 7: tmp = (arg + X->Y) & 0xffff;
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
if (optable[x].type & 1)
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
break;
case 8: tmp = ARead[arg](arg) | (ARead[(arg + 1) & 0xffff]((arg + 1) & 0xffff) << 8);
sprintf(stringo + strlen(stringo), " $%04X", tmp);
break;
case 9: tmp = (arg + X->X) & 0xFF;
tmp = ARead[tmp](tmp) | (ARead[(tmp + 1) & 0xFF]((tmp + 1) & 0xFF) << 8);
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
if (optable[x].type & 1)
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
break;
case 10: tmp = ARead[arg](arg) | (ARead[(arg + 1) & 0xFF]((arg + 1) & 0xFF) << 8);
tmp = (tmp + X->Y) & 0xFFFF;
sprintf(stringo + strlen(stringo), " @ $%04X", tmp);
if (optable[x].type & 1)
sprintf(stringo + strlen(stringo), " = $%02X", ARead[tmp](tmp));
break;
}
}
} else {
strcat(stringo, " ");
strcat(stringo, optable[x].name);
}
} else
sprintf(stringo + strlen(stringo), " .db $%02X", buf);
fceuindbg = 0;
return(a);
}
void FCEUI_MemDump(uint16 a, int32 len, void (*callb)(uint16 a, uint8 v)) {
fceuindbg = 1;
while (len) {
callb(a, ARead[a](a));
a++;
len--;
}
fceuindbg = 0;
}
uint8 FCEUI_MemSafePeek(uint16 A) {
uint8 ret;
fceuindbg = 1;
ret = ARead[A](A);
fceuindbg = 0;
return(ret);
}
void FCEUI_MemPoke(uint16 a, uint8 v, int hl) {
extern uint8 *Page[32];
if (hl) {
if (Page[a / 2048])
Page[a / 2048][a] = v;
} else
BWrite[a](a, v);
}
typedef struct __BPOINT {
struct __BPOINT *next;
void (*Handler)(X6502 *X, int type, uint32 A);
uint32 A[2];
int type;
} BPOINT;
static BPOINT *BreakPoints = NULL;
static BPOINT *LastBP = NULL;
static void (*CPUHook)(X6502 *) = NULL;
static int FindBPoint(X6502 *X, int who, uint32 A) {
BPOINT *tmp;
tmp = BreakPoints;
while (tmp) {
if (who & BPOINT_EXCLUDE) {
if ((tmp->type & who) == who) {
if ((X->PC >= tmp->A[0]) && (X->PC <= tmp->A[1])) {
return(1);
}
}
} else {
if (tmp->type & who) {
if (tmp->type & BPOINT_PC)
if (X->PC != A) goto don;
if (tmp->type & BPOINT_EXCLUDE) goto don;
if ((A >= tmp->A[0]) && (A <= tmp->A[1])) {
if (!FindBPoint(X, who | BPOINT_EXCLUDE, A)) {
tmp->Handler(X, tmp->type, A);
return(1);
}
}
}
}
don:
tmp = tmp->next;
}
return(0);
}
static uint8 ReadHandler(X6502 *X, uint32 A) {
extern X6502 XSave;
if (X->preexec)
FindBPoint(&XSave, BPOINT_READ, A);
return(ARead[A](A));
}
static void WriteHandler(X6502 *X, uint32 A, uint8 V) {
extern X6502 XSave;
if (X->preexec)
FindBPoint(&XSave, BPOINT_WRITE, A);
else
BWrite[A](A, V);
}
int FCEUI_AddBreakPoint(int type, uint32 A1, uint32 A2,
void (*Handler)(X6502 *, int type, uint32 A)) {
BPOINT *tmp;
tmp = (BPOINT*)malloc(sizeof(BPOINT));
tmp->A[0] = A1;
tmp->A[1] = A2;
tmp->Handler = Handler;
tmp->type = type;
tmp->next = 0;
if (BreakPoints == NULL)
BreakPoints = tmp;
else
LastBP->next = tmp;
LastBP = tmp;
X6502_Debug(CPUHook, ReadHandler, WriteHandler);
return(1);
}
int FCEUI_SetBreakPoint(uint32 w, int type, uint32 A1, uint32 A2,
void (*Handler)(X6502 *, int type, uint32 A)) {
uint32 x = 0;
BPOINT *tmp;
tmp = BreakPoints;
while (tmp) {
if (w == x) {
tmp->type = type;
tmp->A[0] = A1;
tmp->A[1] = A2;
tmp->Handler = Handler;
return(1);
}
x++;
tmp = tmp->next;
}
return(0);
}
int FCEUI_GetBreakPoint(uint32 w, int *type, uint32 *A1, uint32 *A2,
void(**Handler) (X6502 *, int type, uint32 A)) {
uint32 x = 0;
BPOINT *tmp;
tmp = BreakPoints;
while (tmp) {
if (w == x) {
*type = tmp->type;
*A1 = tmp->A[0];
*A2 = tmp->A[1];
*Handler = tmp->Handler;
return(1);
}
x++;
tmp = tmp->next;
}
return(0);
}
int FCEUI_ListBreakPoints(int (*callb)(int type, uint32 A1, uint32 A2,
void (*Handler)(X6502 *, int type, uint32 A))) {
BPOINT *tmp;
tmp = BreakPoints;
while (tmp) {
callb(tmp->type, tmp->A[0], tmp->A[1], tmp->Handler);
tmp = tmp->next;
}
return(1);
}
int FCEUI_DeleteBreakPoint(uint32 w) {
BPOINT *tmp, *prev = NULL;
uint32 x = 0;
tmp = BreakPoints;
while (tmp) {
if (w == x) {
if (prev) { /* Not the first breakpoint. */
if (tmp->next) /* More breakpoints. */
prev->next = tmp->next;
else { /* This is the last breakpoint. */
prev->next = 0;
LastBP = prev;
}
} else {/* The first breakpoint. */
if (tmp->next) /* More breakpoints. */
BreakPoints = tmp->next;
else {
BreakPoints = LastBP = 0; /* No more breakpoints. */
/* Update the CPU hooks. */
X6502_Debug(CPUHook, BreakPoints ? ReadHandler : 0, BreakPoints ? WriteHandler : 0);
}
}
free(tmp);
tmp = 0;
return(1);
}
prev = tmp;
tmp = tmp->next;
x++;
}
return(0);
}
void FCEUI_SetCPUCallback(void (*callb)(X6502 *X)) {
CPUHook = callb;
X6502_Debug(CPUHook, BreakPoints ? ReadHandler : 0, BreakPoints ? WriteHandler : 0);
}
#endif