/* * 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 #include #include #include #include #include #include #include #include #include #include #else #define TIGCC_COMPAT #include #endif /* peTI-NESulator modules includes */ #include #include #include #include #include #include #include #include #include #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); } }