cd: pull in Genesis-Plus-GX CD gfx code
[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 ****************************************************************************************/
38#include "genplus_types.h"
39
40typedef struct
41{
42 uint32 cycles; /* current cycles count for graphics operation */
43 uint32 cyclesPerLine; /* current graphics operation timings */
44 uint32 dotMask; /* stamp map size mask */
45 uint16 *tracePtr; /* trace vector pointer */
46 uint16 *mapPtr; /* stamp map table base address */
47 uint8 stampShift; /* stamp pixel shift value (related to stamp size) */
48 uint8 mapShift; /* stamp map table shift value (related to stamp map size) */
49 uint16 bufferOffset; /* image buffer column offset */
50 uint32 bufferStart; /* image buffer start index */
51 uint8 lut_prio[4][0x100][0x100]; /* WORD-RAM data writes priority lookup table */
52 uint8 lut_pixel[0x200]; /* Graphics operation dot offset lookup table */
53 uint8 lut_cell[0x100]; /* Graphics operation stamp offset lookup table */
54} gfx_t;
55
56static gfx_t gfx;
57
58/***************************************************************/
59/* Rotation / Scaling operation (2M Mode) */
60/***************************************************************/
61
62void gfx_init(void)
63{
64 int i, j;
65 uint16 offset;
66 uint8 mask, row, col, temp;
67
68 memset(&gfx, 0, sizeof(gfx));
69
70 /* Initialize priority modes lookup table */
71 for (i=0; i<0x100; i++)
72 {
73 for (j=0; j<0x100; j++)
74 {
75 /* normal */
76 gfx.lut_prio[0][i][j] = j;
77 /* underwrite */
78 gfx.lut_prio[1][i][j] = ((i & 0x0f) ? (i & 0x0f) : (j & 0x0f)) | ((i & 0xf0) ? (i & 0xf0) : (j & 0xf0));
79 /* overwrite */
80 gfx.lut_prio[2][i][j] = ((j & 0x0f) ? (j & 0x0f) : (i & 0x0f)) | ((j & 0xf0) ? (j & 0xf0) : (i & 0xf0));
81 /* invalid */
82 gfx.lut_prio[3][i][j] = i;
83 }
84 }
85
86 /* Initialize cell lookup table */
87 /* table entry = yyxxshrr (8 bits) */
88 /* with: yy = cell row (0-3) */
89 /* xx = cell column (0-3) */
90 /* s = stamp size (0=16x16, 1=32x32) */
91 /* hrr = HFLIP & ROTATION bits */
92 for (i=0; i<0x100; i++)
93 {
94 /* one stamp = 2x2 cells (16x16) or 4x4 cells (32x32) */
95 mask = (i & 8) ? 3 : 1;
96 row = (i >> 6) & mask;
97 col = (i >> 4) & mask;
98
99 if (i & 4) { col = col ^ mask; } /* HFLIP (always first) */
100 if (i & 2) { col = col ^ mask; row = row ^ mask; } /* ROLL1 */
101 if (i & 1) { temp = col; col = row ^ mask; row = temp; } /* ROLL0 */
102
103 /* cell offset (0-3 or 0-15) */
104 gfx.lut_cell[i] = row + col * (mask + 1);
105 }
106
107 /* Initialize pixel lookup table */
108 /* table entry = yyyxxxhrr (9 bits) */
109 /* with: yyy = pixel row (0-7) */
110 /* xxx = pixel column (0-7) */
111 /* hrr = HFLIP & ROTATION bits */
112 for (i=0; i<0x200; i++)
113 {
114 /* one cell = 8x8 pixels */
115 row = (i >> 6) & 7;
116 col = (i >> 3) & 7;
117
118 if (i & 4) { col = col ^ 7; } /* HFLIP (always first) */
119 if (i & 2) { col = col ^ 7; row = row ^ 7; } /* ROLL1 */
120 if (i & 1) { temp = col; col = row ^ 7; row = temp; } /* ROLL0 */
121
122 /* pixel offset (0-63) */
123 gfx.lut_pixel[i] = col + row * 8;
124 }
125}
126
127void gfx_reset(void)
128{
129 /* Reset cycle counter */
130 gfx.cycles = 0;
131}
132
133int gfx_context_save(uint8 *state)
134{
135 uint32 tmp32;
136 int bufferptr = 0;
137
138 save_param(&gfx.cycles, sizeof(gfx.cycles));
139 save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
140 save_param(&gfx.dotMask, sizeof(gfx.dotMask));
141 save_param(&gfx.stampShift, sizeof(gfx.stampShift));
142 save_param(&gfx.mapShift, sizeof(gfx.mapShift));
143 save_param(&gfx.bufferOffset, sizeof(gfx.bufferOffset));
144 save_param(&gfx.bufferStart, sizeof(gfx.bufferStart));
145
146 tmp32 = (uint8 *)(gfx.tracePtr) - scd.word_ram_2M;
147 save_param(&tmp32, 4);
148
149 tmp32 = (uint8 *)(gfx.mapPtr) - scd.word_ram_2M;
150 save_param(&tmp32, 4);
151
152 return bufferptr;
153}
154
155int gfx_context_load(uint8 *state)
156{
157 uint32 tmp32;
158 int bufferptr = 0;
159
160 load_param(&gfx.cycles, sizeof(gfx.cycles));
161 load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
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);
169 gfx.tracePtr = (uint16 *)(scd.word_ram_2M + tmp32);
170
171 load_param(&tmp32, 4);
172 gfx.mapPtr = (uint16 *)(scd.word_ram_2M + tmp32);
173
174 return bufferptr;
175}
176
177INLINE void gfx_render(uint32 bufferIndex, uint32 width)
178{
179 uint8 pixel_in, pixel_out;
180 uint16 stamp_data;
181 uint32 stamp_index;
182
183 /* pixel map start position for current line (13.3 format converted to 13.11) */
184 uint32 xpos = *gfx.tracePtr++ << 8;
185 uint32 ypos = *gfx.tracePtr++ << 8;
186
187 /* pixel map offset values for current line (5.11 format) */
188 uint32 xoffset = (int16) *gfx.tracePtr++;
189 uint32 yoffset = (int16) *gfx.tracePtr++;
190
191 /* process all dots */
192 while (width--)
193 {
194 /* check if stamp map is repeated */
195 if (scd.regs[0x58>>1].byte.l & 0x01)
196 {
197 /* stamp map range */
198 xpos &= gfx.dotMask;
199 ypos &= gfx.dotMask;
200 }
201 else
202 {
203 /* 24-bit range */
204 xpos &= 0xffffff;
205 ypos &= 0xffffff;
206 }
207
208 /* check if pixel is outside stamp map */
209 if ((xpos | ypos) & ~gfx.dotMask)
210 {
211 /* force pixel output to 0 */
212 pixel_out = 0x00;
213 }
214 else
215 {
216 /* read stamp map table data */
217 stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];
218
219 /* stamp generator base index */
220 /* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */
221 /* with: s = stamp number (1 stamp = 16x16 or 32x32 pixels) */
222 /* c = cell offset (0-3 for 16x16, 0-15 for 32x32) */
223 /* yyy = line offset (0-7) */
224 /* xxx = pixel offset (0-7) */
225 stamp_index = (stamp_data & 0x7ff) << 8;
226
227 if (stamp_index)
228 {
229 /* extract HFLIP & ROTATION bits */
230 stamp_data = (stamp_data >> 13) & 7;
231
232 /* cell offset (0-3 or 0-15) */
233 /* table entry = yyxxshrr (8 bits) */
234 /* with: yy = cell row (0-3) = (ypos >> (11 + 3)) & 3 */
235 /* xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */
236 /* s = stamp size (0=16x16, 1=32x32) */
237 /* hrr = HFLIP & ROTATION bits */
238 stamp_index |= gfx.lut_cell[stamp_data | ((scd.regs[0x58>>1].byte.l & 0x02) << 2 ) | ((ypos >> 8) & 0xc0) | ((xpos >> 10) & 0x30)] << 6;
239
240 /* pixel offset (0-63) */
241 /* table entry = yyyxxxhrr (9 bits) */
242 /* with: yyy = pixel row (0-7) = (ypos >> 11) & 7 */
243 /* xxx = pixel column (0-7) = (xpos >> 11) & 7 */
244 /* hrr = HFLIP & ROTATION bits */
245 stamp_index |= gfx.lut_pixel[stamp_data | ((xpos >> 8) & 0x38) | ((ypos >> 5) & 0x1c0)];
246
247 /* read pixel pair (2 pixels/byte) */
248 pixel_out = READ_BYTE(scd.word_ram_2M, stamp_index >> 1);
249
250 /* extract left or rigth pixel */
251 if (stamp_index & 1)
252 {
253 pixel_out &= 0x0f;
254 }
255 else
256 {
257 pixel_out >>= 4;
258 }
259 }
260 else
261 {
262 /* stamp 0 is not used: force pixel output to 0 */
263 pixel_out = 0x00;
264 }
265 }
266
267 /* read out paired pixel data */
268 pixel_in = READ_BYTE(scd.word_ram_2M, bufferIndex >> 1);
269
270 /* update left or rigth pixel */
271 if (bufferIndex & 1)
272 {
273 pixel_out |= (pixel_in & 0xf0);
274 }
275 else
276 {
277 pixel_out = (pixel_out << 4) | (pixel_in & 0x0f);
278 }
279
280 /* priority mode write */
281 pixel_out = gfx.lut_prio[(scd.regs[0x02>>1].w >> 3) & 0x03][pixel_in][pixel_out];
282
283 /* write data to image buffer */
284 WRITE_BYTE(scd.word_ram_2M, bufferIndex >> 1, pixel_out);
285
286 /* check current pixel position */
287 if ((bufferIndex & 7) != 7)
288 {
289 /* next pixel */
290 bufferIndex++;
291 }
292 else
293 {
294 /* next cell: increment image buffer offset by one column (minus 7 pixels) */
295 bufferIndex += gfx.bufferOffset;
296 }
297
298 /* increment pixel position */
299 xpos += xoffset;
300 ypos += yoffset;
301 }
302}
303
304void gfx_start(unsigned int base, int cycles)
305{
306 /* make sure 2M mode is enabled */
307 if (!(scd.regs[0x02>>1].byte.l & 0x04))
308 {
309 uint32 mask;
310
311 /* trace vector pointer */
312 gfx.tracePtr = (uint16 *)(scd.word_ram_2M + ((base << 2) & 0x3fff8));
313
314 /* stamps & stamp map size */
315 switch ((scd.regs[0x58>>1].byte.l >> 1) & 0x03)
316 {
317 case 0:
318 gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
319 gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
320 gfx.mapShift = 4; /* 16x16 stamps/map */
321 mask = 0x3fe00; /* 512 bytes/table */
322 break;
323
324 case 1:
325 gfx.dotMask = 0x07ffff; /* 256x256 dots/map */
326 gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
327 gfx.mapShift = 3; /* 8x8 stamps/map */
328 mask = 0x3ff80; /* 128 bytes/table */
329 break;
330
331 case 2:
332 gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
333 gfx.stampShift = 11 + 4; /* 16x16 dots/stamps */
334 gfx.mapShift = 8; /* 256x256 stamps/map */
335 mask = 0x20000; /* 131072 bytes/table */
336 break;
337
338 case 3:
339 gfx.dotMask = 0x7fffff; /* 4096*4096 dots/map */
340 gfx.stampShift = 11 + 5; /* 32x32 dots/stamps */
341 gfx.mapShift = 7; /* 128x128 stamps/map */
342 mask = 0x38000; /* 32768 bytes/table */
343 break;
344 }
345
346 /* stamp map table base address */
347 gfx.mapPtr = (uint16 *)(scd.word_ram_2M + ((scd.regs[0x5a>>1].w << 2) & mask));
348
349 /* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */
350 gfx.bufferOffset = (((scd.regs[0x5c>>1].byte.l & 0x1f) + 1) << 6) - 7;
351
352 /* image buffer start index in dot units (2 pixels/byte) */
353 gfx.bufferStart = (scd.regs[0x5e>>1].w << 3) & 0x7ffc0;
354
355 /* add image buffer horizontal dot offset */
356 gfx.bufferStart += (scd.regs[0x60>>1].byte.l & 0x3f);
357
358 /* reset GFX chip cycle counter */
359 gfx.cycles = cycles;
360
361 /* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */
362 gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;
363
364 /* start graphics operation */
365 scd.regs[0x58>>1].byte.h = 0x80;
366 }
367}
368
369void gfx_update(int cycles)
370{
371 /* synchronize GFX chip with SUB-CPU */
372 cycles -= gfx.cycles;
373
374 /* make sure SUB-CPU is ahead */
375 if (cycles > 0)
376 {
377 /* number of lines to process */
378 unsigned int lines = (cycles + gfx.cyclesPerLine - 1) / gfx.cyclesPerLine;
379
380 /* check against remaining lines */
381 if (lines < scd.regs[0x64>>1].byte.l)
382 {
383 /* update Vdot remaining size */
384 scd.regs[0x64>>1].byte.l -= lines;
385
386 /* increment cycle counter */
387 gfx.cycles += lines * gfx.cyclesPerLine;
388 }
389 else
390 {
391 /* process remaining lines */
392 lines = scd.regs[0x64>>1].byte.l;
393
394 /* clear Vdot remaining size */
395 scd.regs[0x64>>1].byte.l = 0;
396
397 /* end of graphics operation */
398 scd.regs[0x58>>1].byte.h = 0;
399
400 /* level 1 interrupt enabled ? */
401 if (scd.regs[0x32>>1].byte.l & 0x02)
402 {
403 /* trigger level 1 interrupt */
404 scd.pending |= (1 << 1);
405
406 /* update IRQ level */
407 s68k_update_irq((scd.pending & scd.regs[0x32>>1].byte.l) >> 1);
408 }
409 }
410
411 /* render lines */
412 while (lines--)
413 {
414 /* process dots to image buffer */
415 gfx_render(gfx.bufferStart, scd.regs[0x62>>1].w);
416
417 /* increment image buffer start index for next line (8 pixels/line) */
418 gfx.bufferStart += 8;
419 }
420 }
421}