OK, this is a BIG commit.

Added CRT simulation as described by Ian Bogost in the AtariAge thread
'CRT simulation for Stella.  This is just the first pass, but things
are working nicely so far (for those systems that can support it).  It
requires at least OpenGL 2.0 with GLSL (GL Shading language).  Added
the commandline arugments 'tv_tex', 'tv_bleed', 'tv_noise' and 'tv_phos'
used to set these various effects, as well as the ability to set them
within the Video Settings dialog.  More documentation is forthcoming
on this.

All bankswitch modes that use SC RAM now act correctly when reading
from the write port (the RAM is erased).

Patching ROMs with bankswitch type 0840, SB, UA and X07 is now working.

Went through all the bankswitch classes and converted multiplication/
division to shift operations whenever possible.  It's a
micro-optimization, but what the heck; every little bit of speed counts.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1739 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2009-05-25 17:51:52 +00:00
parent 54a3ddab7a
commit 79e4e42ae7
36 changed files with 1548 additions and 252 deletions

View File

@ -12,6 +12,23 @@
Release History
===============================================================================
2.7.7 to 2.8: (June xx, 2009)
* Added CRT simulation effects as described in the AtariAge posting
'CRT emulation for Stella'. For now, this requires OpenGL 2.0 or
greater with support for GLSL (GL Shading Language).
* All bankswitching schemes which include SC extended RAM will now have
memory erased if you attempt to read from the write port. Related to
this, entering/exiting the debugger will no longer erase the extended
RAM.
* Patching of ROM for bankswitch types '0840', 'SB', 'UA' and 'X07' is
now implemented, but hasn't been extensively tested.
-Have fun!
2.7.6 to 2.7.7: (May 1, 2009)
* Corrected emulation of CPU opcodes involving 'decimal' mode (ADC/RRA
@ -24,8 +41,6 @@
* Changed internal sound frequency of Pitfall 2 from 15.75KHz to 20KHz,
as this sounds much more authentic when compared to a real cartridge.
-Have fun!
2.7.5 to 2.7.6: (April 14, 2009)

View File

@ -30,6 +30,7 @@
#include "Settings.hxx"
#include "Surface.hxx"
#include "TIA.hxx"
#include "GLShaderProgs.hxx"
#include "FrameBufferGL.hxx"
@ -61,6 +62,25 @@ OGL_DECLARE(void,glBindTexture,(GLenum, GLuint));
OGL_DECLARE(void,glTexImage2D,(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*));
OGL_DECLARE(void,glTexSubImage2D,(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*));
OGL_DECLARE(void,glTexParameteri,(GLenum, GLenum, GLint));
OGL_DECLARE(GLuint,glCreateShader,(GLenum));
OGL_DECLARE(void,glDeleteShader,(GLuint));
OGL_DECLARE(void,glShaderSource,(GLuint, int, const char**, int));
OGL_DECLARE(void,glCompileShader,(GLuint));
OGL_DECLARE(GLuint,glCreateProgram,(void));
OGL_DECLARE(void,glDeleteProgram,(GLuint));
OGL_DECLARE(void,glAttachShader,(GLuint, GLuint));
OGL_DECLARE(void,glLinkProgram,(GLuint));
OGL_DECLARE(void,glUseProgram,(GLuint));
OGL_DECLARE(GLint,glGetUniformLocation,(GLuint, const char*));
OGL_DECLARE(void,glUniform1i,(GLint, GLint));
OGL_DECLARE(void,glUniform1f,(GLint, GLfloat));
OGL_DECLARE(void,glCopyTexImage2D,(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint));
OGL_DECLARE(void,glCopyTexSubImage2D,(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei));
OGL_DECLARE(void,glActiveTexture,(GLenum));
OGL_DECLARE(void,glGetIntegerv,(GLenum, GLint*));
OGL_DECLARE(void,glTexEnvi,(GLenum, GLenum, GLint));
OGL_DECLARE(void,glMultiTexCoord2f,(GLenum, GLfloat, GLfloat));
OGL_DECLARE(GLenum,glGetError,(void));
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -143,10 +163,34 @@ bool FrameBufferGL::loadFuncs()
OGL_INIT(void,glTexImage2D,(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*));
OGL_INIT(void,glTexSubImage2D,(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*));
OGL_INIT(void,glTexParameteri,(GLenum, GLenum, GLint));
OGL_INIT(GLuint,glCreateShader,(GLenum));
OGL_INIT(void,glDeleteShader,(GLuint));
OGL_INIT(void,glShaderSource,(GLuint, int, const char**, int));
OGL_INIT(void,glCompileShader,(GLuint));
OGL_INIT(GLuint,glCreateProgram,(void));
OGL_INIT(void,glDeleteProgram,(GLuint));
OGL_INIT(void,glAttachShader,(GLuint, GLuint));
OGL_INIT(void,glLinkProgram,(GLuint));
OGL_INIT(void,glUseProgram,(GLuint));
OGL_INIT(GLint,glGetUniformLocation,(GLuint, const char*));
OGL_INIT(void,glUniform1i,(GLint, GLint));
OGL_INIT(void,glUniform1f,(GLint, GLfloat));
OGL_INIT(void,glCopyTexImage2D,(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint));
OGL_INIT(void,glCopyTexSubImage2D,(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei));
OGL_INIT(void,glActiveTexture,(GLenum));
OGL_INIT(void,glGetIntegerv,(GLenum, GLint*));
OGL_INIT(void,glTexEnvi,(GLenum, GLenum, GLint));
OGL_INIT(void,glMultiTexCoord2f,(GLenum, GLfloat, GLfloat));
OGL_INIT(GLenum,glGetError,(void));
}
else
return false;
// Grab OpenGL version number
string version((const char *)p_glGetString(GL_VERSION));
myGLVersion = atof(version.substr(0, 3).c_str());
return true;
}
@ -215,6 +259,31 @@ bool FrameBufferGL::setVidMode(VideoMode& mode)
uInt32 baseWidth = mode.image_w / mode.gfxmode.zoom;
uInt32 baseHeight = mode.image_h / mode.gfxmode.zoom;
// Update the graphics filter options
myUseTexture = true; myTextureStag = false;
const string& tv_tex = myOSystem->settings().getString("tv_tex");
if(tv_tex == "stag") myTextureStag = true;
else if(tv_tex != "normal") myUseTexture = false;
myUseBleed = true;
const string& tv_bleed = myOSystem->settings().getString("tv_bleed");
if(tv_bleed == "low") myBleedQuality = 0;
else if(tv_bleed == "medium") myBleedQuality = 1;
else if(tv_bleed == "high") myBleedQuality = 2;
else myUseBleed = false;
myUseNoise = true;
const string& tv_noise = myOSystem->settings().getString("tv_noise");
if(tv_noise == "low") myNoiseQuality = 5;
else if(tv_noise == "medium") myNoiseQuality = 15;
else if(tv_noise == "high") myNoiseQuality = 25;
else myUseNoise = false;
myUseGLPhosphor = myOSystem->settings().getBool("tv_phos");
// Set the zoom level
myZoomLevel = mode.gfxmode.zoom;
// Aspect ratio and fullscreen stretching only applies to the TIA
if(!inUIMode)
{
@ -338,8 +407,10 @@ cerr << "dimensions: " << (fullScreen() ? "(full)" : "") << endl
// and other UI surfaces are no longer tied together
// Note that this may change in the future, when we add more
// complex filters/scalers, but for now it's fine
// Also note that TV filtering is only available with OpenGL 2.0+
myTiaSurface = new FBSurfaceGL(*this, baseWidth>>1, baseHeight,
mode.image_w, mode.image_h);
mode.image_w, mode.image_h,
myGLVersion >= 2.0);
myTiaSurface->setPos(mode.image_x, mode.image_y);
myTiaSurface->setFilter(myOSystem->settings().getString("gl_filter"));
}
@ -473,14 +544,28 @@ void FrameBufferGL::scanline(uInt32 row, uInt8* data) const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FBSurfaceGL::FBSurfaceGL(FrameBufferGL& buffer,
uInt32 baseWidth, uInt32 baseHeight,
uInt32 scaleWidth, uInt32 scaleHeight)
uInt32 scaleWidth, uInt32 scaleHeight,
bool allowFiltering)
: myFB(buffer),
myTexture(NULL),
myTexID(0),
myFilterTexID(0),
mySubMaskTexID(0),
myNoiseMaskTexID(NULL),
myPhosphorTexID(0),
mySubpixelTexture(NULL),
myNoiseTexture(NULL),
myXOrig(0),
myYOrig(0),
myWidth(scaleWidth),
myHeight(scaleHeight)
myHeight(scaleHeight),
myBleedProgram(0),
myTextureProgram(0),
myNoiseProgram(0),
myPhosphorProgram(0),
myTextureNoiseProgram(0),
myNoiseNum(0),
myTvFiltersEnabled(allowFiltering)
{
// Fill buffer struct with valid data
// This changes depending on the texturing used
@ -510,6 +595,198 @@ FBSurfaceGL::FBSurfaceGL(FrameBufferGL& buffer,
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
myPitch = myTexture->pitch >> 1;
// Only do this if TV filters enabled, otherwise it won't be used anyway
if(myTvFiltersEnabled)
{
// For a reason that hasn't been investigated yet, some of the filter and mask
// texture coordinates need to be swapped in order for it not to render upside down
myFilterTexCoord[0] = 0.0f;
myFilterTexCoord[3] = 0.0f;
if(myFB.myHaveTexRectEXT)
{
myFilterTexWidth = scaleWidth;
myFilterTexHeight = scaleHeight;
myFilterTexCoord[2] = (GLfloat) myFilterTexWidth;
myFilterTexCoord[1] = (GLfloat) myFilterTexHeight;
}
else
{
myFilterTexWidth = power_of_two(scaleWidth);
myFilterTexHeight = power_of_two(scaleHeight);
myFilterTexCoord[2] = (GLfloat) scaleWidth / myFilterTexWidth;
myFilterTexCoord[1] = (GLfloat) scaleHeight / myFilterTexHeight;
}
}
// Only do this if TV and color bleed filters are enabled
// This filer applies a color averaging of surrounding pixels for each pixel
if(myTvFiltersEnabled && myFB.myUseBleed)
{
// Load shader programs. If it fails, don't use this filter.
myBleedProgram = genShader(SHADER_BLEED);
if(myBleedProgram == 0)
{
myFB.myUseBleed = false;
cout << "ERROR: Failed to make bleed programs" << endl;
}
}
// If the texture and noise filters are enabled together, we can use a single shader
// Make sure we can use three textures at once first
GLint texUnits;
p_glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texUnits);
if(myTvFiltersEnabled && texUnits >= 3 && myFB.myUseTexture && myFB.myUseNoise)
{
// Load shader program. If it fails, don't use this shader.
myTextureNoiseProgram = genShader(SHADER_TEXNOISE);
if(myTextureNoiseProgram == 0)
{
cout << "ERROR: Failed to make texture/noise program" << endl;
// Load shader program. If it fails, don't use this filter.
myTextureProgram = genShader(SHADER_TEX);
if(myTextureProgram == 0)
{
myFB.myUseTexture = false;
cout << "ERROR: Failed to make texture program" << endl;
}
// Load shader program. If it fails, don't use this filter.
myNoiseProgram = genShader(SHADER_NOISE);
if(myNoiseProgram == 0)
{
myFB.myUseNoise = false;
cout << "ERROR: Failed to make noise program" << endl;
}
}
}
// Else, detect individual settings
else if(myTvFiltersEnabled)
{
if(myFB.myUseTexture)
{
// Load shader program. If it fails, don't use this filter.
myTextureProgram = genShader(SHADER_TEX);
if(myTextureProgram == 0)
{
myFB.myUseTexture = false;
cout << "ERROR: Failed to make texture program" << endl;
}
}
if(myFB.myUseNoise)
{
// Load shader program. If it fails, don't use this filter.
myNoiseProgram = genShader(SHADER_NOISE);
if(myNoiseProgram == 0)
{
myFB.myUseNoise = false;
cout << "ERROR: Failed to make noise program" << endl;
}
}
}
// Only do this if TV and color texture filters are enabled
// This filer applies an RGB color pixel mask as well as a blackspace mask
if(myTvFiltersEnabled && myFB.myUseTexture)
{
// Prepare subpixel texture
mySubpixelTexture = SDL_CreateRGBSurface(SDL_SWSURFACE,
myFilterTexWidth, myFilterTexHeight, 16,
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
uInt32 pCounter = 0;
for (uInt32 y = 0; y < (uInt32)myFilterTexHeight; y++)
{
for (uInt32 x = 0; x < (uInt32)myFilterTexWidth; x++)
{
// Cause vertical offset for every other black row if enabled
uInt32 offsetY;
if (!myFB.myTextureStag || x % 6 < 3)
offsetY = y;
else
offsetY = y + 2;
// Make a row of black for the mask every so often
if (offsetY % 4 == 0)
{
((uInt16*)mySubpixelTexture->pixels)[pCounter] = 0x0000;
}
// Apply the coorect color mask
else
{
((uInt16*)mySubpixelTexture->pixels)[pCounter] = 0x7c00 >> ((x % 3) * 5);
}
pCounter++;
}
}
}
// Only do this if TV and noise filters are enabled
// This filter applies a texture filled with gray pixel of random intensities
if(myTvFiltersEnabled && myFB.myUseNoise)
{
// Get the current number of nose textures to use
myNoiseNum = myFB.myNoiseQuality;
// Allocate space for noise textures
myNoiseTexture = new SDL_Surface*[myNoiseNum];
myNoiseMaskTexID = new GLuint[myNoiseNum];
// Prepare noise textures
for(int i = 0; i < myNoiseNum; i++)
{
myNoiseTexture[i] = SDL_CreateRGBSurface(SDL_SWSURFACE,
myFilterTexWidth, myFilterTexHeight, 16,
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
}
uInt32 pCounter = 0;
for(int i = 0; i < myNoiseNum; i++)
{
pCounter = 0;
// Attempt to make the numbers as random as possible
int temp = (unsigned)time(0) + rand()/4;
srand(temp);
for (uInt32 y = 0; y < (uInt32)myFilterTexHeight; y++)
{
for (uInt32 x = 0; x < (uInt32)myFilterTexWidth; x++)
{
// choose random 0 - 2
// 0 = 0x0000
// 1 = 0x0421
// 2 = 0x0842
int num = rand() % 3;
if (num == 0)
((uInt16*)myNoiseTexture[i]->pixels)[pCounter] = 0x0000;
else if (num == 1)
((uInt16*)myNoiseTexture[i]->pixels)[pCounter] = 0x0421;
else if (num == 2)
((uInt16*)myNoiseTexture[i]->pixels)[pCounter] = 0x0842;
pCounter++;
}
}
}
}
// Only do this if TV and phosphor filters are enabled
// This filter merges the past screen with the current one, to give a phosphor burn-off effect
if(myTvFiltersEnabled && myFB.myUseGLPhosphor)
{
// Load shader program. If it fails, don't use this filter.
myPhosphorProgram = genShader(SHADER_PHOS);
if(myPhosphorProgram == 0)
{
myFB.myUseGLPhosphor = false;
cout << "ERROR: Failed to make phosphor program" << endl;
}
}
// Associate the SDL surface with a GL texture object
reload();
}
@ -520,6 +797,13 @@ FBSurfaceGL::~FBSurfaceGL()
if(myTexture)
SDL_FreeSurface(myTexture);
if(mySubpixelTexture)
SDL_FreeSurface(mySubpixelTexture);
if(myNoiseTexture)
for(int i = 0; i < myNoiseNum; i++)
SDL_FreeSurface(myNoiseTexture[i]);
free();
}
@ -708,11 +992,27 @@ void FBSurfaceGL::update()
{
if(mySurfaceIsDirty)
{
GLint loc;
// Set a boolean to tell which filter is a first render (if any are applied).
// Being a first render means using the Atari frame buffer instead of the
// previous rendered data.
bool firstRender = true;
// Render as usual if no filters are used
if(!myTvFiltersEnabled ||
(!myFB.myUseTexture && !myFB.myUseNoise && !myFB.myUseBleed && !myFB.myUseGLPhosphor))
{
p_glUseProgram(0);
// Texturemap complete texture to surface so we have free scaling
// and antialiasing
p_glActiveTexture(GL_TEXTURE0);
p_glBindTexture(myTexTarget, myTexID);
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
// Pass in texture as a variable
p_glBegin(GL_QUADS);
p_glTexCoord2f(myTexCoord[0], myTexCoord[1]);
p_glVertex2i(myXOrig, myYOrig);
@ -726,6 +1026,162 @@ void FBSurfaceGL::update()
p_glTexCoord2f(myTexCoord[0], myTexCoord[3]);
p_glVertex2i(myXOrig, myYOrig + myHeight);
p_glEnd();
}
// If TV filters are enabled
if(myTvFiltersEnabled)
{
// If combined texture/noise program exists,
// use the combined one; else do them separately
if(myTextureNoiseProgram != 0)
{
p_glUseProgram(myTextureNoiseProgram);
// Pass in subpixel mask texture
p_glActiveTexture(GL_TEXTURE1);
p_glBindTexture(myTexTarget, mySubMaskTexID);
loc = p_glGetUniformLocation(myTextureNoiseProgram, "texMask");
p_glUniform1i(loc, 1);
// Choose random mask texture
int num = rand() % myNoiseNum;
// Pass in noise mask texture
p_glActiveTexture(GL_TEXTURE2);
p_glBindTexture(myTexTarget, myNoiseMaskTexID[num]);
loc = p_glGetUniformLocation(myTextureNoiseProgram, "noiseMask");
p_glUniform1i(loc, 2);
renderThreeTexture(myTextureNoiseProgram, firstRender);
// We have rendered, set firstRender to false
firstRender = false;
}
else
{
// Check if texture filter is enabled
if(myFB.myUseTexture)
{
p_glUseProgram(myTextureProgram);
// Pass in subpixel mask texture
p_glActiveTexture(GL_TEXTURE1);
p_glBindTexture(myTexTarget, mySubMaskTexID);
loc = p_glGetUniformLocation(myTextureProgram, "mask");
p_glUniform1i(loc, 1);
renderTwoTexture(myTextureProgram, firstRender);
// We have rendered, set firstRender to false
firstRender = false;
}
if(myFB.myUseNoise)
{
p_glUseProgram(myNoiseProgram);
// Choose random mask texture
int num = rand() % myNoiseNum;
// Pass in noise mask texture
p_glActiveTexture(GL_TEXTURE1);
p_glBindTexture(myTexTarget, myNoiseMaskTexID[num]);
loc = p_glGetUniformLocation(myNoiseProgram, "mask");
p_glUniform1i(loc, 1);
renderTwoTexture(myNoiseProgram, firstRender);
// We have rendered, set firstRender to false
firstRender = false;
}
}
// Check if bleed filter is enabled
if(myFB.myUseBleed)
{
p_glUseProgram(myBleedProgram);
// Set some values based on high, medium, or low quality bleed. The high quality
// scales by applying additional passes, the low and medium quality scales by using
// a width and height based on the zoom level
int passes;
// High quality
if(myFB.myBleedQuality == 2)
{
// Precalculate pixel shifts
GLfloat pH = 1.0 / myHeight;
GLfloat pW = 1.0 / myWidth;
GLfloat pWx2 = pW * 2.0;
loc = p_glGetUniformLocation(myBleedProgram, "pH");
p_glUniform1f(loc, pH);
loc = p_glGetUniformLocation(myBleedProgram, "pW");
p_glUniform1f(loc, pW);
loc = p_glGetUniformLocation(myBleedProgram, "pWx2");
p_glUniform1f(loc, pWx2);
// Set the number of passes based on zoom level
passes = myFB.getZoomLevel();
}
// Medium and low quality
else
{
// The scaling formula was produced through trial and error
// Precalculate pixel shifts
GLfloat pH = 1.0 / (myHeight / (0.35 * myFB.getZoomLevel()));
GLfloat pW = 1.0 / (myWidth / (0.35 * myFB.getZoomLevel()));
GLfloat pWx2 = pW * 2.0;
loc = p_glGetUniformLocation(myBleedProgram, "pH");
p_glUniform1f(loc, pH);
loc = p_glGetUniformLocation(myBleedProgram, "pW");
p_glUniform1f(loc, pW);
loc = p_glGetUniformLocation(myBleedProgram, "pWx2");
p_glUniform1f(loc, pWx2);
// Medium quality
if(myFB.myBleedQuality == 1)
passes = 2;
// Low quality
else
passes = 1;
}
// If we are using a texture effect, we need more bleed
if (myFB.myUseTexture)
passes <<= 1;
for (int i = 0; i < passes; i++)
{
renderTexture(myBleedProgram, firstRender);
// We have rendered, set firstRender to false
firstRender = false;
}
}
// Check if phosphor burn-off filter is enabled
if(myFB.myUseGLPhosphor)
{
p_glUseProgram(myPhosphorProgram);
// Pass in subpixel mask texture
p_glActiveTexture(GL_TEXTURE1);
p_glBindTexture(myTexTarget, myPhosphorTexID);
loc = p_glGetUniformLocation(myPhosphorProgram, "mask");
p_glUniform1i(loc, 1);
renderTwoTexture(myPhosphorProgram, firstRender);
p_glActiveTexture(GL_TEXTURE1);
p_glBindTexture(myTexTarget, myPhosphorTexID);
// We only need to copy the scaled size, which may be smaller than the texture width
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
// We have rendered, set firstRender to false
firstRender = false;
}
}
mySurfaceIsDirty = false;
// Let postFrameUpdate() know that a change has been made
@ -733,10 +1189,223 @@ void FBSurfaceGL::update()
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::renderTexture(GLuint program, bool firstRender)
{
GLint loc;
GLfloat texCoord[4];
p_glActiveTexture(GL_TEXTURE0);
// If this is a first render, use the Atari frame buffer
if(firstRender)
{
// Pass in Atari frame
p_glBindTexture(myTexTarget, myTexID);
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
// Set the texture coord appropriately
texCoord[0] = myTexCoord[0];
texCoord[1] = myTexCoord[1];
texCoord[2] = myTexCoord[2];
texCoord[3] = myTexCoord[3];
}
else
{
// Copy frame buffer to texture, this isn't the fastest way to do it, but it's simple
// (rendering directly to texture instead of copying may be faster)
p_glBindTexture(myTexTarget, myFilterTexID);
// We only need to copy the scaled size, which may be smaller than the texture width
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
// Set the texture coord appropriately
texCoord[0] = myFilterTexCoord[0];
texCoord[1] = myFilterTexCoord[1];
texCoord[2] = myFilterTexCoord[2];
texCoord[3] = myFilterTexCoord[3];
}
// Pass the texture to the program
loc = p_glGetUniformLocation(program, "tex");
p_glUniform1i(loc, 0);
// Pass in texture as a variable
p_glBegin(GL_QUADS);
p_glTexCoord2f(texCoord[0], texCoord[1]);
p_glVertex2i(myXOrig, myYOrig);
p_glTexCoord2f(texCoord[2], texCoord[1]);
p_glVertex2i(myXOrig + myWidth, myYOrig);
p_glTexCoord2f(texCoord[2], texCoord[3]);
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
p_glTexCoord2f(texCoord[0], texCoord[3]);
p_glVertex2i(myXOrig, myYOrig + myHeight);
p_glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::renderTwoTexture(GLuint program, bool firstRender)
{
GLint loc;
GLfloat texCoord[4];
p_glActiveTexture(GL_TEXTURE0);
// If this is a first render, use the Atari frame buffer
if(firstRender)
{
// Pass in Atari frame
p_glBindTexture(myTexTarget, myTexID);
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
// Set the texture coord appropriately
texCoord[0] = myTexCoord[0];
texCoord[1] = myTexCoord[1];
texCoord[2] = myTexCoord[2];
texCoord[3] = myTexCoord[3];
}
else
{
// Copy frame buffer to texture, this isn't the fastest way to do it, but it's simple
// (rendering directly to texture instead of copying may be faster)
p_glBindTexture(myTexTarget, myFilterTexID);
// We only need to copy the scaled size, which may be smaller than the texture width
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
// Set the filter texture coord appropriately
texCoord[0] = myFilterTexCoord[0];
texCoord[1] = myFilterTexCoord[1];
texCoord[2] = myFilterTexCoord[2];
texCoord[3] = myFilterTexCoord[3];
}
// Pass the texture to the program
loc = p_glGetUniformLocation(program, "tex");
p_glUniform1i(loc, 0);
// Pass in textures as variables
p_glBegin(GL_QUADS);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[1]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[1]);
p_glVertex2i(myXOrig, myYOrig);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[1]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[1]);
p_glVertex2i(myXOrig + myWidth, myYOrig);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[3]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[3]);
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[3]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[3]);
p_glVertex2i(myXOrig, myYOrig + myHeight);
p_glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::renderThreeTexture(GLuint program, bool firstRender)
{
GLint loc;
GLfloat texCoord[4];
p_glActiveTexture(GL_TEXTURE0);
// If this is a first render, use the Atari frame buffer
if(firstRender)
{
// Pass in Atari frame
p_glBindTexture(myTexTarget, myTexID);
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
// Set the texture coord appropriately
texCoord[0] = myTexCoord[0];
texCoord[1] = myTexCoord[1];
texCoord[2] = myTexCoord[2];
texCoord[3] = myTexCoord[3];
}
else
{
// Copy frame buffer to texture, this isn't the fastest way to do it, but it's simple
// (rendering directly to texture instead of copying may be faster)
p_glBindTexture(myTexTarget, myFilterTexID);
// We only need to copy the scaled size, which may be smaller than the texture width
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
// Set the filter texture coord appropriately
texCoord[0] = myFilterTexCoord[0];
texCoord[1] = myFilterTexCoord[1];
texCoord[2] = myFilterTexCoord[2];
texCoord[3] = myFilterTexCoord[3];
}
// Pass the texture to the program
loc = p_glGetUniformLocation(program, "tex");
p_glUniform1i(loc, 0);
// Pass in textures as variables
p_glBegin(GL_QUADS);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[1]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[1]);
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[0], myFilterTexCoord[1]);
p_glVertex2i(myXOrig, myYOrig);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[1]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[1]);
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[2], myFilterTexCoord[1]);
p_glVertex2i(myXOrig + myWidth, myYOrig);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[3]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[3]);
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[2], myFilterTexCoord[3]);
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[3]);
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[3]);
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[0], myFilterTexCoord[3]);
p_glVertex2i(myXOrig, myYOrig + myHeight);
p_glEnd();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FBSurfaceGL::free()
{
p_glDeleteTextures(1, &myTexID);
// The below is borken up a bit because of the possible combined texture/noise shader
if(myFilterTexID)
p_glDeleteTextures(1, &myFilterTexID);
if(mySubMaskTexID)
p_glDeleteTextures(1, &mySubMaskTexID);
if(myTextureProgram)
p_glDeleteProgram(myTextureProgram);
if(myNoiseMaskTexID)
{
delete[] myNoiseTexture;
p_glDeleteTextures(myNoiseNum, myNoiseMaskTexID);
delete[] myNoiseMaskTexID;
}
if(myNoiseProgram)
p_glDeleteProgram(myNoiseProgram);
if(myPhosphorTexID)
{
p_glDeleteTextures(1, &myPhosphorTexID);
p_glDeleteProgram(myPhosphorProgram);
}
if(myTextureNoiseProgram)
p_glDeleteProgram(myTextureNoiseProgram);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -751,6 +1420,9 @@ void FBSurfaceGL::reload()
// Basically, all that needs to be done is to re-call glTexImage2D with a
// new texture ID, so that's what we do here
p_glActiveTexture(GL_TEXTURE0);
p_glEnable(myTexTarget);
p_glGenTextures(1, &myTexID);
p_glBindTexture(myTexTarget, myTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@ -763,7 +1435,69 @@ void FBSurfaceGL::reload()
myTexWidth, myTexHeight, 0,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
p_glEnable(myTexTarget);
// Do the same for the TV filter textures
// Only do this if TV filters are enabled
if(myTvFiltersEnabled)
{
// Generate the generic filter texture
p_glGenTextures(1, &myFilterTexID);
p_glBindTexture(myTexTarget, myFilterTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Make the initial texture, this will get overwritten later
p_glCopyTexImage2D(myTexTarget, 0, GL_RGB5, 0, 0, myFilterTexWidth, myFilterTexHeight, 0);
// Only do this if TV and color texture filters are enabled
if(myFB.myUseTexture)
{
// Generate the subpixel mask texture
p_glGenTextures(1, &mySubMaskTexID);
p_glBindTexture(myTexTarget, mySubMaskTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Write the data
p_glTexImage2D(myTexTarget, 0, GL_RGB5,
myFilterTexWidth, myFilterTexHeight, 0,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, mySubpixelTexture->pixels);
}
// Only do this if TV and noise filters are enabled
if(myFB.myUseNoise)
{
// Generate the noise mask textures
p_glGenTextures(myNoiseNum, myNoiseMaskTexID);
for(int i = 0; i < myNoiseNum; i++)
{
p_glBindTexture(myTexTarget, myNoiseMaskTexID[i]);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Write the data
p_glTexImage2D(myTexTarget, 0, GL_RGB5,
myFilterTexWidth, myFilterTexHeight, 0,
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myNoiseTexture[i]->pixels);
}
}
// Only do this if TV and phosphor filters are enabled
if(myFB.myUseGLPhosphor)
{
// Generate the noise mask textures
p_glGenTextures(1, &myPhosphorTexID);
p_glBindTexture(myTexTarget, myPhosphorTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Make the initial texture, this will get overwritten later
p_glCopyTexImage2D(myTexTarget, 0, GL_RGB5, 0, 0, myFilterTexWidth, myFilterTexHeight, 0);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -780,10 +1514,130 @@ void FBSurfaceGL::setFilter(const string& name)
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
// Do the same for the filter textures
// Only do this if TV filters are enabled
if(myTvFiltersEnabled)
{
p_glBindTexture(myTexTarget, myFilterTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
// Only do this if TV and color texture filters are enabled
if(myFB.myUseTexture)
{
p_glBindTexture(myTexTarget, mySubMaskTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
}
// Only do this if TV and noise filters are enabled
if(myFB.myUseNoise)
{
for(int i = 0; i < myNoiseNum; i++)
{
p_glBindTexture(myTexTarget, myNoiseMaskTexID[i]);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
}
}
// Only do this if TV and phosphor filters are enabled
if(myFB.myUseGLPhosphor)
{
p_glBindTexture(myTexTarget, myPhosphorTexID);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
}
}
// The filtering has changed, so redraw the entire screen
mySurfaceIsDirty = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GLuint FBSurfaceGL::genShader(ShaderType type)
{
string fFile = "";
char* fCode = NULL;
switch(type)
{
case SHADER_BLEED:
fFile = "bleed.frag";
fCode = (char*)GLShader::bleed_frag[0];
break;
case SHADER_TEX:
fFile = "texture.frag";
fCode = (char*)GLShader::texture_frag[0];
break;
case SHADER_NOISE:
fFile = "noise.frag";
fCode = (char*)GLShader::noise_frag[0];
break;
case SHADER_PHOS:
fFile = "phosphor.frag";
fCode = (char*)GLShader::phosphor_frag[0];
break;
case SHADER_TEXNOISE:
fFile = "texture_noise.frag";
fCode = (char*)GLShader::texture_noise_frag[0];
break;
}
// First try opening an external fragment file
// These shader files are stored in 'BASEDIR/shaders/'
char* buffer = NULL;
const string& filename =
myFB.myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "shaders" +
BSPF_PATH_SEPARATOR + fFile;
ifstream in(filename.c_str());
if(in && in.is_open())
{
// Get file size
in.seekg(0, std::ios::end);
streampos size = in.tellg();
// Reset position
in.seekg(0);
// Make buffer of proper size;
buffer = new char[size+(streampos)1]; // +1 for '\0'
// Read in file
in.read(buffer, size);
buffer[in.gcount()] = '\0';
in.close();
fCode = buffer;
}
// Make the shader program
GLuint fShader = p_glCreateShader(GL_FRAGMENT_SHADER);
GLuint program = p_glCreateProgram();
p_glShaderSource(fShader, 1, (const char**)&fCode, NULL);
p_glCompileShader(fShader);
p_glAttachShader(program, fShader);
p_glLinkProgram(program);
// Go ahead and flag the shader for deletion so it is deleted once the program is
p_glDeleteShader(fShader);
// Clean up
delete[] buffer;
return program;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float FrameBufferGL::myGLVersion = 0.0;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferGL::myLibraryLoaded = false;

View File

@ -156,6 +156,11 @@ class FrameBufferGL : public FrameBuffer
private:
bool loadFuncs();
/**
Enable/disable texture effect.
*/
void enableTexture(bool enable);
private:
// The lower-most base surface (will always be a TIA surface,
// since Dialog surfaces are allocated by the Dialog class directly).
@ -179,6 +184,30 @@ class FrameBufferGL : public FrameBuffer
// Indicates that the texture has been modified, and should be redrawn
bool myDirtyFlag;
// Indicates whether or not color bleed filter is enabled
bool myUseBleed;
// Indicates the quality of the color bleed filter to use
int myBleedQuality;
// Indicates whether or not color texture filter is enabled
bool myUseTexture;
// Indicates whetehr or not color texture filter is staggered
bool myTextureStag;
// Indicates whether or not the noise filter is enabled
bool myUseNoise;
// Indicates the quality of the noise filter to use
int myNoiseQuality;
// Indicates whether or not the phosphor filter is enabled
bool myUseGLPhosphor;
// Indicates the OpenGL version found (0 indicates none)
static float myGLVersion;
// Indicates if the OpenGL library has been properly loaded
static bool myLibraryLoaded;
};
@ -196,7 +225,8 @@ class FBSurfaceGL : public FBSurface
public:
FBSurfaceGL(FrameBufferGL& buffer,
uInt32 baseWidth, uInt32 baseHeight,
uInt32 scaleWidth, uInt32 scaleHeight);
uInt32 scaleWidth, uInt32 scaleHeight,
bool allowFiltering = false);
virtual ~FBSurfaceGL();
void hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color);
@ -221,6 +251,54 @@ class FBSurfaceGL : public FBSurface
private:
void setFilter(const string& name);
/**
This method generates an OpenGL shader program from a fragment shader.
@param fragment The filename of the fragment shader (not including location)
@return The generated shader program
*/
enum ShaderType {
SHADER_BLEED, SHADER_TEX, SHADER_NOISE, SHADER_PHOS, SHADER_TEXNOISE
};
GLuint genShader(ShaderType type);
/**
This method performs the final steps of rendering a single texture filter:
passing the previously rendered screen to the given program and drawing
to the screen. It does not include setting the program through
p_glUseProgram() because this needs to be done before the custom program
variables are set.
@param program The program to use to render the filter
@param firstRender True if this is the first render for this frame, false if not
*/
void renderTexture(GLuint program, bool firstRender);
/**
This method performs the final steps of rendering a two-texture filter:
passing the previously rendered screen to the given program and drawing
the previous texture and mask texture to the screen. It does not include
setting the program through p_glUseProgram() because this needs to be
done before the mask texture and custom program variables are set.
@param program The program to use to render the filter
@param firstRender True if this is the first render for this frame, false if not
*/
void renderTwoTexture(GLuint program, bool firstRender);
/**
This method performs the final steps of rendering a three-texture filter:
passing the previously rendered screen to the given program and drawing
the previous texture and two mask textures to the screen. It does not include
setting the program through p_glUseProgram() because this needs to be
done before the mask texture and custom program variables are set.
@param program The program to use to render the filter
@param firstRender True if this is the first render for this frame, false if not
*/
void renderThreeTexture(GLuint program, bool firstRender);
inline void* pixels() const { return myTexture->pixels; }
inline uInt32 pitch() const { return myPitch; }
@ -242,9 +320,43 @@ class FBSurfaceGL : public FBSurface
GLsizei myTexHeight;
GLfloat myTexCoord[4];
// The filter texture is what is used to hold data from screen after one
// filter has been used. Needed since more than one filter is being used.
// The size and texture coordinates are also used for the other filter
// textures: mySubMaskTexID and myNoiseTexID
GLuint myFilterTexID;
GLsizei myFilterTexWidth;
GLsizei myFilterTexHeight;
GLfloat myFilterTexCoord[4];
// The subpixel texture used for the texture filter
GLuint mySubMaskTexID;
// The noise textures used for the noise filter
GLuint* myNoiseMaskTexID;
// The past texture used for the phosphor filter
GLuint myPhosphorTexID;
// Surface for the subpixel texture filter mask
SDL_Surface* mySubpixelTexture;
// Surfaces for noise filter mask (array of pointers)
SDL_Surface** myNoiseTexture;
uInt32 myXOrig, myYOrig, myWidth, myHeight;
bool mySurfaceIsDirty;
uInt32 myPitch;
// OpenGL shader programs
GLuint myBleedProgram; // Shader for color bleed filter
GLuint myTextureProgram; // Shader for color texture filter
GLuint myNoiseProgram; // Shader for noise filter
GLuint myPhosphorProgram; // Shader for the phosphor filter
GLuint myTextureNoiseProgram; // Shader for both color texture and noise filters
// Used to save the number of noise textures to use at game launch
int myNoiseNum;
// Specifies whether the TV filters can be applied to this surface
bool myTvFiltersEnabled;
};
#endif // DISPLAY_OPENGL

View File

@ -142,7 +142,6 @@ class FrameBufferSoft : public FrameBuffer
string about() const;
private:
int myZoomLevel;
int myBytesPerPixel;
int myBaseOffset;
int myPitch;

View File

@ -0,0 +1,133 @@
#ifndef GL_SHADER_PROGS_HXX
#define GL_SHADER_PROGS_HXX
/**
This code is generated using the 'create_shaders.pl' script,
located in the src/tools directory.
*/
namespace GLShader {
static const char* bleed_frag[] = {
"uniform sampler2D tex;\n"
"uniform float pH;\n"
"uniform float pW;\n"
"uniform float pWx2;\n"
"\n"
"void main()\n"
"{\n"
" // Save current color\n"
" vec4 current = texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t));\n"
"\n"
" // Box filter\n"
" // Comments for position are given in (x,y) coordinates with the current pixel as the origin\n"
" vec4 color = ( \n"
" // (-1,1)\n"
" texture2D(tex, vec2(gl_TexCoord[0].s-pW, gl_TexCoord[0].t+pH))\n"
" // (0,1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t+pH))\n"
" // (1,1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s+pW, gl_TexCoord[0].t+pH))\n"
" // (-1,0)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s-pW, gl_TexCoord[0].t))\n"
" // (0,0)\n"
" + current\n"
" // (1,0)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s+pW, gl_TexCoord[0].t))\n"
" // (-1,-1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s-pW, gl_TexCoord[0].t-pH))\n"
" // (0,-1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t-pH))\n"
" // (1,-1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s+pW, gl_TexCoord[0].t-pH))\n"
"\n"
" // Make it wider\n"
" // (-2,1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s-pWx2, gl_TexCoord[0].t+pH))\n"
" // (-2,0)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s-pWx2, gl_TexCoord[0].t))\n"
" // (-2,-1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s-pWx2, gl_TexCoord[0].t-pH))\n"
" // (2,1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s+pWx2, gl_TexCoord[0].t+pH))\n"
" // (2,0)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s+pWx2, gl_TexCoord[0].t))\n"
" // (2,-1)\n"
" + texture2D(tex, vec2(gl_TexCoord[0].s+pWx2, gl_TexCoord[0].t-pH))\n"
" ) / 15.0;\n"
"\n"
" // Make darker colors not bleed over lighter colors (act like light)\n"
" color = vec4(max(current.x, color.x), max(current.y, color.y), max(current.z, color.z), 1.0);\n"
"\n"
" gl_FragColor = color;\n"
"}\n"
"\0"
};
static const char* noise_frag[] = {
"uniform sampler2D tex;\n"
"uniform sampler2D mask;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor =\n"
" texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t))\n"
" + texture2D(mask, vec2(gl_TexCoord[1].s, gl_TexCoord[1].t))\n"
" ;\n"
"}\n"
"\0"
};
static const char* phosphor_frag[] = {
"uniform sampler2D tex;\n"
"uniform sampler2D mask;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor =\n"
" 0.65 * texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t))\n"
" + 0.35 * texture2D(mask, vec2(gl_TexCoord[1].s, gl_TexCoord[1].t))\n"
" ;\n"
"}\n"
"\0"
};
static const char* texture_frag[] = {
"uniform sampler2D tex;\n"
"uniform sampler2D mask;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor =\n"
" texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t))\n"
" * texture2D(mask, vec2(gl_TexCoord[1].s, gl_TexCoord[1].t))\n"
" * 1.05\n"
" + 0.07\n"
" ;\n"
"}\n"
"\0"
};
static const char* texture_noise_frag[] = {
"uniform sampler2D tex;\n"
"uniform sampler2D texMask;\n"
"uniform sampler2D noiseMask;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor =\n"
" // Texture part\n"
" texture2D(tex, vec2(gl_TexCoord[0].s, gl_TexCoord[0].t))\n"
" * texture2D(texMask, vec2(gl_TexCoord[1].s, gl_TexCoord[1].t))\n"
" * 1.05\n"
" + 0.07\n"
" // Noise part\n"
" + texture2D(noiseMask, vec2(gl_TexCoord[1].s, gl_TexCoord[1].t))\n"
" ;\n"
"}\n"
"\0"
};
} // namespace GLShader
#endif

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "Cart0840.hxx"
@ -25,10 +26,7 @@
Cartridge0840::Cartridge0840(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 8192; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 8192);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -149,7 +147,7 @@ void Cartridge0840::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank
@ -181,10 +179,7 @@ int Cartridge0840::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge0840::patch(uInt16 address, uInt8 value)
{
address &= 0x0fff;
myImage[myCurrentBank * 4096] = value;
bank(myCurrentBank); // TODO: see if this is really necessary
myImage[(myCurrentBank << 12) + (address & 0x0fff)] = value;
return true;
}

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "Cart2K.hxx"
@ -25,10 +26,7 @@
Cartridge2K::Cartridge2K(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 2048; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 2048);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -97,9 +97,9 @@ uInt8 Cartridge3E::peek(uInt16 address)
if(address < 0x0800)
{
if(myCurrentBank < 256)
return myImage[(address & 0x07FF) + myCurrentBank * 2048];
return myImage[(address & 0x07FF) + (myCurrentBank << 11)];
else
return myRam[(address & 0x03FF) + (myCurrentBank - 256) * 1024];
return myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)];
}
else
{
@ -138,7 +138,7 @@ void Cartridge3E::bank(uInt16 bank)
if(bank < 256)
{
// Make sure the bank they're asking for is reasonable
if((uInt32)bank * 2048 < mySize)
if(((uInt32)bank << 11) < uInt32(mySize))
{
myCurrentBank = bank;
}
@ -146,10 +146,10 @@ void Cartridge3E::bank(uInt16 bank)
{
// Oops, the bank they're asking for isn't valid so let's wrap it
// around to a valid bank number
myCurrentBank = bank % (mySize / 2048);
myCurrentBank = bank % (mySize >> 11);
}
uInt32 offset = myCurrentBank * 2048;
uInt32 offset = myCurrentBank << 11;
uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank
@ -170,7 +170,7 @@ void Cartridge3E::bank(uInt16 bank)
bank %= 32;
myCurrentBank = bank + 256;
uInt32 offset = bank * 1024;
uInt32 offset = bank << 10;
uInt16 shift = mySystem->pageShift();
uInt32 address;
@ -217,9 +217,9 @@ bool Cartridge3E::patch(uInt16 address, uInt8 value)
if(address < 0x0800)
{
if(myCurrentBank < 256)
myImage[(address & 0x07FF) + myCurrentBank * 2048] = value;
myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value;
else
myRam[(address & 0x03FF) + (myCurrentBank - 256) * 1024] = value;
myRam[(address & 0x03FF) + ((myCurrentBank - 256) << 10)] = value;
}
else
myImage[(address & 0x07FF) + mySize - 2048] = value;

View File

@ -90,7 +90,7 @@ uInt8 Cartridge3F::peek(uInt16 address)
if(address < 0x0800)
{
return myImage[(address & 0x07FF) + myCurrentBank * 2048];
return myImage[(address & 0x07FF) + (myCurrentBank << 11)];
}
else
{
@ -122,7 +122,7 @@ void Cartridge3F::bank(uInt16 bank)
if(myBankLocked) return;
// Make sure the bank they're asking for is reasonable
if((uInt32)bank * 2048 < mySize)
if(((uInt32)bank << 11) < mySize)
{
myCurrentBank = bank;
}
@ -130,10 +130,10 @@ void Cartridge3F::bank(uInt16 bank)
{
// Oops, the bank they're asking for isn't valid so let's wrap it
// around to a valid bank number
myCurrentBank = bank % (mySize / 2048);
myCurrentBank = bank % (mySize >> 11);
}
uInt32 offset = myCurrentBank * 2048;
uInt32 offset = myCurrentBank << 11;
uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank
@ -158,7 +158,7 @@ int Cartridge3F::bank()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Cartridge3F::bankCount()
{
return mySize / 2048;
return mySize >> 11;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -167,7 +167,7 @@ bool Cartridge3F::patch(uInt16 address, uInt8 value)
address &= 0x0FFF;
if(address < 0x0800)
myImage[(address & 0x07FF) + myCurrentBank * 2048] = value;
myImage[(address & 0x07FF) + (myCurrentBank << 11)] = value;
else
myImage[(address & 0x07FF) + mySize - 2048] = value;

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "Cart4K.hxx"
@ -25,10 +26,7 @@
Cartridge4K::Cartridge4K(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 4096; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 4096);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -440,7 +440,7 @@ int CartridgeAR::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeAR::patch(uInt16 address, uInt8 value)
{
// myImage[address & 0x0FFF] = value;
// TODO - add support for debugger
return false;
}

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include <iostream>
#include "System.hxx"
@ -25,30 +26,19 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDPC::CartridgeDPC(const uInt8* image, uInt32 size)
{
uInt32 addr;
// Make a copy of the entire image as-is, for use by getImage()
// (this wastes 12K of RAM, should be controlled by a #ifdef)
for(addr = 0; addr < size; ++addr)
myImageCopy[addr] = image[addr];
memcpy(myImageCopy, image, size);
// Copy the program ROM image into my buffer
for(addr = 0; addr < 8192; ++addr)
{
myProgramImage[addr] = image[addr];
}
memcpy(myProgramImage, image, 8192);
// Copy the display ROM image into my buffer
for(addr = 0; addr < 2048; ++addr)
{
myDisplayImage[addr] = image[8192 + addr];
}
memcpy(myDisplayImage, image + 8192, 2048);
// Initialize the DPC data fetcher registers
for(uInt16 i = 0; i < 8; ++i)
{
myTops[i] = myBottoms[i] = myCounters[i] = myFlags[i] = 0;
}
// None of the data fetchers are in music mode
myMusicMode[0] = myMusicMode[1] = myMusicMode[2] = false;
@ -460,8 +450,7 @@ int CartridgeDPC::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDPC::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myProgramImage[myCurrentBank * 4096 + address] = value;
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -211,7 +211,7 @@ int CartridgeE0::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeE0::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
address &= 0x0FFF;
myImage[(myCurrentSlice[address >> 10] << 10) + (address & 0x03FF)] = value;
return true;
}

View File

@ -222,7 +222,6 @@ bool CartridgeE7::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[(myCurrentSlice[address >> 11] << 11) + (address & 0x07FF)] = value;
bank(myCurrentSlice[0]);
return true;
}

View File

@ -74,7 +74,7 @@ uInt8 CartridgeEF::peek(uInt16 address)
if((address >= 0x0FE0) && (address <= 0x0FEF))
bank(address - 0x0FE0);
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -94,7 +94,7 @@ void CartridgeEF::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -127,8 +127,7 @@ int CartridgeEF::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeEF::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -98,10 +98,16 @@ uInt8 CartridgeEFSC::peek(uInt16 address)
if((address >= 0x0FE0) && (address <= 0x0FEF))
bank(address - 0x0FE0);
// NOTE: This does not handle accessing RAM, however, this function
// should never be called for RAM because of the way page accessing
// has been setup
return myImage[myCurrentBank * 4096 + address];
// Reading from the write port triggers an unwanted write
// The value written to RAM is somewhat undefined, so we use 0
// Thanks to Kroko of AtariAge for this advice and code idea
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
{
if(myBankLocked) return 0;
else return myRAM[address] = 0;
}
else
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -125,7 +131,7 @@ void CartridgeEFSC::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -158,8 +164,7 @@ int CartridgeEFSC::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeEFSC::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "Random.hxx"
#include "System.hxx"
@ -26,10 +27,7 @@
CartridgeF4::CartridgeF4(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 32768; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 32768);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -79,7 +77,7 @@ uInt8 CartridgeF4::peek(uInt16 address)
bank(address - 0x0FF4);
}
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -101,7 +99,7 @@ void CartridgeF4::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -134,8 +132,7 @@ int CartridgeF4::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeF4::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -96,14 +96,24 @@ uInt8 CartridgeF4SC::peek(uInt16 address)
// Switch banks if necessary
if((address >= 0x0FF4) && (address <= 0x0FFB))
{
bank(address - 0x0FF4);
// Reading from the write port triggers an unwanted write
// The value written to RAM is somewhat undefined, so we use 0
// Thanks to Kroko of AtariAge for this advice and code idea
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
{
if(myBankLocked) return 0;
else return myRAM[address] = 0;
}
else
return myImage[(myCurrentBank << 12) + address];
// NOTE: This does not handle accessing RAM, however, this function
// should never be called for RAM because of the way page accessing
// has been setup
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -113,9 +123,7 @@ void CartridgeF4SC::poke(uInt16 address, uInt8)
// Switch banks if necessary
if((address >= 0x0FF4) && (address <= 0x0FFB))
{
bank(address - 0x0FF4);
}
// NOTE: This does not handle accessing RAM, however, this function
// should never be called for RAM because of the way page accessing
@ -129,7 +137,7 @@ void CartridgeF4SC::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -162,8 +170,7 @@ int CartridgeF4SC::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeF4SC::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "Serializer.hxx"
@ -27,10 +28,7 @@
CartridgeF6::CartridgeF6(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 16384; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 16384);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -101,7 +99,7 @@ uInt8 CartridgeF6::peek(uInt16 address)
break;
}
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -144,7 +142,7 @@ void CartridgeF6::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -177,8 +175,7 @@ int CartridgeF6::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeF6::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -121,10 +121,16 @@ uInt8 CartridgeF6SC::peek(uInt16 address)
break;
}
// NOTE: This does not handle accessing RAM, however, this function
// should never be called for RAM because of the way page accessing
// has been setup
return myImage[myCurrentBank * 4096 + address];
// Reading from the write port triggers an unwanted write
// The value written to RAM is somewhat undefined, so we use 0
// Thanks to Kroko of AtariAge for this advice and code idea
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
{
if(myBankLocked) return 0;
else return myRAM[address] = 0;
}
else
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -171,7 +177,7 @@ void CartridgeF6SC::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -204,8 +210,7 @@ int CartridgeF6SC::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeF6SC::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -91,7 +91,7 @@ uInt8 CartridgeF8::peek(uInt16 address)
break;
}
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -124,7 +124,7 @@ void CartridgeF8::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -157,9 +157,7 @@ int CartridgeF8::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeF8::patch(uInt16 address, uInt8 value)
{
address &= 0xfff;
myImage[myCurrentBank * 4096 + address] = value;
bank(myCurrentBank);
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -111,10 +111,16 @@ uInt8 CartridgeF8SC::peek(uInt16 address)
break;
}
// NOTE: This does not handle accessing RAM, however, this function
// should never be called for RAM because of the way page accessing
// has been setup
return myImage[myCurrentBank * 4096 + address];
// Reading from the write port triggers an unwanted write
// The value written to RAM is somewhat undefined, so we use 0
// Thanks to Kroko of AtariAge for this advice and code idea
if(address < 0x0080) // Write port is at 0xF000 - 0xF080 (128 bytes)
{
if(myBankLocked) return 0;
else return myRAM[address] = 0;
}
else
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -184,8 +190,7 @@ int CartridgeF8SC::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeF8SC::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -117,14 +117,16 @@ uInt8 CartridgeFASC::peek(uInt16 address)
}
// Reading from the write port triggers an unwanted write
// The value written to RAM is somewhat undefined, so we use 0
// Thanks to Kroko of AtariAge for this advice and code idea
if(address < 0x0100) // Write port is at 0xF000 - 0xF100 (256 bytes)
{
return myRAM[address & 0x00FF] = 0;
if(myBankLocked) return 0;
else return myRAM[address] = 0;
}
else
{
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
}
@ -167,7 +169,7 @@ void CartridgeFASC::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -200,8 +202,7 @@ int CartridgeFASC::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeFASC::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "CartFE.hxx"
@ -25,10 +26,7 @@
CartridgeFE::CartridgeFE(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 8192; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 8192);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "CartMB.hxx"
@ -25,10 +26,7 @@
CartridgeMB::CartridgeMB(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 65536; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 65536);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -75,9 +73,10 @@ uInt8 CartridgeMB::peek(uInt16 address)
address &= 0x0FFF;
// Switch to next bank
if(address == 0x0FF0) incbank();
if(address == 0x0FF0)
incbank();
return myImage[myCurrentBank * 4096 + address];
return myImage[(myCurrentBank << 12) + address];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -86,7 +85,8 @@ void CartridgeMB::poke(uInt16 address, uInt8)
address &= 0x0FFF;
// Switch to next bank
if(address == 0x0FF0) incbank();
if(address == 0x0FF0)
incbank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -97,7 +97,7 @@ void CartridgeMB::incbank()
// Remember what bank we're in
myCurrentBank ++;
myCurrentBank &= 0x0F;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
uInt16 mask = mySystem->pageMask();
@ -120,7 +120,7 @@ void CartridgeMB::bank(uInt16 bank)
{
if(myBankLocked) return;
myCurrentBank = (bank - 1);
myCurrentBank = bank - 1;
incbank();
}
@ -139,8 +139,7 @@ int CartridgeMB::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeMB::patch(uInt16 address, uInt8 value)
{
address = address & 0x0FFF;
myImage[myCurrentBank * 4096 + address] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -128,7 +128,7 @@ uInt8 CartridgeMC::peek(uInt16 address)
if(block & 0x80)
{
// ROM access
return myImage[(uInt32)(block & 0x7F) * 1024 + (address & 0x03FF)];
return myImage[(uInt32)((block & 0x7F) << 10) + (address & 0x03FF)];
}
else
{
@ -136,12 +136,12 @@ uInt8 CartridgeMC::peek(uInt16 address)
if(address & 0x0200)
{
// Reading from the read port of the RAM block
return myRAM[(uInt32)(block & 0x3F) * 512 + (address & 0x01FF)];
return myRAM[(uInt32)((block & 0x3F) << 9) + (address & 0x01FF)];
}
else
{
// Oops, reading from the write port of the RAM block!
myRAM[(uInt32)(block & 0x3F) * 512 + (address & 0x01FF)] = 0;
myRAM[(uInt32)((block & 0x3F) << 9) + (address & 0x01FF)] = 0;
return 0;
}
}
@ -188,7 +188,7 @@ void CartridgeMC::poke(uInt16 address, uInt8 value)
if(!(block & 0x80) && !(address & 0x0200))
{
// Handle the write to RAM
myRAM[(uInt32)(block & 0x3F) * 512 + (address & 0x01FF)] = value;
myRAM[(uInt32)((block & 0x3F) << 9) + (address & 0x01FF)] = value;
}
}
}

View File

@ -17,6 +17,7 @@
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "CartSB.hxx"
@ -30,8 +31,7 @@ CartridgeSB::CartridgeSB(const uInt8* image, uInt32 size)
myImage = new uInt8[mySize];
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < mySize; ++addr)
myImage[addr] = image[addr];
memcpy(myImage, image, mySize);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -127,7 +127,7 @@ void CartridgeSB::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt32 offset = myCurrentBank * 4096;
uInt32 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank
@ -158,9 +158,7 @@ int CartridgeSB::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeSB::patch(uInt16 address, uInt8 value)
{
address &= 0x0fff;
myImage[myCurrentBank * 4096] = value;
bank(myCurrentBank); // TODO: see if this is really necessary
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -70,6 +70,8 @@ void CartridgeUA::install(System& system)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeUA::peek(uInt16 address)
{
// address &= 0x1FFF; TODO - is this needed here?
// Switch banks if necessary
switch(address)
{
@ -132,9 +134,8 @@ void CartridgeUA::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank * 4096;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
// uInt16 mask = mySystem->pageMask();
// Setup the page access methods for the current bank
System::PageAccess access;
@ -164,8 +165,7 @@ int CartridgeUA::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeUA::patch(uInt16 address, uInt8 value)
{
address &= 0x0fff;
myImage[myCurrentBank * 4096] = value;
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -13,10 +13,11 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id:
// $Id$
//============================================================================
#include <cassert>
#include <cstring>
#include "System.hxx"
#include "M6532.hxx"
@ -27,10 +28,7 @@
CartridgeX07::CartridgeX07(const uInt8* image)
{
// Copy the ROM image into my buffer
for(uInt32 addr = 0; addr < 65536; ++addr)
{
myImage[addr] = image[addr];
}
memcpy(myImage, image, 65536);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -84,7 +82,8 @@ uInt8 CartridgeX07::peek(uInt16 address)
value = mySystem->tia().peek(address);
// Switch banks if necessary
if((address & 0x180f) == 0x080d) bank((address & 0xf0) >> 4);
if((address & 0x180f) == 0x080d)
bank((address & 0xf0) >> 4);
else if((address & 0x1880) == 0)
{
if((myCurrentBank & 0xe) == 0xe)
@ -105,7 +104,8 @@ void CartridgeX07::poke(uInt16 address, uInt8 value)
mySystem->tia().poke(address, value);
// Switch banks if necessary
if((address & 0x180f) == 0x080d) bank((address & 0xf0) >> 4);
if((address & 0x180f) == 0x080d)
bank((address & 0xf0) >> 4);
else if((address & 0x1880) == 0)
{
if((myCurrentBank & 0xe) == 0xe)
@ -120,7 +120,7 @@ void CartridgeX07::bank(uInt16 bank)
// Remember what bank we're in
myCurrentBank = (bank & 0x0f);
uInt32 offset = myCurrentBank * 4096;
uInt32 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank
@ -151,9 +151,7 @@ int CartridgeX07::bankCount()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeX07::patch(uInt16 address, uInt8 value)
{
address &= 0x0fff;
myImage[myCurrentBank * 4096] = value;
bank(myCurrentBank); // TODO: see if this is really necessary
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return true;
}

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id:
// $Id$
//============================================================================
#ifndef CARTRIDGEX07_HXX
@ -39,7 +39,7 @@ class System;
Note that the latter will hit on almost any TIA access.
@author Eckhard Stolberg
@version $Id:
@version $Id$
*/
class CartridgeX07 : public Cartridge
{

View File

@ -270,6 +270,11 @@ class FrameBuffer
*/
void stateChanged(EventHandler::State state);
/**
Get the zoom level.
*/
uInt32 getZoomLevel() { return myZoomLevel; }
//////////////////////////////////////////////////////////////////////
// The following methods are system-specific and must be implemented
// in derived classes.
@ -436,6 +441,9 @@ class FrameBuffer
// Names of the TIA filters that can be used for this framebuffer
StringMap myTIAFilters;
// Holds the zoom level being used
uInt32 myZoomLevel;
private:
/**
Set the icon for the main SDL window.

View File

@ -58,6 +58,13 @@ Settings::Settings(OSystem* osystem)
setInternal("colorloss", "false");
setInternal("timing", "sleep");
// TV filter options
setInternal("tv_tex", "off");
setInternal("tv_bleed", "off");
setInternal("tv_noise", "off");
// setInternal("tv_curve", "false"); // not yet implemented
setInternal("tv_phos", "false");
// Sound options
setInternal("sound", "true");
setInternal("fragsize", "512");
@ -218,50 +225,37 @@ void Settings::validate()
int i;
s = getString("video");
if(s != "soft" && s != "gl")
setInternal("video", "soft");
if(s != "soft" && s != "gl") setInternal("video", "soft");
s = getString("timing");
if(s != "sleep" && s != "busy")
setInternal("timing", "sleep");
if(s != "sleep" && s != "busy") setInternal("timing", "sleep");
#ifdef DISPLAY_OPENGL
s = getString("gl_filter");
if(s != "linear" && s != "nearest")
setInternal("gl_filter", "nearest");
if(s != "linear" && s != "nearest") setInternal("gl_filter", "nearest");
i = getInt("gl_aspectn");
if(i < 80 || i > 120)
setInternal("gl_aspectn", "100");
if(i < 80 || i > 120) setInternal("gl_aspectn", "100");
i = getInt("gl_aspectp");
if(i < 80 || i > 120)
setInternal("gl_aspectp", "100");
if(i < 80 || i > 120) setInternal("gl_aspectp", "100");
#endif
#ifdef SOUND_SUPPORT
i = getInt("volume");
if(i < 0 || i > 100)
setInternal("volume", "100");
if(i < 0 || i > 100) setInternal("volume", "100");
i = getInt("freq");
if(i < 0 || i > 48000)
setInternal("freq", "31400");
if(i < 0 || i > 48000) setInternal("freq", "31400");
i = getInt("tiafreq");
if(i < 0 || i > 48000)
setInternal("tiafreq", "31400");
if(i < 0 || i > 48000) setInternal("tiafreq", "31400");
#endif
i = getInt("joydeadzone");
if(i < 0)
setInternal("joydeadzone", "0");
else if(i > 29)
setInternal("joydeadzone", "29");
if(i < 0) setInternal("joydeadzone", "0");
else if(i > 29) setInternal("joydeadzone", "29");
i = getInt("pspeed");
if(i < 1)
setInternal("pspeed", "1");
else if(i > 15)
setInternal("pspeed", "15");
if(i < 1) setInternal("pspeed", "1");
else if(i > 15) setInternal("pspeed", "15");
s = getString("palette");
if(s != "standard" && s != "z26" && s != "user")
@ -272,10 +266,20 @@ void Settings::validate()
setInternal("launcherfont", "medium");
i = getInt("romviewer");
if(i < 0)
setInternal("romviewer", "0");
else if(i > 2)
setInternal("romviewer", "2");
if(i < 0) setInternal("romviewer", "0");
else if(i > 2) setInternal("romviewer", "2");
s = getString("tv_tex");
if(s != "normal" && s != "stag")
setInternal("tv_tex", "off");
s = getString("tv_bleed");
if(s != "low" && s != "medium" && s != "high")
setInternal("tv_bleed", "off");
s = getString("tv_noise");
if(s != "low" && s != "medium" && s != "high")
setInternal("tv_noise", "off");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -306,6 +310,18 @@ void Settings::usage()
<< " -gl_vsync <1|0> Enable synchronize to vertical blank interrupt\n"
<< " -gl_texrect <1|0> Enable GL_TEXTURE_RECTANGLE extension\n"
// << " -gl_accel <1|0> Enable SDL_GL_ACCELERATED_VISUAL\n"
<< " -tv_tex <off|type> TV texturing, type is one of the following:\n"
<< " normal TODO - document\n"
<< " stag TODO - document\n"
<< " -tv_bleed <off|type> TV color bleed, type is one of the following:\n"
<< " low TODO - document\n"
<< " medium TODO - document\n"
<< " high TODO - document\n"
<< " -tv_noise <off|type> TV noise, type is one of the following:\n"
<< " low TODO - document\n"
<< " medium TODO - document\n"
<< " high TODO - document\n"
<< " -tv_phos <1|0> TV phosphor burn-off\n"
<< endl
#endif
<< " -tia_filter <filter> Use the specified filter in emulation mode\n"
@ -339,7 +355,7 @@ void Settings::usage()
<< " -audiofirst <1|0> Initial audio before video (required for some ATI video cards)\n"
<< " -ssdir <path> The directory to save snapshot files to\n"
<< " -sssingle <1|0> Generate single snapshot instead of many\n"
<< " -ss1x <1|0> Generate TIA snapshot in 1x mode (ignore scaling)\n"
<< " -ss1x <1|0> Generate TIA snapshot in 1x mode (ignore scaling/effects)\n"
<< endl
<< " -rominfo <rom> Display detailed information for the given ROM\n"
<< " -listrominfo Display contents of stella.pro, one line per ROM entry\n"
@ -347,7 +363,7 @@ void Settings::usage()
<< " -launcherfont <small|medium| Use the specified font in the ROM launcher\n"
<< " large>\n"
<< " -launcherexts <allfiles| Show files with the given extensions in ROM launcher\n"
<< " allroms| (exts is a ':' separated list of extensions\n"
<< " allroms| (exts is a ':' separated list of extensions)\n"
<< " exts\n"
<< " -romviewer <0|1|2> Show ROM info viewer at given zoom level in ROM launcher (0 for off)\n"
<< " -uipalette <1|2> Used the specified palette for UI elements\n"

View File

@ -55,10 +55,11 @@ M6502::M6502(uInt32 systemCyclesPerProcessorCycle)
mySystemCyclesPerProcessorCycle;
}
#ifdef DEBUG_OUTPUT
debugStream << "( Fm Ln Cyc Clk) ( P0 P1 M0 M1 BL) "
<< "flags A X Y SP Code Disasm" << endl
<< endl;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -33,6 +33,7 @@
#include "Settings.hxx"
#include "StringList.hxx"
#include "Widget.hxx"
#include "TabWidget.hxx"
#include "VideoDialog.hxx"
@ -46,7 +47,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
fontHeight = font.getFontHeight(),
buttonWidth = font.getStringWidth("Defaults") + 20,
buttonHeight = font.getLineHeight() + 4;
int xpos, ypos;
int xpos, ypos, tabID;
int lwidth = font.getStringWidth("GL Aspect (P): "),
pwidth = font.getStringWidth("1920x1200"),
fwidth = font.getStringWidth("Renderer: ");
@ -54,15 +55,24 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
StringMap items;
// Set real dimensions
_w = 48 * fontWidth + 10;
_h = 13 * (lineHeight + 4) + 10;
_w = 49 * fontWidth + 10;
_h = 15 * (lineHeight + 4) + 10;
xpos = 5; ypos = 10;
// The tab widget
xpos = ypos = 5;
myTab = new TabWidget(this, font, xpos, ypos, _w - 2*xpos, _h - buttonHeight - 20);
addTabWidget(myTab);
addFocusWidget(myTab);
//////////////////////////////////////////////////////////
// 1) General options
wid.clear();
tabID = myTab->addTab(" General ");
// Video renderer
new StaticTextWidget(this, font, xpos + (lwidth-fwidth), ypos, fwidth,
new StaticTextWidget(myTab, font, xpos + (lwidth-fwidth), ypos, fwidth,
fontHeight, "Renderer:", kTextAlignLeft);
myRenderer = new EditTextWidget(this, font, xpos+lwidth, ypos,
myRenderer = new EditTextWidget(myTab, font, xpos+lwidth, ypos,
pwidth, fontHeight, "");
ypos += lineHeight + 4;
@ -71,14 +81,14 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
#ifdef DISPLAY_OPENGL
items.push_back("OpenGL", "gl");
#endif
myRendererPopup = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight,
myRendererPopup = new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
items, "(*) ", lwidth);
wid.push_back(myRendererPopup);
ypos += lineHeight + 4;
// TIA filters (will be dynamically filled later)
items.clear();
myTIAFilterPopup = new PopUpWidget(this, font, xpos, ypos, pwidth,
myTIAFilterPopup = new PopUpWidget(myTab, font, xpos, ypos, pwidth,
lineHeight, items, "TIA Filter: ", lwidth);
wid.push_back(myTIAFilterPopup);
ypos += lineHeight + 4;
@ -88,7 +98,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
items.push_back("Standard", "standard");
items.push_back("Z26", "z26");
items.push_back("User", "user");
myTIAPalettePopup = new PopUpWidget(this, font, xpos, ypos, pwidth,
myTIAPalettePopup = new PopUpWidget(myTab, font, xpos, ypos, pwidth,
lineHeight, items, "TIA Palette: ", lwidth);
wid.push_back(myTIAPalettePopup);
ypos += lineHeight + 4;
@ -99,7 +109,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
for(uInt32 i = 0; i < instance().supportedResolutions().size(); ++i)
items.push_back(instance().supportedResolutions()[i].name,
instance().supportedResolutions()[i].name);
myFSResPopup = new PopUpWidget(this, font, xpos, ypos, pwidth,
myFSResPopup = new PopUpWidget(myTab, font, xpos, ypos, pwidth,
lineHeight, items, "FS Res: ", lwidth);
wid.push_back(myFSResPopup);
ypos += lineHeight + 4;
@ -108,7 +118,7 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
items.clear();
items.push_back("Sleep", "sleep");
items.push_back("Busy-wait", "busy");
myFrameTimingPopup = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight,
myFrameTimingPopup = new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
items, "Timing (*): ", lwidth);
wid.push_back(myFrameTimingPopup);
ypos += lineHeight + 4;
@ -117,100 +127,160 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
items.clear();
items.push_back("Linear", "linear");
items.push_back("Nearest", "nearest");
myGLFilterPopup = new PopUpWidget(this, font, xpos, ypos, pwidth, lineHeight,
myGLFilterPopup = new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
items, "GL Filter: ", lwidth);
wid.push_back(myGLFilterPopup);
ypos += lineHeight + 4;
// GL aspect ratio (NTSC mode)
myNAspectRatioSlider =
new SliderWidget(this, font, xpos, ypos, pwidth, lineHeight,
new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
"GL Aspect (N): ", lwidth, kNAspectRatioChanged);
myNAspectRatioSlider->setMinValue(80); myNAspectRatioSlider->setMaxValue(120);
wid.push_back(myNAspectRatioSlider);
myNAspectRatioLabel =
new StaticTextWidget(this, font, xpos + myNAspectRatioSlider->getWidth() + 4,
new StaticTextWidget(myTab, font, xpos + myNAspectRatioSlider->getWidth() + 4,
ypos + 1, fontWidth * 3, fontHeight, "", kTextAlignLeft);
myNAspectRatioLabel->setFlags(WIDGET_CLEARBG);
ypos += lineHeight + 4;
// GL aspect ratio (PAL mode)
myPAspectRatioSlider =
new SliderWidget(this, font, xpos, ypos, pwidth, lineHeight,
new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
"GL Aspect (P): ", lwidth, kPAspectRatioChanged);
myPAspectRatioSlider->setMinValue(80); myPAspectRatioSlider->setMaxValue(120);
wid.push_back(myPAspectRatioSlider);
myPAspectRatioLabel =
new StaticTextWidget(this, font, xpos + myPAspectRatioSlider->getWidth() + 4,
new StaticTextWidget(myTab, font, xpos + myPAspectRatioSlider->getWidth() + 4,
ypos + 1, fontWidth * 3, fontHeight, "", kTextAlignLeft);
myPAspectRatioLabel->setFlags(WIDGET_CLEARBG);
ypos += lineHeight + 4;
// Framerate
myFrameRateSlider =
new SliderWidget(this, font, xpos, ypos, pwidth, lineHeight,
new SliderWidget(myTab, font, xpos, ypos, pwidth, lineHeight,
"Framerate: ", lwidth, kFrameRateChanged);
myFrameRateSlider->setMinValue(0); myFrameRateSlider->setMaxValue(300);
wid.push_back(myFrameRateSlider);
myFrameRateLabel =
new StaticTextWidget(this, font, xpos + myFrameRateSlider->getWidth() + 4,
new StaticTextWidget(myTab, font, xpos + myFrameRateSlider->getWidth() + 4,
ypos + 1, fontWidth * 3, fontHeight, "", kTextAlignLeft);
myFrameRateLabel->setFlags(WIDGET_CLEARBG);
// Add message concerning usage
ypos += (lineHeight + 4) * 2;
lwidth = font.getStringWidth("(*) Requires application restart");
new StaticTextWidget(myTab, font, 10, ypos, lwidth, fontHeight,
"(*) Requires application restart",
kTextAlignLeft);
// Move over to the next column
xpos += myNAspectRatioSlider->getWidth() + myNAspectRatioLabel->getWidth();
xpos += myNAspectRatioSlider->getWidth() + myNAspectRatioLabel->getWidth() + 10;
ypos = 10;
// Fullscreen
myFullscreenCheckbox = new CheckboxWidget(this, font, xpos, ypos,
myFullscreenCheckbox = new CheckboxWidget(myTab, font, xpos, ypos,
"Fullscreen mode", kFullScrChanged);
wid.push_back(myFullscreenCheckbox);
ypos += lineHeight + 4;
// PAL color-loss effect
myColorLossCheckbox = new CheckboxWidget(this, font, xpos, ypos,
myColorLossCheckbox = new CheckboxWidget(myTab, font, xpos, ypos,
"PAL color-loss");
wid.push_back(myColorLossCheckbox);
ypos += lineHeight + 4;
// GL FS stretch
myGLStretchCheckbox = new CheckboxWidget(this, font, xpos, ypos,
myGLStretchCheckbox = new CheckboxWidget(myTab, font, xpos, ypos,
"GL FS Stretch");
wid.push_back(myGLStretchCheckbox);
ypos += lineHeight + 4;
// Use sync to vblank in OpenGL
myUseVSyncCheckbox = new CheckboxWidget(this, font, xpos, ypos,
myUseVSyncCheckbox = new CheckboxWidget(myTab, font, xpos, ypos,
"GL VSync");
wid.push_back(myUseVSyncCheckbox);
ypos += lineHeight + 4;
// Grab mouse (in windowed mode)
myGrabmouseCheckbox = new CheckboxWidget(this, font, xpos, ypos,
myGrabmouseCheckbox = new CheckboxWidget(myTab, font, xpos, ypos,
"Grab mouse");
wid.push_back(myGrabmouseCheckbox);
ypos += lineHeight + 4;
// Center window (in windowed mode)
myCenterCheckbox = new CheckboxWidget(this, font, xpos, ypos,
myCenterCheckbox = new CheckboxWidget(myTab, font, xpos, ypos,
"Center window (*)");
wid.push_back(myCenterCheckbox);
ypos += lineHeight + 4;
// Add message concerning usage
lwidth = font.getStringWidth("(*) Requires application restart");
new StaticTextWidget(this, font, 10, _h - 2*buttonHeight - 10, lwidth, fontHeight,
"(*) Requires application restart",
kTextAlignLeft);
// Add items for tab 0
addToFocusList(wid, tabID);
//////////////////////////////////////////////////////////
// 2) TV effects options
wid.clear();
tabID = myTab->addTab(" TV Effects ");
xpos = ypos = 8;
lwidth = font.getStringWidth("TV Color Texture: ");
pwidth = font.getStringWidth("Staggered");
// Use TV color texture effect
items.clear();
items.push_back("Off", "off");
items.push_back("Normal", "normal");
items.push_back("Staggered", "stag");
myTexturePopup =
new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight, items,
"TV Color Texture: ", lwidth);
wid.push_back(myTexturePopup);
ypos += lineHeight + 4;
// Use color bleed effect
items.clear();
items.push_back("Off", "off");
items.push_back("Low", "low");
items.push_back("Medium", "medium");
items.push_back("High", "high");
myBleedPopup =
new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight, items,
"TV Color Bleed: ", lwidth);
wid.push_back(myBleedPopup);
ypos += lineHeight + 4;
// Use image noise effect
items.clear();
items.push_back("Off", "off");
items.push_back("Low", "low");
items.push_back("Medium", "medium");
items.push_back("High", "high");
myNoisePopup =
new PopUpWidget(myTab, font, xpos, ypos, pwidth, lineHeight, items,
"TV Image Noise: ", lwidth);
wid.push_back(myNoisePopup);
ypos += lineHeight + 4;
// Use phosphor burn-off effect
ypos += 4;
myPhosphorCheckbox =
new CheckboxWidget(myTab, font, xpos, ypos, "TV Phosphor Burn-off");
wid.push_back(myPhosphorCheckbox);
ypos += lineHeight + 4;
// Add items for tab 2
addToFocusList(wid, tabID);
// Activate the first tab
myTab->setActiveTab(0);
// Add Defaults, OK and Cancel buttons
wid.clear();
ButtonWidget* b;
b = new ButtonWidget(this, font, 10, _h - buttonHeight - 10,
buttonWidth, buttonHeight, "Defaults", kDefaultsCmd);
wid.push_back(b);
addOKCancelBGroup(wid, font);
addToFocusList(wid);
addBGroupToFocusList(wid);
// Disable certain functions when we know they aren't present
#ifndef DISPLAY_GL
@ -221,6 +291,11 @@ VideoDialog::VideoDialog(OSystem* osystem, DialogContainer* parent,
myPAspectRatioLabel->clearFlags(WIDGET_ENABLED);
myGLStretchCheckbox->clearFlags(WIDGET_ENABLED);
myUseVSyncCheckbox->clearFlags(WIDGET_ENABLED);
myTexturePopup->clearFlags(WIDGET_ENABLED);
myBleedPopup->clearFlags(WIDGET_ENABLED);
myNoisePopup->clearFlags(WIDGET_ENABLED);
myPhosphorCheckbox->clearFlags(WIDGET_ENABLED);
#endif
#ifndef WINDOWED_SUPPORT
myFullscreenCheckbox->clearFlags(WIDGET_ENABLED);
@ -306,6 +381,24 @@ void VideoDialog::loadConfig()
// Center window
myCenterCheckbox->setState(instance().settings().getBool("center"));
// TV color texture effect
myTexturePopup->setSelected(instance().settings().getString("tv_tex"), "off");
myTexturePopup->setEnabled(gl);
// TV color bleed effect
myBleedPopup->setSelected(instance().settings().getString("tv_bleed"), "off");
myBleedPopup->setEnabled(gl);
// TV random noise effect
myNoisePopup->setSelected(instance().settings().getString("tv_noise"), "off");
myNoisePopup->setEnabled(gl);
// TV phosphor burn-off effect
myPhosphorCheckbox->setState(instance().settings().getBool("tv_phos"));
myPhosphorCheckbox->setEnabled(gl);
myTab->loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -362,6 +455,18 @@ void VideoDialog::saveConfig()
// Center window
instance().settings().setBool("center", myCenterCheckbox->getState());
// TV color texture effect
instance().settings().setString("tv_tex", myTexturePopup->getSelectedTag());
// TV color bleed effect
instance().settings().setString("tv_bleed", myBleedPopup->getSelectedTag());
// TV image noise effect
instance().settings().setString("tv_noise", myNoisePopup->getSelectedTag());
// TV phosphor burn-off effect
instance().settings().setBool("tv_phos", myPhosphorCheckbox->getState());
// Finally, issue a complete framebuffer re-initialization
instance().createFrameBuffer();
}
@ -390,6 +495,11 @@ void VideoDialog::setDefaults()
myGrabmouseCheckbox->setState(false);
myCenterCheckbox->setState(true);
myTexturePopup->setSelected("off", "");
myBleedPopup->setSelected("off", "");
myNoisePopup->setSelected("off", "");
myPhosphorCheckbox->setState(false);
// Make sure that mutually-exclusive items are not enabled at the same time
handleFullscreenChange(false);
}

View File

@ -23,12 +23,13 @@
#define VIDEO_DIALOG_HXX
class CommandSender;
class CheckboxWidget;
class DialogContainer;
class EditTextWidget;
class PopUpWidget;
class SliderWidget;
class StaticTextWidget;
class CheckboxWidget;
class TabWidget;
#include "OSystem.hxx"
#include "Dialog.hxx"
@ -49,6 +50,9 @@ class VideoDialog : public Dialog
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id);
private:
TabWidget* myTab;
// General options
EditTextWidget* myRenderer;
PopUpWidget* myRendererPopup;
PopUpWidget* myTIAFilterPopup;
@ -70,6 +74,12 @@ class VideoDialog : public Dialog
CheckboxWidget* myCenterCheckbox;
CheckboxWidget* myGrabmouseCheckbox;
// TV effects options
PopUpWidget* myTexturePopup;
PopUpWidget* myBleedPopup;
PopUpWidget* myNoisePopup;
CheckboxWidget* myPhosphorCheckbox;
enum {
kNAspectRatioChanged = 'VDan',
kPAspectRatioChanged = 'VDap',

52
src/tools/create_shaders.pl Executable file
View File

@ -0,0 +1,52 @@
#!/usr/bin/perl
use File::Basename;
usage() if @ARGV < 2;
$numfiles = @ARGV;
$outfile = $ARGV[$numfiles-1];
# Construct the output file in C++ format
# Walk the ARGV list and convert each item
open(OUTFILE, ">$outfile");
print OUTFILE "#ifndef GL_SHADER_PROGS_HXX\n";
print OUTFILE "#define GL_SHADER_PROGS_HXX\n";
print OUTFILE "\n";
print OUTFILE "/**\n";
print OUTFILE " This code is generated using the 'create_shaders.pl' script,\n";
print OUTFILE " located in the src/tools directory.\n";
print OUTFILE "*/\n";
print OUTFILE "\n";
print OUTFILE "namespace GLShader {\n\n";
for ($i = 0; $i < $numfiles - 1; $i++)
{
open(INFILE, "$ARGV[$i]");
($base,$path,$type) = fileparse($ARGV[$i]);
$base =~ s/\./_/g;
print OUTFILE "static const char* " . $base . "[] = {\n";
foreach $line (<INFILE>)
{
chomp($line);
print OUTFILE "\"" . $line . "\\n\"\n";
}
print OUTFILE "\"\\0\"\n";
print OUTFILE "};\n\n";
close(INFILE);
}
print OUTFILE "} // namespace GLShader\n\n";
print OUTFILE "#endif\n";
close(OUTFILE);
sub usage {
print "create_shaders.pl <shader programs> <OUTPUT C++ header>\n";
exit(0);
}