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