cd: pull in Genesis-Plus-GX CD gfx code
[picodrive.git] / pico / cd / gfx.c
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
40 typedef 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
56 static gfx_t gfx;
57
58 /***************************************************************/
59 /*      Rotation / Scaling operation (2M Mode)                 */
60 /***************************************************************/
61
62 void 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
127 void gfx_reset(void)
128
129   /* Reset cycle counter */
130   gfx.cycles = 0;
131 }
132
133 int 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
155 int 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
177 INLINE 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
304 void 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
369 void 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 }