#include "StdAfx.h" #include "Video.h" #include "Common.h" #include "CardManager.h" #include "AppleWin.h" #include "Memory.h" #include "Common.h" #include "Frame.h" #include "NTSC.h" #include "Disk.h" #include "CPU.h" #include "linux/benchmark.h" #include void VideoBenchmark(std::function redraw, std::function refresh) { // PREPARE TWO DIFFERENT FRAME BUFFERS, EACH OF WHICH HAVE HALF OF THE // BYTES SET TO 0x14 AND THE OTHER HALF SET TO 0xAA int loop; LPDWORD mem32 = (LPDWORD)mem; for (loop = 4096; loop < 6144; loop++) *(mem32+loop) = ((loop & 1) ^ ((loop & 0x40) >> 6)) ? 0x14141414 : 0xAAAAAAAA; for (loop = 6144; loop < 8192; loop++) *(mem32+loop) = ((loop & 1) ^ ((loop & 0x40) >> 6)) ? 0xAAAAAAAA : 0x14141414; // SEE HOW MANY HIRES FRAMES PER SECOND WE CAN PRODUCE WITH NOTHING ELSE // GOING ON, CHANGING HALF OF THE BYTES IN THE VIDEO BUFFER EACH FRAME TO // SIMULATE THE ACTIVITY OF AN AVERAGE GAME g_uVideoMode = VF_HIRES; FillMemory(mem+0x2000,0x2000,0x14); redraw(); typedef std::chrono::microseconds interval_t; typedef uint64_t counter_t; // avoid overflows const counter_t onesecond = 1000000; counter_t totalhiresfps = 0; counter_t elapsed; auto start = std::chrono::steady_clock::now(); do { if (totalhiresfps & 1) FillMemory(mem+0x2000,0x2000,0x14); else CopyMemory(mem+0x2000,mem+((totalhiresfps & 2) ? 0x4000 : 0x6000),0x2000); refresh(); totalhiresfps++; const auto end = std::chrono::steady_clock::now(); elapsed = std::chrono::duration_cast(end - start).count(); } while (elapsed < onesecond); // adjust for broken ms totalhiresfps = totalhiresfps * onesecond / elapsed; // DETERMINE HOW MANY 65C02 CLOCK CYCLES WE CAN EMULATE PER SECOND WITH // NOTHING ELSE GOING ON counter_t totalmhz10[2] = {0, 0}; // bVideoUpdate & !bVideoUpdate for (size_t i = 0; i < 2; i++) { CpuSetupBenchmark(); start = std::chrono::steady_clock::now(); do { CpuExecute(100000, i==0 ? true : false); totalmhz10[i]++; const auto end = std::chrono::steady_clock::now(); elapsed = std::chrono::duration_cast(end - start).count(); } while (elapsed < onesecond); totalmhz10[i] = totalmhz10[i] * onesecond / elapsed; } // IF THE PROGRAM COUNTER IS NOT IN THE EXPECTED RANGE AT THE END OF THE // CPU BENCHMARK, REPORT AN ERROR AND OPTIONALLY TRACK IT DOWN if ((regs.pc < 0x300) || (regs.pc > 0x400)) if (MessageBox(g_hFrameWindow, TEXT("The emulator has detected a problem while running ") TEXT("the CPU benchmark. Would you like to gather more ") TEXT("information?"), TEXT("Benchmarks"), MB_ICONQUESTION | MB_YESNO | MB_SETFOREGROUND) == IDYES) { BOOL error = 0; WORD lastpc = 0x300; int loop = 0; while ((loop < 10000) && !error) { CpuSetupBenchmark(); CpuExecute(loop, true); if ((regs.pc < 0x300) || (regs.pc > 0x400)) error = 1; else { lastpc = regs.pc; ++loop; } } if (error) { TCHAR outstr[256]; wsprintf(outstr, TEXT("The emulator experienced an error %u clock cycles ") TEXT("into the CPU benchmark. Prior to the error, the ") TEXT("program counter was at $%04X. After the error, it ") TEXT("had jumped to $%04X."), (unsigned)loop, (unsigned)lastpc, (unsigned)regs.pc); MessageBox(g_hFrameWindow, outstr, TEXT("Benchmarks"), MB_ICONINFORMATION | MB_SETFOREGROUND); } else MessageBox(g_hFrameWindow, TEXT("The emulator was unable to locate the exact ") TEXT("point of the error. This probably means that ") TEXT("the problem is external to the emulator, ") TEXT("happening asynchronously, such as a problem in ") TEXT("a timer interrupt handler."), TEXT("Benchmarks"), MB_ICONINFORMATION | MB_SETFOREGROUND); } // DO A REALISTIC TEST OF HOW MANY FRAMES PER SECOND WE CAN PRODUCE // WITH FULL EMULATION OF THE CPU, JOYSTICK, AND DISK HAPPENING AT // THE SAME TIME counter_t realisticfps = 0; FillMemory(mem+0x2000,0x2000,0xAA); redraw(); const size_t dwClksPerFrame = NTSC_GetCyclesPerFrame(); const size_t cyclesPerMs = g_fCurrentCLK6502 / 1000; counter_t cyclesThisFrame = 0; start = std::chrono::steady_clock::now(); do { // this is a simplified version of AppleWin.cpp:ContinueExecution() const DWORD executedcycles = CpuExecute(cyclesPerMs, true); cyclesThisFrame += executedcycles; // every ms disk and joystick are updated GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedcycles); #if 0 JoyUpdateButtonLatch(executedcycles); #endif if (cyclesThisFrame >= dwClksPerFrame) { cyclesThisFrame -= dwClksPerFrame; if (realisticfps & 1) FillMemory(mem+0x2000,0x2000,0xAA); else CopyMemory(mem+0x2000,mem+((realisticfps & 2) ? 0x4000 : 0x6000),0x2000); realisticfps++; refresh(); } const auto end = std::chrono::steady_clock::now(); elapsed = std::chrono::duration_cast(end - start).count(); } while (elapsed < onesecond); realisticfps = realisticfps * onesecond / elapsed; // DISPLAY THE RESULTS TCHAR outstr[256]; wsprintf(outstr, TEXT("Pure Video FPS:\t%u\n") TEXT("Pure CPU MHz:\t%u.%u%s (video update)\n") TEXT("Pure CPU MHz:\t%u.%u%s (full-speed)\n\n") TEXT("EXPECTED AVERAGE VIDEO GAME\n") TEXT("PERFORMANCE: %u FPS"), (unsigned)totalhiresfps, (unsigned)(totalmhz10[0] / 10), (unsigned)(totalmhz10[0] % 10), (LPCTSTR)(IS_APPLE2 ? TEXT(" (6502)") : TEXT("")), (unsigned)(totalmhz10[1] / 10), (unsigned)(totalmhz10[1] % 10), (LPCTSTR)(IS_APPLE2 ? TEXT(" (6502)") : TEXT("")), (unsigned)realisticfps); MessageBox(g_hFrameWindow, outstr, TEXT("Benchmarks"), MB_ICONINFORMATION | MB_SETFOREGROUND); }