32x, take over sh2 cycles setting in-game
[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 */
f9b966de 46 uint32 stampMask; /* stamp number mask */
e53f0499 47 uint16 *tracePtr; /* trace vector pointer */
48 uint16 *mapPtr; /* stamp map table base address */
49 uint8 stampShift; /* stamp pixel shift value (related to stamp size) */
50 uint8 mapShift; /* stamp map table shift value (related to stamp map size) */
51 uint16 bufferOffset; /* image buffer column offset */
52 uint32 bufferStart; /* image buffer start index */
a93a80de 53 uint32 y_step; /* pico: render line step */
f3cb39f2 54 uint8 lut_prio[4][0x10][0x10]; /* WORD-RAM data writes priority lookup table */
e53f0499 55 uint8 lut_pixel[0x200]; /* Graphics operation dot offset lookup table */
b633247f 56 uint16 lut_cell2[0x80]; /* Graphics operation stamp offset lookup table */
57 uint16 lut_cell4[0x80]; /* Graphics operation stamp offset lookup table */
e53f0499 58} gfx_t;
59
60static gfx_t gfx;
61
a93a80de 62static void gfx_schedule(void);
63
e53f0499 64/***************************************************************/
65/* Rotation / Scaling operation (2M Mode) */
66/***************************************************************/
67
68void gfx_init(void)
69{
70 int i, j;
b633247f 71 uint8 row, col, temp;
e53f0499 72
73 memset(&gfx, 0, sizeof(gfx));
74
75 /* Initialize priority modes lookup table */
f3cb39f2 76 for (i = 0; i < 0x10; i++)
e53f0499 77 {
f3cb39f2 78 for (j = 0; j < 0x10; j++)
e53f0499 79 {
80 /* normal */
81 gfx.lut_prio[0][i][j] = j;
82 /* underwrite */
f3cb39f2 83 gfx.lut_prio[1][i][j] = i ? i : j;
e53f0499 84 /* overwrite */
f3cb39f2 85 gfx.lut_prio[2][i][j] = j ? j : i;
e53f0499 86 /* invalid */
87 gfx.lut_prio[3][i][j] = i;
88 }
89 }
90
91 /* Initialize cell lookup table */
7ce6a6d1 92 /* table entry = yyxxhrr (7 bits) */
e53f0499 93 /* with: yy = cell row (0-3) */
94 /* xx = cell column (0-3) */
e53f0499 95 /* hrr = HFLIP & ROTATION bits */
b633247f 96 for (i=0; i<0x80; i++)
e53f0499 97 {
98 /* one stamp = 2x2 cells (16x16) or 4x4 cells (32x32) */
b633247f 99 row = (i >> 5) & 3;
100 col = (i >> 3) & 3;
e53f0499 101
b633247f 102 if (i & 4) { col = col ^ 3; } /* HFLIP (always first) */
103 if (i & 2) { col = col ^ 3; row = row ^ 3; } /* ROLL1 */
104 if (i & 1) { temp = col; col = row ^ 3; row = temp; } /* ROLL0 */
e53f0499 105
106 /* cell offset (0-3 or 0-15) */
b633247f 107 gfx.lut_cell2[i] = ((row&1) + (col&1) * 2) << 6;
108 gfx.lut_cell4[i] = ((row&3) + (col&3) * 4) << 6;
e53f0499 109 }
110
111 /* Initialize pixel lookup table */
112 /* table entry = yyyxxxhrr (9 bits) */
113 /* with: yyy = pixel row (0-7) */
114 /* xxx = pixel column (0-7) */
115 /* hrr = HFLIP & ROTATION bits */
116 for (i=0; i<0x200; i++)
117 {
118 /* one cell = 8x8 pixels */
119 row = (i >> 6) & 7;
120 col = (i >> 3) & 7;
121
122 if (i & 4) { col = col ^ 7; } /* HFLIP (always first) */
123 if (i & 2) { col = col ^ 7; row = row ^ 7; } /* ROLL1 */
124 if (i & 1) { temp = col; col = row ^ 7; row = temp; } /* ROLL0 */
125
126 /* pixel offset (0-63) */
127 gfx.lut_pixel[i] = col + row * 8;
128 }
129}
130
e53f0499 131int gfx_context_save(uint8 *state)
132{
133 uint32 tmp32;
134 int bufferptr = 0;
135
a93a80de 136 //save_param(&gfx.cycles, sizeof(gfx.cycles));
137 //save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
e53f0499 138 save_param(&gfx.dotMask, sizeof(gfx.dotMask));
139 save_param(&gfx.stampShift, sizeof(gfx.stampShift));
140 save_param(&gfx.mapShift, sizeof(gfx.mapShift));
141 save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
142 save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
143
a93a80de 144 tmp32 = (uint8 *)(gfx.tracePtr) - Pico_mcd->word_ram2M;
e53f0499 145 save_param(&tmp32, 4);
146
a93a80de 147 tmp32 = (uint8 *)(gfx.mapPtr) - Pico_mcd->word_ram2M;
e53f0499 148 save_param(&tmp32, 4);
149
a93a80de 150 save_param(&gfx.y_step, sizeof(gfx.y_step));
151
e53f0499 152 return bufferptr;
153}
154
a93a80de 155int gfx_context_load(const uint8 *state)
e53f0499 156{
157 uint32 tmp32;
158 int bufferptr = 0;
159
a93a80de 160 //load_param(&gfx.cycles, sizeof(gfx.cycles));
161 //load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
e53f0499 162 load_param(&gfx.dotMask, sizeof(gfx.dotMask));
163 load_param(&gfx.stampShift, sizeof(gfx.stampShift));
164 load_param(&gfx.mapShift, sizeof(gfx.mapShift));
165 load_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
166 load_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
167
168 load_param(&tmp32, 4);
a93a80de 169 gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
e53f0499 170
171 load_param(&tmp32, 4);
a93a80de 172 gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
173
174 load_param(&gfx.y_step, sizeof(gfx.y_step));
e53f0499 175
176 return bufferptr;
177}
178
b633247f 179static inline int gfx_pixel(uint32 xpos, uint32 ypos, uint16 *lut_cell)
e53f0499 180{
e53f0499 181 uint16 stamp_data;
182 uint32 stamp_index;
b633247f 183 uint8 pixel_out = 0x00;
184
185 /* check if pixel is outside stamp map */
186 if (((xpos | ypos) & ~gfx.dotMask) == 0)
187 {
188 /* read stamp map table data */
189 stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];
190
191 /* stamp generator base index */
192 /* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */
193 /* with: s = stamp number (1 stamp = 16x16 or 32x32 pixels) */
194 /* c = cell offset (0-3 for 16x16, 0-15 for 32x32) */
195 /* yyy = line offset (0-7) */
196 /* xxx = pixel offset (0-7) */
f9b966de 197 stamp_index = (stamp_data & gfx.stampMask) << 8;
b633247f 198
199 if (stamp_index)
200 {
201 /* extract HFLIP & ROTATION bits */
202 stamp_data = (stamp_data >> 13) & 7;
203
204 /* cell offset (0-3 or 0-15) */
7ce6a6d1 205 /* table entry = yyxxhrr (7 bits) */
b633247f 206 /* with: yy = cell row (0-3) = (ypos >> (11 + 3)) & 3 */
207 /* xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */
208 /* hrr = HFLIP & ROTATION bits */
209 stamp_index |= lut_cell[stamp_data | ((ypos >> 9) & 0x60) | ((xpos >> 11) & 0x18)];
210
211 /* pixel offset (0-63) */
212 /* table entry = yyyxxxhrr (9 bits) */
213 /* with: yyy = pixel row (0-7) = (ypos >> 11) & 7 */
214 /* xxx = pixel column (0-7) = (xpos >> 11) & 7 */
215 /* hrr = HFLIP & ROTATION bits */
7ce6a6d1 216 stamp_index |= gfx.lut_pixel[stamp_data | ((ypos >> 5) & 0x1c0) | ((xpos >> 8) & 0x38)];
b633247f 217
218 /* read pixel pair (2 pixels/byte) */
219 pixel_out = READ_BYTE(Pico_mcd->word_ram2M, stamp_index >> 1);
220
7ce6a6d1 221 /* extract left or right pixel */
222 pixel_out >>= 4 * !(stamp_index & 1);
b633247f 223 pixel_out &= 0x0f;
224 }
225 }
226
227 return pixel_out;
228}
229
230#define RENDER_LOOP(N, UPDP, COND1, COND2) do { \
231 if (bufferIndex & 1) { \
7ce6a6d1 232 bufferIndex ^= 1; \
b633247f 233 goto right##N; /* no initial left pixel */ \
234 } \
235 /* process all dots */ \
236 while (width--) \
237 { \
238 /* left pixel */ \
239 xpos &= mask; \
240 ypos &= mask; \
241 \
242 if (COND1) { \
243 pixel_out = gfx_pixel(xpos, ypos, lut_cell); \
244 UPDP; \
245 } \
246 \
247 if (COND2) { \
248 /* read out paired pixel data */ \
249 pixel_in = READ_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1); \
250 \
251 /* priority mode write */ \
252 pixel_in = (lut_prio[(pixel_in & 0xf0) >> 4][pixel_out] << 4) | \
253 (pixel_in & 0x0f); \
254 \
255 /* write data to image buffer */ \
256 WRITE_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1, pixel_in); \
257 } \
258 \
259 /* increment pixel position */ \
260 xpos += xoffset; \
261 ypos += yoffset; \
262 \
263right##N: \
264 if (width-- == 0) break; \
265 \
266 /* right pixel */ \
267 xpos &= mask; \
268 ypos &= mask; \
269 \
270 if (COND1) { \
271 pixel_out = gfx_pixel(xpos, ypos, lut_cell); \
272 UPDP; \
273 } \
274 \
275 if (COND2) { \
276 /* read out paired pixel data */ \
277 pixel_in = READ_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1); \
278 \
279 /* priority mode write */ \
280 pixel_in = (lut_prio[pixel_in & 0x0f][pixel_out]) | \
281 (pixel_in & 0xf0); \
282 \
283 /* write data to image buffer */ \
284 WRITE_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1, pixel_in); \
285 } \
286 \
287 /* increment pixel position */ \
288 xpos += xoffset; \
289 ypos += yoffset; \
290 \
291 /* next pixel */ \
292 bufferIndex += 2; \
293 /* check current pixel position */ \
294 if ((bufferIndex & 7) == 0) \
295 { \
296 /* next cell: increment buffer offset by one column (minus 8 pixels) */ \
297 bufferIndex += gfx.bufferOffset-1; \
298 } \
299 } \
300} while (0)
301
302static void gfx_render(uint32 bufferIndex, uint32 width)
303{
304 uint8 pixel_in, pixel_out;
f3cb39f2 305 uint32 priority;
b633247f 306 uint8 (*lut_prio)[0x10];
307 uint16 *lut_cell;
308 uint32 mask;
e53f0499 309
310 /* pixel map start position for current line (13.3 format converted to 13.11) */
311 uint32 xpos = *gfx.tracePtr++ << 8;
312 uint32 ypos = *gfx.tracePtr++ << 8;
313
314 /* pixel map offset values for current line (5.11 format) */
315 uint32 xoffset = (int16) *gfx.tracePtr++;
316 uint32 yoffset = (int16) *gfx.tracePtr++;
317
f3cb39f2 318 priority = (Pico_mcd->s68k_regs[2] << 8) | Pico_mcd->s68k_regs[3];
319 priority = (priority >> 3) & 0x03;
b633247f 320 lut_prio = gfx.lut_prio[priority];
f3cb39f2 321
b633247f 322 lut_cell = (Pico_mcd->s68k_regs[0x58+1] & 0x02) ? gfx.lut_cell4 : gfx.lut_cell2;
e53f0499 323
b633247f 324 /* check if stamp map is repeated */
325 mask = 0xffffff; /* 24-bit range */
326 if (Pico_mcd->s68k_regs[0x58+1] & 0x01)
327 {
328 /* stamp map range */
329 mask = gfx.dotMask;
330 }
e53f0499 331
b633247f 332 pixel_out = 0;
333 if (xoffset+(1U<<10) <= 1U<<11 && yoffset+(1U<<10) <= 1U<<11) {
334 /* upscaling >= 2x, test for duplicate pixels to avoid recalculation */
335 uint32 oldx, oldy;
336 oldx = oldy = ~xpos;
337 RENDER_LOOP(1, oldx = xpos;oldy = ypos, (oldx^xpos ^ oldy^ypos) >> 11, (!priority) | pixel_out);
338 } else {
339 RENDER_LOOP(3, , 1, (!priority) | pixel_out);
e53f0499 340 }
341}
342
f821bb70 343void gfx_start(uint32 base)
e53f0499 344{
345 /* make sure 2M mode is enabled */
a93a80de 346 if (!(Pico_mcd->s68k_regs[3] & 0x04))
e53f0499 347 {
9770f531 348 uint32 mask = 0;
a93a80de 349 uint32 reg;
e53f0499 350
351 /* trace vector pointer */
a93a80de 352 gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + ((base << 2) & 0x3fff8));
e53f0499 353
354 /* stamps & stamp map size */
a93a80de 355 switch ((Pico_mcd->s68k_regs[0x58+1] >> 1) & 0x03)
e53f0499 356 {
357 case 0:
358 gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
f9b966de 359 gfx.stampMask = 0x7ff; /* 16x16 dots/stamp */
360 gfx.stampShift = 11 + 4; /* 16x16 dots/stamp */
e53f0499 361 gfx.mapShift = 4; /* 16x16 stamps/map */
362 mask = 0x3fe00; /* 512 bytes/table */
363 break;
364
365 case 1:
366 gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
f9b966de 367 gfx.stampMask = 0x7fc; /* 16x16 dots/stamp */
368 gfx.stampShift = 11 + 5; /* 32x32 dots/stamp */
e53f0499 369 gfx.mapShift = 3; /* 8x8 stamps/map */
370 mask = 0x3ff80; /* 128 bytes/table */
371 break;
372
373 case 2:
374 gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
f9b966de 375 gfx.stampMask = 0x7ff; /* 16x16 dots/stamp */
376 gfx.stampShift = 11 + 4; /* 16x16 dots/stamp */
e53f0499 377 gfx.mapShift = 8; /* 256x256 stamps/map */
378 mask = 0x20000; /* 131072 bytes/table */
379 break;
380
381 case 3:
382 gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
f9b966de 383 gfx.stampMask = 0x7fc; /* 16x16 dots/stamp */
384 gfx.stampShift = 11 + 5; /* 32x32 dots/stamp */
e53f0499 385 gfx.mapShift = 7; /* 128x128 stamps/map */
386 mask = 0x38000; /* 32768 bytes/table */
387 break;
388 }
389
390 /* stamp map table base address */
a93a80de 391 reg = (Pico_mcd->s68k_regs[0x5a] << 8) | Pico_mcd->s68k_regs[0x5b];
392 gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + ((reg << 2) & mask));
e53f0499 393
394 /* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */
a93a80de 395 gfx.bufferOffset = (((Pico_mcd->s68k_regs[0x5c+1] & 0x1f) + 1) << 6) - 7;
e53f0499 396
397 /* image buffer start index in dot units (2 pixels/byte) */
a93a80de 398 reg = (Pico_mcd->s68k_regs[0x5e] << 8) | Pico_mcd->s68k_regs[0x5f];
399 gfx.bufferStart = (reg << 3) & 0x7ffc0;
e53f0499 400
401 /* add image buffer horizontal dot offset */
a93a80de 402 gfx.bufferStart += (Pico_mcd->s68k_regs[0x60+1] & 0x3f);
e53f0499 403
404 /* reset GFX chip cycle counter */
a93a80de 405 //gfx.cycles = cycles;
e53f0499 406
407 /* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */
a93a80de 408 //gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;
e53f0499 409
410 /* start graphics operation */
a93a80de 411 Pico_mcd->s68k_regs[0x58] = 0x80;
4985bad0 412 Pico_mcd->m.state_flags &= ~PCD_ST_S68K_POLL;
413 Pico_mcd->m.s68k_poll_cnt = 0;
a93a80de 414
415 gfx_schedule();
e53f0499 416 }
417}
418
a93a80de 419/* PicoDrive specific */
420#define UPDATE_CYCLES 20000
421
422static void gfx_schedule(void)
e53f0499 423{
a93a80de 424 int w, h, cycles;
425 int y_step;
e53f0499 426
a93a80de 427 w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
428 h = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
e53f0499 429
a93a80de 430 cycles = 5 * w * h;
7ce6a6d1 431 y_step = h;
a93a80de 432 if (cycles > UPDATE_CYCLES)
433 y_step = (UPDATE_CYCLES + 5 * w - 1) / (5 * w);
e53f0499 434
a93a80de 435 gfx.y_step = y_step;
436 pcd_event_schedule_s68k(PCD_EVENT_GFX, 5 * w * y_step);
437}
e53f0499 438
a93a80de 439void gfx_update(unsigned int cycles)
440{
441 int lines, lines_reg;
442 int w;
e53f0499 443
a93a80de 444 if (!(Pico_mcd->s68k_regs[0x58] & 0x80))
445 return;
e53f0499 446
a93a80de 447 w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
448 lines = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
449 lines_reg = lines - gfx.y_step;
450
451 if (lines_reg <= 0) {
452 Pico_mcd->s68k_regs[0x58] = 0;
453 Pico_mcd->s68k_regs[0x64] =
454 Pico_mcd->s68k_regs[0x65] = 0;
455
4985bad0 456 Pico_mcd->m.state_flags &= ~PCD_ST_S68K_POLL;
457 Pico_mcd->m.s68k_poll_cnt = 0;
a93a80de 458 if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1) {
459 elprintf(EL_INTS|EL_CD, "s68k: gfx_cd irq 1");
eb36d9c7 460 pcd_irq_s68k(1, 1);
e53f0499 461 }
a93a80de 462 }
463 else {
464 Pico_mcd->s68k_regs[0x64] = lines_reg >> 8;
465 Pico_mcd->s68k_regs[0x65] = lines_reg;
466
467 if (lines > gfx.y_step)
468 lines = gfx.y_step;
469
470 pcd_event_schedule(cycles, PCD_EVENT_GFX, 5 * w * lines);
471 }
e53f0499 472
93f9619e 473 if (PicoIn.opt & POPT_EN_MCD_GFX)
a93a80de 474 {
e53f0499 475 /* render lines */
476 while (lines--)
477 {
478 /* process dots to image buffer */
a93a80de 479 gfx_render(gfx.bufferStart, w);
e53f0499 480
481 /* increment image buffer start index for next line (8 pixels/line) */
482 gfx.bufferStart += 8;
483 }
484 }
485}
a93a80de 486
487// vim:shiftwidth=2:ts=2:expandtab