1 // license:BSD-3-Clause
2 // copyright-holders:Philip Bennett
3 /***************************************************************************
4 
5     Lock-On video hardware
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/lockon.h"
11 #include "cpu/nec/nec.h"
12 #include "video/resnet.h"
13 
14 #define CURSOR_XPOS         168
15 #define CURSOR_YPOS         239
16 #define FRAMEBUFFER_MAX_X   431
17 #define FRAMEBUFFER_MAX_Y   (uint32_t)((FRAMEBUFFER_CLOCK / (FRAMEBUFFER_MAX_X-1)).dvalue() / (PIXEL_CLOCK/(HTOTAL*VTOTAL)).dvalue())
18 
19 
20 /*************************************
21  *
22  *  HD46505S-2 CRT Controller
23  *
24  *************************************/
25 
lockon_crtc_r()26 uint16_t lockon_state::lockon_crtc_r()
27 {
28 	return 0xffff;
29 }
30 
lockon_crtc_w(offs_t offset,uint16_t data)31 void lockon_state::lockon_crtc_w(offs_t offset, uint16_t data)
32 {
33 #if 0
34 	data &= 0xff;
35 
36 	if (offset == 0)
37 	{
38 		switch (data)
39 		{
40 			case 0x00: osd_printf_debug("Horizontal Total         "); break;
41 			case 0x01: osd_printf_debug("Horizontal displayed     "); break;
42 			case 0x02: osd_printf_debug("Horizontal sync position "); break;
43 			case 0x03: osd_printf_debug("Horizontal sync width    "); break;
44 			case 0x04: osd_printf_debug("Vertical total           "); break;
45 			case 0x05: osd_printf_debug("Vertical total adjust    "); break;
46 			case 0x06: osd_printf_debug("Vertical displayed       "); break;
47 			case 0x07: osd_printf_debug("Vertical sync position   "); break;
48 			case 0x08: osd_printf_debug("Interlace mode           "); break;
49 			case 0x09: osd_printf_debug("Max. scan line address   "); break;
50 			case 0x0a: osd_printf_debug("Cursror start            "); break;
51 			case 0x0b: osd_printf_debug("Cursor end               "); break;
52 			case 0x0c: osd_printf_debug("Start address (h)        "); break;
53 			case 0x0d: osd_printf_debug("Start address (l)        "); break;
54 			case 0x0e: osd_printf_debug("Cursor (h)               "); break;
55 			case 0x0f: osd_printf_debug("Cursor (l)               "); break;
56 			case 0x10: osd_printf_debug("Light pen (h))           "); break;
57 			case 0x11: osd_printf_debug("Light pen (l)            "); break;
58 		}
59 	}
60 	else if (offset == 1)
61 	{
62 		osd_printf_debug("0x%.2x, (%d)\n",data, data);
63 	}
64 #endif
65 }
66 
TIMER_CALLBACK_MEMBER(lockon_state::cursor_callback)67 TIMER_CALLBACK_MEMBER(lockon_state::cursor_callback)
68 {
69 	if (m_main_inten)
70 		m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 0xff); // V30
71 
72 	m_cursor_timer->adjust(m_screen->time_until_pos(CURSOR_YPOS, CURSOR_XPOS));
73 }
74 
75 /*************************************
76  *
77  *  Palette decoding
78  *
79  *************************************/
80 
81 static const res_net_info lockon_net_info =
82 {
83 	RES_NET_VCC_5V | RES_NET_VBIAS_5V | RES_NET_VIN_TTL_OUT,
84 	{
85 		{RES_NET_AMP_NONE, 560, 0, 5, {4700, 2200, 1000, 470, 220}},
86 		{RES_NET_AMP_NONE, 560, 0, 5, {4700, 2200, 1000, 470, 220}},
87 		{RES_NET_AMP_NONE, 560, 0, 5, {4700, 2200, 1000, 470, 220}}
88 	}
89 };
90 
91 static const res_net_info lockon_pd_net_info =
92 {
93 	RES_NET_VCC_5V | RES_NET_VBIAS_5V | RES_NET_VIN_TTL_OUT,
94 	{
95 		{RES_NET_AMP_NONE, 560, 580, 5, {4700, 2200, 1000, 470, 220}},
96 		{RES_NET_AMP_NONE, 560, 580, 5, {4700, 2200, 1000, 470, 220}},
97 		{RES_NET_AMP_NONE, 560, 580, 5, {4700, 2200, 1000, 470, 220}}
98 	}
99 };
100 
lockon_palette(palette_device & palette) const101 void lockon_state::lockon_palette(palette_device &palette) const
102 {
103 	uint8_t const *const color_prom = memregion("proms")->base();
104 
105 	for (int i = 0; i < 1024; ++i)
106 	{
107 		uint8_t r, g, b;
108 		uint8_t const p1 = color_prom[i];
109 		uint8_t const p2 = color_prom[i + 0x400];
110 
111 		if (p2 & 0x80)
112 		{
113 			r = compute_res_net((p2 >> 2) & 0x1f, 0, lockon_net_info);
114 			g = compute_res_net(((p1 >> 5) & 0x7) | (p2 & 3) << 3, 1, lockon_net_info);
115 			b = compute_res_net((p1 & 0x1f), 2, lockon_net_info);
116 		}
117 		else
118 		{
119 			r = compute_res_net((p2 >> 2) & 0x1f, 0, lockon_pd_net_info);
120 			g = compute_res_net(((p1 >> 5) & 0x7) | (p2 & 3) << 3, 1, lockon_pd_net_info);
121 			b = compute_res_net((p1 & 0x1f), 2, lockon_pd_net_info);
122 		}
123 
124 		palette.set_pen_color(i, rgb_t(r, g, b));
125 	}
126 }
127 
128 
129 /*************************************
130  *
131  *  Character tilemap handling
132  *
133  *************************************/
134 
lockon_char_w(offs_t offset,uint16_t data)135 void lockon_state::lockon_char_w(offs_t offset, uint16_t data)
136 {
137 	m_char_ram[offset] = data;
138 	m_tilemap->mark_tile_dirty(offset);
139 }
140 
TILE_GET_INFO_MEMBER(lockon_state::get_lockon_tile_info)141 TILE_GET_INFO_MEMBER(lockon_state::get_lockon_tile_info)
142 {
143 	uint32_t tileno = m_char_ram[tile_index] & 0x03ff;
144 	uint32_t col = (m_char_ram[tile_index] >> 10) & 0x3f;
145 
146 	col = (col & 0x1f) + (col & 0x20 ? 64 : 0);
147 	tileinfo.set(0, tileno, col, 0);
148 }
149 
150 
151 /*******************************************************************************************
152 
153   Scene tilemap hardware
154 
155 *******************************************************************************************/
156 
lockon_scene_h_scr_w(uint16_t data)157 void lockon_state::lockon_scene_h_scr_w(uint16_t data)
158 {
159 	m_scroll_h = data & 0x1ff;
160 }
161 
lockon_scene_v_scr_w(uint16_t data)162 void lockon_state::lockon_scene_v_scr_w(uint16_t data)
163 {
164 	m_scroll_v = data & 0x81ff;
165 }
166 
scene_draw()167 void lockon_state::scene_draw(  )
168 {
169 	/* 3bpp characters */
170 	const uint8_t *const gfx1 = memregion("gfx2")->base();
171 	const uint8_t *const gfx2 = gfx1 + 0x10000;
172 	const uint8_t *const gfx3 = gfx1 + 0x20000;
173 	const uint8_t *const clut = gfx1 + 0x30000;
174 
175 	for (uint32_t y = 0; y < FRAMEBUFFER_MAX_Y; ++y)
176 	{
177 		uint32_t d0 = 0, d1 = 0, d2 = 0;
178 		uint32_t colour = 0;
179 		uint32_t y_offs;
180 		uint32_t x_offs;
181 		uint32_t y_gran;
182 		uint32_t ram_mask = 0x7ff;
183 
184 		y_offs = (y + m_scroll_v) & 0x1ff;
185 
186 		/* Clamp - stops tilemap wrapping when screen is rotated */
187 		if (BIT(m_scroll_v, 15) && y_offs & 0x100)
188 			ram_mask = 0x7;
189 
190 		x_offs = (m_scroll_h - 8) & 0x1ff;
191 		y_gran = y_offs & 7;
192 
193 		if (x_offs & 7)
194 		{
195 			uint32_t tileidx;
196 			uint16_t addr = ((y_offs & ~7) << 3) + ((x_offs >> 3) & 0x3f);
197 			uint16_t ram_val = m_scene_ram[addr & ram_mask];
198 
199 			colour = (clut[ram_val & 0x7fff] & 0x3f) << 3;
200 			tileidx = ((ram_val & 0x0fff) << 3) + y_gran;
201 
202 			d0 = *(gfx1 + tileidx);
203 			d1 = *(gfx2 + tileidx);
204 			d2 = *(gfx3 + tileidx);
205 		}
206 
207 		uint16_t *bmpaddr = &m_back_buffer->pix(y);
208 
209 		for (uint32_t x = 0; x < FRAMEBUFFER_MAX_X; ++x)
210 		{
211 			uint32_t x_gran = (x_offs & 7) ^ 7;
212 			uint32_t col;
213 
214 			if (!(x_offs & 7))
215 			{
216 				uint32_t tileidx;
217 				uint16_t addr = ((y_offs & ~7) << 3) + ((x_offs >> 3) & 0x3f);
218 				uint16_t ram_val = m_scene_ram[addr & ram_mask];
219 
220 				colour = (clut[ram_val & 0x7fff] & 0x3f) << 3;
221 				tileidx = ((ram_val & 0x0fff) << 3) + y_gran;
222 
223 				d0 = *(gfx1 + tileidx);
224 				d1 = *(gfx2 + tileidx);
225 				d2 = *(gfx3 + tileidx);
226 			}
227 
228 			col = colour
229 					| (((d2 >> x_gran) & 1) << 2)
230 					| (((d1 >> x_gran) & 1) << 1)
231 					| ( (d0 >> x_gran) & 1);
232 
233 			*bmpaddr++ = 0xa00 + col;
234 
235 			x_offs = (x_offs + 1) & 0x1ff;
236 		}
237 
238 	}
239 }
240 
241 /*******************************************************************************************
242 
243     Ground Hardware
244 
245     Each framebuffer line corresponds to a three word entry in ground RAM,
246     starting from offset 3:
247 
248        FEDCBA9876543210
249     0 |.............xxx|  Tile line (A0-A2 GFX ROMs)
250       |...........xx...|  Tile index (A6-A5 GFX ROMs)
251       |........xxx.....|  ROM lut address (A6-A4)
252       |.xxxxxxx........|  ROM lut address (A13-A7)
253       |x...............|  /Line enable
254 
255     1 |........xxxxxxxx|  TZ2213 value
256       |xxxxxxxx........|  X offset
257 
258     2 |........xxxxxxxx|  TZ2213 DX
259       |.......x........|  Carry
260       |x...............|  End of list marker
261 
262     An 8-bit ground control register controls the following:
263 
264        76543210
265       |......xx|  LUT ROM A15-A14
266       |....xx..|  LUT ROM select
267       |..xx....|  CLUT ROM A13-A12
268       |.x......|  GFX ROM A15, CLUT ROM A14
269       |x.......|  GFX ROM bank select (always 0 - only 1 bank is present)
270 
271  *******************************************************************************************/
272 
lockon_ground_ctrl_w(uint16_t data)273 void lockon_state::lockon_ground_ctrl_w(uint16_t data)
274 {
275 	m_ground_ctrl = data & 0xff;
276 }
277 
TIMER_CALLBACK_MEMBER(lockon_state::bufend_callback)278 TIMER_CALLBACK_MEMBER(lockon_state::bufend_callback)
279 {
280 	m_ground->set_input_line_and_vector(0, HOLD_LINE, 0xff); // V30
281 	m_object->set_input_line(NEC_INPUT_LINE_POLL, ASSERT_LINE);
282 }
283 
284 /* Get data for a each 8x8x3 ground tile */
285 #define GET_GROUND_DATA()                                                                \
286 {                                                                                        \
287 	uint32_t gfx_a4_3  = (ls163 & 0xc) << 1;                                               \
288 	uint32_t lut_addr  = lut_address + ((ls163 >> 4) & 0xf);                               \
289 	uint32_t gfx_a14_7 = lut_rom[lut_addr] << 7;                                           \
290 	clut_addr = (lut_rom[lut_addr] << 4) | clut_a14_12 | clut_a4_3 | (ls163 & 0xc) >> 2; \
291 	gfx_addr  = gfx_a15 | gfx_a14_7 | gfx_a6_5 | gfx_a4_3 | gfx_a2_0;                    \
292 	pal = (clut_rom[clut_addr] << 3);                                                    \
293 	rom_data1 = gfx_rom[gfx_addr];                                                       \
294 	rom_data2 = gfx_rom[gfx_addr + 0x10000];                                             \
295 	rom_data3 = gfx_rom[gfx_addr + 0x20000];                                             \
296 }
297 
ground_draw()298 void lockon_state::ground_draw(  )
299 {
300 	/* ROM pointers */
301 	const uint8_t *const gfx_rom  = memregion("gfx4")->base();
302 	const uint8_t *const lut_rom  = gfx_rom + 0x30000 + ((m_ground_ctrl >> 2) & 0x3 ? 0x10000 : 0);
303 	const uint8_t *const clut_rom = gfx_rom + 0x50000;
304 
305 	uint32_t lut_a15_14   = (m_ground_ctrl & 0x3) << 14;
306 	uint32_t clut_a14_12 = (m_ground_ctrl & 0x70) << 8;
307 	uint32_t gfx_a15 = (m_ground_ctrl & 0x40) << 9;
308 	uint32_t offs = 3;
309 	uint32_t y;
310 
311 	/* TODO: Clean up and emulate CS of GFX ROMs? */
312 	for (y = 0; y < FRAMEBUFFER_MAX_Y; ++y)
313 	{
314 		uint16_t *bmpaddr = &m_back_buffer->pix(y);
315 		uint8_t ls163;
316 		uint32_t clut_addr;
317 		uint32_t gfx_addr;
318 		uint8_t rom_data1 = 0;
319 		uint8_t rom_data2 = 0;
320 		uint8_t rom_data3 = 0;
321 		uint32_t pal = 0;
322 		uint32_t x;
323 
324 		/* Draw this line? */
325 		if (!(m_ground_ram[offs] & 0x8000))
326 		{
327 			uint32_t gfx_a2_0  =  m_ground_ram[offs] & 0x0007;
328 			uint32_t gfx_a6_5  = (m_ground_ram[offs] & 0x0018) << 2;
329 			uint32_t clut_a4_3 = (m_ground_ram[offs] & 0x0018) >> 1;
330 			uint8_t   tz2213_x  = m_ground_ram[offs + 1] & 0xff;
331 			uint8_t   tz2213_dx = m_ground_ram[offs + 2] & 0xff;
332 
333 			uint32_t lut_address = lut_a15_14 + ((m_ground_ram[offs] & 0x7fe0) >> 1);
334 			uint32_t cy = m_ground_ram[offs + 2] & 0x0100;
335 			uint32_t color;
336 			uint32_t gpbal2_0_prev;
337 
338 			ls163 = m_ground_ram[offs + 1] >> 8;
339 
340 			gpbal2_0_prev = ((ls163 & 3) << 1) | BIT(tz2213_x, 7);
341 
342 			if (gpbal2_0_prev)
343 				GET_GROUND_DATA();
344 
345 			for (x = 0; x < FRAMEBUFFER_MAX_X; x++)
346 			{
347 				uint32_t tz2213_cy;
348 				uint32_t gpbal2_0 = ((ls163 & 3) << 1) | BIT(tz2213_x, 7);
349 
350 				/* Stepped into a new tile? */
351 				if (gpbal2_0 < gpbal2_0_prev)
352 					GET_GROUND_DATA();
353 
354 				gpbal2_0_prev = gpbal2_0;
355 
356 				color = pal;
357 				color += (rom_data1 >> gpbal2_0) & 0x1;
358 				color += ((rom_data2 >> gpbal2_0) & 0x1) << 1;
359 				color += ((rom_data3 >> gpbal2_0) & 0x1) << 2;
360 
361 				*bmpaddr++ = 0x800 + color;
362 
363 				/* Update the counters */
364 				tz2213_cy = (uint8_t)tz2213_dx > (uint8_t)~(tz2213_x);
365 				tz2213_x = (tz2213_x + tz2213_dx);
366 
367 				/* Carry? */
368 				if (tz2213_cy || cy)
369 					++ls163;
370 			}
371 		}
372 
373 		offs += 3;
374 
375 		/* End of list marker */
376 		if (m_ground_ram[offs + 2] & 0x8000)
377 		{
378 			m_bufend_timer->adjust(attotime::from_hz(FRAMEBUFFER_CLOCK) * (FRAMEBUFFER_MAX_X * y));
379 		}
380 	}
381 }
382 
383 
384 /*******************************************************************************************
385 
386     Object hardware
387 
388     Customs (4 each, 1 per bitplane)
389     TZA118 - Scaling
390     TZ4203 - Objects with integrated line buffer.
391 
392        FEDCBA9876543210
393     0 |......xxxxxxxxxx|  Y position
394       |....xx..........|  Object Y size
395       |..xx............|  Object X size
396       |.x..............|  Y flip
397       |x...............|  X flip
398     1 |........xxxxxxxx|  X/Y scale
399       |.xxxxxxx........|  Colour
400       |x...............|  End of list marker
401     2 |xxxxxxxxxxxxxxxx|  Chunk ROM address
402     3 |.....xxxxxxxxxxx|  X position
403 
404 *******************************************************************************************/
405 
406 /*
407    There's logic to prevent shadow pixels from being drawn against the scene tilemap,
408    so that shadows don't appear against the sky.
409 */
410 #define DRAW_OBJ_PIXEL(COLOR)                            \
411 do {                                                     \
412 	if (px < FRAMEBUFFER_MAX_X)                          \
413 	if (COLOR != 0xf)                                    \
414 	{                                                    \
415 		uint8_t clr = m_obj_pal_ram[(pal << 4) + COLOR];     \
416 		uint16_t *pix = (line + px);                       \
417 		if (!(clr == 0xff && ((*pix & 0xe00) == 0xa00))) \
418 			*pix = 0x400 + clr;          \
419 	}                                                    \
420 	px = (px + 1) & 0x7ff;                               \
421 } while(0)
422 
objects_draw()423 void lockon_state::objects_draw(  )
424 {
425 	const uint8_t  *const romlut = memregion("user1")->base();
426 	const uint16_t *const chklut = (uint16_t*)memregion("user2")->base();
427 	const uint8_t  *const gfxrom = memregion("gfx5")->base();
428 	const uint8_t  *const sproms = memregion("proms")->base() + 0x800;
429 
430 	for (uint32_t offs = 0; offs < m_object_ram.bytes(); offs += 4)
431 	{
432 		/* Retrieve the object attributes */
433 		uint32_t ypos    = m_object_ram[offs] & 0x03ff;
434 		uint32_t xpos    = m_object_ram[offs + 3] & 0x07ff;
435 		uint32_t ysize   = (m_object_ram[offs] >> 10) & 0x3;
436 		uint32_t xsize   = (m_object_ram[offs] >> 12) & 0x3;
437 		uint32_t yflip   = BIT(m_object_ram[offs], 14);
438 		uint32_t xflip   = BIT(m_object_ram[offs], 15);
439 		uint32_t scale   = m_object_ram[offs + 1] & 0xff;
440 		uint32_t pal = (m_object_ram[offs + 1] >> 8) & 0x7f;
441 		uint32_t opsta   = m_object_ram[offs + 2];
442 
443 		if (m_iden)
444 		{
445 			m_obj_pal_ram[(pal << 4) + m_obj_pal_addr] = m_obj_pal_latch;
446 			break;
447 		}
448 
449 		/* How many lines will this sprite occupy? The PAL @ IC154 knows... */
450 		uint32_t lines = scale >> (3 - ysize);
451 
452 		uint32_t opsta15_8 = opsta & 0xff00;
453 
454 		/* Account for line buffering */
455 		ypos -=1;
456 
457 		for (uint32_t y = 0; y < FRAMEBUFFER_MAX_Y; y++)
458 		{
459 			uint32_t cy = (y + ypos) & 0x3ff;
460 			uint32_t optab;
461 			uint32_t lutaddr;
462 			uint32_t tile;
463 			uint8_t   cnt;
464 			uint32_t yidx;
465 			uint16_t *line = &m_back_buffer->pix(y);
466 			uint32_t px = xpos;
467 
468 			/* Outside the limits? */
469 			if (cy & 0x300)
470 				continue;
471 
472 			if ((cy  & 0xff) >= lines)
473 				break;
474 
475 			lutaddr = (scale & 0x80 ? 0x8000 : 0) | ((scale & 0x7f) << 8) | (cy & 0xff);
476 			optab = romlut[lutaddr] & 0x7f;
477 
478 			if (yflip)
479 				optab ^= 7;
480 
481 			yidx = (optab & 7);
482 
483 			/* Now calculate the lower 7-bits of the LUT ROM address. PAL @ IC157 does this */
484 			cnt = (optab >> 3) * (1 << xsize);
485 
486 			if (xflip)
487 				cnt ^= 7 >> (3 - xsize);
488 			if (yflip)
489 				cnt ^= (0xf >> (3 - ysize)) * (1 << xsize);
490 
491 			cnt = (cnt + (opsta & 0xff));
492 
493 			/* Draw! */
494 			for (tile = 0; tile < (1 << xsize); ++tile)
495 			{
496 				uint16_t sc;
497 				uint16_t scl;
498 				uint32_t x;
499 				uint32_t tileaddr;
500 				uint16_t td0, td1, td2, td3;
501 				uint32_t j;
502 				uint32_t bank;
503 
504 				scl = scale & 0x7f;
505 				tileaddr = (chklut[opsta15_8 + cnt] & 0x7fff);
506 				bank = ((tileaddr >> 12) & 3) * 0x40000;
507 				tileaddr = bank + ((tileaddr & ~0xf000) << 3);
508 
509 				if (xflip)
510 					--cnt;
511 				else
512 					++cnt;
513 
514 				/* Draw two 8x8 tiles */
515 				for (j = 0; j < 2; ++j)
516 				{
517 					/* Get tile data */
518 					uint32_t tileadd = tileaddr + (0x20000 * (j ^ xflip));
519 
520 					/* Retrieve scale values from PROMs */
521 					sc = sproms[(scl << 4) + (tile * 2) + j];
522 
523 					/* Data from ROMs is inverted */
524 					td3 = gfxrom[tileadd + yidx] ^ 0xff;
525 					td2 = gfxrom[tileadd + 0x8000 + yidx] ^ 0xff;
526 					td1 = gfxrom[tileadd + 0x10000 + yidx] ^ 0xff;
527 					td0 = gfxrom[tileadd + 0x18000 + yidx] ^ 0xff;
528 
529 					if (scale & 0x80)
530 					{
531 						for (x = 0; x < 8; ++x)
532 						{
533 							uint8_t col;
534 							uint8_t pix = x;
535 
536 							if (!xflip)
537 								pix ^= 0x7;
538 
539 							col = BIT(td0, pix)
540 								| (BIT(td1, pix) << 1)
541 								| (BIT(td2, pix) << 2)
542 								| (BIT(td3, pix) << 3);
543 
544 							DRAW_OBJ_PIXEL(col);
545 
546 							if (BIT(sc, x))
547 								DRAW_OBJ_PIXEL(col);
548 						}
549 					}
550 					else
551 					{
552 						for (x = 0; x < 8; ++x)
553 						{
554 							uint8_t col;
555 							uint8_t pix = x;
556 
557 							if (BIT(sc, x))
558 							{
559 								if (!xflip)
560 									pix ^= 0x7;
561 
562 								col = BIT(td0, pix)
563 									| (BIT(td1, pix) << 1)
564 									| (BIT(td2, pix) << 2)
565 									| (BIT(td3, pix) << 3);
566 
567 								DRAW_OBJ_PIXEL(col);
568 							}
569 						}
570 					}
571 				}
572 			}
573 		}
574 
575 		/* Check for the end of list marker */
576 		if (m_object_ram[offs + 1] & 0x8000)
577 			return;
578 	}
579 }
580 
581 /* The mechanism used by the object CPU to update the object ASICs palette RAM */
lockon_tza112_w(offs_t offset,uint16_t data)582 void lockon_state::lockon_tza112_w(offs_t offset, uint16_t data)
583 {
584 	if (m_iden)
585 	{
586 		m_obj_pal_latch = data & 0xff;
587 		m_obj_pal_addr = offset & 0xf;
588 		objects_draw();
589 	}
590 }
591 
lockon_obj_4000_r()592 uint16_t lockon_state::lockon_obj_4000_r()
593 {
594 	m_object->set_input_line(NEC_INPUT_LINE_POLL, CLEAR_LINE);
595 	return 0xffff;
596 }
597 
lockon_obj_4000_w(uint16_t data)598 void lockon_state::lockon_obj_4000_w(uint16_t data)
599 {
600 	m_iden = data & 1;
601 }
602 
603 
604 /*******************************************************************************************
605 
606     Frame buffer rotation hardware
607 
608       FEDCBA9876543210
609     0 |........xxxxxxxx|  X start address
610       |.......x........|  Direction
611     1 |........xxxxxxxx|  TZ2213 IC65 value
612       |.......x........|  TZ2213 IC65 /enable
613     2 |........xxxxxxxx|  TZ2213 IC65 delta
614     3 |........xxxxxxxx|  TZ2213 IC106 delta
615       |.......x........|  TZ2213 IC106 enable
616     4 |.......xxxxxxxxx|  Y start address
617     5 |........xxxxxxxx|  TZ2213 IC66 value
618     6 |........xxxxxxxx|  TZ2213 IC66 delta
619       |.......x........|  TZ2213 IC65 /enable
620     7 |........xxxxxxxx|  TZ2213 IC107 delta
621       |.......x........|  TZ2213 /enable
622       |......x.........|  Direction
623 
624 *******************************************************************************************/
625 
lockon_fb_clut_w(offs_t offset,uint16_t data)626 void lockon_state::lockon_fb_clut_w(offs_t offset, uint16_t data)
627 {
628 	rgb_t color;
629 
630 	color = m_palette->pen_color(0x300 + (data & 0xff));
631 	m_palette->set_pen_color(0x400 + offset, color);
632 }
633 
634 /* Rotation control register */
lockon_rotate_w(offs_t offset,uint16_t data)635 void lockon_state::lockon_rotate_w(offs_t offset, uint16_t data)
636 {
637 	switch (offset & 7)
638 	{
639 		case 0: m_xsal  = data & 0x1ff; break;
640 		case 1: m_x0ll  = data & 0xff;  break;
641 		case 2: m_dx0ll = data & 0x1ff; break;
642 		case 3: m_dxll  = data & 0x1ff; break;
643 
644 		case 4: m_ysal  = data & 0x1ff; break;
645 		case 5: m_y0ll  = data & 0xff;  break;
646 		case 6: m_dy0ll =   data & 0x1ff;   break;
647 		case 7: m_dyll  = data & 0x3ff; break;
648 	}
649 }
650 
651 #define INCREMENT(ACC, CNT)              \
652 do {                                     \
653 	carry = (uint8_t)d##ACC > (uint8_t)~ACC; \
654 	ACC += d##ACC;                       \
655 	if (carry) ++CNT;                    \
656 } while(0)
657 
658 #define DECREMENT(ACC, CNT)              \
659 do {                                     \
660 	carry = (uint8_t)d##ACC > (uint8_t)ACC;  \
661 	ACC -= d##ACC;                       \
662 	if (carry) --CNT;                    \
663 } while(0)
664 
rotate_draw(bitmap_ind16 & bitmap,const rectangle & cliprect)665 void lockon_state::rotate_draw( bitmap_ind16 &bitmap, const rectangle &cliprect )
666 {
667 	/* Counters */
668 	uint32_t cxy = m_xsal & 0xff;
669 	uint32_t cyy = m_ysal & 0x1ff;
670 
671 	/* Accumulator values and deltas */
672 	uint8_t axy  = m_x0ll & 0xff;
673 	uint8_t daxy = m_dx0ll & 0xff;
674 	uint8_t ayy  = m_y0ll & 0xff;
675 	uint8_t dayy = m_dy0ll & 0xff;
676 	uint8_t dayx = m_dyll & 0xff;
677 	uint8_t daxx = m_dxll & 0xff;
678 
679 	uint32_t xy_up = BIT(m_xsal, 8);
680 	uint32_t yx_up = BIT(m_dyll, 9);
681 	uint32_t axx_en  = !BIT(m_dxll, 8);
682 	uint32_t ayx_en  = !BIT(m_dyll, 8);
683 	uint32_t axy_en  = !BIT(m_dx0ll, 8);
684 	uint32_t ayy_en  = !BIT(m_dy0ll, 8);
685 
686 	for (uint32_t y = 0; y <= cliprect.max_y; ++y)
687 	{
688 		uint32_t carry;
689 		uint16_t *dst = &bitmap.pix(y);
690 
691 		uint32_t cx = cxy;
692 		uint32_t cy = cyy;
693 
694 		uint8_t axx = axy;
695 		uint8_t ayx = ayy;
696 
697 		for (uint32_t x = 0; x <= cliprect.max_x; ++x)
698 		{
699 			cx &= 0x1ff;
700 			cy &= 0x1ff;
701 
702 			*dst++ = m_front_buffer->pix(cy, cx);
703 
704 			if (axx_en)
705 				INCREMENT(axx, cx);
706 			else
707 				++cx;
708 
709 			if (ayx_en)
710 			{
711 				if (yx_up)
712 					INCREMENT(ayx, cy);
713 				else
714 					DECREMENT(ayx, cy);
715 			}
716 			else
717 			{
718 				if (yx_up)
719 					++cy;
720 				else
721 					--cy;
722 			}
723 		}
724 
725 		if (axy_en)
726 		{
727 			if (xy_up)
728 				INCREMENT(axy, cxy);
729 			else
730 				DECREMENT(axy, cxy);
731 		}
732 		else
733 		{
734 			if (xy_up)
735 				++cxy;
736 			else
737 				--cxy;
738 		}
739 
740 		if (ayy_en)
741 			INCREMENT(ayy, cyy);
742 		else
743 			++cyy;
744 
745 		cxy &= 0xff;
746 		cyy &= 0x1ff;
747 	}
748 }
749 
750 
751 /*******************************************************************************************
752 
753     HUD Drawing Hardware
754 
755     A sprite layer that uses 8x8x1bpp tiles to form bigger sprites
756 
757     0  |.......xxxxxxxxx|  Y Position
758        |xxxxxxx.........|  Code
759     1  |.......xxxxxxxxx|  X Position
760        |....xxx.........|  Colour
761        |.xxx............|  Sprite width (0=8, 1=16, 2=24, 3=32 pixels, etc.)
762        |x...............|  End of list marker
763 
764 *******************************************************************************************/
765 
hud_draw(bitmap_ind16 & bitmap,const rectangle & cliprect)766 void lockon_state::hud_draw( bitmap_ind16 &bitmap, const rectangle &cliprect )
767 {
768 	uint8_t const *const tile_rom = memregion("gfx3")->base();
769 
770 	for (uint32_t offs = 0x0; offs <= m_hud_ram.bytes(); offs += 2)
771 	{
772 		uint32_t y_pos;
773 		uint32_t x_pos;
774 		uint32_t y_size;
775 		uint32_t x_size;
776 		uint32_t layout;
777 		uint16_t colour;
778 		uint32_t code;
779 		uint32_t rom_a12_7;
780 
781 		/* End of sprite list marker */
782 		if (m_hud_ram[offs + 1] & 0x8000)
783 			break;
784 
785 		y_pos   = m_hud_ram[offs] & 0x1ff;
786 		x_pos   = m_hud_ram[offs + 1] & 0x1ff;
787 		x_size = (m_hud_ram[offs + 1] >> 12) & 7;
788 		code    = (m_hud_ram[offs] >> 9) & 0x7f;
789 		colour = 0x200 + ((m_hud_ram[offs + 1] >> 9) & 7);
790 		layout = (code >> 5) & 3;
791 
792 		rom_a12_7 = (code & 0xfe) << 6;
793 
794 		/* Account for line buffering */
795 		y_pos -= 1;
796 
797 		if (layout == 3)
798 			y_size = 32;
799 		else if (layout == 2)
800 			y_size = 16;
801 		else
802 			y_size = 8;
803 
804 		for (uint32_t y = cliprect.min_y; y <= cliprect.max_y; ++y)
805 		{
806 			uint32_t xt;
807 			uint32_t cy;
808 
809 			cy = y_pos + y;
810 
811 			if (cy < 0x200)
812 				continue;
813 
814 			if ((cy & 0xff) == y_size)
815 				break;
816 
817 			for (xt = 0; xt <= x_size; ++xt)
818 			{
819 				uint32_t rom_a6_3;
820 				uint32_t px;
821 				uint8_t   gfx_strip;
822 
823 				if (layout == 3)
824 					rom_a6_3 = (BIT(cy, 4) << 3) | (BIT(cy, 3) << 2) | (BIT(xt, 1) << 1) | BIT(xt, 0);
825 				else if (layout == 2)
826 					rom_a6_3 = ((BIT(code, 0) << 3) | (BIT(xt, 1) << 2) | (BIT(cy, 3) << 1) | (BIT(xt, 0)));
827 				else
828 					rom_a6_3 = (BIT(code, 0) << 3) | (xt & 7);
829 
830 				rom_a6_3 <<= 3;
831 
832 				/* Get tile data */
833 				gfx_strip = tile_rom[rom_a12_7 | rom_a6_3 | (cy & 7)];
834 
835 				if (gfx_strip == 0)
836 					continue;
837 
838 				/* Draw */
839 				for (px = 0; px < 8; ++px)
840 				{
841 					uint32_t x = x_pos + (xt << 3) + px;
842 
843 					if (x <= cliprect.max_x)
844 					{
845 						uint16_t *const dst = &bitmap.pix(y, x);
846 
847 						if (BIT(gfx_strip, px ^ 7) && *dst > 255)
848 							*dst = colour;
849 					}
850 				}
851 			}
852 		}
853 	}
854 }
855 
856 
857 /*************************************
858  *
859  *  Driver video handlers
860  *
861  *************************************/
862 
video_start()863 void lockon_state::video_start()
864 {
865 	m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(lockon_state::get_lockon_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32);
866 	m_tilemap->set_transparent_pen(0);
867 
868 	/* Allocate the two frame buffers for rotation */
869 	m_back_buffer = std::make_unique<bitmap_ind16>(512, 512);
870 	m_front_buffer = std::make_unique<bitmap_ind16>(512, 512);
871 
872 	/* 2kB of object ASIC palette RAM */
873 	m_obj_pal_ram = std::make_unique<uint8_t[]>(2048);
874 
875 	/* Timer for ground display list callback */
876 	m_bufend_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(lockon_state::bufend_callback),this));
877 
878 	/* Timer for the CRTC cursor pulse */
879 	m_cursor_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(lockon_state::cursor_callback),this));
880 	m_cursor_timer->adjust(m_screen->time_until_pos(CURSOR_YPOS, CURSOR_XPOS));
881 
882 	save_item(NAME(*m_back_buffer));
883 	save_item(NAME(*m_front_buffer));
884 	save_pointer(NAME(m_obj_pal_ram), 2048);
885 }
886 
screen_update_lockon(screen_device & screen,bitmap_ind16 & bitmap,const rectangle & cliprect)887 uint32_t lockon_state::screen_update_lockon(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
888 {
889 	/* If screen output is disabled, fill with black */
890 	if (!BIT(m_ctrl_reg, 7))
891 	{
892 		bitmap.fill(m_palette->black_pen(), cliprect);
893 		return 0;
894 	}
895 
896 	/* Scan out the frame buffer in rotated order */
897 	rotate_draw(bitmap, cliprect);
898 
899 	/* Draw the character tilemap */
900 	m_tilemap->draw(screen, bitmap, cliprect, 0, 0);
901 
902 	/* Draw the HUD */
903 	hud_draw(bitmap, cliprect);
904 
905 	return 0;
906 }
907 
WRITE_LINE_MEMBER(lockon_state::screen_vblank_lockon)908 WRITE_LINE_MEMBER(lockon_state::screen_vblank_lockon)
909 {
910 	// on falling edge
911 	if (!state)
912 	{
913 		/* Swap the frame buffers */
914 		m_front_buffer.swap(m_back_buffer);
915 
916 		/* Draw the frame buffer layers */
917 		scene_draw();
918 		ground_draw();
919 		objects_draw();
920 	}
921 }
922