1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     Atari Blasteroids hardware
6 
7 ****************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/blstroid.h"
11 #include "cpu/m68000/m68000.h"
12 
13 
14 
15 /*************************************
16  *
17  *  Tilemap callbacks
18  *
19  *************************************/
20 
TILE_GET_INFO_MEMBER(blstroid_state::get_playfield_tile_info)21 TILE_GET_INFO_MEMBER(blstroid_state::get_playfield_tile_info)
22 {
23 	uint16_t data = m_playfield_tilemap->basemem_read(tile_index);
24 	int code = data & 0x1fff;
25 	int color = (data >> 13) & 0x07;
26 	tileinfo.set(0, code, color, 0);
27 }
28 
29 
30 
31 /*************************************
32  *
33  *  Video system start
34  *
35  *************************************/
36 
37 const atari_motion_objects_config blstroid_state::s_mob_config =
38 {
39 	1,                  /* index to which gfx system */
40 	1,                  /* number of motion object banks */
41 	1,                  /* are the entries linked? */
42 	0,                  /* are the entries split? */
43 	0,                  /* render in reverse order? */
44 	0,                  /* render in swapped X/Y order? */
45 	0,                  /* does the neighbor bit affect the next object? */
46 	0,                  /* pixels per SLIP entry (0 for no-slip) */
47 	0,                  /* pixel offset for SLIPs */
48 	0,                  /* maximum number of links to visit/scanline (0=all) */
49 
50 	0x000,              /* base palette entry */
51 	0x100,              /* maximum number of colors */
52 	0,                  /* transparent pen index */
53 
54 	{{ 0,0,0x0ff8,0 }}, /* mask for the link */
55 	{{ 0,0x3fff,0,0 }}, /* mask for the code index */
56 	{{ 0,0,0,0x000f }}, /* mask for the color */
57 	{{ 0,0,0,0xffc0 }}, /* mask for the X position */
58 	{{ 0xff80,0,0,0 }}, /* mask for the Y position */
59 	{{ 0 }},            /* mask for the width, in tiles*/
60 	{{ 0x000f,0,0,0 }}, /* mask for the height, in tiles */
61 	{{ 0,0x8000,0,0 }}, /* mask for the horizontal flip */
62 	{{ 0,0x4000,0,0 }}, /* mask for the vertical flip */
63 	{{ 0 }},            /* mask for the priority */
64 	{{ 0 }},            /* mask for the neighbor */
65 	{{ 0 }},            /* mask for absolute coordinates */
66 
67 	{{ 0 }},            /* mask for the special value */
68 	0                  /* resulting value to indicate "special" */
69 };
70 
VIDEO_START_MEMBER(blstroid_state,blstroid)71 VIDEO_START_MEMBER(blstroid_state,blstroid)
72 {
73 	m_irq_off_timer = timer_alloc(TIMER_IRQ_OFF);
74 	m_irq_on_timer = timer_alloc(TIMER_IRQ_ON);
75 
76 	m_scanline_int_state = false;
77 
78 	save_item(NAME(m_scanline_int_state));
79 }
80 
81 
82 
83 /*************************************
84  *
85  *  Periodic scanline updater
86  *
87  *************************************/
88 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)89 void blstroid_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
90 {
91 	switch (id)
92 	{
93 		case TIMER_IRQ_OFF:
94 			/* clear the interrupt */
95 			m_maincpu->set_input_line(M68K_IRQ_1, CLEAR_LINE);
96 			break;
97 		case TIMER_IRQ_ON:
98 			/* generate the interrupt */
99 			m_maincpu->set_input_line(M68K_IRQ_1, ASSERT_LINE);
100 			break;
101 		default:
102 			atarigen_state::device_timer(timer, id, param, ptr);
103 			break;
104 	}
105 }
106 
107 
TIMER_DEVICE_CALLBACK_MEMBER(blstroid_state::scanline_update)108 TIMER_DEVICE_CALLBACK_MEMBER(blstroid_state::scanline_update)
109 {
110 	int scanline = param;
111 	int offset = (scanline / 8) * 64 + 40;
112 
113 	/* check for interrupts */
114 	if (offset < 0x1000)
115 		if (m_playfield_tilemap->basemem_read(offset) & 0x8000)
116 		{
117 			/* FIXME: - the only thing this IRQ does it tweak the starting MO link */
118 			/* unfortunately, it does it too early for the given MOs! */
119 			/* perhaps it is not actually hooked up on the real PCB... */
120 			return;
121 
122 			/* set a timer to turn the interrupt on at HBLANK of the 7th scanline */
123 			/* and another to turn it off one scanline later */
124 			int width = m_screen->width();
125 			int vpos  = m_screen->vpos();
126 			attotime period_on  = m_screen->time_until_pos(vpos + 7, width * 0.9);
127 			attotime period_off = m_screen->time_until_pos(vpos + 8, width * 0.9);
128 
129 			m_irq_on_timer->adjust(period_on);
130 			m_irq_off_timer->adjust(period_off);
131 		}
132 }
133 
134 
135 
136 /*************************************
137  *
138  *  Main refresh
139  *
140  *************************************/
141 
screen_update_blstroid(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)142 uint32_t blstroid_state::screen_update_blstroid(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
143 {
144 	// start drawing
145 	m_mob->draw_async(cliprect);
146 
147 	/* draw the playfield */
148 	m_playfield_tilemap->draw(screen, bitmap, cliprect, 0, 0);
149 
150 	/* draw and merge the MO */
151 	bitmap_ind16 &mobitmap = m_mob->bitmap();
152 	for (const sparse_dirty_rect *rect = m_mob->first_dirty_rect(cliprect); rect != nullptr; rect = rect->next())
153 		for (int y = rect->top(); y <= rect->bottom(); y++)
154 		{
155 			uint16_t const *const mo = &mobitmap.pix(y);
156 			uint16_t *const pf = &bitmap.pix(y);
157 			for (int x = rect->left(); x <= rect->right(); x++)
158 				if (mo[x] != 0xffff)
159 				{
160 					/* verified via schematics
161 
162 					    priority address = HPPPMMMM
163 					*/
164 					int const priaddr = ((pf[x] & 8) << 4) | (pf[x] & 0x70) | ((mo[x] & 0xf0) >> 4);
165 					if (m_priorityram[priaddr] & 1)
166 						pf[x] = mo[x];
167 				}
168 		}
169 	return 0;
170 }
171