944 lines
24 KiB
C
Executable File
944 lines
24 KiB
C
Executable File
/*
|
||
* Main application source file - The peTI-NESulator Project
|
||
* main.c
|
||
*
|
||
* Created by Manoël Trapier.
|
||
* Copyright (c) 2002-2019 986-Studio.
|
||
*
|
||
*/
|
||
|
||
/* System includes */
|
||
|
||
#if !defined(__TIGCC__) && !defined(__GCC4TI__) && !defined(__GTC__)
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <signal.h>
|
||
#include <fcntl.h>
|
||
#include <unistd.h>
|
||
|
||
#include <sys/time.h>
|
||
#include <time.h>
|
||
#include <sys/mman.h>
|
||
#include <ctype.h>
|
||
|
||
#include <GLFW/glfw3.h>
|
||
|
||
#else
|
||
|
||
#define TIGCC_COMPAT
|
||
#include <tigcclib.h>
|
||
|
||
#endif
|
||
|
||
/* peTI-NESulator modules includes */
|
||
#include <os_dependent.h>
|
||
|
||
#include <corecpu.h>
|
||
#include <ppu/ppu.h>
|
||
#include <NESCarts.h>
|
||
#include <paddle.h>
|
||
|
||
#include <mappers/manager.h>
|
||
|
||
#include <memory/manager.h>
|
||
|
||
#include <plugins/manager.h>
|
||
#include <apu/apu.h>
|
||
|
||
|
||
#if ISPAL && !ISNTSC
|
||
int VBLANK_TIME = 70;
|
||
int HBLANK_TIME = 103;
|
||
double APU_BASEFREQ = 1.7734474;
|
||
#elif !ISPAL && ISNTSC
|
||
int VBLANK_TIME = 20;
|
||
int HBLANK_TIME = 113;
|
||
double APU_BASEFREQ = 1.7897725;
|
||
#elif !ISPAL && !ISNTSC
|
||
# error You MUST define one of ISPAL Xor ISNTSC
|
||
#else
|
||
# error Cannot use ISPAL with ISNTSC together !
|
||
#endif
|
||
|
||
//#define MEMORY_TEST
|
||
|
||
/* peTI-NESulator Version */
|
||
#if !defined(V_MAJOR) || !defined(V_MINOR) || !defined(V_MICRO)
|
||
#error Something wrong with your building tools
|
||
#endif
|
||
|
||
#ifndef V_TEXT
|
||
#define V_TEXT ""
|
||
#endif
|
||
|
||
|
||
#ifdef USE_SOUND
|
||
#undef USE_SOUND
|
||
#endif
|
||
|
||
/*
|
||
#define MAXLASTOP 42
|
||
|
||
word latestop[MAXLASTOP];
|
||
*/
|
||
|
||
/* NES specific variables */
|
||
quick6502_cpu *MainCPU;
|
||
NesCart *Cart;
|
||
|
||
uint8_t *FDSRom;
|
||
uint8_t *FDSRam;
|
||
|
||
/* Command line options */
|
||
uint8_t START_DEBUG = 0;
|
||
uint8_t START_WITH_FDS = 0;
|
||
char *CART_FILENAME = NULL;
|
||
char *PALETTE_FILENAME = NULL;
|
||
|
||
Paddle P1, P2;
|
||
|
||
uint16_t ScanLine;
|
||
|
||
volatile int frame = 0;
|
||
volatile uint64_t ccount;
|
||
char MapperWantIRQ = 0;
|
||
|
||
char WantClosing = 0;
|
||
|
||
struct timeval timeStart;
|
||
struct timeval timeEnd;
|
||
|
||
volatile uint32_t FPS, IPS;
|
||
|
||
short IRQScanHit = -1;
|
||
short SZHit = -1;
|
||
|
||
/* palette */
|
||
uint32_t ColorPalette[8 * 63];
|
||
|
||
#define SET_RGB(_r, _g, _b) (((_r) << 16) | ((_g) << 8) | (_b) | 0xFF000000)
|
||
|
||
/* Memory functions */
|
||
uint8_t MemoryRead(uint16_t Addr);
|
||
uint8_t MemoryOpCodeRead(uint16_t Addr);
|
||
uint8_t MemoryStackRead(uint16_t Addr);
|
||
uint8_t MemoryPageZeroRead(uint16_t Addr);
|
||
void MemoryWrite(uint16_t Addr, uint8_t Value);
|
||
void MemoryStackWrite(uint16_t Addr, uint8_t Value);
|
||
void MemoryPageZeroWrite(uint16_t Addr, uint8_t Value);
|
||
void Loop6502(quick6502_cpu *R);
|
||
|
||
void CloseHook(void)
|
||
{
|
||
WantClosing = 1;
|
||
}
|
||
|
||
void SaveSaveRam(char *name)
|
||
{
|
||
FILE *fp;
|
||
int i;
|
||
char fname[512];
|
||
|
||
strcpy(fname, name);
|
||
strcat(fname, ".svt");
|
||
if ((fp = fopen(fname, "wb")))
|
||
{
|
||
console_printf(Console_Default, "Saving savestate '%s'\n", fname);
|
||
for (i = 0x60 ; i < 0x80 ; i++)
|
||
{
|
||
fwrite(get_page_ptr(i), 1, 0x100, fp);
|
||
}
|
||
|
||
fclose(fp);
|
||
}
|
||
}
|
||
|
||
void LoadSaveRam(char *name)
|
||
{
|
||
FILE *fp;
|
||
int i;
|
||
char fname[512];
|
||
|
||
strcpy(fname, name);
|
||
strcat(fname, ".svt");
|
||
if ((fp = fopen(fname, "rb")))
|
||
{
|
||
console_printf(Console_Default, "Loading savestate '%s'\n", fname);
|
||
for (i = 0x60 ; i < 0x80 ; i++)
|
||
{
|
||
fread(get_page_ptr(i), 1, 0x0100, fp);
|
||
}
|
||
fclose(fp);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
void LoadPalette(char *filename, Palette *pal)
|
||
{
|
||
FILE *fp;
|
||
uint8_t r, v, b, i;
|
||
console_printf(Console_Default, "%s: try to load pallette file '%s'", __func__, filename);
|
||
if ((fp = fopen(filename, "rb")) != NULL)
|
||
{
|
||
|
||
for (i = 0 ; i < 64 ; i++)
|
||
{
|
||
|
||
fread(&r, 1, 1, fp);
|
||
fread(&v, 1, 1, fp);
|
||
fread(&b, 1, 1, fp);
|
||
|
||
/* r = (r * 64) / 255;
|
||
v = (v * 64) / 255;
|
||
b = (b * 64) / 255;*/
|
||
|
||
|
||
#ifdef USE_24BITS
|
||
ColorPalette[i + (0 * 63)] = SET_RGB(r,v,b);
|
||
|
||
/* Red emphase */
|
||
ColorPalette[i + (1 * 63)] = SET_RGB(r + 10, v - 05, b - 05);
|
||
|
||
/* Green emphase */
|
||
ColorPalette[i + (2 * 63)] = SET_RGB(r - 05, v + 10, b - 05);
|
||
|
||
/* Red + green emphase */
|
||
ColorPalette[i + (3 * 63)] = SET_RGB(r + 05, v + 05, b - 10);
|
||
|
||
/* Blue emphase */
|
||
ColorPalette[i + (4 * 63)] = SET_RGB(r - 05, v - 05, b + 10);
|
||
|
||
/* Red + blue emphase */
|
||
ColorPalette[i + (5 * 63)] = SET_RGB(r + 05, v - 10, b + 05);
|
||
|
||
/* Blue + green emphase */
|
||
ColorPalette[i + (6 * 63)] = SET_RGB(r - 10, v + 05, b + 05);
|
||
|
||
/* Red + Green + Blue emphase */
|
||
ColorPalette[i + (7 * 63)] = SET_RGB(r + 00, v + 00, b + 00);
|
||
#else /* Else Use 8Bits */
|
||
pal[i].r = r;
|
||
pal[i].g = v;
|
||
pal[i].b = b;
|
||
|
||
pal[i + 64].r = r;
|
||
pal[i + 64].g = v;
|
||
pal[i + 64].b = b;
|
||
|
||
pal[i + 128].r = r;
|
||
pal[i + 128].g = v;
|
||
pal[i + 128].b = b;
|
||
|
||
pal[i + 192].r = r;
|
||
pal[i + 192].g = v;
|
||
pal[i + 192].b = b;
|
||
#endif
|
||
}
|
||
fclose(fp);
|
||
console_printf(Console_Default, " [ OK ]\n");
|
||
}
|
||
else
|
||
{
|
||
console_printf(Console_Error, "Error loading palette '%s'!\n", filename);
|
||
exit(-1);
|
||
}
|
||
}
|
||
|
||
void signalhandler(int sig)
|
||
{
|
||
static int state = 0;
|
||
|
||
char name[512];
|
||
|
||
static FILE *fp = NULL;
|
||
sprintf(name, "crashdump-%d.txt", (int)time(NULL));
|
||
if (state != 0)
|
||
{
|
||
console_printf(Console_Error, "\n\n\nCrashed within signal!\nEmergency exit\n");
|
||
exit(42);
|
||
}
|
||
state = 1;
|
||
|
||
if (fp == NULL)
|
||
{
|
||
fp = fopen(name, "wt");
|
||
}
|
||
|
||
state = 2;
|
||
|
||
if (fp)
|
||
{
|
||
console_printf(Console_Error,
|
||
"\n\n\n\n\n"
|
||
"#sick# peTI-NESulator %d.%d.%d%s #sick#\n"
|
||
"see %s for more information",
|
||
V_MAJOR, V_MINOR, V_MICRO, V_TEXT,
|
||
name);
|
||
}
|
||
|
||
if (!fp)
|
||
{
|
||
fp = stderr;
|
||
}
|
||
|
||
fprintf(fp, "\n\n\n\n\n"
|
||
"#sick# peTI-NESulator %d.%d.%d%s #sick# signal: ",
|
||
V_MAJOR, V_MINOR, V_MICRO, V_TEXT);
|
||
switch (sig)
|
||
{
|
||
default:
|
||
case SIGABRT:
|
||
fprintf(fp, "Abnormal termination");
|
||
break;
|
||
case SIGILL:
|
||
fprintf(fp, "Illegal instruction");
|
||
break;
|
||
case SIGINT:
|
||
fprintf(fp, "CTRL+C signal");
|
||
break;
|
||
case SIGSEGV:
|
||
fprintf(fp, "Segmentation fault");
|
||
break;
|
||
case SIGTERM:
|
||
fprintf(fp, "Termination request");
|
||
break;
|
||
}
|
||
fprintf(fp, "\nAn error occurred during the excution.\n Crash report information :\n");
|
||
|
||
//quick6502_dump(cpu, fp);
|
||
|
||
//showlastop(fp);
|
||
// fprintf(fp, "PPU: CR1: 0x%02X (NT:%d AI:%d SP:%d BP:%d SS:%d NMI:%d)\n",ppu.ControlRegister1.b, ppu.ControlRegister1.s.NameTblAddr, ppu.ControlRegister1.s.AddrIncrmt, ppu.ControlRegister1.s.SptPattern, ppu.ControlRegister1.s.BgPattern, ppu.ControlRegister1.s.SpriteSize, ppu.ControlRegister1.s.VBlank_NMI);
|
||
// fprintf(fp, "PPU: CR2: 0x%02X (FBC/CI:%d SV:%d BV:%d SC:%d BC:%d DT:%d)\n",ppu.ControlRegister2.b,ppu.ControlRegister2.s.Colour,ppu.ControlRegister2.s.SpriteVisibility,ppu.ControlRegister2.s.BgVisibility,ppu.ControlRegister2.s.SpriteClipping,ppu.ControlRegister2.s.BgClipping,ppu.ControlRegister2.s.DisplayType);
|
||
// fprintf(fp, "PPU: SR: 0x%02X (VB:%d S0:%d SSC:%d VWF:%d)\n", ppu.StatusRegister.b,ppu.StatusRegister.s.VBlankOccur,ppu.StatusRegister.s.Sprite0Occur,ppu.StatusRegister.s.SprtCount,ppu.StatusRegister.s.VRAMProtect);
|
||
// fprintf(fp, "PPU: M:%d ST:%d VRAMPtr:0x%04X T:0x%04X\n",ppu.MirrorDir,ppu.ScreenType,ppu.VRAMAddrReg2.W,ppu.TmpVRamPtr);
|
||
|
||
//MapperDump(fp);
|
||
#if 0
|
||
for(I = 0; I < 0xFFFF; I += 0x10)
|
||
fprintf(fp, "%04X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X | %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
|
||
I,
|
||
Rd6502(I+0x00), Rd6502(I+0x01), Rd6502(I+0x02), Rd6502(I+0x03),
|
||
Rd6502(I+0x04), Rd6502(I+0x05), Rd6502(I+0x06), Rd6502(I+0x07),
|
||
Rd6502(I+0x08), Rd6502(I+0x09), Rd6502(I+0x0A), Rd6502(I+0x0B),
|
||
Rd6502(I+0x0C), Rd6502(I+0x0D), Rd6502(I+0x0E), Rd6502(I+0x0F),
|
||
// --- //
|
||
isprint(Rd6502(I+0x00))?Rd6502(I+0x00):'_',
|
||
isprint(Rd6502(I+0x01))?Rd6502(I+0x01):'_',
|
||
isprint(Rd6502(I+0x02))?Rd6502(I+0x02):'_',
|
||
isprint(Rd6502(I+0x03))?Rd6502(I+0x03):'_',
|
||
isprint(Rd6502(I+0x04))?Rd6502(I+0x04):'_',
|
||
isprint(Rd6502(I+0x05))?Rd6502(I+0x05):'_',
|
||
isprint(Rd6502(I+0x06))?Rd6502(I+0x06):'_',
|
||
isprint(Rd6502(I+0x07))?Rd6502(I+0x07):'_',
|
||
isprint(Rd6502(I+0x08))?Rd6502(I+0x08):'_',
|
||
isprint(Rd6502(I+0x09))?Rd6502(I+0x09):'_',
|
||
isprint(Rd6502(I+0x0A))?Rd6502(I+0x0A):'_',
|
||
isprint(Rd6502(I+0x0B))?Rd6502(I+0x0B):'_',
|
||
isprint(Rd6502(I+0x0C))?Rd6502(I+0x0C):'_',
|
||
isprint(Rd6502(I+0x0D))?Rd6502(I+0x0D):'_',
|
||
isprint(Rd6502(I+0x0E))?Rd6502(I+0x0E):'_',
|
||
isprint(Rd6502(I+0x0F))?Rd6502(I+0x0F):'_');
|
||
#endif
|
||
DumpMemoryState(fp);
|
||
|
||
console_printf(Console_Error, "\nPlease join this informations when submiting crash report\n");
|
||
if (fp != stderr)
|
||
{
|
||
fclose(fp);
|
||
}
|
||
|
||
exit(-42);
|
||
}
|
||
|
||
uint8_t Page40[256];
|
||
|
||
void WrHook4000Multiplexer(uint8_t addr, uint8_t value)
|
||
{
|
||
switch (addr)
|
||
{
|
||
case 0x14:
|
||
ppu_fillSprRamDMA(value);
|
||
break;
|
||
|
||
case 0x16:
|
||
WritePaddle(&P1, value);
|
||
//WritePaddle(&P2, value);
|
||
break;
|
||
|
||
case 0x17:
|
||
if (value == 0x00)
|
||
{
|
||
quick6502_int(MainCPU, Q6502_IRQ_SIGNAL);
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
Page40[addr] = value;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
uint8_t RdHook4000Multiplexer(uint8_t addr)
|
||
{
|
||
uint8_t ret;
|
||
switch (addr)
|
||
{
|
||
case 0x16:
|
||
ret = ReadPaddle(&P1);
|
||
break;
|
||
|
||
case 0x17:
|
||
ret = 0x40;
|
||
break;
|
||
|
||
case 0x15:
|
||
ret = 0x1F;
|
||
break;
|
||
|
||
default:
|
||
ret = 0x42;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
void printUsage(int argc, char *argv[])
|
||
{
|
||
console_printf(Console_Default, "Usage : %s game.nes [-p number][-f][-b filename.pal][ filename.nes\n"
|
||
" -p: to add plugin 'number'\n"
|
||
" -f: to start in FDS mode\n"
|
||
" -d: to start directily into the debugguer\n"
|
||
" -b: to use palette file 'filename.pal'\n",
|
||
argv[0]);
|
||
exit(0);
|
||
}
|
||
|
||
#define Value(_s) (((_s)%0xFF) + (rand()%0xFF-128) )%0xFF)
|
||
|
||
int main(int argc, char *argv[])
|
||
{
|
||
int i;
|
||
uint8_t *MemoryPage;
|
||
quick6502_cpuconfig CpuConfig;
|
||
|
||
#ifdef RUN_COVERAGE
|
||
uint64_t coverage_loops = 500000000UL;
|
||
#endif
|
||
|
||
/* Here we will fill the memory */
|
||
/*
|
||
--------------------------------------- $10000
|
||
Upper Bank of Cartridge ROM
|
||
--------------------------------------- $C000
|
||
Lower Bank of Cartridge ROM
|
||
--------------------------------------- $8000
|
||
Cartridge RAM (may be battery-backed)
|
||
--------------------------------------- $6000
|
||
Expansion Modules
|
||
--------------------------------------- $5000
|
||
Input/Output
|
||
--------------------------------------- $2000
|
||
2kB Internal RAM, mirrored 4 times
|
||
--------------------------------------- $0000
|
||
*/
|
||
console_init(Console_Debug);
|
||
/* Print the banner */
|
||
console_printf(Console_Default, "--------------------------------------------------------------------------------\n"
|
||
"Welcome to peTI-NESulator v%d.%d.%d%s - by Godzil`\n"
|
||
"Copyright (c) 2002-2019 986-Studio / Manoël Trapier (petines@godzil.net)\n"
|
||
"--------------------------------------------------------------------------------\n\n",
|
||
V_MAJOR, V_MINOR, V_MICRO, V_TEXT);
|
||
|
||
console_printf(Console_Default, "Install signal handlers...\t[");
|
||
signal(SIGABRT, signalhandler);
|
||
console_printf(Console_Default, "A");
|
||
signal(SIGILL, signalhandler);
|
||
console_printf(Console_Default, "I");
|
||
/*signal(SIGINT, signalhandler);*/
|
||
console_printf(Console_Default, ".");
|
||
signal(SIGSEGV, signalhandler);
|
||
console_printf(Console_Default, "S");
|
||
signal(SIGTERM, signalhandler);
|
||
console_printf(Console_Default, "T]\n");
|
||
|
||
/* */
|
||
console_printf(Console_Default, "Initialize memory...\t\t");
|
||
InitMemory();
|
||
console_printf(Console_Default, "[ OK ]\n");
|
||
console_printf(Console_Default, "Parsing parameters (%d)...\n", argc);
|
||
/* Now we use a real argument parser ! */
|
||
for (i = 1 ; (i < argc) && (argv[i][0] == '-') ; i++)
|
||
{
|
||
switch (argv[i][1])
|
||
{
|
||
default: /* Option not recognized */
|
||
case 'h': /* ask for help */
|
||
printUsage(argc, argv);
|
||
break;
|
||
|
||
case 'p':
|
||
if (atoi(argv[i + 1]) != 0)
|
||
{
|
||
console_printf(Console_Default, "-Load plugin #%d...\n", atoi(argv[i + 1]));
|
||
if (plugin_load(atoi(argv[i + 1])) == -1)
|
||
{
|
||
plugin_list();
|
||
exit(0);
|
||
}
|
||
i++;
|
||
}
|
||
else
|
||
{
|
||
plugin_list();
|
||
exit(0);
|
||
}
|
||
break;
|
||
|
||
case 'f':
|
||
console_printf(Console_Default, "-Start with fds!\n");
|
||
START_WITH_FDS = 1;
|
||
break;
|
||
|
||
case 'd':
|
||
console_printf(Console_Default, "-Start with debug!\n");
|
||
START_DEBUG = 1;
|
||
break;
|
||
|
||
case 'b':
|
||
console_printf(Console_Default, "-Palette file is %s\n", argv[i + 1]);
|
||
PALETTE_FILENAME = argv[i + 1];
|
||
i++;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
CART_FILENAME = argv[argc - 1];
|
||
|
||
if (CART_FILENAME == NULL)
|
||
{
|
||
printUsage(argc, argv);
|
||
}
|
||
|
||
console_printf(Console_Default, "Allocating 6502 memory\t\t");
|
||
|
||
/* Allocating first 0x7FF memory */
|
||
MemoryPage = (uint8_t *)malloc(0x800);
|
||
set_page_ptr_2k(0, MemoryPage);
|
||
for (i = 0 ; i < 0x08 ; i++)
|
||
{
|
||
set_page_readable(i, true);
|
||
set_page_writeable(i, true);
|
||
}
|
||
|
||
/* Set ghost starting from 0x800 */
|
||
set_page_ghost(0x08, true, 0x00);
|
||
set_page_ghost(0x09, true, 0x01);
|
||
set_page_ghost(0x0A, true, 0x02);
|
||
set_page_ghost(0x0B, true, 0x03);
|
||
set_page_ghost(0x0C, true, 0x04);
|
||
set_page_ghost(0x0D, true, 0x05);
|
||
set_page_ghost(0x0E, true, 0x06);
|
||
set_page_ghost(0x0F, true, 0x07);
|
||
|
||
/* Set ghost starting from 0x1000 */
|
||
set_page_ghost(0x10, true, 0x00);
|
||
set_page_ghost(0x11, true, 0x01);
|
||
set_page_ghost(0x12, true, 0x02);
|
||
set_page_ghost(0x13, true, 0x03);
|
||
set_page_ghost(0x14, true, 0x04);
|
||
set_page_ghost(0x15, true, 0x05);
|
||
set_page_ghost(0x16, true, 0x06);
|
||
set_page_ghost(0x17, true, 0x07);
|
||
|
||
/* Set ghost starting from 0x1800 */
|
||
set_page_ghost(0x18, true, 0x00);
|
||
set_page_ghost(0x19, true, 0x01);
|
||
set_page_ghost(0x1A, true, 0x02);
|
||
set_page_ghost(0x1B, true, 0x03);
|
||
set_page_ghost(0x1C, true, 0x04);
|
||
set_page_ghost(0x1D, true, 0x05);
|
||
set_page_ghost(0x1E, true, 0x06);
|
||
set_page_ghost(0x1F, true, 0x07);
|
||
|
||
/* Set 0x4000 registers */
|
||
|
||
/* "hack" : only page $40 is used by multiple devices, we need to multiplexe
|
||
it*/
|
||
set_page_wr_hook(0x40, WrHook4000Multiplexer);
|
||
set_page_rd_hook(0x40, RdHook4000Multiplexer);
|
||
|
||
set_page_readable(0x40, true);
|
||
set_page_writeable(0x40, true);
|
||
|
||
/* Exp ROM : Nothing to do actually */
|
||
|
||
/* ROM ptr will be set by mapper */
|
||
/* But we will set the readable bit */
|
||
for (i = 0x80 ; i < 0x100 ; i++)
|
||
{
|
||
set_page_readable(i, true);
|
||
set_page_writeable(i, false);
|
||
}
|
||
|
||
console_printf(Console_Default, "[ OK ]\n");
|
||
|
||
#ifdef MEMORY_TEST
|
||
console_printf(Console_Default, "Testing memory validity...\n");
|
||
|
||
map_sram();
|
||
|
||
console_printf(Console_Verbose, "Testing Page Zero\n");
|
||
for( i = 0 ; i < 0x100 ; i++)
|
||
{
|
||
j = rand() % 0xFF;
|
||
MemoryPage[i] = j;
|
||
if ((k = MemoryPageZeroRead(i)) != j)
|
||
console_printf(Console_Error, "Error MemoryPageZeroRead @ 0x%04X [j:%02X, should:%02X, is:%02X]\n", i, j, MemoryPage[i], k);
|
||
|
||
j = rand() % 0xFF;
|
||
MemoryPageZeroWrite(i, j);
|
||
if ((k = MemoryPage[i]) != j)
|
||
console_printf(Console_Error, "Error MemoryPageZeroWrite @ 0x%04X [j:%02X, should:%02X, is:%02X]\n", i, j, MemoryPage[i], k);
|
||
MemoryPage[i] = 0;
|
||
}
|
||
|
||
console_printf(Console_Verbose, "Testing memory... (<0x2000)\n");
|
||
for( i = 0 ; i < 0x2000 ; i++ )
|
||
{
|
||
j = Value(i);
|
||
MemoryWrite(i, j);
|
||
if ((k=MemoryRead(i)) != j)
|
||
console_printf(Console_Error, "Error read/write @ 0x%X [w:%d,r:%d]\n", i, j, k);
|
||
if ((k=MemoryOpCodeRead(i)) != j)
|
||
console_printf(Console_Error, "Error opcode @ 0x%X [w:%d,r:%d]\n", i, j, k);
|
||
}
|
||
#endif
|
||
/* SRAM (0x6000 : 0x2000 uint8_ts ) */
|
||
MemoryPage = (uint8_t *)malloc(0x2000);
|
||
|
||
set_page_ptr_8k(0x60, MemoryPage);
|
||
|
||
#ifdef MEMORY_TEST
|
||
for(i = 0x6000; i < 0x8000; i ++)
|
||
{
|
||
if (MemoryPage[i-0x6000] != (k = MemoryRead(i)))
|
||
console_printf(Console_Error, "Error MemoryRead @ 0x%X [should:%d,is:%d]\n", i, MemoryPage[i-0x6000], k);
|
||
if (MemoryPage[i-0x6000] != (k = MemoryOpCodeRead(i)))
|
||
console_printf(Console_Error, "Error MemoryOpCodeRead @ 0x%X [should:%d,is:%d]\n", i, MemoryPage[i-0x6000], k);
|
||
}
|
||
|
||
console_printf(Console_Verbose, "Testing memory... (0x6000-0x8000)\n");
|
||
for(i=0x6000;i<0x8000;i++)
|
||
{
|
||
j = Value(i);
|
||
MemoryWrite(i, j);
|
||
if ((k=MemoryRead(i)) != j)
|
||
console_printf(Console_Error, "Error read/write @ 0x%X [w:%d,r:%d]\n", i, j, k);
|
||
if ((k=MemoryOpCodeRead(i)) != j)
|
||
console_printf(Console_Error, "Error opcode @ 0x%X [w:%d,r:%d]\n", i, j, k);
|
||
}
|
||
|
||
console_printf(Console_Default, "Reseting main RAM...\t\t");
|
||
|
||
/* Force the stack to be full of zero */
|
||
for( i = 0x100 ; i < 0x200 ; i++ )
|
||
{
|
||
MemoryWrite(i, 0x00);
|
||
}
|
||
|
||
console_printf(Console_Default, "[ OK ]\n");
|
||
#endif
|
||
|
||
Cart = malloc(sizeof(NesCart));
|
||
if (Cart == NULL)
|
||
{
|
||
console_printf(Console_Error, "Memory allocation error...\n");
|
||
exit(-1);
|
||
}
|
||
|
||
if (START_WITH_FDS)
|
||
{
|
||
int fd;
|
||
console_printf(Console_Default, "Loading FDS ROM...\t\t");
|
||
fd = open("../data/disksys.rom", O_RDONLY);
|
||
//fd = open("peTI-NESulator.app/Contents/Resources/disksys.rom", O_RDONLY);
|
||
if (fd < 0)
|
||
{
|
||
console_printf(Console_Error, "Can't find FDS ROM...\n");
|
||
exit(-1);
|
||
}
|
||
|
||
FDSRom = mmap(NULL, 8 * 1024, PROT_READ, MAP_PRIVATE, fd, 0);
|
||
console_printf(Console_Default, "%p [ OK ]\n", FDSRom);
|
||
close(fd);
|
||
|
||
set_page_ptr_8k(0xE0, FDSRom);
|
||
|
||
console_printf(Console_Default, "Allocating FDS RAM...\n");
|
||
|
||
FDSRam = (uint8_t *)malloc((8 + 16) * 1024);
|
||
|
||
if (FDSRam == NULL)
|
||
{
|
||
console_printf(Console_Error, "Allocation error\n");
|
||
exit(-1);
|
||
}
|
||
|
||
for (i = 0x80 ; i < 0xE0 ; i++)
|
||
{
|
||
set_page_ptr(i, FDSRam + (i * 0x100));
|
||
set_page_readable(i, true);
|
||
set_page_writeable(i, true);
|
||
}
|
||
|
||
Cart->MapperID = 100;
|
||
}
|
||
else
|
||
{
|
||
console_printf(Console_Default, "Please Wait while loading %s cartridge...\n", CART_FILENAME);
|
||
if (LoadCart(CART_FILENAME, Cart) != 0)
|
||
{
|
||
console_printf(Console_Error, "Loading error...\n");
|
||
exit(-1);
|
||
}
|
||
|
||
if (Cart->Flags & iNES_BATTERY)
|
||
{
|
||
LoadSaveRam(CART_FILENAME);
|
||
}
|
||
|
||
}
|
||
|
||
unmap_sram();
|
||
|
||
InitPaddle(&P1);
|
||
|
||
console_printf(Console_Default, "Init PPU...\n");
|
||
|
||
if (ppu_init() != 0)
|
||
{
|
||
console_printf(Console_Error, "PPU Initialisation error..\n");
|
||
exit(-1);
|
||
}
|
||
|
||
//DumpMemoryState(stdout);
|
||
//DumpCartProperties(stdout, Cart);
|
||
|
||
if (Cart->Flags & iNES_4SCREEN)
|
||
{
|
||
ppu_setScreenMode(PPU_SCMODE_FOURSC);
|
||
}
|
||
else
|
||
{
|
||
ppu_setScreenMode(PPU_SCMODE_NORMAL);
|
||
ppu_setMirroring((Cart->Flags & iNES_MIRROR) ? PPU_MIRROR_VERTICAL : PPU_MIRROR_HORIZTAL);
|
||
}
|
||
|
||
//console_printf(Console_Default, "Init mapper...\n");
|
||
if (mapper_init(Cart) == -1)
|
||
{
|
||
return -1;
|
||
}
|
||
//console_printf(Console_Default, "[ OK ]\n");
|
||
|
||
// set_palette(basicPalette);
|
||
|
||
// Actually no real debugguer...
|
||
//console_printf(Console_Default, "Press ESC to pause emulation and jump to debugguer\n");
|
||
|
||
ScanLine = 0;
|
||
|
||
/* Initialize the CPU */
|
||
CpuConfig.memory_read = MemoryRead;
|
||
CpuConfig.memory_write = MemoryWrite;
|
||
CpuConfig.memory_page0_read = MemoryPageZeroRead;
|
||
CpuConfig.memory_page0_write = MemoryPageZeroWrite;
|
||
CpuConfig.memory_stack_read = MemoryStackRead;
|
||
CpuConfig.memory_stack_write = MemoryStackWrite;
|
||
CpuConfig.memory_opcode_read = MemoryOpCodeRead;
|
||
|
||
MainCPU = quick6502_init(&CpuConfig);
|
||
|
||
quick6502_reset(MainCPU);
|
||
|
||
/* No debugger actually
|
||
MainCPU.Trace = 0;
|
||
if (START_DEBUG)
|
||
MainCPU.Trace = 1;
|
||
*/
|
||
|
||
gettimeofday(&timeStart, NULL);
|
||
|
||
while (!WantClosing)
|
||
{
|
||
ccount += quick6502_run(MainCPU, HBLANK_TIME);
|
||
|
||
Loop6502(MainCPU);
|
||
|
||
#ifdef RUN_COVERAGE
|
||
if (ccount > coverage_loops)
|
||
{
|
||
WantClosing = 1;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
if (Cart->Flags & iNES_BATTERY)
|
||
{
|
||
SaveSaveRam(CART_FILENAME);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Access directly to Memory pages *HACKISH* */
|
||
extern uint8_t *memory_pages[0xFF];
|
||
/* Memory functions */
|
||
|
||
/* Read memory, general function */
|
||
uint8_t MemoryRead(uint16_t Addr)
|
||
{
|
||
return ReadMemory((Addr & 0xFF00) >> 8, Addr & 0x00FF);
|
||
}
|
||
|
||
/* Read memory for opcode (need fast access) */
|
||
uint8_t MemoryOpCodeRead(uint16_t Addr)
|
||
{
|
||
uint8_t *ptr;
|
||
return ((ptr = memory_pages[(Addr & 0xFF00) >> 8]) > (uint8_t *)1) ? ptr[Addr & 0x00FF] : 0xEA;
|
||
}
|
||
|
||
uint8_t MemoryStackRead(uint16_t Addr)
|
||
{
|
||
uint8_t *ptr = memory_pages[1];
|
||
return ptr[Addr & 0x00FF];
|
||
}
|
||
|
||
uint8_t MemoryPageZeroRead(uint16_t Addr)
|
||
{
|
||
uint8_t *ptr = memory_pages[0];
|
||
return ptr[Addr & 0x00FF];
|
||
}
|
||
|
||
/* Write to memory, general function */
|
||
void MemoryWrite(uint16_t Addr, uint8_t Value)
|
||
{
|
||
WriteMemory((Addr & 0xFF00) >> 8, Addr & 0x00FF, Value);
|
||
}
|
||
|
||
void MemoryStackWrite(uint16_t Addr, uint8_t Value)
|
||
{
|
||
uint8_t *ptr = memory_pages[1];
|
||
ptr[Addr & 0x00FF] = Value;
|
||
}
|
||
|
||
void MemoryPageZeroWrite(uint16_t Addr, uint8_t Value)
|
||
{
|
||
uint8_t *ptr = memory_pages[0];
|
||
ptr[Addr & 0x00FF] = Value;
|
||
}
|
||
|
||
void Loop6502(quick6502_cpu *R)
|
||
{
|
||
quick6502_signal cpuSignal;
|
||
// short skey;
|
||
|
||
cpuSignal = Q6502_NO_SIGNAL;
|
||
|
||
if ((mapper_irqloop) && (mapper_irqloop(ScanLine)))
|
||
{
|
||
cpuSignal = Q6502_IRQ_SIGNAL;
|
||
IRQScanHit = ScanLine;
|
||
}
|
||
|
||
if (MapperWantIRQ == 1)
|
||
{
|
||
MapperWantIRQ = 0;
|
||
cpuSignal = Q6502_IRQ_SIGNAL;
|
||
}
|
||
|
||
if (ppu_hblank(ScanLine) != 0)
|
||
{
|
||
cpuSignal = Q6502_NMI_SIGNAL;
|
||
}
|
||
|
||
if (ScanLine == (239 + VBLANK_TIME))
|
||
{
|
||
/* End of VBlank Time */
|
||
frame++;
|
||
SZHit = -1;
|
||
IRQScanHit = -1;
|
||
|
||
if (!getKeyStatus('Y'))
|
||
{
|
||
vsync();
|
||
}
|
||
|
||
}
|
||
|
||
/* There is Two dummy scanline */
|
||
if (ScanLine >= (239 + VBLANK_TIME + 4))
|
||
{
|
||
ScanLine = 0;
|
||
}
|
||
else
|
||
{
|
||
ScanLine++;
|
||
}
|
||
|
||
//console_printf(Console_Default, "SL:%d HBT:%d VbT:%d\n", ScanLine, HBLANK_TIME, VBLANK_TIME);
|
||
|
||
// TODO: NO DEBUGER
|
||
if (getKeyStatus(GLFW_KEY_ESCAPE))
|
||
{
|
||
exit(0);
|
||
}
|
||
|
||
#if 0
|
||
if (skey == '9')
|
||
{
|
||
VBLANK_TIME += 2;
|
||
console_printf(Console_Default, "VBLT: %d\n", VBLANK_TIME);
|
||
}
|
||
|
||
if (skey == '6')
|
||
{
|
||
VBLANK_TIME -= 2;
|
||
console_printf(Console_Default, "VBLT: %d\n", VBLANK_TIME);
|
||
}
|
||
|
||
if (skey == '7')
|
||
{
|
||
HBLANK_TIME += 1;
|
||
console_printf(Console_Default, "HBLT: %d\n", HBLANK_TIME);
|
||
}
|
||
|
||
if (skey == '4')
|
||
{
|
||
HBLANK_TIME -= 1;
|
||
console_printf(Console_Default, "HBLT: %d\n", HBLANK_TIME);
|
||
}
|
||
#endif
|
||
|
||
if (getKeyStatus('r') || getKeyStatus('R'))
|
||
{
|
||
console_printf(Console_Default, "-- Reset triggered\n");
|
||
/* Force the PPU to stop NMIs */
|
||
MemoryWrite(0x2000, 0x00);
|
||
quick6502_reset(R);
|
||
}
|
||
|
||
plugin_keypress();
|
||
|
||
if (cpuSignal != 0)
|
||
{
|
||
quick6502_int(R, cpuSignal);
|
||
}
|
||
}
|
||
|