1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #define FORBIDDEN_SYMBOL_EXCEPTION_printf
24 #define FORBIDDEN_SYMBOL_EXCEPTION_abort
25 #define FORBIDDEN_SYMBOL_EXCEPTION_exit
26 
27 #include "Gs2dScreen.h"
28 #include <kernel.h>
29 #include <malloc.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <fileio.h>
33 #include <math.h>
34 #include "DmaPipe.h"
35 #include "GsDefs.h"
36 #include "graphics/surface.h"
37 #include "graphics/colormasks.h"
38 #include "backends/platform/ps2/ps2debug.h"
39 
40 extern void *_gp;
41 
42 enum Buffers {
43 	SCREEN = 0,
44 	MOUSE,
45 	TEXT,
46 	PRINTF
47 };
48 
49 /*
50 	Supported modes:
51 
52 	Mode #1 = SDTV progressive (NTSC: 224p / PAL: 256p)
53 	Mode #2 = SDTV interlaced  (NTSC: 448i / PAL: 512i) <- default
54 	Mode #3 = EDTV progressive (NTSC: 480p / PAL: 576p)
55 	Mode #4 = HDTV progressive (720p)
56 	Mode #5 = HDTV interlaced  (1080i)
57 	Mode #6 = VESA (640x480@60)
58 	Mode #7 = VESA (800x600@60)
59 	Mode #8 = VESA (1024x768@60)
60 */
61 
62 static ps2_mode_t ps2_mode[] = {
63 
64 	// -> w, h, interlaced, pitch, mode, vck, magh, magv, dx, dy
65 
66 	/* #1 : SDTV - progressive */
67 	{ 640, 224, 0, 640, 0x02, 2560, 4, 0, 160 /*158*/, 25 /*22*/ }, /* NTSC */
68 	{ 640, 256, 0, 640, 0x03, 2560, 4, 0, 170 /*163*/, 35 /*36*/ }, /* PAL */
69 
70 	/* #2 : SDTV - interlaced */
71 	{ 640, 448, 1, 640, 0x02, 2560, 4, 0, 156 /*158*/, 50 /*45*/ }, /* NTSC */
72 	{ 640, 512, 1, 640, 0x03, 2560, 4, 0, 170 /*163*/, 70 /*72*/ }, /* PAL */
73 
74 	/* #3 : EDTV */
75 	{ 720, 480, 0, 768, 0x50, 1440, 2, 0, 58, 35 }, /* NTSC */
76 	/* { 720, 576, 0, 768, 0x53, 1440, 2, 0, 62, 45 }, */ /* PAL : full */
77 	/* { 656, 576, 0, 704, 0x53, 1312, 2, 0, 62, 45 }, */ /* PAL : redux @ (0,0) */
78 	{ 656, 576, 0, 704, 0x53, 1312, 2, 0, 78 /*314*/, 45 }, /* PAL : redux @ center'd */
79 
80 	/* #4/#5 : HDTV */
81 	{ 1280,  720, 0, 1280, 0x52, 1280, 1, 0, 76 /*302*/, 24 },
82 	{ 1920, 1080, 1, 1920, 0x51, 1920, 1, 0, 60 /*236*/ /*238*/, 40 },
83 
84 	/* #6/#7/#8 : VESA 4:3 @ 60Hz */
85 	{ 640, 480, 0,  640, 0x1A, 1280, 2, 0, 70 /*276*/, 34 },
86 	{ 800, 600, 0,  832, 0x2B, 1600, 2, 0, 105 /*420*/, 26 },
87 	{ 1024, 768, 0, 1024, 0x3B, 2048, 2, 0, 144 /*580*/, 34 }
88 };
89 
90 #define ANIM_STACK_SIZE (1024 * 32)
91 
92 #define ORG_X 256
93 #define ORG_Y 256
94 #define ORIGIN_X (ORG_X << 4)
95 #define ORIGIN_Y (ORG_Y << 4)
96 #define TEX_POW 10
97 
98 #define SCALE(x) ((x) << 4)
99 
100 #define M_SIZE 128
101 #define M_POW 7
102 
103 static volatile uint32 g_VblankCmd = 0, g_DmacCmd = 0;
104 static int g_VblankSema, g_DmacSema, g_AnimSema;
105 static bool g_RunAnim = false;
106 static GsVertex kFullScreen[2];
107 static TexVertex kMouseTex[2] = {
108 	{ SCALE(1), SCALE(1) },
109 	{ SCALE(M_SIZE - 1), SCALE(M_SIZE - 1) }
110 };
111 static TexVertex kPrintTex[2] = {
112 	{ SCALE(1), SCALE(1) },
113 	{ SCALE(320), SCALE(200) }
114 };
115 
116 void runAnimThread(Gs2dScreen *param);
117 
vblankStartHandler(int cause)118 int vblankStartHandler(int cause) {
119 	// start of VBlank period
120 	if (g_VblankCmd) {                // is there a new image waiting?
121 		GS_DISPFB1 = g_VblankCmd; // show it.
122 		g_VblankCmd = 0;
123 		iSignalSema(g_VblankSema);
124 	}
125 	return 0;
126 }
127 
dmacHandler(int channel)128 int dmacHandler(int channel) {
129 	if (g_DmacCmd && (channel == 2)) { // GS DMA transfer finished,
130 		g_VblankCmd = g_DmacCmd;   // we want to show the image
131 		g_DmacCmd = 0;             // when the next vblank occurs
132 		iSignalSema(g_DmacSema);
133 	}
134 	return 0;
135 }
136 
vblankEndHandler(int cause)137 int vblankEndHandler(int cause) {
138 	if (g_RunAnim)
139 		iSignalSema(g_AnimSema);
140 	return 0;
141 }
142 
143 void createAnimThread(Gs2dScreen *screen);
144 
Gs2dScreen(uint16 width,uint16 height)145 Gs2dScreen::Gs2dScreen(uint16 width, uint16 height) {
146 
147 	_systemQuit = false;
148 	ee_sema_t newSema;
149 	newSema.init_count = 1;
150 	newSema.max_count = 1;
151 	g_VblankSema = CreateSema(&newSema);
152 	g_DmacSema = CreateSema(&newSema);
153 	_screenSema = CreateSema(&newSema);
154 	newSema.init_count = 0;
155 	newSema.max_count = 255;
156 	g_AnimSema = CreateSema(&newSema);
157 	assert((g_VblankSema >= 0) && (g_DmacSema >= 0) && (_screenSema >= 0) && (g_AnimSema >= 0));
158 
159 	_vblankStartId = AddIntcHandler(INT_VBLANK_START, vblankStartHandler, 0);
160 	_vblankEndId   = AddIntcHandler(INT_VBLANK_END, vblankEndHandler, 0);
161 	_dmacId        = AddDmacHandler(2, dmacHandler, 0);
162 
163 	_dmaPipe = new DmaPipe(0x2000);
164 
165 	EnableIntc(INT_VBLANK_START);
166 	EnableIntc(INT_VBLANK_END);
167 	EnableDmac(2);
168 
169 	_tvMode = 0; // force detection
170 	_gfxMode = 0;
171 
172 	_width = width;
173 	_height = height;
174 	_pitch = (width + 127) & ~127;
175 
176 	_screenBuf = (uint8 *)memalign(64, _width * _height);
177 	_overlayBuf = (uint16 *)memalign(64, _pitch * _height * 2);
178 	_clut = (uint32 *)memalign(64, 256 * 4);
179 
180 	memset(_screenBuf, 0, _width * _height);
181 	memset(_clut, 0, 256 * sizeof(uint32));
182 	_clut[1] = GS_RGBA(0xC0, 0xC0, 0xC0, 0);
183 	clearOverlay();
184 
185 	char romver[8];
186 	uint16 biosver;
187 	int fd = fioOpen("rom0:ROMVER", O_RDONLY);
188 	fioRead(fd, &romver, 8);
189 	fioClose(fd);
190 	biosver=atoi(romver);
191 	printf("ROMVER = %s\n", romver);
192 	printf("ver = %d\n", atoi(romver));
193 
194 	if (!_tvMode) { // determine TV standard first
195 		if (ConfMan.hasKey("tv_mode", "PlayStation2")) {
196 			const char *tvname = ConfMan.get("tv_mode", "PlayStation2").c_str();
197 
198 			if (strcmp("ntsc", tvname) == 0) {
199 				_tvMode = 2;
200 			}
201 			else if (strcmp("pal", tvname) == 0) {
202 				_tvMode = 1;
203 			}
204 			else
205 				_tvMode = 0;
206 		}
207 
208 		if (!_tvMode) {
209 			if (romver[4] == 'E')
210 				_tvMode = TV_PAL;
211 			else
212 				_tvMode = TV_NTSC;
213 
214 			printf("Auto-detect TV mode: PSX:%c PS2:%c\n", *(char *)(0x1FC7FF52), romver[4]);
215 		}
216 	}
217 
218 	uint8 mode;
219 	if (!_gfxMode) { // determine GFX mode next
220 		if (ConfMan.hasKey("gfx_mode", "PlayStation2")) {
221 			_gfxMode = ConfMan.getInt("gfx_mode", "PlayStation2");
222 			// TODO: free more video mem to support these modes
223 			if (_gfxMode == 4 || _gfxMode == 5) {
224 				printf("Not enough video mem: using EDTV (3)\n");
225 				_gfxMode = 3;
226 			}
227 			else
228 			if (_gfxMode == 7 || _gfxMode == 8) {
229 				printf("Not enough video mem: using VGA (6)\n");
230 				_gfxMode = 6;
231 			}
232 
233 			if (_gfxMode < 1 || _gfxMode > 8) _gfxMode = 2;
234 			else
235 			if (_gfxMode == 4 || _gfxMode == 5) _tvMode = TV_HDTV;
236 			else
237 			if (_gfxMode > 5) _tvMode = TV_VESA;
238 		}
239 		else
240 			_gfxMode = 2;
241 	}
242 
243 	// Remap Mode Index
244 	mode = _gfxMode;
245 	if (_tvMode == TV_NTSC) {
246 		mode = (mode * 2) - 1;
247 	}
248 	else if (_tvMode == TV_PAL) {
249 		mode = (mode * 2);
250 	}
251 	else if (_tvMode == TV_HDTV) {
252 		mode += 3;
253 	}
254 	else /* VESA */ {
255 		_tvMode = TV_VESA;
256 		mode += 3;
257 	}
258 	mode--;
259 
260 	switch (_tvMode) {
261 	case TV_NTSC:
262 		printf("Setting up TV mode: NTSC\n");
263 	break;
264 	case TV_PAL:
265 		printf("Setting up TV mode: PAL\n");
266 	break;
267 	case TV_HDTV:
268 		printf("Setting up TV mode: HDTV\n");
269 	break;
270 	case TV_VESA:
271 		printf("Setting up TV mode: VESA\n");
272 	break;
273 	}
274 
275 	_tvWidth = ps2_mode[mode].w;
276 	_tvHeight = ps2_mode[mode].h;
277 	_tvPitch = ps2_mode[mode].pitch;
278 
279 	printf("Setting up GFX mode: %d x %d\n", _tvWidth, _tvHeight);
280 
281 	kFullScreen[0].z = kFullScreen[1].z = 0;
282 	kFullScreen[0].x = ORIGIN_X;
283 	kFullScreen[0].y = ORIGIN_Y;
284 	kFullScreen[1].x = SCALE(_tvWidth) + ORIGIN_X;
285 	kFullScreen[1].y = SCALE(_tvHeight) + ORIGIN_Y;
286 	_blitCoords[0] = kFullScreen[0];
287 	_blitCoords[1] = kFullScreen[1];
288 	_texCoords[0].u = SCALE(1);
289 	_texCoords[0].v = SCALE(1);
290 	_texCoords[1].u = SCALE(_width);
291 	_texCoords[1].v = SCALE(_height);
292 
293 	uint32 tvFrameSize = _tvPitch * _tvHeight * 4;  // 32 bits per pixel
294 
295 	// setup frame buffer pointers
296 	_frameBufPtr[0] = 0;
297 	_frameBufPtr[1] = tvFrameSize;
298 	_clutPtrs[SCREEN] = tvFrameSize * 2;
299 	_clutPtrs[MOUSE]  = _clutPtrs[SCREEN] + 0x1000; // the cluts in PSMCT32 take up half a memory page each
300 	_clutPtrs[TEXT]   = _clutPtrs[SCREEN] + 0x2000;
301 	_texPtrs[SCREEN]  = _clutPtrs[SCREEN] + 0x3000;
302 	_texPtrs[TEXT]    = 0;                          // these buffers are stored in the alpha gaps of the frame buffers
303 	_texPtrs[MOUSE]	  = 128 * 256 * 4;
304 	_texPtrs[PRINTF]  = _texPtrs[MOUSE] + M_SIZE * M_SIZE * 4;
305 
306 	_showOverlay = false;
307 	_showMouse = false;
308 	_mouseScaleX = (_tvWidth << 8) / _width;
309 	_mouseScaleY = (_tvHeight << 8) / _height;
310 	setMouseXy(_width / 2, _height / 2);
311 	_mTraCol = 255;
312 	_shakeXOffset = 0;
313 	_shakeYOffset = 0;
314 
315 	_overlayFormat.bytesPerPixel = 2;
316 
317 	_overlayFormat.rLoss = 3;
318 	_overlayFormat.gLoss = 3;
319 	_overlayFormat.bLoss = 3;
320 	_overlayFormat.aLoss = 8; // 7
321 
322 	_overlayFormat.rShift = 0;
323 	_overlayFormat.gShift = 5;
324 	_overlayFormat.bShift = 10;
325 	_overlayFormat.aShift = 0; // 15
326 
327 	// setup hardware now.
328 	GS_CSR = CSR_RESET; // Reset GS
329 	asm ("sync.p");
330 	GS_CSR = 0;
331 	GsPutIMR(0x7F00);
332 
333 
334 	if (biosver < 220 && ps2_mode[mode].mode == 0x53) { // EDTV PAL : mode not in BIOS < 2.20
335 	                                                    // no worries... we work in magic ;-)
336 		/* 720x576p */
337 		asm ("di");
338 		asm ("sync.l; sync.p");
339 		GS_PMODE = 0;
340 		asm ("sync.l; sync.p");
341 		GS_SMODE1 = 0x1742890504;
342 		asm ("sync.l; sync.p");
343 		GS_SMODE2 = 0;
344 		GS_SYNCH1 = 0x402E02003C827;
345 		asm ("sync.l; sync.p");
346 		GS_SYNCH2 = 0x19CA67;
347 		asm ("sync.l; sync.p");
348 		GS_SYNCV = 0xA9000002700005;
349 		asm ("sync.l; sync.p");
350 		GS_SRFSH = 4;
351 		asm ("sync.l; sync.p");
352 		GS_SMODE1 = 0x1742880504;
353 		asm ("sync.l; sync.p");
354 		asm ("sync.l; sync.p");
355 		GS_SMODE2 = 0;
356 		asm("ei");
357 	}
358 	else { // BIOS
359 		SetGsCrt(ps2_mode[mode].interlaced, ps2_mode[mode].mode, 0); // ps2_mode[mode].interlaced);
360 	}
361 
362 	asm("di");
363 	GS_PMODE = GS_SET_PMODE(1, 0, 1, 1, 0, 255);
364 	GS_BGCOLOUR = GS_RGBA(0, 0, 0, 0);
365 	GS_DISPLAY1 = GS_SET_DISPLAY_MODE(ps2_mode[mode]);
366 	asm("ei");
367 
368 	_curDrawBuf = 0;
369 
370 	_dmaPipe->setOrigin(ORIGIN_X, ORIGIN_Y);
371 	_dmaPipe->setConfig(1, 0, 1);
372 	_dmaPipe->setScissorRect(0, 0, _tvWidth - 1, _tvHeight - 1);
373 	_dmaPipe->setDrawBuffer(_frameBufPtr[_curDrawBuf], _tvPitch, GS_PSMCT24, 0);
374 	_dmaPipe->flush();
375 
376 	_clutChanged = _screenChanged = _overlayChanged = true;
377 
378 	clearPrintfOverlay();
379 	updateScreen();
380 
381 	createAnimTextures();
382 
383 	// create animation thread
384 	#ifdef __NEW_PS2SDK__
385 	ee_thread_t animThread;
386 	ee_thread_status_t thisThread;
387 	#else
388 	ee_thread_t animThread, thisThread;
389 	#endif
390 	ReferThreadStatus(GetThreadId(), &thisThread);
391 
392 	_animStack = memalign(64, ANIM_STACK_SIZE);
393 	animThread.initial_priority = thisThread.current_priority - 3;
394 	animThread.stack      = _animStack;
395 	animThread.stack_size = ANIM_STACK_SIZE;
396 	animThread.func       = (void *)runAnimThread;
397 	animThread.gp_reg     = &_gp;
398 
399 	_animTid = CreateThread(&animThread);
400 	assert(_animTid >= 0);
401 	StartThread(_animTid, this);
402 }
403 
quit(void)404 void Gs2dScreen::quit(void) {
405 	_systemQuit = true;
406 	#ifdef __NEW_PS2SDK__
407 	ee_thread_status_t statAnim;
408 	#else
409 	ee_thread_t statAnim;
410 	#endif
411 	do { // wait until thread called ExitThread()
412 		SignalSema(g_AnimSema);
413 		ReferThreadStatus(_animTid, &statAnim);
414 	} while (statAnim.status != 0x10);
415 	DeleteThread(_animTid);
416 	free(_animStack);
417 	_dmaPipe->waitForDma(); // wait for dmac and vblank for the last time
418 	while (g_DmacCmd || g_VblankCmd);
419 
420 	sioprintf("kill handlers\n");
421 	DisableIntc(INT_VBLANK_START);
422 	DisableIntc(INT_VBLANK_END);
423 	DisableDmac(2);
424 	RemoveIntcHandler(INT_VBLANK_START, _vblankStartId);
425 	RemoveIntcHandler(INT_VBLANK_END, _vblankEndId);
426 	RemoveDmacHandler(2, _dmacId);
427 
428 	DeleteSema(g_VblankSema);
429 	DeleteSema(g_DmacSema);
430 	DeleteSema(g_AnimSema);
431 }
432 
createAnimTextures(void)433 void Gs2dScreen::createAnimTextures(void) {
434 	uint8 *buf = (uint8 *)memalign(64, 16 * 64);
435 	memset(buf, 0, 16 * 64);
436 	uint32 vramDest = _texPtrs[TEXT];
437 	for (int i = 0; i < 16; i++) {
438 		uint32 *destPos = (uint32 *)buf;
439 		for (int ch = 15; ch >= 0; ch--) {
440 			const uint32 *src = (const uint32 *)(_binaryData + ((_binaryPattern[i] >> ch) & 1) * 4 * 14);
441 			for (int line = 0; line < 14; line++)
442 				destPos[line << 4] = src[line];
443 			destPos++;
444 		}
445 		if (!(i & 1))
446 			_dmaPipe->uploadTex( vramDest, 128, 0, 0, GS_PSMT4HH, buf, 128, 16);
447 		else {
448 			_dmaPipe->uploadTex( vramDest, 128, 0, 0, GS_PSMT4HL, buf, 128, 16);
449 			vramDest += 128 * 16 * 4;
450 		}
451 		_dmaPipe->flush();
452 		_dmaPipe->waitForDma();
453 	}
454 	_dmaPipe->uploadTex(_clutPtrs[TEXT], 64, 0, 0, GS_PSMCT32, _binaryClut, 8, 2);
455 	_dmaPipe->flush();
456 	free(buf);
457 }
458 
newScreenSize(uint16 width,uint16 height)459 void Gs2dScreen::newScreenSize(uint16 width, uint16 height) {
460 	if ((width == _width) && (height == _height))
461 		return;
462 
463 	WaitSema(g_DmacSema);
464 	WaitSema(g_VblankSema);
465 
466 	_dmaPipe->flush();
467 	_width = width;
468 	_height = height;
469 	_pitch = (width + 127) & ~127;
470 
471 	// malloc new buffers
472 	free(_screenBuf);
473 	free(_overlayBuf);
474 	_screenBuf = (uint8 *)memalign(64, _width * _height);
475 	_overlayBuf = (uint16 *)memalign(64, _width * _height * 2);
476 	memset(_screenBuf, 0, _width * height);
477 	memset(_overlayBuf, 0, _width * height * 2);
478 	memset(_clut, 0, 256 * sizeof(uint32));
479 	_clut[1] = GS_RGBA(0xC0, 0xC0, 0xC0, 0);
480 
481 	// clear video ram
482 	_dmaPipe->uploadTex(_clutPtrs[MOUSE], 64, 0, 0, GS_PSMCT32, _clut, 16, 16);
483 	_dmaPipe->uploadTex(_clutPtrs[SCREEN], 64, 0, 0, GS_PSMCT32, _clut, 16, 16);
484 	_dmaPipe->uploadTex(_texPtrs[SCREEN], _width, 0, 0, GS_PSMCT16, _overlayBuf, _width, _height);
485 	_dmaPipe->flush();
486 	_dmaPipe->waitForDma();
487 
488 	/*_clutChanged = */ _screenChanged = _overlayChanged = false;
489 	_clutChanged = true; // reload palette on scr change
490 
491 	_texCoords[1].u = SCALE(_width);
492 	_texCoords[1].v = SCALE(_height);
493 	_mouseScaleX = (_tvWidth << 8) / _width;
494 	_mouseScaleY = (_tvHeight << 8) / _height;
495 	setMouseXy(_width / 2, _height / 2);
496 
497 	SignalSema(g_VblankSema);
498 	SignalSema(g_DmacSema);
499 }
500 
copyScreenRect(const uint8 * buf,int pitch,int x,int y,int w,int h)501 void Gs2dScreen::copyScreenRect(const uint8 *buf, int pitch, int x, int y, int w, int h) {
502 	if (x < 0) {
503 		w += x;
504 		buf -= x;
505 		x = 0;
506 	}
507 	if (y < 0) {
508 		h += y;
509 		buf -= y * pitch;
510 		y = 0;
511 	}
512 	if (x + w > _width)
513 		w = (int)_width - x;
514 	if (y + h > _height)
515 		h = (int)_height - y;
516 
517 	if ((w > 0) && (h > 0)) {
518 		WaitSema(g_DmacSema);
519 		uint8 *dest = _screenBuf + y * _width + x;
520 		if ((w == pitch) && (pitch == _width))
521 			memcpy(dest, buf, w * h);
522 		else
523 			for (int cnt = 0; cnt < h; cnt++) {
524 				memcpy(dest, buf, w);
525 				buf += pitch;
526 				dest += _width;
527 			}
528 		_screenChanged = true;
529 		SignalSema(g_DmacSema);
530 	}
531 }
532 
lockScreen()533 Graphics::Surface *Gs2dScreen::lockScreen() {
534 	WaitSema(g_DmacSema);
535 
536 	// -not- _pitch; ! It's EE mem, not Tex
537 	_framebuffer.init(_width, _height, _width, _screenBuf, Graphics::PixelFormat::createFormatCLUT8());
538 	return &_framebuffer;
539 }
540 
unlockScreen()541 void Gs2dScreen::unlockScreen() {
542 	_screenChanged = true;
543 	SignalSema(g_DmacSema);
544 }
545 
setPalette(const uint8 * pal,uint8 start,uint16 num)546 void Gs2dScreen::setPalette(const uint8 *pal, uint8 start, uint16 num) {
547 	assert(start + num <= 256);
548 
549 	WaitSema(g_DmacSema);
550 	for (uint16 cnt = 0; cnt < num; cnt++) {
551 		uint16 dest = start + cnt;
552 		dest = (dest & 0xE7) | ((dest & 0x8) << 1) | ((dest & 0x10) >> 1); // rearrange like the GS expects it
553 
554 		uint32 color = pal[0] | (pal[1] << 8) | (pal[2] << 16);
555 		_clut[dest] = color;
556 		pal += 3;
557 	}
558 	_clutChanged = true;
559 	SignalSema(g_DmacSema);
560 }
561 
grabPalette(uint8 * pal,uint8 start,uint16 num) const562 void Gs2dScreen::grabPalette(uint8 *pal, uint8 start, uint16 num) const {
563 	assert(start + num <= 256);
564 	for (uint16 cnt = 0; cnt < num; cnt++) {
565 		uint16 src = start + cnt;
566 		src = (src & 0xE7) | ((src & 0x8) << 1) | ((src & 0x10) >> 1);
567 
568 		uint32 color = _clut[src];
569 		pal[0] = (color >>  0) & 0xFF;
570 		pal[1] = (color >>  8) & 0xFF;
571 		pal[2] = (color >> 16) & 0xFF;
572 		pal += 3;
573 	}
574 }
575 
uploadToVram(void)576 void Gs2dScreen::uploadToVram(void) {
577 	if (_clutChanged) {
578 		_clutChanged = false;
579 		uint32 tmp = _clut[_mTraCol];
580 		_clut[_mTraCol] = GS_RGBA(0, 0, 0, 0x80); // this mouse color is transparent
581 		_dmaPipe->uploadTex(_clutPtrs[MOUSE], 64, 0, 0, GS_PSMCT32, _clut, 16, 16);
582 		_dmaPipe->flush();
583 		_dmaPipe->waitForDma();
584 		_clut[_mTraCol] = tmp;
585 
586 		_dmaPipe->uploadTex(_clutPtrs[SCREEN], 64, 0, 0, GS_PSMCT32, _clut, 16, 16);
587 	}
588 
589 	if (_showOverlay) {
590 		if (_overlayChanged) {
591 			_dmaPipe->uploadTex(_texPtrs[SCREEN], _width, 0, 0, GS_PSMCT16, _overlayBuf, _width, _height);
592 			_overlayChanged = false;
593 		}
594 	} else {
595 		if (_screenChanged) {
596 			_dmaPipe->uploadTex(_texPtrs[SCREEN], _pitch, 0, 0, GS_PSMT8, _screenBuf, _width, _height);
597 			_screenChanged = false;
598 		}
599 	}
600 }
601 
602 extern "C" void _ps2sdk_alloc_lock(void);
603 extern "C" void _ps2sdk_alloc_unlock(void);
604 
updateScreen(void)605 void Gs2dScreen::updateScreen(void) {
606 	WaitSema(_screenSema);
607 	uploadToVram();
608 	if (!g_RunAnim) {
609 		_dmaPipe->flatRect(kFullScreen + 0, kFullScreen + 1, GS_RGBA(0, 0, 0, 0)); // clear screen
610 
611 		if (_showOverlay) {
612 			_dmaPipe->setTex(_texPtrs[SCREEN], _width, TEX_POW, TEX_POW, GS_PSMCT16, 0, 0, 0, 0);
613 			_dmaPipe->textureRect(kFullScreen + 0, kFullScreen + 1, _texCoords + 0, _texCoords + 1);
614 		} else {
615 			_dmaPipe->setTex(_texPtrs[SCREEN], _pitch, TEX_POW, TEX_POW, GS_PSMT8, _clutPtrs[SCREEN], 0, 64, GS_PSMCT32);
616 			_dmaPipe->textureRect(_blitCoords + 0, _blitCoords + 1, _texCoords + 0, _texCoords + 1);
617 		}
618 
619 		if (_showMouse) {
620 			GsVertex mouseCoords[2];
621 			mouseCoords[0].x = (((_mouseX - _hotSpotX) * _mouseScaleX + 8) >> 4) + ORIGIN_X;
622 			mouseCoords[0].y = (((_mouseY - _hotSpotY) * _mouseScaleY + 8) >> 4) + ORIGIN_Y;
623 			mouseCoords[1].x = mouseCoords[0].x + (((M_SIZE * _mouseScaleX) + 8) >> 4);
624 			mouseCoords[1].y = mouseCoords[0].y + (((M_SIZE * _mouseScaleY) + 8) >> 4);
625 			mouseCoords[0].z = mouseCoords[1].z = 0;
626 
627 			_dmaPipe->setTex(_texPtrs[MOUSE], M_SIZE, M_POW, M_POW, GS_PSMT8H, _clutPtrs[MOUSE], 0, 64, GS_PSMCT32);
628 			_dmaPipe->textureRect(mouseCoords + 0, mouseCoords + 1, kMouseTex + 0, kMouseTex + 1);
629 		}
630 
631 		_dmaPipe->setTex(_texPtrs[PRINTF], 3 * 128, TEX_POW, TEX_POW, GS_PSMT8H, _clutPtrs[TEXT], 0, 64, GS_PSMCT32);
632 		_dmaPipe->textureRect(kFullScreen + 0, kFullScreen + 1, kPrintTex + 0, kPrintTex + 1);
633 
634 #if 0
635 		_ps2sdk_alloc_lock();
636 		uint32 heapTop = (uint32)ps2_sbrk(0);
637 		_ps2sdk_alloc_unlock();
638 		if (heapTop != (uint32)-1) {
639 			float yPos = (((float)heapTop) / (32 * 1024 * 1024)) * _tvHeight;
640 			GsVertex bottom = { SCALE(_tvWidth - 40) + ORIGIN_X, SCALE(_tvHeight) + ORIGIN_Y, 0 };
641 			GsVertex top = { SCALE(_tvWidth) + ORIGIN_X, 0, 0 };
642 			top.y = SCALE((uint16)(_tvHeight - yPos)) + ORIGIN_Y;
643 			_dmaPipe->flatRect(&bottom, &top, GS_RGBA(0x80, 0, 0, 0x40));
644 		}
645 #endif
646 
647 		WaitSema(g_DmacSema);	// wait for dma transfer, if there's one running
648 		WaitSema(g_VblankSema); // wait if there's already an image waiting for vblank
649 
650 		g_DmacCmd = GS_SET_DISPFB(_frameBufPtr[_curDrawBuf], _tvPitch, GS_PSMCT24); // put it here for dmac/vblank handler
651 		_dmaPipe->flush();
652 		_curDrawBuf ^= 1;
653 		_dmaPipe->setDrawBuffer(_frameBufPtr[_curDrawBuf], _tvPitch, GS_PSMCT24, 0);
654 	} else
655 		_dmaPipe->flush();
656 	SignalSema(_screenSema);
657 }
658 
showOverlay(void)659 void Gs2dScreen::showOverlay(void) {
660 	_showOverlay = true;
661 	clearOverlay();
662 }
663 
hideOverlay(void)664 void Gs2dScreen::hideOverlay(void) {
665 	_screenChanged = true;
666 	_showOverlay = false;
667 }
668 
getOverlayFormat(void)669 Graphics::PixelFormat Gs2dScreen::getOverlayFormat(void) {
670 	return _overlayFormat;
671 	// return Graphics::createPixelFormat<1555>();
672 }
673 
getOverlayWidth(void)674 int16 Gs2dScreen::getOverlayWidth(void) {
675 	return _width; // _videoMode.overlayWidth;
676 }
677 
getOverlayHeight(void)678 int16 Gs2dScreen::getOverlayHeight(void) {
679 	return _height; // _videoMode.overlayHeight;
680 }
681 
setShakePos(int shakeXOffset,int shakeYOffset)682 void Gs2dScreen::setShakePos(int shakeXOffset, int shakeYOffset) {
683 	_shakeXOffset = (shakeXOffset * _mouseScaleX) >> 8;
684 	_shakeYOffset = (shakeYOffset * _mouseScaleY) >> 8;
685 	_blitCoords[0].x = SCALE(_shakeXOffset) + ORIGIN_X;
686 	_blitCoords[0].y = SCALE(_shakeYOffset) + ORIGIN_Y;
687 	_blitCoords[1].x = SCALE(_tvWidth + _shakeXOffset) + ORIGIN_X;
688 	_blitCoords[1].y = SCALE(_tvHeight + _shakeYOffset) + ORIGIN_Y;
689 }
690 
copyPrintfOverlay(const uint8 * buf)691 void Gs2dScreen::copyPrintfOverlay(const uint8 *buf) {
692 	assert(!((uint32)buf & 63));
693 	_dmaPipe->uploadTex(_texPtrs[PRINTF], 3 * 128, 0, 0, GS_PSMT8H, buf, 320, 200);
694 	_dmaPipe->flush();
695 	_dmaPipe->waitForDma();
696 }
697 
clearPrintfOverlay(void)698 void Gs2dScreen::clearPrintfOverlay(void) {
699 	uint8 *tmpBuf = (uint8 *)memalign(64, 320 * 200);
700 	memset(tmpBuf, 4, 320 * 200);
701 	_dmaPipe->uploadTex(_texPtrs[PRINTF], 3 * 128, 0, 0, GS_PSMT8H, tmpBuf, 320, 200);
702 	_dmaPipe->flush();
703 	_dmaPipe->waitForDma();
704 	free(tmpBuf);
705 }
706 
copyOverlayRect(const byte * buf,uint16 pitch,uint16 x,uint16 y,uint16 w,uint16 h)707 void Gs2dScreen::copyOverlayRect(const byte *buf, uint16 pitch, uint16 x, uint16 y, uint16 w, uint16 h) {
708 	WaitSema(g_DmacSema);
709 
710 	// warning("_overlayBuf [dst] = %x", _overlayBuf);
711 	// warning("buf [src] = %x", buf);
712 
713 	// warning("pitch=%d _width=%d - x=%d y=%d w=%d h=%d",
714 	//	pitch, _width, x, y, w, h);
715 
716 	if (x >= 65535) x=0;
717 	if (y >= 65535) y=0;
718 
719 	_overlayChanged = true;
720 	uint16 *dest = _overlayBuf + y * _width + x;
721 	for (uint32 cnt = 0; cnt < h; cnt++) {
722 		memcpy(dest, buf, w * 2);
723 		dest += _width;
724 		buf += pitch;
725 	}
726 	SignalSema(g_DmacSema);
727 }
728 
clearOverlay(void)729 void Gs2dScreen::clearOverlay(void) {
730 	WaitSema(g_DmacSema);
731 	_overlayChanged = true;
732 	// first convert our clut to 16 bit RGBA for the overlay...
733 	uint16 palette[256];
734 	for (uint32 cnt = 0; cnt < 256; cnt++) {
735 		uint32 rgba = _clut[(cnt & 0xE7) | ((cnt & 0x8) << 1) | ((cnt & 0x10) >> 1)];
736 		palette[cnt] = ((rgba >> 3) & 0x1F) | (((rgba >> 11) & 0x1F) << 5) | (((rgba >> 19) & 0x1F) << 10);
737 	}
738 	// now copy the current screen over
739 	for (int cnt = 0; cnt < _width * _height; cnt++)
740 		_overlayBuf[cnt] = palette[_screenBuf[cnt]];
741 	SignalSema(g_DmacSema);
742 }
743 
grabOverlay(byte * buf,uint16 pitch)744 void Gs2dScreen::grabOverlay(byte *buf, uint16 pitch) {
745 	uint16 *src = _overlayBuf;
746 	for (uint32 cnt = 0; cnt < _height; cnt++) {
747 		memcpy(buf, src, _width * 2);
748 		buf += pitch;
749 		src += _width;
750 	}
751 }
752 
setMouseOverlay(const uint8 * buf,uint16 width,uint16 height,uint16 hotSpotX,uint16 hotSpotY,uint8 transpCol)753 void Gs2dScreen::setMouseOverlay(const uint8 *buf, uint16 width, uint16 height, uint16 hotSpotX, uint16 hotSpotY, uint8 transpCol) {
754 	assert((width <= M_SIZE) && (height <= M_SIZE));
755 
756 	_hotSpotX = hotSpotX;
757 	_hotSpotY = hotSpotY;
758 	if (_mTraCol != transpCol) {
759 		_mTraCol = transpCol;
760 		_clutChanged = true;
761 	}
762 	uint8 *bufCopy = (uint8 *)memalign(64, M_SIZE * M_SIZE); // make a copy to align to 64 bytes
763 	memset(bufCopy, _mTraCol, M_SIZE * M_SIZE);
764 	for (int cnt = 0; cnt < height; cnt++)
765 		memcpy(bufCopy + cnt * M_SIZE, buf + cnt * width, width);
766 
767 	_dmaPipe->uploadTex( _texPtrs[MOUSE], M_SIZE, 0, 0, GS_PSMT8H, bufCopy, M_SIZE, M_SIZE);
768 	_dmaPipe->flush();
769 	_dmaPipe->waitForDma(); // make sure all data has been transferred when we free bufCopy
770 	free(bufCopy);
771 }
772 
showMouse(bool show)773 void Gs2dScreen::showMouse(bool show) {
774 	_showMouse = show;
775 }
776 
setMouseXy(int16 x,int16 y)777 void Gs2dScreen::setMouseXy(int16 x, int16 y) {
778 	_mouseX = x;
779 	_mouseY = y;
780 }
781 /*
782 uint8 Gs2dScreen::tvMode(void) {
783 	return _tvMode;
784 }
785 */
getWidth(void)786 uint16 Gs2dScreen::getWidth(void) {
787 	return _width;
788 }
789 
getHeight(void)790 uint16 Gs2dScreen::getHeight(void) {
791 	return _height;
792 }
793 
wantAnim(bool runIt)794 void Gs2dScreen::wantAnim(bool runIt) {
795 	g_RunAnim = runIt;
796 }
797 
798 #define LINE_SPACE 20
799 #define SCRL_TIME 8
800 #define V 1000
801 #define Z_TRANSL 65
802 
playAnim(void)803 void Gs2dScreen::playAnim(void) {
804 	// animate zeros and ones while game accesses memory card, etc.
805 	g_RunAnim = false;
806 	float yPos   = 0.0;
807 	uint8 texSta = 0;
808 	float scrlSpeed = (_tvMode == TV_PAL) ? (_tvHeight / (SCRL_TIME * 50.0)) : (_tvHeight / (SCRL_TIME * 60.0));
809 	uint8 texMax = (_tvHeight / LINE_SPACE) + (ORG_Y / LINE_SPACE);
810 	TexVertex texNodes[4] = {
811 		{ SCALE(1),   SCALE(1) }, { SCALE(1),   SCALE(14) },
812 		{ SCALE(128), SCALE(1) }, { SCALE(128), SCALE(14) }
813 	};
814 	float angleStep = ((2 * M_PI) / _tvHeight);
815 
816 	while (!_systemQuit) {
817 		do {
818 			WaitSema(g_AnimSema);
819 		} while ((!_systemQuit) && (!g_RunAnim));
820 
821 		if (_systemQuit)
822 			break;
823 
824 		if (PollSema(_screenSema) > 0) { // make sure no thread is currently drawing
825 			WaitSema(g_DmacSema);   // dma transfers have to be finished
826 			WaitSema(g_VblankSema); // wait for image, if there is one...
827 
828 			// redraw the engine's last frame
829 			_dmaPipe->flatRect(kFullScreen + 0, kFullScreen + 1, GS_RGBA(0, 0, 0, 0)); // clear screen
830 
831 			if (_showOverlay) {
832 				_dmaPipe->setTex(_texPtrs[SCREEN], _width, TEX_POW, TEX_POW, GS_PSMCT16, 0, 0, 0, 0);
833 				_dmaPipe->textureRect(kFullScreen + 0, kFullScreen + 1, _texCoords + 0, _texCoords + 1);
834 			} else {
835 				_dmaPipe->setTex(_texPtrs[SCREEN], _pitch, TEX_POW, TEX_POW, GS_PSMT8, _clutPtrs[SCREEN], 0, 64, GS_PSMCT32);
836 				_dmaPipe->textureRect(_blitCoords + 0, _blitCoords + 1, _texCoords + 0, _texCoords + 1);
837 			}
838 
839 			_dmaPipe->setTex(_texPtrs[PRINTF], 3 * 128, TEX_POW, TEX_POW, GS_PSMT8H, _clutPtrs[TEXT], 0, 64, GS_PSMCT32);
840 			_dmaPipe->textureRect(kFullScreen + 0, kFullScreen + 1, kPrintTex + 0, kPrintTex + 1);
841 
842 			if (_showMouse) {
843 				GsVertex mouseCoords[2];
844 				mouseCoords[0].x = (((_mouseX - _hotSpotX) * _mouseScaleX + 8) >> 4) + ORIGIN_X;
845 				mouseCoords[0].y = (((_mouseY - _hotSpotY) * _mouseScaleY + 8) >> 4) + ORIGIN_Y;
846 				mouseCoords[1].x = mouseCoords[0].x + (((M_SIZE * _mouseScaleX) + 8) >> 4);
847 				mouseCoords[1].y = mouseCoords[0].y + (((M_SIZE * _mouseScaleY) + 8) >> 4);
848 				mouseCoords[0].z = mouseCoords[1].z = 0;
849 
850 				_dmaPipe->setTex(_texPtrs[MOUSE], M_SIZE, M_POW, M_POW, GS_PSMT8H, _clutPtrs[MOUSE], 0, 64, GS_PSMCT32);
851 				_dmaPipe->textureRect(mouseCoords + 0, mouseCoords + 1, kMouseTex + 0, kMouseTex + 1);
852 			}
853 
854 			_dmaPipe->setAlphaBlend(SOURCE_COLOR, ZERO_COLOR, SOURCE_ALPHA, DEST_COLOR, 0);
855 			yPos -= scrlSpeed;
856 			if (yPos <= -LINE_SPACE) {
857 				yPos += LINE_SPACE;
858 				texSta++;
859 			}
860 
861 			float drawY = yPos;
862 
863 			for (int i = 0; i < texMax; i++) {
864 				uint8 texIdx = (texSta + i) & 0xF;
865 
866 				float x[4] = { -64.0, -64.0, 64.0, 64.0 };
867 				float y[4];
868 				y[0] = y[2] = drawY - _tvHeight / 2 - LINE_SPACE / 2;
869 				y[1] = y[3] = y[0] + LINE_SPACE;
870 				float z[4];
871 				GsVertex nodes[4];
872 
873 				float angle = M_PI / 2 + angleStep * drawY;
874 				float rotSin = sinf(angle);
875 				float rotCos = cosf(angle);
876 				for (int coord = 0; coord < 4; coord++) {
877 					z[coord] = rotCos * x[coord];
878 					x[coord] = rotSin * x[coord];
879 
880 					nodes[coord].z = 0;
881 					nodes[coord].x = (uint16)(((V * x[coord]) / (z[coord] + V + Z_TRANSL)) * 16);
882 					nodes[coord].y = (uint16)(((V * y[coord]) / (z[coord] + V + Z_TRANSL)) * 16);
883 					nodes[coord].x += SCALE(_tvWidth - 80 + ORG_X);
884 					nodes[coord].y += SCALE(_tvHeight / 2 + ORG_Y);
885 				}
886 
887 				uint32 texPtr = _texPtrs[TEXT] + 128 * 16 * 4 * (texIdx >> 1);
888 				if (texIdx & 1)
889 					_dmaPipe->setTex(texPtr, 128, 7, 4, GS_PSMT4HL, _clutPtrs[TEXT], 0, 64, GS_PSMCT32);
890 				else
891 					_dmaPipe->setTex(texPtr, 128, 7, 4, GS_PSMT4HH, _clutPtrs[TEXT], 0, 64, GS_PSMCT32);
892 
893 				_dmaPipe->textureRect(nodes + 0, nodes + 1, nodes + 2, nodes + 3,
894 					texNodes + 0, texNodes + 1, texNodes + 2, texNodes + 3, GS_RGBA(0x80, 0x80, 0x80, 0x80));
895 
896 				drawY += LINE_SPACE;
897 			}
898 			g_DmacCmd = GS_SET_DISPFB(_frameBufPtr[_curDrawBuf], _tvPitch, GS_PSMCT24); // put it here for dmac/vblank handler
899 			_dmaPipe->flush();
900 			_curDrawBuf ^= 1;
901 			_dmaPipe->setDrawBuffer(_frameBufPtr[_curDrawBuf], _tvPitch, GS_PSMCT24, 0);
902 			_dmaPipe->setAlphaBlend(DEST_COLOR, ZERO_COLOR, SOURCE_ALPHA, SOURCE_COLOR, 0);
903 
904 			SignalSema(_screenSema);
905 		}
906 	}
907 	ExitThread();
908 }
909 
runAnimThread(Gs2dScreen * param)910 void runAnimThread(Gs2dScreen *param) {
911 	param->playAnim();
912 }
913 
914 // data for the animated zeros and ones...
915 const uint8 Gs2dScreen::_binaryData[4 * 14 * 2] = {
916 	// figure zero
917 	0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x22, 0x22, 0x00, 0x31, 0x13,
918 	0x31, 0x13, 0x20, 0x02, 0x22, 0x02, 0x31, 0x13, 0x33, 0x13, 0x20, 0x02, 0x20, 0x02,
919 	0x31, 0x33, 0x31, 0x13, 0x20, 0x22, 0x20, 0x02, 0x31, 0x13, 0x31, 0x13, 0x00, 0x22,
920 	0x22, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11,
921 	// figure one
922 	0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x20, 0x02, 0x00, 0x11, 0x33,
923 	0x13, 0x11, 0x22, 0x22, 0x02, 0x00, 0x11, 0x31, 0x13, 0x11, 0x00, 0x20, 0x02, 0x00,
924 	0x11, 0x31, 0x13, 0x11, 0x00, 0x20, 0x02, 0x00, 0x11, 0x31, 0x13, 0x11, 0x00, 0x20,
925 	0x02, 0x00, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11
926 };
927 
928 const uint16 Gs2dScreen::_binaryPattern[16] = {
929 	0xD992, 0x344B, 0xA592, 0x110D,
930 	0x9234, 0x2326, 0x5199, 0xC8A6,
931 	0x4D29, 0x18B0, 0xA5AA, 0x2949,
932 	0x6DB3, 0xB2AA, 0x64A4, 0x3329
933 };
934 
935 const uint32 Gs2dScreen::_binaryClut[16] __attribute__((aligned(64))) = {
936 	GS_RGBA(   0,    0,    0, 0x40),
937 	GS_RGBA(  50,   50,   50, 0x40),
938 	GS_RGBA( 204,  204, 0xFF, 0x40),
939 	GS_RGBA( 140,  140, 0xFF, 0x40),
940 
941 	GS_RGBA(   0,    0,    0, 0x80), // scrPrintf: transparent
942 	GS_RGBA(   0,    0,    0, 0x20), // scrPrintf: semitransparent
943 	GS_RGBA(0xC0, 0xC0, 0xC0,    0), // scrPrintf: red
944 	GS_RGBA(0x16, 0x16, 0xF0,    0), // scrPrintf: blue
945 
946 	GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), // unused
947 	GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
948 	GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80),
949 	GS_RGBA(0xFF, 0xFF, 0xFF, 0x80), GS_RGBA(0xFF, 0xFF, 0xFF, 0x80)
950 };
951