⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpu.cpp

📁 SNES game emulator. C and asm files.
💻 CPP
📖 第 1 页 / 共 5 页
字号:
/*  CPU.CPP - 65c816 emulation module and main "run" module
	- The main emulation routine is called runsnes ()
	  Allow the user to go to the GUI by pressing ESC.  While in the GUI you can
	  return to the debugger where you left off by clicking Return to Game.
	- The resetsystem () function is supposed to reset the game.
	  This function is called right after a rom is loaded, so you can init the
	  SNES.  It can also be called when you click the Reset button in the Startup
	  dialog box.  This function should not clear the SRAM.
	- When you have time, write the instantload() and instantsave () functions.
	  I already wrote the SRAM save/load function, which automatically decides
	  how many kilobits to save.
	- I made a new array: byte vram [0x10000]; I think the vram is 64K??
	- To set text mode (for your debugger) use:
	  setvideomode (0, false);
	  But if you want, I can make a full-featured GUI debugger.  I haven't written
	  any text functions (eg gotoxy ()).
	- Feel free to use the debug-out function for debugging.  You will notice when you
	  run SNEqr 2 debug files are created called DebugN.doc.  I do all my debug outs to
	  Debug0.doc so if you don't want yours mixed with mine you can use Debug1.doc.
	  The debug-out function is like this:
		void debugf (int file_num, char *format, ...);
	  It's basically just like printf ().  A time stamp is automatically recorded.
*/

#include "Common.hpp"
	// Defines true & false, includes STDIO and other common headers; defines byte,
	// word, dword and boolean data types; and includes MiscFunc.hpp, which contains
	// Timer, keyboard, mouse, joystick and debugging functions.
#include "GrEngine.hpp"
#include "FileMan.hpp"
#include "GUI.hpp"
#include "CPU.hpp"
#include "emu65816.hpp"
#include "SPC700.hpp"
#include "Screen.hpp"
#include <ASSERT.H>

//#define USE_C_CPU_CORE

byte __cdecl *startaddr [0x800];
	/*  For quick address aaccess.  To access a 65c816 address bb.aaaa, stored in variable addr:
		startaddr [addr >> 13] + (addr & 0x1FFF)
		This array is filled by getstartaddresses ().
	*/
byte ram [0x20000], sram [0x10000], expram [0x2000];
byte vram [0x10000];
byte __cdecl registers [0x4000];
byte cgram [512], oam [544];
byte *rom = NULL;

boolean debugmode = false;
boolean returntogui;
boolean showfps = true;

int snesframe = 0;
int snesrenderedframe = 0;

int instantsaveslot = 0;

int frameskipwait = 0;

int autoback_countdown = 0, autoback_restorecount;

struct headerdata curheader;
	// This is the header data that has been extracted from the ROM image that is currently
	// loaded.

struct ppuinternalregisters state;

byte *autobacktrackmem[10] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
int autobackpos[9], autobacksize[9];

void bcdaddadjust_byte (byte &A)
{
	if ((A & 15) > 9) A += 6;
	if (((A >> 4) & 15) > 9) A += (6 << 4);
}
void bcdsubadjust_byte (byte &A)
{
	if ((A & 15) > 9) A -= 6;
	if (((A >> 4) & 15) > 9) A -= (6 << 4);
}
void bcdaddadjust_word (word &A)
{
	if ((A & 15) > 9) A += 6;
	if (((A >> 4) & 15) > 9) A += (6 << 4);
	if (((A >> 8) & 15) > 9) A += (6 << 8);
	if (((A >> 12) & 15) > 9) A += (6 << 12);
}
void bcdsubadjust_word (word &A)
{
	if ((A & 15) > 9) A -= 6;
	if (((A >> 4) & 15) > 9) A -= (6 << 4);
	if (((A >> 8) & 15) > 9) A -= (6 << 8);
	if (((A >> 12) & 15) > 9) A -= (6 << 12);
}

int debug_slice = -1;
long __cdecl debug_instr = 0;
int __cdecl frame_cycles, scan_cycles, screen_scanline, total_frames, cycles_per_scan;

void emulate_m0_x0 ();
void emulate_m0_x1 ();
void emulate_m1_x0 ();
void emulate_m1_x1 ();
// Create different versions of the emulation routine.
// These functions return the amount of cycles left.  If number is negative, the number
// of cycles "over" that occured.

int debug_trapaddr = -1; boolean debug_readtrap;

