📄 cpu.cpp
字号:
// ^ Pointer to pre-table
*(word*)REG4305(x) = *(word*)SNESMEM ((*(dword*)REG4302(x) & 0xFFFFFF) + 1);
//*((byte*)REG4305(x) + 2) = *((byte*)REG4302(x) + 2); // Bank
//Apparently the bank should be set in regular HDMA and not indirect HDMA
// ^ Pointer to final table (data values)
} else { // Regular HDMA
*(word*)REG4305(x) = *(word*)REG4302(x); // Not used in regular HDMA? Or perhaps used for continuous mode. Yes, that's it.
*((byte*)REG4305(x) + 2) = *((byte*)REG4302(x) + 2); // Bank
*REG4308(x) = *(word*)REG4302(x); // Pointer to table
}
*REG430A(x) = getcount (x);
//*REG430A(x) = *SNESMEM(*REG4302(x) & 0xFFFFFF) - 1;
// Get count byte, first byte of table
debug0 ("HDMA Channel %d mode $%X cpu$%X reg$%X waitcount%d", x, *REG4300(x), *REG4302(x) & 0xFFFFFF, *REG4301(x) | 0x2100, *REG430A(x));
//tmp = SNESMEM (*REG4302(x) & 0xFFFFFF);
//for (y=0; y < 3; y++) {
// debug0 ("Table: %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9]);
// tmp += 10;
//}
}
debug0 ("Inprogress this frame: %0.2X", state.hdma_inprogress);
//dohdmaline(); // First line
}
inline int dmadatasize (int mode)
{
static int modes[] = { 1,2,2,4,4,0,0,0 };
return modes [mode & 7];
}
int writehdma (int mode, int addr, int breg)
{ // Returns size of data written.
int size, regand;
byte *cpu, *reg;
int y;
switch (mode & 0x7) {
case 0: size = 1; regand = 0; break;
case 1: size = 2; regand = 1; break;
case 2: size = 2; regand = 0; break;
case 3: size = 4; regand = 2; break;
case 4: size = 4; regand = 3; break;
default:size = 0; regand = 0; debug0 ("Invalid HDMA Mode %d b$%X",mode,breg); break;
}
//debug0 ("HDMA sc%d mem%X %X %X %X ", screen_scanline, *SNESMEM(addr), *SNESMEM(addr+1), *SNESMEM(addr+2), *SNESMEM(addr+3));
for (y = 0; y < size; y++) {
cpu = SNESMEM (addr + y); // Value to write
reg = SNESMEM (0x2100 | (breg + (regand == 2 ? (y >> 1) : (y & regand))));
*reg = *cpu;
trapregwrite (reg, false);
}
return size;
}
void getpointers (int x, int &addr, int &iaddr)
{
if (!(*REG4300(x) & 0x40)) {
addr = HBCOMBINE (*REG4308(x));
// addr is SNES address of HDMA table at current offset
iaddr = (*(dword*)REG4305(x) + 1) & 0xFFFFFF; // Equal to addr+1 when NOT in continuous mode.
// iaddr is address of the actual value(s) to write
} else {
// REG4305,6,7 used as address after indirection; 4308,9 used before.
addr = HBCOMBINE (*REG4308(x));
// addr is SNES address of HDMA pre-table at current offset
iaddr = *(dword*)REG4305(x) & 0xFFFFFF; // Equal to addr+1 when NOT in continuous mode.
// iaddr is address of the actual value(s) to write
}
}
void dohdmaline ()
{
int x;
int addr, iaddr, size;
for (x = 0; x < 8; x++) {
if (!(state.hdma_inprogress & (1 << x)))
continue;
if (*REG430A(x) == 0) {
if (!(*REG4300(x) & 0x40)) {
(*REG4305(x))++;
*REG4308(x) = *REG4305(x);
} else {
(*REG4308(x)) += 3; // Pre-table entries are always spaced three bytes apart.
*REG4305(x) = *(word*)SNESMEM(HBCOMBINE(*REG4308(x)+1));
}
*REG430A(x) = getcount (x);
if (!(state.hdma_inprogress & (1 << x)))
continue;
}
getpointers (x, addr, iaddr);
if (*REG430A(x) == getcount (x) || *SNESMEM(addr) & 0x80) {
size = writehdma (*REG4300(x), iaddr, *REG4301(x));
*(word*)REG4305(x) += size;
}
(*REG430A(x))--;
}
}
inline void executecpu ()
{
#ifdef USE_C_CPU_CORE
switch (reg.P & (MEMORY | INDEX)) {
case 0:
emulate_m0_x0 (); return;
case MEMORY:
emulate_m1_x0 (); return;
case INDEX:
emulate_m0_x1 (); return;
case MEMORY | INDEX:
emulate_m1_x1 (); return;
}
#else
doemulate ();
#endif
}
void debug_executecpu (int cycles)
{
int left, temp;
// frame cycles initted to 0, so cpu frame is always started at the beginning of the frame.
// If debugger is entered from game, new frame will already be initted.
if (frame_cycles <= 0) {
startcpuframe ();
total_frames++;
startscanline ();
debug0 ("Frame %d: debug_instr=%d", total_frames, debug_instr);
}
temp = scan_cycles;
scan_cycles = cycles;
executecpu ();
left = scan_cycles;
scan_cycles = temp + left - cycles;
if (scan_cycles < 0) {
frame_cycles -= cycles_per_scan;
scan_cycles += cycles_per_scan;
screen_scanline++;
startscanline ();
//if (screen_scanline < 10 || screen_scanline > 220)
// debug0 ("scanline %d new scan_cycles %d frame_cycles %d PC$%X", screen_scanline, scan_cycles, frame_cycles, reg.PC);
}
}
boolean debug_nmistart;
void startvblank ()
{
int x;
state.reg4210 |= 0x80; // VBL start occurred--Required even if screen is off
if (!(*REG4200 & 0x80)) {
return; // NMI Disabled
}
if (reg.E) x = *(word*)SNESMEM(0x00FFFA);
else x = *(word*)SNESMEM(0x00FFEA);
if (x != 0) {
if (debugmode) adddebugline ("[Jumped to NMI vector]", false, '*');
state.end_wai = true;
*(byte*)SNESMEM(reg.S) = (byte)(reg.PC >> 16); (*(word*)®.S)--;
*(word*)SNESMEM(reg.S-1) = (word)(reg.PC); (*(word*)®.S) -= 2;
*(byte*)SNESMEM(reg.S) = (byte)(reg.P); (*(word*)®.S)--;
// Push PC and P
reg.P |= 4; // Interrupt flag
reg.PC = x;
debug0 ("Jumped to NMI Vector: %LX; E = %d", reg.PC, reg.E);
debug_nmistart = true;
}
}
void startcpuframe ()
{
int hz;
state.reg4210 &= ~0x80; // NMI over
// decide on how many cycles to do this frame
if (curheader.fastrom && (*SNESMEM (0x420D) & 1) && !curgs->ignorefastrom) { // fastrom
hz = 3580000 * curgs->cpupercent / 100;
curfastrom = true;
} else {
hz = 2680000 * curgs->cpupercent / 100;
curfastrom = false;
}
if (ispal ()) {
frame_cycles = hz / 50;
} else {
frame_cycles = hz / 60;
}
cycles_per_scan = scan_cycles = frame_cycles / 260;
screen_scanline = 0; // start with scanline 0
// Restart HDMA here
resethdma ();
resetscreenframe ();
setjoypadreg (0);
setjoypadreg (1);
setjoypadreg (2);
setjoypadreg (3);
}
void startscanline ()
{
int x;
if (!(reg.P & 0x4) /*&& !(state.reg4211 & 0x80)*/) { // IRQs possible
//if (((*REG4200 & 0x10) && ((*REG4207 & 0x1FF) <= 339)) || // H-IRQs
// ((*REG4200 & 0x20) && ((*REG4209 & 0x1FF) == screen_scanline))) { // V-IRQs
if ((*REG4200 & 0x30) && (*REG4209 & 0x1FF) == screen_scanline) {
state.reg4211 |= 0x80; // IRQ start occurred
if (reg.E) x = *(word*)SNESMEM(0x00FFFE);
else x = *(word*)SNESMEM(0x00FFEE);
if (x != 0) {
state.end_wai = true;
*(byte*)SNESMEM(reg.S) = (byte)(reg.PC >> 16); (*(word*)®.S)--;
*(word*)SNESMEM(reg.S-1) = (word)(reg.PC); (*(word*)®.S) -= 2;
*(byte*)SNESMEM(reg.S) = (byte)(reg.P); (*(word*)®.S)--;
// Push PC and P
reg.P |= 4; // Interrupt flag
reg.PC = x;
}
}
}
if (screen_scanline == scans_before_vbl()) {
startvblank (); // After the frame is done, do a VBL int
}
dohdmaline ();
if (!debugmode && screen_scanline < scans_before_vbl() && frameskipwait < 0) {
drawnextscanline ();
}
}
#define MAXDEBUGHISTORY 20
#define MAXDEBUGLINELEN 40
char *debug_input(int &scancode)
{ // gets a debug command from user.
// if scancode != 0, special key was pressed and returned
static char line [MAXDEBUGHISTORY][MAXDEBUGLINELEN];
// [0]=most recent line
static int history = 0;
int key, x, seekline = 0;
scancode = 0;
for (;;) {
while (!kbhit()) {
drawbox (0, 238, 255, 248, 0);
setcolor (30); printf8 (0, 240, "Command:");
setcolor (63); printf8 (50, 240, "%s%c", line[0], (tmsec & 128) ? '_' : ' ');
copyscreen ();
}
key = getch ();
if (key == 8 && strlen (line[0]) > 0)
line[0][strlen(line[0])-1] = '\0';
if (key == 13) {
if (line [0][0] == '\0')
return line[0];
for (x = MAXDEBUGHISTORY-1; x > 0; x--) {
strcpy (line[x], line[x-1]);
}
if (history < MAXDEBUGHISTORY)
history++;
line [0][0] = '\0';
return line [1];
}
if (key >= 32 && key <= 127) {
x = strlen (line[0]);
if (x < MAXDEBUGLINELEN) {
line[0][x] = toupper (key);
line[0][x + 1] = '\0';
}
}
if (key == 0) {
key = getch ();
switch (key) {
case 'H': // up
if (seekline < MAXDEBUGHISTORY - 1) {
seekline++;
strcpy (line[0], line[seekline]);
}
break;
case 'P': // down
if (seekline > 0) {
seekline--;
strcpy (line[0], line[seekline]);
}
break;
}
if ((key >= 59 && key <= 68) || key == 87 || key == 88) {
scancode = key;
return line[0];
}
}
if (key == 27) {
scancode = key;
return line[0];
}
}
}
int grabnumber (char *line, int &i)
{
int x;
i = 0;
while ((line[i] < '0' || line[i] > '9') && line[i] != '-' && line[i] != '$') {
if (line[i] == '\0')
return 0;
i++;
}
if (line[i] == '$') {
i++;
x = 0;
while ((line[i] >= '0' && line[i] <= '9') || (line[i] >= 'A' && line[i] <= 'F')) {
x <<= 4;
if (line[i] >= '0' && line[i] <= '9')
x += line[i] - '0';
if (line[i] >= 'A' && line[i] <= 'F')
x += line[i] - 'A' + 10;
i++;
}
} else {
x = 0;
while (line[i] >= '0' && line[i] <= '9') {
x = x * 10 + (line[i] - '0');
i++;
}
}
return x;
}
void dodebugmode ()
{
char *debugline, debuglinetext [55];
boolean newinstr;
dword opdata;
int x, i, n, n2, esccount, key;
FILE *fp;
#define DEBUG_DOESCCHECK esccount = (esccount + 1) & 0x3F; if (esccount == 0 && kbhit() && getch () == 27) break
(void) fp;
newinstr = true;
debugscreen = 1;
applycustompalette ();
while (!returntogui && debugmode) {
if (newinstr) {
newinstr = false;
opdata = *(dword*)SNESMEM (reg.PC);
unassemble (opdata, debuglinetext, reg.P);
adddebugline (debuglinetext, true);
applycustompalette ();
if ((reg.A_dummy & 0xFFFF0000) || (reg.X_dummy & 0xFFFF0000) || (reg.Y_dummy & 0xFFFF0000)) {
sprintf (debuglinetext, "Err! A$%0.8X X%0.8X Y%0.8X", reg.A_dummy, reg.X_dummy, reg.Y_dummy);
adddebugline (debuglinetext, false, '@');
}
if ((reg.D_dummy & 0xFFFF0000) || (reg.S_dummy & 0xFFFF0000) || (reg.P_dummy & 0xFFFFFF00)) {
sprintf (debuglinetext, "Err! D$%0.8X S%0.8X P%0.8X", reg.D_dummy, reg.S_dummy, reg.P_dummy);
adddebugline (debuglinetext, false, '@');
}
if (reg.DBR_dummy1 || reg.DBR_dummy2 || reg.DBR_dummy3 || (reg.PC & 0xFF000000)) {
sprintf (debuglinetext, "Err! D1$%0.2X D2$%0.2X D3$%0.2X PC$%0.8X", reg.DBR_dummy1, reg.DBR_dummy2, reg.DBR_dummy3, reg.PC);
adddebugline (debuglinetext, false, '@');
}
}
rendersnesscreen ();
debugline = debug_input (key);
if (key == 0) {
if (debugline[0] == '\0') {
debug_executecpu (0);
newinstr = true;
}
for (x = 0; debugline[x] != '\0'; x++) {
while (debugline[x] == ' ')
x++;
if (debugline [x] >= '0' && debugline[x] <= '9')
debugscreen = debugline[x] - '0';
switch (debugline [x]) {
case 'T': // Trace
n = grabnumber (debugline + x + 1, i);
x += i;
sprintf (debuglinetext, "[Tracing for %d instructions; ESC stops]", n);
adddebugline (debuglinetext, false, '*');
rendersnesscreen ();
copyscreen ();
while (n > 0 && !returntogui) {
debug_executecpu(0);
n--;
DEBUG_DOESCCHECK;
}
newinstr = true;
break;
case 'W': // Wait for instruction
while (debugline[x+1] == ' ')
x++;
for (n = 0; n < 256; n++) {
if (stricmp (opcodelist[n].format, debugline + x+1) == 0) {
break;
}
}
if (n >= 256) {
adddebugline ("[Format spec not found, waiting for string]", false, '*');
do {
debug_executecpu(0);
DEBUG_DOESCCHECK;
opdata = *(dword*)SNESMEM (reg.PC);
unassemble (opdata, debuglinetext, reg.P);
for (i = 0; *(debugline+x+1+i); i++)
if (*(debugline+x+1+i) == '?' && debuglinetext[i] != '\0')
debuglinetext[i] = '?';
} while (strcmp (debugline+x+1, debuglinetext) != 0 && !returntogui);
x += strlen (debugline + x+1);
newinstr = true;
break;
}
x += strlen (debugline + x+1);
sprintf (debuglinetext, "[Tracing for opcode $%X; ESC stops]", n, opcodelist[n].format);
adddebugline (debuglinetext, false, '*');
rendersnesscreen ();
copyscreen ();
do {
debug_executecpu(0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -