Add core option to force the Region FPS (NTSC, PAL)
[picodrive.git] / pico / cd / gfx.c
CommitLineData
e53f0499 1/***************************************************************************************
2 * Genesis Plus
3 * CD graphics processor
4 *
5 * Copyright (C) 2012 Eke-Eke (Genesis Plus GX)
6 *
7 * Redistribution and use of this code or any derivative works are permitted
8 * provided that the following conditions are met:
9 *
10 * - Redistributions may not be sold, nor may they be used in a commercial
11 * product or activity.
12 *
13 * - Redistributions that are modified from the original source must include the
14 * complete source code, including the source code for all components used by a
15 * binary built from the modified sources. However, as a special exception, the
16 * source code distributed need not include anything that is normally distributed
17 * (in either source or binary form) with the major components (compiler, kernel,
18 * and so on) of the operating system on which the executable runs, unless that
19 * component itself accompanies the executable.
20 *
21 * - Redistributions must reproduce the above copyright notice, this list of
22 * conditions and the following disclaimer in the documentation and/or other
23 * materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 ****************************************************************************************/
a93a80de 38#include "../pico_int.h"
39#include "genplus_macros.h"
e53f0499 40
41typedef struct
42{
a93a80de 43 //uint32 cycles; /* current cycles count for graphics operation */
44 //uint32 cyclesPerLine; /* current graphics operation timings */
e53f0499 45 uint32 dotMask; /* stamp map size mask */
46 uint16 *tracePtr; /* trace vector pointer */
47 uint16 *mapPtr; /* stamp map table base address */
48 uint8 stampShift; /* stamp pixel shift value (related to stamp size) */
49 uint8 mapShift; /* stamp map table shift value (related to stamp map size) */
50 uint16 bufferOffset; /* image buffer column offset */
51 uint32 bufferStart; /* image buffer start index */
a93a80de 52 uint32 y_step; /* pico: render line step */
f3cb39f2 53 uint8 lut_prio[4][0x10][0x10]; /* WORD-RAM data writes priority lookup table */
e53f0499 54 uint8 lut_pixel[0x200]; /* Graphics operation dot offset lookup table */
55 uint8 lut_cell[0x100]; /* Graphics operation stamp offset lookup table */
56} gfx_t;
57
58static gfx_t gfx;
59
a93a80de 60static void gfx_schedule(void);
61
e53f0499 62/***************************************************************/
63/* Rotation / Scaling operation (2M Mode) */
64/***************************************************************/
65
66void gfx_init(void)
67{
68 int i, j;
e53f0499 69 uint8 mask, row, col, temp;
70
71 memset(&gfx, 0, sizeof(gfx));
72
73 /* Initialize priority modes lookup table */
f3cb39f2 74 for (i = 0; i < 0x10; i++)
e53f0499 75 {
f3cb39f2 76 for (j = 0; j < 0x10; j++)
e53f0499 77 {
78 /* normal */
79 gfx.lut_prio[0][i][j] = j;
80 /* underwrite */
f3cb39f2 81 gfx.lut_prio[1][i][j] = i ? i : j;
e53f0499 82 /* overwrite */
f3cb39f2 83 gfx.lut_prio[2][i][j] = j ? j : i;
e53f0499 84 /* invalid */
85 gfx.lut_prio[3][i][j] = i;
86 }
87 }
88
89 /* Initialize cell lookup table */
90 /* table entry = yyxxshrr (8 bits) */
91 /* with: yy = cell row (0-3) */
92 /* xx = cell column (0-3) */
93 /* s = stamp size (0=16x16, 1=32x32) */
94 /* hrr = HFLIP & ROTATION bits */
95 for (i=0; i<0x100; i++)
96 {
97 /* one stamp = 2x2 cells (16x16) or 4x4 cells (32x32) */
98 mask = (i & 8) ? 3 : 1;
99 row = (i >> 6) & mask;
100 col = (i >> 4) & mask;
101
102 if (i & 4) { col = col ^ mask; } /* HFLIP (always first) */
103 if (i & 2) { col = col ^ mask; row = row ^ mask; } /* ROLL1 */
104 if (i & 1) { temp = col; col = row ^ mask; row = temp; } /* ROLL0 */
105
106 /* cell offset (0-3 or 0-15) */
107 gfx.lut_cell[i] = row + col * (mask + 1);
108 }
109
110 /* Initialize pixel lookup table */
111 /* table entry = yyyxxxhrr (9 bits) */
112 /* with: yyy = pixel row (0-7) */
113 /* xxx = pixel column (0-7) */
114 /* hrr = HFLIP & ROTATION bits */
115 for (i=0; i<0x200; i++)
116 {
117 /* one cell = 8x8 pixels */
118 row = (i >> 6) & 7;
119 col = (i >> 3) & 7;
120
121 if (i & 4) { col = col ^ 7; } /* HFLIP (always first) */
122 if (i & 2) { col = col ^ 7; row = row ^ 7; } /* ROLL1 */
123 if (i & 1) { temp = col; col = row ^ 7; row = temp; } /* ROLL0 */
124
125 /* pixel offset (0-63) */
126 gfx.lut_pixel[i] = col + row * 8;
127 }
128}
129
e53f0499 130int gfx_context_save(uint8 *state)
131{
132 uint32 tmp32;
133 int bufferptr = 0;
134
a93a80de 135 //save_param(&gfx.cycles, sizeof(gfx.cycles));
136 //save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
e53f0499 137 save_param(&gfx.dotMask, sizeof(gfx.dotMask));
138 save_param(&gfx.stampShift, sizeof(gfx.stampShift));
139 save_param(&gfx.mapShift, sizeof(gfx.mapShift));
140 save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
141 save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
142
a93a80de 143 tmp32 = (uint8 *)(gfx.tracePtr) - Pico_mcd->word_ram2M;
e53f0499 144 save_param(&tmp32, 4);
145
a93a80de 146 tmp32 = (uint8 *)(gfx.mapPtr) - Pico_mcd->word_ram2M;
e53f0499 147 save_param(&tmp32, 4);
148
a93a80de 149 save_param(&gfx.y_step, sizeof(gfx.y_step));
150
e53f0499 151 return bufferptr;
152}
153
a93a80de 154int gfx_context_load(const uint8 *state)
e53f0499 155{
156 uint32 tmp32;
157 int bufferptr = 0;
158
a93a80de 159 //load_param(&gfx.cycles, sizeof(gfx.cycles));
160 //load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
e53f0499 161 load_param(&gfx.dotMask, sizeof(gfx.dotMask));
162 load_param(&gfx.stampShift, sizeof(gfx.stampShift));
163 load_param(&gfx.mapShift, sizeof(gfx.mapShift));
164 load_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
165 load_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
166
167 load_param(&tmp32, 4);
a93a80de 168 gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
e53f0499 169
170 load_param(&tmp32, 4);
a93a80de 171 gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
172
173 load_param(&gfx.y_step, sizeof(gfx.y_step));
e53f0499 174
175 return bufferptr;
176}
177
a93a80de 178static void gfx_render(uint32 bufferIndex, uint32 width)
e53f0499 179{
180 uint8 pixel_in, pixel_out;
181 uint16 stamp_data;
182 uint32 stamp_index;
f3cb39f2 183 uint32 priority;
e53f0499 184
185 /* pixel map start position for current line (13.3 format converted to 13.11) */
186 uint32 xpos = *gfx.tracePtr++ << 8;
187 uint32 ypos = *gfx.tracePtr++ << 8;
188
189 /* pixel map offset values for current line (5.11 format) */
190 uint32 xoffset = (int16) *gfx.tracePtr++;
191 uint32 yoffset = (int16) *gfx.tracePtr++;
192
f3cb39f2 193 priority = (Pico_mcd->s68k_regs[2] << 8) | Pico_mcd->s68k_regs[3];
194 priority = (priority >> 3) & 0x03;
195
e53f0499 196 /* process all dots */
197 while (width--)
198 {
199 /* check if stamp map is repeated */
a93a80de 200 if (Pico_mcd->s68k_regs[0x58+1] & 0x01)
e53f0499 201 {
202 /* stamp map range */
203 xpos &= gfx.dotMask;
204 ypos &= gfx.dotMask;
205 }
206 else
207 {
208 /* 24-bit range */
209 xpos &= 0xffffff;
210 ypos &= 0xffffff;
211 }
212
213 /* check if pixel is outside stamp map */
214 if ((xpos | ypos) & ~gfx.dotMask)
215 {
216 /* force pixel output to 0 */
217 pixel_out = 0x00;
218 }
219 else
220 {
221 /* read stamp map table data */
222 stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];
223
224 /* stamp generator base index */
225 /* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */
226 /* with: s = stamp number (1 stamp = 16x16 or 32x32 pixels) */
227 /* c = cell offset (0-3 for 16x16, 0-15 for 32x32) */
228 /* yyy = line offset (0-7) */
229 /* xxx = pixel offset (0-7) */
230 stamp_index = (stamp_data & 0x7ff) << 8;
231
232 if (stamp_index)
233 {
234 /* extract HFLIP & ROTATION bits */
235 stamp_data = (stamp_data >> 13) & 7;
236
237 /* cell offset (0-3 or 0-15) */
238 /* table entry = yyxxshrr (8 bits) */
239 /* with: yy = cell row (0-3) = (ypos >> (11 + 3)) & 3 */
240 /* xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */
241 /* s = stamp size (0=16x16, 1=32x32) */
242 /* hrr = HFLIP & ROTATION bits */
a93a80de 243 stamp_index |= gfx.lut_cell[
244 stamp_data | ((Pico_mcd->s68k_regs[0x58+1] & 0x02) << 2 )
245 | ((ypos >> 8) & 0xc0) | ((xpos >> 10) & 0x30)] << 6;
e53f0499 246
247 /* pixel offset (0-63) */
248 /* table entry = yyyxxxhrr (9 bits) */
249 /* with: yyy = pixel row (0-7) = (ypos >> 11) & 7 */
250 /* xxx = pixel column (0-7) = (xpos >> 11) & 7 */
251 /* hrr = HFLIP & ROTATION bits */
252 stamp_index |= gfx.lut_pixel[stamp_data | ((xpos >> 8) & 0x38) | ((ypos >> 5) & 0x1c0)];
253
254 /* read pixel pair (2 pixels/byte) */
a93a80de 255 pixel_out = READ_BYTE(Pico_mcd->word_ram2M, stamp_index >> 1);
e53f0499 256
257 /* extract left or rigth pixel */
258 if (stamp_index & 1)
259 {
260 pixel_out &= 0x0f;
261 }
262 else
263 {
264 pixel_out >>= 4;
265 }
266 }
267 else
268 {
269 /* stamp 0 is not used: force pixel output to 0 */
270 pixel_out = 0x00;
271 }
272 }
273
274 /* read out paired pixel data */
a93a80de 275 pixel_in = READ_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1);
e53f0499 276
277 /* update left or rigth pixel */
278 if (bufferIndex & 1)
279 {
f3cb39f2 280 /* priority mode write */
281 pixel_out = gfx.lut_prio[priority][pixel_in & 0x0f][pixel_out];
282
e53f0499 283 pixel_out |= (pixel_in & 0xf0);
284 }
285 else
286 {
f3cb39f2 287 /* priority mode write */
288 pixel_out = gfx.lut_prio[priority][pixel_in >> 4][pixel_out];
289
e53f0499 290 pixel_out = (pixel_out << 4) | (pixel_in & 0x0f);
291 }
292
e53f0499 293 /* write data to image buffer */
a93a80de 294 WRITE_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1, pixel_out);
e53f0499 295
296 /* check current pixel position */
297 if ((bufferIndex & 7) != 7)
298 {
299 /* next pixel */
300 bufferIndex++;
301 }
302 else
303 {
304 /* next cell: increment image buffer offset by one column (minus 7 pixels) */
305 bufferIndex += gfx.bufferOffset;
306 }
307
308 /* increment pixel position */
309 xpos += xoffset;
310 ypos += yoffset;
311 }
312}
313
a93a80de 314void gfx_start(unsigned int base)
e53f0499 315{
316 /* make sure 2M mode is enabled */
a93a80de 317 if (!(Pico_mcd->s68k_regs[3] & 0x04))
e53f0499 318 {
9770f531 319 uint32 mask = 0;
a93a80de 320 uint32 reg;
e53f0499 321
322 /* trace vector pointer */
a93a80de 323 gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + ((base << 2) & 0x3fff8));
e53f0499 324
325 /* stamps & stamp map size */
a93a80de 326 switch ((Pico_mcd->s68k_regs[0x58+1] >> 1) & 0x03)
e53f0499 327 {
328 case 0:
329 gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
330 gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
331 gfx.mapShift = 4; /* 16x16 stamps/map */
332 mask = 0x3fe00; /* 512 bytes/table */
333 break;
334
335 case 1:
336 gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
337 gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
338 gfx.mapShift = 3; /* 8x8 stamps/map */
339 mask = 0x3ff80; /* 128 bytes/table */
340 break;
341
342 case 2:
343 gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
344 gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
345 gfx.mapShift = 8; /* 256x256 stamps/map */
346 mask = 0x20000; /* 131072 bytes/table */
347 break;
348
349 case 3:
350 gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
351 gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
352 gfx.mapShift = 7; /* 128x128 stamps/map */
353 mask = 0x38000; /* 32768 bytes/table */
354 break;
355 }
356
357 /* stamp map table base address */
a93a80de 358 reg = (Pico_mcd->s68k_regs[0x5a] << 8) | Pico_mcd->s68k_regs[0x5b];
359 gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + ((reg << 2) & mask));
e53f0499 360
361 /* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */
a93a80de 362 gfx.bufferOffset = (((Pico_mcd->s68k_regs[0x5c+1] & 0x1f) + 1) << 6) - 7;
e53f0499 363
364 /* image buffer start index in dot units (2 pixels/byte) */
a93a80de 365 reg = (Pico_mcd->s68k_regs[0x5e] << 8) | Pico_mcd->s68k_regs[0x5f];
366 gfx.bufferStart = (reg << 3) & 0x7ffc0;
e53f0499 367
368 /* add image buffer horizontal dot offset */
a93a80de 369 gfx.bufferStart += (Pico_mcd->s68k_regs[0x60+1] & 0x3f);
e53f0499 370
371 /* reset GFX chip cycle counter */
a93a80de 372 //gfx.cycles = cycles;
e53f0499 373
374 /* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */
a93a80de 375 //gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;
e53f0499 376
377 /* start graphics operation */
a93a80de 378 Pico_mcd->s68k_regs[0x58] = 0x80;
379
380 gfx_schedule();
e53f0499 381 }
382}
383
a93a80de 384/* PicoDrive specific */
385#define UPDATE_CYCLES 20000
386
387static void gfx_schedule(void)
e53f0499 388{
a93a80de 389 int w, h, cycles;
390 int y_step;
e53f0499 391
a93a80de 392 w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
393 h = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
e53f0499 394
a93a80de 395 cycles = 5 * w * h;
396 if (cycles > UPDATE_CYCLES)
397 y_step = (UPDATE_CYCLES + 5 * w - 1) / (5 * w);
398 else
399 y_step = h;
e53f0499 400
a93a80de 401 gfx.y_step = y_step;
402 pcd_event_schedule_s68k(PCD_EVENT_GFX, 5 * w * y_step);
403}
e53f0499 404
a93a80de 405void gfx_update(unsigned int cycles)
406{
407 int lines, lines_reg;
408 int w;
e53f0499 409
a93a80de 410 if (!(Pico_mcd->s68k_regs[0x58] & 0x80))
411 return;
e53f0499 412
a93a80de 413 w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
414 lines = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
415 lines_reg = lines - gfx.y_step;
416
417 if (lines_reg <= 0) {
418 Pico_mcd->s68k_regs[0x58] = 0;
419 Pico_mcd->s68k_regs[0x64] =
420 Pico_mcd->s68k_regs[0x65] = 0;
421
422 if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1) {
423 elprintf(EL_INTS|EL_CD, "s68k: gfx_cd irq 1");
424 SekInterruptS68k(1);
e53f0499 425 }
a93a80de 426 }
427 else {
428 Pico_mcd->s68k_regs[0x64] = lines_reg >> 8;
429 Pico_mcd->s68k_regs[0x65] = lines_reg;
430
431 if (lines > gfx.y_step)
432 lines = gfx.y_step;
433
434 pcd_event_schedule(cycles, PCD_EVENT_GFX, 5 * w * lines);
435 }
e53f0499 436
a93a80de 437 if (PicoOpt & POPT_EN_MCD_GFX)
438 {
e53f0499 439 /* render lines */
440 while (lines--)
441 {
442 /* process dots to image buffer */
a93a80de 443 gfx_render(gfx.bufferStart, w);
e53f0499 444
445 /* increment image buffer start index for next line (8 pixels/line) */
446 gfx.bufferStart += 8;
447 }
448 }
449}
a93a80de 450
451// vim:shiftwidth=2:ts=2:expandtab