byte debug_thing;
int instr_tally [256][2];
int mmio_rtally [768];
int mmio_wtally [768];

void savetallies ()
{
#ifndef NODEBUGOUT
	char *fn, ext[4] = "DOC";
	FILE *fp;
	int x, y, c;
	char *s;
	boolean usedlist [256];

	fn = getaccessoryname (curgs->filename, instdir, ext);
	if ((fp = fopen (fn, "wt")) == NULL) {
		addmessage ("Couldn't open tally file %s...", fn);
		return;
	}
	fprintf (fp, "Addressing Mode Counts:\n");
	memset (usedlist,false,sizeof(usedlist));
	for (x = 0; x < 256; x++) {
		if (usedlist[x])
			continue;
		for (y = x, c = 0; y < 256; y++) {
			if (stricmp (opcodelist[x].format+3, opcodelist[y].format+3) == 0) {
				usedlist[y] = true;
				c += instr_tally[y][0] + instr_tally[y][0];
			}
		}
		fprintf (fp, "%d\t%s\n", c, opcodelist[x].format+3);
	}
	fprintf (fp, "\n\nOpcode Counts:\n");
	for (x = 0; x < 256; x++) {
		s = opcodelist[x].format;
		if ((s[0]=='B' && s[1]=='N' && s[2]=='E') || \
			(s[0]=='B' && s[1]=='E' && s[2]=='Q') || \
			(s[0]=='S' && s[1]=='T') || \
			(s[0]=='L' && s[1]=='D') || \
			(s[0]=='I' && s[1]=='N'))
				continue;
		if ((s[0]=='A' && s[1]=='D' && s[2]=='C') ||\
			(s[0]=='S' && s[1]=='B' && s[2]=='C')) {
			fprintf (fp, "%d/%d\t%s\n", instr_tally[x][0],instr_tally[x][1], s);
		} else {
			fprintf (fp, "%d\t%s\n", instr_tally[x][0]+instr_tally[x][1], s);
		}
	}
	fprintf (fp, "\n\nMMIO Read Tallies\n");
	for (x = 0; x < 256; x++) {
		if (mmio_rtally[x])
			fprintf (fp, "0x%X:%d\t", 0x2100 + x, mmio_rtally[x]);
	}
	fprintf (fp, "\n");
	for (x = 0; x < 512; x++) {
		if (mmio_rtally[x + 256])
			fprintf (fp, "0x%X:%d\t", 0x4200 + x, mmio_rtally[x + 256]);
	}
	fprintf (fp, "\n\nMMIO Write Tallies\n");
	for (x = 0; x < 256; x++) {
		if (mmio_wtally[x])
			fprintf (fp, "0x%X:%d\t", 0x2100 + x, mmio_wtally[x]);
	}
	fprintf (fp, "\n");
	for (x = 0; x < 512; x++) {
		if (mmio_wtally[x + 256])
			fprintf (fp, "0x%X:%d\t", 0x4200 + x, mmio_wtally[x + 256]);
	}
	fclose (fp);

#endif
}

#define CARRY 1
#define ZERO 2
#define INT_DISABLE 4
#define BCD 8
#define INDEX 16
#define MEMORY 32
#define OVERFLOW 64
#define NEGATIVE 128

#ifdef USE_C_CPU_CORE
	#define A_16BIT
	#define XY_16BIT
	#define EMUROUTINE emulate_m0_x0
	#include "65816emu.cpp"
	#undef A_16BIT
	#define XY_16BIT
	#undef EMUROUTINE
	#define EMUROUTINE emulate_m1_x0
	#include "65816emu.cpp"
	#undef XY_16BIT
	#undef EMUROUTINE
	#define EMUROUTINE emulate_m1_x1
	#include "65816emu.cpp"
	#define A_16BIT
	#undef EMUROUTINE
	#define EMUROUTINE emulate_m0_x1
	#include "65816emu.cpp"
#endif

void setjoypadreg (int joypad)
{
//      static int toggle = 0;
	struct snesjoypad *joy = &joykey [pcontrol[joypad]];

	*REG4218(joypad) = (keydown[joy->b] << 15) | (keydown[joy->y] << 14) | (keydown[joy->select] << 13) \
		| (keydown[joy->start] << 12) | (keydown[joy->up] << 11) | (keydown[joy->down] << 10) \
		| (keydown[joy->left] <<  9) | (keydown[joy->right] <<  8) | (keydown[joy->a] << 7) \
		| (keydown[joy->x] <<  6) | (keydown[joy->l] <<  5) | (keydown[joy->r] << 4);
//	toggle++;
//	*REG4218(joypad) = ((toggle & 2) << 6) | ((screen_scanline & 2) << 14); // a & b
		// debug thing
}

