diff --git a/Src/Model3/DSB.cpp b/Src/Model3/DSB.cpp index 724b4a7..f968e27 100644 --- a/Src/Model3/DSB.cpp +++ b/Src/Model3/DSB.cpp @@ -291,9 +291,11 @@ void CDSB1::IOWrite8(UINT32 addr, UINT8 data) break; case 0xE8: // MPEG volume + volume = data; break; case 0xE9: // MPEG stereo + stereo = data; break; case 0xF0: // command echo back @@ -394,6 +396,8 @@ void CDSB1::RunFrame(INT16 *audioL, INT16 *audioR) // Run remaining cycles Z80.Run(cycles); + //printf("VOLUME=%02X STEREO=%02X\n", volume, stereo); + // Decode MPEG for this frame INT16 *mpegFill[2] = { &mpegL[retainedSamples], &mpegR[retainedSamples] }; MPEG_Decode(mpegFill, 32000/60-retainedSamples+2); @@ -490,6 +494,7 @@ CDSB1::~CDSB1(void) Digital Sound Board Type 2: 68K CPU ******************************************************************************/ +// MPEG state machine enum { ST_IDLE = 0, @@ -510,9 +515,28 @@ enum ST_GOTB5, }; +static const char *stateName[] = +{ + "idle", + "st_14_0", + "st_14_1", + "st_got24", + "st_24_0", + "st_24_1", + "st_got74", + "st_gota0", + "st_gota1", + "st_gota4", + "st_gota5", + "st_gotb0", + "st_gotb1", + "st_gotb4", + "st_gotb5" +}; + void CDSB2::WriteMPEGFIFO(UINT8 byte) { - //printf("fifo: %x (state %d)\n", byte, mpegState); + printf("fifo: %x (state %s)\n", byte, stateName[mpegState]); switch (mpegState) { case ST_IDLE: @@ -524,6 +548,7 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte) else if (byte == 0x74 || byte == 0x75) // "start play" { MPEG_PlayMemory((const char *) &mpegROM[mpegStart], mpegEnd-mpegStart); + printf("playing %X\n", mpegStart); mpegState = ST_IDLE; playing = 1; } @@ -563,11 +588,11 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte) if (playing) { - //printf("Setting loop point to %x\n", mpegStart); + printf("Setting loop point to %x\n", mpegStart); MPEG_PlayMemory((const char *) &mpegROM[mpegStart], mpegEnd-mpegStart); } - //printf("mpegStart=%x\n", mpegStart); + printf("mpegStart=%x\n", mpegStart); break; case ST_GOT24: mpegEnd &= ~0xff0000; @@ -582,7 +607,7 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte) case ST_24_1: mpegEnd &= ~0xff; mpegEnd |= (byte); - //printf("mpegEnd=%x\n", mpegEnd); + printf("mpegEnd=%x\n", mpegEnd); // default to full stereo // mixer_set_stereo_volume(0, 255, 255); @@ -599,8 +624,14 @@ void CDSB2::WriteMPEGFIFO(UINT8 byte) case ST_GOTA1: mpegState = ST_IDLE; break; - case ST_GOTA4: + case ST_GOTA4: // dayto2pe plays advertise tune from this state by writing 0x75 mpegState = ST_IDLE; + if (byte == 0x75) + { + MPEG_PlayMemory((const char *) &mpegROM[mpegStart], mpegEnd-mpegStart); + printf("playing %X (from st_gota4)\n", mpegStart); + playing = 1; + } break; case ST_GOTA5: mpegState = ST_IDLE; @@ -720,7 +751,7 @@ void CDSB2::Write16(UINT32 addr, UINT16 data) *(UINT16 *) &ram[addr] = data; return; } -// printf("W16: %x @ %x\n", data, addr); + printf("W16: %x @ %x\n", data, addr); } void CDSB2::Write32(UINT32 addr, UINT32 data) @@ -732,7 +763,7 @@ void CDSB2::Write32(UINT32 addr, UINT32 data) *(UINT16 *) &ram[addr+2] = data&0xFFFF; return; } -// printf("W32: %x @ %x\n", data, addr); + printf("W32: %x @ %x\n", data, addr); } void CDSB2::SendCommand(UINT8 data) diff --git a/Src/Model3/DSB.h b/Src/Model3/DSB.h index 590b9d2..f3d7913 100644 --- a/Src/Model3/DSB.h +++ b/Src/Model3/DSB.h @@ -188,8 +188,9 @@ private: UINT32 startLatch; // MPEG start address latch UINT32 endLatch; // MPEG end address latch UINT8 status; - UINT8 chain; UINT8 cmdLatch; + UINT8 volume; + UINT8 stereo; // Z80 CPU CZ80 Z80; diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 07338fa..e6d184d 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -1,1276 +1,1277 @@ -/** - ** Supermodel - ** A Sega Model 3 Arcade Emulator. - ** Copyright 2011 Bart Trzynadlowski - ** - ** This file is part of Supermodel. - ** - ** Supermodel is free software: you can redistribute it and/or modify it under - ** the terms of the GNU General Public License as published by the Free - ** Software Foundation, either version 3 of the License, or (at your option) - ** any later version. - ** - ** Supermodel is distributed in the hope that it will be useful, but WITHOUT - ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - ** more details. - ** - ** You should have received a copy of the GNU General Public License along - ** with Supermodel. If not, see . - **/ - -/* - * Main.cpp - * - * Main program driver for the SDL port. - * - * Compile-Time Options - * -------------------- - * - SUPERMODEL_SOUND: Enables experimental sound code. The 68K core (Turbo68K) - * only works on x86 (32-bit) systems, so this cannot be enabled on 64-bit - * builds. - * - SUPERMODEL_WIN32: Define this if compiling on Windows. - * - SUPERMODEL_OSX: Define this if compiling on Mac OS X. - * - * TO-DO List - * ---------- - * - A lot of this code is actually OS-independent! Should it be moved into the - * root of the source tree? Might not be worth it; eventually, OS-dependent - * UIs will be introduced. - */ - -#include -#include -#include -#include -#include -#include "Pkgs/glew.h" -#ifdef SUPERMODEL_OSX -#include -#else -#include -#endif - -#include "Supermodel.h" -#include "SDLInputSystem.h" -#ifdef SUPERMODEL_WIN32 -#include "DirectInputSystem.h" -#endif - -CLogger *GetLogger(); -void SetLogger(CLogger *logger); - -/****************************************************************************** - Display Management -******************************************************************************/ - -/* - * Position and size of rectangular region within OpenGL display to render to - */ -unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport -unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport) - -/* - * CreateGLScreen(): - * - * Creates an OpenGL display surface of the requested size. xOffset and yOffset - * are used to return a display surface offset (for OpenGL viewport commands) - * because the actual drawing area may need to be adjusted to preserve the - * Model 3 aspect ratio. The new resolution will be passed back as well. - */ -static BOOL CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, - BOOL keepAspectRatio, BOOL fullScreen) -{ - const SDL_VideoInfo *VideoInfo; - GLenum err; - float model3Ratio, ratio; - float xRes, yRes; - - // Initialize video subsystem - if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) - return ErrorLog("Unable to initialize SDL video subsystem: %s\n", SDL_GetError()); - - // Important GL attributes - SDL_GL_SetAttribute(SDL_GL_RED_SIZE,5); // need at least RGB555 for Model 3 textures - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,5); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,5); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); - - // Set video mode - if (SDL_SetVideoMode(*xResPtr,*yResPtr,0,SDL_OPENGL|(fullScreen?SDL_FULLSCREEN|SDL_HWSURFACE:0)) == NULL) - { - ErrorLog("Unable to create an OpenGL display: %s\n", SDL_GetError()); - return FAIL; - } - - VideoInfo = SDL_GetVideoInfo(); // what resolution did we actually get? - - // If required, fix the aspect ratio of the resolution that the user passed to match Model 3 ratio - xRes = (float) *xResPtr; - yRes = (float) *yResPtr; - if (keepAspectRatio) - { - model3Ratio = 496.0f/384.0f; - ratio = xRes/yRes; - if (yRes < (xRes/model3Ratio)) - xRes = yRes*model3Ratio; - if (xRes < (yRes*model3Ratio)) - yRes = xRes/model3Ratio; - } - - // Center the visible area - *xOffsetPtr = (*xResPtr - (unsigned) xRes)/2; - *yOffsetPtr = (*yResPtr - (unsigned) yRes)/2; - - // If the desired resolution is smaller than what we got, re-center again - if (*xResPtr < VideoInfo->current_w) - *xOffsetPtr += (VideoInfo->current_w - *xResPtr)/2; - if (*yResPtr < VideoInfo->current_h) - *yOffsetPtr += (VideoInfo->current_h - *yResPtr)/2; - - // Create window caption - SDL_WM_SetCaption(caption,NULL); - - // Initialize GLEW, allowing us to use features beyond OpenGL 1.2 - err = glewInit(); - if (GLEW_OK != err) - { - ErrorLog("OpenGL initialization failed: %s\n", glewGetErrorString(err)); - return FAIL; - } - - // OpenGL initialization - glViewport(0,0,*xResPtr,*yResPtr); - glClearColor(0.0,0.0,0.0,0.0); - glClearDepth(1.0); - glDepthFunc(GL_LESS); - glEnable(GL_DEPTH_TEST); - glShadeModel(GL_SMOOTH); - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - glDisable(GL_CULL_FACE); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(90.0,(GLfloat)xRes/(GLfloat)yRes,0.1,1e5); - glMatrixMode(GL_MODELVIEW); - glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // clear at least once to ensure black border - - // Write back resolution parameters - *xResPtr = (unsigned) xRes; - *yResPtr = (unsigned) yRes; - - return 0; -} - -/****************************************************************************** - Configuration -******************************************************************************/ - -#define CONFIG_FILE_PATH "Config/Supermodel.ini" -#define CONFIG_FILE_COMMENT ";\n" \ - "; Supermodel Configuration File\n" \ - ";\n" - -// Create and configure inputs -static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure) -{ - // Create and initialize inputs - CInputs* Inputs = new CInputs(InputSystem); - if (!Inputs->Initialize()) - { - ErrorLog("Unable to initalize inputs.\n"); - return NULL; - } - - // Open and parse configuration file - CINIFile INI; - INI.Open(CONFIG_FILE_PATH); // doesn't matter if it exists or not, will get overwritten - INI.Parse(); - - Inputs->ReadFromINIFile(&INI, "Global"); - - // If the user wants to configure the inputs, do that now - if (configure) - { - // Open an SDL window - unsigned xOffset, yOffset, xRes=496, yRes=384; - if (OKAY != CreateGLScreen("Supermodel - Configuring Inputs...",&xOffset,&yOffset,&xRes,&yRes,FALSE,FALSE)) - { - ErrorLog("Unable to start SDL to configure inputs.\n"); - return NULL; - } - - // Configure the inputs - if (Inputs->ConfigureInputs(NULL, xOffset, yOffset, xRes, yRes)) - { - // Write input configuration and input system settings to config file - Inputs->WriteToINIFile(&INI, "Global"); - - if (OKAY != INI.Write(CONFIG_FILE_COMMENT)) - ErrorLog("Unable to save configuration to %s.", CONFIG_FILE_PATH); - else - printf("Configuration successfully saved to %s.\n", CONFIG_FILE_PATH); - } - else - puts("Configuration aborted..."); - puts(""); - } - - return Inputs; -} - -/****************************************************************************** - Save States and NVRAM - - Save states and NVRAM use the same basic format. - - Header block name: "Supermodel Save State" or "Supermodel NVRAM State" - Data: Save state file version (4-byte integer), ROM set ID (up to 9 bytes, - including terminating \0). - - Different subsystems output their own blocks. -******************************************************************************/ - -#define STATE_FILE_VERSION 0 // save state file version - -static unsigned saveSlot = 0; // save state slot # - -static void SaveState(CModel3 *Model3) -{ - CBlockFile SaveState; - char filePath[24]; - int fileVersion = STATE_FILE_VERSION; - - sprintf(filePath, "Saves/%s.st%d", Model3->GetGameInfo()->id, saveSlot); - if (OKAY != SaveState.Create(filePath, "Supermodel Save State", "Supermodel Version " SUPERMODEL_VERSION)) - { - ErrorLog("Unable to save state to %s.", filePath); - return; - } - - // Write file format version and ROM set ID to header block - SaveState.Write(&fileVersion, sizeof(fileVersion)); - SaveState.Write(Model3->GetGameInfo()->id, strlen(Model3->GetGameInfo()->id)+1); - - // Save state - Model3->SaveState(&SaveState); - SaveState.Close(); - printf("Saved state to %s.\n", filePath); - DebugLog("Saved state to %s.\n", filePath); -} - -static void LoadState(CModel3 *Model3) -{ - CBlockFile SaveState; - char filePath[24]; - int fileVersion; - - // Generate file path - sprintf(filePath, "Saves/%s.st%d", Model3->GetGameInfo()->id, saveSlot); - - // Open and check to make sure format is correct - if (OKAY != SaveState.Load(filePath)) - { - ErrorLog("Unable to load state from %s.", filePath); - return; - } - - if (OKAY != SaveState.FindBlock("Supermodel Save State")) - { - ErrorLog("%s does not appear to be a valid save state file.", filePath); - return; - } - - SaveState.Read(&fileVersion, sizeof(fileVersion)); - if (fileVersion != STATE_FILE_VERSION) - { - ErrorLog("Format of %s is incompatible with this version of Supermodel.", filePath); - return; - } - - // Load - Model3->LoadState(&SaveState); - SaveState.Close(); - printf("Loaded state from %s.\n", filePath); - DebugLog("Loaded state from %s.\n", filePath); -} - -static void SaveNVRAM(CModel3 *Model3) -{ - CBlockFile NVRAM; - char filePath[24]; - int fileVersion = STATE_FILE_VERSION; - - sprintf(filePath, "NVRAM/%s.nv", Model3->GetGameInfo()->id); - if (OKAY != NVRAM.Create(filePath, "Supermodel NVRAM State", "Supermodel Version " SUPERMODEL_VERSION)) - { - ErrorLog("Unable to save NVRAM to %s. Make sure directory exists!", filePath); - return; - } - - // Write file format version and ROM set ID to header block - NVRAM.Write(&fileVersion, sizeof(fileVersion)); - NVRAM.Write(Model3->GetGameInfo()->id, strlen(Model3->GetGameInfo()->id)+1); - - // Save NVRAM - Model3->SaveNVRAM(&NVRAM); - NVRAM.Close(); - DebugLog("Saved NVRAM to %s.\n", filePath); -} - -static void LoadNVRAM(CModel3 *Model3) -{ - CBlockFile NVRAM; - char filePath[24]; - int fileVersion; - - // Generate file path - sprintf(filePath, "NVRAM/%s.nv", Model3->GetGameInfo()->id); - - // Open and check to make sure format is correct - if (OKAY != NVRAM.Load(filePath)) - { - //ErrorLog("Unable to restore NVRAM from %s.", filePath); - return; - } - - if (OKAY != NVRAM.FindBlock("Supermodel NVRAM State")) - { - ErrorLog("%s does not appear to be a valid NVRAM file.", filePath); - return; - } - - NVRAM.Read(&fileVersion, sizeof(fileVersion)); - if (fileVersion != STATE_FILE_VERSION) - { - ErrorLog("Format of %s is incompatible with this version of Supermodel.", filePath); - return; - } - - // Load - Model3->LoadNVRAM(&NVRAM); - NVRAM.Close(); - DebugLog("Loaded NVRAM from %s.\n", filePath); -} - - -/****************************************************************************** - Main Program Driver - - All configuration management is done prior to calling Supermodel(). -******************************************************************************/ - -#ifdef SUPERMODEL_DEBUGGER -int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, unsigned ppcFrequency, BOOL multiThreaded, - unsigned xResParam, unsigned yResParam, BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS, - const char *vsFile, const char *fsFile) -{ -#else -int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL multiThreaded, unsigned xResParam, unsigned yResParam, - BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS, const char *vsFile, const char *fsFile) -{ - CModel3 *Model3 = new CModel3(); -#endif // SUPERMODEL_DEBUGGER - char titleStr[128], titleFPSStr[128]; - CRender2D *Render2D = new CRender2D(); - CRender3D *Render3D = new CRender3D(); - unsigned prevFPSTicks, currentFPSTicks, currentTicks, targetTicks, startTicks; - unsigned fpsFramesElapsed, framesElapsed; - BOOL showCursor = FALSE; // show cursor in full screen mode? - BOOL quit = 0; - BOOL paused = 0; - - // Info log user options - InfoLog("PowerPC frequency: %d Hz", ppcFrequency); - InfoLog("Resolution: %dx%d (%s)", xResParam, yResParam, fullScreen?"full screen":"windowed"); - InfoLog("Frame rate limiting: %s", noThrottle?"Disabled":"Enabled"); - - // Initialize and load ROMs - Model3->Init(ppcFrequency, multiThreaded); - if (OKAY != Model3->LoadROMSet(Model3GameList, zipFile)) - return 1; - - // Load NVRAM - LoadNVRAM(Model3); - - // Start up SDL and open a GL window - xRes = xResParam; - yRes = yResParam; - sprintf(titleStr, "Supermodel - %s", Model3->GetGameInfo()->title); - if (OKAY != CreateGLScreen(titleStr,&xOffset,&yOffset,&xRes,&yRes,keepAspectRatio,fullScreen)) - return 1; - - // Initialize audio system - if (OKAY != OpenAudio()) - return 1; - - // Hide mouse if fullscreen - Inputs->GetInputSystem()->SetMouseVisibility(!fullScreen); - - // Attach the inputs to the emulator - Model3->AttachInputs(Inputs); - - // Initialize the renderer - if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes)) - goto QuitError; - if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, vsFile, fsFile)) - goto QuitError; - Model3->AttachRenderers(Render2D,Render3D); - - // Reset emulator - Model3->Reset(); - -#ifdef SUPERMODEL_DEBUGGER - // If debugger was supplied, set it as logger and attach it to system - CLogger *oldLogger = GetLogger(); - if (Debugger != NULL) - { - SetLogger(Debugger); - Debugger->Attach(); - } -#endif // SUPERMODEL_DEBUGGER - - // Emulate! - fpsFramesElapsed = 0; - framesElapsed = 0; - prevFPSTicks = SDL_GetTicks(); - startTicks = prevFPSTicks; - while (!quit) - { - // Check if paused - if (!paused) - { - // If not, run one frame - Model3->RunFrame(); - - // Swap the buffers - SDL_GL_SwapBuffers(); - } - - // Poll the inputs - if (!Inputs->Poll(Model3->GetGameInfo(), xOffset, yOffset, xRes, yRes)) - quit = 1; - -#ifdef SUPERMODEL_DEBUGGER - if (Debugger != NULL) - { - Debugger->Poll(); - - // Check if debugger requests exit or pause - if (Debugger->CheckExit()) - quit = 1; - else if (Debugger->CheckPause()) - paused = 1; - else - { -#endif // SUPERMODEL_DEBUGGER - - // Check UI controls - if (Inputs->uiExit->Pressed()) - { - // Quit emulator - quit = 1; - } - else if (Inputs->uiReset->Pressed()) - { - // Reset emulator - Model3->Reset(); - -#ifdef SUPERMODEL_DEBUGGER - // If debugger was supplied, reset it too - if (Debugger != NULL) - Debugger->Reset(); -#endif // SUPERMODEL_DEBUGGER - - printf("Model 3 reset.\n"); - } - else if (Inputs->uiPause->Pressed()) - { - // Toggle emulator paused flag - paused = !paused; - } - else if (Inputs->uiSaveState->Pressed()) - { - // Save game state - SaveState(Model3); - } - else if (Inputs->uiChangeSlot->Pressed()) - { - // Change save slot - ++saveSlot; - saveSlot %= 10; // clamp to [0,9] - printf("Save slot: %d\n", saveSlot); - } - else if (Inputs->uiLoadState->Pressed()) - { - // Load game state - LoadState(Model3); - -#ifdef SUPERMODEL_DEBUGGER - // If debugger was supplied, reset it after loading state - if (Debugger != NULL) - Debugger->Reset(); -#endif // SUPERMODEL_DEBUGGER - } - else if (Inputs->uiDumpInpState->Pressed()) - { - // Dump input states - Inputs->DumpState(Model3->GetGameInfo()); - } - else if (Inputs->uiToggleCursor->Pressed() && fullScreen) - { - // Toggle cursor in full screen mode - showCursor = !showCursor; - Inputs->GetInputSystem()->SetMouseVisibility(!!showCursor); - } - else if (Inputs->uiClearNVRAM->Pressed()) - { - // Clear NVRAM - Model3->ClearNVRAM(); - printf("NVRAM cleared.\n"); - } - else if (Inputs->uiToggleFrLimit->Pressed()) - { - // Toggle frame limiting - noThrottle = !noThrottle; - printf("Frame limiting: %s\n", noThrottle?"Off":"On"); - } -#ifdef SUPERMODEL_DEBUGGER - else if (Inputs->uiEnterDebugger->Pressed()) - { - // Break execution and enter debugger - Debugger->ForceBreak(true); - } - } - } -#endif // SUPERMODEL_DEBUGGER - - // FPS and frame rate - currentFPSTicks = SDL_GetTicks(); - currentTicks = currentFPSTicks; - - // FPS - if (showFPS) - { - ++fpsFramesElapsed; - if((currentFPSTicks-prevFPSTicks) >= 1000) // update FPS every 1 second (each tick is 1 ms) - { - sprintf(titleFPSStr, "%s - %1.1f FPS", titleStr, (float)fpsFramesElapsed*(float)(currentFPSTicks-prevFPSTicks)/1000.0f); - SDL_WM_SetCaption(titleFPSStr,NULL); - prevFPSTicks = currentFPSTicks; // reset tick count - fpsFramesElapsed = 0; // reset frame count - } - } - - // Frame limiting/paused - if (paused || !noThrottle) - { - ++framesElapsed; - targetTicks = startTicks + (unsigned) ((float)framesElapsed * 1000.0f/60.0f); - if (currentTicks <= targetTicks) // add a delay until we reach the next (target) frame time - SDL_Delay(targetTicks-currentTicks); - else // begin a new frame - { - framesElapsed = 0; - startTicks = currentTicks; - } - } - } - -#ifdef SUPERMODEL_DEBUGGER - // If debugger was supplied, detach it from system and restore old logger - if (Debugger != NULL) - { - Debugger->Detach(); - SetLogger(oldLogger); - } -#endif // SUPERMODEL_DEBUGGER - - // Save NVRAM - SaveNVRAM(Model3); - - // Close audio - CloseAudio(); - - // Shut down -#ifndef SUPERMODEL_DEBUGGER - delete Model3; -#endif // SUPERMODEL_DEBUGGER - delete Render2D; - delete Render3D; - - // Dump PowerPC registers -#ifdef DEBUG - for (int i = 0; i < 32; i += 4) - printf("R%d=%08X\tR%d=%08X\tR%d=%08X\tR%d=%08X\n", - i + 0, ppc_get_gpr(i + 0), - i + 1, ppc_get_gpr(i + 1), - i + 2, ppc_get_gpr(i + 2), - i + 3, ppc_get_gpr(i + 3)); - printf("PC =%08X\n", ppc_get_pc()); - printf("LR =%08X\n", ppc_get_lr()); - /* - printf("DBAT0U=%08X\tIBAT0U=%08X\n", ppc_read_spr(SPR603E_DBAT0U), ppc_read_spr(SPR603E_IBAT0U)); - printf("DBAT0L=%08X\tIBAT0L=%08X\n", ppc_read_spr(SPR603E_DBAT0L), ppc_read_spr(SPR603E_IBAT0L)); - printf("DBAT1U=%08X\tIBAT1U=%08X\n", ppc_read_spr(SPR603E_DBAT1U), ppc_read_spr(SPR603E_IBAT1U)); - printf("DBAT1L=%08X\tIBAT1L=%08X\n", ppc_read_spr(SPR603E_DBAT1L), ppc_read_spr(SPR603E_IBAT1L)); - printf("DBAT2U=%08X\tIBAT2U=%08X\n", ppc_read_spr(SPR603E_DBAT2U), ppc_read_spr(SPR603E_IBAT2U)); - printf("DBAT2L=%08X\tIBAT2L=%08X\n", ppc_read_spr(SPR603E_DBAT2L), ppc_read_spr(SPR603E_IBAT2L)); - printf("DBAT3U=%08X\tIBAT3U=%08X\n", ppc_read_spr(SPR603E_DBAT3U), ppc_read_spr(SPR603E_IBAT3U)); - printf("DBAT3L=%08X\tIBAT3L=%08X\n", ppc_read_spr(SPR603E_DBAT3L), ppc_read_spr(SPR603E_IBAT3L)); - for (int i = 0; i < 10; i++) - printf("SR%d =%08X\n", i, ppc_read_sr(i)); - for (int i = 10; i < 16; i++) - printf("SR%d=%08X\n", i, ppc_read_sr(i)); - printf("SDR1=%08X\n", ppc_read_spr(SPR603E_SDR1)); - */ -#endif - - return 0; - - // Quit with an error -QuitError: -#ifndef SUPERMODEL_DEBUGGER - delete Model3; -#endif // SUPERMODEL_DEBUGGER - delete Render2D; - delete Render3D; - return 1; -} - - -/****************************************************************************** - Error and Debug Logging -******************************************************************************/ - -static CLogger *s_logger = NULL; - -/* - * Returns the current logger. - */ -CLogger *GetLogger() -{ - return s_logger; -} - -/* - * Sets the current logger. - */ -void SetLogger(CLogger *logger) -{ - s_logger = logger; -} - -/* - * DebugLog(fmt, ...): - * - * Logs a debug message with the logger. - * - * Parameters: - * fmt Format string (same as printf()). - * ... Variable number of arguments as required by format string. - */ -void DebugLog(const char *fmt, ...) -{ - if (s_logger == NULL) - return; - va_list vl; - va_start(vl, fmt); - s_logger->DebugLog(fmt, vl); - va_end(vl); -} - -/* - * InfoLog(fmt, ...); - * - * Logs an info message with the logger. - * - * Parameters: - * fmt Format string (same as printf()). - * ... Variable number of arguments as required by format string. - */ -void InfoLog(const char *fmt, ...) -{ - if (s_logger == NULL) - return; - va_list vl; - va_start(vl, fmt); - s_logger->InfoLog(fmt, vl); - va_end(vl); -} - -/* - * ErrorLog(fmt, ...): - * - * Logs an error message with the logger. - * - * Parameters: - * fmt Format string (same as printf()). - * ... Variable number of arguments as required by format string. - * - * Returns: - * Always returns FAIL. - */ -BOOL ErrorLog(const char *fmt, ...) -{ - if (s_logger == NULL) - return FAIL; - va_list vl; - va_start(vl, fmt); - s_logger->ErrorLog(fmt, vl); - va_end(vl); - return FAIL; -} - -#define DEBUG_LOG_FILE "debug.log" -#define ERROR_LOG_FILE "error.log" - -/* - * Default logger that logs to debug and error log files. - */ -class CFileLogger : public CLogger -{ -private: - const char *m_debugLogFile; - const char *m_errorLogFile; - -public: - CFileLogger(const char *debugLogFile, const char *errorLogFile) : - m_debugLogFile(debugLogFile), m_errorLogFile(errorLogFile) - { - // - } - - /* - * DebugLog(fmt, ...): - * - * Prints to debug log. The file is opened and closed each time so that its - * contents are preserved even if the program crashes. - */ - void DebugLog(const char *fmt, va_list vl) - { -#ifdef DEBUG - char string[1024]; - FILE *fp; - - fp = fopen(m_debugLogFile, "ab"); - if (NULL != fp) - { - vsprintf(string, fmt, vl); - fprintf(fp, string); - fclose(fp); - } -#endif // DEBUG - } - - /* - * InfoLog(fmt, ...); - * - * Prints information to the error log file but does not print to stderr. This - * is useful for logging non-error information. - */ - void InfoLog(const char *fmt, va_list vl) - { - char string[4096]; - FILE *fp; - - vsprintf(string, fmt, vl); - - fp = fopen(m_errorLogFile, "ab"); - if (NULL != fp) - { - fprintf(fp, "%s\n", string); - fclose(fp); - } - - CLogger::DebugLog("Info: "); - CLogger::DebugLog(string); - CLogger::DebugLog("\n"); - } - - /* - * ErrorLog(fmt, ...): - * - * Prints an error to stderr and the error log file. - */ - void ErrorLog(const char *fmt, va_list vl) - { - char string[4096]; - FILE *fp; - - vsprintf(string, fmt, vl); - fprintf(stderr, "Error: %s\n", string); - - fp = fopen(m_errorLogFile, "ab"); - if (NULL != fp) - { - fprintf(fp, "%s\n", string); - fclose(fp); - } - - CLogger::DebugLog("Error: "); - CLogger::DebugLog(string); - CLogger::DebugLog("\n"); - } - - void ClearLogs() - { -#ifdef DEBUG - ClearLog(DEBUG_LOG_FILE, "Supermodel v"SUPERMODEL_VERSION" Debug Log"); -#endif // DEBUG - ClearLog(ERROR_LOG_FILE, "Supermodel v"SUPERMODEL_VERSION" Error Log"); - } - - // Clear log file - void ClearLog(const char *file, const char *title) - { - FILE *fp = fopen(file, "w"); - if (NULL != fp) - { - unsigned i; - fprintf(fp, "%s\n", title); - for (i = 0; i < strlen(title); i++) - fputc('-', fp); - fprintf(fp, "\n\n"); - fclose(fp); - } - } -}; - -/****************************************************************************** - Diagnostic Commands -******************************************************************************/ - -// Disassemble instructions from CROM -static int DisassembleCROM(const char *zipFile, UINT32 addr, unsigned n) -{ - const struct GameInfo *Game; - UINT8 *crom; - struct ROMMap Map[] = - { - { "CROM", NULL }, - { "CROMxx", NULL }, - { NULL, NULL } - }; - char mnem[16], oprs[48]; - UINT32 op; - - // Do we have a valid CROM address? - if (addr < 0xFF800000) - return ErrorLog("Valid CROM address range is FF800000-FFFFFFFF."); - - // Allocate memory and set ROM region - crom = new(std::nothrow) UINT8[0x8800000]; - if (NULL == crom) - return ErrorLog("Insufficient memory to load CROM (need %d MB).", (0x8800000/8)); - Map[0].ptr = crom; - Map[1].ptr = &crom[0x800000]; - - // Load ROM set - Game = LoadROMSetFromZIPFile(Map, Model3GameList, zipFile, FALSE); - if (NULL == Game) - return ErrorLog("Failed to load ROM set."); - - // Mirror CROM if necessary - if (Game->cromSize < 0x800000) // high part of fixed CROM region contains CROM0 - CopyRegion(crom, 0, 0x800000-0x200000, &crom[0x800000], 0x800000); - - // Disassemble! - addr -= 0xFF800000; - while ((n > 0) && ((addr+4) <= 0x800000)) - { - op = (crom[addr+0]<<24) | (crom[addr+1]<<16) | (crom[addr+2]<<8) | crom[addr+3]; - - printf("%08X: ", addr+0xFF800000); - if (DisassemblePowerPC(op, addr+0xFF800000, mnem, oprs, 1)) - { - if (mnem[0] != '\0') // invalid form - printf("%08X %s*\t%s\n", op, mnem, oprs); - else - printf("%08X ?\n", op); - } - else - printf("%08X %s\t%s\n", op, mnem, oprs); - - addr += 4; - --n; - } - - delete [] crom; - return OKAY; -} - -/* - * PrintGLInfo(): - * - * Queries and prints OpenGL information. A full list of extensions can - * optionally be printed. - */ -static void PrintGLInfo(BOOL printExtensions) -{ - const GLubyte *str; - char *strLocal; - GLint value; - unsigned xOffset, yOffset, xRes=496, yRes=384; - - if (OKAY != CreateGLScreen("Supermodel - Querying OpenGL Information...",&xOffset,&yOffset,&xRes,&yRes,FALSE,FALSE)) - { - ErrorLog("Unable to query OpenGL.\n"); - return; - } - - puts("OpenGL information:\n"); - - str = glGetString(GL_VENDOR); - printf(" Vendor: %s\n", str); - - str = glGetString(GL_RENDERER); - printf(" Renderer: %s\n", str); - - str = glGetString(GL_VERSION); - printf(" Version: %s\n", str); - - str = glGetString(GL_SHADING_LANGUAGE_VERSION); - printf(" Shading Language Version: %s\n", str); - - glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &value); - printf("Maximum Vertex Array Size: %d vertices\n", value); - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); - printf(" Maximum Texture Size: %d texels\n", value); - - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value); - printf("Maximum Vertex Attributes: %d\n", value); - - glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &value); - printf(" Maximum Vertex Uniforms: %d\n", value); - - if (printExtensions) - { - str = glGetString(GL_EXTENSIONS); - strLocal = (char *) malloc((strlen((char *) str)+1)*sizeof(char)); - if (NULL == strLocal) - printf(" Supported Extensions: %s\n", str); - else - { - strcpy(strLocal, (char *) str); - printf(" Supported Extensions: %s\n", (strLocal = strtok(strLocal, " \t\n"))); - while ((strLocal = strtok(NULL, " \t\n")) != NULL) - printf(" %s\n", strLocal); - } - } - - printf("\n"); -} - - -/****************************************************************************** - Entry Point and Command Line Procesing -******************************************************************************/ - -// Print Supermodel title and copyright information -static void Title(void) -{ - puts("Supermodel: A Sega Model 3 Arcade Emulator (Version "SUPERMODEL_VERSION")"); - puts("Copyright (C) 2011 by Bart Trzynadlowski"); - puts(""); -} - -// Print usage information -static void Help(void) -{ - puts("Usage: Supermodel [options]"); - puts("ROM set must be a valid ZIP file containing a single game."); - puts(""); - puts("General Options:"); - puts(" -?, -h Print this help text"); - puts(" -print-games List supported games"); - puts(""); - puts("Emulation Options:"); - puts(" -ppc-frequency= Set PowerPC frequency in MHz [Default: 25]"); - puts(" -multi-threaded Enable multi-threading"); -#ifdef SUPERMODEL_DEBUGGER - puts(" -disable-debugger Completely disable debugger functionality"); - puts(" -enter-debugger Enter debugger at start of emulation"); -#endif // SUPERMODEL_DEBUGGER - puts(""); - puts("Video Options:"); - puts(" -res=, Resolution"); - puts(" -fullscreen Full screen mode"); - puts(" -no-throttle Disable 60 Hz frame rate limit"); - puts(" -show-fps Display frame rate in window title bar"); -#ifdef DEBUG // ordinary users do not need to know about these, but they are always available - puts(" -vert-shader= Load 3D vertex shader from external file"); - puts(" -frag-shader= Load 3D fragment shader from external file"); -#endif - puts(""); - puts("Input Options:"); - puts(" -input-system= Set input system [Default: SDL]"); - puts(" -print-inputs Prints current input configuration"); - puts(" -config-inputs Configure inputs for keyboards, mice and joysticks"); - puts(""); - puts("Diagnostic Options:"); -#ifdef DEBUG - puts(" -dis=[,n] Disassemble PowerPC code from CROM"); -#endif - puts(" -print-gl-info Print extensive OpenGL information\n"); -} - -// Print game list -static void PrintGameList(void) -{ - int i, j; - - puts("Supported games:"); - puts(""); - puts(" ROM Set Title"); - puts(" ------- -----"); - for (i = 0; Model3GameList[i].title != NULL; i++) - { - printf(" %s", Model3GameList[i].id); - for (j = strlen(Model3GameList[i].id); j < 8; j++) // pad for alignment (no game ID is more than 8 letters) - printf(" "); - printf(" %s\n", Model3GameList[i].title); - } -} - -/* - * main(argc, argv): - * - * Program entry point. - */ -int main(int argc, char **argv) -{ - int i, ret; - int cmd=0, fileIdx=0, cmdMultiThreaded=0, cmdFullScreen=0, cmdNoThrottle=0, cmdShowFPS=0, cmdPrintInputs=0, cmdConfigInputs=0, cmdPrintGames=0, cmdDis=0, cmdPrintGLInfo=0; -#ifdef SUPERMODEL_DEBUGGER - int cmdDisableDebugger = 0, cmdEnterDebugger=0; -#endif // SUPERMODEL_DEBUGGER - unsigned n, xRes=496, yRes=384, ppcFrequency=25000000; - char *vsFile = NULL, *fsFile = NULL, *inpSysName = NULL; - UINT32 addr; - - Title(); - if (argc <= 1) - { - Help(); - return 0; - } - - // Create default logger - CFileLogger Logger(DEBUG_LOG_FILE, ERROR_LOG_FILE); - Logger.ClearLogs(); - SetLogger(&Logger); - - // Parse command line - for (i = 1; i < argc; i++) - { - if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"-?")) - { - Help(); - return 0; - } - else if (!strcmp(argv[i],"-print-games")) - cmd = cmdPrintGames = 1; - else if (!strncmp(argv[i],"-ppc-frequency",14)) - { - int f; - ret = sscanf(&argv[i][14],"=%d",&f); - if (ret != 1) - ErrorLog("-ppc-frequency requires a frequency."); - else - { - if ((f<1) || (f>1000)) // limit to 1-1000MHz - ErrorLog("PowerPC frequency must be between 1 and 1000 MHz. Ignoring."); - else - ppcFrequency = f*1000000; - } - } - else if (!strncmp(argv[i],"-multi-threaded", 16)) - cmd = cmdMultiThreaded = 1; -#ifdef SUPERMODEL_DEBUGGER - else if (!strncmp(argv[i],"-disable-debugger",17)) - cmd = cmdDisableDebugger = 1; - else if (!strncmp(argv[i],"-enter-debugger",15)) - cmd = cmdEnterDebugger = 1; -#endif // SUPERMODEL_DEBUGGER - else if (!strncmp(argv[i],"-res",4)) - { - unsigned x, y; - - ret = sscanf(&argv[i][4],"=%d,%d",&x,&y); - if (ret != 2) - ErrorLog("-res requires both a width and a height."); - else - { - xRes = x; - yRes = y; - } - } - else if (!strcmp(argv[i],"-fullscreen")) - cmd = cmdFullScreen = 1; - else if (!strcmp(argv[i],"-no-throttle")) - cmd = cmdNoThrottle = 1; - else if (!strcmp(argv[i],"-show-fps")) - cmd = cmdShowFPS = 1; - else if (!strncmp(argv[i],"-vert-shader=",13)) - { - if (argv[i][13] == '\0') - ErrorLog("-vert-shader requires a file path."); - else - vsFile = &argv[i][13]; - } - else if (!strncmp(argv[i],"-frag-shader=",13)) - { - if (argv[i][13] == '\0') - ErrorLog("-frag-shader requires a file path."); - else - fsFile = &argv[i][13]; - } - else if (!strncmp(argv[i],"-input-system=", 14)) - { - if (argv[i][14] == '\0') - ErrorLog("-input-system requires an input system name."); - else - inpSysName = &argv[i][14]; - } - else if (!strcmp(argv[i],"-print-inputs")) - cmd = cmdPrintInputs = 1; - else if (!strcmp(argv[i],"-config-inputs")) - cmd = cmdConfigInputs = 1; - else if (!strncmp(argv[i],"-dis",4)) - { - ret = sscanf(&argv[i][4],"=%X,%X",&addr,&n); - if (ret == 1) - { - n = 16; - cmd = cmdDis = 1; - } - else if (ret == 2) - cmd = cmdDis = 1; - else - ErrorLog("-dis requires address and, optionally, number of instructions."); - } - else if (!strcmp(argv[i],"-print-gl-info")) - cmd = cmdPrintGLInfo = 1; - else if (argv[i][0] == '-') - ErrorLog("Ignoring invalid option: %s.", argv[i]); - else - { - if (fileIdx) // already specified a file - ErrorLog("Multiple files specified. Using %s, ignoring %s.", argv[i], argv[fileIdx]); - else - fileIdx = i; - } - } - - // Initialize SDL (individual subsystems get initialized later) - if (SDL_Init(0) != 0) - { - ErrorLog("Unable to initialize SDL: %s\n", SDL_GetError()); - return 1; - } - - CInputSystem *InputSystem = NULL; - CInputs *Inputs = NULL; - int exitCode = 0; -#ifdef SUPERMODEL_DEBUGGER - CModel3 *Model3 = NULL; - Debugger::CSupermodelDebugger *Debugger = NULL; -#endif // SUPERMODEL_DEBUGGER - - // Create input system (default is SDL) - if (inpSysName == NULL || stricmp(inpSysName, "sdl") == 0) - InputSystem = new CSDLInputSystem(); -#ifdef SUPERMODEL_WIN32 - else if (stricmp(inpSysName, "dinput") == 0) - InputSystem = new CDirectInputSystem(false, false, false); - else if (stricmp(inpSysName, "xinput") == 0) - InputSystem = new CDirectInputSystem(false, true, false); - else if (stricmp(inpSysName, "rawinput") == 0) - InputSystem = new CDirectInputSystem(true, false, false); -#endif // SUPERMODEL_WIN32 - else - { - ErrorLog("Unknown input system: '%s'.\n", inpSysName); - exitCode = 1; - goto Exit; - } - - // Create inputs from input system (configuring them if required) - Inputs = CreateInputs(InputSystem, cmdConfigInputs); - if (Inputs == NULL) - { - exitCode = 1; - goto Exit; - } - - if (cmdPrintInputs) - { - Inputs->PrintInputs(NULL); - InputSystem->PrintSettings(); - } - - // Process commands that don't require ROM set - if (cmd) - { - if (cmdPrintGames) - { - PrintGameList(); - goto Exit; - } - - if (cmdPrintGLInfo) - { - PrintGLInfo(FALSE); - goto Exit; - } - } - - if (fileIdx == 0) - { - ErrorLog("No ROM set specified."); - exitCode = 1; - goto Exit; - } - - // Process commands that require ROMs - if (cmd) - { - if (cmdDis) - { - if (OKAY != DisassembleCROM(argv[fileIdx], addr, n)) - exitCode = 1; - goto Exit; - } - } - -#ifdef SUPERMODEL_DEBUGGER - // Create Model3 - Model3 = new CModel3(); - // Create Supermodel debugger unless debugging is disabled - if (!cmdDisableDebugger) - { - Debugger = new Debugger::CSupermodelDebugger(Model3, Inputs, &Logger); - // If -enter-debugger option was set force debugger to break straightaway - if (cmdEnterDebugger) - Debugger->ForceBreak(true); - } - // Fire up Supermodel with debugger - exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile); - if (Debugger != NULL) - delete Debugger; - delete Model3; -#else - // Fire up Supermodel - exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile); -#endif // SUPERMODEL_DEBUGGER - -Exit: - if (Inputs != NULL) - delete Inputs; - if (InputSystem != NULL) - delete InputSystem; - SDL_Quit(); - return exitCode; -} +/** + ** Supermodel + ** A Sega Model 3 Arcade Emulator. + ** Copyright 2011 Bart Trzynadlowski + ** + ** This file is part of Supermodel. + ** + ** Supermodel is free software: you can redistribute it and/or modify it under + ** the terms of the GNU General Public License as published by the Free + ** Software Foundation, either version 3 of the License, or (at your option) + ** any later version. + ** + ** Supermodel is distributed in the hope that it will be useful, but WITHOUT + ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + ** more details. + ** + ** You should have received a copy of the GNU General Public License along + ** with Supermodel. If not, see . + **/ + +/* + * Main.cpp + * + * Main program driver for the SDL port. + * + * Compile-Time Options + * -------------------- + * - SUPERMODEL_SOUND: Enables experimental sound code. The 68K core (Turbo68K) + * only works on x86 (32-bit) systems, so this cannot be enabled on 64-bit + * builds. + * - SUPERMODEL_WIN32: Define this if compiling on Windows. + * - SUPERMODEL_OSX: Define this if compiling on Mac OS X. + * + * TO-DO List + * ---------- + * - A lot of this code is actually OS-independent! Should it be moved into the + * root of the source tree? Might not be worth it; eventually, OS-dependent + * UIs will be introduced. + */ + +#include +#include +#include +#include +#include +#include "Pkgs/glew.h" +#ifdef SUPERMODEL_OSX +#include +#else +#include +#endif + +#include "Supermodel.h" +#include "SDLInputSystem.h" +#ifdef SUPERMODEL_WIN32 +#include "DirectInputSystem.h" +#endif + +CLogger *GetLogger(); +void SetLogger(CLogger *logger); + +/****************************************************************************** + Display Management +******************************************************************************/ + +/* + * Position and size of rectangular region within OpenGL display to render to + */ +unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport +unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport) + +/* + * CreateGLScreen(): + * + * Creates an OpenGL display surface of the requested size. xOffset and yOffset + * are used to return a display surface offset (for OpenGL viewport commands) + * because the actual drawing area may need to be adjusted to preserve the + * Model 3 aspect ratio. The new resolution will be passed back as well. + */ +static BOOL CreateGLScreen(const char *caption, unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *xResPtr, unsigned *yResPtr, + BOOL keepAspectRatio, BOOL fullScreen) +{ + const SDL_VideoInfo *VideoInfo; + GLenum err; + float model3Ratio, ratio; + float xRes, yRes; + + // Initialize video subsystem + if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) + return ErrorLog("Unable to initialize SDL video subsystem: %s\n", SDL_GetError()); + + // Important GL attributes + SDL_GL_SetAttribute(SDL_GL_RED_SIZE,5); // need at least RGB555 for Model 3 textures + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,5); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,5); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,16); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1); + + // Set video mode + if (SDL_SetVideoMode(*xResPtr,*yResPtr,0,SDL_OPENGL|(fullScreen?SDL_FULLSCREEN|SDL_HWSURFACE:0)) == NULL) + { + ErrorLog("Unable to create an OpenGL display: %s\n", SDL_GetError()); + return FAIL; + } + + VideoInfo = SDL_GetVideoInfo(); // what resolution did we actually get? + + // If required, fix the aspect ratio of the resolution that the user passed to match Model 3 ratio + xRes = (float) *xResPtr; + yRes = (float) *yResPtr; + if (keepAspectRatio) + { + model3Ratio = 496.0f/384.0f; + ratio = xRes/yRes; + if (yRes < (xRes/model3Ratio)) + xRes = yRes*model3Ratio; + if (xRes < (yRes*model3Ratio)) + yRes = xRes/model3Ratio; + } + + // Center the visible area + *xOffsetPtr = (*xResPtr - (unsigned) xRes)/2; + *yOffsetPtr = (*yResPtr - (unsigned) yRes)/2; + + // If the desired resolution is smaller than what we got, re-center again + if (*xResPtr < VideoInfo->current_w) + *xOffsetPtr += (VideoInfo->current_w - *xResPtr)/2; + if (*yResPtr < VideoInfo->current_h) + *yOffsetPtr += (VideoInfo->current_h - *yResPtr)/2; + + // Create window caption + SDL_WM_SetCaption(caption,NULL); + + // Initialize GLEW, allowing us to use features beyond OpenGL 1.2 + err = glewInit(); + if (GLEW_OK != err) + { + ErrorLog("OpenGL initialization failed: %s\n", glewGetErrorString(err)); + return FAIL; + } + + // OpenGL initialization + glViewport(0,0,*xResPtr,*yResPtr); + glClearColor(0.0,0.0,0.0,0.0); + glClearDepth(1.0); + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glDisable(GL_CULL_FACE); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(90.0,(GLfloat)xRes/(GLfloat)yRes,0.1,1e5); + glMatrixMode(GL_MODELVIEW); + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); // clear at least once to ensure black border + + // Write back resolution parameters + *xResPtr = (unsigned) xRes; + *yResPtr = (unsigned) yRes; + + return 0; +} + +/****************************************************************************** + Configuration +******************************************************************************/ + +#define CONFIG_FILE_PATH "Config/Supermodel.ini" +#define CONFIG_FILE_COMMENT ";\n" \ + "; Supermodel Configuration File\n" \ + ";\n" + +// Create and configure inputs +static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure) +{ + // Create and initialize inputs + CInputs* Inputs = new CInputs(InputSystem); + if (!Inputs->Initialize()) + { + ErrorLog("Unable to initalize inputs.\n"); + return NULL; + } + + // Open and parse configuration file + CINIFile INI; + INI.Open(CONFIG_FILE_PATH); // doesn't matter if it exists or not, will get overwritten + INI.Parse(); + + Inputs->ReadFromINIFile(&INI, "Global"); + + // If the user wants to configure the inputs, do that now + if (configure) + { + // Open an SDL window + unsigned xOffset, yOffset, xRes=496, yRes=384; + if (OKAY != CreateGLScreen("Supermodel - Configuring Inputs...",&xOffset,&yOffset,&xRes,&yRes,FALSE,FALSE)) + { + ErrorLog("Unable to start SDL to configure inputs.\n"); + return NULL; + } + + // Configure the inputs + if (Inputs->ConfigureInputs(NULL, xOffset, yOffset, xRes, yRes)) + { + // Write input configuration and input system settings to config file + Inputs->WriteToINIFile(&INI, "Global"); + + if (OKAY != INI.Write(CONFIG_FILE_COMMENT)) + ErrorLog("Unable to save configuration to %s.", CONFIG_FILE_PATH); + else + printf("Configuration successfully saved to %s.\n", CONFIG_FILE_PATH); + } + else + puts("Configuration aborted..."); + puts(""); + } + + return Inputs; +} + +/****************************************************************************** + Save States and NVRAM + + Save states and NVRAM use the same basic format. + + Header block name: "Supermodel Save State" or "Supermodel NVRAM State" + Data: Save state file version (4-byte integer), ROM set ID (up to 9 bytes, + including terminating \0). + + Different subsystems output their own blocks. +******************************************************************************/ + +#define STATE_FILE_VERSION 0 // save state file version + +static unsigned saveSlot = 0; // save state slot # + +static void SaveState(CModel3 *Model3) +{ + CBlockFile SaveState; + char filePath[24]; + int fileVersion = STATE_FILE_VERSION; + + sprintf(filePath, "Saves/%s.st%d", Model3->GetGameInfo()->id, saveSlot); + if (OKAY != SaveState.Create(filePath, "Supermodel Save State", "Supermodel Version " SUPERMODEL_VERSION)) + { + ErrorLog("Unable to save state to %s.", filePath); + return; + } + + // Write file format version and ROM set ID to header block + SaveState.Write(&fileVersion, sizeof(fileVersion)); + SaveState.Write(Model3->GetGameInfo()->id, strlen(Model3->GetGameInfo()->id)+1); + + // Save state + Model3->SaveState(&SaveState); + SaveState.Close(); + printf("Saved state to %s.\n", filePath); + DebugLog("Saved state to %s.\n", filePath); +} + +static void LoadState(CModel3 *Model3) +{ + CBlockFile SaveState; + char filePath[24]; + int fileVersion; + + // Generate file path + sprintf(filePath, "Saves/%s.st%d", Model3->GetGameInfo()->id, saveSlot); + + // Open and check to make sure format is correct + if (OKAY != SaveState.Load(filePath)) + { + ErrorLog("Unable to load state from %s.", filePath); + return; + } + + if (OKAY != SaveState.FindBlock("Supermodel Save State")) + { + ErrorLog("%s does not appear to be a valid save state file.", filePath); + return; + } + + SaveState.Read(&fileVersion, sizeof(fileVersion)); + if (fileVersion != STATE_FILE_VERSION) + { + ErrorLog("Format of %s is incompatible with this version of Supermodel.", filePath); + return; + } + + // Load + Model3->LoadState(&SaveState); + SaveState.Close(); + printf("Loaded state from %s.\n", filePath); + DebugLog("Loaded state from %s.\n", filePath); +} + +static void SaveNVRAM(CModel3 *Model3) +{ + CBlockFile NVRAM; + char filePath[24]; + int fileVersion = STATE_FILE_VERSION; + + sprintf(filePath, "NVRAM/%s.nv", Model3->GetGameInfo()->id); + if (OKAY != NVRAM.Create(filePath, "Supermodel NVRAM State", "Supermodel Version " SUPERMODEL_VERSION)) + { + ErrorLog("Unable to save NVRAM to %s. Make sure directory exists!", filePath); + return; + } + + // Write file format version and ROM set ID to header block + NVRAM.Write(&fileVersion, sizeof(fileVersion)); + NVRAM.Write(Model3->GetGameInfo()->id, strlen(Model3->GetGameInfo()->id)+1); + + // Save NVRAM + Model3->SaveNVRAM(&NVRAM); + NVRAM.Close(); + DebugLog("Saved NVRAM to %s.\n", filePath); +} + +static void LoadNVRAM(CModel3 *Model3) +{ + CBlockFile NVRAM; + char filePath[24]; + int fileVersion; + + // Generate file path + sprintf(filePath, "NVRAM/%s.nv", Model3->GetGameInfo()->id); + + // Open and check to make sure format is correct + if (OKAY != NVRAM.Load(filePath)) + { + //ErrorLog("Unable to restore NVRAM from %s.", filePath); + return; + } + + if (OKAY != NVRAM.FindBlock("Supermodel NVRAM State")) + { + ErrorLog("%s does not appear to be a valid NVRAM file.", filePath); + return; + } + + NVRAM.Read(&fileVersion, sizeof(fileVersion)); + if (fileVersion != STATE_FILE_VERSION) + { + ErrorLog("Format of %s is incompatible with this version of Supermodel.", filePath); + return; + } + + // Load + Model3->LoadNVRAM(&NVRAM); + NVRAM.Close(); + DebugLog("Loaded NVRAM from %s.\n", filePath); +} + + +/****************************************************************************** + Main Program Driver + + All configuration management is done prior to calling Supermodel(). +******************************************************************************/ + +#ifdef SUPERMODEL_DEBUGGER +int Supermodel(const char *zipFile, CModel3 *Model3, CInputs *Inputs, Debugger::CDebugger *Debugger, unsigned ppcFrequency, BOOL multiThreaded, + unsigned xResParam, unsigned yResParam, BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS, + const char *vsFile, const char *fsFile) +{ +CLogger *oldLogger; +#else +int Supermodel(const char *zipFile, CInputs *Inputs, unsigned ppcFrequency, BOOL multiThreaded, unsigned xResParam, unsigned yResParam, + BOOL keepAspectRatio, BOOL fullScreen, BOOL noThrottle, BOOL showFPS, const char *vsFile, const char *fsFile) +{ + CModel3 *Model3 = new CModel3(); +#endif // SUPERMODEL_DEBUGGER + char titleStr[128], titleFPSStr[128]; + CRender2D *Render2D = new CRender2D(); + CRender3D *Render3D = new CRender3D(); + unsigned prevFPSTicks, currentFPSTicks, currentTicks, targetTicks, startTicks; + unsigned fpsFramesElapsed, framesElapsed; + BOOL showCursor = FALSE; // show cursor in full screen mode? + BOOL quit = 0; + BOOL paused = 0; + + // Info log user options + InfoLog("PowerPC frequency: %d Hz", ppcFrequency); + InfoLog("Resolution: %dx%d (%s)", xResParam, yResParam, fullScreen?"full screen":"windowed"); + InfoLog("Frame rate limiting: %s", noThrottle?"Disabled":"Enabled"); + + // Initialize and load ROMs + Model3->Init(ppcFrequency, multiThreaded); + if (OKAY != Model3->LoadROMSet(Model3GameList, zipFile)) + return 1; + + // Load NVRAM + LoadNVRAM(Model3); + + // Start up SDL and open a GL window + xRes = xResParam; + yRes = yResParam; + sprintf(titleStr, "Supermodel - %s", Model3->GetGameInfo()->title); + if (OKAY != CreateGLScreen(titleStr,&xOffset,&yOffset,&xRes,&yRes,keepAspectRatio,fullScreen)) + return 1; + + // Initialize audio system + if (OKAY != OpenAudio()) + return 1; + + // Hide mouse if fullscreen + Inputs->GetInputSystem()->SetMouseVisibility(!fullScreen); + + // Attach the inputs to the emulator + Model3->AttachInputs(Inputs); + + // Initialize the renderer + if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes)) + goto QuitError; + if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, vsFile, fsFile)) + goto QuitError; + Model3->AttachRenderers(Render2D,Render3D); + + // Reset emulator + Model3->Reset(); + +#ifdef SUPERMODEL_DEBUGGER + // If debugger was supplied, set it as logger and attach it to system + oldLogger = GetLogger(); + if (Debugger != NULL) + { + SetLogger(Debugger); + Debugger->Attach(); + } +#endif // SUPERMODEL_DEBUGGER + + // Emulate! + fpsFramesElapsed = 0; + framesElapsed = 0; + prevFPSTicks = SDL_GetTicks(); + startTicks = prevFPSTicks; + while (!quit) + { + // Check if paused + if (!paused) + { + // If not, run one frame + Model3->RunFrame(); + + // Swap the buffers + SDL_GL_SwapBuffers(); + } + + // Poll the inputs + if (!Inputs->Poll(Model3->GetGameInfo(), xOffset, yOffset, xRes, yRes)) + quit = 1; + +#ifdef SUPERMODEL_DEBUGGER + if (Debugger != NULL) + { + Debugger->Poll(); + + // Check if debugger requests exit or pause + if (Debugger->CheckExit()) + quit = 1; + else if (Debugger->CheckPause()) + paused = 1; + else + { +#endif // SUPERMODEL_DEBUGGER + + // Check UI controls + if (Inputs->uiExit->Pressed()) + { + // Quit emulator + quit = 1; + } + else if (Inputs->uiReset->Pressed()) + { + // Reset emulator + Model3->Reset(); + +#ifdef SUPERMODEL_DEBUGGER + // If debugger was supplied, reset it too + if (Debugger != NULL) + Debugger->Reset(); +#endif // SUPERMODEL_DEBUGGER + + printf("Model 3 reset.\n"); + } + else if (Inputs->uiPause->Pressed()) + { + // Toggle emulator paused flag + paused = !paused; + } + else if (Inputs->uiSaveState->Pressed()) + { + // Save game state + SaveState(Model3); + } + else if (Inputs->uiChangeSlot->Pressed()) + { + // Change save slot + ++saveSlot; + saveSlot %= 10; // clamp to [0,9] + printf("Save slot: %d\n", saveSlot); + } + else if (Inputs->uiLoadState->Pressed()) + { + // Load game state + LoadState(Model3); + +#ifdef SUPERMODEL_DEBUGGER + // If debugger was supplied, reset it after loading state + if (Debugger != NULL) + Debugger->Reset(); +#endif // SUPERMODEL_DEBUGGER + } + else if (Inputs->uiDumpInpState->Pressed()) + { + // Dump input states + Inputs->DumpState(Model3->GetGameInfo()); + } + else if (Inputs->uiToggleCursor->Pressed() && fullScreen) + { + // Toggle cursor in full screen mode + showCursor = !showCursor; + Inputs->GetInputSystem()->SetMouseVisibility(!!showCursor); + } + else if (Inputs->uiClearNVRAM->Pressed()) + { + // Clear NVRAM + Model3->ClearNVRAM(); + printf("NVRAM cleared.\n"); + } + else if (Inputs->uiToggleFrLimit->Pressed()) + { + // Toggle frame limiting + noThrottle = !noThrottle; + printf("Frame limiting: %s\n", noThrottle?"Off":"On"); + } +#ifdef SUPERMODEL_DEBUGGER + else if (Inputs->uiEnterDebugger->Pressed()) + { + // Break execution and enter debugger + Debugger->ForceBreak(true); + } + } + } +#endif // SUPERMODEL_DEBUGGER + + // FPS and frame rate + currentFPSTicks = SDL_GetTicks(); + currentTicks = currentFPSTicks; + + // FPS + if (showFPS) + { + ++fpsFramesElapsed; + if((currentFPSTicks-prevFPSTicks) >= 1000) // update FPS every 1 second (each tick is 1 ms) + { + sprintf(titleFPSStr, "%s - %1.1f FPS", titleStr, (float)fpsFramesElapsed*(float)(currentFPSTicks-prevFPSTicks)/1000.0f); + SDL_WM_SetCaption(titleFPSStr,NULL); + prevFPSTicks = currentFPSTicks; // reset tick count + fpsFramesElapsed = 0; // reset frame count + } + } + + // Frame limiting/paused + if (paused || !noThrottle) + { + ++framesElapsed; + targetTicks = startTicks + (unsigned) ((float)framesElapsed * 1000.0f/60.0f); + if (currentTicks <= targetTicks) // add a delay until we reach the next (target) frame time + SDL_Delay(targetTicks-currentTicks); + else // begin a new frame + { + framesElapsed = 0; + startTicks = currentTicks; + } + } + } + +#ifdef SUPERMODEL_DEBUGGER + // If debugger was supplied, detach it from system and restore old logger + if (Debugger != NULL) + { + Debugger->Detach(); + SetLogger(oldLogger); + } +#endif // SUPERMODEL_DEBUGGER + + // Save NVRAM + SaveNVRAM(Model3); + + // Close audio + CloseAudio(); + + // Shut down +#ifndef SUPERMODEL_DEBUGGER + delete Model3; +#endif // SUPERMODEL_DEBUGGER + delete Render2D; + delete Render3D; + + // Dump PowerPC registers +#ifdef DEBUG + for (int i = 0; i < 32; i += 4) + printf("R%d=%08X\tR%d=%08X\tR%d=%08X\tR%d=%08X\n", + i + 0, ppc_get_gpr(i + 0), + i + 1, ppc_get_gpr(i + 1), + i + 2, ppc_get_gpr(i + 2), + i + 3, ppc_get_gpr(i + 3)); + printf("PC =%08X\n", ppc_get_pc()); + printf("LR =%08X\n", ppc_get_lr()); + /* + printf("DBAT0U=%08X\tIBAT0U=%08X\n", ppc_read_spr(SPR603E_DBAT0U), ppc_read_spr(SPR603E_IBAT0U)); + printf("DBAT0L=%08X\tIBAT0L=%08X\n", ppc_read_spr(SPR603E_DBAT0L), ppc_read_spr(SPR603E_IBAT0L)); + printf("DBAT1U=%08X\tIBAT1U=%08X\n", ppc_read_spr(SPR603E_DBAT1U), ppc_read_spr(SPR603E_IBAT1U)); + printf("DBAT1L=%08X\tIBAT1L=%08X\n", ppc_read_spr(SPR603E_DBAT1L), ppc_read_spr(SPR603E_IBAT1L)); + printf("DBAT2U=%08X\tIBAT2U=%08X\n", ppc_read_spr(SPR603E_DBAT2U), ppc_read_spr(SPR603E_IBAT2U)); + printf("DBAT2L=%08X\tIBAT2L=%08X\n", ppc_read_spr(SPR603E_DBAT2L), ppc_read_spr(SPR603E_IBAT2L)); + printf("DBAT3U=%08X\tIBAT3U=%08X\n", ppc_read_spr(SPR603E_DBAT3U), ppc_read_spr(SPR603E_IBAT3U)); + printf("DBAT3L=%08X\tIBAT3L=%08X\n", ppc_read_spr(SPR603E_DBAT3L), ppc_read_spr(SPR603E_IBAT3L)); + for (int i = 0; i < 10; i++) + printf("SR%d =%08X\n", i, ppc_read_sr(i)); + for (int i = 10; i < 16; i++) + printf("SR%d=%08X\n", i, ppc_read_sr(i)); + printf("SDR1=%08X\n", ppc_read_spr(SPR603E_SDR1)); + */ +#endif + + return 0; + + // Quit with an error +QuitError: +#ifndef SUPERMODEL_DEBUGGER + delete Model3; +#endif // SUPERMODEL_DEBUGGER + delete Render2D; + delete Render3D; + return 1; +} + + +/****************************************************************************** + Error and Debug Logging +******************************************************************************/ + +static CLogger *s_logger = NULL; + +/* + * Returns the current logger. + */ +CLogger *GetLogger() +{ + return s_logger; +} + +/* + * Sets the current logger. + */ +void SetLogger(CLogger *logger) +{ + s_logger = logger; +} + +/* + * DebugLog(fmt, ...): + * + * Logs a debug message with the logger. + * + * Parameters: + * fmt Format string (same as printf()). + * ... Variable number of arguments as required by format string. + */ +void DebugLog(const char *fmt, ...) +{ + if (s_logger == NULL) + return; + va_list vl; + va_start(vl, fmt); + s_logger->DebugLog(fmt, vl); + va_end(vl); +} + +/* + * InfoLog(fmt, ...); + * + * Logs an info message with the logger. + * + * Parameters: + * fmt Format string (same as printf()). + * ... Variable number of arguments as required by format string. + */ +void InfoLog(const char *fmt, ...) +{ + if (s_logger == NULL) + return; + va_list vl; + va_start(vl, fmt); + s_logger->InfoLog(fmt, vl); + va_end(vl); +} + +/* + * ErrorLog(fmt, ...): + * + * Logs an error message with the logger. + * + * Parameters: + * fmt Format string (same as printf()). + * ... Variable number of arguments as required by format string. + * + * Returns: + * Always returns FAIL. + */ +BOOL ErrorLog(const char *fmt, ...) +{ + if (s_logger == NULL) + return FAIL; + va_list vl; + va_start(vl, fmt); + s_logger->ErrorLog(fmt, vl); + va_end(vl); + return FAIL; +} + +#define DEBUG_LOG_FILE "debug.log" +#define ERROR_LOG_FILE "error.log" + +/* + * Default logger that logs to debug and error log files. + */ +class CFileLogger : public CLogger +{ +private: + const char *m_debugLogFile; + const char *m_errorLogFile; + +public: + CFileLogger(const char *debugLogFile, const char *errorLogFile) : + m_debugLogFile(debugLogFile), m_errorLogFile(errorLogFile) + { + // + } + + /* + * DebugLog(fmt, ...): + * + * Prints to debug log. The file is opened and closed each time so that its + * contents are preserved even if the program crashes. + */ + void DebugLog(const char *fmt, va_list vl) + { +#ifdef DEBUG + char string[1024]; + FILE *fp; + + fp = fopen(m_debugLogFile, "ab"); + if (NULL != fp) + { + vsprintf(string, fmt, vl); + fprintf(fp, string); + fclose(fp); + } +#endif // DEBUG + } + + /* + * InfoLog(fmt, ...); + * + * Prints information to the error log file but does not print to stderr. This + * is useful for logging non-error information. + */ + void InfoLog(const char *fmt, va_list vl) + { + char string[4096]; + FILE *fp; + + vsprintf(string, fmt, vl); + + fp = fopen(m_errorLogFile, "ab"); + if (NULL != fp) + { + fprintf(fp, "%s\n", string); + fclose(fp); + } + + CLogger::DebugLog("Info: "); + CLogger::DebugLog(string); + CLogger::DebugLog("\n"); + } + + /* + * ErrorLog(fmt, ...): + * + * Prints an error to stderr and the error log file. + */ + void ErrorLog(const char *fmt, va_list vl) + { + char string[4096]; + FILE *fp; + + vsprintf(string, fmt, vl); + fprintf(stderr, "Error: %s\n", string); + + fp = fopen(m_errorLogFile, "ab"); + if (NULL != fp) + { + fprintf(fp, "%s\n", string); + fclose(fp); + } + + CLogger::DebugLog("Error: "); + CLogger::DebugLog(string); + CLogger::DebugLog("\n"); + } + + void ClearLogs() + { +#ifdef DEBUG + ClearLog(DEBUG_LOG_FILE, "Supermodel v"SUPERMODEL_VERSION" Debug Log"); +#endif // DEBUG + ClearLog(ERROR_LOG_FILE, "Supermodel v"SUPERMODEL_VERSION" Error Log"); + } + + // Clear log file + void ClearLog(const char *file, const char *title) + { + FILE *fp = fopen(file, "w"); + if (NULL != fp) + { + unsigned i; + fprintf(fp, "%s\n", title); + for (i = 0; i < strlen(title); i++) + fputc('-', fp); + fprintf(fp, "\n\n"); + fclose(fp); + } + } +}; + +/****************************************************************************** + Diagnostic Commands +******************************************************************************/ + +// Disassemble instructions from CROM +static int DisassembleCROM(const char *zipFile, UINT32 addr, unsigned n) +{ + const struct GameInfo *Game; + UINT8 *crom; + struct ROMMap Map[] = + { + { "CROM", NULL }, + { "CROMxx", NULL }, + { NULL, NULL } + }; + char mnem[16], oprs[48]; + UINT32 op; + + // Do we have a valid CROM address? + if (addr < 0xFF800000) + return ErrorLog("Valid CROM address range is FF800000-FFFFFFFF."); + + // Allocate memory and set ROM region + crom = new(std::nothrow) UINT8[0x8800000]; + if (NULL == crom) + return ErrorLog("Insufficient memory to load CROM (need %d MB).", (0x8800000/8)); + Map[0].ptr = crom; + Map[1].ptr = &crom[0x800000]; + + // Load ROM set + Game = LoadROMSetFromZIPFile(Map, Model3GameList, zipFile, FALSE); + if (NULL == Game) + return ErrorLog("Failed to load ROM set."); + + // Mirror CROM if necessary + if (Game->cromSize < 0x800000) // high part of fixed CROM region contains CROM0 + CopyRegion(crom, 0, 0x800000-0x200000, &crom[0x800000], 0x800000); + + // Disassemble! + addr -= 0xFF800000; + while ((n > 0) && ((addr+4) <= 0x800000)) + { + op = (crom[addr+0]<<24) | (crom[addr+1]<<16) | (crom[addr+2]<<8) | crom[addr+3]; + + printf("%08X: ", addr+0xFF800000); + if (DisassemblePowerPC(op, addr+0xFF800000, mnem, oprs, 1)) + { + if (mnem[0] != '\0') // invalid form + printf("%08X %s*\t%s\n", op, mnem, oprs); + else + printf("%08X ?\n", op); + } + else + printf("%08X %s\t%s\n", op, mnem, oprs); + + addr += 4; + --n; + } + + delete [] crom; + return OKAY; +} + +/* + * PrintGLInfo(): + * + * Queries and prints OpenGL information. A full list of extensions can + * optionally be printed. + */ +static void PrintGLInfo(BOOL printExtensions) +{ + const GLubyte *str; + char *strLocal; + GLint value; + unsigned xOffset, yOffset, xRes=496, yRes=384; + + if (OKAY != CreateGLScreen("Supermodel - Querying OpenGL Information...",&xOffset,&yOffset,&xRes,&yRes,FALSE,FALSE)) + { + ErrorLog("Unable to query OpenGL.\n"); + return; + } + + puts("OpenGL information:\n"); + + str = glGetString(GL_VENDOR); + printf(" Vendor: %s\n", str); + + str = glGetString(GL_RENDERER); + printf(" Renderer: %s\n", str); + + str = glGetString(GL_VERSION); + printf(" Version: %s\n", str); + + str = glGetString(GL_SHADING_LANGUAGE_VERSION); + printf(" Shading Language Version: %s\n", str); + + glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &value); + printf("Maximum Vertex Array Size: %d vertices\n", value); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value); + printf(" Maximum Texture Size: %d texels\n", value); + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &value); + printf("Maximum Vertex Attributes: %d\n", value); + + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &value); + printf(" Maximum Vertex Uniforms: %d\n", value); + + if (printExtensions) + { + str = glGetString(GL_EXTENSIONS); + strLocal = (char *) malloc((strlen((char *) str)+1)*sizeof(char)); + if (NULL == strLocal) + printf(" Supported Extensions: %s\n", str); + else + { + strcpy(strLocal, (char *) str); + printf(" Supported Extensions: %s\n", (strLocal = strtok(strLocal, " \t\n"))); + while ((strLocal = strtok(NULL, " \t\n")) != NULL) + printf(" %s\n", strLocal); + } + } + + printf("\n"); +} + + +/****************************************************************************** + Entry Point and Command Line Procesing +******************************************************************************/ + +// Print Supermodel title and copyright information +static void Title(void) +{ + puts("Supermodel: A Sega Model 3 Arcade Emulator (Version "SUPERMODEL_VERSION")"); + puts("Copyright (C) 2011 by Bart Trzynadlowski"); + puts(""); +} + +// Print usage information +static void Help(void) +{ + puts("Usage: Supermodel [options]"); + puts("ROM set must be a valid ZIP file containing a single game."); + puts(""); + puts("General Options:"); + puts(" -?, -h Print this help text"); + puts(" -print-games List supported games"); + puts(""); + puts("Emulation Options:"); + puts(" -ppc-frequency= Set PowerPC frequency in MHz [Default: 25]"); + puts(" -multi-threaded Enable multi-threading"); +#ifdef SUPERMODEL_DEBUGGER + puts(" -disable-debugger Completely disable debugger functionality"); + puts(" -enter-debugger Enter debugger at start of emulation"); +#endif // SUPERMODEL_DEBUGGER + puts(""); + puts("Video Options:"); + puts(" -res=, Resolution"); + puts(" -fullscreen Full screen mode"); + puts(" -no-throttle Disable 60 Hz frame rate limit"); + puts(" -show-fps Display frame rate in window title bar"); +#ifdef DEBUG // ordinary users do not need to know about these, but they are always available + puts(" -vert-shader= Load 3D vertex shader from external file"); + puts(" -frag-shader= Load 3D fragment shader from external file"); +#endif + puts(""); + puts("Input Options:"); + puts(" -input-system= Set input system [Default: SDL]"); + puts(" -print-inputs Prints current input configuration"); + puts(" -config-inputs Configure inputs for keyboards, mice and joysticks"); + puts(""); + puts("Diagnostic Options:"); +#ifdef DEBUG + puts(" -dis=[,n] Disassemble PowerPC code from CROM"); +#endif + puts(" -print-gl-info Print extensive OpenGL information\n"); +} + +// Print game list +static void PrintGameList(void) +{ + int i, j; + + puts("Supported games:"); + puts(""); + puts(" ROM Set Title"); + puts(" ------- -----"); + for (i = 0; Model3GameList[i].title != NULL; i++) + { + printf(" %s", Model3GameList[i].id); + for (j = strlen(Model3GameList[i].id); j < 9; j++) // pad for alignment (no game ID is more than 9 letters) + printf(" "); + printf(" %s\n", Model3GameList[i].title); + } +} + +/* + * main(argc, argv): + * + * Program entry point. + */ +int main(int argc, char **argv) +{ + int i, ret; + int cmd=0, fileIdx=0, cmdMultiThreaded=0, cmdFullScreen=0, cmdNoThrottle=0, cmdShowFPS=0, cmdPrintInputs=0, cmdConfigInputs=0, cmdPrintGames=0, cmdDis=0, cmdPrintGLInfo=0; +#ifdef SUPERMODEL_DEBUGGER + int cmdDisableDebugger = 0, cmdEnterDebugger=0; +#endif // SUPERMODEL_DEBUGGER + unsigned n, xRes=496, yRes=384, ppcFrequency=25000000; + char *vsFile = NULL, *fsFile = NULL, *inpSysName = NULL; + UINT32 addr; + + Title(); + if (argc <= 1) + { + Help(); + return 0; + } + + // Create default logger + CFileLogger Logger(DEBUG_LOG_FILE, ERROR_LOG_FILE); + Logger.ClearLogs(); + SetLogger(&Logger); + + // Parse command line + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"-?")) + { + Help(); + return 0; + } + else if (!strcmp(argv[i],"-print-games")) + cmd = cmdPrintGames = 1; + else if (!strncmp(argv[i],"-ppc-frequency",14)) + { + int f; + ret = sscanf(&argv[i][14],"=%d",&f); + if (ret != 1) + ErrorLog("-ppc-frequency requires a frequency."); + else + { + if ((f<1) || (f>1000)) // limit to 1-1000MHz + ErrorLog("PowerPC frequency must be between 1 and 1000 MHz. Ignoring."); + else + ppcFrequency = f*1000000; + } + } + else if (!strncmp(argv[i],"-multi-threaded", 16)) + cmd = cmdMultiThreaded = 1; +#ifdef SUPERMODEL_DEBUGGER + else if (!strncmp(argv[i],"-disable-debugger",17)) + cmd = cmdDisableDebugger = 1; + else if (!strncmp(argv[i],"-enter-debugger",15)) + cmd = cmdEnterDebugger = 1; +#endif // SUPERMODEL_DEBUGGER + else if (!strncmp(argv[i],"-res",4)) + { + unsigned x, y; + + ret = sscanf(&argv[i][4],"=%d,%d",&x,&y); + if (ret != 2) + ErrorLog("-res requires both a width and a height."); + else + { + xRes = x; + yRes = y; + } + } + else if (!strcmp(argv[i],"-fullscreen")) + cmd = cmdFullScreen = 1; + else if (!strcmp(argv[i],"-no-throttle")) + cmd = cmdNoThrottle = 1; + else if (!strcmp(argv[i],"-show-fps")) + cmd = cmdShowFPS = 1; + else if (!strncmp(argv[i],"-vert-shader=",13)) + { + if (argv[i][13] == '\0') + ErrorLog("-vert-shader requires a file path."); + else + vsFile = &argv[i][13]; + } + else if (!strncmp(argv[i],"-frag-shader=",13)) + { + if (argv[i][13] == '\0') + ErrorLog("-frag-shader requires a file path."); + else + fsFile = &argv[i][13]; + } + else if (!strncmp(argv[i],"-input-system=", 14)) + { + if (argv[i][14] == '\0') + ErrorLog("-input-system requires an input system name."); + else + inpSysName = &argv[i][14]; + } + else if (!strcmp(argv[i],"-print-inputs")) + cmd = cmdPrintInputs = 1; + else if (!strcmp(argv[i],"-config-inputs")) + cmd = cmdConfigInputs = 1; + else if (!strncmp(argv[i],"-dis",4)) + { + ret = sscanf(&argv[i][4],"=%X,%X",&addr,&n); + if (ret == 1) + { + n = 16; + cmd = cmdDis = 1; + } + else if (ret == 2) + cmd = cmdDis = 1; + else + ErrorLog("-dis requires address and, optionally, number of instructions."); + } + else if (!strcmp(argv[i],"-print-gl-info")) + cmd = cmdPrintGLInfo = 1; + else if (argv[i][0] == '-') + ErrorLog("Ignoring invalid option: %s.", argv[i]); + else + { + if (fileIdx) // already specified a file + ErrorLog("Multiple files specified. Using %s, ignoring %s.", argv[i], argv[fileIdx]); + else + fileIdx = i; + } + } + + // Initialize SDL (individual subsystems get initialized later) + if (SDL_Init(0) != 0) + { + ErrorLog("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + CInputSystem *InputSystem = NULL; + CInputs *Inputs = NULL; + int exitCode = 0; +#ifdef SUPERMODEL_DEBUGGER + CModel3 *Model3 = NULL; + Debugger::CSupermodelDebugger *Debugger = NULL; +#endif // SUPERMODEL_DEBUGGER + + // Create input system (default is SDL) + if (inpSysName == NULL || stricmp(inpSysName, "sdl") == 0) + InputSystem = new CSDLInputSystem(); +#ifdef SUPERMODEL_WIN32 + else if (stricmp(inpSysName, "dinput") == 0) + InputSystem = new CDirectInputSystem(false, false, false); + else if (stricmp(inpSysName, "xinput") == 0) + InputSystem = new CDirectInputSystem(false, true, false); + else if (stricmp(inpSysName, "rawinput") == 0) + InputSystem = new CDirectInputSystem(true, false, false); +#endif // SUPERMODEL_WIN32 + else + { + ErrorLog("Unknown input system: '%s'.\n", inpSysName); + exitCode = 1; + goto Exit; + } + + // Create inputs from input system (configuring them if required) + Inputs = CreateInputs(InputSystem, cmdConfigInputs); + if (Inputs == NULL) + { + exitCode = 1; + goto Exit; + } + + if (cmdPrintInputs) + { + Inputs->PrintInputs(NULL); + InputSystem->PrintSettings(); + } + + // Process commands that don't require ROM set + if (cmd) + { + if (cmdPrintGames) + { + PrintGameList(); + goto Exit; + } + + if (cmdPrintGLInfo) + { + PrintGLInfo(FALSE); + goto Exit; + } + } + + if (fileIdx == 0) + { + ErrorLog("No ROM set specified."); + exitCode = 1; + goto Exit; + } + + // Process commands that require ROMs + if (cmd) + { + if (cmdDis) + { + if (OKAY != DisassembleCROM(argv[fileIdx], addr, n)) + exitCode = 1; + goto Exit; + } + } + +#ifdef SUPERMODEL_DEBUGGER + // Create Model3 + Model3 = new CModel3(); + // Create Supermodel debugger unless debugging is disabled + if (!cmdDisableDebugger) + { + Debugger = new Debugger::CSupermodelDebugger(Model3, Inputs, &Logger); + // If -enter-debugger option was set force debugger to break straightaway + if (cmdEnterDebugger) + Debugger->ForceBreak(true); + } + // Fire up Supermodel with debugger + exitCode = Supermodel(argv[fileIdx],Model3,Inputs,Debugger,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile); + if (Debugger != NULL) + delete Debugger; + delete Model3; +#else + // Fire up Supermodel + exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,cmdMultiThreaded,xRes,yRes,TRUE,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile); +#endif // SUPERMODEL_DEBUGGER + +Exit: + if (Inputs != NULL) + delete Inputs; + if (InputSystem != NULL) + delete InputSystem; + SDL_Quit(); + return exitCode; +}