1 // license:???
2 // copyright-holders:Alex Pasadyn, Zsolt Vasvari, Kurt Mahan, Ernesto Corvi, Aaron Giles
3 /*************************************************************************
4 
5     Driver for Midway T-unit games.
6 
7 **************************************************************************/
8 // Adapted to FBAlpha
9 #define XPOSMASK        0x3ff
10 #define YPOSMASK        0x1ff
11 
12 enum
13 {
14     DMA_LRSKIP = 0,
15     DMA_COMMAND,
16     DMA_OFFSETLO,
17     DMA_OFFSETHI,
18     DMA_XSTART,
19     DMA_YSTART,
20     DMA_WIDTH,
21     DMA_HEIGHT,
22     DMA_PALETTE,
23     DMA_COLOR,
24     DMA_SCALE_X,
25     DMA_SCALE_Y,
26     DMA_TOPCLIP,
27     DMA_BOTCLIP,
28     DMA_UNKNOWN_E,  /* MK1/2 never write here; NBA only writes 0 */
29     DMA_CONFIG,
30     DMA_LEFTCLIP,   /* pseudo-register */
31     DMA_RIGHTCLIP   /* pseudo-register */
32 };
33 
34 
35 struct dma_state_s
36 {
37     UINT32      offset;         /* source offset, in bits */
38     INT32       rowbits;        /* source bits to skip each row */
39     INT32       xpos;           /* x position, clipped */
40     INT32       ypos;           /* y position, clipped */
41     INT32       width;          /* horizontal pixel count */
42     INT32       height;         /* vertical pixel count */
43     UINT16      palette;        /* palette base */
44     UINT16      color;          /* current foreground color with palette */
45 
46     UINT8       yflip;          /* yflip? */
47     UINT8       bpp;            /* bits per pixel */
48     UINT8       preskip;        /* preskip scale */
49     UINT8       postskip;       /* postskip scale */
50     INT32       topclip;        /* top clipping scanline */
51     INT32       botclip;        /* bottom clipping scanline */
52     INT32       leftclip;       /* left clipping column */
53     INT32       rightclip;      /* right clipping column */
54     INT32       startskip;      /* pixels to skip at start */
55     INT32       endskip;        /* pixels to skip at end */
56     UINT16      xstep;          /* 8.8 fixed number scale x factor */
57 	UINT16      ystep;          /* 8.8 fixed number scale y factor */
58 };
59 
60 static dma_state_s *dma_state;
61 
62 static UINT8 *     dma_gfxrom;
63 static INT32 midtunit_cpurate = 0;
64 
65 /*** constant definitions ***/
66 #define PIXEL_SKIP      0
67 #define PIXEL_COLOR     1
68 #define PIXEL_COPY      2
69 
70 #define XFLIP_NO        0
71 #define XFLIP_YES       1
72 
73 #define SKIP_NO         0
74 #define SKIP_YES        1
75 
76 #define SCALE_NO        0
77 #define SCALE_YES       1
78 
79 #define XPOSMASK        0x3ff
80 #define YPOSMASK        0x1ff
81 
82 
83 typedef void (*dma_draw_func)(void);
84 
85 
86 /*** fast pixel extractors ***/
87 #ifndef __LIBRETRO__
88 #define EXTRACTGEN(m)   ((*(UINT16 *)&base[o >> 3] >> (o & 7)) & (m))
89 #else
90 #define EXTRACTGEN(m)   (((base[o >> 3] | (base[(o >> 3) + 1] << 8)) >> (o & 7)) & (m))
91 #endif
92 
93 /*** core blitter routine macro ***/
94 #define DMA_DRAW_FUNC_BODY(name, bitsperpixel, extractor, xflip, skip, scale, zero, nonzero)    \
95 {                                                             \
96     int height = dma_state->height << 8;                       \
97     UINT8 *base = dma_gfxrom;                                 \
98     UINT32 offset = dma_state->offset;                         \
99     UINT16 pal = dma_state->palette;                           \
100     UINT16 color = pal | dma_state->color;                     \
101     int sy = dma_state->ypos, iy = 0, ty;                      \
102     int bpp = bitsperpixel;                                   \
103     int mask = (1 << bpp) - 1;                                \
104     int xstep = scale ? dma_state->xstep : 0x100;              \
105                                                               \
106     /* loop over the height */                                \
107     while (iy < height)                                       \
108     {                                                         \
109         int startskip = dma_state->startskip << 8;             \
110         int endskip = dma_state->endskip << 8;                 \
111         int width = dma_state->width << 8;                     \
112         int sx = dma_state->xpos, ix = 0, tx;                  \
113         UINT32 o = offset;                                    \
114         int pre, post;                                        \
115         UINT16 *d;                                            \
116                                                               \
117         /* handle skipping */                                 \
118         if (skip)                                             \
119         {                                                     \
120             UINT8 value = EXTRACTGEN(0xff);                   \
121             o += 8;                                           \
122                                                               \
123             /* adjust for preskip */                          \
124             pre = (value & 0x0f) << (dma_state->preskip + 8);  \
125             tx = pre / xstep;                                 \
126             if (xflip)                                        \
127                 sx = (sx - tx) & XPOSMASK;                    \
128             else                                              \
129                 sx = (sx + tx) & XPOSMASK;                    \
130             ix += tx * xstep;                                 \
131                                                               \
132             /* adjust for postskip */                         \
133             post = ((value >> 4) & 0x0f) << (dma_state->postskip + 8); \
134             width -= post;                                    \
135             endskip -= post;                                  \
136         }                                                     \
137                                                               \
138         /* handle Y clipping */                               \
139         if (sy < dma_state->topclip || sy > dma_state->botclip) \
140             goto clipy;                                       \
141                                                               \
142         /* handle start skip */                               \
143         if (ix < startskip)                                   \
144         {                                                     \
145             tx = ((startskip - ix) / xstep) * xstep;          \
146             ix += tx;                                         \
147             o += (tx >> 8) * bpp;                             \
148         }                                                     \
149                                                               \
150         /* handle end skip */                                 \
151         if ((width >> 8) > dma_state->width - dma_state->endskip) \
152             width = (dma_state->width - dma_state->endskip) << 8; \
153                                                               \
154         /* determine destination pointer */                   \
155         d = &DrvVRAM16[sy * 512];                             \
156                                                               \
157         /* loop until we draw the entire width */             \
158         while (ix < width)                                    \
159         {                                                     \
160             /* only process if not clipped */                 \
161             if (sx >= dma_state->leftclip && sx <= dma_state->rightclip) \
162             {                                                 \
163                 /* special case similar handling of zero/non-zero */ \
164                 if (zero == nonzero)                          \
165                 {                                             \
166                     if (zero == PIXEL_COLOR)                  \
167                         d[sx] = BURN_ENDIAN_SWAP_INT16(color);                        \
168                     else if (zero == PIXEL_COPY)              \
169                         d[sx] = BURN_ENDIAN_SWAP_INT16((extractor(mask)) | pal);      \
170                 }                                             \
171                                                               \
172                 /* otherwise, read the pixel and look */      \
173                 else                                          \
174                 {                                             \
175                     int pixel = (extractor(mask));            \
176                                                               \
177                     /* non-zero pixel case */                 \
178                     if (pixel)                                \
179                     {                                         \
180                         if (nonzero == PIXEL_COLOR)           \
181                             d[sx] = BURN_ENDIAN_SWAP_INT16(color);                    \
182                         else if (nonzero == PIXEL_COPY)       \
183                             d[sx] = BURN_ENDIAN_SWAP_INT16(pixel | pal);              \
184                     }                                         \
185                                                               \
186                     /* zero pixel case */                     \
187                     else                                      \
188                     {                                         \
189                         if (zero == PIXEL_COLOR)              \
190                             d[sx] = BURN_ENDIAN_SWAP_INT16(color);                    \
191                         else if (zero == PIXEL_COPY)          \
192                             d[sx] = BURN_ENDIAN_SWAP_INT16(pal);                      \
193                     }                                         \
194                 }                                             \
195             }                                                 \
196                                                               \
197             /* update pointers */                             \
198             if (xflip)                                        \
199                 sx = (sx - 1) & XPOSMASK;                     \
200             else                                              \
201                 sx = (sx + 1) & XPOSMASK;                     \
202                                                               \
203             /* advance to the next pixel */                   \
204             if (!scale)                                       \
205             {                                                 \
206                 ix += 0x100;                                  \
207                 o += bpp;                                     \
208             }                                                 \
209             else                                              \
210             {                                                 \
211                 tx = ix >> 8;                                 \
212                 ix += xstep;                                  \
213                 tx = (ix >> 8) - tx;                          \
214                 o += bpp * tx;                                \
215             }                                                 \
216         }                                                     \
217                                                               \
218 clipy:                                                        \
219         /* advance to the next row */                         \
220         if (dma_state->yflip)                                  \
221             sy = (sy - 1) & YPOSMASK;                         \
222         else                                                  \
223             sy = (sy + 1) & YPOSMASK;                         \
224         if (!scale)                                           \
225         {                                                     \
226             iy += 0x100;                                      \
227             width = dma_state->width;                          \
228             if (skip)                                         \
229             {                                                 \
230                 offset += 8;                                  \
231                 width -= (pre + post) >> 8;                   \
232                 if (width > 0) offset += width * bpp;         \
233             }                                                 \
234             else                                              \
235                 offset += width * bpp;                        \
236         }                                                     \
237         else                                                  \
238         {                                                     \
239             ty = iy >> 8;                                     \
240             iy += dma_state->ystep;                            \
241             ty = (iy >> 8) - ty;                              \
242             if (!skip)                                        \
243                 offset += ty * dma_state->width * bpp;         \
244             else if (ty--)                                    \
245             {                                                 \
246                 o = offset + 8;                               \
247                 width = dma_state->width - ((pre + post) >> 8);\
248                 if (width > 0) o += width * bpp;              \
249                 while (ty--)                                  \
250                 {                                             \
251                     UINT8 value = EXTRACTGEN(0xff);           \
252                     o += 8;                                   \
253                     pre = (value & 0x0f) << dma_state->preskip;\
254                     post = ((value >> 4) & 0x0f) << dma_state->postskip; \
255                     width = dma_state->width - pre - post;     \
256                     if (width > 0) o += width * bpp;          \
257                 }                                             \
258                 offset = o;                                   \
259             }                                                 \
260         }                                                     \
261     }                                                         \
262 }
263 
264 
265 /*** slightly simplified one for most blitters ***/
266 #define DMA_DRAW_FUNC(name, bpp, extract, xflip, skip, scale, zero, nonzero)    \
267     static void name(void)                                                      \
268 {                                                                               \
269     DMA_DRAW_FUNC_BODY(name, bpp, extract, xflip, skip, scale, zero, nonzero)   \
270 }
271 
272 /*** empty blitter ***/
dma_draw_none(void)273 static void dma_draw_none(void)
274 {
275 }
276 
277 /*** super macro for declaring an entire blitter family ***/
278 #define DECLARE_BLITTER_SET(prefix, bpp, extract, skip, scale)                                         \
279     DMA_DRAW_FUNC(prefix##_p0,      bpp, extract, XFLIP_NO,  skip, scale, PIXEL_COPY,  PIXEL_SKIP)      \
280     DMA_DRAW_FUNC(prefix##_p1,      bpp, extract, XFLIP_NO,  skip, scale, PIXEL_SKIP,  PIXEL_COPY)      \
281     DMA_DRAW_FUNC(prefix##_c0,      bpp, extract, XFLIP_NO,  skip, scale, PIXEL_COLOR, PIXEL_SKIP)      \
282     DMA_DRAW_FUNC(prefix##_c1,      bpp, extract, XFLIP_NO,  skip, scale, PIXEL_SKIP,  PIXEL_COLOR)     \
283     DMA_DRAW_FUNC(prefix##_p0p1,    bpp, extract, XFLIP_NO,  skip, scale, PIXEL_COPY,  PIXEL_COPY)      \
284     DMA_DRAW_FUNC(prefix##_c0c1,    bpp, extract, XFLIP_NO,  skip, scale, PIXEL_COLOR, PIXEL_COLOR)     \
285     DMA_DRAW_FUNC(prefix##_c0p1,    bpp, extract, XFLIP_NO,  skip, scale, PIXEL_COLOR, PIXEL_COPY)      \
286     DMA_DRAW_FUNC(prefix##_p0c1,    bpp, extract, XFLIP_NO,  skip, scale, PIXEL_COPY,  PIXEL_COLOR)     \
287     \
288     DMA_DRAW_FUNC(prefix##_p0_xf,   bpp, extract, XFLIP_YES, skip, scale, PIXEL_COPY,  PIXEL_SKIP)      \
289     DMA_DRAW_FUNC(prefix##_p1_xf,   bpp, extract, XFLIP_YES, skip, scale, PIXEL_SKIP,  PIXEL_COPY)      \
290     DMA_DRAW_FUNC(prefix##_c0_xf,   bpp, extract, XFLIP_YES, skip, scale, PIXEL_COLOR, PIXEL_SKIP)      \
291     DMA_DRAW_FUNC(prefix##_c1_xf,   bpp, extract, XFLIP_YES, skip, scale, PIXEL_SKIP,  PIXEL_COLOR)     \
292     DMA_DRAW_FUNC(prefix##_p0p1_xf, bpp, extract, XFLIP_YES, skip, scale, PIXEL_COPY,  PIXEL_COPY)      \
293     DMA_DRAW_FUNC(prefix##_c0c1_xf, bpp, extract, XFLIP_YES, skip, scale, PIXEL_COLOR, PIXEL_COLOR)     \
294     DMA_DRAW_FUNC(prefix##_c0p1_xf, bpp, extract, XFLIP_YES, skip, scale, PIXEL_COLOR, PIXEL_COPY)      \
295     DMA_DRAW_FUNC(prefix##_p0c1_xf, bpp, extract, XFLIP_YES, skip, scale, PIXEL_COPY,  PIXEL_COLOR)     \
296     \
297     static const dma_draw_func prefix[32] =                                                                 \
298     {                                                                                                       \
299     /*  B0:N / B1:N         B0:Y / B1:N         B0:N / B1:Y         B0:Y / B1:Y */                          \
300     dma_draw_none,      prefix##_p0,        prefix##_p1,        prefix##_p0p1,      /* no color */          \
301     prefix##_c0,        prefix##_c0,        prefix##_c0p1,      prefix##_c0p1,      /* color 0 pixels */    \
302     prefix##_c1,        prefix##_p0c1,      prefix##_c1,        prefix##_p0c1,      /* color non-0 pixels */\
303     prefix##_c0c1,      prefix##_c0c1,      prefix##_c0c1,      prefix##_c0c1,      /* fill */              \
304     \
305     dma_draw_none,      prefix##_p0_xf,     prefix##_p1_xf,     prefix##_p0p1_xf,   /* no color */          \
306     prefix##_c0_xf,     prefix##_c0_xf,     prefix##_c0p1_xf,   prefix##_c0p1_xf,   /* color 0 pixels */    \
307     prefix##_c1_xf,     prefix##_p0c1_xf,   prefix##_c1_xf,     prefix##_p0c1_xf,   /* color non-0 pixels */\
308     prefix##_c0c1_xf,   prefix##_c0c1_xf,   prefix##_c0c1_xf,   prefix##_c0c1_xf    /* fill */              \
309     };
310 
311 
312 /*** blitter family declarations ***/
313 DECLARE_BLITTER_SET(dma_draw_skip_scale,       dma_state->bpp, EXTRACTGEN,   SKIP_YES, SCALE_YES)
314 DECLARE_BLITTER_SET(dma_draw_noskip_scale,     dma_state->bpp, EXTRACTGEN,   SKIP_NO,  SCALE_YES)
315 DECLARE_BLITTER_SET(dma_draw_skip_noscale,     dma_state->bpp, EXTRACTGEN,   SKIP_YES, SCALE_NO)
316 DECLARE_BLITTER_SET(dma_draw_noskip_noscale,   dma_state->bpp, EXTRACTGEN,   SKIP_NO,  SCALE_NO)
317 
318 #define DMA_IRQ     TMS34010_INT_EX1
319 
TUnitDmaCallback()320 static void TUnitDmaCallback()
321 {
322 	TMS34010GenerateIRQ(DMA_IRQ);
323 	nDMA[DMA_COMMAND] &= ~0x8000;
324 }
325 
TUnitDmaRead(UINT32 address)326 static UINT16 TUnitDmaRead(UINT32 address)
327 {
328 	UINT32 offset = (address >> 4) & 0xF;
329 	if (offset == 0)
330 		offset = 1;
331 	return nDMA[offset];
332 }
333 
TUnitDmaWrite(UINT32 address,UINT16 value)334 static void TUnitDmaWrite(UINT32 address, UINT16 value)
335 {
336     dma_gfxrom = DrvGfxROM;
337     static const UINT8 register_map[2][16] =
338     {
339         { 0,1,2,3,4,5,6,7,8,9,10,11,16,17,14,15 },
340         { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }
341     };
342     int regbank = (nDMA[DMA_CONFIG] >> 5) & 1;
343     int reg = register_map[regbank][(address >> 4) & 0xF];
344     int command, bpp, regnum;
345     UINT32 gfxoffset;
346     int pixels = 0;
347 
348 	if (midtunit_cpurate == 0) {
349 		bprintf(0, _T("set midtunit_cpurate!!\n"));
350 	}
351 
352     nDMA[reg] = value;
353 
354     if (reg != DMA_COMMAND)
355         return;
356     command = nDMA[DMA_COMMAND];
357     regnum = reg;
358 
359     TMS34010ClearIRQ(DMA_IRQ);
360     if (!(value & 0x8000)) {
361         return;
362     }
363     bpp = (command >> 12) & 7;
364 
365     /* fill in the basic data */
366     dma_state->xpos = nDMA[DMA_XSTART] & XPOSMASK;
367     dma_state->ypos = nDMA[DMA_YSTART] & YPOSMASK;
368     dma_state->width = nDMA[DMA_WIDTH] & 0x3ff;
369     dma_state->height = nDMA[DMA_HEIGHT] & 0x3ff;
370     dma_state->palette = nDMA[DMA_PALETTE] & 0x7f00;
371     dma_state->color = nDMA[DMA_COLOR] & 0xff;
372 
373     /* fill in the rev 2 data */
374     dma_state->yflip = (command & 0x20) >> 5;
375     dma_state->bpp = bpp ? bpp : 8;
376     dma_state->preskip = (command >> 8) & 3;
377     dma_state->postskip = (command >> 10) & 3;
378     dma_state->xstep = nDMA[DMA_SCALE_X] ? nDMA[DMA_SCALE_X] : 0x100;
379     dma_state->ystep = nDMA[DMA_SCALE_Y] ? nDMA[DMA_SCALE_Y] : 0x100;
380 
381     /* clip the clippers */
382     dma_state->topclip = nDMA[DMA_TOPCLIP] & 0x1ff;
383     dma_state->botclip = nDMA[DMA_BOTCLIP] & 0x1ff;
384     dma_state->leftclip = nDMA[DMA_LEFTCLIP] & 0x3ff;
385     dma_state->rightclip = nDMA[DMA_RIGHTCLIP] & 0x3ff;
386 
387     /* determine the offset */
388     gfxoffset = nDMA[DMA_OFFSETLO] | (nDMA[DMA_OFFSETHI] << 16);
389 
390     /* special case: drawing mode C doesn't need to know about any pixel data */
391     if ((command & 0x0f) == 0x0c)
392         gfxoffset = 0;
393 
394     /* determine the location */
395     if (!bGfxRomLarge && gfxoffset >= 0x2000000)
396         gfxoffset -= 0x2000000;
397     if (gfxoffset >= 0xf8000000)
398         gfxoffset -= 0xf8000000;
399     if (gfxoffset < 0x10000000)
400         dma_state->offset = gfxoffset;
401     else
402     {
403         goto skipdma;
404     }
405 
406     /* there seems to be two types of behavior for the DMA chip */
407     /* for MK1 and MK2, the upper byte of the LRSKIP is the     */
408     /* starting skip value, and the lower byte is the ending    */
409     /* skip value; for the NBA Jam, Hangtime, and Open Ice, the */
410     /* full word seems to be the starting skip value.           */
411     if (command & 0x40)
412     {
413         dma_state->startskip = nDMA[DMA_LRSKIP] & 0xff;
414         dma_state->endskip = nDMA[DMA_LRSKIP] >> 8;
415     }
416     else
417     {
418         dma_state->startskip = 0;
419         dma_state->endskip = nDMA[DMA_LRSKIP];
420     }
421 
422     /* then draw */
423     if (dma_state->xstep == 0x100 && dma_state->ystep == 0x100)
424     {
425         if (command & 0x80)
426             (*dma_draw_skip_noscale[command & 0x1f])();
427         else
428             (*dma_draw_noskip_noscale[command & 0x1f])();
429 
430         pixels = dma_state->width * dma_state->height;
431     }
432     else
433     {
434         if (command & 0x80)
435             (*dma_draw_skip_scale[command & 0x1f])();
436         else
437             (*dma_draw_noskip_scale[command & 0x1f])();
438 
439         if (dma_state->xstep && dma_state->ystep)
440             pixels = ((dma_state->width << 8) / dma_state->xstep) * ((dma_state->height << 8) / dma_state->ystep);
441         else
442             pixels = 0;
443     }
444 skipdma:
445 	TMS34010TimerSet(((double)((double)midtunit_cpurate/1000000000) * (41*pixels)));
446 }
447