1
/***************************************************************************
3
Atari System 1 hardware
5
****************************************************************************/
8
#include "video/atarimo.h"
9
#include "includes/atarisy1.h"
13
/*************************************
17
*************************************/
19
/* the color and remap PROMs are mapped as follows */
20
#define PROM1_BANK_4 0x80 /* active low */
21
#define PROM1_BANK_3 0x40 /* active low */
22
#define PROM1_BANK_2 0x20 /* active low */
23
#define PROM1_BANK_1 0x10 /* active low */
24
#define PROM1_OFFSET_MASK 0x0f /* postive logic */
26
#define PROM2_BANK_6_OR_7 0x80 /* active low */
27
#define PROM2_BANK_5 0x40 /* active low */
28
#define PROM2_PLANE_5_ENABLE 0x20 /* active high */
29
#define PROM2_PLANE_4_ENABLE 0x10 /* active high */
30
#define PROM2_PF_COLOR_MASK 0x0f /* negative logic */
31
#define PROM2_BANK_7 0x08 /* active low, plus PROM2_BANK_6_OR_7 low as well */
32
#define PROM2_MO_COLOR_MASK 0x07 /* negative logic */
36
/*************************************
40
*************************************/
42
static const gfx_layout objlayout_4bpp =
44
8,8, /* 8*8 sprites */
45
4096, /* 4096 of them */
46
4, /* 4 bits per pixel */
47
{ 3*8*0x10000, 2*8*0x10000, 1*8*0x10000, 0*8*0x10000 },
48
{ 0, 1, 2, 3, 4, 5, 6, 7 },
49
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
50
8*8 /* every sprite takes 8 consecutive bytes */
53
static const gfx_layout objlayout_5bpp =
55
8,8, /* 8*8 sprites */
56
4096, /* 4096 of them */
57
5, /* 5 bits per pixel */
58
{ 4*8*0x10000, 3*8*0x10000, 2*8*0x10000, 1*8*0x10000, 0*8*0x10000 },
59
{ 0, 1, 2, 3, 4, 5, 6, 7 },
60
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
61
8*8 /* every sprite takes 8 consecutive bytes */
64
static const gfx_layout objlayout_6bpp =
66
8,8, /* 8*8 sprites */
67
4096, /* 4096 of them */
68
6, /* 6 bits per pixel */
69
{ 5*8*0x10000, 4*8*0x10000, 3*8*0x10000, 2*8*0x10000, 1*8*0x10000, 0*8*0x10000 },
70
{ 0, 1, 2, 3, 4, 5, 6, 7 },
71
{ 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 },
72
8*8 /* every sprite takes 8 consecutive bytes */
77
/*************************************
81
*************************************/
83
static void update_timers(running_machine &machine, int scanline);
84
static void decode_gfx(running_machine &machine, UINT16 *pflookup, UINT16 *molookup);
85
static int get_bank(running_machine &machine, UINT8 prom1, UINT8 prom2, int bpp);
89
/*************************************
93
*************************************/
95
static TILE_GET_INFO( get_alpha_tile_info )
97
atarisy1_state *state = machine.driver_data<atarisy1_state>();
98
UINT16 data = state->m_alpha[tile_index];
99
int code = data & 0x3ff;
100
int color = (data >> 10) & 0x07;
101
int opaque = data & 0x2000;
102
SET_TILE_INFO(0, code, color, opaque ? TILE_FORCE_LAYER0 : 0);
106
static TILE_GET_INFO( get_playfield_tile_info )
108
atarisy1_state *state = machine.driver_data<atarisy1_state>();
109
UINT16 data = state->m_playfield[tile_index];
110
UINT16 lookup = state->m_playfield_lookup[((data >> 8) & 0x7f) | (state->m_playfield_tile_bank << 7)];
111
int gfxindex = (lookup >> 8) & 15;
112
int code = ((lookup & 0xff) << 8) | (data & 0xff);
113
int color = 0x20 + (((lookup >> 12) & 15) << state->m_bank_color_shift[gfxindex]);
114
SET_TILE_INFO(gfxindex, code, color, (data >> 15) & 1);
119
/*************************************
121
* Generic video system start
123
*************************************/
125
VIDEO_START( atarisy1 )
127
static const atarimo_desc modesc =
129
0, /* index to which gfx system */
130
8, /* number of motion object banks */
131
1, /* are the entries linked? */
132
1, /* are the entries split? */
133
0, /* render in reverse order? */
134
0, /* render in swapped X/Y order? */
135
0, /* does the neighbor bit affect the next object? */
136
0, /* pixels per SLIP entry (0 for no-slip) */
137
0, /* pixel offset for SLIPs */
138
0x38, /* maximum number of links to visit/scanline (0=all) */
140
0x100, /* base palette entry */
141
0x100, /* maximum number of colors */
142
0, /* transparent pen index */
144
{{ 0,0,0,0x003f }}, /* mask for the link */
145
{{ 0,0xff00,0,0 }}, /* mask for the graphics bank */
146
{{ 0,0xffff,0,0 }}, /* mask for the code index */
147
{{ 0 }}, /* mask for the upper code index */
148
{{ 0,0xff00,0,0 }}, /* mask for the color */
149
{{ 0,0,0x3fe0,0 }}, /* mask for the X position */
150
{{ 0x3fe0,0,0,0 }}, /* mask for the Y position */
151
{{ 0 }}, /* mask for the width, in tiles*/
152
{{ 0x000f,0,0,0 }}, /* mask for the height, in tiles */
153
{{ 0x8000,0,0,0 }}, /* mask for the horizontal flip */
154
{{ 0 }}, /* mask for the vertical flip */
155
{{ 0,0,0x8000,0 }}, /* mask for the priority */
156
{{ 0 }}, /* mask for the neighbor */
157
{{ 0 }}, /* mask for absolute coordinates */
159
{{ 0,0xffff,0,0 }}, /* mask for the special value */
160
0xffff, /* resulting value to indicate "special" */
161
0 /* callback routine for special entries */
164
atarisy1_state *state = machine.driver_data<atarisy1_state>();
167
UINT8 *colorlookup, *gfxlookup;
170
/* first decode the graphics */
171
decode_gfx(machine, state->m_playfield_lookup, motable);
173
/* initialize the playfield */
174
state->m_playfield_tilemap = tilemap_create(machine, get_playfield_tile_info, tilemap_scan_rows, 8,8, 64,64);
176
/* initialize the motion objects */
177
atarimo_init(machine, 0, &modesc);
179
/* initialize the alphanumerics */
180
state->m_alpha_tilemap = tilemap_create(machine, get_alpha_tile_info, tilemap_scan_rows, 8,8, 64,32);
181
tilemap_set_transparent_pen(state->m_alpha_tilemap, 0);
183
/* modify the motion object code lookup */
184
codelookup = atarimo_get_code_lookup(0, &size);
185
for (i = 0; i < size; i++)
186
codelookup[i] = (i & 0xff) | ((motable[i >> 8] & 0xff) << 8);
188
/* modify the motion object color and gfx lookups */
189
colorlookup = atarimo_get_color_lookup(0, &size);
190
gfxlookup = atarimo_get_gfx_lookup(0, &size);
191
for (i = 0; i < size; i++)
193
colorlookup[i] = ((motable[i] >> 12) & 15) << 1;
194
gfxlookup[i] = (motable[i] >> 8) & 15;
197
/* reset the statics */
198
atarimo_set_yscroll(0, 256);
199
state->m_next_timer_scanline = -1;
202
state->save_item(NAME(state->m_playfield_tile_bank));
203
state->save_item(NAME(state->m_playfield_priority_pens));
204
state->save_item(NAME(state->m_next_timer_scanline));
209
/*************************************
211
* Graphics bank selection
213
*************************************/
215
WRITE16_HANDLER( atarisy1_bankselect_w )
217
atarisy1_state *state = space->machine().driver_data<atarisy1_state>();
218
UINT16 oldselect = *state->m_bankselect;
219
UINT16 newselect = oldselect, diff;
220
int scanline = space->machine().primary_screen->vpos();
223
COMBINE_DATA(&newselect);
224
diff = oldselect ^ newselect;
226
/* sound CPU reset */
229
cputag_set_input_line(space->machine(), "audiocpu", INPUT_LINE_RESET, (newselect & 0x0080) ? CLEAR_LINE : ASSERT_LINE);
230
if (!(newselect & 0x0080)) atarigen_sound_reset(space->machine());
233
/* if MO or playfield banks change, force a partial update */
235
space->machine().primary_screen->update_partial(scanline);
237
/* motion object bank select */
238
atarimo_set_bank(0, (newselect >> 3) & 7);
239
update_timers(space->machine(), scanline);
241
/* playfield bank select */
244
state->m_playfield_tile_bank = (newselect >> 2) & 1;
245
tilemap_mark_all_tiles_dirty(state->m_playfield_tilemap);
248
/* stash the new value */
249
*state->m_bankselect = newselect;
254
/*************************************
256
* Playfield priority pens
258
*************************************/
260
WRITE16_HANDLER( atarisy1_priority_w )
262
atarisy1_state *state = space->machine().driver_data<atarisy1_state>();
263
UINT16 oldpens = state->m_playfield_priority_pens;
264
UINT16 newpens = oldpens;
266
/* force a partial update in case this changes mid-screen */
267
COMBINE_DATA(&newpens);
268
if (oldpens != newpens)
269
space->machine().primary_screen->update_partial(space->machine().primary_screen->vpos());
270
state->m_playfield_priority_pens = newpens;
275
/*************************************
277
* Playfield horizontal scroll
279
*************************************/
281
WRITE16_HANDLER( atarisy1_xscroll_w )
283
atarisy1_state *state = space->machine().driver_data<atarisy1_state>();
284
UINT16 oldscroll = *state->m_xscroll;
285
UINT16 newscroll = oldscroll;
287
/* force a partial update in case this changes mid-screen */
288
COMBINE_DATA(&newscroll);
289
if (oldscroll != newscroll)
290
space->machine().primary_screen->update_partial(space->machine().primary_screen->vpos());
292
/* set the new scroll value */
293
tilemap_set_scrollx(state->m_playfield_tilemap, 0, newscroll);
295
/* update the data */
296
*state->m_xscroll = newscroll;
301
/*************************************
303
* Playfield vertical scroll
305
*************************************/
307
TIMER_DEVICE_CALLBACK( atarisy1_reset_yscroll_callback )
309
atarisy1_state *state = timer.machine().driver_data<atarisy1_state>();
310
tilemap_set_scrolly(state->m_playfield_tilemap, 0, param);
314
WRITE16_HANDLER( atarisy1_yscroll_w )
316
atarisy1_state *state = space->machine().driver_data<atarisy1_state>();
317
UINT16 oldscroll = *state->m_yscroll;
318
UINT16 newscroll = oldscroll;
319
int scanline = space->machine().primary_screen->vpos();
322
/* force a partial update in case this changes mid-screen */
323
COMBINE_DATA(&newscroll);
324
space->machine().primary_screen->update_partial(scanline);
326
/* because this latches a new value into the scroll base,
327
we need to adjust for the scanline */
328
adjusted_scroll = newscroll;
329
if (scanline <= space->machine().primary_screen->visible_area().max_y)
330
adjusted_scroll -= (scanline + 1);
331
tilemap_set_scrolly(state->m_playfield_tilemap, 0, adjusted_scroll);
333
/* but since we've adjusted it, we must reset it to the normal value
334
once we hit scanline 0 again */
335
state->m_yscroll_reset_timer->adjust(space->machine().primary_screen->time_until_pos(0), newscroll);
337
/* update the data */
338
*state->m_yscroll = newscroll;
343
/*************************************
345
* Sprite RAM write handler
347
*************************************/
349
WRITE16_HANDLER( atarisy1_spriteram_w )
351
int active_bank = atarimo_get_bank(0);
352
int oldword = atarimo_0_spriteram_r(space, offset, mem_mask);
353
int newword = oldword;
354
COMBINE_DATA(&newword);
356
/* if the data changed, and it modified the live sprite bank, do some extra work */
357
if (oldword != newword && (offset >> 8) == active_bank)
359
/* if modifying a timer, beware */
360
if (((offset & 0xc0) == 0x00 && atarimo_0_spriteram_r(space, offset | 0x40, mem_mask) == 0xffff) ||
361
((offset & 0xc0) == 0x40 && (newword == 0xffff || oldword == 0xffff)))
363
/* if the timer is in the active bank, update the display list */
364
atarimo_0_spriteram_w(space, offset, data, 0xffff);
365
update_timers(space->machine(), space->machine().primary_screen->vpos());
368
/* if we're about to modify data in the active sprite bank, make sure the video is up-to-date */
369
/* Road Runner needs this to work; note the +2 kludge -- +1 would be correct since the video */
370
/* renders the next scanline's sprites to the line buffers, but Road Runner still glitches */
371
/* without the extra +1 */
373
space->machine().primary_screen->update_partial(space->machine().primary_screen->vpos() + 2);
376
/* let the MO handler do the basic work */
377
atarimo_0_spriteram_w(space, offset, data, 0xffff);
382
/*************************************
384
* MO interrupt handlers
386
*************************************/
388
TIMER_DEVICE_CALLBACK( atarisy1_int3off_callback )
390
address_space *space = timer.machine().device("maincpu")->memory().space(AS_PROGRAM);
392
/* clear the state */
393
atarigen_scanline_int_ack_w(space, 0, 0, 0xffff);
397
TIMER_DEVICE_CALLBACK( atarisy1_int3_callback )
399
atarisy1_state *state = timer.machine().driver_data<atarisy1_state>();
400
int scanline = param;
402
/* update the state */
403
atarigen_scanline_int_gen(timer.machine().device("maincpu"));
405
/* set a timer to turn it off */
406
state->m_int3off_timer->adjust(timer.machine().primary_screen->scan_period());
408
/* determine the time of the next one */
409
state->m_next_timer_scanline = -1;
410
update_timers(timer.machine(), scanline);
415
/*************************************
417
* MO interrupt state read
419
*************************************/
421
READ16_HANDLER( atarisy1_int3state_r )
423
atarigen_state *atarigen = space->machine().driver_data<atarigen_state>();
424
return atarigen->m_scanline_int_state ? 0x0080 : 0x0000;
429
/*************************************
433
*************************************/
435
static void update_timers(running_machine &machine, int scanline)
437
atarisy1_state *state = machine.driver_data<atarisy1_state>();
438
address_space *space = NULL;
439
UINT16 mem_mask = 0xffff;
440
int offset = atarimo_get_bank(0) * 64 * 4;
441
int link = 0, best = scanline, found = 0;
442
UINT8 spritevisit[64];
444
/* track which ones we've visited */
445
memset(spritevisit, 0, sizeof(spritevisit));
447
/* walk the list until we loop */
448
while (!spritevisit[link])
450
/* timers are indicated by 0xffff in entry 2 */
451
if (atarimo_0_spriteram_r(space, offset + link + 0x40, mem_mask) == 0xffff)
453
int data = atarimo_0_spriteram_r(space, offset + link, mem_mask);
454
int vsize = (data & 15) + 1;
455
int ypos = (256 - (data >> 5) - vsize * 8 - 1) & 0x1ff;
457
/* note that we found something */
460
/* is this a better entry than the best so far? */
461
if (best <= scanline)
463
if ((ypos <= scanline && ypos < best) || ypos > scanline)
473
/* link to the next */
474
spritevisit[link] = 1;
475
link = atarimo_0_spriteram_r(space, offset + link + 0xc0, mem_mask) & 0x3f;
478
/* if nothing was found, use scanline -1 */
482
/* update the timer */
483
if (best != state->m_next_timer_scanline)
485
state->m_next_timer_scanline = best;
489
state->m_scanline_timer->adjust(machine.primary_screen->time_until_pos(best), best);
491
state->m_scanline_timer->reset();
497
/*************************************
501
*************************************/
503
SCREEN_UPDATE( atarisy1 )
505
atarisy1_state *state = screen->machine().driver_data<atarisy1_state>();
506
atarimo_rect_list rectlist;
510
/* draw the playfield */
511
tilemap_draw(bitmap, cliprect, state->m_playfield_tilemap, 0, 0);
513
/* draw and merge the MO */
514
mobitmap = atarimo_render(0, cliprect, &rectlist);
515
for (r = 0; r < rectlist.numrects; r++, rectlist.rect++)
516
for (y = rectlist.rect->min_y; y <= rectlist.rect->max_y; y++)
518
UINT16 *mo = (UINT16 *)mobitmap->base + mobitmap->rowpixels * y;
519
UINT16 *pf = (UINT16 *)bitmap->base + bitmap->rowpixels * y;
520
for (x = rectlist.rect->min_x; x <= rectlist.rect->max_x; x++)
523
/* high priority MO? */
524
if (mo[x] & ATARIMO_PRIORITY_MASK)
526
/* only gets priority if MO pen is not 1 */
527
if ((mo[x] & 0x0f) != 1)
528
pf[x] = 0x300 + ((pf[x] & 0x0f) << 4) + (mo[x] & 0x0f);
534
/* priority pens for playfield color 0 */
535
if ((pf[x] & 0xf8) != 0 || !(state->m_playfield_priority_pens & (1 << (pf[x] & 0x07))))
539
/* erase behind ourselves */
544
/* add the alpha on top */
545
tilemap_draw(bitmap, cliprect, state->m_alpha_tilemap, 0, 0);
551
/*************************************
555
*************************************/
557
static void decode_gfx(running_machine &machine, UINT16 *pflookup, UINT16 *molookup)
559
atarisy1_state *state = machine.driver_data<atarisy1_state>();
560
UINT8 *prom1 = &machine.region("proms")->u8(0x000);
561
UINT8 *prom2 = &machine.region("proms")->u8(0x200);
564
/* reset the globals */
565
memset(&state->m_bank_gfx[0][0], 0, sizeof(state->m_bank_gfx));
567
/* loop for two sets of objects */
568
for (obj = 0; obj < 2; obj++)
570
/* loop for 256 objects in the set */
571
for (i = 0; i < 256; i++, prom1++, prom2++)
573
int bank, bpp, color, offset;
575
/* determine the bpp */
577
if (*prom2 & PROM2_PLANE_4_ENABLE)
580
if (*prom2 & PROM2_PLANE_5_ENABLE)
584
/* determine the offset */
585
offset = *prom1 & PROM1_OFFSET_MASK;
587
/* determine the bank */
588
bank = get_bank(machine, *prom1, *prom2, bpp);
594
color = (~*prom2 & PROM2_PF_COLOR_MASK) >> (bpp - 4);
600
pflookup[i] = offset | (bank << 8) | (color << 12);
604
/* motion objects (high bit ignored) */
605
color = (~*prom2 & PROM2_MO_COLOR_MASK) >> (bpp - 4);
606
molookup[i] = offset | (bank << 8) | (color << 12);
614
/*************************************
616
* Graphics bank mapping
618
*************************************/
620
static int get_bank(running_machine &machine, UINT8 prom1, UINT8 prom2, int bpp)
622
atarisy1_state *state = machine.driver_data<atarisy1_state>();
623
const UINT8 *srcdata;
624
int bank_index, gfx_index;
626
/* determine the bank index */
627
if ((prom1 & PROM1_BANK_1) == 0)
629
else if ((prom1 & PROM1_BANK_2) == 0)
631
else if ((prom1 & PROM1_BANK_3) == 0)
633
else if ((prom1 & PROM1_BANK_4) == 0)
635
else if ((prom2 & PROM2_BANK_5) == 0)
637
else if ((prom2 & PROM2_BANK_6_OR_7) == 0)
639
if ((prom2 & PROM2_BANK_7) == 0)
648
if (state->m_bank_gfx[bpp - 4][bank_index])
649
return state->m_bank_gfx[bpp - 4][bank_index];
651
/* if the bank is out of range, call it 0 */
652
const memory_region *tiles = machine.region("tiles");
653
if (0x80000 * (bank_index - 1) >= tiles->bytes())
656
/* don't have one? let's make it ... first find any empty slot */
657
for (gfx_index = 0; gfx_index < MAX_GFX_ELEMENTS; gfx_index++)
658
if (machine.gfx[gfx_index] == NULL)
660
assert(gfx_index != MAX_GFX_ELEMENTS);
662
/* decode the graphics */
663
srcdata = &tiles->u8(0x80000 * (bank_index - 1));
667
machine.gfx[gfx_index] = gfx_element_alloc(machine, &objlayout_4bpp, srcdata, 0x40, 256);
671
machine.gfx[gfx_index] = gfx_element_alloc(machine, &objlayout_5bpp, srcdata, 0x40, 256);
675
machine.gfx[gfx_index] = gfx_element_alloc(machine, &objlayout_6bpp, srcdata, 0x40, 256);
679
fatalerror("Unsupported bpp");
682
/* set the color information */
683
machine.gfx[gfx_index]->color_granularity = 8;
684
state->m_bank_color_shift[gfx_index] = bpp - 3;
686
/* set the entry and return it */
687
return state->m_bank_gfx[bpp - 4][bank_index] = gfx_index;