1 //---------------------------------------------------------------------------
2 // NEOPOP : Emulator as in Dreamland
3 //
4 // Copyright (c) 2001-2002 by neopop_uk
5 //---------------------------------------------------------------------------
6 
7 //---------------------------------------------------------------------------
8 //	This program is free software; you can redistribute it and/or modify
9 //	it under the terms of the GNU General Public License as published by
10 //	the Free Software Foundation; either version 2 of the License, or
11 //	(at your option) any later version. See also the license.txt file for
12 //	additional informations.
13 //---------------------------------------------------------------------------
14 
15 #include <stdlib.h>
16 #include <string.h>
17 #include <math.h>
18 
19 #include "neopop.h"
20 #include "mem.h"
21 #include "gfx.h"
22 #include "interrupt.h"
23 #include "dma.h"
24 #include "TLCS-900h/TLCS900h_registers.h"
25 #include "../state.h"
26 #include "../video.h"
27 #ifdef MSB_FIRST
28 #include "../masmem.h"
29 #endif
30 
31 static const unsigned char mirrored[] = {
32     0x00, 0x40, 0x80, 0xc0, 0x10, 0x50, 0x90, 0xd0,
33     0x20, 0x60, 0xa0, 0xe0, 0x30, 0x70, 0xb0, 0xf0,
34     0x04, 0x44, 0x84, 0xc4, 0x14, 0x54, 0x94, 0xd4,
35     0x24, 0x64, 0xa4, 0xe4, 0x34, 0x74, 0xb4, 0xf4,
36     0x08, 0x48, 0x88, 0xc8, 0x18, 0x58, 0x98, 0xd8,
37     0x28, 0x68, 0xa8, 0xe8, 0x38, 0x78, 0xb8, 0xf8,
38     0x0c, 0x4c, 0x8c, 0xcc, 0x1c, 0x5c, 0x9c, 0xdc,
39     0x2c, 0x6c, 0xac, 0xec, 0x3c, 0x7c, 0xbc, 0xfc,
40     0x01, 0x41, 0x81, 0xc1, 0x11, 0x51, 0x91, 0xd1,
41     0x21, 0x61, 0xa1, 0xe1, 0x31, 0x71, 0xb1, 0xf1,
42     0x05, 0x45, 0x85, 0xc5, 0x15, 0x55, 0x95, 0xd5,
43     0x25, 0x65, 0xa5, 0xe5, 0x35, 0x75, 0xb5, 0xf5,
44     0x09, 0x49, 0x89, 0xc9, 0x19, 0x59, 0x99, 0xd9,
45     0x29, 0x69, 0xa9, 0xe9, 0x39, 0x79, 0xb9, 0xf9,
46     0x0d, 0x4d, 0x8d, 0xcd, 0x1d, 0x5d, 0x9d, 0xdd,
47     0x2d, 0x6d, 0xad, 0xed, 0x3d, 0x7d, 0xbd, 0xfd,
48     0x02, 0x42, 0x82, 0xc2, 0x12, 0x52, 0x92, 0xd2,
49     0x22, 0x62, 0xa2, 0xe2, 0x32, 0x72, 0xb2, 0xf2,
50     0x06, 0x46, 0x86, 0xc6, 0x16, 0x56, 0x96, 0xd6,
51     0x26, 0x66, 0xa6, 0xe6, 0x36, 0x76, 0xb6, 0xf6,
52     0x0a, 0x4a, 0x8a, 0xca, 0x1a, 0x5a, 0x9a, 0xda,
53     0x2a, 0x6a, 0xaa, 0xea, 0x3a, 0x7a, 0xba, 0xfa,
54     0x0e, 0x4e, 0x8e, 0xce, 0x1e, 0x5e, 0x9e, 0xde,
55     0x2e, 0x6e, 0xae, 0xee, 0x3e, 0x7e, 0xbe, 0xfe,
56     0x03, 0x43, 0x83, 0xc3, 0x13, 0x53, 0x93, 0xd3,
57     0x23, 0x63, 0xa3, 0xe3, 0x33, 0x73, 0xb3, 0xf3,
58     0x07, 0x47, 0x87, 0xc7, 0x17, 0x57, 0x97, 0xd7,
59     0x27, 0x67, 0xa7, 0xe7, 0x37, 0x77, 0xb7, 0xf7,
60     0x0b, 0x4b, 0x8b, 0xcb, 0x1b, 0x5b, 0x9b, 0xdb,
61     0x2b, 0x6b, 0xab, 0xeb, 0x3b, 0x7b, 0xbb, 0xfb,
62     0x0f, 0x4f, 0x8f, 0xcf, 0x1f, 0x5f, 0x9f, 0xdf,
63     0x2f, 0x6f, 0xaf, 0xef, 0x3f, 0x7f, 0xbf, 0xff
64 };
65 
drawColourPattern(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 screenx,uint16 tile,uint8 tiley,uint16 mirror,uint16 * palette_ptr,uint8 pal,uint8 depth)66 static void drawColourPattern(ngpgfx_t *gfx, uint16_t *cfb_scanline, uint8_t *zbuffer,
67       uint8 screenx, uint16 tile, uint8 tiley, uint16 mirror,
68       uint16* palette_ptr, uint8 pal, uint8 depth)
69 {
70    uint16_t *ptr16;
71 	int index, left, right, highmark, xx;
72 	int x = screenx;
73 	if (x > 0xf8)
74 		x -= 256;
75 	if (x >= SCREEN_WIDTH)
76 		return;
77 
78 	/* Get the data for the "tiley'th" line of "tile". */
79    ptr16 = (uint16_t*)(gfx->CharacterRAM + (tile * 16) + (tiley * 2));
80 #ifdef MSB_FIRST
81 	index = LoadU16_RBO(ptr16);
82 #else
83    index = *ptr16;
84 #endif
85 
86 	/* Horizontal Flip */
87 	if (mirror)
88 		index = mirrored[(index & 0xff00)>>8] | (mirrored[(index & 0xff)] << 8);
89 
90 	palette_ptr += pal << 2;
91 	left = MAX(MAX(x, gfx->winx), 0);
92 	right = x+7;
93 
94 	highmark = MIN(gfx->winw + gfx->winx, SCREEN_WIDTH)-1;
95 
96 	if (right > highmark)
97    {
98       index >>= (right - highmark)*2;
99       right = highmark;
100    }
101 
102 	for (xx = right; xx>=left; --xx,index>>=2)
103    {
104       uint16_t data16;
105       uint16_t *scan = &cfb_scanline[xx];
106       uint8_t *zbuf = &zbuffer[xx];
107 		if (depth <= *zbuf || (index&3)==0)
108 			continue;
109 		*zbuf = depth;
110 
111 		/* Get the colour of the pixel */
112       ptr16 = (uint16_t*)&palette_ptr[index&3];
113 
114 #ifdef MSB_FIRST
115 		data16 = LoadU16_RBO(ptr16);
116 #else
117 		data16 = *ptr16;
118 #endif
119 		if (gfx->negative)
120          data16 = ~data16;
121 
122       *scan = data16;
123 	}
124 }
125 
draw_colour_scroll1(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 depth,int ngpc_scanline)126 static void draw_colour_scroll1(ngpgfx_t *gfx,
127       uint16_t *cfb_scanline,
128       uint8_t *zbuffer, uint8 depth, int ngpc_scanline)
129 {
130 	unsigned i;
131 	uint8_t line = ngpc_scanline + gfx->scroll1y;
132 	uint8_t row = line & 7;	/* Which row? */
133 
134 	/* Draw Foreground scroll plane (Scroll 1) */
135 	for (i = 0; i < 32; i++)
136 	{
137       uint16_t *ptr16 = (uint16_t*)(gfx->ScrollVRAM + ((i + ((line >> 3) << 5)) << 1));
138 #ifdef MSB_FIRST
139 		uint16_t data16 = LoadU16_RBO(ptr16);
140 #else
141       uint16_t data16 = *ptr16;
142 #endif
143 
144 		/* Draw the line of the tile */
145 		drawColourPattern(gfx, cfb_scanline, zbuffer, (i << 3) - gfx->scroll1x, data16 & 0x01FF,
146 			(data16 & 0x4000) ? (7 - row) : row, data16 & 0x8000, (uint16_t*)(gfx->ColorPaletteRAM + 0x0080),
147 			(data16 & 0x1E00) >> 9, depth);
148 	}
149 }
150 
draw_colour_scroll2(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 depth,int ngpc_scanline)151 static void draw_colour_scroll2(ngpgfx_t *gfx, uint16_t *cfb_scanline,
152       uint8_t *zbuffer, uint8 depth, int ngpc_scanline)
153 {
154    unsigned i;
155 	uint8_t line = ngpc_scanline + gfx->scroll2y;
156 	uint8_t row = line & 7;	/* Which row? */
157 
158 	/* Draw Background scroll plane (Scroll 2) */
159 	for (i = 0; i < 32; i++)
160 	{
161       uint16_t *ptr16 = (uint16_t*)(gfx->ScrollVRAM + 0x0800 + ((i + ((line >> 3) << 5)) << 1));
162 #ifdef MSB_FIRST
163 		uint16_t data16 = LoadU16_RBO(ptr16);
164 #else
165 		uint16_t data16 = *ptr16;
166 #endif
167 
168 		/* Draw the line of the tile */
169 		drawColourPattern(gfx, cfb_scanline, zbuffer, (i << 3) - gfx->scroll2x, data16 & 0x01FF,
170 			(data16 & 0x4000) ? (7 - row) : row, data16 & 0x8000, (uint16_t*)(gfx->ColorPaletteRAM + 0x0100),
171 			(data16 & 0x1E00) >> 9, depth);
172 	}
173 }
174 
draw_scanline_colour(ngpgfx_t * gfx,uint16_t * cfb_scanline,int layer_enable,int ngpc_scanline)175 static void draw_scanline_colour(ngpgfx_t *gfx, uint16_t *cfb_scanline,
176       int layer_enable, int ngpc_scanline)
177 {
178 	int16_t lastSpriteX;
179 	int16_t lastSpriteY;
180 	int spr;
181    uint16_t *scan;
182    int x = 0;
183    uint8_t zbuffer[256] = {0};
184 	/* Window colour */
185    uint16_t *ptr16 = (uint16_t*)(gfx->ColorPaletteRAM + 0x01F0 + (gfx->oowc << 1));
186 #ifdef MSB_FIRST
187 	uint16_t data16 = LoadU16_RBO(ptr16);
188 #else
189 	uint16_t data16 = *ptr16;
190 #endif
191 	if (gfx->negative)
192       data16 = ~data16;
193 
194    scan = &cfb_scanline[0];
195 
196    /* Middle */
197    if (!(ngpc_scanline < gfx->winy) && ngpc_scanline < gfx->winy + gfx->winh)
198    {
199       for (x = 0; x < MIN(gfx->winx, SCREEN_WIDTH); x++)
200          *scan++ = data16;
201 
202       x = MIN(gfx->winx + gfx->winw, SCREEN_WIDTH);
203       scan = &cfb_scanline[x];
204    }
205 
206    /*Bottom and Top */
207    for (; x < SCREEN_WIDTH; x++)
208       *scan++ = data16;
209 
210 	/* Ignore above and below the window's top and bottom */
211 
212 	if (ngpc_scanline >= gfx->winy && ngpc_scanline < gfx->winy + gfx->winh)
213 	{
214       int x;
215       uint16_t *scan;
216       uint16_t *ptr16 = (uint16_t*)(uint8*)(gfx->ColorPaletteRAM + 0x01E0 + ((gfx->bgc & 7) << 1));
217 #ifdef MSB_FIRST
218       data16 = LoadU16_RBO(ptr16);
219 #else
220       data16 = *ptr16;
221 #endif
222 
223 		if (gfx->negative)
224          data16 = ~data16;
225 
226       data16 = data16;
227 
228       x    = gfx->winx;
229       scan = &cfb_scanline[x];
230 
231 		/*Draw background */
232 		for (; x < MIN(gfx->winx + gfx->winw, SCREEN_WIDTH); x++)
233 			*scan++ = data16;
234 
235 		/*Swap Front/Back scroll planes? */
236 		if (gfx->planeSwap)
237 		{
238 			if(layer_enable & 1)
239 			 draw_colour_scroll1(gfx, cfb_scanline, zbuffer, ZDEPTH_BACKGROUND_SCROLL, ngpc_scanline);		/* Swap */
240 			if(layer_enable & 2)
241 			 draw_colour_scroll2(gfx, cfb_scanline, zbuffer, ZDEPTH_FOREGROUND_SCROLL, ngpc_scanline);
242 		}
243 		else
244 		{
245 			if(layer_enable & 1)
246 			 draw_colour_scroll2(gfx, cfb_scanline, zbuffer, ZDEPTH_BACKGROUND_SCROLL, ngpc_scanline);		/* Normal */
247 			if(layer_enable & 2)
248 			 draw_colour_scroll1(gfx, cfb_scanline, zbuffer, ZDEPTH_FOREGROUND_SCROLL, ngpc_scanline);
249 		}
250 
251 		/* Draw Sprites */
252 		/*Last sprite position, (defaults to top-left, sure?) */
253 		lastSpriteX = 0;
254 		lastSpriteY = 0;
255 
256 		if(layer_enable & 4)
257 		for (spr = 0; spr < 64; spr++)
258 		{
259 			uint8_t sx       = gfx->SpriteVRAM[(spr * 4) + 2];	/* X position */
260 			uint8_t sy       = gfx->SpriteVRAM[(spr * 4) + 3];	/* Y position */
261 			int16_t x        = sx;
262 			int16_t y        = sy;
263          uint16_t *ptr16  = (uint16_t*)(gfx->SpriteVRAM + (spr * 4));
264 #ifdef MSB_FIRST
265 			uint16_t data16  = LoadU16_RBO(ptr16);
266 #else
267          uint16_t data16  = *ptr16;
268 #endif
269 			uint8_t priority = (data16 & 0x1800) >> 11;
270 
271 			if (data16 & 0x0400)
272             x = lastSpriteX + sx;	/* Horizontal chain? */
273 			if (data16 & 0x0200)
274             y = lastSpriteY + sy;	/* Vertical chain? */
275 
276 			/* Store the position for chaining */
277 			lastSpriteX = x;
278 			lastSpriteY = y;
279 
280 			/* Visible? */
281 			if (priority == 0)
282             continue;
283 
284 			/* Scroll the sprite */
285 			x += gfx->scrollsprx;
286 			y += gfx->scrollspry;
287 
288 			/* Off-screen? */
289 			if (x > 248 && x < 256)
290             x = x - 256;
291          else
292             x &= 0xFF;
293 			if (y > 248 && y < 256)
294             y = y - 256;
295          else
296             y &= 0xFF;
297 
298 			/* In range? */
299 			if (ngpc_scanline >= y && ngpc_scanline <= y + 7)
300 			{
301 				uint8_t row = (ngpc_scanline - y) & 7;	/* Which row? */
302 				drawColourPattern(gfx, cfb_scanline, zbuffer, (uint8)x, data16 & 0x01FF,
303 					(data16 & 0x4000) ? 7 - row : row, data16 & 0x8000,
304 					(uint16_t*)gfx->ColorPaletteRAM, gfx->SpriteVRAMColor[spr] & 0xF, priority << 1);
305 			}
306 		}
307 	}
308 }
309 
MonoPlot(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 x,uint8 * palette_ptr,uint16 pal_hi,uint8 index,uint8 depth)310 static void MonoPlot(ngpgfx_t *gfx, uint16_t *cfb_scanline, uint8_t *zbuffer,
311       uint8 x, uint8* palette_ptr, uint16 pal_hi, uint8 index, uint8 depth)
312 {
313    uint16 r, g, b;
314 	uint8_t data8, *zbuf;
315    uint16_t *scan, data16;
316 
317 	/* Clip */
318 	if (index == 0 || x < gfx->winx || x >= (gfx->winw + gfx->winx) || x >= SCREEN_WIDTH)
319 		return;
320 
321    zbuf = &zbuffer[x];
322 
323 	/*Depth check, <= to stop later sprites overwriting pixels! */
324 	if (depth <= *zbuf)
325       return;
326 	*zbuf = depth;
327 
328 	/*Get the colour of the pixel */
329    data8 = palette_ptr[(pal_hi ? 3 : 0) + index - 1];
330 
331 	r     = (data8 & 7) << 1;
332 	g     = (data8 & 7) << 5;
333 	b     = (data8 & 7) << 9;
334 
335    scan = &cfb_scanline[x];
336    data16 = ~(r | g | b);
337 	if (gfx->negative)
338       data16 = ~(data16);
339 
340    *scan++ = data16;
341 }
342 
drawMonoPattern(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 screenx,uint16 tile,uint8 tiley,uint16 mirror,uint8 * palette_ptr,uint16 pal,uint8 depth)343 static void drawMonoPattern(ngpgfx_t *gfx,
344       uint16_t *cfb_scanline, uint8_t *zbuffer, uint8 screenx, uint16 tile, uint8 tiley, uint16 mirror,
345       uint8* palette_ptr, uint16 pal, uint8 depth)
346 {
347 	/* Get the data for th e "tiley'th" line of "tile". */
348    uint16_t *ptr16 = (uint16_t*)(gfx->CharacterRAM + (tile * 16) + (tiley * 2));
349 #ifdef MSB_FIRST
350 	uint16_t   data = LoadU16_RBO(ptr16);
351 #else
352    uint16_t   data = *ptr16;
353 #endif
354 
355 	if (mirror)
356 	{
357       /* Horizontal Flip */
358 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 7, palette_ptr, pal, (data & 0xC000) >> 0xE, depth);
359 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 6, palette_ptr, pal, (data & 0x3000) >> 0xC, depth);
360 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 5, palette_ptr, pal, (data & 0x0C00) >> 0xA, depth);
361 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 4, palette_ptr, pal, (data & 0x0300) >> 0x8, depth);
362 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 3, palette_ptr, pal, (data & 0x00C0) >> 0x6, depth);
363 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 2, palette_ptr, pal, (data & 0x0030) >> 0x4, depth);
364 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 1, palette_ptr, pal, (data & 0x000C) >> 0x2, depth);
365 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 0, palette_ptr, pal, (data & 0x0003) >> 0x0, depth);
366 	}
367 	else
368 	{
369       /*Normal */
370 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 0, palette_ptr, pal, (data & 0xC000) >> 0xE, depth);
371 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 1, palette_ptr, pal, (data & 0x3000) >> 0xC, depth);
372 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 2, palette_ptr, pal, (data & 0x0C00) >> 0xA, depth);
373 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 3, palette_ptr, pal, (data & 0x0300) >> 0x8, depth);
374 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 4, palette_ptr, pal, (data & 0x00C0) >> 0x6, depth);
375 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 5, palette_ptr, pal, (data & 0x0030) >> 0x4, depth);
376 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 6, palette_ptr, pal, (data & 0x000C) >> 0x2, depth);
377 		MonoPlot(gfx, cfb_scanline, zbuffer, screenx + 7, palette_ptr, pal, (data & 0x0003) >> 0x0, depth);
378 	}
379 }
380 
draw_mono_scroll1(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 depth,int ngpc_scanline)381 static void draw_mono_scroll1(ngpgfx_t *gfx, uint16_t *cfb_scanline,
382       uint8_t *zbuffer, uint8 depth, int ngpc_scanline)
383 {
384 	unsigned i;
385 	uint8_t line = ngpc_scanline + gfx->scroll1y;
386 	uint8_t row = line & 7;	/* Which row? */
387 
388 	/* Draw Foreground scroll plane (Scroll 1) */
389 	for (i = 0; i < 32; i++)
390 	{
391       uint16_t *ptr16 = (uint16_t*)(gfx->ScrollVRAM + ((i + ((line >> 3) << 5)) << 1));
392 #ifdef MSB_FIRST
393 		uint16_t data16 = LoadU16_RBO(ptr16);
394 #else
395       uint16_t data16 = *ptr16;
396 #endif
397 
398 		//Draw the line of the tile
399 		drawMonoPattern(gfx, cfb_scanline, zbuffer, (i << 3) - gfx->scroll1x, data16 & 0x01FF,
400 			(data16 & 0x4000) ? 7 - row : row, data16 & 0x8000, gfx->SCRP1PLT,
401 			data16 & 0x2000, depth);
402 	}
403 }
404 
draw_mono_scroll2(ngpgfx_t * gfx,uint16_t * cfb_scanline,uint8_t * zbuffer,uint8 depth,int ngpc_scanline)405 static void draw_mono_scroll2(ngpgfx_t *gfx, uint16_t *cfb_scanline, uint8_t *zbuffer, uint8 depth, int ngpc_scanline)
406 {
407    unsigned i;
408 	uint8_t line = ngpc_scanline + gfx->scroll2y;
409 	uint8_t row = line & 7;	//Which row?
410 
411 	/* Draw Background scroll plane (Scroll 2) */
412 	for (i = 0; i < 32; i++)
413 	{
414       uint16_t *ptr16 = (uint16_t*)(gfx->ScrollVRAM + 0x0800 + ((i + ((line >> 3) << 5)) << 1));
415 #ifdef MSB_FIRST
416 		uint16_t data16 = LoadU16_RBO(ptr16);
417 #else
418       uint16_t data16 = *ptr16;
419 #endif
420 
421 		/* Draw the line of the tile */
422 		drawMonoPattern(gfx, cfb_scanline, zbuffer, (i << 3) - gfx->scroll2x, data16 & 0x01FF,
423 			(data16 & 0x4000) ? 7 - row : row, data16 & 0x8000, gfx->SCRP2PLT,
424 			data16 & 0x2000, depth);
425 	}
426 }
427 
draw_scanline_mono(ngpgfx_t * gfx,uint16_t * cfb_scanline,int layer_enable,int ngpc_scanline)428 static void draw_scanline_mono(ngpgfx_t *gfx,
429       uint16_t *cfb_scanline, int layer_enable, int ngpc_scanline)
430 {
431    int x;
432    uint16_t *scan;
433 	int16_t lastSpriteX;
434 	int16_t lastSpriteY;
435 	int spr;
436    uint8_t zbuffer[256] = {0};
437 
438 	/* Window colour */
439 	uint16_t r = (uint16)gfx->oowc << 1;
440 	uint16_t g = (uint16)gfx->oowc << 5;
441 	uint16_t b = (uint16)gfx->oowc << 9;
442    uint16_t data16 = ~(r | g | b);
443 
444 	if (gfx->negative)
445 		data16 = ~data16;
446 
447    x = 0;
448    scan = &cfb_scanline[x];
449 
450    /* Middle */
451    if (!(ngpc_scanline < gfx->winy) && ngpc_scanline < gfx->winy + gfx->winh)
452    {
453       for (; x < MIN(gfx->winx, SCREEN_WIDTH); x++)
454          *scan++ = data16;
455 
456       x = MIN(gfx->winx + gfx->winw, SCREEN_WIDTH);
457    }
458 
459    /* Bottom and Top */
460    for (; x < SCREEN_WIDTH; x++)
461       *scan++ = data16;
462 
463 	/* Ignore above and below the window's top and bottom */
464 	if (ngpc_scanline >= gfx->winy && ngpc_scanline < gfx->winy + gfx->winh)
465 	{
466       int x;
467       uint16_t *scan;
468 
469       data16 = 0x0FFF;
470 
471 		/* Background colour Enabled? */
472 		if ((gfx->bgc & 0xC0) == 0x80)
473 		{
474 			r = (uint16)(gfx->bgc & 7) << 1;
475 			g = (uint16)(gfx->bgc & 7) << 5;
476 			b = (uint16)(gfx->bgc & 7) << 9;
477 			data16 = ~(r | g | b);
478 		}
479 
480 		if (gfx->negative)
481          data16 = ~data16;
482 
483       x    = gfx->winx;
484       scan = &cfb_scanline[x];
485 
486 		/* Draw background */
487 		for (; x < MIN(gfx->winx + gfx->winw, SCREEN_WIDTH); x++)
488 			*scan++ = data16;
489 
490 		/* Swap Front/Back scroll planes? */
491 		if (gfx->planeSwap)
492 		{
493 			if(layer_enable & 1)
494 			 draw_mono_scroll1(gfx, cfb_scanline, zbuffer, ZDEPTH_BACKGROUND_SCROLL, ngpc_scanline);		//Swap
495 			if(layer_enable & 2)
496 			 draw_mono_scroll2(gfx, cfb_scanline, zbuffer, ZDEPTH_FOREGROUND_SCROLL, ngpc_scanline);
497 		}
498 		else
499 		{
500 			if(layer_enable & 1)
501 			 draw_mono_scroll2(gfx, cfb_scanline, zbuffer, ZDEPTH_BACKGROUND_SCROLL, ngpc_scanline);		//Normal
502 			if(layer_enable & 2)
503 			 draw_mono_scroll1(gfx, cfb_scanline, zbuffer, ZDEPTH_FOREGROUND_SCROLL, ngpc_scanline);
504 		}
505 
506 		/* Draw Sprites */
507 		/* Last sprite position, (defaults to top-left, sure?) */
508 		lastSpriteX = 0;
509 		lastSpriteY = 0;
510 		if(layer_enable & 4)
511 		for (spr = 0; spr < 64; spr++)
512 		{
513 			uint8_t priority, row;
514 			uint8_t sx = gfx->SpriteVRAM[(spr * 4) + 2];	//X position
515 			uint8_t sy = gfx->SpriteVRAM[(spr * 4) + 3];	//Y position
516 			int16_t x = sx;
517 			int16_t y = sy;
518          uint16_t *ptr16 = (uint16_t*)(gfx->SpriteVRAM + (spr * 4));
519 
520 #ifdef MSB_FIRST
521 			data16 = LoadU16_RBO(ptr16);
522 #else
523          data16 = *ptr16;
524 #endif
525 			priority = (data16 & 0x1800) >> 11;
526 
527 			if (data16 & 0x0400)
528             x = lastSpriteX + sx;	//Horizontal chain?
529 			if (data16 & 0x0200)
530             y = lastSpriteY + sy;	//Vertical chain?
531 
532 			//Store the position for chaining
533 			lastSpriteX = x;
534 			lastSpriteY = y;
535 
536 			//Visible?
537 			if (priority == 0)
538             continue;
539 
540 			//Scroll the sprite
541 			x += gfx->scrollsprx;
542 			y += gfx->scrollspry;
543 
544 			//Off-screen?
545 			if (x > 248 && x < 256)	x = x - 256; else x &= 0xFF;
546 			if (y > 248 && y < 256)	y = y - 256; else y &= 0xFF;
547 
548 			//In range?
549 			if (ngpc_scanline >= y && ngpc_scanline <= y + 7)
550 			{
551 				row = (ngpc_scanline - y) & 7;	//Which row?
552 				drawMonoPattern(gfx, cfb_scanline, zbuffer, (uint8)x, data16 & 0x01FF,
553 					(data16 & 0x4000) ? 7 - row : row, data16 & 0x8000,
554 					gfx->SPPLT, data16 & 0x2000, priority << 1);
555 			}
556 		}
557 
558 	}
559 }
560 
ngpgfx_delayed_settings(ngpgfx_t * gfx)561 static void ngpgfx_delayed_settings(ngpgfx_t *gfx)
562 {
563 	//Window dimensions
564 	gfx->winx = gfx->WBA_H;
565 	gfx->winy = gfx->WBA_V;
566 	gfx->winw = gfx->WSI_H;
567 	gfx->winh = gfx->WSI_V;
568 
569 	/* Scroll Planes (Confirmed delayed) */
570 	gfx->scroll1x = gfx->S1SO_H;
571 	gfx->scroll1y = gfx->S1SO_V;
572 
573 	gfx->scroll2x = gfx->S2SO_H;
574 	gfx->scroll2y = gfx->S2SO_V;
575 
576 	/* Sprite offset (Confirmed delayed) */
577 	gfx->scrollsprx = gfx->PO_H;
578 	gfx->scrollspry = gfx->PO_V;
579 
580 	/* Plane Priority (Confirmed delayed) */
581 	gfx->planeSwap = gfx->P_F & 0x80;
582 
583 	/* Background colour register (Confirmed delayed) */
584 	gfx->bgc = gfx->BG_COL;
585 
586 	/* 2D Control register (Confirmed delayed) */
587 	gfx->oowc     = gfx->CONTROL_2D & 7;
588 	gfx->negative = gfx->CONTROL_2D & 0x80;
589 }
590 
ngpgfx_reset(ngpgfx_t * gfx)591 void ngpgfx_reset(ngpgfx_t *gfx)
592 {
593    memset(gfx->SPPLT,    0x7, sizeof(gfx->SPPLT));
594    memset(gfx->SCRP1PLT, 0x7, sizeof(gfx->SCRP1PLT));
595    memset(gfx->SCRP2PLT, 0x7, sizeof(gfx->SCRP2PLT));
596 
597    gfx->raster_line    = 0;
598    gfx->S1SO_H         = 0;
599    gfx->S1SO_V         = 0;
600    gfx->S2SO_H         = 0;
601    gfx->S2SO_V         = 0;
602    gfx->WBA_H          = 0;
603    gfx->WBA_V          = 0;
604 
605    gfx->WSI_H          = 0xFF;
606    gfx->WSI_V          = 0xFF;
607 
608    gfx->C_OVR          = 0;
609    gfx->BLNK           = 0;
610 
611    gfx->PO_H           = 0;
612    gfx->PO_V           = 0;
613    gfx->P_F            = 0;
614 
615    gfx->BG_COL         = 0x7;
616    gfx->CONTROL_2D     = 0;
617    gfx->CONTROL_INT    = 0;
618    gfx->SCREEN_PERIOD  = 0xC6;
619    gfx->K2GE_MODE      = 0;
620 
621    ngpgfx_delayed_settings(gfx);
622 }
623 
ngpgfx_power(ngpgfx_t * gfx)624 void ngpgfx_power(ngpgfx_t *gfx)
625 {
626    ngpgfx_reset(gfx);
627 
628    memset(gfx->ScrollVRAM, 0, sizeof(gfx->ScrollVRAM));
629    memset(gfx->CharacterRAM, 0, sizeof(gfx->CharacterRAM));
630    memset(gfx->SpriteVRAM, 0, sizeof(gfx->SpriteVRAM));
631    memset(gfx->SpriteVRAMColor, 0, sizeof(gfx->SpriteVRAMColor));
632    memset(gfx->ColorPaletteRAM, 0, sizeof(gfx->ColorPaletteRAM));
633 }
634 
635 
ngpgfx_hint(ngpgfx_t * gfx)636 bool ngpgfx_hint(ngpgfx_t *gfx)
637 {
638    /* H_Int / Delayed settings */
639    if ((gfx->raster_line < SCREEN_HEIGHT-1 || gfx->raster_line == gfx->SCREEN_PERIOD))
640    {
641       /* Get delayed settings */
642       ngpgfx_delayed_settings(gfx);
643 
644       /* Allowed? */
645       if (gfx->CONTROL_INT & 0x40)
646          return 1;
647    }
648 
649    return 0;
650 }
651 
ngpgfx_set_pixel_format(ngpgfx_t * gfx,int depth)652 void ngpgfx_set_pixel_format(ngpgfx_t *gfx, int depth)
653 {
654    unsigned i;
655 
656    for(i = 0; i < 4096; i++)
657    {
658       int r = (i & 0xF) * 17;
659       int g = ((i >> 4) & 0xF) * 17;
660       int b = ((i >> 8) & 0xF) * 17;
661 
662       switch(depth)
663       {
664          case 15: gfx->ColorMap[i] = MAKECOLOR_15(r, g, b, 0); break;
665          case 16: gfx->ColorMap[i] = MAKECOLOR_16(r, g, b, 0); break;
666          default: gfx->ColorMap[i] = MAKECOLOR_24(r, g, b, 0); break;
667       }
668    }
669 }
670 
ngpgfx_draw(ngpgfx_t * gfx,void * data,bool skip)671 bool ngpgfx_draw(ngpgfx_t *gfx, void *data, bool skip)
672 {
673    unsigned x;
674    bool ret = 0;
675    MDFN_Surface *surface = (MDFN_Surface*)data;
676 
677    /* Draw the scanline */
678    if (gfx->raster_line < SCREEN_HEIGHT && !skip)
679    {
680       uint16_t cfb_scanline[256];
681 
682       if (!gfx->K2GE_MODE)
683          draw_scanline_colour(gfx, cfb_scanline, gfx->layer_enable, gfx->raster_line);
684       else
685          draw_scanline_mono(gfx, cfb_scanline, gfx->layer_enable, gfx->raster_line);
686 
687       switch (surface->depth)
688       {
689          case 15:
690          case 16: {
691             uint16_t *dest = surface->pixels + surface->pitch * gfx->raster_line;
692 
693             for (x = 0; x < SCREEN_WIDTH; x++)
694                dest[x] = gfx->ColorMap[cfb_scanline[x] & 4095];
695          } break;
696 
697          case 24: {
698             uint32_t *dest = ((uint32_t *) surface->pixels) + surface->pitch * gfx->raster_line;
699 
700             for (x = 0; x < SCREEN_WIDTH; x++)
701                dest[x] = gfx->ColorMap[cfb_scanline[x] & 4095];
702          } break;
703       }
704    }
705    gfx->raster_line++;
706 
707    /* V_Int? */
708    if (gfx->raster_line == SCREEN_HEIGHT)
709    {
710       gfx->BLNK = 1;
711       ret = 1;
712 
713       if(gfx->CONTROL_INT & 0x80) /* (statusIFF() <= 4 */
714          TestIntHDMA(5, 0x0B);
715    }
716 
717    /* End of V_Int */
718    if(gfx->raster_line == gfx->SCREEN_PERIOD + 1)
719    {
720       /* Last scanline + 1 */
721       gfx->raster_line = 0;
722       gfx->C_OVR = 0;
723       gfx->BLNK = 0;
724    }
725 
726    return ret;
727 }
728 
ngpgfx_StateAction(ngpgfx_t * gfx,void * data,int load,int data_only)729 int ngpgfx_StateAction(ngpgfx_t *gfx, void *data, int load, int data_only)
730 {
731    StateMem *sm = (StateMem*)data;
732    SFORMAT StateRegs[] =
733    {
734       { &(gfx->raster_line), sizeof(gfx->raster_line), 0x80000000, "raster_line" },
735       { &(gfx->S1SO_H), (uint32_t)(sizeof(gfx->S1SO_H)), 0x80000000, "S1SO_H" },
736       { &(gfx->S1SO_V), (uint32_t)(sizeof(gfx->S1SO_V)), 0x80000000, "S1SO_V" },
737       { &(gfx->S2SO_H), (uint32_t)(sizeof(gfx->S2SO_H)), 0x80000000, "S2SO_H" },
738       { &(gfx->S2SO_V), (uint32_t)(sizeof(gfx->S2SO_V)), 0x80000000, "S2SO_V" },
739       { &(gfx->WBA_H), (uint32_t)(sizeof(gfx->WBA_H)),   0x80000000, "WBA_H" },
740       { &(gfx->WBA_V), (uint32_t)(sizeof(gfx->WBA_V)),   0x80000000, "WBA_V" },
741       { &(gfx->WSI_H), (uint32_t)(sizeof(gfx->WSI_H)),   0x80000000, "WSI_H" },
742       { &(gfx->WSI_V), (uint32_t)(sizeof(gfx->WSI_V)),   0x80000000, "WSI_V" },
743       { &(gfx->C_OVR), (uint32_t)1, 0x80000000 | 0x08000000, "C_OVR" },
744       { &(gfx->BLNK), (uint32_t)1,  0x80000000 | 0x08000000, "BLNK" },
745       { &(gfx->PO_H), (uint32_t)(sizeof(gfx->PO_H)), 0x80000000, "PO_H" },
746       { &(gfx->PO_V), (uint32_t)(sizeof(gfx->PO_V)), 0x80000000, "PO_V" },
747       { &(gfx->P_F), (uint32_t)(sizeof(gfx->P_F)),   0x80000000, "P_F" },
748       { &(gfx->BG_COL), (uint32_t)(sizeof(gfx->BG_COL)), 0x80000000, "BG_COL" },
749       { &(gfx->CONTROL_2D), (uint32_t)(sizeof(gfx->CONTROL_2D)), 0x80000000, "CONTROL_2D" },
750       { &(gfx->CONTROL_INT), (uint32_t)(sizeof(gfx->CONTROL_INT)), 0x80000000, "CONTROL_INT" },
751       { &(gfx->SCREEN_PERIOD), (uint32_t)(sizeof(gfx->SCREEN_PERIOD)), 0x80000000, "SCREEN_PERIOD" },
752       { &(gfx->K2GE_MODE), (uint32_t)(sizeof(gfx->K2GE_MODE)), 0x80000000, "K2GE_MODE" },
753 
754       { (gfx->SPPLT), (uint32_t)(6), 0, "SPPLT" },
755       { (gfx->SCRP1PLT), (uint32_t)(6), 0, "SCRP1PLT" },
756       { (gfx->SCRP2PLT), (uint32_t)(6), 0, "SCRP2PLT" },
757 
758       { &(gfx->winx), (uint32_t)(sizeof(gfx->winx)), 0x80000000, "winx" },
759       { &(gfx->winw), (uint32_t)(sizeof(gfx->winw)), 0x80000000, "winw" },
760       { &(gfx->winy), (uint32_t)(sizeof(gfx->winy)), 0x80000000, "winy" },
761       { &(gfx->winh), (uint32_t)(sizeof(gfx->winh)), 0x80000000, "winh" },
762       { &(gfx->scroll1x), (uint32_t)(sizeof(gfx->scroll1x)), 0x80000000, "scroll1x" },
763       { &(gfx->scroll1y), (uint32_t)(sizeof(gfx->scroll1y)), 0x80000000, "scroll1y" },
764       { &(gfx->scroll2x), (uint32_t)(sizeof(gfx->scroll2x)), 0x80000000, "scroll2x" },
765       { &(gfx->scroll2y), (uint32_t)(sizeof(gfx->scroll2y)), 0x80000000, "scroll2y" },
766       { &(gfx->scrollsprx), (uint32_t)(sizeof(gfx->scrollsprx)), 0x80000000, "scrollsprx" },
767       { &(gfx->scrollspry), (uint32_t)(sizeof(gfx->scrollspry)), 0x80000000, "scrollspry" },
768       { &(gfx->planeSwap), (uint32_t)(sizeof(gfx->planeSwap)), 0x80000000, "planeSwap" },
769       { &(gfx->bgc), (uint32_t)(sizeof(gfx->bgc)), 0x80000000, "bgc" },
770       { &(gfx->oowc), (uint32_t)(sizeof(gfx->oowc)), 0x80000000, "oowc" },
771 
772       { &(gfx->negative), (uint32_t)(sizeof(gfx->negative)), 0x80000000, "negative" },
773 
774       { (gfx->ScrollVRAM), (uint32_t)(4096), 0, "ScrollVRAM" },
775       { (gfx->CharacterRAM), (uint32_t)(8192), 0, "CharacterRAM" },
776       { (gfx->SpriteVRAM), (uint32_t)(256), 0, "SpriteVRAM" },
777       { (gfx->SpriteVRAMColor), (uint32_t)(0x40), 0, "SpriteVRAMColor" },
778       { (gfx->ColorPaletteRAM), (uint32_t)(0x200), 0, "ColorPaletteRAM" },
779 
780       { 0, 0, 0, 0 }
781    };
782 
783    if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, "GFX", false))
784       return(0);
785 
786    return(1);
787 }
788 
ngpgfx_SetLayerEnableMask(ngpgfx_t * gfx,uint64_t mask)789 void ngpgfx_SetLayerEnableMask(ngpgfx_t *gfx, uint64_t mask)
790 {
791    gfx->layer_enable = mask;
792 }
793 
ngpgfx_write8(ngpgfx_t * gfx,uint32 address,uint8 data)794 void ngpgfx_write8(ngpgfx_t *gfx, uint32 address, uint8 data)
795 {
796    if(address >= 0x9000 && address <= 0x9fff)
797       gfx->ScrollVRAM[address - 0x9000] = data;
798    else if(address >= 0xa000 && address <= 0xbfff)
799       gfx->CharacterRAM[address - 0xa000] = data;
800    else if(address >= 0x8800 && address <= 0x88ff)
801       gfx->SpriteVRAM[address - 0x8800] = data;
802    else if(address >= 0x8c00 && address <= 0x8c3f)
803       gfx->SpriteVRAMColor[address - 0x8c00] = data & 0x0f;
804    else if(address >= 0x8200 && address <= 0x83ff)
805       gfx->ColorPaletteRAM[address - 0x8200] = data;
806    else switch(address)
807    {
808       case 0x8000:
809          gfx->CONTROL_INT = data & 0xC0;
810          break;
811       case 0x8002:
812          gfx->WBA_H = data;
813          break;
814       case 0x8003:
815          gfx->WBA_V = data;
816          break;
817       case 0x8004:
818          gfx->WSI_H = data;
819          break;
820       case 0x8005:
821          gfx->WSI_V = data;
822          break;
823       case 0x8006:
824          gfx->SCREEN_PERIOD = data;
825          break;
826       case 0x8012:
827          gfx->CONTROL_2D = data & 0x87;
828          break;
829       case 0x8020:
830          gfx->PO_H = data;
831          break;
832       case 0x8021:
833          gfx->PO_V = data;
834          break;
835       case 0x8030:
836          gfx->P_F = data & 0x80;
837          break;
838       case 0x8032:
839          gfx->S1SO_H = data;
840          break;
841       case 0x8033:
842          gfx->S1SO_V = data;
843          break;
844       case 0x8034:
845          gfx->S2SO_H = data;
846          break;
847       case 0x8035:
848          gfx->S2SO_V = data;
849          break;
850       case 0x8101:
851          gfx->SPPLT[0] = data & 0x7;
852          break;
853       case 0x8102:
854          gfx->SPPLT[1] = data & 0x7;
855          break;
856       case 0x8103:
857          gfx->SPPLT[2] = data & 0x7;
858          break;
859       case 0x8105:
860          gfx->SPPLT[3] = data & 0x7;
861          break;
862       case 0x8106:
863          gfx->SPPLT[4] = data & 0x7;
864          break;
865       case 0x8107:
866          gfx->SPPLT[5] = data & 0x7;
867          break;
868       case 0x8109:
869          gfx->SCRP1PLT[0] = data & 0x7;
870          break;
871       case 0x810a:
872          gfx->SCRP1PLT[1] = data & 0x7;
873          break;
874       case 0x810b:
875          gfx->SCRP1PLT[2] = data & 0x7;
876          break;
877       case 0x810d:
878          gfx->SCRP1PLT[3] = data & 0x7;
879          break;
880       case 0x810e:
881          gfx->SCRP1PLT[4] = data & 0x7;
882          break;
883       case 0x810f:
884          gfx->SCRP1PLT[5] = data & 0x7;
885          break;
886       case 0x8111:
887          gfx->SCRP2PLT[0] = data & 0x7;
888          break;
889       case 0x8112:
890          gfx->SCRP2PLT[1] = data & 0x7;
891          break;
892       case 0x8113:
893          gfx->SCRP2PLT[2] = data & 0x7;
894          break;
895 
896       case 0x8115:
897          gfx->SCRP2PLT[3] = data & 0x7;
898          break;
899       case 0x8116:
900          gfx->SCRP2PLT[4] = data & 0x7;
901          break;
902       case 0x8117:
903          gfx->SCRP2PLT[5] = data & 0x7;
904          break;
905 
906       case 0x8118:
907          gfx->BG_COL = data & 0xC7;
908          break;
909 
910       case 0x87e0:
911          if(data == 0x52)
912          {
913             /* GEreset */
914             neopop_reset();
915          }
916          break;
917       case 0x87e2:
918          gfx->K2GE_MODE = data & 0x80;
919          break;
920    }
921 }
922 
ngpgfx_write16(ngpgfx_t * gfx,uint32 address,uint16 data)923 void ngpgfx_write16(ngpgfx_t *gfx, uint32 address, uint16 data)
924 {
925    ngpgfx_write8(gfx, address, data & 0xFF);
926    ngpgfx_write8(gfx, address + 1, data >> 8);
927 }
928 
ngpgfx_read8(ngpgfx_t * gfx,uint32_t address)929 uint8_t ngpgfx_read8(ngpgfx_t *gfx, uint32_t address)
930 {
931    if(address >= 0x9000 && address <= 0x9fff)
932       return(gfx->ScrollVRAM[address - 0x9000]);
933    else if(address >= 0xa000 && address <= 0xbfff)
934       return(gfx->CharacterRAM[address - 0xa000]);
935    else if(address >= 0x8800 && address <= 0x88ff)
936       return(gfx->SpriteVRAM[address - 0x8800]);
937    else if(address >= 0x8c00 && address <= 0x8c3f)
938       return(gfx->SpriteVRAMColor[address - 0x8c00]);
939    else if(address >= 0x8200 && address <= 0x83ff)
940       return(gfx->ColorPaletteRAM[address - 0x8200]);
941    else switch(address)
942    {
943       case 0x8000:
944          return gfx->CONTROL_INT;
945       case 0x8002:
946          return gfx->WBA_H;
947       case 0x8003:
948          return gfx->WBA_V;
949       case 0x8004:
950          return gfx->WSI_H;
951       case 0x8005:
952          return gfx->WSI_V;
953       case 0x8006:
954          return gfx->SCREEN_PERIOD;
955       case 0x8008:
956          return( (uint8)((abs(TIMER_HINT_RATE - (int)timer_hint)) >> 2) ); //RAS.H read (Simulated horizontal raster position)
957       case 0x8009:
958          return gfx->raster_line;
959       case 0x8010:
960          return((gfx->C_OVR ? 0x80 : 0x00) | (gfx->BLNK ? 0x40 : 0x00));
961       case 0x8012:
962          return gfx->CONTROL_2D;
963       case 0x8020:
964          return gfx->PO_H;
965       case 0x8021:
966          return gfx->PO_V;
967       case 0x8030:
968          return gfx->P_F;
969       case 0x8032:
970          return gfx->S1SO_H;
971       case 0x8033:
972          return gfx->S1SO_V;
973       case 0x8034:
974          return gfx->S2SO_H;
975       case 0x8035:
976          return gfx->S2SO_V;
977       case 0x8101:
978          return gfx->SPPLT[0];
979       case 0x8102:
980          return gfx->SPPLT[1];
981       case 0x8103:
982          return gfx->SPPLT[2];
983       case 0x8105:
984          return gfx->SPPLT[3];
985       case 0x8106:
986          return gfx->SPPLT[4];
987       case 0x8107:
988          return gfx->SPPLT[5];
989       case 0x8108:
990          return gfx->SCRP1PLT[0];
991       case 0x8109:
992          return gfx->SCRP1PLT[1];
993       case 0x810a:
994          return gfx->SCRP1PLT[2];
995       case 0x810d:
996          return gfx->SCRP1PLT[3];
997       case 0x810e:
998          return gfx->SCRP1PLT[4];
999       case 0x810f:
1000          return gfx->SCRP1PLT[5];
1001       case 0x8111:
1002          return gfx->SCRP2PLT[0];
1003       case 0x8112:
1004          return gfx->SCRP2PLT[1];
1005       case 0x8113:
1006          return gfx->SCRP2PLT[2];
1007       case 0x8115:
1008          return gfx->SCRP2PLT[3];
1009       case 0x8116:
1010          return gfx->SCRP2PLT[4];
1011       case 0x8117:
1012          return gfx->SCRP2PLT[5];
1013       case 0x8118:
1014          return(gfx->BG_COL);
1015       case 0x87e2:
1016          return(gfx->K2GE_MODE);
1017    }
1018 
1019    return 0;
1020 }
1021 
ngpgfx_read16(ngpgfx_t * gfx,uint32_t address)1022 uint16_t ngpgfx_read16(ngpgfx_t *gfx, uint32_t address)
1023 {
1024    uint16_t ret;
1025 
1026    ret = ngpgfx_read8(gfx, address);
1027    ret |= ngpgfx_read8(gfx, address + 1) << 8;
1028 
1029    return(ret);
1030 }
1031 
1032