mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 05:45:38 +00:00
- Finished cleaning up and optimizing the 2D renderer.
- Fixed up color offset register support for new 2D rendering system. Now maintains 2 computed palettes for layers A/A' and B/B'. - Fixed a minor bug in InitPalette(); VRAM was not being typecast properly. - Fixed specular lighting bug that occurred on some OpenGL drivers because integers were not being interpreted as floats in the vertex shader. - Began to update copyright date in some files. - Graphics modules now use the C++-style names for C standard library headers (e.g., stdio.h -> cstdio) consistent with the rest of Supermodel.
This commit is contained in:
parent
0257f1f9e8
commit
453df4f5f5
|
@ -34,8 +34,8 @@
|
|||
* texture base coordinates are not re-decoded in two different places!
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include "Supermodel.h"
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -278,9 +278,12 @@
|
|||
* Where 'r', 'g', and 'b' appear to be signed 8-bit color offsets. Because
|
||||
* they exceed the color resolution of the palette, they must be scaled
|
||||
* appropriately.
|
||||
*
|
||||
* Color offset registers are handled in TileGen.cpp. Two palettes are computed
|
||||
* -- one for A/A' and another for B/B'. These are passed to the renderer.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include "Pkgs/glew.h"
|
||||
#include "Supermodel.h"
|
||||
#include "Graphics/Shaders2D.h" // fragment and vertex shaders
|
||||
|
@ -300,7 +303,7 @@
|
|||
******************************************************************************/
|
||||
|
||||
// Draw 4-bit tile line, no clipping performed
|
||||
void CRender2D::DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
|
||||
void CRender2D::DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine, const UINT32 *pal)
|
||||
{
|
||||
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||
unsigned palette; // color palette bits obtained from tile
|
||||
|
@ -326,8 +329,8 @@ void CRender2D::DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
|
|||
*buf++ = pal[((pattern>>0)&0xF) | palette];
|
||||
}
|
||||
|
||||
// Draw 8-bit tile line, clipped at left edge
|
||||
void CRender2D::DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
|
||||
// Draw 8-bit tile line, no clipping performed
|
||||
void CRender2D::DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine, const UINT32 *pal)
|
||||
{
|
||||
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||
unsigned palette; // color palette bits obtained from tile
|
||||
|
@ -379,8 +382,9 @@ void CRender2D::DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
|
|||
* for this layer.
|
||||
* hScrollTable Pointer to the line-by-line horizontal scroll value
|
||||
* table for this layer.
|
||||
* pal Palette to draw with.
|
||||
*/
|
||||
void CRender2D::DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTableBase)
|
||||
void CRender2D::DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTableBase, const UINT32 *pal)
|
||||
{
|
||||
|
||||
// Determine the layer color depth (4 or 8-bit pixels)
|
||||
|
@ -397,10 +401,10 @@ void CRender2D::DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTa
|
|||
for (int tx = 0; tx < 64; tx += 4)
|
||||
{
|
||||
// Little endian: offsets 0,1,2,3 become 1,0,3,2
|
||||
DrawTileLine4BitNoClip(dest, nameTable[1], vOffset); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[0], vOffset); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[3], vOffset); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[2], vOffset); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[1], vOffset, pal); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[0], vOffset, pal); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[3], vOffset, pal); dest += 8;
|
||||
DrawTileLine4BitNoClip(dest, nameTable[2], vOffset, pal); dest += 8;
|
||||
nameTable += 4; // next set of 4 tiles
|
||||
}
|
||||
}
|
||||
|
@ -408,41 +412,28 @@ void CRender2D::DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTa
|
|||
{
|
||||
for (int tx = 0; tx < 64; tx += 4)
|
||||
{
|
||||
DrawTileLine8BitNoClip(dest, nameTable[1], vOffset); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[0], vOffset); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[3], vOffset); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[2], vOffset); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[1], vOffset, pal); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[0], vOffset, pal); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[3], vOffset, pal); dest += 8;
|
||||
DrawTileLine8BitNoClip(dest, nameTable[2], vOffset, pal); dest += 8;
|
||||
nameTable += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bool isBottom)
|
||||
// Mix in the appropriate layer (add on top of current contents) with horizontal scrolling under control of the stencil mask
|
||||
static void MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bool isBottom, const UINT16 *hScrollTable, const UINT16 *maskTableLine, int hFullScroll, bool lineScrollMode)
|
||||
{
|
||||
/*
|
||||
* Mix in the appropriate layer under control of the stencil mask, applying
|
||||
* horizontal scrolling in theprocess
|
||||
*/
|
||||
|
||||
// Line scroll table
|
||||
const UINT16 *hScrollTable = (UINT16 *) &vram[(0xF6000+layerNum*0x400)/4];
|
||||
|
||||
// Load horizontal full-screen scroll values and scroll mode
|
||||
int hFullScroll = regs[0x60/4+layerNum]&0x3FF;
|
||||
bool lineScrollMode = regs[0x60/4+layerNum]&0x8000;
|
||||
|
||||
// Load horizontal scroll values
|
||||
// Determine horizontal scroll values
|
||||
int hScroll;
|
||||
if (lineScrollMode)
|
||||
hScroll = hScrollTable[y];
|
||||
else
|
||||
hScroll = hFullScroll;
|
||||
|
||||
// Get correct offset into mask table
|
||||
const UINT16 *maskTable = (UINT16 *) &vram[0xF7000/4];
|
||||
maskTable += 2*y;
|
||||
// Get correct mask table entry
|
||||
if (layerNum < 2) // little endian: layers A and A' use second word in each pair
|
||||
++maskTable;
|
||||
++maskTableLine;
|
||||
|
||||
// Figure out what mask bit should be to mix in this layer
|
||||
UINT16 doCopy;
|
||||
|
@ -452,13 +443,15 @@ void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bo
|
|||
doCopy = 0x8000; // copy primary layer when mask is set
|
||||
|
||||
// Mix first 60 tiles (4 at a time)
|
||||
UINT16 mask = *maskTable; // mask for this line (each bit covers 4 tiles)
|
||||
int i = hScroll&511; // line index (where to copy from)
|
||||
for (int tx = 0; tx < 60; tx += 4)
|
||||
UINT16 mask = *maskTableLine; // mask for this line (each bit covers 4 tiles)
|
||||
int i = hScroll&511; // line index (where to copy from)
|
||||
if (isBottom)
|
||||
{
|
||||
// If bottom layer, we can copy without worrying about transparency, and must also write blank values when this layer is not showing
|
||||
//TODO: move this test outside of loop
|
||||
if (isBottom)
|
||||
/*
|
||||
* Bottom layers can be copied in without worrying about transparency
|
||||
* but we must write blank values when layer is not showing.
|
||||
*/
|
||||
for (int tx = 0; tx < 60; tx += 4)
|
||||
{
|
||||
// Only copy pixels if the mask bit is appropriate for this layer type
|
||||
if ((mask&0x8000) == doCopy)
|
||||
|
@ -486,36 +479,11 @@ void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bo
|
|||
i &= 511; // wrap line boundaries
|
||||
dest += 32;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy while testing for transparencies
|
||||
if ((mask&0x8000) == doCopy)
|
||||
{
|
||||
UINT32 p;
|
||||
for (int k = 0; k < 32; k++)
|
||||
{
|
||||
i &= 511;
|
||||
p = src[i++];
|
||||
if ((p>>24) != 0) // opaque pixel, put it down
|
||||
*dest = p;
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 32;
|
||||
i &= 511;
|
||||
dest += 32;
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
// Mix last two tiles
|
||||
if (isBottom)
|
||||
{
|
||||
// Mix last two tiles
|
||||
if ((mask&0x8000) == doCopy)
|
||||
{
|
||||
for (int k = 0; k < 16; k++)
|
||||
|
@ -535,6 +503,34 @@ void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bo
|
|||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Subsequent layers must test for transparency while mixing.
|
||||
*/
|
||||
for (int tx = 0; tx < 60; tx += 4)
|
||||
{
|
||||
if ((mask&0x8000) == doCopy)
|
||||
{
|
||||
UINT32 p;
|
||||
for (int k = 0; k < 32; k++)
|
||||
{
|
||||
i &= 511;
|
||||
p = src[i++];
|
||||
if ((p>>24) != 0) // opaque pixel, put it down
|
||||
*dest = p;
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i += 32;
|
||||
i &= 511;
|
||||
dest += 32;
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
|
||||
if ((mask&0x8000) == doCopy)
|
||||
{
|
||||
UINT32 p;
|
||||
|
@ -552,64 +548,127 @@ void CRender2D::MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bo
|
|||
|
||||
void CRender2D::DrawTilemaps(UINT32 *destBottom, UINT32 *destTop)
|
||||
{
|
||||
// Base address of all 4 name tables
|
||||
const UINT16 *nameTableBase[4];
|
||||
nameTableBase[0] = (UINT16 *) &vram[(0xF8000+0*0x2000)/4]; // A
|
||||
nameTableBase[1] = (UINT16 *) &vram[(0xF8000+1*0x2000)/4]; // A'
|
||||
nameTableBase[2] = (UINT16 *) &vram[(0xF8000+2*0x2000)/4]; // B
|
||||
nameTableBase[3] = (UINT16 *) &vram[(0xF8000+3*0x2000)/4]; // B'
|
||||
/*
|
||||
* Precompute data needed for each layer
|
||||
*/
|
||||
const UINT16 *nameTableBase[4];
|
||||
const UINT16 *hScrollTable[4];
|
||||
const UINT16 *maskTableLine = (UINT16 *) &vram[0xF7000/4]; // start at line 0
|
||||
int hFullScroll[4];
|
||||
bool lineScrollMode[4];
|
||||
|
||||
// Render and mix each line
|
||||
for (int y = 0; y < 384; y++)
|
||||
for (int i = 0; i < 4; i++) // 0=A, 1=A', 2=B, 3=B'
|
||||
{
|
||||
// Draw each layer
|
||||
DrawLine(lineBuffer[0], 0, y, nameTableBase[0]);
|
||||
DrawLine(lineBuffer[1], 1, y, nameTableBase[1]);
|
||||
DrawLine(lineBuffer[2], 2, y, nameTableBase[2]);
|
||||
DrawLine(lineBuffer[3], 3, y, nameTableBase[3]);
|
||||
|
||||
//TODO: could probably further optimize: only have a single layer clear masked-out areas, then if alt. layer is being written to same place, don't bother worrying about transparencies if directly on top
|
||||
// Combine according to priority settings
|
||||
// NOTE: question mark indicates unobserved and therefore unknown
|
||||
switch ((regs[0x20/4]>>8)&0xF)
|
||||
{
|
||||
case 0x5: // top: A, B, A'? bottom: B'
|
||||
MixLine(destBottom, lineBuffer[3], 3, y, true);
|
||||
MixLine(destTop, lineBuffer[2], 2, y, true);
|
||||
MixLine(destTop, lineBuffer[0], 0, y, false);
|
||||
MixLine(destTop, lineBuffer[1], 1, y, false);
|
||||
break;
|
||||
case 0x9: // ? all layers on top but relative order unknown (Spikeout Final Edition, after first boss)
|
||||
memset(destBottom, 0, 496*sizeof(UINT32)); //TODO: use glClear(GL_COLOR_BUFFER_BIT) if there is no bottom layer
|
||||
MixLine(destTop, lineBuffer[2], 2, y, true);
|
||||
MixLine(destTop, lineBuffer[3], 3, y, false);
|
||||
MixLine(destTop, lineBuffer[1], 1, y, false);
|
||||
MixLine(destTop, lineBuffer[0], 0, y, false);
|
||||
break;
|
||||
case 0xF: // all on top
|
||||
memset(destBottom, 0, 496*sizeof(UINT32)); //TODO: use glClear(GL_COLOR_BUFFER_BIT) if there is no bottom layer
|
||||
MixLine(destTop, lineBuffer[2], 2, y, true);
|
||||
MixLine(destTop, lineBuffer[3], 3, y, false);
|
||||
MixLine(destTop, lineBuffer[0], 0, y, false);
|
||||
MixLine(destTop, lineBuffer[1], 1, y, false);
|
||||
break;
|
||||
case 0x7: // top: A, B bottom: A'?, B'
|
||||
MixLine(destBottom, lineBuffer[3], 3, y, true);
|
||||
MixLine(destBottom, lineBuffer[1], 1, y, false);
|
||||
MixLine(destTop, lineBuffer[2], 2, y, true);
|
||||
MixLine(destTop, lineBuffer[0], 0, y, false);
|
||||
break;
|
||||
default: // unknown, use A and A' on top, B and B' on the bottom
|
||||
MixLine(destBottom, lineBuffer[2], 2, y, true);
|
||||
MixLine(destBottom, lineBuffer[3], 3, y, false);
|
||||
MixLine(destTop, lineBuffer[0], 0, y, true);
|
||||
MixLine(destTop, lineBuffer[1], 1, y, false);
|
||||
break;
|
||||
}
|
||||
// Base of name table
|
||||
nameTableBase[i] = (UINT16 *) &vram[(0xF8000+i*0x2000)/4];
|
||||
|
||||
// Advance to next line in output surfaces
|
||||
destBottom += 496;
|
||||
destTop += 496;
|
||||
// Horizontal line scroll tables
|
||||
hScrollTable[i] = (UINT16 *) &vram[(0xF6000+i*0x400)/4];
|
||||
|
||||
// Load horizontal full-screen scroll values and scroll mode
|
||||
hFullScroll[i] = regs[0x60/4+i]&0x3FF;
|
||||
lineScrollMode[i] = regs[0x60/4+i]&0x8000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Precompute layer mixing order
|
||||
*/
|
||||
UINT32 *dest[4];
|
||||
const UINT32 *src[4];
|
||||
int sortedLayerNum[4];
|
||||
bool sortedIsBottom[4];
|
||||
const UINT16 *sortedHScrollTable[4];
|
||||
int sortedHFullScroll[4];
|
||||
bool sortedLineScrollMode[4];
|
||||
bool clearBottom; // when true, no layer assigned to bottom surface
|
||||
|
||||
switch ((regs[0x20/4]>>8)&0xF)
|
||||
{
|
||||
case 0x5: // top: A, B, A'? bottom: B'
|
||||
clearBottom = false;
|
||||
dest[0]=destBottom; src[0]=lineBuffer[3]; sortedLayerNum[0]=3; sortedIsBottom[0]=true; sortedHScrollTable[0] = hScrollTable[3]; sortedHFullScroll[0]=hFullScroll[3]; sortedLineScrollMode[0]=lineScrollMode[3];
|
||||
dest[1]=destTop; src[1]=lineBuffer[2]; sortedLayerNum[1]=2; sortedIsBottom[1]=true; sortedHScrollTable[1] = hScrollTable[2]; sortedHFullScroll[1]=hFullScroll[2]; sortedLineScrollMode[1]=lineScrollMode[2];
|
||||
dest[2]=destTop; src[2]=lineBuffer[0]; sortedLayerNum[2]=0; sortedIsBottom[2]=false; sortedHScrollTable[2] = hScrollTable[0]; sortedHFullScroll[2]=hFullScroll[0]; sortedLineScrollMode[2]=lineScrollMode[0];
|
||||
dest[3]=destTop; src[3]=lineBuffer[1]; sortedLayerNum[3]=1; sortedIsBottom[3]=false; sortedHScrollTable[3] = hScrollTable[1]; sortedHFullScroll[3]=hFullScroll[1]; sortedLineScrollMode[3]=lineScrollMode[1];
|
||||
break;
|
||||
case 0x9: // ? all layers on top but relative order unknown (Spikeout Final Edition, after first boss)
|
||||
clearBottom = true;
|
||||
dest[0]=destTop; src[0]=lineBuffer[2]; sortedLayerNum[0]=2; sortedIsBottom[0]=true; sortedHScrollTable[0] = hScrollTable[2]; sortedHFullScroll[0]=hFullScroll[2]; sortedLineScrollMode[0]=lineScrollMode[3];
|
||||
dest[1]=destTop; src[1]=lineBuffer[3]; sortedLayerNum[1]=3; sortedIsBottom[1]=false; sortedHScrollTable[1] = hScrollTable[3]; sortedHFullScroll[1]=hFullScroll[3]; sortedLineScrollMode[1]=lineScrollMode[2];
|
||||
dest[2]=destTop; src[2]=lineBuffer[1]; sortedLayerNum[2]=1; sortedIsBottom[2]=false; sortedHScrollTable[2] = hScrollTable[1]; sortedHFullScroll[2]=hFullScroll[1]; sortedLineScrollMode[2]=lineScrollMode[1];
|
||||
dest[3]=destTop; src[3]=lineBuffer[0]; sortedLayerNum[3]=0; sortedIsBottom[3]=false; sortedHScrollTable[3] = hScrollTable[0]; sortedHFullScroll[3]=hFullScroll[0]; sortedLineScrollMode[3]=lineScrollMode[0];
|
||||
break;
|
||||
case 0xF: // all on top
|
||||
clearBottom = true;
|
||||
dest[0]=destTop; src[0]=lineBuffer[2]; sortedLayerNum[0]=2; sortedIsBottom[0]=true; sortedHScrollTable[0] = hScrollTable[2]; sortedHFullScroll[0]=hFullScroll[2]; sortedLineScrollMode[0]=lineScrollMode[2];
|
||||
dest[1]=destTop; src[1]=lineBuffer[3]; sortedLayerNum[1]=3; sortedIsBottom[1]=false; sortedHScrollTable[1] = hScrollTable[3]; sortedHFullScroll[1]=hFullScroll[3]; sortedLineScrollMode[1]=lineScrollMode[3];
|
||||
dest[2]=destTop; src[2]=lineBuffer[0]; sortedLayerNum[2]=0; sortedIsBottom[2]=false; sortedHScrollTable[2] = hScrollTable[0]; sortedHFullScroll[2]=hFullScroll[0]; sortedLineScrollMode[2]=lineScrollMode[0];
|
||||
dest[3]=destTop; src[3]=lineBuffer[1]; sortedLayerNum[3]=1; sortedIsBottom[3]=false; sortedHScrollTable[3] = hScrollTable[1]; sortedHFullScroll[3]=hFullScroll[1]; sortedLineScrollMode[3]=lineScrollMode[1];
|
||||
break;
|
||||
case 0x7: // top: A, B bottom: A'?, B'
|
||||
clearBottom = false;
|
||||
dest[0]=destBottom; src[0]=lineBuffer[3]; sortedLayerNum[0]=3; sortedIsBottom[0]=true; sortedHScrollTable[0] = hScrollTable[3]; sortedHFullScroll[0]=hFullScroll[3]; sortedLineScrollMode[0]=lineScrollMode[3];
|
||||
dest[1]=destBottom; src[1]=lineBuffer[1]; sortedLayerNum[1]=1; sortedIsBottom[1]=false; sortedHScrollTable[1] = hScrollTable[1]; sortedHFullScroll[1]=hFullScroll[1]; sortedLineScrollMode[1]=lineScrollMode[1];
|
||||
dest[2]=destTop; src[2]=lineBuffer[2]; sortedLayerNum[2]=2; sortedIsBottom[2]=true; sortedHScrollTable[2] = hScrollTable[2]; sortedHFullScroll[2]=hFullScroll[2]; sortedLineScrollMode[2]=lineScrollMode[2];
|
||||
dest[3]=destTop; src[3]=lineBuffer[0]; sortedLayerNum[3]=0; sortedIsBottom[3]=false; sortedHScrollTable[3] = hScrollTable[0]; sortedHFullScroll[3]=hFullScroll[0]; sortedLineScrollMode[3]=lineScrollMode[0];
|
||||
break;
|
||||
default: // unknown, use A and A' on top, B and B' on the bottom
|
||||
clearBottom = false;
|
||||
dest[0]=destBottom; src[0]=lineBuffer[2]; sortedLayerNum[0]=2; sortedIsBottom[0]=true; sortedHScrollTable[0] = hScrollTable[2]; sortedHFullScroll[0]=hFullScroll[2]; sortedLineScrollMode[0]=lineScrollMode[2];
|
||||
dest[1]=destBottom; src[1]=lineBuffer[3]; sortedLayerNum[1]=3; sortedIsBottom[1]=false; sortedHScrollTable[1] = hScrollTable[3]; sortedHFullScroll[1]=hFullScroll[3]; sortedLineScrollMode[1]=lineScrollMode[3];
|
||||
dest[2]=destTop; src[2]=lineBuffer[0]; sortedLayerNum[2]=0; sortedIsBottom[2]=true; sortedHScrollTable[2] = hScrollTable[0]; sortedHFullScroll[2]=hFullScroll[0]; sortedLineScrollMode[2]=lineScrollMode[0];
|
||||
dest[3]=destTop; src[3]=lineBuffer[1]; sortedLayerNum[3]=1; sortedIsBottom[3]=false; sortedHScrollTable[3] = hScrollTable[1]; sortedHFullScroll[3]=hFullScroll[1]; sortedLineScrollMode[3]=lineScrollMode[1];
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Render and mix each line
|
||||
*/
|
||||
if (clearBottom)
|
||||
{
|
||||
for (int y = 0; y < 384; y++)
|
||||
{
|
||||
// Draw one scanline from each layer
|
||||
DrawLine(lineBuffer[0], 0, y, nameTableBase[0], pal[0]);
|
||||
DrawLine(lineBuffer[1], 1, y, nameTableBase[1], pal[0]);
|
||||
DrawLine(lineBuffer[2], 2, y, nameTableBase[2], pal[1]);
|
||||
DrawLine(lineBuffer[3], 3, y, nameTableBase[3], pal[1]);
|
||||
|
||||
// No bottom layer
|
||||
memset(destBottom, 0, 496*sizeof(UINT32));
|
||||
|
||||
// Mix the layers in the correct order
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
MixLine(dest[i], src[i], sortedLayerNum[i], y, sortedIsBottom[i], sortedHScrollTable[i], maskTableLine, sortedHFullScroll[i], sortedLineScrollMode[i]);
|
||||
dest[i] += 496; // next line
|
||||
}
|
||||
|
||||
// Next line in mask table
|
||||
maskTableLine += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = 0; y < 384; y++)
|
||||
{
|
||||
// Draw one scanline from each layer
|
||||
DrawLine(lineBuffer[0], 0, y, nameTableBase[0], pal[0]);
|
||||
DrawLine(lineBuffer[1], 1, y, nameTableBase[1], pal[0]);
|
||||
DrawLine(lineBuffer[2], 2, y, nameTableBase[2], pal[1]);
|
||||
DrawLine(lineBuffer[3], 3, y, nameTableBase[3], pal[1]);
|
||||
|
||||
// Mix the layers in the correct order
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
MixLine(dest[i], src[i], sortedLayerNum[i], y, sortedIsBottom[i], sortedHScrollTable[i], maskTableLine, sortedHFullScroll[i], sortedLineScrollMode[i]);
|
||||
dest[i] += 496; // next line
|
||||
}
|
||||
|
||||
// Next line in mask table
|
||||
maskTableLine += 2;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -624,11 +683,8 @@ void CRender2D::DisplaySurface(int surface, GLfloat z)
|
|||
// If bottom surface and wide screen, clear overscan areas
|
||||
if (surface && g_Config.wideScreen)
|
||||
{
|
||||
UINT32 c = pal[0]; // just use palette color 0 for now (not the best solution, it's usually black)
|
||||
GLfloat r = (GLfloat)(c&0xFF) / 255.0f;
|
||||
GLfloat g = (GLfloat)((c>>8)&0xFF) / 255.0f;
|
||||
GLfloat b = (GLfloat)((c>>16)&0xFF) / 255.0f;
|
||||
glClearColor(r, g, b, 0.0);
|
||||
// For now, clear w/ black (may want to use color 0 later)
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glViewport(0, 0, xOffs, totalYPixels);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(xOffs+xPixels, 0, totalXPixels, totalYPixels);
|
||||
|
@ -670,31 +726,9 @@ void CRender2D::Setup2D(void)
|
|||
glUseProgram(shaderProgram);
|
||||
}
|
||||
|
||||
// Convert color offset register data to RGB
|
||||
void CRender2D::ColorOffset(GLfloat colorOffset[3], UINT32 reg)
|
||||
{
|
||||
INT8 ir, ig, ib;
|
||||
|
||||
ib = (reg>>16)&0xFF;
|
||||
ig = (reg>>8)&0xFF;
|
||||
ir = (reg>>0)&0xFF;
|
||||
|
||||
/*
|
||||
* Uncertain how these should be interpreted. It appears to be signed,
|
||||
* which means the values range from -128 to +127. The division by 128
|
||||
* normalizes this to roughly -1,+1.
|
||||
*/
|
||||
colorOffset[0] = (GLfloat) ir * (1.0f/128.0f);
|
||||
colorOffset[1] = (GLfloat) ig * (1.0f/128.0f);
|
||||
colorOffset[2] = (GLfloat) ib * (1.0f/128.0f);
|
||||
//printf("%08X -> %g,%g,%g\n", reg, colorOffset[2], colorOffset[1], colorOffset[0]);
|
||||
}
|
||||
|
||||
// Bottom layers
|
||||
void CRender2D::BeginFrame(void)
|
||||
{
|
||||
GLfloat colorOffset[3];
|
||||
|
||||
// Update all layers
|
||||
DrawTilemaps(surfBottom, surfTop);
|
||||
glActiveTexture(GL_TEXTURE0); // texture unit 0
|
||||
|
@ -705,21 +739,15 @@ void CRender2D::BeginFrame(void)
|
|||
|
||||
// Display bottom surface
|
||||
Setup2D();
|
||||
ColorOffset(colorOffset, regs[0x44/4]);
|
||||
glUniform3fv(colorOffsetLoc, 1, colorOffset);
|
||||
DisplaySurface(1, 0.0);
|
||||
}
|
||||
|
||||
// Top layers
|
||||
void CRender2D::EndFrame(void)
|
||||
{
|
||||
GLfloat colorOffset[3];
|
||||
|
||||
// Display top surface
|
||||
Setup2D();
|
||||
glEnable(GL_BLEND);
|
||||
ColorOffset(colorOffset, regs[0x40/4]);
|
||||
glUniform3fv(colorOffsetLoc, 1, colorOffset);
|
||||
DisplaySurface(0, -0.5);
|
||||
}
|
||||
|
||||
|
@ -745,9 +773,10 @@ void CRender2D::AttachRegisters(const UINT32 *regPtr)
|
|||
DebugLog("Render2D attached registers\n");
|
||||
}
|
||||
|
||||
void CRender2D::AttachPalette(const UINT32 *palPtr)
|
||||
void CRender2D::AttachPalette(const UINT32 *palPtr[2])
|
||||
{
|
||||
pal = palPtr;
|
||||
pal[0] = palPtr[0];
|
||||
pal[1] = palPtr[1];
|
||||
DebugLog("Render2D attached palette\n");
|
||||
}
|
||||
|
||||
|
@ -775,7 +804,6 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
|
|||
glUseProgram(shaderProgram); // bind program
|
||||
textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap");
|
||||
glUniform1i(textureMapLoc,0); // attach it to texture unit 0
|
||||
colorOffsetLoc = glGetUniformLocation(shaderProgram, "colorOffset");
|
||||
|
||||
// Allocate memory for layer surfaces
|
||||
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -82,12 +82,22 @@ public:
|
|||
* rendering otherwise the program may crash with an access violation.
|
||||
*
|
||||
* Parameters:
|
||||
* regPtr Pointer to the base of the tile generator registers.
|
||||
* There are assumed to be 64 in all.
|
||||
* regPtr Pointer to the base of the tile generator registers. There
|
||||
* are assumed to be 64 in all.
|
||||
*/
|
||||
void AttachRegisters(const UINT32 *regPtr);
|
||||
|
||||
void AttachPalette(const UINT32 *palPtr);
|
||||
/*
|
||||
* AttachPalette(palPtr):
|
||||
*
|
||||
* Attaches tile generator palettes. This must be done prior to any
|
||||
* rendering.
|
||||
*
|
||||
* Parameters:
|
||||
* palPtr Pointer to two palettes. The first is for layers A/A' and
|
||||
* the second is for B/B'.
|
||||
*/
|
||||
void AttachPalette(const UINT32 *palPtr[2]);
|
||||
|
||||
/*
|
||||
* AttachVRAM(vramPtr):
|
||||
|
@ -134,22 +144,16 @@ public:
|
|||
|
||||
private:
|
||||
// Private member functions
|
||||
void DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine);
|
||||
void DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine);
|
||||
void DrawTileLine4Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine);
|
||||
void DrawTileLine4BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels);
|
||||
void DrawTileLine8Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine);
|
||||
void DrawTileLine8BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels);
|
||||
void DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTableBase);
|
||||
void MixLine(UINT32 *dest, const UINT32 *src, int layerNum, int y, bool isBottom);
|
||||
void DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine, const UINT32 *pal);
|
||||
void DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine, const UINT32 *pal);
|
||||
void DrawLine(UINT32 *dest, int layerNum, int y, const UINT16 *nameTableBase, const UINT32 *pal);
|
||||
void DrawTilemaps(UINT32 *destBottom, UINT32 *destTop);
|
||||
void DisplaySurface(int surface, GLfloat z);
|
||||
void Setup2D(void);
|
||||
void ColorOffset(GLfloat colorOffset[3], UINT32 reg);
|
||||
|
||||
|
||||
// Data received from tile generator device object
|
||||
const UINT32 *vram;
|
||||
const UINT32 *pal;
|
||||
const UINT32 *pal[2]; // palettes for A/A' and B/B'
|
||||
const UINT32 *regs;
|
||||
|
||||
// OpenGL data
|
||||
|
@ -163,7 +167,6 @@ private:
|
|||
GLuint vertexShader; // vertex shader handle
|
||||
GLuint fragmentShader; // fragment shader
|
||||
GLuint textureMapLoc; // location of "textureMap" uniform
|
||||
GLuint colorOffsetLoc; // uniform
|
||||
|
||||
// Buffers
|
||||
UINT8 *memoryPool; // all memory is allocated here
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -853,7 +853,7 @@ void CRender3D::RenderViewport(UINT32 addr, int pri)
|
|||
// Set up viewport and projection (TO-DO: near and far clipping)
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
if (g_Config.wideScreen && (vpX==0) && (vpY==0) && (vpWidth>=495) && (vpHeight >= 383))
|
||||
if (g_Config.wideScreen && (vpX==0) && (vpWidth>=495) && (vpY==0) && (vpHeight >= 383)) // only expand viewports that occupy whole screen
|
||||
{
|
||||
// Wide screen hack only modifies X axis and not the Y FOV
|
||||
viewportX = 0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -43,7 +43,7 @@
|
|||
*/
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include "Pkgs/glew.h"
|
||||
#include "Supermodel.h"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -29,7 +29,6 @@
|
|||
|
||||
// Global uniforms
|
||||
uniform sampler2D textureMap; // 512x512 layer surface
|
||||
uniform vec3 colorOffset; // color offset for this layer
|
||||
|
||||
/*
|
||||
* main():
|
||||
|
@ -40,5 +39,4 @@ uniform vec3 colorOffset; // color offset for this layer
|
|||
void main(void)
|
||||
{
|
||||
gl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);
|
||||
gl_FragColor.rgb = clamp(gl_FragColor.rgb+colorOffset,0.0,1.0);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -145,19 +145,19 @@ void main(void)
|
|||
// Standard specular lighting equation
|
||||
vec3 V = normalize(-viewVertex);
|
||||
vec3 H = normalize(sunVector+V); // halfway vector
|
||||
float s = max(10,64-shininess); // seems to look nice, but probably not correct
|
||||
fsSpecularTerm = pow(max(dot(viewNormal,H),0),s);
|
||||
if (sunFactor <= 0) fsSpecularTerm = 0;
|
||||
float s = max(10.0,64.0-shininess); // seems to look nice, but probably not correct
|
||||
fsSpecularTerm = pow(max(dot(viewNormal,H),0.0),s);
|
||||
if (sunFactor <= 0.0) fsSpecularTerm = 0.0;
|
||||
|
||||
// Faster approximation
|
||||
//float temp = max(dot(viewNormal,H),0);
|
||||
//float s = 64-shininess;
|
||||
//float temp = max(dot(viewNormal,H),0.0);
|
||||
//float s = 64.0-shininess;
|
||||
//fsSpecularTerm = temp/(s-temp*s+temp);
|
||||
|
||||
// Phong formula
|
||||
//vec3 R = normalize(2*dot(sunVector,viewNormal)*viewNormal - sunVector);
|
||||
//vec3 R = normalize(2.0*dot(sunVector,viewNormal)*viewNormal - sunVector);
|
||||
//vec3 V = normalize(-viewVertex);
|
||||
//float s = max(2,64-shininess);
|
||||
//float s = max(2.0,64.0-shininess);
|
||||
//fsSpecularTerm = pow(max(dot(R,V),0),s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -31,88 +31,86 @@
|
|||
// Vertex shader
|
||||
static const char vertexShaderSource[] =
|
||||
{
|
||||
"/** \n"
|
||||
" ** Supermodel \n"
|
||||
" ** A Sega Model 3 Arcade Emulator. \n"
|
||||
" ** Copyright 2011 Bart Trzynadlowski \n"
|
||||
" ** \n"
|
||||
" ** This file is part of Supermodel. \n"
|
||||
" ** \n"
|
||||
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
|
||||
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
|
||||
" ** any later version. \n"
|
||||
" ** \n"
|
||||
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
|
||||
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
|
||||
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
|
||||
" ** more details. \n"
|
||||
" ** \n"
|
||||
" ** You should have received a copy of the GNU General Public License along \n"
|
||||
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
|
||||
" **/ \n"
|
||||
"/**\n"
|
||||
" ** Supermodel\n"
|
||||
" ** A Sega Model 3 Arcade Emulator.\n"
|
||||
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
|
||||
" **\n"
|
||||
" ** This file is part of Supermodel.\n"
|
||||
" **\n"
|
||||
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
|
||||
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
|
||||
" ** any later version.\n"
|
||||
" **\n"
|
||||
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
|
||||
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
|
||||
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
|
||||
" ** more details.\n"
|
||||
" **\n"
|
||||
" ** You should have received a copy of the GNU General Public License along\n"
|
||||
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
|
||||
" **/\n"
|
||||
"\n"
|
||||
"/* \n"
|
||||
" * Vertex2D.glsl \n"
|
||||
" * \n"
|
||||
" * Vertex shader for 2D tilemap rendering. \n"
|
||||
" */ \n"
|
||||
" \n"
|
||||
"#version 120 \n"
|
||||
"/*\n"
|
||||
" * Vertex2D.glsl\n"
|
||||
" *\n"
|
||||
" * Vertex shader for 2D tilemap rendering.\n"
|
||||
" */\n"
|
||||
" \n"
|
||||
"#version 120\n"
|
||||
"\n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
||||
" gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex; \n"
|
||||
"void main(void)\n"
|
||||
"{\n"
|
||||
"\tgl_TexCoord[0] = gl_MultiTexCoord0;\n"
|
||||
"\tgl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;\n"
|
||||
"}\n"
|
||||
};
|
||||
|
||||
// Fragment shader
|
||||
static const char fragmentShaderSource[] =
|
||||
{
|
||||
"/** \n"
|
||||
" ** Supermodel \n"
|
||||
" ** A Sega Model 3 Arcade Emulator. \n"
|
||||
" ** Copyright 2011 Bart Trzynadlowski \n"
|
||||
" ** \n"
|
||||
" ** This file is part of Supermodel. \n"
|
||||
" ** \n"
|
||||
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
|
||||
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
|
||||
" ** any later version. \n"
|
||||
" ** \n"
|
||||
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
|
||||
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
|
||||
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
|
||||
" ** more details. \n"
|
||||
" ** \n"
|
||||
" ** You should have received a copy of the GNU General Public License along \n"
|
||||
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
|
||||
" **/ \n"
|
||||
"/**\n"
|
||||
" ** Supermodel\n"
|
||||
" ** A Sega Model 3 Arcade Emulator.\n"
|
||||
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
|
||||
" **\n"
|
||||
" ** This file is part of Supermodel.\n"
|
||||
" **\n"
|
||||
" ** Supermodel is free software: you can redistribute it and/or modify it under\n"
|
||||
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||
" ** Software Foundation, either version 3 of the License, or (at your option)\n"
|
||||
" ** any later version.\n"
|
||||
" **\n"
|
||||
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT\n"
|
||||
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n"
|
||||
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n"
|
||||
" ** more details.\n"
|
||||
" **\n"
|
||||
" ** You should have received a copy of the GNU General Public License along\n"
|
||||
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>.\n"
|
||||
" **/\n"
|
||||
" \n"
|
||||
"/*\n"
|
||||
" * Fragment2D.glsl\n"
|
||||
" *\n"
|
||||
" * Fragment shader for 2D tilemap rendering.\n"
|
||||
" */\n"
|
||||
"\n"
|
||||
"/* \n"
|
||||
" * Fragment2D.glsl \n"
|
||||
" * \n"
|
||||
" * Fragment shader for 2D tilemap rendering. \n"
|
||||
" */ \n"
|
||||
"#version 120\n"
|
||||
"\n"
|
||||
"#version 120 \n"
|
||||
"// Global uniforms\n"
|
||||
"uniform sampler2D\ttextureMap;\t\t// 512x512 layer surface\n"
|
||||
"\n"
|
||||
"// Global uniforms \n"
|
||||
"uniform sampler2D textureMap; // 512x512 layer surface \n"
|
||||
"uniform vec3 colorOffset; // color offset for this layer \n"
|
||||
"/*\n"
|
||||
" * main():\n"
|
||||
" *\n"
|
||||
" * Fragment shader entry point.\n"
|
||||
" */\n"
|
||||
"\n"
|
||||
"/* \n"
|
||||
" * main(): \n"
|
||||
" * \n"
|
||||
" * Fragment shader entry point. \n"
|
||||
" */ \n"
|
||||
"\n"
|
||||
"void main(void) \n"
|
||||
"{ \n"
|
||||
" gl_FragColor = texture2D(textureMap, gl_TexCoord[0].st); \n"
|
||||
" gl_FragColor.rgb = clamp(gl_FragColor.rgb+colorOffset,0.0,1.0); \n"
|
||||
"void main(void)\n"
|
||||
"{\t\n"
|
||||
"\tgl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);\n"
|
||||
"}\n"
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -34,7 +34,7 @@ static const char vertexShaderSource[] =
|
|||
"/**\n"
|
||||
" ** Supermodel\n"
|
||||
" ** A Sega Model 3 Arcade Emulator.\n"
|
||||
" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
|
||||
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
|
||||
" **\n"
|
||||
" ** This file is part of Supermodel.\n"
|
||||
" **\n"
|
||||
|
@ -178,20 +178,20 @@ static const char vertexShaderSource[] =
|
|||
" \t\t\t// Standard specular lighting equation\n"
|
||||
" \t\t\tvec3 V = normalize(-viewVertex);\n"
|
||||
" \t\t\tvec3 H = normalize(sunVector+V);\t// halfway vector\n"
|
||||
" \t\t\tfloat s = max(10,64-shininess);\t\t// seems to look nice, but probably not correct\n"
|
||||
" \t\t\tfsSpecularTerm = pow(max(dot(viewNormal,H),0),s);\n"
|
||||
" \t\t\tif (sunFactor <= 0) fsSpecularTerm = 0;\n"
|
||||
" \t\t\tfloat s = max(10.0,64.0-shininess);\t\t// seems to look nice, but probably not correct\n"
|
||||
" \t\t\tfsSpecularTerm = pow(max(dot(viewNormal,H),0.0),s);\n"
|
||||
" \t\t\tif (sunFactor <= 0.0) fsSpecularTerm = 0.0;\n"
|
||||
" \t\t\t\n"
|
||||
" \t\t\t// Faster approximation \t\t\t\n"
|
||||
" \t\t\t//float temp = max(dot(viewNormal,H),0);\n"
|
||||
" \t\t\t//float s = 64-shininess;\n"
|
||||
" \t\t\t//float temp = max(dot(viewNormal,H),0.0);\n"
|
||||
" \t\t\t//float s = 64.0-shininess;\n"
|
||||
" \t\t\t//fsSpecularTerm = temp/(s-temp*s+temp);\n"
|
||||
" \t\t\t\n"
|
||||
" \t\t\t// Phong formula\n"
|
||||
" \t\t\t//vec3 R = normalize(2*dot(sunVector,viewNormal)*viewNormal - sunVector);\n"
|
||||
" \t\t\t//vec3 R = normalize(2.0*dot(sunVector,viewNormal)*viewNormal - sunVector);\n"
|
||||
" \t\t\t//vec3 V = normalize(-viewVertex);\n"
|
||||
" \t\t\t//float s = max(2,64-shininess);\n"
|
||||
" \t\t\t//fsSpecularTerm = pow(max(dot(R,V),0),s);\n"
|
||||
" \t\t\t//float s = max(2.0,64.0-shininess);\n"
|
||||
" \t\t\t//fsSpecularTerm = pow(max(dot(R,V),0.0),s);\n"
|
||||
" \t\t}\n"
|
||||
"\t}\n"
|
||||
"\t\n"
|
||||
|
@ -218,7 +218,7 @@ static const char fragmentShaderSingleSheetSource[] =
|
|||
"/**\n"
|
||||
" ** Supermodel\n"
|
||||
" ** A Sega Model 3 Arcade Emulator.\n"
|
||||
" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
|
||||
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
|
||||
" **\n"
|
||||
" ** This file is part of Supermodel.\n"
|
||||
" **\n"
|
||||
|
@ -416,7 +416,7 @@ static const char fragmentShaderMultiSheetSource[] =
|
|||
"/**\n"
|
||||
" ** Supermodel\n"
|
||||
" ** A Sega Model 3 Arcade Emulator.\n"
|
||||
" ** Copyright 2011 Bart Trzynadlowski, Nik Henson \n"
|
||||
" ** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson \n"
|
||||
" **\n"
|
||||
" ** This file is part of Supermodel.\n"
|
||||
" **\n"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -22,7 +22,26 @@
|
|||
/*
|
||||
* TileGen.cpp
|
||||
*
|
||||
* Implementation of the CTileGen class: 2D tile generator.
|
||||
* Implementation of the CTileGen class: 2D tile generator. Palette decoding
|
||||
* and synchronization with the renderer (which may run in a separate thread)
|
||||
* are performed here as well. For a description of the tile generator
|
||||
* hardware, please refer to the 2D rendering engine source code.
|
||||
*
|
||||
* Palettes
|
||||
* --------
|
||||
*
|
||||
* Multiple copies of the 32K-color palette data are maintained. The first is
|
||||
* the raw data as written to the VRAM. Two copies are computed, one for layers
|
||||
* A/A' and the other for layers B/B'. These pairs of layers have independent
|
||||
* color offset registers associated with them. The renderer uses these
|
||||
* "computed" palettes.
|
||||
*
|
||||
* The computed palettes are updated whenever the real palette is modified, a
|
||||
* single color entry at a time. If the color register is modified, the entire
|
||||
* palette has to be recomputed accordingly.
|
||||
*
|
||||
* The read-only copy of the palette, which is generated for the renderer, only
|
||||
* stores the two computed palettes.
|
||||
*
|
||||
* TO-DO List:
|
||||
* -----------
|
||||
|
@ -41,17 +60,24 @@
|
|||
#define MARK_DIRTY(dirtyArray, addr) dirtyArray[addr>>(PAGE_WIDTH+3)] |= 1<<((addr>>PAGE_WIDTH)&7)
|
||||
|
||||
// Offsets of memory regions within TileGen memory pool
|
||||
#define OFFSET_VRAM 0x000000
|
||||
#define OFFSET_PAL 0x120000
|
||||
#define MEM_POOL_SIZE_RW (0x120000+0x020000)
|
||||
#define OFFSET_VRAM_RO 0x140000 // [read-only snapshot]
|
||||
#define OFFSET_PAL_RO 0x260000 // [read-only snapshot]
|
||||
#define MEM_POOL_SIZE_RO (0x120000+0x020000)
|
||||
#define OFFSET_VRAM_DIRTY 0x280000
|
||||
#define OFFSET_PAL_DIRTY (OFFSET_VRAM_DIRTY+DIRTY_SIZE(0x120000))
|
||||
#define MEM_POOL_SIZE_DIRTY (DIRTY_SIZE(MEM_POOL_SIZE_RO))
|
||||
#define OFFSET_VRAM 0x000000 // VRAM and palette data
|
||||
#define OFFSET_PAL_A 0x120000 // computed A/A' palette
|
||||
#define OFFSET_PAL_B 0x140000 // computed B/B' palette
|
||||
#define MEM_POOL_SIZE_RW (0x120000+0x040000)
|
||||
|
||||
#define OFFSET_VRAM_RO 0x160000 // [read-only snapshot]
|
||||
#define OFFSET_PAL_RO_A 0x280000 // [read-only snapshot]
|
||||
#define OFFSET_PAL_RO_B 0x2A0000
|
||||
#define MEM_POOL_SIZE_RO (0x120000+0x040000)
|
||||
|
||||
#define OFFSET_VRAM_DIRTY 0x2C0000
|
||||
#define OFFSET_PAL_A_DIRTY (OFFSET_VRAM_DIRTY+DIRTY_SIZE(0x120000))
|
||||
#define OFFSET_PAL_B_DIRTY (OFFSET_PAL_A_DIRTY+DIRTY_SIZE(0x20000))
|
||||
#define MEM_POOL_SIZE_DIRTY (DIRTY_SIZE(0x120000)+2*DIRTY_SIZE(0x20000)) // VRAM + 2 palette dirty buffers
|
||||
|
||||
#define MEMORY_POOL_SIZE (MEM_POOL_SIZE_RW+MEM_POOL_SIZE_RO+MEM_POOL_SIZE_DIRTY)
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
Save States
|
||||
******************************************************************************/
|
||||
|
@ -80,6 +106,10 @@ void CTileGen::LoadState(CBlockFile *SaveState)
|
|||
WriteRAM(i, data);
|
||||
}
|
||||
SaveState->Read(regs, sizeof(regs));
|
||||
|
||||
// Because regs were read after palette, must recompute
|
||||
RecomputePalettes();
|
||||
|
||||
// If multi-threaded, update read-only snapshots too
|
||||
if (g_Config.gpuMultiThreaded)
|
||||
UpdateSnapshots(true);
|
||||
|
@ -102,6 +132,7 @@ void CTileGen::BeginVBlank(void)
|
|||
printf("64: %08X\n", regs[0x64/4]);
|
||||
printf("68: %08X\n", regs[0x68/4]);
|
||||
printf("6C: %08X\n", regs[0x6C/4]);
|
||||
printf("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -110,8 +141,34 @@ void CTileGen::EndVBlank(void)
|
|||
//
|
||||
}
|
||||
|
||||
void CTileGen::RecomputePalettes(void)
|
||||
{
|
||||
// Writing the colors forces palettes to be computed
|
||||
if (g_Config.gpuMultiThreaded)
|
||||
{
|
||||
for (unsigned colorAddr = 0; colorAddr < 32768*4; colorAddr += 4 )
|
||||
{
|
||||
MARK_DIRTY(palDirty[0], colorAddr);
|
||||
MARK_DIRTY(palDirty[1], colorAddr);
|
||||
WritePalette(colorAddr/4, *(UINT32 *) &vram[0x100000+colorAddr]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned colorAddr = 0; colorAddr < 32768*4; colorAddr += 4 )
|
||||
WritePalette(colorAddr/4, *(UINT32 *) &vram[0x100000+colorAddr]);
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 CTileGen::SyncSnapshots(void)
|
||||
{
|
||||
// Good time to recompute the palettes
|
||||
if (recomputePalettes)
|
||||
{
|
||||
RecomputePalettes();
|
||||
recomputePalettes = false;
|
||||
}
|
||||
|
||||
if (!g_Config.gpuMultiThreaded)
|
||||
return 0;
|
||||
|
||||
|
@ -168,11 +225,12 @@ UINT32 CTileGen::UpdateSnapshot(bool copyWhole, UINT8 *src, UINT8 *dst, unsigned
|
|||
UINT32 CTileGen::UpdateSnapshots(bool copyWhole)
|
||||
{
|
||||
// Update all memory region snapshots
|
||||
UINT32 palCopied = UpdateSnapshot(copyWhole, (UINT8*)pal, (UINT8*)palRO, 0x020000, palDirty);
|
||||
UINT32 palACopied = UpdateSnapshot(copyWhole, (UINT8*)pal[0], (UINT8*)palRO[0], 0x020000, palDirty[0]);
|
||||
UINT32 palBCopied = UpdateSnapshot(copyWhole, (UINT8*)pal[1], (UINT8*)palRO[1], 0x020000, palDirty[1]);
|
||||
UINT32 vramCopied = UpdateSnapshot(copyWhole, (UINT8*)vram, (UINT8*)vramRO, 0x120000, vramDirty);
|
||||
memcpy(regsRO, regs, sizeof(regs)); // Always copy whole of regs buffer
|
||||
//printf("TileGen copied - pal:%4uK, vram:%4uK, regs:%uK\n", palCopied / 1024, vramCopied / 1024, sizeof(regs) / 1024);
|
||||
return palCopied + vramCopied + sizeof(regs);
|
||||
//printf("TileGen copied - palA:%4uK, palB:%4uK, vram:%4uK, regs:%uK\n", palACopied / 1024, palBCopied / 1024, vramCopied / 1024, sizeof(regs) / 1024);
|
||||
return palACopied + palBCopied + vramCopied + sizeof(regs);
|
||||
}
|
||||
|
||||
void CTileGen::BeginFrame(void)
|
||||
|
@ -210,8 +268,15 @@ void CTileGen::WriteRAM(unsigned addr, UINT32 data)
|
|||
{
|
||||
addr -= 0x100000;
|
||||
unsigned color = addr/4; // color index
|
||||
|
||||
// Same address in both palettes must be marked dirty
|
||||
if (g_Config.gpuMultiThreaded)
|
||||
MARK_DIRTY(palDirty, addr);
|
||||
{
|
||||
MARK_DIRTY(palDirty[0], addr);
|
||||
MARK_DIRTY(palDirty[1], addr);
|
||||
}
|
||||
|
||||
// Both palettes will be modified simultaneously
|
||||
WritePalette(color, data);
|
||||
}
|
||||
}
|
||||
|
@ -220,12 +285,51 @@ void CTileGen::InitPalette(void)
|
|||
{
|
||||
for (int i = 0; i < 0x20000/4; i++)
|
||||
{
|
||||
WritePalette(i, vram[0x100000/4 + i]);
|
||||
WritePalette(i, *(UINT32 *) &vram[0x100000 + i*4]);
|
||||
if (g_Config.gpuMultiThreaded)
|
||||
palRO[i] = pal[i];
|
||||
{
|
||||
palRO[0][i] = pal[0][i];
|
||||
palRO[1][i] = pal[1][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline UINT32 AddColorOffset(UINT8 r, UINT8 g, UINT8 b, UINT8 a, UINT32 offsetReg)
|
||||
{
|
||||
INT32 ir, ig, ib;
|
||||
|
||||
/*
|
||||
* Color offsets are signed but I'm not sure whether or not their range is
|
||||
* merely [-128,+127], which would mean adding to a 0 component would not
|
||||
* result full intensity (only +127 at most). Alternatively, the signed
|
||||
* value might have to be multiplied by 2. That is assumed here. In either
|
||||
* case, the signed addition should be saturated.
|
||||
*/
|
||||
ib = (INT32) (INT8)((offsetReg>>16)&0xFF);
|
||||
ig = (INT32) (INT8)((offsetReg>>8)&0xFF);
|
||||
ir = (INT32) (INT8)((offsetReg>>0)&0xFF);
|
||||
ib *= 2;
|
||||
ig *= 2;
|
||||
ir *= 2;
|
||||
|
||||
// Add with saturation
|
||||
ib += (INT32) (UINT32) b;
|
||||
if (ib < 0) ib = 0;
|
||||
else if (ib > 0xFF) ib = 0xFF;
|
||||
ig += (INT32) (UINT32) g;
|
||||
if (ig < 0) ig = 0;
|
||||
else if (ig > 0xFF) ig = 0xFF;
|
||||
ir += (INT32) (UINT32) r;
|
||||
if (ir < 0) ir = 0;
|
||||
else if (ir > 0xFF) ir = 0xFF;
|
||||
|
||||
// Construct the final 32-bit ABGR-format color
|
||||
r = (UINT8) ir;
|
||||
g = (UINT8) ig;
|
||||
b = (UINT8) ib;
|
||||
return ((UINT32)a<<24)|((UINT32)b<<16)|((UINT32)g<<8)|(UINT32)r;
|
||||
}
|
||||
|
||||
void CTileGen::WritePalette(unsigned color, UINT32 data)
|
||||
{
|
||||
UINT8 r, g, b, a;
|
||||
|
@ -242,32 +346,43 @@ void CTileGen::WritePalette(unsigned color, UINT32 data)
|
|||
r = (data<<3)&0xF8;
|
||||
}
|
||||
|
||||
pal[color] = (a<<24)|(b<<16)|(g<<8)|r;
|
||||
pal[0][color] = AddColorOffset(r, g, b, a, regs[0x40/4]); // A/A'
|
||||
pal[1][color] = AddColorOffset(r, g, b, a, regs[0x44/4]); // B/B'
|
||||
}
|
||||
|
||||
void CTileGen::WriteRegister(unsigned reg, UINT32 data)
|
||||
{
|
||||
reg &= 0xFF;
|
||||
regs[reg/4] = data;
|
||||
|
||||
|
||||
switch (reg)
|
||||
{
|
||||
case 0x08:
|
||||
case 0x0C:
|
||||
case 0x20:
|
||||
case 0x60:
|
||||
case 0x64:
|
||||
case 0x68:
|
||||
case 0x6C:
|
||||
break;
|
||||
case 0x40: // layer A/A' color offset
|
||||
case 0x44: // layer B/B' color offset
|
||||
// We only have a mechanism to recompute both palettes simultaneously.
|
||||
// These regs are often written together in the same frame. To avoid
|
||||
// needlessly recomputing both palettes twice, we defer the operation.
|
||||
if (regs[reg] != data) // only if changed
|
||||
recomputePalettes = true;
|
||||
break;
|
||||
case 0x10: // IRQ acknowledge
|
||||
IRQ->Deassert(data&0xFF);
|
||||
break;
|
||||
case 0x60:
|
||||
break;
|
||||
case 0x64:
|
||||
break;
|
||||
case 0x68:
|
||||
break;
|
||||
case 0x6C:
|
||||
break;
|
||||
default:
|
||||
DebugLog("Tile Generator reg %02X = %08X\n", reg, data);
|
||||
//printf("%02X = %08X\n", reg, data);
|
||||
break;
|
||||
}
|
||||
|
||||
// Modify register
|
||||
regs[reg/4] = data;
|
||||
}
|
||||
|
||||
void CTileGen::Reset(void)
|
||||
|
@ -278,6 +393,7 @@ void CTileGen::Reset(void)
|
|||
memset(regsRO, 0, sizeof(regsRO));
|
||||
|
||||
InitPalette();
|
||||
recomputePalettes = false;
|
||||
|
||||
DebugLog("Tile Generator reset\n");
|
||||
}
|
||||
|
@ -295,13 +411,13 @@ void CTileGen::AttachRenderer(CRender2D *Render2DPtr)
|
|||
if (g_Config.gpuMultiThreaded)
|
||||
{
|
||||
Render2D->AttachVRAM(vramRO);
|
||||
Render2D->AttachPalette(palRO);
|
||||
Render2D->AttachPalette((const UINT32 **)palRO);
|
||||
Render2D->AttachRegisters(regsRO);
|
||||
}
|
||||
else
|
||||
{
|
||||
Render2D->AttachVRAM(vram);
|
||||
Render2D->AttachPalette(pal);
|
||||
Render2D->AttachPalette((const UINT32 **)pal);
|
||||
Render2D->AttachRegisters(regs);
|
||||
}
|
||||
|
||||
|
@ -321,15 +437,18 @@ bool CTileGen::Init(CIRQ *IRQObjectPtr)
|
|||
|
||||
// Set up main pointers
|
||||
vram = (UINT8 *) &memoryPool[OFFSET_VRAM];
|
||||
pal = (UINT32 *) &memoryPool[OFFSET_PAL];
|
||||
pal[0] = (UINT32 *) &memoryPool[OFFSET_PAL_A];
|
||||
pal[1] = (UINT32 *) &memoryPool[OFFSET_PAL_B];
|
||||
|
||||
// If multi-threaded, set up pointers for read-only snapshots and dirty page arrays too
|
||||
if (g_Config.gpuMultiThreaded)
|
||||
{
|
||||
vramRO = (UINT8 *) &memoryPool[OFFSET_VRAM_RO];
|
||||
palRO = (UINT32 *) &memoryPool[OFFSET_PAL_RO];
|
||||
palRO[0] = (UINT32 *) &memoryPool[OFFSET_PAL_RO_A];
|
||||
palRO[1] = (UINT32 *) &memoryPool[OFFSET_PAL_RO_B];
|
||||
vramDirty = (UINT8 *) &memoryPool[OFFSET_VRAM_DIRTY];
|
||||
palDirty = (UINT8 *) &memoryPool[OFFSET_PAL_DIRTY];
|
||||
palDirty[0] = (UINT8 *) &memoryPool[OFFSET_PAL_A_DIRTY];
|
||||
palDirty[1] = (UINT8 *) &memoryPool[OFFSET_PAL_B_DIRTY];
|
||||
}
|
||||
|
||||
// Hook up the IRQ controller
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -191,6 +191,7 @@ public:
|
|||
|
||||
private:
|
||||
// Private member functions
|
||||
void RecomputePalettes(void);
|
||||
void InitPalette(void);
|
||||
void WritePalette(unsigned color, UINT32 data);
|
||||
UINT32 UpdateSnapshots(bool copyWhole);
|
||||
|
@ -199,18 +200,23 @@ private:
|
|||
CIRQ *IRQ; // IRQ controller the tile generator is attached to
|
||||
CRender2D *Render2D; // 2D renderer the tile generator is attached to
|
||||
|
||||
// Tile generator VRAM
|
||||
UINT8 *memoryPool; // all memory allocated here
|
||||
UINT8 *vram; // 1.8MB of VRAM
|
||||
UINT32 *pal; // 0x20000 byte (32K colors) palette
|
||||
/*
|
||||
* Tile generator VRAM. The upper 128KB of VRAM stores the palette data.
|
||||
* Two palettes are computed from this based on the color offset registers:
|
||||
* A/A' and B/B'.
|
||||
*/
|
||||
UINT8 *memoryPool; // all memory allocated here
|
||||
UINT8 *vram; // 1.125MB of VRAM
|
||||
UINT32 *pal[2]; // 2 x 0x20000 byte (32K colors) palette
|
||||
bool recomputePalettes; // whether to recompute palettes A/A' and B/B' during sync
|
||||
|
||||
// Read-only snapshots
|
||||
UINT8 *vramRO; // 1.8MB of VRAM [read-only snapshot]
|
||||
UINT32 *palRO; // 0x20000 byte (32K colors) palette [read-only snapshot]
|
||||
UINT8 *vramRO; // 1.125MB of VRAM [read-only snapshot]
|
||||
UINT32 *palRO[2]; // 2 x 0x20000 byte (32K colors) palette [read-only snapshot]
|
||||
|
||||
// Arrays to keep track of dirty pages in memory regions
|
||||
UINT8 *vramDirty;
|
||||
UINT8 *palDirty;
|
||||
UINT8 *palDirty[2]; // one for each palette
|
||||
|
||||
// Registers
|
||||
UINT32 regs[64];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
@ -1242,7 +1242,7 @@ static int DisassembleCROM(const char *zipFile, UINT32 addr, unsigned n)
|
|||
static void Title(void)
|
||||
{
|
||||
puts("Supermodel: A Sega Model 3 Arcade Emulator (Version "SUPERMODEL_VERSION")");
|
||||
puts("Copyright (C) 2011 by Bart Trzynadlowski and Nik Henson\n");
|
||||
puts("Copyright (C) 2011-2012 by Bart Trzynadlowski and Nik Henson\n");
|
||||
}
|
||||
|
||||
// Print usage information
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
** Supermodel
|
||||
** A Sega Model 3 Arcade Emulator.
|
||||
** Copyright 2011 Bart Trzynadlowski, Nik Henson
|
||||
** Copyright 2011-2012 Bart Trzynadlowski, Nik Henson
|
||||
**
|
||||
** This file is part of Supermodel.
|
||||
**
|
||||
|
|
Loading…
Reference in a new issue