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;
+}