aboutsummaryrefslogtreecommitdiff
path: root/backends/ps2/Gs2dScreen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/ps2/Gs2dScreen.cpp')
-rw-r--r--backends/ps2/Gs2dScreen.cpp585
1 files changed, 585 insertions, 0 deletions
diff --git a/backends/ps2/Gs2dScreen.cpp b/backends/ps2/Gs2dScreen.cpp
new file mode 100644
index 0000000000..5a50c7d9cc
--- /dev/null
+++ b/backends/ps2/Gs2dScreen.cpp
@@ -0,0 +1,585 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001-2004 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+#include "Gs2dScreen.h"
+#include <kernel.h>
+#include <malloc.h>
+#include <string.h>
+#include <assert.h>
+#include <fileio.h>
+#include <math.h> // for sqrt()
+#include "DmaPipe.h"
+#include "GsDefs.h"
+#include <sio.h>
+
+enum Buffers {
+ SCREEN = 0,
+ MOUSE,
+ TEXT
+};
+
+#define DEFAULT_PAL_X 175
+#define DEFAULT_PAL_Y 40
+#define DEFAULT_NTSC_X 165
+#define DEFAULT_NTSC_Y 45
+#define ORG_X 256
+#define ORG_Y 256
+#define ORIGIN_X (ORG_X << 4)
+#define ORIGIN_Y (ORG_Y << 4)
+#define TEX_POW 10
+
+#define SCALE(x) ((x) << 4)
+#define COORD_XY(x, y) SCALE((x) + ORG_X), SCALE((y) + ORG_Y)
+#define COORD_UV(u, v) SCALE(u), SCALE(v)
+
+/*#define COORD_X1(x) SCALE((x) + ORG_X + GS_RECT_OFFSET)
+#define COORD_Y1(y) SCALE((y) + ORG_Y + GS_RECT_OFFSET)
+#define COORD_X2(x) SCALE((x) + ORG_X - GS_RECT_OFFSET)
+#define COORD_Y2(y) SCALE((y) + ORG_Y - GS_RECT_OFFSET)
+#define COORD_XY1(x, y) COORD_X1(x), COORD_Y1(y)
+#define COORD_XY2(x, y) COORD_X2(x), COORD_Y2(y)*/
+
+#define M_SIZE 128
+#define M_POW 7
+
+#define PI 3.1415926535897932384626433832795
+
+static volatile uint32 g_VblankCmd, g_DmacCmd;
+
+int32 vblankHandler(int32 cause) {
+ // start of VBlank period
+ if (g_VblankCmd) { // is there a new image waiting?
+ GS_DISPFB1 = g_VblankCmd; // show it.
+ g_VblankCmd = 0;
+ }
+ return 0;
+}
+
+int32 dmacHandler(int32 channel) {
+ if (g_DmacCmd && (channel == 2)) { // GS DMA transfer finished,
+ g_VblankCmd = g_DmacCmd; // we want to show the image
+ g_DmacCmd = 0; // when the next vblank occurs
+ }
+ return 0;
+}
+
+void createAnimThread(Gs2dScreen *screen);
+
+Gs2dScreen::Gs2dScreen(uint16 width, uint16 height, TVMode tvMode) {
+
+ g_DmacCmd = g_VblankCmd = 0;
+ AddIntcHandler(INT_VBLANK_START, vblankHandler, 0);
+ AddDmacHandler(2, dmacHandler, 0); // 2 = 2nd dma channel = EE <-> GS
+
+ _dmaPipe = new DmaPipe(0x2000);
+
+ EnableIntc(INT_VBLANK_START);
+ EnableDmac(2);
+
+ _width = width;
+ _height = height;
+ _pitch = (width + 127) & ~127;
+
+ _screenBuf = (uint8*)memalign(64, _width * _height);
+ _overlayBuf = (uint16*)memalign(64, _width * _height * 2);
+ _clut = (uint32*)memalign(64, 256 * 4);
+
+ memset(_screenBuf, 0, _width * _height);
+ for (uint32 cnt = 0; cnt < 256; cnt++)
+ _clut[cnt] = GS_RGBA(0, 0, 0, 0x80);
+ clearOverlay();
+
+ if (tvMode == TV_DONT_CARE) {
+ if (PAL_NTSC_FLAG == 'E')
+ _videoMode = TV_NTSC;
+ else
+ _videoMode = TV_PAL;
+ } else
+ _videoMode = tvMode;
+
+ printf("Setting up %s mode\n", (_videoMode == TV_PAL) ? "PAL" : "NTSC");
+
+ // set screen size, 640x576 for pal, 640x448 for ntsc
+ _tvWidth = 640;
+ _tvHeight = ((_videoMode == TV_PAL) ? 576 : 448);
+
+ uint32 tvFrameSize = _tvWidth * _tvHeight * 4; // 32 bits per pixel
+
+ // setup frame buffer pointers
+ _frameBufPtr[0] = 0;
+ _frameBufPtr[1] = tvFrameSize;
+ _clutPtrs[SCREEN] = tvFrameSize * 2;
+ _clutPtrs[MOUSE] = _clutPtrs[SCREEN] + 0x1000; // the cluts in PSMCT32 take up half a memory page each
+ _clutPtrs[TEXT] = _clutPtrs[SCREEN] + 0x2000;
+ _texPtrs[SCREEN] = _clutPtrs[SCREEN] + 0x3000;
+ _texPtrs[TEXT] = 0;
+ _texPtrs[MOUSE] = 128 * 128 * 4; // mouse cursor is loaded into the gaps of the frame buffer
+
+ _showOverlay = true;
+ _showMouse = false;
+ _mouseScaleX = (_tvWidth << 8) / _width;
+ _mouseScaleY = (_tvHeight << 8) / _height;
+ setMouseXy(_width / 2, _height / 2);
+ _mTraCol = 255;
+ _shakePos = 0;
+
+ // setup hardware now.
+ GS_CSR = GS_SET_CSR(0, 0, 0, 0, 0, 1, 0);
+ asm ("sync.p");
+ GS_CSR = 0;
+ GsPutIMR(0xFF00);
+
+ uint16 dispPosX, dispPosY;
+
+ if (_videoMode == TV_PAL) {
+ SetGsCrt(GS_INTERLACED, 3, 0);
+ dispPosX = DEFAULT_PAL_X;
+ dispPosY = DEFAULT_PAL_Y;
+ } else {
+ SetGsCrt(GS_INTERLACED, 2, 0);
+ dispPosX = DEFAULT_NTSC_X;
+ dispPosY = DEFAULT_NTSC_Y;
+ }
+
+ asm("di");
+ GS_PMODE = GS_SET_PMODE(1, 0, 1, 1, 0, 255);
+ GS_BGCOLOUR = GS_RGBA(0, 0, 0, 0);
+ GS_DISPLAY1 = GS_SET_DISPLAY(_tvWidth, _tvHeight, dispPosX, dispPosY);
+ asm("ei");
+
+ _curDrawBuf = 0;
+
+ _dmaPipe->setOrigin(ORIGIN_X, ORIGIN_Y);
+ _dmaPipe->setConfig(1, 0, 1);
+ _dmaPipe->setScissorRect(0, 0, _tvWidth - 1, _tvHeight - 1);
+ _dmaPipe->setDrawBuffer(_frameBufPtr[_curDrawBuf], _tvWidth, GS_PSMCT24, 0);
+
+ _clutChanged = _screenChanged = _overlayChanged = true;
+ updateScreen();
+
+ createAnimTextures();
+
+ ee_sema_t newSema;
+ newSema.init_count = 1;
+ newSema.max_count = 1;
+ _screenSema = CreateSema(&newSema);
+
+ newSema.init_count = 0;
+ _timerSema = CreateSema(&newSema);
+
+ if ((_screenSema < 0) || (_timerSema < 0)) {
+ printf("Can't create semaphores.\n");
+ SleepThread();
+ }
+
+ createAnimThread(this);
+}
+
+void Gs2dScreen::createAnimTextures(void) {
+ uint8 *buf = (uint8*)memalign(64, 14 * 64);
+ uint32 vramDest = _texPtrs[TEXT];
+ for (int i = 0; i < 16; i++) {
+ uint32 *destPos = (uint32*)buf;
+ for (int ch = 15; ch >= 0; ch--) {
+ uint32 *src = (uint32*)(_binaryData + ((_binaryPattern[i] >> ch) & 1) * 4 * 14);
+ for (int line = 0; line < 14; line++)
+ destPos[line << 4] = src[line];
+ destPos++;
+ }
+ if (!(i & 1))
+ _dmaPipe->uploadTex( vramDest, 128, 0, 0, GS_PSMT4HH, buf, 128, 14);
+ else {
+ _dmaPipe->uploadTex( vramDest, 128, 0, 0, GS_PSMT4HL, buf, 128, 14);
+ vramDest += 128 * 16 * 4;
+ }
+ _dmaPipe->flush();
+ _dmaPipe->waitForDma();
+ }
+ _dmaPipe->uploadTex(_clutPtrs[TEXT], 64, 0, 0, GS_PSMCT32, _binaryClut, 8, 2);
+ free(buf);
+}
+
+void Gs2dScreen::newScreenSize(uint16 width, uint16 height) {
+ if ((width == _width) && (height == _height))
+ return;
+
+ printf("New screen size %d/%d\n", width, height);
+ _dmaPipe->flush();
+ _screenChanged = _overlayChanged = false;
+ _width = width;
+ _height = height;
+ _pitch = (width + 127) & ~127;
+ waitForImage(); // if there's a frame waiting to be shown, wait for vblank handler
+
+ // now malloc new buffers
+ free(_screenBuf);
+ free(_overlayBuf);
+ _screenBuf = (uint8*)memalign(64, _width * _height);
+ memset(_screenBuf, 0, _width * height);
+ _overlayBuf = (uint16*)memalign(64, _width * _height * 2);
+ memset(_overlayBuf, 0, _width * height * 2);
+ _screenChanged = _overlayChanged = true;
+
+ _mouseScaleX = (_tvWidth << 8) / _width;
+ _mouseScaleY = (_tvHeight << 8) / _height;
+ setMouseXy(_width / 2, _height / 2);
+ printf("done\n");
+}
+
+void Gs2dScreen::copyScreenRect(const uint8 *buf, uint16 pitch, uint16 x, uint16 y, uint16 w, uint16 h) {
+ waitForDma();
+
+ assert((x + w <= _width) && (y + h <= _height));
+ uint8 *dest = _screenBuf + y * _width + x;
+ for (uint16 cnt = 0; cnt < h; cnt++) {
+ memcpy(dest, buf, w);
+ buf += pitch;
+ dest += _width;
+ }
+ _screenChanged = true;
+}
+
+void Gs2dScreen::setPalette(const uint32 *pal, uint8 start, uint16 num) {
+ waitForDma();
+
+ assert(start + num <= 256);
+ for (uint16 cnt = 0; cnt < num; cnt++) {
+ uint16 dest = start + cnt;
+ dest = (dest & 0x7) | ((dest & 0x8) << 1) | ((dest & 0x10) >> 1) | (dest & 0xE0); // rearrange like the GS expects it
+ _clut[dest] = pal[cnt] & 0xFFFFFF;
+ }
+ _clutChanged = true;
+}
+
+void Gs2dScreen::updateScreen(void) {
+ /* we can't draw more than 50 images on PAL and 60 on NTSC, wait until the other buffer is shown.
+ especcially necessary for BS2 which does hundreds of updateScreen()s per second */
+ waitForImage();
+
+ WaitSema(_screenSema);
+
+ if (_clutChanged) {
+ _clutChanged = false;
+ uint32 tmp = _clut[_mTraCol];
+ _clut[_mTraCol] = GS_RGBA(0, 0, 0, 0x80); // this mouse color is transparent
+ _dmaPipe->uploadTex(_clutPtrs[MOUSE], 64, 0, 0, GS_PSMCT32, _clut, 16, 16);
+ _dmaPipe->flush();
+ _dmaPipe->waitForDma();
+ _clut[_mTraCol] = tmp;
+
+ _dmaPipe->uploadTex(_clutPtrs[SCREEN], 64, 0, 0, GS_PSMCT32, _clut, 16, 16);
+ }
+
+ drawScreen();
+
+ g_DmacCmd = GS_SET_DISPFB(_frameBufPtr[_curDrawBuf], _tvWidth, GS_PSMCT24); // put it here for dmac/vblank handler
+ _dmaPipe->flush();
+ _curDrawBuf ^= 1;
+ _dmaPipe->setDrawBuffer(_frameBufPtr[_curDrawBuf], _tvWidth, GS_PSMCT24, 0);
+ SignalSema(_screenSema);
+}
+
+void Gs2dScreen::drawScreen(void) {
+ static GsVertex fullScreen[2] = {
+ { COORD_XY(0, 0), SCALE(0) },
+ { COORD_XY(_tvWidth, _tvHeight), SCALE(0) }
+ };
+
+ _dmaPipe->flatRect(fullScreen + 0, fullScreen + 1, GS_RGBA(0, 0, 0, 0)); // clear screen
+
+ if (_showOverlay) {
+ if (_overlayChanged) {
+ _dmaPipe->uploadTex(_texPtrs[SCREEN], _width, 0, 0, GS_PSMCT16, _overlayBuf, _width, _height);
+ _overlayChanged = false;
+ }
+ _dmaPipe->setTex(_texPtrs[SCREEN], _width, TEX_POW, TEX_POW, GS_PSMCT16, 0, 0, 0, 0);
+ _dmaPipe->textureRect(COORD_XY(0, 0), COORD_UV(1, 1),
+ COORD_XY(_tvWidth, _tvHeight), COORD_UV(_width, _height), 0, GS_RGBA(0x80, 0x80, 0x80, 0x80));
+ } else {
+ if (_screenChanged) {
+ _dmaPipe->uploadTex(_texPtrs[SCREEN], _pitch, 0, 0, GS_PSMT8, _screenBuf, _width, _height);
+ _screenChanged = false;
+ }
+ _dmaPipe->setTex(_texPtrs[SCREEN], _pitch, TEX_POW, TEX_POW, GS_PSMT8, _clutPtrs[SCREEN], 0, 64, GS_PSMCT32);
+ _dmaPipe->textureRect(COORD_XY(0, -_shakePos), COORD_UV(1, 1),
+ COORD_XY(_tvWidth, _tvHeight - _shakePos), COORD_UV(_width, _height), 0, GS_RGBA(0x80, 0x80, 0x80, 0x80));
+ }
+
+ if (_showMouse) {
+ _dmaPipe->setTex(_texPtrs[MOUSE], M_SIZE, M_POW, M_POW, GS_PSMT8H, _clutPtrs[MOUSE], 0, 64, GS_PSMCT32);
+ uint16 mpX1 = (((_mouseX - _hotSpotX) * _mouseScaleX + 128) >> 4) + ORIGIN_X;
+ uint16 mpY1 = (((_mouseY - _hotSpotY) * _mouseScaleY + 128) >> 4) + ORIGIN_Y;
+ uint16 mpX2 = mpX1 + ((M_SIZE * _mouseScaleX + 128) >> 4);
+ uint16 mpY2 = mpY1 + ((M_SIZE * _mouseScaleY + 128) >> 4);
+ _dmaPipe->textureRect(mpX1, mpY1, COORD_UV(0, 0),
+ mpX2, mpY2, COORD_UV(M_SIZE - 1, M_SIZE - 1), 0, GS_RGBA(0x80, 0x80, 0x80, 0x80));
+ }
+}
+
+void Gs2dScreen::showOverlay(void) {
+ _showOverlay = true;
+ clearOverlay();
+}
+
+void Gs2dScreen::hideOverlay(void) {
+ _screenChanged = true;
+ _showOverlay = false;
+}
+
+void Gs2dScreen::setShakePos(int shake) {
+ _shakePos = (shake * _mouseScaleY) >> 8;
+}
+
+void Gs2dScreen::copyOverlayRect(const uint16 *buf, uint16 pitch, uint16 x, uint16 y, uint16 w, uint16 h) {
+ waitForDma();
+
+ _overlayChanged = true;
+ uint16 *dest = _overlayBuf + y * _width + x;
+ for (uint32 cnt = 0; cnt < h; cnt++) {
+ memcpy(dest, buf, w * 2);
+ dest += _width;
+ buf += pitch;
+ }
+}
+
+void Gs2dScreen::clearOverlay(void) {
+ waitForDma();
+
+ _overlayChanged = true;
+ // first convert our clut to 16 bit RGBA for the overlay...
+ uint16 palette[256];
+ for (uint32 cnt = 0; cnt < 256; cnt++) {
+ uint32 rgba = _clut[(cnt & 0x7) | ((cnt & 0x8) << 1) | ((cnt & 0x10) >> 1) | (cnt & 0xE0)];
+ palette[cnt] = ((rgba >> 3) & 0x1F) | (((rgba >> 11) & 0x1F) << 5) | (((rgba >> 19) & 0x1F) << 10);
+ }
+ // now copy the current screen over
+ for (uint32 cnt = 0; cnt < _width * _height; cnt++)
+ _overlayBuf[cnt] = palette[_screenBuf[cnt]];
+}
+
+void Gs2dScreen::grabOverlay(uint16 *buf, uint16 pitch) {
+ uint16 *src = _overlayBuf;
+ for (uint32 cnt = 0; cnt < _height; cnt++) {
+ memcpy(buf, src, _width * 2);
+ buf += pitch;
+ src += _width;
+ }
+}
+
+void Gs2dScreen::setMouseOverlay(const uint8 *buf, uint16 width, uint16 height, uint16 hotSpotX, uint16 hotSpotY, uint8 transpCol) {
+ assert((width <= M_SIZE) && (height <= M_SIZE));
+
+ _hotSpotX = hotSpotX;
+ _hotSpotY = hotSpotY;
+ if (_mTraCol != transpCol) {
+ _mTraCol = transpCol;
+ _clutChanged = true;
+ }
+ uint8 *bufCopy = (uint8*)memalign(64, M_SIZE * M_SIZE); // make a copy to align to 64 bytes
+ memset(bufCopy, _mTraCol, M_SIZE * M_SIZE);
+ for (int cnt = 0; cnt < height; cnt++)
+ memcpy(bufCopy + cnt * M_SIZE, buf + cnt * width, width);
+
+ _dmaPipe->uploadTex( _texPtrs[MOUSE], M_SIZE, 0, 0, GS_PSMT8H, bufCopy, M_SIZE, M_SIZE);
+ _dmaPipe->flush();
+ _dmaPipe->waitForDma(); // make sure all data has been transferred when we free bufCopy
+ free(bufCopy);
+}
+
+void Gs2dScreen::waitForDma(void) {
+ // wait until dma transfer finished
+ while (g_DmacCmd)
+ ;
+}
+
+void Gs2dScreen::waitForImage(void) {
+ /* if there's an image waiting to be shown on next vblank, wait for it.
+ however, first we must wait for the dma transfer (if running) as it will
+ result in a new image */
+ waitForDma();
+ while (g_VblankCmd)
+ ;
+ /* both waitForImage and waitForDma should only get called by the main thread,
+ so they may be implemented as busy waits, as both the timer- and sound-thread have
+ a higher priority than this one. There's no thread we could switch to anyways... */
+}
+
+void Gs2dScreen::showMouse(bool show) {
+ _showMouse = show;
+}
+
+void Gs2dScreen::setMouseXy(int16 x, int16 y) {
+ _mouseX = x;
+ _mouseY = y;
+}
+
+uint8 Gs2dScreen::tvMode(void) {
+ return _videoMode;
+}
+
+#define LINE_SPACE 20
+#define SCRL_TIME 8
+#define V 1000
+#define Z_TRANSL 65
+
+void Gs2dScreen::wantAnim(bool runIt) {
+ _runAnim = runIt;
+}
+
+void Gs2dScreen::animThread(void) {
+ // animate zeros and ones while game accesses memory card, etc.
+ _runAnim = false;
+ float yPos = 0.0;
+ uint8 texSta = 0;
+ float scrlSpeed = (_videoMode == TV_PAL) ? (_tvHeight / (SCRL_TIME * 50.0)) : (_tvHeight / (SCRL_TIME * 60.0));
+ uint8 texMax = (_tvHeight / LINE_SPACE) + (ORG_Y / LINE_SPACE);
+ TexVertex texNodes[4] = {
+ { COORD_UV( 0, 0) }, { COORD_UV( 0, 14) },
+ { COORD_UV(128, 0) }, { COORD_UV(128, 14) }
+ };
+ float angleStep = ((2 * PI) / _tvHeight);
+
+ while (1) {
+ WaitSema(_timerSema);
+ if (_runAnim && !g_DmacCmd && !g_VblankCmd) {
+ if (PollSema(_screenSema) > 0) {
+ drawScreen(); // draw the last screen the engine did again
+
+ _dmaPipe->setAlphaBlend(SOURCE_COLOR, ZERO_COLOR, SOURCE_ALPHA, DEST_COLOR, 0);
+ yPos -= scrlSpeed;
+ if (yPos <= -LINE_SPACE) {
+ yPos += LINE_SPACE;
+ texSta++;
+ }
+
+ float drawY = yPos;
+
+ for (int i = 0; i < texMax; i++) {
+ uint8 texIdx = (texSta + i) & 0xF;
+
+ float x[4] = { -64.0, -64.0, 64.0, 64.0 };
+ float y[4];
+ y[0] = y[2] = drawY - _tvHeight / 2 - LINE_SPACE / 2;
+ y[1] = y[3] = y[0] + LINE_SPACE;
+ float z[4] = { 0.0, 0.0, 0.0, 0.0 };
+ GsVertex nodes[4];
+
+ float angle = PI / 2 + angleStep * drawY;
+ float rotSin = sinf(angle);
+ float rotCos = cosf(angle);
+ for (int coord = 0; coord < 4; coord++) {
+ z[coord] = rotCos * x[coord];
+ x[coord] = rotSin * x[coord];
+
+ nodes[coord].z = 0;
+ nodes[coord].x = (uint16)(((V * x[coord]) / (z[coord] + V + Z_TRANSL)) * 16);
+ nodes[coord].y = (uint16)(((V * y[coord]) / (z[coord] + V + Z_TRANSL)) * 16);
+ nodes[coord].x += SCALE(_tvWidth - 80 + ORG_X);
+ nodes[coord].y += SCALE(_tvHeight / 2 + ORG_Y);
+ }
+
+ uint32 texPtr = _texPtrs[TEXT] + 128 * 16 * 4 * (texIdx >> 1);
+ if (texIdx & 1)
+ _dmaPipe->setTex(_texPtrs[TEXT], 128, 7, 4, GS_PSMT4HL, _clutPtrs[TEXT], 0, 64, GS_PSMCT32);
+ else
+ _dmaPipe->setTex(_texPtrs[TEXT], 128, 7, 4, GS_PSMT4HH, _clutPtrs[TEXT], 0, 64, GS_PSMCT32);
+
+ _dmaPipe->textureRect(nodes + 0, nodes + 1, nodes + 2, nodes + 3,
+ texNodes + 0, texNodes + 1, texNodes + 2, texNodes + 3, GS_RGBA(0x80, 0x80, 0x80, 0x80));
+
+ drawY += LINE_SPACE;
+ }
+ g_DmacCmd = GS_SET_DISPFB(_frameBufPtr[_curDrawBuf], _tvWidth, GS_PSMCT24); // put it here for dmac/vblank handler
+ _dmaPipe->flush();
+ _curDrawBuf ^= 1;
+ _dmaPipe->setDrawBuffer(_frameBufPtr[_curDrawBuf], _tvWidth, GS_PSMCT24, 0);
+ _dmaPipe->setAlphaBlend(DEST_COLOR, ZERO_COLOR, SOURCE_ALPHA, SOURCE_COLOR, 0);
+ SignalSema(_screenSema);
+ }
+ }
+ }
+}
+
+void Gs2dScreen::timerTick(void) {
+ if (_runAnim)
+ SignalSema(_timerSema);
+}
+
+void runAnimThread(Gs2dScreen *param) {
+ param->animThread();
+}
+
+#define ANIM_STACK_SIZE (1024 * 32)
+
+void createAnimThread(Gs2dScreen *screen) {
+ ee_thread_t animThread, thisThread;
+ ReferThreadStatus(GetThreadId(), &thisThread);
+
+ animThread.initial_priority = thisThread.current_priority - 1;
+ animThread.stack = malloc(ANIM_STACK_SIZE);
+ animThread.stack_size = ANIM_STACK_SIZE;
+ animThread.func = (void *)runAnimThread;
+ //animThread.gp_reg = _gp; for some reason _gp is always NULL
+ asm("move %0, $gp\n": "=r"(animThread.gp_reg));
+
+ int tid = CreateThread(&animThread);
+ if (tid >= 0) {
+ StartThread(tid, screen);
+ } else
+ free(animThread.stack);
+}
+
+// data for the animated zeros and ones...
+const uint8 Gs2dScreen::_binaryData[4 * 14 * 2] = {
+ // figure zero
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x22, 0x22, 0x00, 0x31, 0x13,
+ 0x31, 0x13, 0x20, 0x02, 0x22, 0x02, 0x31, 0x13, 0x33, 0x13, 0x20, 0x02, 0x20, 0x02,
+ 0x31, 0x33, 0x31, 0x13, 0x20, 0x22, 0x20, 0x02, 0x31, 0x13, 0x31, 0x13, 0x00, 0x22,
+ 0x22, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,
+ // figure one
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x20, 0x02, 0x00, 0x11, 0x33,
+ 0x13, 0x11, 0x22, 0x22, 0x02, 0x00, 0x11, 0x31, 0x13, 0x11, 0x00, 0x20, 0x02, 0x00,
+ 0x11, 0x31, 0x13, 0x11, 0x00, 0x20, 0x02, 0x00, 0x11, 0x31, 0x13, 0x11, 0x00, 0x20,
+ 0x02, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11
+};
+
+const uint16 Gs2dScreen::_binaryPattern[16] = {
+ 0xD992, 0x344B, 0xA592, 0x110D,
+ 0x9234, 0x2326, 0x5199, 0xC8A6,
+ 0x4D29, 0x18B0, 0xA5AA, 0x2949,
+ 0x6DB3, 0xB2AA, 0x64A4, 0x3329
+};
+
+const uint32 Gs2dScreen::_binaryClut[16] __attribute__((aligned(64))) = {
+ GS_RGBA( 0, 0, 0, 0x40),
+ GS_RGBA( 50, 50, 50, 0x40),
+ GS_RGBA( 204, 204, 0xFF, 0x40),
+ GS_RGBA( 140, 140, 0xFF, 0x40),
+
+ GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
+ GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
+ GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
+ GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
+ GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
+ GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80)
+};
+
+