aboutsummaryrefslogtreecommitdiff
path: root/backends/ps2/systemps2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backends/ps2/systemps2.cpp')
-rw-r--r--backends/ps2/systemps2.cpp600
1 files changed, 600 insertions, 0 deletions
diff --git a/backends/ps2/systemps2.cpp b/backends/ps2/systemps2.cpp
new file mode 100644
index 0000000000..12026e03d8
--- /dev/null
+++ b/backends/ps2/systemps2.cpp
@@ -0,0 +1,600 @@
+/* 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 "stdafx.h"
+#include <kernel.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sifrpc.h>
+#include <loadfile.h>
+#include <malloc.h>
+#include <assert.h>
+#include <fileio.h>
+#include <iopcontrol.h>
+#include <iopheap.h>
+#include "scummsys.h"
+#include "../intern.h"
+#include "base/engine.h"
+#include "backends/ps2/systemps2.h"
+#include "backends/ps2/Gs2dScreen.h"
+#include "backends/ps2/ps2input.h"
+#include "sjpcm.h"
+#include <cdvd_rpc.h>
+#include "backends/ps2/savefile.h"
+#include "common/file.h"
+#include "backends/ps2/sysdefs.h"
+#include <libmc.h>
+#include "backends/ps2/cd.h"
+#include <sio.h>
+
+#define TIMER_STACK_SIZE (1024 * 32)
+#define SOUND_STACK_SIZE (1024 * 32)
+#define SMP_PER_BLOCK 800
+#define FROM_BCD(a) ((a >> 4) * 10 + (a & 0xF))
+
+#define CHECK_STACK_USAGE
+
+#ifdef USE_PS2LINK
+#define IRX_PREFIX "host:"
+#define IRX_SUFFIX
+#else
+#define IRX_PREFIX "cdrom0:\\"
+#define IRX_SUFFIX ";1"
+#endif
+
+static volatile int32 g_TimerThreadSema = -1;
+static volatile int32 g_SoundThreadSema = -1;
+static volatile uint64 msecCount = 0;
+
+extern void NORETURN CDECL error(const char *s, ...);
+
+static OSystem_PS2 *g_systemPs2 = NULL;
+
+void readRtcTime(void);
+
+int gBitFormat = 555;
+
+void sioprintf(const char *zFormat, ...) {
+ va_list ap;
+ char resStr[2048];
+
+ va_start(ap,zFormat);
+ int res = vsnprintf(resStr, 2048, zFormat, ap);
+ va_end(ap);
+
+ sio_puts(resStr);
+}
+
+OSystem *OSystem_PS2_create(void) {
+ if (!g_systemPs2)
+ g_systemPs2 = new OSystem_PS2();
+ return g_systemPs2;
+}
+
+extern "C" int scummvm_main(int argc, char *argv[]);
+
+extern "C" int main(int argc, char *argv[]) {
+ SifInitRpc(0);
+#ifdef USE_PS2LINK
+ fioInit();
+#else // reset the IOP if this is a CD build
+ cdvdInit(CDVD_EXIT);
+ cdvdExit();
+ fioExit();
+ SifExitIopHeap();
+ SifLoadFileExit();
+ SifExitRpc();
+ sio_puts("Resetting IOP.");
+ SifIopReset("rom0:UDNL rom0:EELOADCNF",0);
+ while (!SifIopSync())
+ ;
+ sio_puts("IOP synced.");
+ SifInitRpc(0);
+ fioInit();
+ SifLoadFileInit();
+ cdvdInit(CDVD_INIT_NOWAIT);
+#endif
+
+ ee_thread_t thisThread;
+ int tid = GetThreadId();
+ ReferThreadStatus(tid, &thisThread);
+
+ sioprintf("Thread Start Priority = %d\n", thisThread.current_priority);
+ if ((thisThread.current_priority < 5) || (thisThread.current_priority > 80)) {
+ /* Depending on the way ScummVM is run, we may get here with different
+ thread priorities.
+ The PS2 BIOS executes it with priority = 0, ps2link uses priority 64.
+ Don't know about NapLink, etc.
+ The priority doesn't matter too much, but we need to be at least at prio 3,
+ so we can have the timer thread run at prio 2 and the sound thread at prio 1 */
+ sioprintf("Changing thread priority");
+ int res = ChangeThreadPriority(tid, 20);
+ sioprintf("Result = %d", res);
+ }
+
+ sioprintf("Creating system");
+ /* The OSystem has to be created before we enter ScummVM's main.
+ It sets up the memory card, etc. */
+ OSystem_PS2_create();
+
+ sioprintf("init done. starting ScummVM.");
+ return scummvm_main(argc, argv);
+}
+
+s32 timerInterruptHandler(s32 cause) {
+ msecCount += 10;
+ T0_MODE = 0xDC2; // same value as in initialization.
+
+ iSignalSema(g_SoundThreadSema);
+ iSignalSema(g_TimerThreadSema);
+ return 0;
+}
+
+void systemTimerThread(OSystem_PS2 *system) {
+ system->timerThread();
+}
+
+void systemSoundThread(OSystem_PS2 *system) {
+ system->soundThread();
+}
+
+OSystem_PS2::OSystem_PS2(void) {
+ sioprintf("OSystem_PS2 constructor\n");
+
+ _soundStack = _timerStack = NULL;
+ _scummTimerProc = NULL;
+ _scummSoundProc = NULL;
+ _scummSoundParam = NULL;
+
+ _screen = new Gs2dScreen(320, 200, TV_DONT_CARE);
+ _width = 320;
+ _height = 200;
+
+ sioprintf("Initializing timer\n");
+ initTimer();
+
+ _screen->wantAnim(true);
+
+ if (!loadModules()) {
+ sioprintf("ERROR: Can't load modules");
+ printf("ERROR: Can't load modules\n");
+ _screen->wantAnim(false);
+ SleepThread();
+ // Todo: handle this correctly...
+ }
+ sioprintf("Modules: UsbMouse %sloaded, UsbKbd %sloaded, Hdd %sloaded.", _useMouse ? "" : "not ", _useKbd ? "" : "not ", _useHdd ? "" : "not ");
+
+ sioprintf("Initializing SjPCM");
+ if (SjPCM_Init(0) < 0)
+ sioprintf("SjPCM Bind failed");
+
+ sioprintf("Initializing LibCDVD.");
+ int res = CDVD_Init();
+ sioprintf("result = %d\n", res);
+
+ _timerTid = _soundTid = -1;
+ _mouseVisible = false;
+
+ sioprintf("reading RTC");
+ readRtcTime();
+
+ sioprintf("Setting non-blocking fio");
+ fioSetBlockMode(FIO_NOWAIT); // asynchronous file i/o
+
+ sioprintf("Starting SavefileManager");
+ _saveManager = new Ps2SaveFileManager(NULL, TO_MC, _screen);
+
+ _soundBuf = (int16*)malloc(SMP_PER_BLOCK * 2 * sizeof(int16));
+ _soundBuf2 = (int16*)malloc(SMP_PER_BLOCK * sizeof(int16));
+
+ sioprintf("Initializing ps2Input");
+ _input = new Ps2Input(this, _useMouse, _useKbd);
+
+ sio_puts("OSystem_PS2 constructor done\n");
+ _screen->wantAnim(false);
+}
+
+OSystem_PS2::~OSystem_PS2(void) {
+}
+
+void OSystem_PS2::initTimer(void) {
+ // this has to be set before the timer interrupt handler gets called
+ g_systemPs2 = this;
+
+ // first setup the two threads that get activated by the timer:
+ // the timerthread and the soundthread
+ ee_sema_t threadSema;
+ threadSema.init_count = 0;
+ threadSema.max_count = 255;
+ g_TimerThreadSema = CreateSema(&threadSema);
+ g_SoundThreadSema = CreateSema(&threadSema);
+ assert((g_TimerThreadSema >= 0) && (g_SoundThreadSema >= 0));
+
+ ee_thread_t timerThread, soundThread, thisThread;
+ ReferThreadStatus(GetThreadId(), &thisThread);
+
+ _timerStack = (uint8*)malloc(TIMER_STACK_SIZE);
+ memset(_timerStack, 0xE7, TIMER_STACK_SIZE);
+ _soundStack = (uint8*)malloc(SOUND_STACK_SIZE);
+ memset(_soundStack, 0xE7, SOUND_STACK_SIZE);
+
+ // give timer thread a higher priority than main thread
+ timerThread.initial_priority = thisThread.current_priority - 1;
+ timerThread.stack = _timerStack;
+ timerThread.stack_size = TIMER_STACK_SIZE;
+ timerThread.func = (void *)systemTimerThread;
+ //timerThread.gp_reg = _gp; // _gp is always NULL.. broken linkfile?
+ asm("move %0, $gp\n": "=r"(timerThread.gp_reg));
+
+ // soundthread's priority is higher than main- and timerthread
+ soundThread.initial_priority = thisThread.current_priority - 2;
+ soundThread.stack = _soundStack;
+ soundThread.stack_size = SOUND_STACK_SIZE;
+ soundThread.func = (void *)systemSoundThread;
+ asm("move %0, $gp\n": "=r"(soundThread.gp_reg));
+
+ _timerTid = CreateThread(&timerThread);
+ _soundTid = CreateThread(&soundThread);
+
+ assert((_timerTid >= 0) && (_soundTid >= 0));
+
+ StartThread(_timerTid, this);
+ StartThread(_soundTid, this);
+
+ // threads done, start the interrupt handler
+ AddIntcHandler( INT_TIMER0, timerInterruptHandler, 0); // 0=first handler, 9 = cause = timer0
+ EnableIntc(INT_TIMER0);
+ T0_HOLD = 0;
+ T0_COUNT = 0;
+ T0_COMP = 5859; // (busclock / 256) / 5859 = ~ 100.0064
+ T0_MODE = TIMER_MODE( 2, 0, 0, 0, 1, 1, 1, 0, 1, 1);
+}
+
+void OSystem_PS2::timerThread(void) {
+ while (1) {
+ WaitSema(g_TimerThreadSema);
+ if (_scummTimerProc)
+ _scummTimerProc(0);
+ _screen->timerTick();
+ }
+}
+
+void OSystem_PS2::soundThread(void) {
+ ee_sema_t soundSema;
+ soundSema.init_count = 1;
+ soundSema.max_count = 1;
+ _soundSema = CreateSema(&soundSema);
+ assert(_soundSema >= 0);
+ while (1) {
+ WaitSema(g_SoundThreadSema);
+
+ WaitSema(_soundSema);
+ if (_scummSoundProc) {
+ while (SjPCM_Buffered() <= 4 * SMP_PER_BLOCK) {
+ // call sound mixer
+ _scummSoundProc(_scummSoundParam, (uint8*)_soundBuf, SMP_PER_BLOCK * 2 * sizeof(int16));
+ // split data into 2 buffers, L and R
+ _soundBuf2[0] = _soundBuf[1];
+ for (uint32 cnt = 1; cnt < SMP_PER_BLOCK; cnt++) {
+ _soundBuf[cnt] = _soundBuf[cnt << 1];
+ _soundBuf2[cnt] = _soundBuf[(cnt << 1) | 1];
+ }
+ // and feed it into the SPU
+ SjPCM_Enqueue((short int*)_soundBuf, (short int*)_soundBuf2, SMP_PER_BLOCK, 1);
+ }
+ }
+ SignalSema(_soundSema);
+ }
+}
+
+bool OSystem_PS2::loadModules(void) {
+
+ _useHdd = _useMouse = _useKbd = false;
+
+ int res;
+ if ((res = SifLoadModule("rom0:SIO2MAN", 0, NULL)) < 0)
+ sioprintf("Cannot load module: SIO2MAN (%d)\n", res);
+ else if ((res = SifLoadModule("rom0:MCMAN", 0, NULL)) < 0)
+ sioprintf("Cannot load module: MCMAN (%d)\n", res);
+ else if ((res = SifLoadModule("rom0:MCSERV", 0, NULL)) < 0)
+ sioprintf("Cannot load module: MCSERV (%d)\n", res);
+ else if ((res = SifLoadModule("rom0:PADMAN", 0, NULL)) < 0)
+ sioprintf("Cannot load module: PADMAN (%d)\n", res);
+ else if ((res = SifLoadModule("rom0:LIBSD", 0, NULL)) < 0)
+ sioprintf("Cannot load module: LIBSD (%d)\n", res);
+ else if ((res = SifLoadModule(IRX_PREFIX "CDVD.IRX" IRX_SUFFIX, 0, NULL)) < 0)
+ sioprintf("Cannot load module CDVD.IRX (%d)\n", res);
+ else if ((res = SifLoadModule(IRX_PREFIX "SJPCM.IRX" IRX_SUFFIX, 0, NULL)) < 0)
+ sioprintf("Cannot load module: SJPCM.IRX (%d)\n", res);
+ else {
+ sioprintf("modules loaded\n");
+ if ((res = SifLoadModule(IRX_PREFIX "USBD.IRX" IRX_SUFFIX, 0, NULL)) < 0)
+ sioprintf("Cannot load module: USBD.IRX (%d)\n", res);
+#ifndef USE_PS2LINK
+ else if ((res = SifLoadModule(IRX_PREFIX "IOMANX.IRX" IRX_SUFFIX, 0, NULL)) < 0)
+ sioprintf("Cannot load module: IOMANX.IRX (%d)\n", res);
+#endif
+ else {
+ if ((res = SifLoadModule(IRX_PREFIX "PS2MOUSE.IRX" IRX_SUFFIX, 0, NULL)) < 0)
+ sioprintf("Cannot load module: PS2MOUSE.IRX (%d)\n", res);
+ else
+ _useMouse = true;
+ if ((res = SifLoadModule(IRX_PREFIX "PS2KBD.IRX" IRX_SUFFIX, 0, NULL)) < 0)
+ sioprintf("Cannot load module: PS2KBD.IRX (%d)\n", res);
+ else
+ _useKbd = true;
+ }
+ return true;
+ }
+ return false;
+}
+
+void OSystem_PS2::initSize(uint width, uint height, int overscale) {
+ printf("initializing new size: (%d/%d)...", width, height);
+ _screen->newScreenSize(width, height);
+ _width = width;
+ _height = height;
+ _screen->setMouseXy(width / 2, height / 2);
+ _input->newRange(0, 0, width - 1, height - 1);
+ _input->warpTo(width / 2, height / 2);
+
+ _oldMouseX = width / 2;
+ _oldMouseY = height / 2;
+ printf("done\n");
+}
+
+void OSystem_PS2::setPalette(const byte *colors, uint start, uint num) {
+ _screen->setPalette((const uint32*)colors, (uint8)start, (uint16)num);
+}
+
+void OSystem_PS2::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
+ _screen->copyScreenRect((const uint8*)buf, (uint16)pitch, (uint16)x, (uint16)y, (uint16)w, (uint16)h);
+}
+
+void OSystem_PS2::updateScreen(void) {
+ _screen->updateScreen();
+}
+
+uint32 OSystem_PS2::getMillis(void) {
+ return (uint32)msecCount;
+}
+
+void OSystem_PS2::delayMillis(uint msecs) {
+ uint64 endTime = msecCount + msecs;
+
+ while (endTime > msecCount) {
+ // idle
+ }
+}
+
+void OSystem_PS2::setTimerCallback(OSystem::TimerProc callback, int interval) {
+ if (callback && (interval != 10))
+ sioprintf("unhandled timer interval: %d\n", interval);
+ _scummTimerProc = callback;
+}
+
+int OSystem_PS2::getOutputSampleRate(void) const {
+ return 48000;
+}
+
+bool OSystem_PS2::setSoundCallback(SoundProc proc, void *param) {
+ assert(proc != NULL);
+
+ WaitSema(_soundSema);
+ _scummSoundProc = proc;
+ _scummSoundParam = param;
+ SjPCM_Play();
+ SignalSema(_soundSema);
+ return true;
+}
+
+void OSystem_PS2::clearSoundCallback(void) {
+ WaitSema(_soundSema);
+ _scummSoundProc = NULL;
+ _scummSoundParam = NULL;
+ SjPCM_Pause();
+ SignalSema(_soundSema);
+}
+
+SaveFileManager *OSystem_PS2::getSavefileManager(void) {
+ return _saveManager;
+}
+
+OSystem::MutexRef OSystem_PS2::createMutex(void) {
+ ee_sema_t newSema;
+ newSema.init_count = 1;
+ newSema.max_count = 1;
+ int resSema = CreateSema(&newSema);
+ if (resSema < 0)
+ printf("createMutex: unable to create Semaphore.\n");
+ return (MutexRef)resSema;
+}
+
+void OSystem_PS2::lockMutex(MutexRef mutex) {
+ WaitSema((int)mutex);
+}
+
+void OSystem_PS2::unlockMutex(MutexRef mutex) {
+ SignalSema((int)mutex);
+}
+
+void OSystem_PS2::deleteMutex(MutexRef mutex) {
+ DeleteSema((int)mutex);
+}
+
+void OSystem_PS2::setShakePos(int shakeOffset) {
+ _screen->setShakePos(shakeOffset);
+}
+
+bool OSystem_PS2::showMouse(bool visible) {
+ bool retVal = _mouseVisible;
+ _screen->showMouse(visible);
+ _mouseVisible = visible;
+ return retVal;
+}
+
+void OSystem_PS2::warpMouse(int x, int y) {
+ _input->warpTo((uint16)x, (uint16)y);
+ _screen->setMouseXy(x, y);
+}
+
+void OSystem_PS2::setMouseCursor(const byte *buf, uint w, uint h, int hotspot_x, int hotspot_y, byte keycolor, int cursorTargetScale) {
+ _screen->setMouseOverlay(buf, w, h, hotspot_x, hotspot_y, keycolor);
+}
+
+bool OSystem_PS2::openCD(int drive) {
+ return false;
+}
+
+bool OSystem_PS2::pollCD(void) {
+ return false;
+}
+
+void OSystem_PS2::playCD(int track, int num_loops, int start_frame, int duration) {
+}
+
+void OSystem_PS2::stopCD(void) {
+}
+
+void OSystem_PS2::updateCD(void) {
+}
+
+void OSystem_PS2::showOverlay(void) {
+ _screen->showOverlay();
+}
+
+void OSystem_PS2::hideOverlay(void) {
+ _screen->hideOverlay();
+}
+
+void OSystem_PS2::clearOverlay(void) {
+ _screen->clearOverlay();
+}
+
+void OSystem_PS2::grabOverlay(OverlayColor *buf, int pitch) {
+ _screen->grabOverlay((uint16*)buf, (uint16)pitch);
+}
+
+void OSystem_PS2::copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h) {
+ _screen->copyOverlayRect((uint16*)buf, (uint16)pitch, (uint16)x, (uint16)y, (uint16)w, (uint16)h);
+}
+
+const OSystem::GraphicsMode OSystem_PS2::_graphicsMode = { NULL, NULL, 0 };
+
+const OSystem::GraphicsMode *OSystem_PS2::getSupportedGraphicsModes(void) const {
+ return &_graphicsMode;
+}
+
+bool OSystem_PS2::setGraphicsMode(int mode) {
+ return (mode == 0);
+}
+
+int OSystem_PS2::getGraphicsMode(void) const {
+ return 0;
+}
+
+int OSystem_PS2::getDefaultGraphicsMode(void) const {
+ return 0;
+}
+
+bool OSystem_PS2::pollEvent(Event &event) {
+ bool res = _input->pollEvent(&event);
+ if (res && (event.type == EVENT_MOUSEMOVE))
+ _screen->setMouseXy(event.mouse.x, event.mouse.y);
+ return res;
+}
+
+OverlayColor OSystem_PS2::RGBToColor(uint8 r, uint8 g, uint8 b) {
+ return (r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10);
+}
+
+void OSystem_PS2::colorToRGB(OverlayColor color, uint8 &r, uint8 &g, uint8 &b) {
+ r = (color & 0x1F) << 3;
+ g = ((color >> 5) & 0x1F) << 3;
+ b = ((color >> 10) & 0x1F) << 3;
+}
+
+int16 OSystem_PS2::getHeight(void) {
+ return _height;
+}
+
+int16 OSystem_PS2::getWidth(void) {
+ return _width;
+}
+
+void OSystem_PS2::quit(void) {
+ printf("OSystem_PS2::quit\n");
+ clearSoundCallback();
+ setTimerCallback(NULL, 0);
+ SleepThread();
+}
+
+static uint32 g_timeSecs;
+static uint8 g_day, g_month, g_year;
+static uint64 g_lastTimeCheck;
+
+void readRtcTime(void) {
+ struct CdClock cdClock;
+ CDVD_ReadClock(&cdClock);
+ g_lastTimeCheck = msecCount;
+
+ if (cdClock.stat)
+ printf("Unable to read RTC time.\n");
+
+ g_timeSecs = ((FROM_BCD(cdClock.hour) * 60) + FROM_BCD(cdClock.minute)) * 60 + FROM_BCD(cdClock.second);
+ g_day = FROM_BCD(cdClock.day);
+ g_month = FROM_BCD(cdClock.month);
+ g_year = FROM_BCD(cdClock.year);
+ // todo: add something here to convert from JST to the right time zone
+ sioprintf("Got RTC time: %d:%02d:%02d %d.%d.%4d\n",
+ FROM_BCD(cdClock.hour), FROM_BCD(cdClock.minute), FROM_BCD(cdClock.second),
+ g_day, g_month, g_year + 2000);
+}
+
+time_t time(time_t *p) {
+ time_t blah;
+ memset(&blah, 0, sizeof(time_t));
+ return blah;
+}
+
+#define SECONDS_PER_DAY (24 * 60 * 60)
+
+struct tm *localtime(const time_t *p) {
+ uint32 currentSecs = g_timeSecs + (msecCount - g_lastTimeCheck) / 1000;
+ if (currentSecs >= SECONDS_PER_DAY) {
+ readRtcTime();
+ currentSecs = g_timeSecs + (msecCount - g_lastTimeCheck) / 1000;
+ }
+
+ static struct tm retStruct;
+ memset(&retStruct, 0, sizeof(retStruct));
+
+ retStruct.tm_hour = currentSecs / (60 * 60);
+ retStruct.tm_min = (currentSecs / 60) % 60;
+ retStruct.tm_sec = currentSecs % 60;
+ retStruct.tm_year = g_year + 100;
+ retStruct.tm_mday = g_day;
+ retStruct.tm_mon = g_month;
+ // tm_wday, tm_yday and tm_isdst are zero for now
+ return &retStruct;
+}
+