cd: integrate new 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 "../pico_int.h"
39 #include "genplus_macros.h"
40
41 typedef struct
42 {
43   //uint32 cycles;                    /* current cycles count for graphics operation */
44   //uint32 cyclesPerLine;             /* current graphics operation timings */
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 */
52   uint32 y_step;                    /* pico: render line step */
53   uint8 lut_prio[4][0x100][0x100];  /* WORD-RAM data writes priority lookup table */
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
58 static gfx_t gfx;
59
60 static void gfx_schedule(void);
61
62 /***************************************************************/
63 /*      Rotation / Scaling operation (2M Mode)                 */
64 /***************************************************************/
65
66 void gfx_init(void)
67 {
68   int i, j;
69   uint8 mask, row, col, temp;
70
71   memset(&gfx, 0, sizeof(gfx));
72
73   /* Initialize priority modes lookup table */
74   for (i=0; i<0x100; i++)
75   {
76     for (j=0; j<0x100; j++)
77     {
78       /* normal */
79       gfx.lut_prio[0][i][j] = j;
80       /* underwrite */
81       gfx.lut_prio[1][i][j] = ((i & 0x0f) ? (i & 0x0f) : (j & 0x0f)) | ((i & 0xf0) ? (i & 0xf0) : (j & 0xf0));
82       /* overwrite */
83       gfx.lut_prio[2][i][j] = ((j & 0x0f) ? (j & 0x0f) : (i & 0x0f)) | ((j & 0xf0) ? (j & 0xf0) : (i & 0xf0));
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
130 int gfx_context_save(uint8 *state)
131 {
132   uint32 tmp32;
133   int bufferptr = 0;
134
135   //save_param(&gfx.cycles, sizeof(gfx.cycles));
136   //save_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
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
143   tmp32 = (uint8 *)(gfx.tracePtr) - Pico_mcd->word_ram2M;
144   save_param(&tmp32, 4);
145
146   tmp32 = (uint8 *)(gfx.mapPtr) - Pico_mcd->word_ram2M;
147   save_param(&tmp32, 4);
148
149   save_param(&gfx.y_step, sizeof(gfx.y_step));
150
151   return bufferptr;
152 }
153
154 int gfx_context_load(const uint8 *state)
155 {
156   uint32 tmp32;
157   int bufferptr = 0;
158
159   //load_param(&gfx.cycles, sizeof(gfx.cycles));
160   //load_param(&gfx.cyclesPerLine, sizeof(gfx.cyclesPerLine));
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);
168   gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
169
170   load_param(&tmp32, 4);
171   gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + tmp32);
172
173   load_param(&gfx.y_step, sizeof(gfx.y_step));
174
175   return bufferptr;
176 }
177
178 static void gfx_render(uint32 bufferIndex, uint32 width)
179 {
180   uint8 pixel_in, pixel_out;
181   uint16 stamp_data;
182   uint32 stamp_index;
183   uint32 reg;
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
193   /* process all dots */
194   while (width--)
195   {
196     /* check if stamp map is repeated */
197     if (Pico_mcd->s68k_regs[0x58+1] & 0x01)
198     {
199       /* stamp map range */
200       xpos &= gfx.dotMask;
201       ypos &= gfx.dotMask;
202     }
203     else
204     {
205       /* 24-bit range */
206       xpos &= 0xffffff;
207       ypos &= 0xffffff;
208     }
209
210     /* check if pixel is outside stamp map */
211     if ((xpos | ypos) & ~gfx.dotMask)
212     {
213       /* force pixel output to 0 */
214       pixel_out = 0x00;
215     }
216     else
217     {
218       /* read stamp map table data */
219       stamp_data = gfx.mapPtr[(xpos >> gfx.stampShift) | ((ypos >> gfx.stampShift) << gfx.mapShift)];
220
221       /* stamp generator base index                                     */
222       /* sss ssssssss ccyyyxxx (16x16) or sss sssssscc ccyyyxxx (32x32) */
223       /* with:  s = stamp number (1 stamp = 16x16 or 32x32 pixels)      */
224       /*        c = cell offset  (0-3 for 16x16, 0-15 for 32x32)        */
225       /*      yyy = line offset  (0-7)                                  */
226       /*      xxx = pixel offset (0-7)                                  */
227       stamp_index = (stamp_data & 0x7ff) << 8;
228
229       if (stamp_index)
230       {
231         /* extract HFLIP & ROTATION bits */
232         stamp_data = (stamp_data >> 13) & 7;
233
234         /* cell offset (0-3 or 0-15)                             */
235         /* table entry = yyxxshrr (8 bits)                       */
236         /* with: yy = cell row  (0-3) = (ypos >> (11 + 3)) & 3   */
237         /*       xx = cell column (0-3) = (xpos >> (11 + 3)) & 3 */
238         /*        s = stamp size (0=16x16, 1=32x32)              */
239         /*      hrr = HFLIP & ROTATION bits                      */
240         stamp_index |= gfx.lut_cell[
241           stamp_data | ((Pico_mcd->s68k_regs[0x58+1] & 0x02) << 2 )
242           | ((ypos >> 8) & 0xc0) | ((xpos >> 10) & 0x30)] << 6;
243             
244         /* pixel  offset (0-63)                              */
245         /* table entry = yyyxxxhrr (9 bits)                  */
246         /* with: yyy = pixel row  (0-7) = (ypos >> 11) & 7   */
247         /*       xxx = pixel column (0-7) = (xpos >> 11) & 7 */
248         /*       hrr = HFLIP & ROTATION bits                 */
249         stamp_index |= gfx.lut_pixel[stamp_data | ((xpos >> 8) & 0x38) | ((ypos >> 5) & 0x1c0)];
250
251         /* read pixel pair (2 pixels/byte) */
252         pixel_out = READ_BYTE(Pico_mcd->word_ram2M, stamp_index >> 1);
253
254         /* extract left or rigth pixel */
255         if (stamp_index & 1)
256         {
257            pixel_out &= 0x0f;
258         }
259         else
260         {
261            pixel_out >>= 4;
262         }
263       }
264       else
265       {
266         /* stamp 0 is not used: force pixel output to 0 */
267         pixel_out = 0x00;
268       }
269     }
270
271     /* read out paired pixel data */
272     pixel_in = READ_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1);
273
274     /* update left or rigth pixel */
275     if (bufferIndex & 1)
276     {
277       pixel_out |= (pixel_in & 0xf0);
278     }
279     else
280     {
281       pixel_out = (pixel_out << 4) | (pixel_in & 0x0f);
282     }
283
284     /* priority mode write */
285     reg = (Pico_mcd->s68k_regs[2] << 8) | Pico_mcd->s68k_regs[3];
286     pixel_out = gfx.lut_prio[(reg >> 3) & 0x03][pixel_in][pixel_out];
287
288     /* write data to image buffer */
289     WRITE_BYTE(Pico_mcd->word_ram2M, bufferIndex >> 1, pixel_out);
290
291     /* check current pixel position  */
292     if ((bufferIndex & 7) != 7)
293     {
294       /* next pixel */
295       bufferIndex++;
296     }
297     else
298     {
299       /* next cell: increment image buffer offset by one column (minus 7 pixels) */
300       bufferIndex += gfx.bufferOffset;
301     }
302
303     /* increment pixel position */
304     xpos += xoffset;
305     ypos += yoffset;
306   }
307 }
308
309 void gfx_start(unsigned int base)
310 {
311   /* make sure 2M mode is enabled */
312   if (!(Pico_mcd->s68k_regs[3] & 0x04))
313   {
314     uint32 mask;
315     uint32 reg;
316     
317     /* trace vector pointer */
318     gfx.tracePtr = (uint16 *)(Pico_mcd->word_ram2M + ((base << 2) & 0x3fff8));
319
320     /* stamps & stamp map size */
321     switch ((Pico_mcd->s68k_regs[0x58+1] >> 1) & 0x03)
322     {
323       case 0:
324         gfx.dotMask = 0x07ffff;   /* 256x256 dots/map  */
325         gfx.stampShift = 11 + 4;  /* 16x16 dots/stamps */
326         gfx.mapShift = 4;         /* 16x16 stamps/map  */
327         mask = 0x3fe00;           /* 512 bytes/table   */
328         break;
329
330       case 1:
331         gfx.dotMask = 0x07ffff;   /* 256x256 dots/map  */
332         gfx.stampShift = 11 + 5;  /* 32x32 dots/stamps */
333         gfx.mapShift = 3;         /* 8x8 stamps/map    */
334         mask = 0x3ff80;           /* 128 bytes/table   */
335         break;
336
337       case 2:
338         gfx.dotMask = 0x7fffff;   /* 4096*4096 dots/map */
339         gfx.stampShift = 11 + 4;  /* 16x16 dots/stamps  */
340         gfx.mapShift = 8;         /* 256x256 stamps/map */
341         mask = 0x20000;           /* 131072 bytes/table */
342         break;
343
344       case 3:
345         gfx.dotMask = 0x7fffff;   /* 4096*4096 dots/map */
346         gfx.stampShift = 11 + 5;  /* 32x32 dots/stamps  */
347         gfx.mapShift = 7;         /* 128x128 stamps/map */
348         mask = 0x38000;           /* 32768 bytes/table  */
349         break;
350     }
351
352     /* stamp map table base address */
353     reg = (Pico_mcd->s68k_regs[0x5a] << 8) | Pico_mcd->s68k_regs[0x5b];
354     gfx.mapPtr = (uint16 *)(Pico_mcd->word_ram2M + ((reg << 2) & mask));
355
356     /* image buffer column offset (64 pixels/cell, minus 7 pixels to restart at cell beginning) */
357     gfx.bufferOffset = (((Pico_mcd->s68k_regs[0x5c+1] & 0x1f) + 1) << 6) - 7;
358
359     /* image buffer start index in dot units (2 pixels/byte) */
360     reg = (Pico_mcd->s68k_regs[0x5e] << 8) | Pico_mcd->s68k_regs[0x5f];
361     gfx.bufferStart = (reg << 3) & 0x7ffc0;
362
363     /* add image buffer horizontal dot offset */
364     gfx.bufferStart += (Pico_mcd->s68k_regs[0x60+1] & 0x3f);
365
366     /* reset GFX chip cycle counter */
367     //gfx.cycles = cycles;
368
369     /* update GFX chip timings (see AC3:Thunderhawk / Thunderstrike) */
370     //gfx.cyclesPerLine = 4 * 5 * scd.regs[0x62>>1].w;
371
372     /* start graphics operation */
373     Pico_mcd->s68k_regs[0x58] = 0x80;
374
375     gfx_schedule();
376   }
377 }
378
379 /* PicoDrive specific */
380 #define UPDATE_CYCLES 20000
381
382 static void gfx_schedule(void)
383 {
384   int w, h, cycles;
385   int y_step;
386
387   w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
388   h = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
389
390   cycles = 5 * w * h;
391   if (cycles > UPDATE_CYCLES)
392     y_step = (UPDATE_CYCLES + 5 * w - 1) / (5 * w);
393   else
394     y_step = h;
395
396   gfx.y_step = y_step;
397   pcd_event_schedule_s68k(PCD_EVENT_GFX, 5 * w * y_step);
398 }
399
400 void gfx_update(unsigned int cycles)
401 {
402   int lines, lines_reg;
403   int w;
404
405   if (!(Pico_mcd->s68k_regs[0x58] & 0x80))
406     return;
407
408   w = (Pico_mcd->s68k_regs[0x62] << 8) | Pico_mcd->s68k_regs[0x63];
409   lines = (Pico_mcd->s68k_regs[0x64] << 8) | Pico_mcd->s68k_regs[0x65];
410   lines_reg = lines - gfx.y_step;
411
412   if (lines_reg <= 0) {
413     Pico_mcd->s68k_regs[0x58] = 0;
414     Pico_mcd->s68k_regs[0x64] =
415     Pico_mcd->s68k_regs[0x65] = 0;
416
417     if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN1) {
418       elprintf(EL_INTS|EL_CD, "s68k: gfx_cd irq 1");
419       SekInterruptS68k(1);
420     }
421   }
422   else {
423     Pico_mcd->s68k_regs[0x64] = lines_reg >> 8;
424     Pico_mcd->s68k_regs[0x65] = lines_reg;
425
426     if (lines > gfx.y_step)
427       lines = gfx.y_step;
428
429     pcd_event_schedule(cycles, PCD_EVENT_GFX, 5 * w * lines);
430   }
431
432   if (PicoOpt & POPT_EN_MCD_GFX)
433   {
434     /* render lines */
435     while (lines--)
436     {
437       /* process dots to image buffer */
438       gfx_render(gfx.bufferStart, w);
439
440       /* increment image buffer start index for next line (8 pixels/line) */
441       gfx.bufferStart += 8;
442     }
443   }
444 }
445
446 // vim:shiftwidth=2:ts=2:expandtab