/* 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 #include #include #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