Reorganized the codebase, since half the stuff here is obsolete.

It's still in the main CVS repository if you want it ...


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@245 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2004-05-24 17:18:23 +00:00
parent d9c3b3eb60
commit 469f00eefc
25 changed files with 3929 additions and 3423 deletions

View File

@ -0,0 +1,537 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "FrameBufferGL.hxx"
#include "MediaSrc.hxx"
#include "Settings.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferGL::FrameBufferGL()
: myTexture(0),
myFilterParam(GL_NEAREST)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferGL::~FrameBufferGL()
{
if(myTexture)
SDL_FreeSurface(myTexture);
glDeleteTextures(1, &myTextureID);
glDeleteTextures(256, myFontTextureID);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::createScreen()
{
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, myRGB[0] );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, myRGB[1] );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, myRGB[2] );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, myRGB[3] );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
GLint viewportX = 0, viewportY = 0;
uInt32 screenWidth = 0;
uInt32 screenHeight = 0;
uInt32 imageWidth = (uInt32) (myWidth * theZoomLevel * theAspectRatio);
uInt32 imageHeight = myHeight * theZoomLevel;
// Determine if we're in fullscreen or windowed mode
// In fullscreen mode, we clip the SDL screen to known resolutions
// In windowed mode, we use the actual image resolution for the SDL screen
if(mySDLFlags & SDL_FULLSCREEN)
{
SDL_Rect rect = viewport(imageWidth, imageHeight);
viewportX = rect.x;
viewportY = rect.y;
screenWidth = rect.w - 1;
screenHeight = rect.h - 1;
}
else
{
screenWidth = imageWidth;
screenHeight = imageHeight;
viewportX = viewportY = 0;
}
myScreen = SDL_SetVideoMode(screenWidth, screenHeight, 0, mySDLFlags);
if(myScreen == NULL)
{
cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
return false;
}
glPushAttrib(GL_ENABLE_BIT);
// Center the screen horizontally and vertically
glViewport(viewportX, viewportY, imageWidth, imageHeight);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0, (GLdouble) imageWidth/(theZoomLevel * theAspectRatio),
(GLdouble) imageHeight/theZoomLevel, 0.0, 0.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
#ifdef TEXTURES_ARE_LOST
createTextures();
#endif
// Make sure any old parts of the screen are erased
glClear(GL_COLOR_BUFFER_BIT);
theRedrawEntireFrameIndicator = true;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::init()
{
// Get the desired width and height of the display
myWidth = myMediaSource->width() << 1;
myHeight = myMediaSource->height();
// Get the aspect ratio for the display
// Since the display is already doubled horizontally, we half the
// ratio that is provided
theAspectRatio = myConsole->settings().getFloat("gl_aspect") / 2;
if(theAspectRatio <= 0.0)
theAspectRatio = 1.0;
// Now create the OpenGL SDL screen
Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
if(SDL_Init(initflags) < 0)
return false;
// Check which system we are running under
x11Available = false;
#if UNIX && (!__APPLE__)
SDL_VERSION(&myWMInfo.version);
if(SDL_GetWMInfo(&myWMInfo) > 0)
if(myWMInfo.subsystem == SDL_SYSWM_X11)
x11Available = true;
#endif
// Get the maximum size of a window for THIS screen
theMaxZoomLevel = maxWindowSizeForScreen();
// Check to see if window size will fit in the screen
if((uInt32)myConsole->settings().getInt("zoom") > theMaxZoomLevel)
theZoomLevel = theMaxZoomLevel;
else
theZoomLevel = myConsole->settings().getInt("zoom");
mySDLFlags = SDL_OPENGL;
mySDLFlags |= myConsole->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0;
// Set the window title and icon
setWindowAttributes();
// Set up the OpenGL attributes
myDepth = SDL_GetVideoInfo()->vfmt->BitsPerPixel;
switch(myDepth)
{
case 8:
myRGB[0] = 3;
myRGB[1] = 3;
myRGB[2] = 2;
myRGB[3] = 0;
break;
case 15:
myRGB[0] = 5;
myRGB[1] = 5;
myRGB[2] = 5;
myRGB[3] = 0;
break;
case 16:
myRGB[0] = 5;
myRGB[1] = 6;
myRGB[2] = 5;
myRGB[3] = 0;
break;
case 24:
myRGB[0] = 8;
myRGB[1] = 8;
myRGB[2] = 8;
myRGB[3] = 0;
break;
case 32:
myRGB[0] = 8;
myRGB[1] = 8;
myRGB[2] = 8;
myRGB[3] = 8;
break;
default: // This should never happen
break;
}
// Create the screen
if(!createScreen())
return false;
// Now check to see what color components were actually created
SDL_GL_GetAttribute( SDL_GL_RED_SIZE, (int*)&myRGB[0] );
SDL_GL_GetAttribute( SDL_GL_GREEN_SIZE, (int*)&myRGB[1] );
SDL_GL_GetAttribute( SDL_GL_BLUE_SIZE, (int*)&myRGB[2] );
SDL_GL_GetAttribute( SDL_GL_ALPHA_SIZE, (int*)&myRGB[3] );
#ifndef TEXTURES_ARE_LOST
// Create the texture surface and texture fonts
createTextures();
#endif
// Set up the palette *after* we know the color components
// and the textures
setupPalette();
// Show some OpenGL info
if(myConsole->settings().getBool("showinfo"))
{
ostringstream colormode;
colormode << "Color : " << myDepth << " bit, " << myRGB[0] << "-"
<< myRGB[1] << "-" << myRGB[2] << "-" << myRGB[3];
cout << endl
<< "Vendor : " << glGetString(GL_VENDOR) << endl
<< "Renderer: " << glGetString(GL_RENDERER) << endl
<< "Version : " << glGetString(GL_VERSION) << endl
<< colormode.str() << endl << endl;
}
// Make sure that theUseFullScreenFlag sets up fullscreen mode correctly
if(myConsole->settings().getBool("fullscreen"))
{
grabMouse(true);
showCursor(false);
}
else
{
// Keep mouse in game window if grabmouse is selected
grabMouse(myConsole->settings().getBool("grabmouse"));
// Show or hide the cursor depending on the 'hidecursor' argument
showCursor(!myConsole->settings().getBool("hidecursor"));
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawMediaSource()
{
// Copy the mediasource framebuffer to the RGB texture
uInt8* currentFrame = myMediaSource->currentFrameBuffer();
uInt8* previousFrame = myMediaSource->previousFrameBuffer();
uInt32 width = myMediaSource->width();
uInt32 height = myMediaSource->height();
uInt16* buffer = (uInt16*) myTexture->pixels;
register uInt32 y;
for(y = 0; y < height; ++y )
{
const uInt32 bufofsY = y * width;
const uInt32 screenofsY = y * myTexture->w;
register uInt32 x;
for(x = 0; x < width; ++x )
{
const uInt32 bufofs = bufofsY + x;
uInt8 v = currentFrame[bufofs];
if(v == previousFrame[bufofs] && !theRedrawEntireFrameIndicator)
continue;
// x << 1 is times 2 ( doubling width )
const uInt32 pos = screenofsY + (x << 1);
buffer[pos] = buffer[pos+1] = (uInt16) myPalette[v];
}
}
// Texturemap complete texture to surface so we have free scaling
// and antialiasing
glBindTexture(GL_TEXTURE_2D, myTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, myTexture->w, myTexture->h,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, myTexture->pixels);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_QUADS);
glTexCoord2f(myTexCoord[0], myTexCoord[1]); glVertex2i(0, 0);
glTexCoord2f(myTexCoord[2], myTexCoord[1]); glVertex2i(myWidth, 0);
glTexCoord2f(myTexCoord[2], myTexCoord[3]); glVertex2i(myWidth, myHeight);
glTexCoord2f(myTexCoord[0], myTexCoord[3]); glVertex2i(0, myHeight);
glEnd();
// The frame doesn't need to be completely redrawn anymore
theRedrawEntireFrameIndicator = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::preFrameUpdate()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::postFrameUpdate()
{
// Now show all changes made to the textures
SDL_GL_SwapBuffers();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
// First draw the box in the background, alpha-blended
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
glColor4f(0.0, 0.0, 0.0, 0.7);
glRecti(x, y, x+w, y+h);
// Now draw the outer edges
glLineWidth(theZoomLevel/2);
glColor4f(0.8, 0.8, 0.8, 1.0);
glBegin(GL_LINE_LOOP);
glVertex2i(x, y ); // Top Left
glVertex2i(x+w, y ); // Top Right
glVertex2i(x+w, y+h); // Bottom Right
glVertex2i(x, y+h); // Bottom Left
glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawText(uInt32 x, uInt32 y, const string& message)
{
for(uInt32 i = 0; i < message.length(); i++)
drawChar(x + i*8, y, (uInt32) message[i]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::drawChar(uInt32 x, uInt32 y, uInt32 c)
{
if(c >= 256 )
return;
glBindTexture(GL_TEXTURE_2D, myFontTextureID[c]);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2i(x, y );
glTexCoord2f(1, 0); glVertex2i(x+8, y );
glTexCoord2f(1, 1); glVertex2i(x+8, y+8);
glTexCoord2f(0, 1); glVertex2i(x, y+8);
glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::createTextures()
{
if(myTexture)
SDL_FreeSurface(myTexture);
glDeleteTextures(1, &myTextureID);
glDeleteTextures(256, myFontTextureID);
uInt32 w = power_of_two(myWidth);
uInt32 h = power_of_two(myHeight);
myTexCoord[0] = 0.0f;
myTexCoord[1] = 0.0f;
myTexCoord[2] = (GLfloat) myWidth / w;
myTexCoord[3] = (GLfloat) myHeight / h;
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 16,
0x0000F800, 0x000007E0, 0x0000001F, 0x00000000);
if(myTexture == NULL)
return false;
// Create an OpenGL texture from the SDL texture
bool showinfo = myConsole->settings().getBool("showinfo");
string filter = myConsole->settings().getString("gl_filter");
if(filter == "linear")
{
myFilterParam = GL_LINEAR;
if(showinfo)
cout << "Using GL_LINEAR filtering.\n";
}
else if(filter == "nearest")
{
myFilterParam = GL_NEAREST;
if(showinfo)
cout << "Using GL_NEAREST filtering.\n";
}
glGenTextures(1, &myTextureID);
glBindTexture(GL_TEXTURE_2D, myTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
myTexture->pixels);
// Now create the font textures. There are 256 fonts of 8x8 pixels.
// These will be stored in 256 textures of size 8x8.
SDL_Surface* fontTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, 8, 8, 32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
#else
0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
#endif
if(fontTexture == NULL)
return false;
// Create a texture for each character
glGenTextures(256, myFontTextureID);
for(uInt32 c = 0; c < 256; c++)
{
// First clear the texture
SDL_Rect tmp;
tmp.x = 0; tmp.y = 0; tmp.w = 8; tmp.h = 8;
SDL_FillRect(fontTexture, &tmp,
SDL_MapRGBA(fontTexture->format, 0xff, 0xff, 0xff, 0x0));
// Now fill the texture with font data
for(uInt32 y = 0; y < 8; y++)
{
for(uInt32 x = 0; x < 8; x++)
{
if((ourFontData[(c << 3) + y] >> x) & 1)
{
tmp.x = x;
tmp.y = y;
tmp.w = tmp.h = 1;
SDL_FillRect(fontTexture, &tmp,
SDL_MapRGBA(fontTexture->format, 0x10, 0x10, 0x10, 0xff));
}
}
}
glBindTexture(GL_TEXTURE_2D, myFontTextureID[c]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
fontTexture->pixels);
}
SDL_FreeSurface(fontTexture);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferGL::toggleFilter()
{
if(myFilterParam == GL_NEAREST)
{
myFilterParam = GL_LINEAR;
myConsole->settings().setString("gl_filter", "linear");
showMessage("GL_LINEAR filtering");
}
else
{
myFilterParam = GL_NEAREST;
myConsole->settings().setString("gl_filter", "nearest");
showMessage("GL_NEAREST filtering");
}
glBindTexture(GL_TEXTURE_2D, myTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
for(uInt32 i = 0; i < 256; i++)
{
glBindTexture(GL_TEXTURE_2D, myFontTextureID[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myFilterParam);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myFilterParam);
}
// The filtering has changed, so redraw the entire screen
theRedrawEntireFrameIndicator = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SDL_Rect FrameBufferGL::viewport(uInt32 width, uInt32 height)
{
SDL_Rect rect;
rect.x = rect.y = 0;
rect.w = width; rect.h = height;
struct Screenmode
{
uInt32 w;
uInt32 h;
};
// List of valid fullscreen OpenGL modes
Screenmode myScreenmode[6] = {
{320, 240 },
{640, 480 },
{800, 600 },
{1024, 768 },
{1280, 1024},
{1600, 1200}
};
for(uInt32 i = 0; i < 6; i++)
{
if(width <= myScreenmode[i].w && height <= myScreenmode[i].h)
{
rect.x = (myScreenmode[i].w - width) / 2;
rect.y = (myScreenmode[i].h - height) / 2;
rect.w = myScreenmode[i].w;
rect.h = myScreenmode[i].h;
return rect;
}
}
// If we get here, it probably indicates an error
// But we have to return something ...
return rect;
}

View File

@ -0,0 +1,169 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferGL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_GL_HXX
#define FRAMEBUFFER_GL_HXX
#include <SDL.h>
#include <SDL_opengl.h>
#include <SDL_syswm.h>
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "bspf.hxx"
class Console;
class MediaSource;
/**
This class implements an SDL OpenGL framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferGL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
*/
class FrameBufferGL : public FrameBufferSDL
{
public:
/**
Creates a new SDL OpenGL framebuffer
*/
FrameBufferGL();
/**
Destructor
*/
virtual ~FrameBufferGL();
/**
Switches between the two filtering options in OpenGL.
Currently, these are GL_NEAREST and GL_LINEAR.
*/
void toggleFilter();
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBufferSDL.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine is called whenever the screen needs to be recreated.
It updates the global screen variable.
*/
virtual bool createScreen();
/**
This routine is called to map a given r,g,b triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b)
{ return SDL_MapRGB(myTexture->format, r, g, b); }
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine should be called once the console is created to setup
the video system for us to use. Return false if any operation fails,
otherwise return true.
*/
virtual bool init();
/**
This routine should be called anytime the MediaSource needs to be redrawn
to the screen.
*/
virtual void drawMediaSource();
/**
This routine should be called to draw a rectangular box with sides
at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param w The width of the box
@param h The height of the box
*/
virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
/**
This routine should be called to draw text at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param message The message text
*/
virtual void drawText(uInt32 x, uInt32 y, const string& message);
/**
This routine should be called to draw character 'c' at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param c The character to draw
*/
virtual void drawChar(uInt32 x, uInt32 y, uInt32 c);
/**
This routine is called before any drawing is done (per-frame).
*/
virtual void preFrameUpdate();
/**
This routine is called after any drawing is done (per-frame).
*/
virtual void postFrameUpdate();
private:
bool createTextures();
SDL_Rect viewport(uInt32 width, uInt32 height);
uInt32 power_of_two(uInt32 input)
{
uInt32 value = 1;
while( value < input )
value <<= 1;
return value;
}
private:
// The main texture buffer
SDL_Surface* myTexture;
// The depth of the texture buffer
uInt32 myDepth;
// The size of color components for OpenGL
uInt32 myRGB[4];
// The OpenGL main texture handle
GLuint myTextureID;
// OpenGL texture coordinates for the main surface
GLfloat myTexCoord[4];
// The OpenGL font texture handles (one for each character)
GLuint myFontTextureID[256];
// The texture filtering to use
GLint myFilterParam;
};
#endif

View File

@ -0,0 +1,268 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSDL.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "MediaSrc.hxx"
#include "Settings.hxx"
#include "stella.xpm" // The Stella icon
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSDL::FrameBufferSDL()
: x11Available(false),
theZoomLevel(1),
theMaxZoomLevel(1),
theAspectRatio(1.0),
myPauseStatus(false)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSDL::~FrameBufferSDL()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::pauseEvent(bool status)
{
myPauseStatus = status;
setupPalette();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::setupPalette()
{
// Shade the palette to 75% normal value in pause mode
float shade = 1.0;
if(myPauseStatus)
shade = 0.75;
const uInt32* gamePalette = myMediaSource->palette();
for(uInt32 i = 0; i < 256; ++i)
{
Uint8 r, g, b;
r = (Uint8) (((gamePalette[i] & 0x00ff0000) >> 16) * shade);
g = (Uint8) (((gamePalette[i] & 0x0000ff00) >> 8) * shade);
b = (Uint8) ((gamePalette[i] & 0x000000ff) * shade);
myPalette[i] = mapRGB(r, g, b);
}
theRedrawEntireFrameIndicator = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::toggleFullscreen()
{
bool isFullscreen = !myConsole->settings().getBool("fullscreen");
// Update the settings
myConsole->settings().setBool("fullscreen", isFullscreen);
if(isFullscreen)
mySDLFlags |= SDL_FULLSCREEN;
else
mySDLFlags &= ~SDL_FULLSCREEN;
if(!createScreen())
return;
if(isFullscreen) // now in fullscreen mode
{
grabMouse(true);
showCursor(false);
}
else // now in windowed mode
{
grabMouse(myConsole->settings().getBool("grabmouse"));
showCursor(!myConsole->settings().getBool("hidecursor"));
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::resize(int mode)
{
// reset size to that given in properties
// this is a special case of allowing a resize while in fullscreen mode
if(mode == 0)
{
myWidth = myMediaSource->width() << 1;
myHeight = myMediaSource->height();
}
else if(mode == 1) // increase size
{
if(myConsole->settings().getBool("fullscreen"))
return;
if(theZoomLevel == theMaxZoomLevel)
theZoomLevel = 1;
else
theZoomLevel++;
}
else if(mode == -1) // decrease size
{
if(myConsole->settings().getBool("fullscreen"))
return;
if(theZoomLevel == 1)
theZoomLevel = theMaxZoomLevel;
else
theZoomLevel--;
}
if(!createScreen())
return;
// Update the settings
myConsole->settings().setInt("zoom", theZoomLevel);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::showCursor(bool show)
{
if(show)
SDL_ShowCursor(SDL_ENABLE);
else
SDL_ShowCursor(SDL_DISABLE);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::grabMouse(bool grab)
{
if(grab)
SDL_WM_GrabInput(SDL_GRAB_ON);
else
SDL_WM_GrabInput(SDL_GRAB_OFF);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSDL::fullScreen()
{
return myConsole->settings().getBool("fullscreen");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBufferSDL::maxWindowSizeForScreen()
{
if(!x11Available)
return 4;
#ifdef UNIX
// Otherwise, lock the screen and get the width and height
myWMInfo.info.x11.lock_func();
Display* theX11Display = myWMInfo.info.x11.display;
myWMInfo.info.x11.unlock_func();
int screenWidth = DisplayWidth(theX11Display, DefaultScreen(theX11Display));
int screenHeight = DisplayHeight(theX11Display, DefaultScreen(theX11Display));
uInt32 multiplier = screenWidth / myWidth;
bool found = false;
while(!found && (multiplier > 0))
{
// Figure out the desired size of the window
int width = (int) (myWidth * multiplier * theAspectRatio);
int height = myHeight * multiplier;
if((width < screenWidth) && (height < screenHeight))
found = true;
else
multiplier--;
}
if(found)
return multiplier;
else
return 1;
#endif
return 4;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSDL::setWindowAttributes()
{
// Set the window title
ostringstream name;
name << "Stella: \"" << myConsole->properties().get("Cartridge.Name") << "\"";
SDL_WM_SetCaption(name.str().c_str(), "stella");
#ifndef MAC_OSX
// Set the window icon
uInt32 w, h, ncols, nbytes;
uInt32 rgba[256], icon[32 * 32];
uInt8 mask[32][4];
sscanf(stella_icon[0], "%d %d %d %d", &w, &h, &ncols, &nbytes);
if((w != 32) || (h != 32) || (ncols > 255) || (nbytes > 1))
{
cerr << "ERROR: Couldn't load the icon.\n";
return;
}
for(uInt32 i = 0; i < ncols; i++)
{
unsigned char code;
char color[32];
uInt32 col;
sscanf(stella_icon[1 + i], "%c c %s", &code, color);
if(!strcmp(color, "None"))
col = 0x00000000;
else if(!strcmp(color, "black"))
col = 0xFF000000;
else if (color[0] == '#')
{
sscanf(color + 1, "%06x", &col);
col |= 0xFF000000;
}
else
{
cerr << "ERROR: Couldn't load the icon.\n";
return;
}
rgba[code] = col;
}
memset(mask, 0, sizeof(mask));
for(h = 0; h < 32; h++)
{
const char* line = stella_icon[1 + ncols + h];
for(w = 0; w < 32; w++)
{
icon[w + 32 * h] = rgba[(int)line[w]];
if(rgba[(int)line[w]] & 0xFF000000)
mask[h][w >> 3] |= 1 << (7 - (w & 0x07));
}
}
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(icon, 32, 32, 32,
32 * 4, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF000000);
SDL_WM_SetIcon(surface, (unsigned char *) mask);
SDL_FreeSurface(surface);
#endif
}

View File

@ -0,0 +1,163 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSDL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_SDL_HXX
#define FRAMEBUFFER_SDL_HXX
#include <SDL.h>
#include <SDL_syswm.h>
#include "FrameBuffer.hxx"
#include "Settings.hxx"
#include "bspf.hxx"
/**
This class is a base class for the SDL framebuffer and is derived from
the core FrameBuffer class.
It defines all common code shared between the software
and OpenGL video modes, as well as required methods defined in
the core FrameBuffer.
@author Stephen Anthony
@version $Id: FrameBufferSDL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
*/
class FrameBufferSDL : public FrameBuffer
{
public:
/**
Creates a new SDL framebuffer
*/
FrameBufferSDL();
/**
Destructor
*/
virtual ~FrameBufferSDL();
/**
Toggles between fullscreen and window mode. Grabmouse and hidecursor
activated when in fullscreen mode.
*/
void toggleFullscreen();
/**
This routine is called when the user wants to resize the window.
A '1' argument indicates that the window should increase in size, while '-1'
indicates that the windows should decrease in size. A '0' indicates that
the window should be sized according to the current properties.
Can't resize in fullscreen mode. Will only resize up to the maximum size
of the screen.
*/
void resize(int mode);
/**
Shows or hides the cursor based on the given boolean value.
*/
void showCursor(bool show);
/**
Grabs or ungrabs the mouse based on the given boolean value.
*/
void grabMouse(bool grab);
/**
Answers if the display is currently in fullscreen mode.
*/
bool fullScreen();
/**
Answers the current zoom level of the SDL
*/
uInt32 zoomLevel() { return theZoomLevel; }
/**
Calculate the maximum window size that the current screen can hold.
Only works in X11 for now. If not running under X11, always return 4.
*/
uInt32 maxWindowSizeForScreen();
/**
Set the title and icon for the main SDL window.
*/
void setWindowAttributes();
/**
Set up the palette for a screen of any depth > 8.
*/
void setupPalette();
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine is called when the emulation has been paused.
@param status Toggle pause based on status
*/
void pauseEvent(bool status);
//////////////////////////////////////////////////////////////////////
// The following methods must be defined in child classes
//////////////////////////////////////////////////////////////////////
/**
This routine is called whenever the screen needs to be recreated.
It updates the global screen variable.
*/
virtual bool createScreen() = 0;
/**
This routine is called to map a given r,g,b triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b) = 0;
protected:
// The SDL video buffer
SDL_Surface* myScreen;
// SDL initialization flags
uInt32 mySDLFlags;
// SDL palette
Uint32 myPalette[256];
// Used to get window-manager specifics
SDL_SysWMinfo myWMInfo;
// Indicates if we are running under X11
bool x11Available;
// Indicates the current zoom level of the SDL screen
uInt32 theZoomLevel;
// Indicates the maximum zoom of the SDL screen
uInt32 theMaxZoomLevel;
// The aspect ratio of the window
float theAspectRatio;
// Indicates whether the emulation has paused
bool myPauseStatus;
};
#endif

View File

@ -0,0 +1,440 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#include <SDL.h>
#include <SDL_syswm.h>
#include <sstream>
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "FrameBufferSoft.hxx"
#include "MediaSrc.hxx"
#include "Settings.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::FrameBufferSoft()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBufferSoft::~FrameBufferSoft()
{
if(myRectList)
delete myRectList;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSoft::createScreen()
{
int w = myWidth * theZoomLevel;
int h = myHeight * theZoomLevel;
myScreen = SDL_SetVideoMode(w, h, 0, mySDLFlags);
if(myScreen == NULL)
{
cerr << "ERROR: Unable to open SDL window: " << SDL_GetError() << endl;
return false;
}
theRedrawEntireFrameIndicator = true;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSoft::init()
{
// Get the desired width and height of the display
myWidth = myMediaSource->width() << 1;
myHeight = myMediaSource->height();
// Now create the software SDL screen
Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_TIMER;
if(SDL_Init(initflags) < 0)
return false;
// Check which system we are running under
x11Available = false;
#if UNIX && (!__APPLE__)
SDL_VERSION(&myWMInfo.version);
if(SDL_GetWMInfo(&myWMInfo) > 0)
if(myWMInfo.subsystem == SDL_SYSWM_X11)
x11Available = true;
#endif
// Get the maximum size of a window for THIS screen
theMaxZoomLevel = maxWindowSizeForScreen();
// Check to see if window size will fit in the screen
if((uInt32)myConsole->settings().getInt("zoom") > theMaxZoomLevel)
theZoomLevel = theMaxZoomLevel;
else
theZoomLevel = myConsole->settings().getInt("zoom");
mySDLFlags = SDL_SWSURFACE;
mySDLFlags |= myConsole->settings().getBool("fullscreen") ? SDL_FULLSCREEN : 0;
// Set up the rectangle list to be used in the dirty update
myRectList = new RectList();
if(!myRectList)
{
cerr << "ERROR: Unable to get memory for SDL rects" << endl;
return false;
}
// Set the window title and icon
setWindowAttributes();
// Create the screen
if(!createScreen())
return false;
setupPalette();
// Make sure that theUseFullScreenFlag sets up fullscreen mode correctly
if(myConsole->settings().getBool("fullscreen"))
{
grabMouse(true);
showCursor(false);
}
else
{
// Keep mouse in game window if grabmouse is selected
grabMouse(myConsole->settings().getBool("grabmouse"));
// Show or hide the cursor depending on the 'hidecursor' argument
showCursor(!myConsole->settings().getBool("hidecursor"));
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawMediaSource()
{
uInt8* currentFrame = myMediaSource->currentFrameBuffer();
uInt8* previousFrame = myMediaSource->previousFrameBuffer();
uInt16 screenMultiple = (uInt16) theZoomLevel;
uInt32 width = myMediaSource->width();
uInt32 height = myMediaSource->height();
struct Rectangle
{
uInt8 color;
uInt16 x, y, width, height;
} rectangles[2][160];
// This array represents the rectangles that need displaying
// on the current scanline we're processing
Rectangle* currentRectangles = rectangles[0];
// This array represents the rectangles that are still active
// from the previous scanlines we have processed
Rectangle* activeRectangles = rectangles[1];
// Indicates the number of active rectangles
uInt16 activeCount = 0;
// This update procedure requires theWidth to be a multiple of four.
// This is validated when the properties are loaded.
for(uInt16 y = 0; y < height; ++y)
{
// Indicates the number of current rectangles
uInt16 currentCount = 0;
// Look at four pixels at a time to see if anything has changed
uInt32* current = (uInt32*)(currentFrame);
uInt32* previous = (uInt32*)(previousFrame);
for(uInt16 x = 0; x < width; x += 4, ++current, ++previous)
{
// Has something changed in this set of four pixels?
if((*current != *previous) || theRedrawEntireFrameIndicator)
{
uInt8* c = (uInt8*)current;
uInt8* p = (uInt8*)previous;
// Look at each of the bytes that make up the uInt32
for(uInt16 i = 0; i < 4; ++i, ++c, ++p)
{
// See if this pixel has changed
if((*c != *p) || theRedrawEntireFrameIndicator)
{
// Can we extend a rectangle or do we have to create a new one?
if((currentCount != 0) &&
(currentRectangles[currentCount - 1].color == *c) &&
((currentRectangles[currentCount - 1].x +
currentRectangles[currentCount - 1].width) == (x + i)))
{
currentRectangles[currentCount - 1].width += 1;
}
else
{
currentRectangles[currentCount].x = x + i;
currentRectangles[currentCount].y = y;
currentRectangles[currentCount].width = 1;
currentRectangles[currentCount].height = 1;
currentRectangles[currentCount].color = *c;
currentCount++;
}
}
}
}
}
// Merge the active and current rectangles flushing any that are of no use
uInt16 activeIndex = 0;
for(uInt16 t = 0; (t < currentCount) && (activeIndex < activeCount); ++t)
{
Rectangle& current = currentRectangles[t];
Rectangle& active = activeRectangles[activeIndex];
// Can we merge the current rectangle with an active one?
if((current.x == active.x) && (current.width == active.width) &&
(current.color == active.color))
{
current.y = active.y;
current.height = active.height + 1;
++activeIndex;
}
// Is it impossible for this active rectangle to be merged?
else if(current.x >= active.x)
{
// Flush the active rectangle
SDL_Rect temp;
temp.x = active.x * screenMultiple << 1;
temp.y = active.y * screenMultiple;
temp.w = active.width * screenMultiple << 1;
temp.h = active.height * screenMultiple;
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myPalette[active.color]);
++activeIndex;
}
}
// Flush any remaining active rectangles
for(uInt16 s = activeIndex; s < activeCount; ++s)
{
Rectangle& active = activeRectangles[s];
SDL_Rect temp;
temp.x = active.x * screenMultiple << 1;
temp.y = active.y * screenMultiple;
temp.w = active.width * screenMultiple << 1;
temp.h = active.height * screenMultiple;
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myPalette[active.color]);
}
// We can now make the current rectangles into the active rectangles
Rectangle* tmp = currentRectangles;
currentRectangles = activeRectangles;
activeRectangles = tmp;
activeCount = currentCount;
currentFrame += width;
previousFrame += width;
}
// Flush any rectangles that are still active
for(uInt16 t = 0; t < activeCount; ++t)
{
Rectangle& active = activeRectangles[t];
SDL_Rect temp;
temp.x = active.x * screenMultiple << 1;
temp.y = active.y * screenMultiple;
temp.w = active.width * screenMultiple << 1;
temp.h = active.height * screenMultiple;
myRectList->add(&temp);
SDL_FillRect(myScreen, &temp, myPalette[active.color]);
}
// The frame doesn't need to be completely redrawn anymore
theRedrawEntireFrameIndicator = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::preFrameUpdate()
{
// Start a new rectlist on each display update
myRectList->start();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::postFrameUpdate()
{
// Now update all the rectangles at once
SDL_UpdateRects(myScreen, myRectList->numRects(), myRectList->rects());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
{
SDL_Rect tmp;
// Scale all values to the current window size
x *= theZoomLevel;
y *= theZoomLevel;
w *= theZoomLevel;
h *= theZoomLevel;
// First draw the underlying box
tmp.x = x;
tmp.y = y;
tmp.w = w;
tmp.h = h;
myRectList->add(&tmp);
SDL_FillRect(myScreen, &tmp, myPalette[myBGColor]);
// Now draw the bounding sides
tmp.x = x;
tmp.y = y;
tmp.w = w;
tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // top
tmp.x = x;
tmp.y = y + h - theZoomLevel;
tmp.w = w;
tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // bottom
tmp.x = x;
tmp.y = y;
tmp.w = theZoomLevel;
tmp.h = h;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // left
tmp.x = x + w - theZoomLevel;
tmp.y = y;
tmp.w = theZoomLevel;
tmp.h = h;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]); // right
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawText(uInt32 xorig, uInt32 yorig, const string& message)
{
SDL_Rect tmp;
uInt8 length = message.length();
for(uInt32 x = 0; x < length; x++)
{
for(uInt32 y = 0; y < 8; y++)
{
for(uInt32 z = 0; z < 8; z++)
{
char letter = message[x];
if((ourFontData[(letter << 3) + y] >> z) & 1)
{
tmp.x = ((x<<3) + z + xorig) * theZoomLevel;
tmp.y = (y + yorig) * theZoomLevel;
tmp.w = tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]);
}
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBufferSoft::drawChar(uInt32 xorig, uInt32 yorig, uInt32 c)
{
if(c >= 256 )
return;
SDL_Rect tmp;
for(uInt32 y = 0; y < 8; y++)
{
for(uInt32 z = 0; z < 8; z++)
{
if((ourFontData[(c << 3) + y] >> z) & 1)
{
tmp.x = (z + xorig) * theZoomLevel;
tmp.y = (y + yorig) * theZoomLevel;
tmp.w = tmp.h = theZoomLevel;
SDL_FillRect(myScreen, &tmp, myPalette[myFGColor]);
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RectList::RectList(Uint32 size)
{
currentSize = size;
currentRect = 0;
rectArray = new SDL_Rect[currentSize];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RectList::~RectList()
{
delete[] rectArray;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::add(SDL_Rect* newRect)
{
if(currentRect >= currentSize)
{
currentSize = currentSize * 2;
SDL_Rect *temp = new SDL_Rect[currentSize];
for(Uint32 i = 0; i < currentRect; ++i)
temp[i] = rectArray[i];
delete[] rectArray;
rectArray = temp;
}
rectArray[currentRect].x = newRect->x;
rectArray[currentRect].y = newRect->y;
rectArray[currentRect].w = newRect->w;
rectArray[currentRect].h = newRect->h;
++currentRect;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SDL_Rect* RectList::rects()
{
return rectArray;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Uint32 RectList::numRects()
{
return currentRect;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RectList::start()
{
currentRect = 0;
}

View File

@ -0,0 +1,153 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: FrameBufferSoft.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#ifndef FRAMEBUFFER_SOFT_HXX
#define FRAMEBUFFER_SOFT_HXX
#include <SDL.h>
#include <SDL_syswm.h>
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "bspf.hxx"
class Console;
class MediaSource;
class RectList;
/**
This class implements an SDL software framebuffer.
@author Stephen Anthony
@version $Id: FrameBufferSoft.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
*/
class FrameBufferSoft : public FrameBufferSDL
{
public:
/**
Creates a new SDL software framebuffer
*/
FrameBufferSoft();
/**
Destructor
*/
virtual ~FrameBufferSoft();
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBufferSDL.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine is called whenever the screen needs to be recreated.
It updates the global screen variable.
*/
virtual bool createScreen();
/**
This routine is called to map a given r,g,b triple to the screen palette.
@param r The red component of the color.
@param g The green component of the color.
@param b The blue component of the color.
*/
virtual Uint32 mapRGB(Uint8 r, Uint8 g, Uint8 b)
{ return SDL_MapRGB(myScreen->format, r, g, b); }
//////////////////////////////////////////////////////////////////////
// The following methods are derived from FrameBuffer.hxx
//////////////////////////////////////////////////////////////////////
/**
This routine should be called once the console is created to setup
the video system for us to use. Return false if any operation fails,
otherwise return true.
*/
virtual bool init();
/**
This routine should be called anytime the MediaSource needs to be redrawn
to the screen.
*/
virtual void drawMediaSource();
/**
This routine should be called to draw a rectangular box with sides
at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param w The width of the box
@param h The height of the box
*/
virtual void drawBoundedBox(uInt32 x, uInt32 y, uInt32 w, uInt32 h);
/**
This routine should be called to draw text at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param message The message text
*/
virtual void drawText(uInt32 x, uInt32 y, const string& message);
/**
This routine should be called to draw character 'c' at the specified coordinates.
@param x The x coordinate
@param y The y coordinate
@param c The character to draw
*/
virtual void drawChar(uInt32 x, uInt32 y, uInt32 c);
/**
This routine is called before any drawing is done (per-frame).
*/
virtual void preFrameUpdate();
/**
This routine is called after any drawing is done (per-frame).
*/
virtual void postFrameUpdate();
private:
// Used in the dirty update of the SDL surface
RectList* myRectList;
};
/**
*/
class RectList
{
public:
RectList(Uint32 size = 512);
~RectList();
void add(SDL_Rect* rect);
SDL_Rect* rects();
Uint32 numRects();
void start();
private:
Uint32 currentSize, currentRect;
SDL_Rect* rectArray;
};
#endif

View File

@ -0,0 +1,188 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Snapshot.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#include <png.h>
#include <iostream>
#include <fstream>
#include "bspf.hxx"
#include "FrameBuffer.hxx"
#include "Console.hxx"
#include "MediaSrc.hxx"
#include "Snapshot.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Snapshot::Snapshot(Console* console, MediaSource* mediasrc)
: myConsole(console),
myMediaSource(mediasrc),
palette((png_colorp) NULL)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Snapshot::~Snapshot()
{
if(palette)
delete[] palette;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_write_data(png_structp ctx, png_bytep area, png_size_t size)
{
ofstream* out = (ofstream *) png_get_io_ptr(ctx);
out->write((const char *)area, size);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_io_flush(png_structp ctx)
{
ofstream* out = (ofstream *) png_get_io_ptr(ctx);
out->flush();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_user_warn(png_structp ctx, png_const_charp str)
{
cerr << "Snapshot: libpng warning: " << str << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Snapshot::png_user_error(png_structp ctx, png_const_charp str)
{
cerr << "Snapshot: libpng error: " << str << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Snapshot::savePNG(string filename, uInt32 multiplier)
{
// FIXME - we shouldn't use the mediasource, but should instead use
// the framebuffer.
// Right now, the snapshot doesn't take into account any special effects
// that the framebuffer may be performing (aspect correction, OpenGL, etc)
// This will require framebuffer support for exporting its actual pixels,
// so it will have to wait ...
png_structp png_ptr = 0;
png_infop info_ptr = 0;
uInt8* pixels = myMediaSource->currentFrameBuffer();
// PNG and window dimensions will be different because of scaling
uInt32 picWidth = myMediaSource->width() * multiplier << 1;
uInt32 picHeight = myMediaSource->height() * multiplier;
uInt32 width = myMediaSource->width();
uInt32 height = myMediaSource->height();
ofstream* out = new ofstream(filename.c_str(), ios_base::binary);
if(!out)
return 0;
// Create the palette if it hasn't previously been set
if(!palette)
{
palette = (png_colorp) new png_color[256];
if(!palette)
{
cerr << "Snapshot: Couldn't allocate memory for PNG palette\n";
out->close();
return 0;
}
const uInt32* gamePalette = myMediaSource->palette();
for(uInt32 i = 0; i < 256; ++i)
{
palette[i].red = (uInt8) ((gamePalette[i] & 0x00ff0000) >> 16);
palette[i].green = (uInt8) ((gamePalette[i] & 0x0000ff00) >> 8);
palette[i].blue = (uInt8) (gamePalette[i] & 0x000000ff);
}
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_user_error, png_user_warn);
if(png_ptr == NULL)
{
cerr << "Snapshot: Couldn't allocate memory for PNG file\n";
return 0;
}
// Allocate/initialize the image information data. REQUIRED.
info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL)
{
cerr << "Snapshot: Couldn't create image information for PNG file\n";
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
out->close();
return 0;
}
png_set_write_fn(png_ptr, out, png_write_data, png_io_flush);
png_set_IHDR(png_ptr, info_ptr, picWidth, picHeight, 8,
PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Set the 8-bit palette
png_set_PLTE(png_ptr, info_ptr, palette, 256);
// Write the file header information. REQUIRED
png_write_info(png_ptr, info_ptr);
// Pack pixels into bytes
png_set_packing(png_ptr);
// The width has to be scaled by 2 * multiplier. Each pixel must be
// present scaleX times. Each scanline must be present scaleY times.
uInt32 scaleX = multiplier << 1;
uInt32 scaleY = multiplier;
// Create a buffer to hold the new scanline.
uInt8* newScanline = new uInt8[width * scaleX];
uInt8* oldScanline;
// Look at each original scanline
for(uInt32 y = 0; y < height; y++)
{
// First construct a new scanline that is scaled
oldScanline = (uInt8*) pixels + y*width;
uInt32 px = 0;
for(uInt32 x = 0; x < width; x++)
for(uInt32 offx = 0; offx < scaleX; offx++)
newScanline[px++] = oldScanline[x];
// Now output the new scanline 'scaleY' times
for(uInt32 offy = 0; offy < scaleY; offy++)
{
png_bytep row_pointer = (uInt8*) newScanline;
png_write_row(png_ptr, row_pointer);
}
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
if(newScanline)
delete[] newScanline;
out->close();
delete out;
return 1;
}

View File

@ -0,0 +1,75 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Snapshot.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#ifndef SNAPSHOT_HXX
#define SNAPSHOT_HXX
class Console;
class MediaSource;
#include <png.h>
#include "bspf.hxx"
class Snapshot
{
public:
/**
Create a new shapshot class for taking snapshots in PNG format.
@param console The console
@param mediasrc The mediasource
*/
Snapshot(Console* console, MediaSource* mediasrc);
/**
The destructor.
*/
~Snapshot();
/**
This routine saves the current frame buffer to a PNG file,
appropriately scaled by the amount specified in 'multiplier'.
@param filename The filename of the PNG file
@param multiplier The amount that multiplication (zoom level)
@return The resulting error code
*/
uInt32 savePNG(string filename, uInt32 multiplier = 1);
private:
static void png_write_data(png_structp ctx, png_bytep area, png_size_t size);
static void png_io_flush(png_structp ctx);
static void png_user_warn(png_structp ctx, png_const_charp str);
static void png_user_error(png_structp ctx, png_const_charp str);
private:
// The Console for the system
Console* myConsole;
// The Mediasource for the system
MediaSource* myMediaSource;
// The PNG palette
png_colorp palette;
};
#endif

View File

@ -0,0 +1,334 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2004 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundSDL.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#include <SDL.h>
#include "TIASound.h"
#include "Serializer.hxx"
#include "Deserializer.hxx"
#include "System.hxx"
#include "SoundSDL.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
: myCurrentVolume(SDL_MIX_MAXVOLUME),
myFragmentSize(fragsize),
myIsInitializedFlag(false),
myIsMuted(false),
mySampleRate(31400),
mySampleQueue(queuesize)
{
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
{
cerr << "WARNING: Couldn't initialize SDL audio system! " << endl;
cerr << " " << SDL_GetError() << endl;
return;
}
else
{
SDL_AudioSpec desired;
desired.freq = mySampleRate;
desired.format = AUDIO_U8;
desired.channels = 1;
desired.samples = myFragmentSize;
desired.callback = callback;
desired.userdata = (void*)this;
if(SDL_OpenAudio(&desired, &myHardwareSpec) < 0)
{
cerr << "WARNING: Couldn't open SDL audio system! " << endl;
cerr << " " << SDL_GetError() << endl;
return;
}
// Make sure the sample buffer isn't to big (if it is the sound code
// will not work so we'll need to disable the audio support)
if(((float)myHardwareSpec.samples / (float)myHardwareSpec.freq) >= 0.25)
{
cerr << "WARNING: Audio device doesn't support real time audio! Make ";
cerr << "sure a sound" << endl;
cerr << " server isn't running. Audio is disabled..." << endl;
SDL_CloseAudio();
return;
}
myIsInitializedFlag = true;
myIsMuted = false;
mySampleRate = myHardwareSpec.freq;
myFragmentSize = myHardwareSpec.samples;
cerr << "Freq: " << (int)myHardwareSpec.freq << endl;
cerr << "Format: " << (int)myHardwareSpec.format << endl;
cerr << "Channels: " << (int)myHardwareSpec.channels << endl;
cerr << "Silence: " << (int)myHardwareSpec.silence << endl;
cerr << "Samples: " << (int)myHardwareSpec.samples << endl;
cerr << "Size: " << (int)myHardwareSpec.size << endl;
// Now initialize the TIASound object which will actually generate sound
Tia_sound_init(31400, mySampleRate);
// And start the SDL sound subsystem ...
SDL_PauseAudio(0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::~SoundSDL()
{
if(myIsInitializedFlag)
{
SDL_CloseAudio();
}
myIsInitializedFlag = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::isSuccessfullyInitialized() const
{
return myIsInitializedFlag;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::mute(bool state)
{
if(myIsInitializedFlag)
{
// Ignore multiple calls to do the same thing
if(myIsMuted == state)
{
return;
}
myIsMuted = state;
SDL_PauseAudio(myIsMuted ? 1 : 0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setVolume(Int32 percent)
{
// TODO: Verify that this works...
if(myIsInitializedFlag)
{
if((percent >= 0) && (percent <= 100))
{
SDL_LockAudio();
myCurrentVolume = (uInt32)(((float)percent / 100.0) * SDL_MIX_MAXVOLUME);
SDL_UnlockAudio();
}
else if(percent == -1) // If -1 has been specified, play sound at default volume
{
myCurrentVolume = SDL_MIX_MAXVOLUME;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::update()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::set(uInt16 addr, uInt8 value)
{
Update_tia_sound(addr, value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::save(Serializer& out)
{
string device = "TIASound";
try
{
out.putString(device);
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
// Only get the TIA sound registers if sound is enabled
if(myIsInitializedFlag)
Tia_get_registers(&reg1, &reg2, &reg3, &reg4, &reg5, &reg6);
out.putLong(reg1);
out.putLong(reg2);
out.putLong(reg3);
out.putLong(reg4);
out.putLong(reg5);
out.putLong(reg6);
out.putLong(myLastSoundUpdateCycle);
}
catch(char *msg)
{
cerr << msg << endl;
return false;
}
catch(...)
{
cerr << "Unknown error in save state for " << device << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::load(Deserializer& in)
{
string device = "TIASound";
try
{
if(in.getString() != device)
return false;
uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
reg1 = (uInt8) in.getLong();
reg2 = (uInt8) in.getLong();
reg3 = (uInt8) in.getLong();
reg4 = (uInt8) in.getLong();
reg5 = (uInt8) in.getLong();
reg6 = (uInt8) in.getLong();
myLastSoundUpdateCycle = (Int32) in.getLong();
// Only update the TIA sound registers if sound is enabled
if(myIsInitializedFlag)
Tia_set_registers(reg1, reg2, reg3, reg4, reg5, reg6);
}
catch(char *msg)
{
cerr << msg << endl;
return false;
}
catch(...)
{
cerr << "Unknown error in load state for " << device << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::callback(void* udata, uInt8* stream, int len)
{
SoundSDL* sound = (SoundSDL*)udata;
if(sound->isSuccessfullyInitialized())
{
Tia_process(stream, len);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SampleQueue::SampleQueue(uInt32 capacity)
: myCapacity(capacity),
myBuffer(0),
mySize(0),
myHead(0),
myTail(0)
{
myBuffer = new uInt8[myCapacity];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SampleQueue::~SampleQueue()
{
delete[] myBuffer;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::SampleQueue::clear()
{
myHead = myTail = mySize = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::SampleQueue::dequeue(uInt8* buffer, uInt32 size)
{
// We can only dequeue up to the number of items in the queue
if(size > mySize)
{
size = mySize;
}
if((myHead + size) < myCapacity)
{
memcpy((void*)buffer, (const void*)(myBuffer + myHead), size);
myHead += size;
}
else
{
uInt32 s1 = myCapacity - myHead;
uInt32 s2 = size - s1;
memcpy((void*)buffer, (const void*)(myBuffer + myHead), s1);
memcpy((void*)(buffer + s1), (const void*)myBuffer, s2);
myHead = (myHead + size) % myCapacity;
}
mySize -= size;
return size;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
{
// If an attempt is made to enqueue more than the queue can hold then
// we'll only enqueue the last myCapacity elements.
if(size > myCapacity)
{
buffer += (size - myCapacity);
size = myCapacity;
}
if((myTail + size) < myCapacity)
{
memcpy((void*)(myBuffer + myTail), (const void*)buffer, size);
myTail += size;
}
else
{
uInt32 s1 = myCapacity - myTail;
uInt32 s2 = size - s1;
memcpy((void*)(myBuffer + myTail), (const void*)buffer, s1);
memcpy((void*)myBuffer, (const void*)(buffer + s1), s2);
myTail = (myTail + size) % myCapacity;
}
if((mySize + size) > myCapacity)
{
myHead = (myHead + ((mySize + size) - myCapacity)) % myCapacity;
mySize = myCapacity;
}
else
{
mySize += size;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::SampleQueue::size() const
{
return mySize;
}

View File

@ -13,40 +13,42 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SndDOS.hxx,v 1.2 2002-11-13 03:47:55 bwmott Exp $
// $Id: SoundSDL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#ifndef SOUNDDOS_HXX
#define SOUNDDOS_HXX
#ifndef SOUNDSDL_HXX
#define SOUNDSDL_HXX
#include <SDL.h>
#include "Sound.hxx"
#include "bspf.hxx"
#include "MediaSrc.hxx"
/**
This class implements aa sound class for the DOS front-end. It supports
SoundBlaster compatible sound cards.
This class implements the sound API for SDL.
@author Bradford W. Mott
@version $Id: SndDOS.hxx,v 1.2 2002-11-13 03:47:55 bwmott Exp $
@author Stephen Anthony and Bradford W. Mott
@version $Id: SoundSDL.hxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
*/
class SoundDOS
class SoundSDL : public Sound
{
public:
/**
Create a new sound object
*/
SoundDOS(bool activate = true);
SoundSDL(uInt32 fragsize, uInt32 queuesize);
/**
Destructor
*/
virtual ~SoundDOS();
virtual ~SoundSDL();
public:
/**
Closes the sound device
*/
void close();
void closeDevice();
/**
Return the playback sample rate for the sound device.
@ -71,19 +73,41 @@ class SoundDOS
/**
Sets the volume of the sound device to the specified level. The
volume is given as a precentage from 0 to 100.
volume is given as a percentage from 0 to 100. A -1 indicates
that the volume shouldn't be changed at all.
@param volume The new volume for the sound device
@param percent The new volume percentage level for the sound device
*/
void setSoundVolume(uInt32 volume);
void setVolume(Int32 percent);
/**
Update the sound device using the audio sample from the specified
media source.
@param mediaSource The media source to get audio samples from.
Generates audio samples to fill the sample queue.
*/
void updateSound(MediaSource& mediaSource);
void update();
/**
Sets the sound register to a given value.
@param addr The register address
@param value The value to save into the register
*/
void set(uInt16 addr, uInt8 value);
/**
Saves the current state of this device to the given Serializer.
@param out The serializer device to save to.
@return The result of the save. True on success, false on failure.
*/
bool save(Serializer& out);
/**
Loads the current state of this device from the given Deserializer.
@param in The deserializer device to load from.
@return The result of the load. True on success, false on failure.
*/
bool load(Deserializer& in);
private:
/**
@ -132,12 +156,19 @@ class SoundDOS
*/
uInt32 size() const;
/**
Answers the maximum number of samples the queue can hold.
@return The maximum number of samples in the queue.
*/
uInt32 capacity() const { return myCapacity; }
private:
const uInt32 myCapacity;
uInt8* myBuffer;
volatile uInt32 mySize;
volatile uInt32 myHead;
volatile uInt32 myTail;
uInt32 mySize;
uInt32 myHead;
uInt32 myTail;
};
private:
@ -147,25 +178,27 @@ class SoundDOS
// SDL fragment size
uInt32 myFragmentSize;
// Audio specification structure
SDL_AudioSpec myHardwareSpec;
// Indicates if the sound device was successfully initialized
bool myIsInitializedFlag;
// Mutex
bool myUpdateLock;
bool myCallbackLock;
// Indicates if the sound is currently muted
bool myIsMuted;
// DSP sample rate
uInt32 mySampleRate;
// The sample queue size (which is auto-adapting)
uInt32 mySampleQueueSize;
// Queue which holds samples from the media source before they are played
SampleQueue mySampleQueue;
private:
// Callback function invoked by the sound library when it needs data
static void callback(void* udata, void* stream, int len);
// Callback function invoked by the SDL Audio library when it needs data
static void callback(void* udata, uInt8* stream, int len);
};
#endif
#endif

View File

@ -0,0 +1,992 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: mainSDL.cxx,v 1.1 2004-05-24 17:18:22 stephena Exp $
//============================================================================
#include <fstream>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdlib>
#ifdef HAVE_GETTIMEOFDAY
#include <time.h>
#include <sys/time.h>
#endif
#include <SDL.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "Event.hxx"
#include "StellaEvent.hxx"
#include "EventHandler.hxx"
#include "FrameBuffer.hxx"
#include "FrameBufferSDL.hxx"
#include "FrameBufferSoft.hxx"
#include "PropsSet.hxx"
#include "Sound.hxx"
#include "SoundSDL.hxx"
#include "Settings.hxx"
#ifdef DISPLAY_OPENGL
#include "FrameBufferGL.hxx"
// Indicates whether to use OpenGL mode
static bool theUseOpenGLFlag;
#endif
#if defined(UNIX)
#include "SettingsUNIX.hxx"
#elif defined(WIN32)
#include "SettingsWin32.hxx"
#else
#error Unsupported platform!
#endif
static void cleanup();
static bool setupJoystick();
static void handleEvents();
static uInt32 getTicks();
static bool setupProperties(PropertiesSet& set);
#ifdef JOYSTICK_SUPPORT
// static uInt32 thePaddleNumber;
// Lookup table for joystick numbers and events
StellaEvent::JoyStick joyList[StellaEvent::LastJSTICK] = {
StellaEvent::JSTICK_0, StellaEvent::JSTICK_1, StellaEvent::JSTICK_2,
StellaEvent::JSTICK_3, StellaEvent::JSTICK_5, StellaEvent::JSTICK_5
};
StellaEvent::JoyCode joyButtonList[StellaEvent::LastJCODE] = {
StellaEvent::JBUTTON_0, StellaEvent::JBUTTON_1, StellaEvent::JBUTTON_2,
StellaEvent::JBUTTON_3, StellaEvent::JBUTTON_4, StellaEvent::JBUTTON_5,
StellaEvent::JBUTTON_6, StellaEvent::JBUTTON_7, StellaEvent::JBUTTON_8,
StellaEvent::JBUTTON_9, StellaEvent::JBUTTON_10, StellaEvent::JBUTTON_11,
StellaEvent::JBUTTON_12, StellaEvent::JBUTTON_13, StellaEvent::JBUTTON_14,
StellaEvent::JBUTTON_15, StellaEvent::JBUTTON_16, StellaEvent::JBUTTON_17,
StellaEvent::JBUTTON_18, StellaEvent::JBUTTON_19
};
enum JoyType { JT_NONE, JT_REGULAR, JT_STELLADAPTOR_1, JT_STELLADAPTOR_2 };
struct Stella_Joystick
{
SDL_Joystick* stick;
JoyType type;
};
static Stella_Joystick theJoysticks[StellaEvent::LastJSTICK];
// Static lookup tables for Stelladaptor axis support
static Event::Type SA_Axis[2][2][3] = {
Event::JoystickZeroLeft, Event::JoystickZeroRight, Event::PaddleZeroResistance,
Event::JoystickZeroUp, Event::JoystickZeroDown, Event::PaddleOneResistance,
Event::JoystickOneLeft, Event::JoystickOneRight, Event::PaddleTwoResistance,
Event::JoystickOneUp, Event::JoystickOneDown, Event::PaddleThreeResistance
};
#endif
// Pointer to the console object or the null pointer
static Console* theConsole = (Console*) NULL;
// Pointer to the display object or the null pointer
static FrameBufferSDL* theDisplay = (FrameBufferSDL*) NULL;
// Pointer to the sound object or the null pointer
static Sound* theSound = (Sound*) NULL;
// Pointer to the settings object or the null pointer
static Settings* theSettings = (Settings*) NULL;
// Indicates if the mouse should be grabbed
static bool theGrabMouseIndicator = false;
// Indicates if the mouse cursor should be hidden
static bool theHideCursorIndicator = false;
// Indicates the current paddle mode
static Int32 thePaddleMode;
// Indicates relative mouse position horizontally
static Int32 mouseX = 0;
// Indicates whether to show information during program execution
static bool theShowInfoFlag;
struct Switches
{
SDLKey scanCode;
StellaEvent::KeyCode keyCode;
};
// Place the most used keys first to speed up access
// Todo - initialize this array in the same order as the SDLK
// keys are defined, so it can be a constant-time LUT
static Switches keyList[] = {
{ SDLK_F1, StellaEvent::KCODE_F1 },
{ SDLK_F2, StellaEvent::KCODE_F2 },
{ SDLK_F3, StellaEvent::KCODE_F3 },
{ SDLK_F4, StellaEvent::KCODE_F4 },
{ SDLK_F5, StellaEvent::KCODE_F5 },
{ SDLK_F6, StellaEvent::KCODE_F6 },
{ SDLK_F7, StellaEvent::KCODE_F7 },
{ SDLK_F8, StellaEvent::KCODE_F8 },
{ SDLK_F9, StellaEvent::KCODE_F9 },
{ SDLK_F10, StellaEvent::KCODE_F10 },
{ SDLK_F11, StellaEvent::KCODE_F11 },
{ SDLK_F12, StellaEvent::KCODE_F12 },
{ SDLK_F13, StellaEvent::KCODE_F13 },
{ SDLK_F14, StellaEvent::KCODE_F14 },
{ SDLK_F15, StellaEvent::KCODE_F15 },
{ SDLK_UP, StellaEvent::KCODE_UP },
{ SDLK_DOWN, StellaEvent::KCODE_DOWN },
{ SDLK_LEFT, StellaEvent::KCODE_LEFT },
{ SDLK_RIGHT, StellaEvent::KCODE_RIGHT },
{ SDLK_SPACE, StellaEvent::KCODE_SPACE },
{ SDLK_LCTRL, StellaEvent::KCODE_LCTRL },
{ SDLK_RCTRL, StellaEvent::KCODE_RCTRL },
{ SDLK_LALT, StellaEvent::KCODE_LALT },
{ SDLK_RALT, StellaEvent::KCODE_RALT },
{ SDLK_LSUPER, StellaEvent::KCODE_LWIN },
{ SDLK_RSUPER, StellaEvent::KCODE_RWIN },
{ SDLK_MENU, StellaEvent::KCODE_MENU },
{ SDLK_a, StellaEvent::KCODE_a },
{ SDLK_b, StellaEvent::KCODE_b },
{ SDLK_c, StellaEvent::KCODE_c },
{ SDLK_d, StellaEvent::KCODE_d },
{ SDLK_e, StellaEvent::KCODE_e },
{ SDLK_f, StellaEvent::KCODE_f },
{ SDLK_g, StellaEvent::KCODE_g },
{ SDLK_h, StellaEvent::KCODE_h },
{ SDLK_i, StellaEvent::KCODE_i },
{ SDLK_j, StellaEvent::KCODE_j },
{ SDLK_k, StellaEvent::KCODE_k },
{ SDLK_l, StellaEvent::KCODE_l },
{ SDLK_m, StellaEvent::KCODE_m },
{ SDLK_n, StellaEvent::KCODE_n },
{ SDLK_o, StellaEvent::KCODE_o },
{ SDLK_p, StellaEvent::KCODE_p },
{ SDLK_q, StellaEvent::KCODE_q },
{ SDLK_r, StellaEvent::KCODE_r },
{ SDLK_s, StellaEvent::KCODE_s },
{ SDLK_t, StellaEvent::KCODE_t },
{ SDLK_u, StellaEvent::KCODE_u },
{ SDLK_v, StellaEvent::KCODE_v },
{ SDLK_w, StellaEvent::KCODE_w },
{ SDLK_x, StellaEvent::KCODE_x },
{ SDLK_y, StellaEvent::KCODE_y },
{ SDLK_z, StellaEvent::KCODE_z },
{ SDLK_0, StellaEvent::KCODE_0 },
{ SDLK_1, StellaEvent::KCODE_1 },
{ SDLK_2, StellaEvent::KCODE_2 },
{ SDLK_3, StellaEvent::KCODE_3 },
{ SDLK_4, StellaEvent::KCODE_4 },
{ SDLK_5, StellaEvent::KCODE_5 },
{ SDLK_6, StellaEvent::KCODE_6 },
{ SDLK_7, StellaEvent::KCODE_7 },
{ SDLK_8, StellaEvent::KCODE_8 },
{ SDLK_9, StellaEvent::KCODE_9 },
{ SDLK_KP0, StellaEvent::KCODE_KP0 },
{ SDLK_KP1, StellaEvent::KCODE_KP1 },
{ SDLK_KP2, StellaEvent::KCODE_KP2 },
{ SDLK_KP3, StellaEvent::KCODE_KP3 },
{ SDLK_KP4, StellaEvent::KCODE_KP4 },
{ SDLK_KP5, StellaEvent::KCODE_KP5 },
{ SDLK_KP6, StellaEvent::KCODE_KP6 },
{ SDLK_KP7, StellaEvent::KCODE_KP7 },
{ SDLK_KP8, StellaEvent::KCODE_KP8 },
{ SDLK_KP9, StellaEvent::KCODE_KP9 },
{ SDLK_KP_PERIOD, StellaEvent::KCODE_KP_PERIOD },
{ SDLK_KP_DIVIDE, StellaEvent::KCODE_KP_DIVIDE },
{ SDLK_KP_MULTIPLY, StellaEvent::KCODE_KP_MULTIPLY},
{ SDLK_KP_MINUS, StellaEvent::KCODE_KP_MINUS },
{ SDLK_KP_PLUS, StellaEvent::KCODE_KP_PLUS },
{ SDLK_KP_ENTER, StellaEvent::KCODE_KP_ENTER },
{ SDLK_KP_EQUALS, StellaEvent::KCODE_KP_EQUALS },
{ SDLK_BACKSPACE, StellaEvent::KCODE_BACKSPACE },
{ SDLK_TAB, StellaEvent::KCODE_TAB },
{ SDLK_CLEAR, StellaEvent::KCODE_CLEAR },
{ SDLK_RETURN, StellaEvent::KCODE_RETURN },
{ SDLK_ESCAPE, StellaEvent::KCODE_ESCAPE },
{ SDLK_COMMA, StellaEvent::KCODE_COMMA },
{ SDLK_MINUS, StellaEvent::KCODE_MINUS },
{ SDLK_PERIOD, StellaEvent::KCODE_PERIOD },
{ SDLK_SLASH, StellaEvent::KCODE_SLASH },
{ SDLK_BACKSLASH, StellaEvent::KCODE_BACKSLASH },
{ SDLK_SEMICOLON, StellaEvent::KCODE_SEMICOLON },
{ SDLK_EQUALS, StellaEvent::KCODE_EQUALS },
{ SDLK_QUOTE, StellaEvent::KCODE_QUOTE },
{ SDLK_BACKQUOTE, StellaEvent::KCODE_BACKQUOTE },
{ SDLK_LEFTBRACKET, StellaEvent::KCODE_LEFTBRACKET},
{ SDLK_RIGHTBRACKET,StellaEvent::KCODE_RIGHTBRACKET},
{ SDLK_PRINT, StellaEvent::KCODE_PRTSCREEN },
{ SDLK_MODE, StellaEvent::KCODE_SCRLOCK },
{ SDLK_PAUSE, StellaEvent::KCODE_PAUSE },
{ SDLK_INSERT, StellaEvent::KCODE_INSERT },
{ SDLK_HOME, StellaEvent::KCODE_HOME },
{ SDLK_PAGEUP, StellaEvent::KCODE_PAGEUP },
{ SDLK_DELETE, StellaEvent::KCODE_DELETE },
{ SDLK_END, StellaEvent::KCODE_END },
{ SDLK_PAGEDOWN, StellaEvent::KCODE_PAGEDOWN }
};
/**
Returns number of ticks in microseconds
*/
#ifdef HAVE_GETTIMEOFDAY
inline uInt32 getTicks()
{
timeval now;
gettimeofday(&now, 0);
return (uInt32) (now.tv_sec * 1000000 + now.tv_usec);
}
#else
inline uInt32 getTicks()
{
return (uInt32) SDL_GetTicks() * 1000;
}
#endif
/**
This routine should be called once setupDisplay is called
to create the joystick stuff.
*/
bool setupJoystick()
{
#ifdef JOYSTICK_SUPPORT
// Keep track of how many Stelladaptors we've found
uInt8 saCount = 0;
// First clear the joystick array
for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++)
{
theJoysticks[i].stick = (SDL_Joystick*) NULL;
theJoysticks[i].type = JT_NONE;
}
// Initialize the joystick subsystem
if((SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) || (SDL_NumJoysticks() <= 0))
{
if(theShowInfoFlag)
cout << "No joysticks present, use the keyboard.\n";
return true;
}
// Try to open 4 regular joysticks and 2 Stelladaptor devices
uInt32 limit = SDL_NumJoysticks() <= StellaEvent::LastJSTICK ?
SDL_NumJoysticks() : StellaEvent::LastJSTICK;
for(uInt32 i = 0; i < limit; i++)
{
string name = SDL_JoystickName(i);
theJoysticks[i].stick = SDL_JoystickOpen(i);
// Skip if we couldn't open it for any reason
if(theJoysticks[i].stick == NULL)
continue;
// Figure out what type of joystick this is
if(name.find("Stelladaptor", 0) != string::npos)
{
saCount++;
if(saCount > 2) // Ignore more than 2 Stelladaptors
{
theJoysticks[i].type = JT_NONE;
continue;
}
else if(saCount == 1)
{
name = "Left Stelladaptor (Left joystick, Paddles 0 and 1, Left driving controller)";
theJoysticks[i].type = JT_STELLADAPTOR_1;
}
else if(saCount == 2)
{
name = "Right Stelladaptor (Right joystick, Paddles 2 and 3, Right driving controller)";
theJoysticks[i].type = JT_STELLADAPTOR_2;
}
if(theShowInfoFlag)
cout << "Joystick " << i << ": " << name << endl;
}
else
{
theJoysticks[i].type = JT_REGULAR;
if(theShowInfoFlag)
{
cout << "Joystick " << i << ": " << SDL_JoystickName(i)
<< " with " << SDL_JoystickNumButtons(theJoysticks[i].stick)
<< " buttons.\n";
}
}
}
#endif
return true;
}
/**
This routine should be called regularly to handle events
*/
void handleEvents()
{
SDL_Event event;
Uint8 type;
SDLKey key;
SDLMod mod;
// Check for an event
while(SDL_PollEvent(&event))
{
// keyboard events
if(event.type == SDL_KEYDOWN)
{
key = event.key.keysym.sym;
mod = event.key.keysym.mod;
type = event.type;
// An attempt to speed up event processing
// All SDL-specific event actions are accessed by either
// Control or Alt keys. So we quickly check for those.
if(mod & KMOD_ALT)
{
if(key == SDLK_EQUALS)
theDisplay->resize(1);
else if(key == SDLK_MINUS)
theDisplay->resize(-1);
else if(key == SDLK_RETURN)
{
theDisplay->toggleFullscreen();
}
#ifdef DISPLAY_OPENGL
else if(key == SDLK_f && theUseOpenGLFlag)
((FrameBufferGL*)theDisplay)->toggleFilter();
#endif
#ifdef DEVELOPER_SUPPORT
else if(key == SDLK_END) // Alt-End increases XStart
{
theConsole->changeXStart(1);
theDisplay->resize(0);
}
else if(key == SDLK_HOME) // Alt-Home decreases XStart
{
theConsole->changeXStart(0);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEUP) // Alt-PageUp increases YStart
{
theConsole->changeYStart(1);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEDOWN) // Alt-PageDown decreases YStart
{
theConsole->changeYStart(0);
theDisplay->resize(0);
}
#endif
}
else if(mod & KMOD_CTRL)
{
if(key == SDLK_g)
{
// don't change grabmouse in fullscreen mode
if(!theDisplay->fullScreen())
{
theGrabMouseIndicator = !theGrabMouseIndicator;
theSettings->setBool("grabmouse", theGrabMouseIndicator);
theDisplay->grabMouse(theGrabMouseIndicator);
}
}
else if(key == SDLK_h)
{
// don't change hidecursor in fullscreen mode
if(!theDisplay->fullScreen())
{
theHideCursorIndicator = !theHideCursorIndicator;
theSettings->setBool("hidecursor", theHideCursorIndicator);
theDisplay->showCursor(!theHideCursorIndicator);
}
}
#ifdef DEVELOPER_SUPPORT
else if(key == SDLK_f) // Ctrl-f toggles NTSC/PAL mode
{
theConsole->toggleFormat();
theDisplay->setupPalette();
}
else if(key == SDLK_p) // Ctrl-p toggles different palettes
{
theConsole->togglePalette();
theDisplay->setupPalette();
}
else if(key == SDLK_END) // Ctrl-End increases Width
{
theConsole->changeWidth(1);
theDisplay->resize(0);
}
else if(key == SDLK_HOME) // Ctrl-Home decreases Width
{
theConsole->changeWidth(0);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEUP) // Ctrl-PageUp increases Height
{
theConsole->changeHeight(1);
theDisplay->resize(0);
}
else if(key == SDLK_PAGEDOWN) // Ctrl-PageDown decreases Height
{
theConsole->changeHeight(0);
theDisplay->resize(0);
}
else if(key == SDLK_s) // Ctrl-s saves properties to a file
{
if(theConsole->settings().getBool("mergeprops")) // Attempt to merge with propertiesSet
{
theConsole->saveProperties(theSettings->userPropertiesFilename(), true);
}
else // Save to file in home directory
{
string newPropertiesFile = theConsole->settings().baseDir() + "/" + \
theConsole->properties().get("Cartridge.Name") + ".pro";
replace(newPropertiesFile.begin(), newPropertiesFile.end(), ' ', '_');
theConsole->saveProperties(newPropertiesFile);
}
}
#endif
}
else // check all the other keys
{
for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i)
{
if(keyList[i].scanCode == key)
theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 1);
}
}
}
else if(event.type == SDL_KEYUP)
{
key = event.key.keysym.sym;
type = event.type;
for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i)
{
if(keyList[i].scanCode == key)
theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 0);
}
}
else if(event.type == SDL_MOUSEMOTION)
{
Int32 resistance;
uInt32 zoom = theDisplay->zoomLevel();
Int32 width = theDisplay->width() * zoom;
Event::Type type = Event::NoType;
// Grabmouse and hidecursor introduce some lag into the mouse movement,
// so we need to fudge the numbers a bit
if(theGrabMouseIndicator && theHideCursorIndicator)
{
mouseX = (int)((float)mouseX + (float)event.motion.xrel
* 1.5 * (float) zoom);
}
else
{
mouseX = mouseX + event.motion.xrel * zoom;
}
// Check to make sure mouseX is within the game window
if(mouseX < 0)
mouseX = 0;
else if(mouseX > width)
mouseX = width;
resistance = (Int32)(1000000.0 * (width - mouseX) / width);
// Now, set the event of the correct paddle to the calculated resistance
if(thePaddleMode == 0)
type = Event::PaddleZeroResistance;
else if(thePaddleMode == 1)
type = Event::PaddleOneResistance;
else if(thePaddleMode == 2)
type = Event::PaddleTwoResistance;
else if(thePaddleMode == 3)
type = Event::PaddleThreeResistance;
theConsole->eventHandler().sendEvent(type, resistance);
}
else if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP)
{
Event::Type type = Event::LastType;
Int32 value;
if(event.type == SDL_MOUSEBUTTONDOWN)
value = 1;
else
value = 0;
if(thePaddleMode == 0)
type = Event::PaddleZeroFire;
else if(thePaddleMode == 1)
type = Event::PaddleOneFire;
else if(thePaddleMode == 2)
type = Event::PaddleTwoFire;
else if(thePaddleMode == 3)
type = Event::PaddleThreeFire;
theConsole->eventHandler().sendEvent(type, value);
}
else if(event.type == SDL_ACTIVEEVENT)
{
if((event.active.state & SDL_APPACTIVE) && (event.active.gain == 0))
{
if(!theConsole->eventHandler().doPause())
{
theConsole->eventHandler().sendEvent(Event::Pause, 1);
}
}
}
else if(event.type == SDL_QUIT)
{
theConsole->eventHandler().sendEvent(Event::Quit, 1);
}
else if(event.type == SDL_VIDEOEXPOSE)
{
theDisplay->refresh();
}
#ifdef JOYSTICK_SUPPORT
// Read joystick events and modify event states
StellaEvent::JoyStick stick;
StellaEvent::JoyCode code;
Int32 state;
Uint8 axis;
Uint8 button;
Int32 resistance;
Sint16 value;
JoyType type;
if(event.jbutton.which >= StellaEvent::LastJSTICK)
return;
stick = joyList[event.jbutton.which];
type = theJoysticks[event.jbutton.which].type;
// Figure put what type of joystick we're dealing with
// Stelladaptors behave differently, and can't be remapped
switch(type)
{
case JT_NONE:
break;
case JT_REGULAR:
if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP))
{
if(event.jbutton.button >= StellaEvent::LastJCODE)
return;
code = joyButtonList[event.jbutton.button];
state = event.jbutton.state == SDL_PRESSED ? 1 : 0;
theConsole->eventHandler().sendJoyEvent(stick, code, state);
}
else if(event.type == SDL_JOYAXISMOTION)
{
axis = event.jaxis.axis;
value = event.jaxis.value;
if(axis == 0) // x-axis
{
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_LEFT,
(value < -16384) ? 1 : 0);
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_RIGHT,
(value > 16384) ? 1 : 0);
}
else if(axis == 1) // y-axis
{
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_UP,
(value < -16384) ? 1 : 0);
theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_DOWN,
(value > 16384) ? 1 : 0);
}
}
break; // Regular joystick
case JT_STELLADAPTOR_1:
case JT_STELLADAPTOR_2:
if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP))
{
button = event.jbutton.button;
state = event.jbutton.state == SDL_PRESSED ? 1 : 0;
// Send button events for the joysticks/paddles
if(button == 0)
{
if(type == JT_STELLADAPTOR_1)
{
theConsole->eventHandler().sendEvent(Event::JoystickZeroFire, state);
theConsole->eventHandler().sendEvent(Event::PaddleZeroFire, state);
}
else
{
theConsole->eventHandler().sendEvent(Event::JoystickOneFire, state);
theConsole->eventHandler().sendEvent(Event::PaddleTwoFire, state);
}
}
else if(button == 1)
{
if(type == JT_STELLADAPTOR_1)
theConsole->eventHandler().sendEvent(Event::PaddleOneFire, state);
else
theConsole->eventHandler().sendEvent(Event::PaddleThreeFire, state);
}
}
else if(event.type == SDL_JOYAXISMOTION)
{
axis = event.jaxis.axis;
value = event.jaxis.value;
// Send axis events for the joysticks
theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][0],
(value < -16384) ? 1 : 0);
theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][1],
(value > 16384) ? 1 : 0);
// Send axis events for the paddles
resistance = (Int32) (1000000.0 * (32767 - value) / 65534);
theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][2], resistance);
}
break; // Stelladaptor joystick
default:
break;
}
#endif
}
}
/**
Setup the properties set by first checking for a user file
"$HOME/.stella/stella.pro", then a system-wide file "/etc/stella.pro".
Return false if neither file is found, else return true.
@param set The properties set to setup
*/
bool setupProperties(PropertiesSet& set)
{
bool useMemList = false;
string theAlternateProFile = theSettings->getString("altpro");
string theUserProFile = theSettings->userPropertiesFilename();
string theSystemProFile = theSettings->systemPropertiesFilename();
#ifdef DEVELOPER_SUPPORT
// If the user wishes to merge any property modifications to the
// PropertiesSet file, then the PropertiesSet file MUST be loaded
// into memory.
useMemList = theSettings->getBool("mergeprops");
#endif
// Check to see if the user has specified an alternate .pro file.
if(theAlternateProFile != "")
{
set.load(theAlternateProFile, &Console::defaultProperties(), useMemList);
return true;
}
if(theUserProFile != "")
{
set.load(theUserProFile, &Console::defaultProperties(), useMemList);
return true;
}
else if(theSystemProFile != "")
{
set.load(theSystemProFile, &Console::defaultProperties(), useMemList);
return true;
}
else
{
set.load("", &Console::defaultProperties(), false);
return true;
}
}
/**
Does general cleanup in case any operation failed (or at end of program).
*/
void cleanup()
{
#ifdef JOYSTICK_SUPPORT
for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++)
{
if(SDL_JoystickOpened(i))
SDL_JoystickClose(theJoysticks[i].stick);
}
#endif
if(theSettings)
delete theSettings;
if(theConsole)
delete theConsole;
if(theSound)
delete theSound;
if(theDisplay)
delete theDisplay;
SDL_Quit();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int main(int argc, char* argv[])
{
#if defined(UNIX)
theSettings = new SettingsUNIX();
#elif defined(WIN32)
theSettings = new SettingsWin32();
#else
#error Unsupported platform!
#endif
if(!theSettings)
{
cleanup();
return 0;
}
theSettings->loadConfig();
// Take care of commandline arguments
if(!theSettings->loadCommandLine(argc, argv))
{
string message = "Stella version 1.4_cvs\n\nUsage: stella [options ...] romfile";
theSettings->usage(message);
cleanup();
return 0;
}
// Cache some settings so they don't have to be repeatedly searched for
thePaddleMode = theSettings->getInt("paddle");
theShowInfoFlag = theSettings->getBool("showinfo");
theGrabMouseIndicator = theSettings->getBool("grabmouse");
theHideCursorIndicator = theSettings->getBool("hidecursor");
// Request that the SDL window be centered, if possible
putenv("SDL_VIDEO_CENTERED=1");
// Get a pointer to the file which contains the cartridge ROM
const char* file = argv[argc - 1];
// Open the cartridge image and read it in
ifstream in(file, ios_base::binary);
if(!in)
{
cerr << "ERROR: Couldn't open " << file << "..." << endl;
cleanup();
return 0;
}
uInt8* image = new uInt8[512 * 1024];
in.read((char*)image, 512 * 1024);
uInt32 size = in.gcount();
in.close();
// Create a properties set for us to use and set it up
PropertiesSet propertiesSet;
if(!setupProperties(propertiesSet))
{
delete[] image;
cleanup();
return 0;
}
// Create an SDL window
string videodriver = theSettings->getString("video");
if(videodriver == "soft")
{
theDisplay = new FrameBufferSoft();
if(theShowInfoFlag)
cout << "Using software mode for video.\n";
}
#ifdef DISPLAY_OPENGL
else if(videodriver == "gl")
{
theDisplay = new FrameBufferGL();
theUseOpenGLFlag = true;
if(theShowInfoFlag)
cout << "Using OpenGL mode for video.\n";
}
#endif
else // a driver that doesn't exist was requested, so use software mode
{
theDisplay = new FrameBufferSoft();
if(theShowInfoFlag)
cout << "Using software mode for video.\n";
}
if(!theDisplay)
{
cerr << "ERROR: Couldn't set up display.\n";
delete[] image;
cleanup();
return 0;
}
// Create a sound object for playing audio
if(theSettings->getBool("sound"))
{
uInt32 fragsize = theSettings->getInt("fragsize");
uInt32 bufsize = theSettings->getInt("bufsize");
theSound = new SoundSDL(fragsize, bufsize);
if(theShowInfoFlag)
{
cout << "Sound enabled, using fragment size = " << fragsize;
cout << " and buffer size = " << bufsize << "." << endl;
}
}
else // even if sound has been disabled, we still need a sound object
{
theSound = new Sound();
if(theShowInfoFlag)
cout << "Sound disabled.\n";
}
theSound->setVolume(theSettings->getInt("volume"));
// Get just the filename of the file containing the ROM image
const char* filename = (!strrchr(file, '/')) ? file : strrchr(file, '/') + 1;
// Create the 2600 game console
theConsole = new Console(image, size, filename, *theSettings, propertiesSet,
*theDisplay, *theSound);
// Free the image since we don't need it any longer
delete[] image;
// Setup the SDL joysticks
// This must be done after the console is created
if(!setupJoystick())
{
cerr << "ERROR: Couldn't set up joysticks.\n";
cleanup();
return 0;
}
// These variables are common to both timing options
// and are needed to calculate the overall frames per second.
uInt32 frameTime = 0, numberOfFrames = 0;
if(theSettings->getBool("accurate")) // normal, CPU-intensive timing
{
// Set up accurate timing stuff
uInt32 startTime, delta;
uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate"));
// Set the base for the timers
frameTime = 0;
// Main game loop
for(;;)
{
// Exit if the user wants to quit
if(theConsole->eventHandler().doQuit())
{
break;
}
startTime = getTicks();
handleEvents();
theConsole->update();
// Now, waste time if we need to so that we are at the desired frame rate
for(;;)
{
delta = getTicks() - startTime;
if(delta >= timePerFrame)
break;
}
frameTime += getTicks() - startTime;
++numberOfFrames;
}
}
else // less accurate, less CPU-intensive timing
{
// Set up less accurate timing stuff
uInt32 startTime, virtualTime, currentTime;
uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate"));
// Set the base for the timers
virtualTime = getTicks();
frameTime = 0;
// Main game loop
for(;;)
{
// Exit if the user wants to quit
if(theConsole->eventHandler().doQuit())
{
break;
}
startTime = getTicks();
handleEvents();
theConsole->update();
currentTime = getTicks();
virtualTime += timePerFrame;
if(currentTime < virtualTime)
{
SDL_Delay((virtualTime - currentTime)/1000);
}
currentTime = getTicks() - startTime;
frameTime += currentTime;
++numberOfFrames;
}
}
if(theShowInfoFlag)
{
double executionTime = (double) frameTime / 1000000.0;
double framesPerSecond = (double) numberOfFrames / executionTime;
cout << endl;
cout << numberOfFrames << " total frames drawn\n";
cout << framesPerSecond << " frames/second\n";
cout << endl;
cout << "Cartridge Name: " << theConsole->properties().get("Cartridge.Name");
cout << endl;
cout << "Cartridge MD5: " << theConsole->properties().get("Cartridge.MD5");
cout << endl << endl;
}
// Cleanup time ...
cleanup();
return 0;
}

View File

@ -0,0 +1,38 @@
/* XPM */
static char * stella_icon[] = {
"32 32 3 1",
" c None",
". c #000000",
"+ c #FFFFFF",
" ",
" ",
" ",
" ",
" .............. ",
" .............. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ..++..++..++.. ",
" ....++..++..++.... ",
" ....++..++..++.... ",
" ....++++..++..++++.... ",
" ....++++..++..++++.... ",
" ..++++....++....++++.. ",
" ..++++....++....++++.. ",
" ......++......++......++...... ",
" ......++......++......++...... ",
" ..++++++.. ..++.. ..++++++.. ",
" ..++++++.. ..++.. ..++++++.. ",
" ..++++.... ..++.. ....++++.. ",
" ..++++.... ..++.. ....++++.. ",
" ........ ...... ........ ",
" ........ ...... ........ ",
" ",
" ",
" ",
" "};

View File

@ -1,208 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: PCJoys.cxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
//============================================================================
#include <dos.h>
#include <time.h>
#include "PCJoys.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PCJoysticks::PCJoysticks(bool useAxisMidpoint)
: myGamePort(0x0201),
myUseAxisMidpoint(useAxisMidpoint)
{
for(uInt32 i = 0; i < 4; ++i)
{
myMinimum[i] = 0x0FFFFFFF;
myMaximum[i] = 0;
myMidpoint[i] = 0;
}
// Determine which joysticks are present
myPresent = detect();
// If we're using midpoints then calibrate them now
if(myUseAxisMidpoint)
{
calibrate();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PCJoysticks::~PCJoysticks()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PCJoysticks::present() const
{
return myPresent;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PCJoysticks::read(bool button[4], Int16 axis[4])
{
// Get the state of each of the buttons
uInt8 b = (~inportb(myGamePort)) >> 4;
button[0] = b & 0x01 ? true : false;
button[1] = b & 0x02 ? true : false;
button[2] = b & 0x04 ? true : false;
button[3] = b & 0x08 ? true : false;
// Count how long it takes each axis to change states
Int32 count[4] = {0, 0, 0, 0};
disable();
outportb(myGamePort, 0);
for(uInt32 counter = 0; counter < 0x000FFFFF; ++counter)
{
uInt8 i = inportb(myGamePort);
if(i & 0x01)
++count[0];
if(i & 0x02)
++count[1];
if(i & 0x04)
++count[2];
if(i & 0x08)
++count[3];
if(!(i & myPresent))
break;
}
enable();
// Determine the position of each of the axes
for(uInt32 t = 0; t < 4; ++t)
{
// Is this axis being used?
if(myPresent & (1 << t))
{
// Update the maximum for this axis if needed
if(count[t] > myMaximum[t])
myMaximum[t] = count[t];
// Update the minimum for this axis if needed
if(count[t] < myMinimum[t])
myMinimum[t] = count[t];
// If the minimum and maximum aren't far enough apart then assume 0
if(myMaximum[t] - myMinimum[t] < (myMaximum[t] >> 3))
{
axis[t] = 0;
}
else
{
// Are we using midpoints?
if(myUseAxisMidpoint)
{
// Yes, so calculate axis value using midpoint
if(count[t] < myMidpoint[t])
{
double n = myMidpoint[t] - count[t];
double d = myMidpoint[t] - myMinimum[t];
double f = ((d == 0.0) ? 0.0 : (n / d));
if(d < (myMaximum[t] >> 3))
axis[t] = 0;
else
axis[t] = (Int16)((Int32)(-32767L * f));
}
else
{
double n = count[t] - myMidpoint[t];
double d = myMaximum[t] - myMidpoint[t];
double f = ((d == 0.0) ? 0.0 : (n / d));
if(d < (myMaximum[t] >> 3))
axis[t] = 0;
else
axis[t] = (Int16)((Int32)(32767L * f));
}
}
else
{
// No, so calculate axis value without using the midpoint
double n = count[t] - myMinimum[t];
double d = myMaximum[t] - myMinimum[t];
double f = ((d == 0.0) ? 0.5 : (n / d));
axis[t] = (Int16)((Int32)(65534L * f) - 32767);
}
}
}
else
{
axis[t] = 0;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 PCJoysticks::detect() const
{
uInt8 present = 0;
// Reset the game port bits to 1's
outportb(myGamePort, 0);
clock_t start = clock();
// Wait for low on all four joystick bits or for time to expire
do
{
present = inportb(myGamePort) & 0x0F;
}
while(((clock() - start) < (CLOCKS_PER_SEC / 2)) && present);
// Setup joystick present mask that tells which joysticks are present
present = (present ^ 0x0F) & 0x0F;
present = present & (((present & 0x03) == 0x03) ? 0x0F : 0x0C);
present = present & (((present & 0x0C) == 0x0C) ? 0x0F : 0x03);
return present;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PCJoysticks::calibrate()
{
// Make sure a joystick is present before we do a lot of work
if(present())
{
// Count how long it takes each axis to change states
Int32 count[4] = {0, 0, 0, 0};
disable();
outportb(myGamePort, 0);
for(uInt32 counter = 0; counter < 0x000FFFFF; ++counter)
{
uInt8 i = inportb(myGamePort);
if(i & 0x01)
++count[0];
if(i & 0x02)
++count[1];
if(i & 0x04)
++count[2];
if(i & 0x08)
++count[3];
if(!(i & myPresent))
break;
}
enable();
for(uInt32 t = 0; t < 4; ++t)
{
myMidpoint[t] = count[t];
}
}
}

View File

@ -1,71 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: PCJoys.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $
//============================================================================
#ifndef PCJOYSTICKS_HXX
#define PCJOYSTICKS_HXX
#include "bspf.hxx"
class PCJoysticks
{
public:
/**
Constructor
@param useAxisMidpoint Indicates if a "midpoints" should be used
*/
PCJoysticks(bool useAxisMidpoint);
/**
Destructor
*/
~PCJoysticks();
public:
/**
Answers true iff a joystick is connected to the system
@return true iff a joysticks is connected
*/
bool present() const;
/**
Read the state of the joystick and update the button and axis arrays
@param buttons Array which holds the button state upon exit
@param axes Array which holds the axis state upon exit
*/
void read(bool buttons[4], Int16 axes[4]);
private:
// Answers mask that indicates which joysticks are present
uInt8 detect() const;
// Calibrate axes midpoints
void calibrate();
private:
uInt8 myPresent;
const uInt16 myGamePort;
const bool myUseAxisMidpoint;
Int32 myMinimum[4];
Int32 myMaximum[4];
Int32 myMidpoint[4];
};
#endif

View File

@ -1,264 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2003 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SndDOS.cxx,v 1.3 2003-02-17 05:09:21 bwmott Exp $
//============================================================================
#include "SndDOS.hxx"
#include "dos.h"
#include "dos_sb.h"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundDOS::SoundDOS(bool activate)
: myCurrentVolume(100),
myFragmentSize(2048),
myIsInitializedFlag(false),
myUpdateLock(false),
myIsMuted(false),
mySampleRate(31400),
mySampleQueue(mySampleRate)
{
if(activate)
{
int bps = 8;
int stereo = 0;
int buffersize = myFragmentSize;
int playback_freq = mySampleRate;
if(sb_init(&playback_freq, &bps, &buffersize, &stereo) < 0)
{
cerr << "WARNING: Couldn't initialize audio system! " << endl;
myIsInitializedFlag = false;
mySampleRate = 0;
return;
}
mySampleRate = playback_freq;
myFragmentSize = buffersize;
myIsInitializedFlag = true;
myIsMuted = false;
// Start playing audio
sb_startoutput((sbmix_t)callback, (void*)this);
}
else
{
myIsInitializedFlag = false;
myIsMuted = true;
mySampleRate = 0;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundDOS::~SoundDOS()
{
close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundDOS::getSampleRate() const
{
return myIsInitializedFlag ? mySampleRate : 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundDOS::isSuccessfullyInitialized() const
{
return myIsInitializedFlag;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::mute(bool state)
{
if(!myIsInitializedFlag)
{
return;
}
// Ignore multiple calls to do the same thing
if(myIsMuted == state)
{
return;
}
myIsMuted = state;
if(myIsMuted)
{
sb_stopoutput();
}
else
{
sb_startoutput((sbmix_t)callback, (void*)this);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::close()
{
if(myIsInitializedFlag)
{
sb_shutdown();
}
myIsInitializedFlag = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::setSoundVolume(uInt32 percent)
{
if(myIsInitializedFlag)
{
if((percent >= 0) && (percent <= 100))
{
// TODO: At some point we should support setting the volume...
myCurrentVolume = percent;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::updateSound(MediaSource& mediaSource)
{
if(myIsInitializedFlag)
{
// Move all of the generated samples into our private sample queue
uInt8 buffer[4096];
while(mediaSource.numberOfAudioSamples() > 0)
{
uInt32 size = mediaSource.dequeueAudioSamples(buffer, 4096);
mySampleQueue.enqueue(buffer, size);
}
// Block until the sound interrupt has consumed all but 142 milliseconds
// of the available audio samples
uInt32 leave = mySampleRate / 7;
while(mySampleQueue.size() > leave)
{
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::callback(void* udata, void* stream, int len)
{
SoundDOS* sound = (SoundDOS*)udata;
if(!sound->myIsInitializedFlag)
{
return;
}
// Don't use samples unless there's at least 100 milliseconds worth of data
if(sound->mySampleQueue.size() < (sound->mySampleRate / 10))
{
return;
}
sound->mySampleQueue.dequeue((uInt8*)stream, len);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundDOS::SampleQueue::SampleQueue(uInt32 capacity)
: myCapacity(capacity),
myBuffer(0),
mySize(0),
myHead(0),
myTail(0)
{
myBuffer = new uInt8[myCapacity];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundDOS::SampleQueue::~SampleQueue()
{
delete[] myBuffer;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::SampleQueue::clear()
{
myHead = myTail = mySize = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundDOS::SampleQueue::dequeue(uInt8* buffer, uInt32 size)
{
// We can only dequeue up to the number of items in the queue
if(size > mySize)
{
size = mySize;
}
if((myHead + size) < myCapacity)
{
memcpy((void*)buffer, (const void*)(myBuffer + myHead), size);
myHead += size;
}
else
{
uInt32 s1 = myCapacity - myHead;
uInt32 s2 = size - s1;
memcpy((void*)buffer, (const void*)(myBuffer + myHead), s1);
memcpy((void*)(buffer + s1), (const void*)myBuffer, s2);
myHead = (myHead + size) % myCapacity;
}
mySize -= size;
return size;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundDOS::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
{
disable();
if((mySize + size) > myCapacity)
{
size = myCapacity - mySize;
}
enable();
if(size == 0)
{
return;
}
if((myTail + size) < myCapacity)
{
memcpy((void*)(myBuffer + myTail), (const void*)buffer, size);
myTail += size;
}
else
{
uInt32 s1 = myCapacity - myTail;
uInt32 s2 = size - s1;
memcpy((void*)(myBuffer + myTail), (const void*)buffer, s1);
memcpy((void*)myBuffer, (const void*)(buffer + s1), s2);
myTail = (myTail + size) % myCapacity;
}
disable();
mySize += size;
enable();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundDOS::SampleQueue::size() const
{
return mySize;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
/*
** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of version 2 of the GNU Library General
** Public License as published by the Free Software Foundation.
**
** This program 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
** Library General Public License for more details. To obtain a
** copy of the GNU Library General Public License, write to the Free
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
**
** dos_sb.h
**
** DOS Sound Blaster header file
** $Id: dos_sb.h,v 1.1 2002-11-13 03:47:55 bwmott Exp $
*/
#ifndef DOS_SB_H
#define DOS_SB_H
/* Thanks, Allegro! */
#define BPS_TO_TIMER(x) (1193182L / (long)(x))
#define END_OF_FUNCTION(x) void x##_end(void) {}
#define END_OF_STATIC_FUNCTION(x) static void x##_end(void) {}
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void*)&x, sizeof(x))
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x, (long)x##_end - (long)x)
#define DISABLE_INTS() __asm__ __volatile__ ("cli")
#define ENABLE_INTS() __asm__ __volatile__ ("sti")
typedef void (*sbmix_t)(void *userdata, void *buffer, int size);
#ifdef __cplusplus
extern "C" {
#endif
extern int sb_init(int *sample_rate, int *bps, int *buf_size, int *stereo);
extern void sb_shutdown(void);
extern int sb_startoutput(sbmix_t fillbuf, void *userdata);
extern void sb_stopoutput(void);
extern void sb_setrate(int rate);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DOS_SB_H */

File diff suppressed because it is too large Load Diff

View File

@ -1,132 +0,0 @@
/*****************************************************************************/
/* */
/* LIBRARY Functions - Scan code definitions Copyright K.W 1994 */
/* */
/*****************************************************************************/
/* */
/* Title : Keyboard Code Definitions */
/* */
/* File Name : SCANDEF.H */
/* */
/* Author : Keith Wilkins */
/* */
/* Version : 0.01 */
/* */
/* Desciption : This header file defines all of the scan code definitions */
/* */
/* Functions : None */
/* */
/*****************************************************************************/
/* */
/* Revision History: */
/* */
/* Version Date Who Description of changes */
/* ------- ---- --- ---------------------- */
/* */
/* 0.01 13/11/94 K.W Creation */
/* */
/* */
/*****************************************************************************/
#ifndef SCANDEF_H
#define SCANDEF_H
#define SCAN_BASE (0x00)
#define SCAN_PAUSE (0x00) // We'll use 0x00 for the pause code
#define SCAN_ESC (0x01)
#define SCAN_1 (0x02)
#define SCAN_2 (0x03)
#define SCAN_3 (0x04)
#define SCAN_4 (0x05)
#define SCAN_5 (0x06)
#define SCAN_6 (0x07)
#define SCAN_7 (0x08)
#define SCAN_8 (0x09)
#define SCAN_9 (0x0a)
#define SCAN_0 (0x0b)
#define SCAN_MINUS (0x0c)
#define SCAN_EQUALS (0x0d)
#define SCAN_BSPACE (0x0e)
#define SCAN_TAB (0x0f)
#define SCAN_Q (0x10)
#define SCAN_W (0x11)
#define SCAN_E (0x12)
#define SCAN_R (0x13)
#define SCAN_T (0x14)
#define SCAN_Y (0x15)
#define SCAN_U (0x16)
#define SCAN_I (0x17)
#define SCAN_O (0x18)
#define SCAN_P (0x19)
#define SCAN_LSQRB (0x1a)
#define SCAN_RSQRB (0x1b)
#define SCAN_RETURN (0x1c)
#define SCAN_ENTER (0x1c)
#define SCAN_CTRL (0x1d)
#define SCAN_A (0x1e)
#define SCAN_S (0x1f)
#define SCAN_D (0x20)
#define SCAN_F (0x21)
#define SCAN_G (0x22)
#define SCAN_H (0x23)
#define SCAN_J (0x24)
#define SCAN_K (0x25)
#define SCAN_L (0x26)
#define SCAN_SCOLON (0x27)
#define SCAN_APSTPY (0x28) // Apostrophy '''''
#define SCAN_TILDE (0x29)
#define SCAN_LSHIFT (0x2a)
#define SCAN_HASH (0x2b)
#define SCAN_Z (0x2c)
#define SCAN_X (0x2d)
#define SCAN_C (0x2e)
#define SCAN_V (0x2f)
#define SCAN_B (0x30)
#define SCAN_N (0x31)
#define SCAN_M (0x32)
#define SCAN_COMMA (0x33)
#define SCAN_STOP (0x34)
#define SCAN_FSLASH (0x35)
#define SCAN_RSHIFT (0x36)
#define SCAN_STAR (0x37)
#define SCAN_ALT (0x38)
#define SCAN_SPACE (0x39)
#define SCAN_CAPS (0x3a)
#define SCAN_F1 (0x3b)
#define SCAN_F2 (0x3c)
#define SCAN_F3 (0x3d)
#define SCAN_F4 (0x3e)
#define SCAN_F5 (0x3f)
#define SCAN_F6 (0x40)
#define SCAN_F7 (0x41)
#define SCAN_F8 (0x42)
#define SCAN_F9 (0x43)
#define SCAN_F10 (0x44)
#define SCAN_NUMLCK (0x45)
#define SCAN_SCRLCK (0x46)
#define SCAN_HOME (0x47)
#define SCAN_UP (0x48)
#define SCAN_PGUP (0x49)
#define SCAN_DASH (0x4a) // Number pad minus
#define SCAN_LEFT (0x4b)
#define SCAN_CENTRE (0x4c) // Number pad centre
#define SCAN_RIGHT (0x4d)
#define SCAN_PLUS (0x4e) // Number pad plus
#define SCAN_END (0x4f)
#define SCAN_DOWN (0x50)
#define SCAN_PGDN (0x51)
#define SCAN_INS (0x52)
#define SCAN_DEL (0x53)
#define SCAN_BSLASH (0x56)
#define SCAN_F11 (0x57)
#define SCAN_F12 (0x58)
#endif
/* END OF SCANDEF.H */

View File

@ -1,287 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2003 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// This code is based on the vga256fb frame buffer device driver for
// Linux by Salvatore Sanfilippo <antirez@invece.org>. The vga256fb
// code can be found at http://www.kyuzz.org/antirez/vga256fb.htm. It
// was released under the GNU General Public License.
//
// $Id: vga.cxx,v 1.1 2003-02-17 05:17:42 bwmott Exp $
//============================================================================
#include <sys/farptr.h>
#include <dos.h>
#include "vga.hxx"
// Structure for holding VGA mode information and register settings
struct VgaModeInfo
{
unsigned int xres; // X resolution
unsigned int yres; // Y resolution
bool chained; // Chained flag
unsigned char crt[0x19]; // 24 CRT sub-registers
unsigned char attrib[0x15]; // 21 attribute sub-registers
unsigned char graphic[0x09]; // 9 graphic sub-registers
unsigned char sequencer[0x05]; // 5 sequencer sub-registers
unsigned char misc; // misc register
};
// VGA mode information for 320x200x256 colors at 60Hz
static VgaModeInfo Vga320x200x60Hz = {
xres: 320,
yres: 200,
chained: true,
crt: {
0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0A, 0x3E,
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC2, 0x84, 0x8F, 0x28, 0x40, 0x90, 0x08, 0xA3 },
attrib: {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00, },
graphic: {
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF },
sequencer: {
0x03, 0x01, 0x0F, 0x00, 0x0E },
misc: 0x63
};
// VGA mode information for 320x200x256 colors at 70Hz (standard BIOS 13h mode)
static VgaModeInfo Vga320x200x70Hz = {
xres: 320,
yres: 200,
chained: true,
crt: {
0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F,
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x9C, 0x8E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3 },
attrib: {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00 },
graphic: {
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF },
sequencer: {
0x03, 0x01, 0x0F, 0x00, 0x0E },
misc: 0x63
};
// VGA mode information for 320x240x256 colors at 60Hz (square pixels)
static VgaModeInfo Vga320x240x60Hz = {
xres: 320,
yres: 240,
chained: false,
crt: {
0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0D, 0x3E,
0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xEA, 0xAC, 0xDF, 0x28, 0x00, 0xE7, 0x06, 0xE3 },
attrib: {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x41, 0x00, 0x0F, 0x00, 0x00, },
graphic: {
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF },
sequencer: {
0x03, 0x01, 0x0F, 0x00, 0x06 },
misc: 0xE3
};
#define V_CRT_INDEX 0x3D4 // CRT address (index) register
#define V_CRT_RW 0x3D5 // CRT data register
#define V_ISTAT1_R 0x3DA // Input status register #1
#define V_FEATURE_W 0x3DA // Feature control register, (write)
#define V_SEQ_INDEX 0x3C4 // Sequencer address (index) register
#define V_SEQ_RW 0x3C5 // Sequencer data register
#define V_GR_INDEX 0x3CE // VGA address (index) register
#define V_GR_RW 0x3CF // VGA data register
#define V_MISC_R 0x3CC // VGA misc register (read)
#define V_MISC_W 0x3C2 // VGA misc register (write)
#define V_ATTR_IW 0x3C0 // Attribute index and data register (write)
#define V_ATTR_R 0x3C1 // Attribute data register (read)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static inline void put_sequence(int i, unsigned char b)
{
outportb(V_SEQ_INDEX, i);
outportb(V_SEQ_RW, b);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static inline void put_graph(int i, unsigned char b)
{
outportb(V_GR_INDEX, i);
outportb(V_GR_RW, b);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static inline void put_misc(unsigned char b)
{
outportb(V_MISC_W, b);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static inline void put_attr(int i, unsigned char b)
{
// Warning: as you can see the 0x20 bit will be set to zero
// so the video output will be disabled, if you want read or write
// (since some VGA cards allows you to write without setting the PAS
// bit) without put the video off OR the index with 0x20 */
// reset the flip/flop
inportb(V_ISTAT1_R);
// set the index
outportb(V_ATTR_IW, i);
// write data
outportb(V_ATTR_IW, b);
// reset the flip/flop
inportb(V_ISTAT1_R);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static inline void put_crt(int i, unsigned char b)
{
outportb(V_CRT_INDEX, i);
outportb(V_CRT_RW, b);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static inline unsigned char get_crt(int i)
{
outportb(V_CRT_INDEX, i);
return inportb(V_CRT_RW);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void disable_video(void)
{
// Get the current value
volatile unsigned char t = inportb(V_ATTR_IW);
// Reset the flip/flop
inportb(V_ISTAT1_R);
// Clear the PAS bit
t &= 0xDF;
// Set the port
outportb(V_ATTR_IW, t);
// Reset the flip/flop
inportb(V_ISTAT1_R);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void enable_video(void)
{
// Get the current value
volatile unsigned char t = inportb(V_ATTR_IW);
// Reset the flip/flop
inportb(V_ISTAT1_R);
// Set the PAS bit
t |= 0x20;
// set the port
outportb(V_ATTR_IW, t);
// Reset the flip/flop
inportb(V_ISTAT1_R);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void unlock_crt_registers(void)
{
volatile unsigned char aux = get_crt(0x11);
aux &= 0x7f;
put_crt(0x11, aux);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void load_regs(VgaModeInfo *regs)
{
int j;
disable_video();
disable();
// Set misc register
put_misc(regs->misc);
// Sequencer sync reset on
put_sequence(0x00, 0x01);
// Sequencer registers
for(j = 0; j <= 0x04; j++)
{
put_sequence(j, regs->sequencer[j]);
}
// Sequencer reset off
put_sequence(0x00, 0x03);
unlock_crt_registers();
// crt registers
for(j = 0; j <= 0x18; j++)
{
put_crt(j, regs->crt[j]);
}
// Graphic registers
for(j = 0; j <= 0x08; j++)
{
put_graph(j, regs->graphic[j]);
}
// Attrib registers
for(j = 0; j <= 0x14; j++)
{
put_attr(j, regs->attrib[j]);
}
enable();
enable_video();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool VgaSetMode(int mode)
{
VgaModeInfo* info = 0;
if(mode == VGA_320_200_60HZ)
{
info = &Vga320x200x60Hz;
}
else if(mode == VGA_320_200_70HZ)
{
info = &Vga320x200x70Hz;
}
else if(mode == VGA_320_240_60HZ)
{
info = &Vga320x240x60Hz;
}
else
{
assert(false);
}
load_regs(info);
return info->chained;
}

View File

@ -1,34 +0,0 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2003 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: vga.hxx,v 1.1 2003-02-17 05:17:42 bwmott Exp $
//============================================================================
#ifndef VGA_HXX
#define VGA_HXX
#include "bspf.hxx"
#define VGA_320_200_60HZ 1
#define VGA_320_200_70HZ 2
#define VGA_320_240_60HZ 3
/**
Change the graphics mode to the specified mode.
*/
extern bool VgaSetMode(int mode);
#endif

View File

@ -0,0 +1,182 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsUNIX.cxx,v 1.1 2004-05-24 17:18:23 stephena Exp $
//============================================================================
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "EventHandler.hxx"
#include "StellaEvent.hxx"
#include "Settings.hxx"
#include "SettingsUNIX.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsUNIX::SettingsUNIX()
{
// First set variables that the parent class needs
myBaseDir = getenv("HOME");
string stelladir = myBaseDir + "/.stella";
if(access(stelladir.c_str(), R_OK|W_OK|X_OK) != 0 )
mkdir(stelladir.c_str(), 0777);
myStateDir = stelladir + "/state/";
if(access(myStateDir.c_str(), R_OK|W_OK|X_OK) != 0 )
mkdir(myStateDir.c_str(), 0777);
myUserPropertiesFile = stelladir + "/stella.pro";
mySystemPropertiesFile = "/etc/stella.pro";
myUserConfigFile = stelladir + "/stellarc";
mySystemConfigFile = "/etc/stellarc";
// Set up the names of the input and output config files
mySettingsOutputFilename = myUserConfigFile;
if(access(myUserConfigFile.c_str(), R_OK) == 0)
mySettingsInputFilename = myUserConfigFile;
else
mySettingsInputFilename = mySystemConfigFile;
mySnapshotFile = "";
myStateFile = "";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsUNIX::~SettingsUNIX()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SettingsUNIX::usage(string& message)
{
cout << endl
<< message << endl
<< endl
<< "Valid options are:" << endl
<< endl
<< " -video <type> Type is one of the following:\n"
<< " soft SDL software mode\n"
#ifdef DISPLAY_OPENGL
<< " gl SDL OpenGL mode\n"
<< endl
<< " -gl_filter <type> Type is one of the following:\n"
<< " nearest Normal scaling (GL_NEAREST)\n"
<< " linear Blurred scaling (GL_LINEAR)\n"
<< " -gl_aspect <number> Scale the width by the given amount\n"
<< endl
#endif
<< " -sound <0|1> Enable sound generation\n"
<< " -fragsize <number> The size of sound fragments (should be a power of two)\n"
<< " -bufsize <number> The size of the sound buffer\n"
<< " -framerate <number> Display the given number of frames per second\n"
<< " -zoom <size> Makes window be 'size' times normal\n"
<< " -fullscreen <0|1> Play the game in fullscreen mode\n"
<< " -grabmouse <0|1> Keeps the mouse in the game window\n"
<< " -hidecursor <0|1> Hides the mouse cursor in the game window\n"
<< " -volume <number> Set the volume (0 - 100)\n"
#ifdef JOYSTICK_SUPPORT
<< " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate\n"
<< " or that real Atari 2600 paddles are being used\n"
#else
<< " -paddle <0|1|2|3> Indicates which paddle the mouse should emulate\n"
#endif
<< " -altpro <props file> Use the given properties file instead of stella.pro\n"
<< " -showinfo <0|1> Shows some game info\n"
<< " -accurate <0|1> Accurate game timing (uses more CPU)\n"
#ifdef SNAPSHOT_SUPPORT
<< " -ssdir <path> The directory to save snapshot files to\n"
<< " -ssname <name> How to name the snapshot (romname or md5sum)\n"
<< " -sssingle <0|1> Generate single snapshot instead of many\n"
#endif
<< endl
#ifdef DEVELOPER_SUPPORT
<< " DEVELOPER options (see Stella manual for details)\n"
<< " -Dformat Sets \"Display.Format\"\n"
<< " -Dxstart Sets \"Display.XStart\"\n"
<< " -Dwidth Sets \"Display.Width\"\n"
<< " -Dystart Sets \"Display.YStart\"\n"
<< " -Dheight Sets \"Display.Height\"\n"
<< " -mergeprops <0|1> Merge changed properties into properties file,\n"
<< " or save into a separate file\n"
#endif
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsUNIX::stateFilename(uInt32 state)
{
if(!myConsole)
return "";
ostringstream buf;
buf << myStateDir << myConsole->properties().get("Cartridge.MD5")
<< ".st" << state;
myStateFile = buf.str();
return myStateFile;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsUNIX::snapshotFilename()
{
if(!myConsole)
return "";
string filename;
string path = getString("ssdir");
string theSnapshotName = getString("ssname");
if(theSnapshotName == "romname")
path = path + "/" + myConsole->properties().get("Cartridge.Name");
else if(theSnapshotName == "md5sum")
path = path + "/" + myConsole->properties().get("Cartridge.MD5");
// Replace all spaces in name with underscores
replace(path.begin(), path.end(), ' ', '_');
// Check whether we want multiple snapshots created
if(!getBool("sssingle"))
{
// Determine if the file already exists, checking each successive filename
// until one doesn't exist
filename = path + ".png";
if(access(filename.c_str(), F_OK) == 0 )
{
ostringstream buf;
for(uInt32 i = 1; ;++i)
{
buf.str("");
buf << path << "_" << i << ".png";
if(access(buf.str().c_str(), F_OK) == -1 )
break;
}
filename = buf.str();
}
}
else
filename = path + ".png";
mySnapshotFile = filename;
return mySnapshotFile;
}

View File

@ -0,0 +1,70 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsUNIX.hxx,v 1.1 2004-05-24 17:18:23 stephena Exp $
//============================================================================
#ifndef SETTINGS_UNIX_HXX
#define SETTINGS_UNIX_HXX
#include "bspf.hxx"
class Console;
/**
This class defines UNIX-like OS's (Linux) system specific settings.
@author Stephen Anthony
@version $Id: SettingsUNIX.hxx,v 1.1 2004-05-24 17:18:23 stephena Exp $
*/
class SettingsUNIX : public Settings
{
public:
/**
Create a new UNIX settings object
*/
SettingsUNIX();
/**
Destructor
*/
virtual ~SettingsUNIX();
public:
/**
This method should be called to get the filename of a state file
given the state number.
@return String representing the full path of the state filename.
*/
virtual string stateFilename(uInt32 state);
/**
This method should be called to get the filename of a snapshot.
@return String representing the full path of the snapshot filename.
*/
virtual string snapshotFilename();
/**
Display the commandline settings for this UNIX version of Stella.
@param message A short message about this version of Stella
*/
virtual void usage(string& message);
};
#endif

View File

@ -0,0 +1,188 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsWin32.cxx,v 1.1 2004-05-24 17:18:23 stephena Exp $
//============================================================================
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bspf.hxx"
#include "Console.hxx"
#include "EventHandler.hxx"
#include "StellaEvent.hxx"
#include "Settings.hxx"
#include "SettingsWin32.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsWin32::SettingsWin32()
{
// First set variables that the parent class needs
myBaseDir = ".\\";
string stelladir = myBaseDir;
// if(access(stelladir.c_str(), R_OK|W_OK|X_OK) != 0 )
// mkdir(stelladir.c_str(), 0777);
myStateDir = stelladir + "state\\";
// if(access(myStateDir.c_str(), R_OK|W_OK|X_OK) != 0 )
// mkdir(myStateDir.c_str(), 0777);
myUserPropertiesFile = stelladir + "stella.pro";
mySystemPropertiesFile = stelladir + "stella.pro";
myUserConfigFile = stelladir + "stellarc";
mySystemConfigFile = stelladir + "stellarc";
// Set up the names of the input and output config files
mySettingsOutputFilename = myUserConfigFile;
// if(access(myUserConfigFile.c_str(), R_OK) == 0)
mySettingsInputFilename = myUserConfigFile;
// else
// mySettingsInputFilename = mySystemConfigFile;
mySnapshotFile = "";
myStateFile = "";
// Now create Win32 specific settings
set("accurate", "false"); // Don't change this, or the sound will skip
#ifdef SNAPSHOT_SUPPORT
set("ssdir", ".\\");
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SettingsWin32::~SettingsWin32()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SettingsWin32::usage(string& message)
{
cout << endl
<< message << endl
<< endl
<< "Valid options are:" << endl
<< endl
<< " -video <type> Type is one of the following:\n"
<< " soft SDL software mode\n"
#ifdef DISPLAY_OPENGL
<< " gl SDL OpenGL mode\n"
<< endl
<< " -gl_filter <type> Type is one of the following:\n"
<< " nearest Normal scaling (GL_NEAREST)\n"
<< " linear Blurred scaling (GL_LINEAR)\n"
<< " -gl_aspect <number> Scale the width by the given amount\n"
<< endl
#endif
<< " -sound <0|1> Enable sound generation\n"
<< " -fragsize <number> The size of sound fragments (should be a power of two)\n"
<< " -bufsize <number> The size of the sound buffer\n"
<< " -framerate <number> Display the given number of frames per second\n"
<< " -zoom <size> Makes window be 'size' times normal\n"
<< " -fullscreen <0|1> Play the game in fullscreen mode\n"
<< " -grabmouse <0|1> Keeps the mouse in the game window\n"
<< " -hidecursor <0|1> Hides the mouse cursor in the game window\n"
<< " -volume <number> Set the volume (0 - 100)\n"
#ifdef JOYSTICK_SUPPORT
<< " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate\n"
<< " or that real Atari 2600 paddles are being used\n"
#else
<< " -paddle <0|1|2|3> Indicates which paddle the mouse should emulate\n"
#endif
<< " -altpro <props file> Use the given properties file instead of stella.pro\n"
<< " -showinfo <0|1> Shows some game info\n"
<< " -accurate <0|1> Accurate game timing (uses more CPU)\n"
#ifdef SNAPSHOT_SUPPORT
<< " -ssdir <path> The directory to save snapshot files to\n"
<< " -ssname <name> How to name the snapshot (romname or md5sum)\n"
<< " -sssingle <0|1> Generate single snapshot instead of many\n"
#endif
<< endl
#ifdef DEVELOPER_SUPPORT
<< " DEVELOPER options (see Stella manual for details)\n"
<< " -Dformat Sets \"Display.Format\"\n"
<< " -Dxstart Sets \"Display.XStart\"\n"
<< " -Dwidth Sets \"Display.Width\"\n"
<< " -Dystart Sets \"Display.YStart\"\n"
<< " -Dheight Sets \"Display.Height\"\n"
<< " -mergeprops <0|1> Merge changed properties into properties file,\n"
<< " or save into a separate file\n"
#endif
<< endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsWin32::stateFilename(uInt32 state)
{
if(!myConsole)
return "";
ostringstream buf;
buf << myStateDir << myConsole->properties().get("Cartridge.MD5")
<< ".st" << state;
myStateFile = buf.str();
return myStateFile;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string SettingsWin32::snapshotFilename()
{
if(!myConsole)
return "";
string filename;
string path = getString("ssdir");
string theSnapshotName = getString("ssname");
if(theSnapshotName == "romname")
path = path + "\\" + myConsole->properties().get("Cartridge.Name");
else if(theSnapshotName == "md5sum")
path = path + "\\" + myConsole->properties().get("Cartridge.MD5");
// Replace all spaces in name with underscores
replace(path.begin(), path.end(), ' ', '_');
// Check whether we want multiple snapshots created
if(!getBool("sssingle"))
{
// Determine if the file already exists, checking each successive filename
// until one doesn't exist
filename = path + ".png";
if(access(filename.c_str(), F_OK) == 0 )
{
ostringstream buf;
for(uInt32 i = 1; ;++i)
{
buf.str("");
buf << path << "_" << i << ".png";
if(access(buf.str().c_str(), F_OK) == -1 )
break;
}
filename = buf.str();
}
}
else
filename = path + ".png";
mySnapshotFile = filename;
return mySnapshotFile;
}

View File

@ -0,0 +1,70 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-1999 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SettingsWin32.hxx,v 1.1 2004-05-24 17:18:23 stephena Exp $
//============================================================================
#ifndef SETTINGS_WIN32_HXX
#define SETTINGS_WIN32_HXX
#include "bspf.hxx"
class Console;
/**
This class defines Windows system specific settings.
@author Stephen Anthony
@version $Id: SettingsWin32.hxx,v 1.1 2004-05-24 17:18:23 stephena Exp $
*/
class SettingsWin32 : public Settings
{
public:
/**
Create a new UNIX settings object
*/
SettingsWin32();
/**
Destructor
*/
virtual ~SettingsWin32();
public:
/**
This method should be called to get the filename of a state file
given the state number.
@return String representing the full path of the state filename.
*/
virtual string stateFilename(uInt32 state);
/**
This method should be called to get the filename of a snapshot.
@return String representing the full path of the snapshot filename.
*/
virtual string snapshotFilename();
/**
Display the commandline settings for this UNIX version of Stella.
@param message A short message about this version of Stella
*/
virtual void usage(string& message);
};
#endif