boolean getoldjoypadbit (boolean p2, int bit)
{
	int player = p2 + (bit >= 8 ? 2 : 0);
	struct snesjoypad *joy = &joykey [pcontrol[player]];

	if (*REG4200 & 1) {
		if (pcontrol[p2] != 3) return 1;
		else return 0;
	} else if (bit >= 32) {
		switch (bit) {
		case 34: return (pcontrol[p2]   == 3 ? 0 : 1);
		case 35: return (pcontrol[p2+1] == 3 ? 0 : 1);
		default: return 1;
		}
	} else {
		switch (bit & 0xF) {
		case  0: return keydown[joy->b];
		case  1: return keydown[joy->y];
		case  2: return keydown[joy->select];
		case  3: return keydown[joy->start];
		case  4: return keydown[joy->up];
		case  5: return keydown[joy->down];
		case  6: return keydown[joy->left];
		case  7: return keydown[joy->right];
		case  8: return keydown[joy->a];
		case  9: return keydown[joy->x];
		case 10: return keydown[joy->l];
		case 11: return keydown[joy->r];
		default: return 0;
		}
	}
}

byte _cdecl near *trapregread (byte *ptr, boolean wordread)
{
	int x, n;

	if (ptr-registers+0x2000 >= 0x4200 && ptr-registers+0x2000 <= 0x43FF)
		mmio_rtally [ptr-registers+0x2000-0x4200+256]++;
	if (ptr-registers+0x2000 >= 0x2100 && ptr-registers+0x2000 <= 0x21FF)
		mmio_rtally [ptr-registers+0x2000-0x2100]++;
	debug_readtrap = true; debug_trapaddr = ptr - registers + 0x2000;

	switch (ptr - registers + 0x2000) {
	case 0x2138: // OAM read & inc
		*ptr = oam [state.oampointer];
		state.oampointer = (state.oampointer + 1) % 544;
		if (state.oampointer == 0) debug0 ("SNES Program caused wrap of oampointer to 0 (on read)");
		if (state.oampointer == 1) debug0 ("oampointer is now 1 (on read)");
		break;
	case 0x2139: // VRAM read low
		(*(word*)REG2139) = *(word*)(&vram [state.vrampointer]);
		if (state.dummyread2139) {
			state.dummyread2139 = false;
		} if ((*REG2115 & 0x80) == 0) {
			state.vrampointer = (state.vrampointer + state.vramincrate) & 0xFFFF;
		}
		break;
	case 0x213A: // VRAM read high
		(*(word*)REG2139) = *(word*)(&vram [state.vrampointer]);
		//if (*REG2115 & 0x80) {
		//	state.vrampointer = (state.vrampointer + state.vramincrate) & 0xFFFF;
		//}
		break;
	case 0x213B: // CGRAM (palette) read
		*ptr = cgram [state.colorpointer];
		state.colorpointer = (state.colorpointer + 1) % 512;
		if (state.colorpointer == 0) debug0 ("SNES Program caused wrap of colorpointer to 0 (on read)");
		if (state.colorpointer == 1) debug0 ("colorpointer is now 1 (on read)");
		break;
	case 0x213E: // PPU status flag/version number b7:time over b6:range over b5:master/slave b3-0:version
		*ptr = 1;
		break;
	case 0x213F: // PPU status flag/version number b7:field# in int.mode b6:ext.signal b4:1=PAL b0-3:version
		// SNES type
		*ptr = (ispal() << 4) + 1;
		break;
	case 0x2140: case 0x2141: case 0x2142: case 0x2143:
		{ static boolean recursed = false, skipreg;

		if (curgs->emulatespc) {
		} else {
			if (recursed) { // Prevent SPC skip from being applied to high byte
				// of a word read.
				recursed = false;
				break;
			} else if (wordread) {
				recursed = true;
			}
			switch (skipreg) {
			case 0:
				if (reg.P & 0x20) *ptr = reg.A_lo;
				else *(word*)ptr = reg.A;
				break;
			case 1:
				if (reg.P & 0x10) *ptr = reg.X_lo;
				else *(word*)ptr = reg.X;
				break;
			case 2:
				if (reg.P & 0x10) *ptr = reg.Y_lo;
				else *(word*)ptr = reg.Y;
				break;
			}
			skipreg = (skipreg + 1) % 3;
			if (curgs->spcskipmethod == 0) {
				#ifdef USE_C_CPU_CORE
				x = reg.PC + opcodelist[*SNESMEM(reg.PC)].bytes;
				#else
				x = reg.PC;
				#endif
				n = *SNESMEM(x);
				#define ISCONDITIONALOP(n) ((n) == 0x10 || (n) == 0x30 || (n) == 0xD0 || (n) == 0x90 || (n) == 0xB0 || (n) == 0xF0)
				if (ISCONDITIONALOP(n) && (*SNESMEM(x+1) & 0x80)) {
					// Cond. Branch backward
					(*(word*)SNESMEM(x)) = 0xEAEA; // two NOPs
				} else {
					x += opcodelist[n].bytes;
					n = *SNESMEM(x);
					if (ISCONDITIONALOP(n) && (*SNESMEM(x+1) & 0x80)) {
						(*(word*)SNESMEM(x)) = 0xEAEA;
					} else if ((reg.P & 0x20) == 0) {
						x++;
						n = *SNESMEM(x);
						if (ISCONDITIONALOP(n) && (*SNESMEM(x+1) & 0x80))
							(*(word*)SNESMEM(x)) = 0xEAEA;
					}
				}

				if (ptr != ((byte*)REG2140) + 3)
					(*(word*)ptr) = reg.A;
				else
					*ptr = (byte) reg.A;
			} else {
				if (screen_scanline & 2) { // Alternate method, used half the time...
					// Possibly useful if method before ifs doesn't work
					if (screen_scanline & 1) {
						*SNESMEM(0x2140) = (byte)rand();
						*SNESMEM(0x2141) = (byte)rand();
						*SNESMEM(0x2142) = (byte)rand();
						*SNESMEM(0x2143) = (byte)rand();
					} else {
						*(word*)SNESMEM(ptr - registers + 0x2000) = reg.A;
					}
				}
			}
		}

		}
		break;
	case 0x2180: // WRAM data read
		// Accesses only RAM?
		x = ((*REG2181) & 0x1FFFF);
		*REG2180 = ram[x];
		(*(word*)REG2181)++;

		//x = (*REG2181) & 0xFFFFFF;
		//*REG2180 = *SNESMEM(x);
		//(*(word*)REG2181)++;
		if (*(word*)REG2181 == 0) // inc bank or not?
			(*(((byte*)REG2181) + 2))++;
		break;
	case 0x2137: // H/V counter latch
		state.hcounter = scan_cycles;
		state.vcounter = screen_scanline;
		break;
	case 0x213C: // Horizontal counter
		*REG213C = (byte)state.hcounter;
		x = (byte)state.hcounter;
		state.hcounter = (state.hcounter >> 8) + (x << 8);
		break;
	case 0x213D: // Vertical counter
		*REG213D = (byte)state.vcounter;
		x = (byte)state.vcounter;
		state.vcounter = (state.vcounter >> 8) + (x << 8);
		break;
	case 0x4210: // Clear NMI int request.  zSNES often sets the bit only once--at scan 224-regardless of whether NMIs are enabled.  Reading once clears the register.
		//if (screen_scanline < scans_before_vbl()) //|| (*REG4200 & 0x80) == 0)
		//	reg4210 = 1;
		//else
		//	reg4210 = 0x81;
		*REG4210 = state.reg4210;
		state.reg4210 &= ~0x80;
		break;
	case 0x4211:
		*REG4211 = state.reg4211;
		state.reg4211 &= ~0x80;
		break;
	case 0x4212: // V/HBlank/Joypad state
		*REG4212 = (scan_cycles < 40 ? 0x40 : 0) | (screen_scanline >= scans_before_vbl() ? 0x80 : 0) | (screen_scanline & 1);
		if (debugmode && (tmsec & 1)) // To break infinite hblank check loops in debugger
			*REG4212 &= ~0x40;
		break;
	/*case 0x4218: case 0x4219:
	case 0x421A: case 0x421B:
	case 0x421C: case 0x421D:
	case 0x421E: case 0x421F:
		setjoypadreg ((ptr - registers - 0x2218) / 2);
		break;*/
	case 0x4016:
		*REG4016 = getoldjoypadbit (false, state.oldjoybit[false]);
		state.oldjoybit[false]++;
		break;
	case 0x4017:
		*REG4017 = getoldjoypadbit (true, state.oldjoybit[true]);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -