1 // license:GPL-2.0+
2 // copyright-holders:Peter Trauner
3 /******************************************************************************
4  PeT mess@utanet.at 2000,2001
5 ******************************************************************************/
6 
7 #include "emu.h"
8 #include "includes/lynx.h"
9 #include "cpu/m6502/m65sc02.h"
10 #include "render.h"
11 
12 #define PAD_UP      0x80
13 #define PAD_DOWN    0x40
14 #define PAD_LEFT    0x20
15 #define PAD_RIGHT   0x10
16 
17 
18 /****************************************
19 
20     Graphics Drawing
21 
22 ****************************************/
23 
24 /* modes from blitter command */
25 enum {
26 	BACKGROUND = 0,
27 	BACKGROUND_NO_COLL,
28 	BOUNDARY_SHADOW,
29 	BOUNDARY,
30 	NORMAL_SPRITE,
31 	NO_COLL,
32 	XOR_SPRITE,
33 	SHADOW
34 };
35 
lynx_read_ram(uint16_t address)36 uint8_t lynx_state::lynx_read_ram(uint16_t address)
37 {
38 	uint8_t result = 0x00;
39 	if (address <= 0xfbff)
40 		result = m_mem_0000[address - 0x0000];
41 	else if (address <= 0xfcff)
42 		result = m_mem_fc00[address - 0xfc00];
43 	else if (address <= 0xfdff)
44 		result = m_mem_fd00[address - 0xfd00];
45 	else if (address <= 0xfff7)
46 		result = m_mem_fe00[address - 0xfe00];
47 	else if (address >= 0xfffa)
48 		result = m_mem_fffa[address - 0xfffa];
49 	return result;
50 }
51 
lynx_write_ram(uint16_t address,uint8_t data)52 void lynx_state::lynx_write_ram(uint16_t address, uint8_t data)
53 {
54 	if (address <= 0xfbff)
55 		m_mem_0000[address - 0x0000] = data;
56 	else if (address <= 0xfcff)
57 		m_mem_fc00[address - 0xfc00] = data;
58 	else if (address <= 0xfdff)
59 		m_mem_fd00[address - 0xfd00] = data;
60 	else if (address <= 0xfff7)
61 		m_mem_fe00[address - 0xfe00] = data;
62 	else if (address >= 0xfffa)
63 		m_mem_fffa[address - 0xfffa] = data;
64 }
65 
66 /* The pen numbers range from '0' to 'F. Pen numbers '1' through 'D' are always collidable and opaque. The other
67 ones have different behavior depending on the sprite type: there are 8 types of sprites, each has different
68 characteristics relating to some or all of their pen numbers.
69 
70 * Shadow Error: The hardware is missing an inverter in the 'shadow' generator. This causes sprite types that
71 did not invoke shadow to now invoke it and vice versa. The only actual functionality loss is that 'exclusive or'
72 sprites and 'background' sprites will have shadow enabled.
73 
74 The sprite types relate to specific hardware functions according to the following table:
75 
76 
77    -------------------------- SHADOW
78   |   ----------------------- BOUNDARY_SHADOW
79   |  |   -------------------- NORMAL_SPRITE
80   |  |  |   ----------------- BOUNDARY
81   |  |  |  |   -------------- BACKGROUND (+ shadow, due to bug in 'E' pen)
82   |  |  |  |  |   ----------- BACKGROUND_NO_COLL
83   |  |  |  |  |  |   -------- NO_COLL
84   |  |  |  |  |  |  |   ----- XOR_SPRITE (+ shadow, due to bug in 'E' pen)
85   |  |  |  |  |  |  |  |
86   1  0  1  0  1  1  1  1      F is opaque
87   0  0  1  1  0  0  0  0      E is collideable
88   0  0  1  1  0  0  0  0      0 is opaque and collideable
89   1  1  1  1  0  0  0  1      allow collision detect
90   1  1  1  1  1  0  0  1      allow coll. buffer access
91   0  0  0  0  0  0  0  1      exclusive-or the data
92 */
93 
lynx_plot_pixel(const int mode,const int16_t x,const int y,const int color)94 inline void lynx_state::lynx_plot_pixel(const int mode, const int16_t x, const int y, const int color)
95 {
96 	uint8_t back;
97 	uint16_t screen;
98 	uint16_t colbuf;
99 
100 	m_blitter.everon = true;
101 	screen = m_blitter.screen + y * 80 + x / 2;
102 	colbuf = m_blitter.colbuf + y * 80 + x / 2;
103 
104 	/* a note on timing: The hardware packs the pixel data and updates the screen and collision buffer a byte at a time.
105 	Thus the buffer update for two pixels takes 3 memory accesses for a normal sprite (write to screen buffer, read/write to collision buffer).
106 	+1 memory access for palette fetch?
107 	*/
108 
109 	switch (mode&0x7)
110 	{
111 		case NORMAL_SPRITE:
112 		/* A sprite may be set to 'normal'. This means that pen number '0' will be transparent and
113 		non-collideable. All other pens will be opaque and collideable */
114 			if (color == 0)
115 				break;
116 			if (!(x & 0x01))        /* Upper nibble */
117 			{
118 				back = lynx_read_ram(screen);
119 				lynx_write_ram(screen, (back & 0x0f) | (color << 4));
120 				m_blitter.memory_accesses++;
121 
122 				if(m_blitter.sprite_collide && !(m_blitter.no_collide))
123 				{
124 					back = lynx_read_ram(colbuf);
125 					lynx_write_ram(colbuf, (back & ~0xf0) | (m_blitter.spritenr << 4));
126 					m_blitter.memory_accesses += 2;
127 					if ((back >> 4) > m_blitter.fred)
128 						m_blitter.fred = back >> 4;
129 				}
130 				m_blitter.memory_accesses++;
131 			}
132 			else                    /* Lower nibble */
133 			{
134 				back = lynx_read_ram(screen);
135 				lynx_write_ram(screen, (back & 0xf0) | color);
136 
137 				if(m_blitter.sprite_collide && !(m_blitter.no_collide))
138 				{
139 					back = lynx_read_ram(colbuf);
140 					lynx_write_ram(colbuf, (back & ~0x0f) | (m_blitter.spritenr));
141 					if ((back & 0x0f) > m_blitter.fred)
142 						m_blitter.fred = back >> 4;
143 				}
144 			}
145 			break;
146 
147 		case BOUNDARY:
148 		/* A sprite may be set to 'boundary'. This is a 'normal' sprite with the exception that pen
149 		number 'F' is transparent (and still collideable). */
150 			if (color == 0)
151 				break;
152 			if (!(x & 0x01))        /* Upper nibble */
153 			{
154 				if (color != 0x0f)
155 				{
156 					back = lynx_read_ram(screen);
157 					lynx_write_ram(screen, (back & 0x0f) | (color << 4));
158 					m_blitter.memory_accesses++;
159 				}
160 				if(m_blitter.sprite_collide && !(m_blitter.no_collide))
161 				{
162 					back = lynx_read_ram(colbuf);
163 					lynx_write_ram(colbuf, (back & ~0xf0) | (m_blitter.spritenr << 4));
164 					if ((back >> 4) > m_blitter.fred)
165 						m_blitter.fred = back >> 4;
166 					m_blitter.memory_accesses += 2;
167 				}
168 				m_blitter.memory_accesses++;
169 			}
170 			else                    /* Lower nibble */
171 			{
172 				if (color != 0x0f)
173 				{
174 					back = lynx_read_ram(screen);
175 					lynx_write_ram(screen, (back & 0xf0) | color);
176 				}
177 				if(m_blitter.sprite_collide && !(m_blitter.no_collide))
178 				{
179 					back = lynx_read_ram(colbuf);
180 					lynx_write_ram(colbuf, (back & ~0x0f) | (m_blitter.spritenr));
181 					if ((back & 0x0f) > m_blitter.fred)
182 						m_blitter.fred = back >> 4;
183 				}
184 			}
185 			break;
186 
187 		case SHADOW:
188 		/* A sprite may be set to 'shadow'. This is a 'normal' sprite with the exception that pen
189 		number 'E' is non-collideable (but still opaque) */
190 			if (color == 0)
191 				break;
192 			if (!(x & 0x01))        /* Upper nibble */
193 			{
194 				back = lynx_read_ram(screen);
195 				lynx_write_ram(screen, (back & 0x0f) | (color << 4));
196 				m_blitter.memory_accesses++;
197 
198 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
199 				{
200 					back = lynx_read_ram(colbuf);
201 					lynx_write_ram(colbuf, (back & ~0xf0) | (m_blitter.spritenr << 4));
202 					if ((back >> 4) > m_blitter.fred)
203 						m_blitter.fred = back >> 4;
204 					m_blitter.memory_accesses += 2;
205 				}
206 				m_blitter.memory_accesses++;
207 			}
208 			else                    /* Lower nibble */
209 			{
210 				back = lynx_read_ram(screen);
211 				lynx_write_ram(screen, (back & 0xf0) | color);
212 
213 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
214 				{
215 					back = lynx_read_ram(colbuf);
216 					lynx_write_ram(colbuf, (back & ~0x0f) | (m_blitter.spritenr));
217 					if ((back & 0x0f) > m_blitter.fred)
218 						m_blitter.fred = back >> 4;
219 				}
220 			}
221 			break;
222 
223 		case BOUNDARY_SHADOW:
224 		/* This sprite is a 'normal' sprite with the characteristics of both 'boundary'
225 		and 'shadow'. That is, pen number 'F' is transparent (and still collideable) and
226 		pen number 'E' is non-collideable (but still opaque). */
227 			if (color == 0)
228 				break;
229 			if (!(x & 0x01))        /* Upper nibble */
230 			{
231 				if (color != 0x0f)
232 				{
233 					back = lynx_read_ram(screen);
234 					lynx_write_ram(screen, (back & 0x0f) | (color << 4));
235 					m_blitter.memory_accesses++;
236 				}
237 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
238 				{
239 					back = lynx_read_ram(colbuf);
240 					lynx_write_ram(colbuf, (back & ~0xf0) | (m_blitter.spritenr << 4));
241 					if ((back >> 4) > m_blitter.fred)
242 						m_blitter.fred = back >> 4;
243 					m_blitter.memory_accesses += 2;
244 				}
245 				m_blitter.memory_accesses++;
246 			}
247 			else                    /* Lower nibble */
248 			{
249 				if (color != 0x0f)
250 				{
251 					back = lynx_read_ram(screen);
252 					lynx_write_ram(screen, (back & 0xf0) | color);
253 				}
254 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
255 				{
256 					back = lynx_read_ram(colbuf);
257 					lynx_write_ram(colbuf, (back & ~0x0f) | (m_blitter.spritenr));
258 					if ((back & 0x0f) > m_blitter.fred)
259 						m_blitter.fred = back >> 4;
260 				}
261 			}
262 			break;
263 
264 		case BACKGROUND:
265 		/* A sprite may be set to 'background'. This sprite will overwrite the contents of the video and
266 		collision buffers. Pens '0' and 'F' are no longer transparent. This sprite is used to initialize
267 		the buffers at the start of a 'painting'. Additionally, no collision detection is done, and no write
268 		to the collision depository occurs. The 'E' error will cause the pen number 'E' to be non-collideable
269 		and therefore not clear the collision buffer */
270 			if (!(x & 0x01))        /* Upper nibble */
271 			{
272 				back = lynx_read_ram(screen);
273 				lynx_write_ram(screen, (back & 0x0f) | (color << 4));
274 				m_blitter.memory_accesses++;
275 
276 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
277 				{
278 					back = lynx_read_ram(colbuf);
279 					lynx_write_ram(colbuf, (back & ~0xf0) | (m_blitter.spritenr << 4));
280 					m_blitter.memory_accesses++;
281 				}
282 				m_blitter.memory_accesses++;
283 			}
284 			else                    /* Lower nibble */
285 			{
286 				back = lynx_read_ram(screen);
287 				lynx_write_ram(screen, (back & 0xf0) | color);
288 
289 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
290 				{
291 					back = lynx_read_ram(colbuf);
292 					lynx_write_ram(colbuf, (back & ~0x0f) | (m_blitter.spritenr));
293 				}
294 			}
295 			break;
296 
297 		case BACKGROUND_NO_COLL:
298 		/* This is a 'background' sprite with the exception that no activity occurs in the collision buffer */
299 			if (!(x & 0x01))        /* Upper nibble */
300 			{
301 				back = lynx_read_ram(screen);
302 				lynx_write_ram(screen, (back & 0x0f) | (color << 4));
303 				m_blitter.memory_accesses++;
304 				m_blitter.memory_accesses++;
305 			}
306 			else                    /* Lower nibble */
307 			{
308 				back = lynx_read_ram(screen);
309 				lynx_write_ram(screen, (back & 0xf0) | color);
310 			}
311 			break;
312 
313 		case NO_COLL:
314 		/* A sprite may be set to 'non-collideable'. This means that it will have no affect on the contents of
315 		the collision buffer and all other collision activities are overridden (pen 'F' is not collideable). */
316 			if (color == 0)
317 				break;
318 			if (!(x & 0x01))        /* Upper nibble */
319 			{
320 				back = lynx_read_ram(screen);
321 				lynx_write_ram(screen, (back & 0x0f) | (color << 4));
322 				m_blitter.memory_accesses++;
323 				m_blitter.memory_accesses++;
324 			}
325 			else                    /* Lower nibble */
326 			{
327 				back = lynx_read_ram(screen);
328 				lynx_write_ram(screen, (back & 0xf0) | color);
329 			}
330 			break;
331 
332 		case XOR_SPRITE:
333 		/* This is a 'normal' sprite with the exception that the data from the video buffer is exclusive-ored
334 		with the sprite data and written back out to the video buffer. Collision activity is 'normal'. The 'E'
335 		error will cause the pen number 'E' to be non-collideable and therefore not react with the collision
336 		buffer */
337 			if (color == 0)
338 				break;
339 			if (!(x & 0x01))        /* Upper nibble */
340 			{
341 				back = lynx_read_ram(screen);
342 				lynx_write_ram(screen, back^(color << 4));
343 				m_blitter.memory_accesses += 2;
344 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
345 				{
346 					back = lynx_read_ram(colbuf);
347 					lynx_write_ram(colbuf, (back & ~0xf0) | (m_blitter.spritenr << 4));
348 					if ((back >> 4) > m_blitter.fred)
349 						m_blitter.fred = back >> 4;
350 					m_blitter.memory_accesses += 2;
351 				}
352 				m_blitter.memory_accesses++;
353 			}
354 			else                    /* Lower nibble */
355 			{
356 				back = lynx_read_ram(screen);
357 				lynx_write_ram(screen, back^color);
358 				if (m_blitter.sprite_collide && (color != 0x0e) && !(m_blitter.no_collide))
359 				{
360 					back = lynx_read_ram(colbuf);
361 					lynx_write_ram(colbuf, (back & ~0x0f) | (m_blitter.spritenr));
362 					if ((back & 0x0f) > m_blitter.fred)
363 						m_blitter.fred = back >> 4;
364 				}
365 			}
366 			break;
367 	}
368 }
369 
lynx_blit_do_work(const int y,const int xdir,const int bits_per_pixel,const int mask)370 void lynx_state::lynx_blit_do_work( const int y, const int xdir, const int bits_per_pixel, const int mask )
371 {
372 	int next_line_addr,i,j;
373 	int xi, bits, color;
374 	uint16_t width_accum, buffer;
375 
376 	next_line_addr = lynx_read_ram(m_blitter.bitmap); // offset to second sprite line
377 	width_accum = (xdir == 1) ? m_blitter.width_offset : 0;
378 	m_blitter.memory_accesses++;
379 
380 	for (xi = m_blitter.x_pos - m_blitter.xoff, bits = 0, buffer = 0, j = 1; j < next_line_addr;j++)
381 	{
382 		buffer = (buffer << 8) | lynx_read_ram(m_blitter.bitmap + j);
383 		bits += 8; // current bits in buffer
384 		m_blitter.memory_accesses++;
385 
386 		for ( ; bits > bits_per_pixel; bits -= bits_per_pixel) // last data packet at end of scanline is not rendered (qix, blulght)
387 		{
388 			color = m_blitter.color[(buffer >> (bits - bits_per_pixel)) & mask];
389 			width_accum += m_blitter.width;
390 			for (i = 0; i < (width_accum>>8); i++, xi += xdir)
391 			{
392 				if ((xi >= 0) && (xi < 160))
393 				{
394 					lynx_plot_pixel(m_blitter.mode, xi, y, color);
395 				}
396 			}
397 			width_accum &= 0xff;
398 		}
399 	}
400 }
401 
lynx_blit_rle_do_work(const int16_t y,const int xdir,const int bits_per_pixel,const int mask)402 void lynx_state::lynx_blit_rle_do_work( const int16_t y, const int xdir, const int bits_per_pixel, const int mask )
403 {
404 	int i;
405 	int xi;
406 	int buffer, bits, j;
407 	int literal_data, count, color;
408 	uint16_t width_accum;
409 
410 	width_accum = (xdir == 1) ? m_blitter.width_offset : 0;
411 	for( bits = 0, j = 0, buffer = 0, xi = m_blitter.x_pos - m_blitter.xoff; ; )      /* through the rle entries */
412 	{
413 		if (bits < 5 + bits_per_pixel) /* under 7 bits no complete entry */
414 		{
415 			j++;
416 			if (j >= lynx_read_ram(m_blitter.bitmap))
417 				return;
418 
419 			bits += 8;
420 			buffer = (buffer << 8) | lynx_read_ram(m_blitter.bitmap + j);
421 			m_blitter.memory_accesses++;
422 		}
423 
424 		literal_data = (buffer >> (bits - 1)) & 0x01;
425 		bits--;
426 		count = (buffer >> (bits - 4)) & 0x0f; // repeat count (packed) or pixel count (literal)
427 		bits -= 4;
428 
429 		if (literal_data)       /* count of different pixels */
430 		{
431 			for ( ; count >= 0; count--)
432 			{
433 				if (bits < bits_per_pixel)
434 				{
435 					j++;
436 					if (j >= lynx_read_ram(m_blitter.bitmap))
437 						return;
438 					bits += 8;
439 					buffer = (buffer << 8) | lynx_read_ram(m_blitter.bitmap + j);
440 					m_blitter.memory_accesses++;
441 				}
442 
443 				color = m_blitter.color[(buffer >> (bits - bits_per_pixel)) & mask];
444 				bits -= bits_per_pixel;
445 				width_accum += m_blitter.width;
446 				for (i = 0; i < (width_accum>>8); i++, xi += xdir)
447 				{
448 					if ((xi >= 0) && (xi < 160))
449 						lynx_plot_pixel(m_blitter.mode, xi, y, color);
450 				}
451 				width_accum &= 0xff;
452 			}
453 		}
454 		else        /* count of same pixels */
455 		{
456 			if (count == 0) // 4 bit count value of zero indicates end-of-line in a packed sprite
457 				return;
458 
459 			if (bits < bits_per_pixel)
460 			{
461 				j++;
462 				if (j >= lynx_read_ram(m_blitter.bitmap))
463 					return;
464 				bits += 8;
465 				buffer = (buffer << 8) | lynx_read_ram(m_blitter.bitmap + j);
466 				m_blitter.memory_accesses++;
467 			}
468 
469 			color = m_blitter.color[(buffer >> (bits - bits_per_pixel)) & mask];
470 			bits -= bits_per_pixel;
471 
472 			for ( ; count>=0; count--)
473 			{
474 				width_accum += m_blitter.width;
475 				for (i = 0; i < (width_accum >> 8); i++, xi += xdir)
476 				{
477 					if ((xi >= 0) && (xi < 160))
478 						lynx_plot_pixel(m_blitter.mode, xi, y, color);
479 				}
480 				width_accum &= 0xff;
481 			}
482 		}
483 	}
484 }
485 
lynx_blit_lines()486 void lynx_state::lynx_blit_lines()
487 {
488 	static const int lynx_color_masks[4] = { 0x01, 0x03, 0x07, 0x0f };
489 	int16_t y;
490 	int i;
491 	int ydir = 0, xdir = 0;
492 	int flip = 0;
493 
494 	m_blitter.everon = false;
495 
496 	switch (m_blitter.spr_ctl1 & 0x03)   /* Initial drawing direction */
497 	{
498 		case 0: // Down/Right (quadrant 0)
499 			xdir = 1;
500 			ydir = 1;
501 			flip = 0;
502 			break;
503 		case 1: // Down/Left (blockout) (quadrant 3)
504 			xdir = -1;
505 			ydir = 1;
506 			flip = 3;
507 			break;
508 		case 2: // Up/Right (fat bobby) (quadrant 1)
509 			xdir = 1;
510 			ydir = -1;
511 			flip = 1;
512 			break;
513 		case 3: // Up/Left (quadrant 2)
514 			xdir = -1;
515 			ydir = -1;
516 			flip = 2;
517 			break;
518 	}
519 
520 	if (m_blitter.spr_ctl0 & 0x20)   /* Horizontal Flip */
521 	{
522 		xdir *= -1;
523 	}
524 
525 	if (m_blitter.spr_ctl0 & 0x10)   /* Vertical Flip */
526 	{
527 		ydir *= -1;
528 	}
529 
530 	// Set height accumulator based on drawing direction
531 	m_blitter.height_accumulator = (ydir == 1) ? m_blitter.height_offset : 0x00;
532 
533 	// loop through lines, next line offset of zero indicates end of sprite
534 	for (y = m_blitter.y_pos - m_blitter.yoff; (i = lynx_read_ram(m_blitter.bitmap)); m_blitter.bitmap += i)
535 	{
536 		m_blitter.memory_accesses++;
537 
538 		if (i == 1) // draw next quadrant
539 		{
540 			// centered sprites sprdemo3, fat bobby, blockout
541 			switch (flip & 0x03)
542 			{
543 				case 0:
544 				case 2:
545 					ydir *= -1;
546 					m_blitter.y_pos += ydir;
547 					break;
548 				case 1:
549 				case 3:
550 					xdir *= -1;
551 					m_blitter.x_pos += xdir;
552 					break;
553 			}
554 			flip++;
555 			y = m_blitter.y_pos - m_blitter.yoff;
556 			m_blitter.height_accumulator = (ydir == 1) ? m_blitter.height_offset : 0x00;
557 			continue;
558 		}
559 
560 		m_blitter.height_accumulator += m_blitter.height;
561 		for (int j = 0; j < (m_blitter.height_accumulator >> 8); j++, y += ydir)
562 		{
563 			if (y >= 0 && y < 102)
564 			{
565 				if (m_blitter.use_rle)
566 					lynx_blit_rle_do_work(y, xdir, m_blitter.line_color + 1, lynx_color_masks[m_blitter.line_color]);
567 				else
568 					lynx_blit_do_work(y, xdir, m_blitter.line_color + 1, lynx_color_masks[m_blitter.line_color]);
569 			}
570 			m_blitter.width += (int16_t)m_blitter.stretch;
571 			if (m_blitter.vstretch) // doesn't seem to be used
572 			{
573 				m_blitter.height += (int16_t)m_blitter.stretch;
574 				logerror("vertical stretch enabled");
575 			}
576 			m_blitter.tilt_accumulator += m_blitter.tilt;
577 			m_blitter.x_pos += (m_blitter.tilt_accumulator>>8);
578 			m_blitter.tilt_accumulator &= 0xff;
579 		}
580 		m_blitter.height_accumulator &= 0xff;
581 	}
582 }
583 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)584 void lynx_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
585 {
586 	switch (id)
587 	{
588 	case TIMER_BLITTER:
589 		lynx_blitter_timer(ptr, param);
590 		break;
591 	case TIMER_SHOT:
592 		lynx_timer_shot(ptr, param);
593 		break;
594 	case TIMER_UART_LOOPBACK:
595 		lynx_uart_loopback_timer(ptr, param);
596 		break;
597 	case TIMER_UART:
598 		lynx_uart_timer(ptr, param);
599 		break;
600 	default:
601 		throw emu_fatalerror("Unknown id in lynx_state::device_timer");
602 	}
603 }
604 
TIMER_CALLBACK_MEMBER(lynx_state::lynx_blitter_timer)605 TIMER_CALLBACK_MEMBER(lynx_state::lynx_blitter_timer)
606 {
607 	m_blitter.busy=0; // blitter finished
608 	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
609 }
610 
611 /*
612   control 0
613    bit 7,6: 00 2 color
614             01 4 color
615             11 8 colors?
616             11 16 color
617    bit 5,4: 00 right down
618             01 right up
619             10 left down
620             11 left up
621 
622 #define SHADOW         (0x07)
623 #define XORSHADOW      (0x06)
624 #define NONCOLLIDABLE  (0x05)
625 #define NORMAL         (0x04)
626 #define BOUNDARY       (0x03)
627 #define BOUNDARYSHADOW (0x02)
628 #define BKGRNDNOCOL    (0x01)
629 #define BKGRND         (0x00)
630 
631   control 1
632    bit 7: 0 bitmap rle encoded
633           1 not encoded
634    bit 3: 0 color info with command
635           1 no color info with command
636 
637 #define RELHVST        (0x30)
638 #define RELHVS         (0x20)
639 #define RELHV          (0x10)
640 
641 #define SKIPSPRITE     (0x04)
642 
643 #define DUP            (0x02)
644 #define DDOWN          (0x00)
645 #define DLEFT          (0x01)
646 #define DRIGHT         (0x00)
647 
648 
649   coll
650 #define DONTCOLLIDE    (0x20)
651 
652   word next
653   word data
654   word x
655   word y
656   word width
657   word height
658 
659   pixel c0 90 20 0000 datapointer x y 0100 0100 color (8 colorbytes)
660   4 bit direct?
661   datapointer 2 10 0
662   98 (0 colorbytes)
663 
664   box c0 90 20 0000 datapointer x y width height color
665   datapointer 2 10 0
666 
667   c1 98 00 4 bit direct without color bytes (raycast)
668 
669   40 10 20 4 bit rle (sprdemo2)
670 
671   line c1 b0 20 0000 datapointer x y 0100 0100 stretch tilt:x/y color (8 color bytes)
672   or
673   line c0 b0 20 0000 datapointer x y 0100 0100 stretch tilt:x/y color
674   datapointer 2 11 0
675 
676   text ?(04) 90 20 0000 datapointer x y width height color
677   datapointer 2 10 0
678 
679   stretch: hsize adder
680   tilt: hpos adder
681 
682 */
683 
lynx_blitter()684 void lynx_state::lynx_blitter()
685 {
686 	static const int lynx_colors[4] = { 2, 4, 8, 16 };
687 	uint8_t palette_offset;
688 	uint8_t coldep;
689 	int colors;
690 
691 	m_blitter.busy = 1; // blitter working
692 	m_blitter.memory_accesses = 0;
693 
694 	// Last SCB is indicated by zero in the high byte of SCBNEXT
695 	while (m_blitter.scb_next & 0xff00)
696 	{
697 		m_blitter.stretch = 0;
698 		m_blitter.tilt    = 0;
699 		m_blitter.tilt_accumulator = 0;
700 
701 		m_blitter.scb = m_blitter.scb_next; // current scb
702 		m_blitter.scb_next = lynx_read_ram(m_blitter.scb + SCB_SCBNEXT) | (lynx_read_ram(m_blitter.scb + SCB_SCBNEXT + 1) << 8); // next scb
703 		m_blitter.spr_ctl0 = lynx_read_ram(m_blitter.scb + SCB_SPRCTL0);
704 		m_blitter.spr_ctl1 = lynx_read_ram(m_blitter.scb + SCB_SPRCTL1);
705 		m_blitter.spr_coll = lynx_read_ram(m_blitter.scb + SCB_SPRCOLL);
706 		m_blitter.memory_accesses += 5;
707 
708 		if(!(m_blitter.spr_ctl1 & 0x04)) // sprite will be processed (if sprite is skipped first 5 bytes are still copied to suzy)
709 		{
710 			m_blitter.bitmap = lynx_read_ram(m_blitter.scb + SCB_SPRDLINE) | (lynx_read_ram(m_blitter.scb + SCB_SPRDLINE + 1) << 8);
711 			m_blitter.x_pos = lynx_read_ram(m_blitter.scb + SCB_HPOSSTRT) | (lynx_read_ram(m_blitter.scb + SCB_HPOSSTRT + 1) << 8);
712 			m_blitter.y_pos = lynx_read_ram(m_blitter.scb + SCB_VPOSSTRT) | (lynx_read_ram(m_blitter.scb + SCB_VPOSSTRT + 1) << 8);
713 			m_blitter.memory_accesses += 6;
714 
715 			switch(m_blitter.spr_ctl1 & 0x30) // reload sprite scaling
716 			{
717 				case 0x30: // width, height, tilt, stretch
718 					m_blitter.tilt = lynx_read_ram(m_blitter.scb + SCB_TILT) | (lynx_read_ram(m_blitter.scb + SCB_TILT + 1) << 8);
719 					m_blitter.memory_accesses+=2;
720 				case 0x20: // width, height, stretch
721 					m_blitter.stretch = lynx_read_ram(m_blitter.scb + SCB_STRETCH) | (lynx_read_ram(m_blitter.scb + SCB_STRETCH + 1) << 8);
722 					m_blitter.memory_accesses+=2;
723 				case 0x10: // width, height
724 					m_blitter.width = lynx_read_ram(m_blitter.scb + SCB_SPRHSIZ) | (lynx_read_ram(m_blitter.scb + SCB_SPRHSIZ + 1) << 8);
725 					m_blitter.height = lynx_read_ram(m_blitter.scb + SCB_SPRVSIZ) | (lynx_read_ram(m_blitter.scb + SCB_SPRVSIZ + 1) << 8);
726 					m_blitter.memory_accesses+=4;
727 			}
728 
729 			if(!(m_blitter.spr_ctl1 & 0x08)) // reload palette
730 			{
731 				if (m_blitter.spr_ctl1 & 0x30)
732 					palette_offset = 0x0b + 2*(((m_blitter.spr_ctl1 & 0x30)>>4) + 1); // palette data offset depends on width, height, etc. reloading
733 				else
734 					palette_offset = 0x0b;
735 
736 				colors = lynx_colors[m_blitter.spr_ctl0 >> 6];
737 
738 				for (int i = 0; i < colors / 2; i++)
739 				{
740 					m_blitter.color[i * 2]      = lynx_read_ram(m_blitter.scb + palette_offset + i) >> 4;
741 					m_blitter.color[i * 2 + 1 ] = lynx_read_ram(m_blitter.scb + palette_offset + i) & 0x0f;
742 					m_blitter.memory_accesses++;
743 				}
744 			}
745 			}
746 
747 
748 		if (!(m_blitter.spr_ctl1 & 0x04))        // if 0, we skip this sprite
749 		{
750 			m_blitter.colpos = m_blitter.scb + (m_suzy.data[COLLOFFL] | (m_suzy.data[COLLOFFH]<<8));
751 			m_blitter.mode = m_blitter.spr_ctl0 & 0x07;
752 			m_blitter.use_rle = m_blitter.spr_ctl1 & 0x80 ? 0 : 1;
753 			m_blitter.line_color = (m_blitter.spr_ctl0 >> 6) & 0x03;
754 
755 			m_blitter.sprite_collide = !(m_blitter.spr_coll & 0x20);
756 			m_blitter.spritenr = m_blitter.spr_coll & 0x0f;
757 			m_blitter.fred = 0;
758 
759 			/* Draw Sprite */
760 			lynx_blit_lines();
761 
762 			if (m_blitter.sprite_collide && !(m_blitter.no_collide))
763 			{
764 				switch (m_blitter.mode)
765 				{
766 					case BOUNDARY_SHADOW:
767 					case BOUNDARY:
768 					case NORMAL_SPRITE:
769 					case XOR_SPRITE:
770 					case SHADOW:
771 						lynx_write_ram(m_blitter.colpos, m_blitter.fred);
772 						break;
773 				}
774 			}
775 
776 			if (m_suzy.data[SPRGO] & 0x04) // Everon enabled
777 			{
778 				coldep = lynx_read_ram(m_blitter.colpos);
779 				if (!m_blitter.everon)
780 					coldep |= 0x80;
781 				else
782 					coldep &= 0x7f;
783 				lynx_write_ram(m_blitter.colpos, coldep);
784 			}
785 		}
786 	}
787 
788 	timer_set(m_maincpu->cycles_to_attotime(m_blitter.memory_accesses), TIMER_BLITTER);
789 }
790 
791 
792 /****************************************
793 
794     Suzy Emulation
795 
796 ****************************************/
797 
798 
799 /* Math bugs of the original hardware:
800 
801 - in signed multiply, the hardware thinks that 8000 is a positive number
802 - in signed multiply, the hardware thinks that 0 is a negative number. This is not an immediate
803 problem for a multiply by zero, since the answer will be re-negated to the correct polarity of
804 zero. However, since it will set the sign flag, you can not depend on the sign flag to be correct
805 if you just load the lower byte after a multiply by zero.
806 - in divide, the remainder will have 2 possible errors, depending on its actual value (no further
807 notes on these errors available) */
808 
lynx_divide()809 void lynx_state::lynx_divide()
810 {
811 	uint32_t left;
812 	uint16_t right;
813 	uint32_t res, mod;
814 	/*
815 	Hardware divide:
816 	            EFGH
817 	*             NP
818 	----------------
819 	            ABCD
820 	Remainder (JK)LM
821 	*/
822 
823 	left = m_suzy.data[MATH_H] | (m_suzy.data[MATH_G] << 8) | (m_suzy.data[MATH_F] << 16) | (m_suzy.data[MATH_E] << 24);
824 	right = m_suzy.data[MATH_P] | (m_suzy.data[MATH_N] << 8);
825 
826 	m_suzy.accumulate_overflow = false;
827 	if (right == 0)
828 	{
829 		m_suzy.accumulate_overflow = true;  /* during divisions, this bit is used to detect denominator = 0 */
830 		res = 0xffffffff;
831 		mod = 0; //?
832 	}
833 	else
834 	{
835 		res = left / right;
836 		mod = left % right;
837 	}
838 //  logerror("coprocessor %8x / %8x = %4x\n", left, right, res);
839 	m_suzy.data[MATH_D] = res & 0xff;
840 	m_suzy.data[MATH_C] = res >> 8;
841 	m_suzy.data[MATH_B] = res >> 16;
842 	m_suzy.data[MATH_A] = res >> 24;
843 
844 	m_suzy.data[MATH_M] = mod & 0xff;
845 	m_suzy.data[MATH_L] = mod >> 8;
846 	m_suzy.data[MATH_K] = 0; // documentation states the hardware sets these to zero on divides
847 	m_suzy.data[MATH_J] = 0;
848 }
849 
lynx_multiply()850 void lynx_state::lynx_multiply()
851 {
852 	uint16_t left, right;
853 	uint32_t res, accu;
854 	/*
855 	Hardware multiply:
856 	              AB
857 	*             CD
858 	----------------
859 	            EFGH
860 	Accumulate  JKLM
861 	*/
862 	m_suzy.accumulate_overflow = false;
863 
864 	left = m_suzy.data[MATH_B] | (m_suzy.data[MATH_A] << 8);
865 	right = m_suzy.data[MATH_D] | (m_suzy.data[MATH_C] << 8);
866 
867 	res = left * right;
868 
869 	if (m_suzy.signed_math)
870 	{
871 		if (!(m_sign_AB + m_sign_CD))   /* different signs */
872 			res = (res ^ 0xffffffff) + 1;
873 	}
874 
875 	m_suzy.data[MATH_H] = res & 0xff;
876 	m_suzy.data[MATH_G] = res >> 8;
877 	m_suzy.data[MATH_F] = res >> 16;
878 	m_suzy.data[MATH_E] = res >> 24;
879 
880 	if (m_suzy.accumulate)
881 	{
882 		accu = m_suzy.data[MATH_M] | m_suzy.data[MATH_L] << 8 | m_suzy.data[MATH_K] << 16 | m_suzy.data[MATH_J] << 24;
883 		accu += res;
884 
885 		if (accu < res)
886 			m_suzy.accumulate_overflow = true;
887 
888 		m_suzy.data[MATH_M] = accu;
889 		m_suzy.data[MATH_L] = accu >> 8;
890 		m_suzy.data[MATH_K] = accu >> 16;
891 		m_suzy.data[MATH_J] = accu >> 24;
892 	}
893 }
894 
suzy_read(offs_t offset)895 uint8_t lynx_state::suzy_read(offs_t offset)
896 {
897 	uint8_t value = 0, input;
898 
899 	switch (offset)
900 	{
901 		case TILTACUML:
902 			return m_blitter.tilt_accumulator & 0xff;
903 		case TILTACUMH:
904 			return m_blitter.tilt_accumulator>>8;
905 		case HOFFL:
906 			return m_blitter.xoff & 0xff;
907 		case HOFFH:
908 			return m_blitter.xoff>>8;
909 		case VOFFL:
910 			return m_blitter.yoff & 0xff;
911 		case VOFFH:
912 			return m_blitter.yoff>>8;
913 		case VIDBASL:
914 			return m_blitter.screen & 0xff;
915 		case VIDBASH:
916 			return m_blitter.screen>>8;
917 		case COLLBASL:
918 			return m_blitter.colbuf & 0xff;
919 		case COLLBASH:
920 			return m_blitter.colbuf>>8;
921 		case SCBNEXTL:
922 			return m_blitter.scb_next & 0xff;
923 		case SCBNEXTH:
924 			return m_blitter.scb_next>>8;
925 		case SPRDLINEL:
926 			return m_blitter.bitmap & 0xff;
927 		case SPRDLINEH:
928 			return m_blitter.bitmap>>8;
929 		case HPOSSTRTL:
930 			return m_blitter.x_pos & 0xff;
931 		case HPOSSTRTH:
932 			return m_blitter.x_pos>>8;
933 		case VPOSSTRTL:
934 			return m_blitter.y_pos & 0xff;
935 		case VPOSSTRTH:
936 			return m_blitter.y_pos>>8;
937 		case SPRHSIZL:
938 			return m_blitter.width & 0xff;
939 		case SPRHSIZH:
940 			return m_blitter.width>>8;
941 		case SPRVSIZL:
942 			return m_blitter.height & 0xff;
943 		case SPRVSIZH:
944 			return m_blitter.height>>8;
945 		case STRETCHL:
946 			return m_blitter.stretch & 0xff;
947 		case STRETCHH:
948 			return m_blitter.stretch>>8;
949 		case TILTL:
950 			return m_blitter.tilt & 0xff;
951 		case TILTH:
952 			return m_blitter.tilt>>8;
953 		// case SPRDOFFL:
954 		// case SPRVPOSL:
955 		// case COLLOFFL:
956 		case VSIZACUML:
957 			return m_blitter.height_accumulator & 0xff;
958 		case VSIZACUMH:
959 			return m_blitter.height_accumulator>>8;
960 		case HSIZOFFL:
961 			return m_blitter.width_offset & 0xff;
962 		case HSIZOFFH:
963 			return m_blitter.width_offset>>8;
964 		case VSIZOFFL:
965 			return m_blitter.height_offset & 0xff;
966 		case VSIZOFFH:
967 			return m_blitter.height_offset>>8;
968 		case SCBADRL:
969 			return m_blitter.scb & 0xff;
970 		case SCBADRH:
971 			return m_blitter.scb>>8;
972 		//case PROCADRL:
973 		case SUZYHREV:
974 			return 0x01; // must not be 0 for correct power up
975 		case SPRSYS:
976 			// math busy, last carry, unsafe access, and stop on current sprite bits not implemented.
977 			if (m_suzy.accumulate_overflow)
978 				value |= 0x40;
979 			if (m_blitter.vstretch)
980 				value |= 0x10;
981 			if (m_blitter.lefthanded)
982 				value |= 0x08;
983 			if (m_blitter.busy)
984 				value |= 0x01;
985 			break;
986 		case JOYSTICK:
987 			input = ioport("JOY")->read();
988 			switch (m_rotate)
989 			{
990 				case 1:
991 					value = input;
992 					input &= 0x0f;
993 					if (value & PAD_UP) input |= PAD_LEFT;
994 					if (value & PAD_LEFT) input |= PAD_DOWN;
995 					if (value & PAD_DOWN) input |= PAD_RIGHT;
996 					if (value & PAD_RIGHT) input |= PAD_UP;
997 					break;
998 				case 2:
999 					value = input;
1000 					input &= 0x0f;
1001 					if (value & PAD_UP) input |= PAD_RIGHT;
1002 					if (value & PAD_RIGHT) input |= PAD_DOWN;
1003 					if (value & PAD_DOWN) input |= PAD_LEFT;
1004 					if (value & PAD_LEFT) input |= PAD_UP;
1005 					break;
1006 			}
1007 			if (m_blitter.lefthanded)
1008 			{
1009 				value = input & 0x0f;
1010 				if (input & PAD_UP) value |= PAD_DOWN;
1011 				if (input & PAD_DOWN) value |= PAD_UP;
1012 				if (input & PAD_LEFT) value |= PAD_RIGHT;
1013 				if (input & PAD_RIGHT) value |= PAD_LEFT;
1014 			}
1015 			else
1016 				value = input;
1017 			break;
1018 		case SWITCHES:
1019 			value = ioport("PAUSE")->read();
1020 			break;
1021 		case RCART:
1022 			if (m_cart->exists())
1023 				value = m_cart->read_rom((m_suzy.high * m_granularity) + m_suzy.low);
1024 			else
1025 				value = 0;
1026 			m_suzy.low = (m_suzy.low + 1) & (m_granularity - 1);
1027 			break;
1028 		//case RCART_BANK1: /* we need bank 1 emulation!!! */
1029 		case SPRCTL0:
1030 		case SPRCTL1:
1031 		case SPRCOLL:
1032 		case SPRINIT:
1033 		case SUZYBUSEN:
1034 		case SPRGO:
1035 			logerror("read from write only register %x\n", offset);
1036 			value = 0;
1037 			break;
1038 		default:
1039 			value = m_suzy.data[offset];
1040 	}
1041 	//logerror("suzy read %.2x %.2x\n",offset,value);
1042 	return value;
1043 }
1044 
suzy_write(offs_t offset,uint8_t data)1045 void lynx_state::suzy_write(offs_t offset, uint8_t data)
1046 {
1047 	m_suzy.data[offset] = data;
1048 	//logerror("suzy write %.2x %.2x\n",offset,data);
1049 	/* Additional effects of a write */
1050 	/* Even addresses are the LSB. Any CPU write to an LSB in 0x00-0x7f will set the MSB to 0. */
1051 	/* This in particular holds for math quantities:  Writing to B (0x54), D (0x52),
1052 	F (0x62), H (0x60), K (0x6e) or M (0x6c) will force a '0' to be written to A (0x55),
1053 	C (0x53), E (0x63), G (0x61), J (0x6f) or L (0x6d) respectively */
1054 	if ((offset < 0x80) && !(offset & 0x01))
1055 	m_suzy.data[offset + 1] = 0;
1056 
1057 	switch(offset)
1058 	{
1059 		//case TMPADRL:
1060 		//case TMPADRH:
1061 		case TILTACUML:
1062 			m_blitter.tilt_accumulator = data; // upper byte forced to zero see above.
1063 			break;
1064 		case TILTACUMH:
1065 			m_blitter.tilt_accumulator &= 0xff;
1066 			m_blitter.tilt_accumulator |= data<<8;
1067 			break;
1068 		case HOFFL:
1069 			m_blitter.xoff = data;
1070 			break;
1071 		case HOFFH:
1072 			m_blitter.xoff &= 0xff;
1073 			m_blitter.xoff |= data<<8;
1074 			break;
1075 		case VOFFL:
1076 			m_blitter.yoff = data;
1077 			break;
1078 		case VOFFH:
1079 			m_blitter.yoff &= 0xff;
1080 			m_blitter.yoff |= data<<8;
1081 			break;
1082 		case VIDBASL:
1083 			m_blitter.screen = data;
1084 			break;
1085 		case VIDBASH:
1086 			m_blitter.screen &= 0xff;
1087 			m_blitter.screen |= data<<8;
1088 			break;
1089 		case COLLBASL:
1090 			m_blitter.colbuf = data;
1091 			break;
1092 		case COLLBASH:
1093 			m_blitter.colbuf &= 0xff;
1094 			m_blitter.colbuf |= data<<8;
1095 			break;
1096 		case SCBNEXTL:
1097 			m_blitter.scb_next = data;
1098 			break;
1099 		case SCBNEXTH:
1100 			m_blitter.scb_next &= 0xff;
1101 			m_blitter.scb_next |= data<<8;
1102 			break;
1103 		case SPRDLINEL:
1104 			m_blitter.bitmap = data;
1105 			break;
1106 		case SPRDLINEH:
1107 			m_blitter.bitmap &= 0xff;
1108 			m_blitter.bitmap |= data<<8;
1109 			break;
1110 		case HPOSSTRTL:
1111 			m_blitter.x_pos = data;
1112 		case HPOSSTRTH:
1113 			m_blitter.x_pos &= 0xff;
1114 			m_blitter.x_pos |= data<<8;
1115 		case VPOSSTRTL:
1116 			m_blitter.y_pos = data;
1117 		case VPOSSTRTH:
1118 			m_blitter.y_pos &= 0xff;
1119 			m_blitter.y_pos |= data<<8;
1120 		case SPRHSIZL:
1121 			m_blitter.width = data;
1122 			break;
1123 		case SPRHSIZH:
1124 			m_blitter.width &= 0xff;
1125 			m_blitter.width |= data<<8;
1126 			break;
1127 		case SPRVSIZL:
1128 			m_blitter.height = data;
1129 			break;
1130 		case SPRVSIZH:
1131 			m_blitter.height &= 0xff;
1132 			m_blitter.height |= data<<8;
1133 			break;
1134 		case STRETCHL:
1135 			m_blitter.stretch = data;
1136 			break;
1137 		case STRETCHH:
1138 			m_blitter.stretch &= 0xff;
1139 			m_blitter.stretch |= data<<8;
1140 			break;
1141 		case TILTL:
1142 			m_blitter.tilt = data;
1143 			break;
1144 		case TILTH:
1145 			m_blitter.tilt &= 0xff;
1146 			m_blitter.tilt |= data<<8;
1147 			break;
1148 		// case SPRDOFFL:
1149 		// case SPRVPOSL:
1150 		// case COLLOFFL:
1151 		case VSIZACUML:
1152 			m_blitter.height_accumulator = data;
1153 			break;
1154 		case VSIZACUMH:
1155 			m_blitter.height_accumulator &= 0xff;
1156 			m_blitter.height_accumulator |= data<<8;
1157 			break;
1158 		case HSIZOFFL:
1159 			m_blitter.width_offset = data;
1160 			break;
1161 		case HSIZOFFH:
1162 			m_blitter.width_offset &= 0xff;
1163 			m_blitter.width_offset |= data<<8;
1164 			break;
1165 		case VSIZOFFL:
1166 			m_blitter.height_offset = data;
1167 			break;
1168 		case VSIZOFFH:
1169 			m_blitter.height_offset &= 0xff;
1170 			m_blitter.height_offset |= data<<8;
1171 			break;
1172 		case SCBADRL:
1173 			m_blitter.scb = data;
1174 			break;
1175 		case SCBADRH:
1176 			m_blitter.scb &= 0xff;
1177 			m_blitter.scb |= data<<8;
1178 			break;
1179 		//case PROCADRL:
1180 
1181 		/* Writing to M (0x6c) will also clear the accumulator overflow bit */
1182 		case MATH_M:
1183 			m_suzy.accumulate_overflow = false;
1184 			break;
1185 		case MATH_C:
1186 			/* If we are going to perform a signed multiplication, we store the sign and convert the number
1187 			to an unsigned one */
1188 			if (m_suzy.signed_math)
1189 			{
1190 				uint16_t factor, temp;
1191 				factor = m_suzy.data[MATH_D] | (m_suzy.data[MATH_C] << 8);
1192 				if ((factor - 1) & 0x8000)      /* here we use -1 to cover the math bugs on the sign of 0 and 0x8000 */
1193 				{
1194 					temp = (factor ^ 0xffff) + 1;
1195 					m_sign_CD = - 1;
1196 					m_suzy.data[MATH_D] = temp & 0xff;
1197 					m_suzy.data[MATH_C] = temp >> 8;
1198 				}
1199 				else
1200 					m_sign_CD = 1;
1201 			}
1202 			break;
1203 		case MATH_D:
1204 		/* Documentation states that writing to the MATH_D will set MATH_C to zero but not update the sign flag.
1205 		Implementing the sign detection as described in the documentation causes Stun Runners to not work.
1206 		Either the sign error in the docs is not as described or writing to the lower byte does update the sign flag.
1207 		Here I assume the sign flag gets updated. */
1208 			if (data)
1209 				m_sign_CD = 1;
1210 			break;
1211 		/* Writing to A will start a 16 bit multiply */
1212 		/* If we are going to perform a signed multiplication, we also store the sign and convert the
1213 		number to an unsigned one */
1214 		case MATH_A:
1215 			if (m_suzy.signed_math)
1216 			{
1217 				uint16_t factor, temp;
1218 				factor = m_suzy.data[MATH_B] | (m_suzy.data[MATH_A] << 8);
1219 				if ((factor - 1) & 0x8000)      /* here we use -1 to cover the math bugs on the sign of 0 and 0x8000 */
1220 				{
1221 					temp = (factor ^ 0xffff) + 1;
1222 					m_sign_AB = - 1;
1223 					m_suzy.data[MATH_B] = temp & 0xff;
1224 					m_suzy.data[MATH_A] = temp >> 8;
1225 				}
1226 				else
1227 					m_sign_AB = 1;
1228 			}
1229 			lynx_multiply();
1230 			break;
1231 		/* Writing to E will start a 16 bit divide */
1232 		case MATH_E:
1233 			lynx_divide();
1234 			break;
1235 		case SPRCTL0:
1236 			m_blitter.spr_ctl0 = data;
1237 			break;
1238 		case SPRCTL1:
1239 			m_blitter.spr_ctl1 = data;
1240 			break;
1241 		case SPRCOLL:
1242 			m_blitter.spr_coll = data;
1243 			break;
1244 		case SUZYBUSEN:
1245 			logerror("write to SUSYBUSEN %x \n", data);
1246 			break;
1247 		case SPRSYS:
1248 				m_suzy.signed_math = (data & 0x80) ? 1:0;
1249 				m_suzy.accumulate = (data & 0x40) ? 1:0;
1250 				m_blitter.no_collide = (data & 0x20) ? 1:0;
1251 				m_blitter.vstretch = (data & 0x10) ? 1:0;
1252 				m_blitter.lefthanded = (data & 0x08) ? 1:0;
1253 				// unsafe access clear and sprite engine stop request are not enabled
1254 				if (data & 0x02) logerror("sprite engine stop request\n");
1255 				break;
1256 		case SPRGO:
1257 			if ((data & 0x01) && m_suzy.data[SUZYBUSEN])
1258 			{
1259 				//m_blitter.time = machine().time();
1260 				lynx_blitter();
1261 			}
1262 			break;
1263 		case JOYSTICK:
1264 		case SWITCHES:
1265 			logerror("warning: write to read-only button registers\n");
1266 			break;
1267 	}
1268 }
1269 
1270 
1271 /****************************************
1272 
1273     Mikey emulation
1274 
1275 ****************************************/
1276 
1277 /*
1278  0xfd0a r sync signal?
1279  0xfd81 r interrupt source bit 2 vertical refresh
1280  0xfd80 w interrupt quit
1281  0xfd87 w bit 1 !clr bit 0 blocknumber clk
1282  0xfd8b w bit 1 blocknumber hi B
1283  0xfd94 w 0
1284  0xfd95 w 4
1285  0xfda0-f rw farben 0..15
1286  0xfdb0-f rw bit0..3 farben 0..15
1287 */
1288 
1289 
1290 /*
1291 DISPCTL EQU $FD92       ; set to $D by INITMIKEY
1292 
1293 ; B7..B4        0
1294 ; B3    1 EQU color
1295 ; B2    1 EQU 4 bit mode
1296 ; B1    1 EQU flip screen
1297 ; B0    1 EQU video DMA enabled
1298 */
1299 
lynx_draw_line()1300 void lynx_state::lynx_draw_line()
1301 {
1302 	pen_t const *const pen = m_palette->pens();
1303 	uint16_t j; // clipping needed!
1304 
1305 	// calculate y: first three lines are vblank,
1306 	int const y = 101-m_timer[2].counter;
1307 	// Documentation states lower two bits of buffer address are ignored (thus 0xfffc mask)
1308 	j = (m_mikey.disp_addr & 0xfffc) + y * 160 / 2;
1309 
1310 	if (m_mikey.data[0x92] & 0x02)
1311 	{
1312 		j -= 160 * 102 / 2 - 1;
1313 		uint32_t *const line = &m_bitmap_temp.pix(102 - 1 - y);
1314 		for (int x = 160 - 2; x >= 0; j++, x -= 2)
1315 		{
1316 			uint8_t const byte = lynx_read_ram(j);
1317 			line[x + 1] = pen[(byte >> 4) & 0x0f];
1318 			line[x + 0] = pen[(byte >> 0) & 0x0f];
1319 		}
1320 	}
1321 	else
1322 	{
1323 		uint32_t *const line = &m_bitmap_temp.pix(y);
1324 		for (int x = 0; x < 160; j++, x += 2)
1325 		{
1326 			uint8_t const byte = lynx_read_ram(j);
1327 			line[x + 0] = pen[(byte >> 4) & 0x0f];
1328 			line[x + 1] = pen[(byte >> 0) & 0x0f];
1329 		}
1330 	}
1331 }
1332 
1333 /****************************************
1334 
1335     Timers
1336 
1337 ****************************************/
1338 
1339 /*
1340 HCOUNTER        EQU TIMER0
1341 VCOUNTER        EQU TIMER2
1342 SERIALRATE      EQU TIMER4
1343 
1344 TIM_BAKUP       EQU 0   ; backup-value (count+1)
1345 TIM_CNTRL1      EQU 1   ; timer-control register
1346 TIM_CNT EQU 2   ; current counter
1347 TIM_CNTRL2      EQU 3   ; dynamic control
1348 
1349 ; TIM_CNTRL1
1350 TIM_IRQ EQU %10000000   ; enable interrupt (not TIMER4 !)
1351 TIM_RESETDONE   EQU %01000000   ; reset timer done
1352 TIM_MAGMODE     EQU %00100000   ; nonsense in Lynx !!
1353 TIM_RELOAD      EQU %00010000   ; enable reload
1354 TIM_COUNT       EQU %00001000   ; enable counter
1355 TIM_LINK        EQU %00000111
1356 ; link timers (0->2->4 / 1->3->5->7->Aud0->Aud1->Aud2->Aud3->1
1357 TIM_64us        EQU %00000110
1358 TIM_32us        EQU %00000101
1359 TIM_16us        EQU %00000100
1360 TIM_8us EQU %00000011
1361 TIM_4us EQU %00000010
1362 TIM_2us EQU %00000001
1363 TIM_1us EQU %00000000
1364 
1365 ;TIM_CNTRL2 (read-only)
1366 ; B7..B4 unused
1367 TIM_DONE        EQU %00001000   ; set if timer's done; reset with TIM_RESETDONE
1368 TIM_LAST        EQU %00000100   ; last clock (??)
1369 TIM_BORROWIN    EQU %00000010
1370 TIM_BORROWOUT   EQU %00000001
1371 */
1372 
1373 #define NR_LYNX_TIMERS  8
1374 
1375 
1376 
1377 
lynx_timer_init(int which)1378 void lynx_state::lynx_timer_init(int which)
1379 {
1380 	memset(&m_timer[which], 0, sizeof(LYNX_TIMER));
1381 	m_timer[which].timer = timer_alloc(TIMER_SHOT);
1382 
1383 	save_item(NAME(m_timer[which].bakup), which);
1384 	save_item(NAME(m_timer[which].cntrl1), which);
1385 	save_item(NAME(m_timer[which].cntrl2), which);
1386 	save_item(NAME(m_timer[which].counter), which);
1387 	save_item(NAME(m_timer[which].timer_active), which);
1388 }
1389 
lynx_timer_signal_irq(int which)1390 void lynx_state::lynx_timer_signal_irq(int which)
1391 {
1392 	if ((m_timer[which].cntrl1 & 0x80) && (which != 4)) // if interrupts are enabled and timer != 4
1393 	{
1394 		m_mikey.data[0x81] |= (1 << which); // set interrupt poll register
1395 		m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
1396 		m_maincpu->set_input_line(M65SC02_IRQ_LINE, ASSERT_LINE);
1397 	}
1398 	switch (which) // count down linked timers
1399 	{
1400 	case 0:
1401 		switch (m_timer[2].counter)
1402 		{
1403 			case 104:
1404 				break;
1405 			case 103:
1406 				m_mikey.vb_rest = 1;
1407 				break;
1408 			case 102:
1409 				m_mikey.disp_addr = m_mikey.data[0x94] | (m_mikey.data[0x95] << 8);
1410 				break;
1411 			case 101:
1412 				m_mikey.vb_rest = 0;
1413 				lynx_draw_line();
1414 				break;
1415 			default:
1416 				lynx_draw_line();
1417 		}
1418 		lynx_timer_count_down(2);
1419 		break;
1420 	case 2:
1421 		copybitmap(m_bitmap, m_bitmap_temp, 0, 0, 0, 0, m_screen->cliprect());
1422 		lynx_timer_count_down(4);
1423 		break;
1424 	case 1:
1425 		lynx_timer_count_down(3);
1426 		break;
1427 	case 3:
1428 		lynx_timer_count_down(5);
1429 		break;
1430 	case 5:
1431 		lynx_timer_count_down(7);
1432 		break;
1433 	case 7:
1434 		m_sound->count_down(0);
1435 		break;
1436 	}
1437 }
1438 
lynx_timer_count_down(int which)1439 void lynx_state::lynx_timer_count_down(int which)
1440 {
1441 	if ((m_timer[which].cntrl1 & 0x0f) == 0x0f) // count and linking enabled
1442 	{
1443 		if (m_timer[which].counter > 0)
1444 		{
1445 			m_timer[which].counter--;
1446 			//m_timer[which].borrow_in = 1;
1447 			return;
1448 		}
1449 		if (m_timer[which].counter == 0)
1450 		{
1451 			if (m_timer[which].cntrl2 & 0x01) // borrow out
1452 			{
1453 				lynx_timer_signal_irq(which);
1454 				if (m_timer[which].cntrl1 & 0x10) // if reload enabled
1455 				{
1456 					m_timer[which].counter = m_timer[which].bakup;
1457 				}
1458 				else
1459 				{
1460 					m_timer[which].cntrl2 |= 8; // set timer done
1461 				}
1462 				m_timer[which].cntrl2 &= ~0x01; // clear borrow out
1463 			}
1464 			else
1465 				m_timer[which].cntrl2 |= 0x01; // set borrow out
1466 			return;
1467 		}
1468 	}
1469 	else
1470 	{
1471 		//m_timer[which].borrow_in = 0;
1472 		m_timer[which].cntrl2 &= ~0x01;
1473 	}
1474 }
1475 
lynx_time_factor(int val)1476 uint32_t lynx_state::lynx_time_factor(int val)
1477 {
1478 	switch(val)
1479 	{
1480 		case 0: return 1000000;
1481 		case 1: return 500000;
1482 		case 2: return 250000;
1483 		case 3: return 125000;
1484 		case 4: return 62500;
1485 		case 5: return 31250;
1486 		case 6: return 15625;
1487 		default: fatalerror("invalid value\n");
1488 	}
1489 }
1490 
TIMER_CALLBACK_MEMBER(lynx_state::lynx_timer_shot)1491 TIMER_CALLBACK_MEMBER(lynx_state::lynx_timer_shot)
1492 {
1493 	lynx_timer_signal_irq(param);
1494 	if (!(m_timer[param].cntrl1 & 0x10)) // if reload not enabled
1495 	{
1496 		m_timer[param].timer_active = 0;
1497 		m_timer[param].cntrl2 |= 8; // set timer done
1498 	}
1499 	else
1500 	{
1501 		attotime t = (attotime::from_hz(lynx_time_factor(m_timer[param].cntrl1 & 0x07)) * (m_timer[param].bakup + 1));
1502 		m_timer[param].timer->adjust(t, param);
1503 	}
1504 }
1505 
lynx_timer_read(int which,int offset)1506 uint8_t lynx_state::lynx_timer_read(int which, int offset)
1507 {
1508 	uint8_t value = 0;
1509 
1510 	switch (offset)
1511 	{
1512 		case 0:
1513 			value = m_timer[which].bakup;
1514 			break;
1515 		case 1:
1516 			value = m_timer[which].cntrl1;
1517 			break;
1518 		case 2:
1519 			if ((m_timer[which].cntrl1 & 0x07) == 0x07) // linked timer
1520 			{
1521 				value = m_timer[which].counter;
1522 			}
1523 			else
1524 			{
1525 				if ( m_timer[which].timer_active )
1526 				{
1527 					value = (uint8_t) (m_timer[which].timer->remaining().as_ticks(1000000>>(m_timer[which].cntrl1 & 0x07)));
1528 					value -= value ? 1 : 0;
1529 				}
1530 			}
1531 			break;
1532 
1533 		case 3:
1534 			value = m_timer[which].cntrl2;
1535 			break;
1536 	}
1537 	// logerror("timer %d read %x %.2x\n", which, offset, value);
1538 	return value;
1539 }
1540 
lynx_timer_write(int which,int offset,uint8_t data)1541 void lynx_state::lynx_timer_write(int which, int offset, uint8_t data)
1542 {
1543 	//logerror("timer %d write %x %.2x\n", which, offset, data);
1544 	attotime t;
1545 
1546 	if ( m_timer[which].timer_active && ((m_timer[which].cntrl1 & 0x07) != 0x07))
1547 	{
1548 		m_timer[which].counter = (uint8_t) (m_timer[which].timer->remaining().as_ticks(1000000>>(m_timer[which].cntrl1 & 0x07)));
1549 		m_timer[which].counter -= (m_timer[which].counter) ? 1 : 0;
1550 	}
1551 
1552 	switch (offset)
1553 	{
1554 		case 0:
1555 			m_timer[which].bakup = data;
1556 			break;
1557 		case 1:
1558 			m_timer[which].cntrl1 = data;
1559 			if (data & 0x40) // reset timer done
1560 				m_timer[which].cntrl2 &= ~0x08;
1561 			break;
1562 		case 2:
1563 			m_timer[which].counter = data;
1564 			break;
1565 		case 3:
1566 			m_timer[which].cntrl2 = (m_timer[which].cntrl2 & ~0x08) | (data & 0x08);
1567 			break;
1568 	}
1569 
1570 	/* Update timers */
1571 	//if ( offset < 3 )
1572 	//{
1573 		m_timer[which].timer->reset();
1574 		m_timer[which].timer_active = 0;
1575 		if ((m_timer[which].cntrl1 & 0x08) && !(m_timer[which].cntrl2 & 0x08))      // if enable count
1576 		{
1577 			if ((m_timer[which].cntrl1 & 0x07) != 0x07)  // if not set to link mode
1578 			{
1579 				t = (attotime::from_hz(lynx_time_factor(m_timer[which].cntrl1 & 0x07)) * (m_timer[which].counter + 1));
1580 				m_timer[which].timer->adjust(t, which);
1581 				m_timer[which].timer_active = 1;
1582 			}
1583 		}
1584 	//}
1585 }
1586 
1587 
1588 /****************************************
1589 
1590     UART Emulation
1591 
1592 ****************************************/
1593 
1594 
lynx_uart_reset()1595 void lynx_state::lynx_uart_reset()
1596 {
1597 	memset(&m_uart, 0, sizeof(m_uart));
1598 }
1599 
TIMER_CALLBACK_MEMBER(lynx_state::lynx_uart_loopback_timer)1600 TIMER_CALLBACK_MEMBER(lynx_state::lynx_uart_loopback_timer)
1601 {
1602 	m_uart.received = false;
1603 }
1604 
TIMER_CALLBACK_MEMBER(lynx_state::lynx_uart_timer)1605 TIMER_CALLBACK_MEMBER(lynx_state::lynx_uart_timer)
1606 {
1607 	if (m_uart.buffer_loaded)
1608 	{
1609 		m_uart.data_to_send = m_uart.buffer;
1610 		m_uart.buffer_loaded = false;
1611 		timer_set(attotime::from_usec(11*16), TIMER_UART);
1612 	}
1613 	else
1614 	{
1615 		m_uart.sending = false;
1616 		m_uart.received = true;
1617 		m_uart.data_received = m_uart.data_to_send;
1618 		timer_set(attotime::from_usec(11*16), TIMER_UART_LOOPBACK);
1619 		if (m_uart.serctl & 0x40)
1620 		{
1621 			m_mikey.data[0x81] |= 0x10;
1622 			m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
1623 			m_maincpu->set_input_line(M65SC02_IRQ_LINE, ASSERT_LINE);
1624 		}
1625 	}
1626 
1627 	if (m_uart.serctl & 0x80)
1628 	{
1629 		m_mikey.data[0x81] |= 0x10;
1630 		m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
1631 		m_maincpu->set_input_line(M65SC02_IRQ_LINE, ASSERT_LINE);
1632 	}
1633 }
1634 
lynx_uart_r(offs_t offset)1635 uint8_t lynx_state::lynx_uart_r(offs_t offset)
1636 {
1637 	uint8_t value = 0x00;
1638 	switch (offset)
1639 	{
1640 		case 0x8c:
1641 			if (!m_uart.buffer_loaded)
1642 				value |= 0x80;
1643 			if (m_uart.received)
1644 				value |= 0x40;
1645 			if (!m_uart.sending)
1646 				value |= 0x20;
1647 			break;
1648 
1649 		case 0x8d:
1650 			value = m_uart.data_received;
1651 			break;
1652 	}
1653 	logerror("uart read %.2x %.2x\n", offset, value);
1654 	return value;
1655 }
1656 
lynx_uart_w(offs_t offset,uint8_t data)1657 void lynx_state::lynx_uart_w(offs_t offset, uint8_t data)
1658 {
1659 	logerror("uart write %.2x %.2x\n", offset, data);
1660 	switch (offset)
1661 	{
1662 		case 0x8c:
1663 			m_uart.serctl = data;
1664 			break;
1665 
1666 		case 0x8d:
1667 			if (m_uart.sending)
1668 			{
1669 				m_uart.buffer = data;
1670 				m_uart.buffer_loaded = true;
1671 			}
1672 			else
1673 			{
1674 				m_uart.sending = true;
1675 				m_uart.data_to_send = data;
1676 				// timing not accurate, baude rate should be calculated from timer 4 backup value and clock rate
1677 				timer_set(attotime::from_usec(11*16), TIMER_UART);
1678 			}
1679 			break;
1680 	}
1681 }
1682 
1683 
1684 /****************************************
1685 
1686     Mikey memory handlers
1687 
1688 ****************************************/
1689 
1690 
mikey_read(offs_t offset)1691 uint8_t lynx_state::mikey_read(offs_t offset)
1692 {
1693 	uint8_t direction, value = 0x00;
1694 
1695 	switch (offset)
1696 	{
1697 	case 0x00: case 0x01: case 0x02: case 0x03:
1698 	case 0x04: case 0x05: case 0x06: case 0x07:
1699 	case 0x08: case 0x09: case 0x0a: case 0x0b:
1700 	case 0x0c: case 0x0d: case 0x0e: case 0x0f:
1701 	case 0x10: case 0x11: case 0x12: case 0x13:
1702 	case 0x14: case 0x15: case 0x16: case 0x17:
1703 	case 0x18: case 0x19: case 0x1a: case 0x1b:
1704 	case 0x1c: case 0x1d: case 0x1e: case 0x1f:
1705 		value = lynx_timer_read(offset >> 2, offset & 0x03);
1706 		break;
1707 
1708 	case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
1709 	case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
1710 	case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
1711 	case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
1712 	case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x50:
1713 		value = m_sound->read(offset);
1714 		break;
1715 
1716 	case 0x80:
1717 	case 0x81:
1718 		value = m_mikey.data[0x81]; // both registers access the same interrupt status byte
1719 		// logerror( "mikey read %.2x %.2x\n", offset, value );
1720 		break;
1721 
1722 	case 0x84:
1723 	case 0x85:
1724 		value = 0x00;
1725 		break;
1726 
1727 	case 0x86:
1728 		value = 0x80;
1729 		break;
1730 
1731 	case 0x88:
1732 		value = 0x01;
1733 		break;
1734 
1735 	case 0x8b:
1736 		direction = m_mikey.data[0x8a];
1737 		value |= (direction & 0x01) ? (m_mikey.data[offset] & 0x01) : 0x01; // External Power input
1738 		value |= (direction & 0x02) ? (m_mikey.data[offset] & 0x02) : 0x00; // Cart Address Data output (0 turns cart power on)
1739 		value |= (direction & 0x04) ? (m_mikey.data[offset] & 0x04) : 0x04; // noexp input
1740 		// REST read returns actual rest state anded with rest output bit
1741 		value |= (direction & 0x08) ? (((m_mikey.data[offset] & 0x08) && (m_mikey.vb_rest)) ? 0x00 : 0x08) : 0x00;  // rest output
1742 		value |= (direction & 0x10) ? (m_mikey.data[offset] & 0x10) : 0x10; // audin input
1743 		/* Hack: we disable COMLynx  */
1744 		value |= 0x04;
1745 		/* B5, B6 & B7 are not used */
1746 		break;
1747 
1748 	case 0x8c:
1749 	case 0x8d:
1750 		value = lynx_uart_r(offset);
1751 		break;
1752 
1753 	default:
1754 		value = m_mikey.data[offset];
1755 		//logerror( "mikey read %.2x %.2x\n", offset, value );
1756 	}
1757 	return value;
1758 }
1759 
mikey_write(offs_t offset,uint8_t data)1760 void lynx_state::mikey_write(offs_t offset, uint8_t data)
1761 {
1762 	switch (offset)
1763 	{
1764 	case 0x00: case 0x01: case 0x02: case 0x03:
1765 	case 0x04: case 0x05: case 0x06: case 0x07:
1766 	case 0x08: case 0x09: case 0x0a: case 0x0b:
1767 	case 0x0c: case 0x0d: case 0x0e: case 0x0f:
1768 	case 0x10: case 0x11: case 0x12: case 0x13:
1769 	case 0x14: case 0x15: case 0x16: case 0x17:
1770 	case 0x18: case 0x19: case 0x1a: case 0x1b:
1771 	case 0x1c: case 0x1d: case 0x1e: case 0x1f:
1772 		lynx_timer_write(offset >> 2, offset & 3, data);
1773 		return;
1774 
1775 	case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
1776 	case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
1777 	case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
1778 	case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
1779 	case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x50:
1780 		m_sound->write(offset, data);
1781 		return;
1782 
1783 	case 0x80:
1784 		m_mikey.data[0x81] &= ~data; // clear interrupt source
1785 		// logerror("mikey write %.2x %.2x\n", offset, data);
1786 		if (!m_mikey.data[0x81])
1787 			m_maincpu->set_input_line(M65SC02_IRQ_LINE, CLEAR_LINE);
1788 		break;
1789 
1790 	/* Is this correct? */ // Notes say writing to register will result in interrupt being triggered.
1791 	case 0x81:
1792 		m_mikey.data[0x81] |= data;
1793 		if (data)
1794 		{
1795 			m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
1796 			m_maincpu->set_input_line(M65SC02_IRQ_LINE, ASSERT_LINE);
1797 			logerror("direct write to interrupt register\n");
1798 		}
1799 		break;
1800 
1801 	case 0x87:
1802 		m_mikey.data[offset] = data;
1803 		if (data & 0x02)        // Power (1 = on)
1804 		{
1805 			if (data & 0x01)    // Cart Address Strobe
1806 			{
1807 				m_suzy.high <<= 1;
1808 				if (m_mikey.data[0x8b] & 0x02)
1809 					m_suzy.high |= 1;
1810 				m_suzy.high &= 0xff;
1811 				m_suzy.low = 0;
1812 			}
1813 		}
1814 		else
1815 		{
1816 			m_suzy.high = 0;
1817 			m_suzy.low = 0;
1818 		}
1819 		break;
1820 
1821 	case 0x8c: case 0x8d:
1822 		lynx_uart_w(offset, data);
1823 		break;
1824 
1825 	case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
1826 	case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
1827 	case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
1828 	case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
1829 		m_mikey.data[offset] = data;
1830 
1831 		/* RED = 0xb- & 0x0f, GREEN = 0xa- & 0x0f, BLUE = (0xb- & 0xf0) >> 4 */
1832 		m_palette->set_pen_color(offset & 0x0f, rgb_t(
1833 			pal4bit(m_mikey.data[0xb0 | (offset & 0x0f)] & 0x0f),
1834 			pal4bit(m_mikey.data[0xa0 | (offset & 0x0f)] & 0x0f),
1835 			pal4bit((m_mikey.data[0xb0 | (offset & 0x0f)] & 0xf0) >> 4)));
1836 		break;
1837 
1838 	/* TODO: properly implement these writes */
1839 	case 0x8b:
1840 		m_mikey.data[offset] = data;
1841 		if (m_mikey.data[0x8a] & 0x10)
1842 			logerror("Trying to enable bank 1 write. %d\n", m_mikey.data[offset] & 0x10);
1843 		break;
1844 
1845 	//case 0x90: // SDONEACK - Suzy Done Acknowledge
1846 	case 0x91: // CPUSLEEP - CPU Bus Request Disable
1847 		m_mikey.data[offset] = data;
1848 		if (!data && m_blitter.busy)
1849 		{
1850 			m_maincpu->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
1851 			/* A write of '0' to this address will reset the CPU bus request flip flop */
1852 		}
1853 		break;
1854 	case 0x94: case 0x95:
1855 		m_mikey.data[offset]=data;
1856 		break;
1857 	case 0x9c: case 0x9d: case 0x9e:
1858 		m_mikey.data[offset]=data;
1859 		logerror("Mtest%d write: %x\n", offset&0x3, data);
1860 		break;
1861 
1862 	default:
1863 		m_mikey.data[offset]=data;
1864 		//logerror("mikey write %.2x %.2x\n",offset,data);
1865 		break;
1866 	}
1867 }
1868 
1869 /****************************************
1870 
1871     Init / Config
1872 
1873 ****************************************/
1874 
lynx_memory_config_r()1875 uint8_t lynx_state::lynx_memory_config_r()
1876 {
1877 	return m_memory_config;
1878 }
1879 
lynx_memory_config_w(uint8_t data)1880 void lynx_state::lynx_memory_config_w(uint8_t data)
1881 {
1882 	/* bit 7: hispeed, uses page mode accesses (4 instead of 5 cycles )
1883 	 * when these are safe in the cpu */
1884 	m_memory_config = data;
1885 
1886 	m_bank_fc00->set_bank(BIT(data, 0));
1887 	m_bank_fd00->set_bank(BIT(data, 1));
1888 	m_bank_fe00->set_entry(BIT(data, 2));
1889 	m_bank_fffa->set_entry(BIT(data, 3));
1890 }
1891 
machine_reset()1892 void lynx_state::machine_reset()
1893 {
1894 	lynx_memory_config_w(0);
1895 
1896 	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);
1897 	m_maincpu->set_input_line(M65SC02_IRQ_LINE, CLEAR_LINE);
1898 
1899 	memset(&m_suzy, 0, sizeof(m_suzy));
1900 	memset(&m_mikey, 0, sizeof(m_mikey));
1901 
1902 	m_suzy.data[0x88]  = 0x01;
1903 	m_suzy.data[0x90]  = 0x00;
1904 	m_suzy.data[0x91]  = 0x00;
1905 	m_mikey.data[0x80] = 0x00;
1906 	m_mikey.data[0x81] = 0x00;
1907 	m_mikey.data[0x88] = 0x01;
1908 	m_mikey.data[0x8a] = 0x00;
1909 	m_mikey.data[0x8c] = 0x00;
1910 	m_mikey.data[0x90] = 0x00;
1911 	m_mikey.data[0x92] = 0x00;
1912 
1913 	lynx_uart_reset();
1914 
1915 	// hack to allow current object loading to work
1916 #if 0
1917 	lynx_timer_write( this, 0, 0, 160 ); // set backup value (hpos) = 160
1918 	lynx_timer_write( this, 0, 1, 0x10 | 0x8 | 0 ); // enable count, enable reload, 1us period
1919 	lynx_timer_write( this, 2, 0, 105 ); // set backup value (vpos) = 102
1920 	lynx_timer_write( this, 2, 1, 0x10 | 0x8 | 7 ); // enable count, enable reload, link
1921 #endif
1922 
1923 	render_target *target = machine().render().first_target();
1924 	target->set_view(m_rotate);
1925 }
1926 
lynx_postload()1927 void lynx_state::lynx_postload()
1928 {
1929 	lynx_memory_config_w(m_memory_config);
1930 }
1931 
machine_start()1932 void lynx_state::machine_start()
1933 {
1934 	m_bitmap_temp.allocate(160,102,0,0);
1935 
1936 	// save driver variables
1937 	save_item(NAME(m_memory_config));
1938 	save_item(NAME(m_sign_AB));
1939 	save_item(NAME(m_sign_CD));
1940 	save_item(NAME(m_rotate));
1941 	// save blitter variables
1942 	save_item(NAME(m_blitter.screen));
1943 	save_item(NAME(m_blitter.colbuf));
1944 	save_item(NAME(m_blitter.colpos));
1945 	save_item(NAME(m_blitter.xoff));
1946 	save_item(NAME(m_blitter.yoff));
1947 	save_item(NAME(m_blitter.mode));
1948 	save_item(NAME(m_blitter.spr_coll));
1949 	save_item(NAME(m_blitter.spritenr));
1950 	save_item(NAME(m_blitter.x_pos));
1951 	save_item(NAME(m_blitter.y_pos));
1952 	save_item(NAME(m_blitter.width));
1953 	save_item(NAME(m_blitter.height));
1954 	save_item(NAME(m_blitter.tilt_accumulator));
1955 	save_item(NAME(m_blitter.width_accumulator));
1956 	save_item(NAME(m_blitter.height_accumulator));
1957 	save_item(NAME(m_blitter.width_offset));
1958 	save_item(NAME(m_blitter.height_offset));
1959 	save_item(NAME(m_blitter.stretch));
1960 	save_item(NAME(m_blitter.tilt));
1961 	save_item(NAME(m_blitter.color));
1962 	save_item(NAME(m_blitter.bitmap));
1963 	save_item(NAME(m_blitter.use_rle));
1964 	save_item(NAME(m_blitter.line_color));
1965 	save_item(NAME(m_blitter.spr_ctl0));
1966 	save_item(NAME(m_blitter.spr_ctl1));
1967 	save_item(NAME(m_blitter.scb));
1968 	save_item(NAME(m_blitter.scb_next));
1969 	save_item(NAME(m_blitter.sprite_collide));
1970 	save_item(NAME(m_blitter.everon));
1971 	save_item(NAME(m_blitter.fred));
1972 	save_item(NAME(m_blitter.memory_accesses));
1973 	save_item(NAME(m_blitter.no_collide));
1974 	save_item(NAME(m_blitter.vstretch));
1975 	save_item(NAME(m_blitter.lefthanded));
1976 	save_item(NAME(m_blitter.busy));
1977 	// save suzy variables
1978 	save_item(NAME(m_suzy.data));
1979 	save_item(NAME(m_suzy.high));
1980 	save_item(NAME(m_suzy.low));
1981 	save_item(NAME(m_suzy.signed_math));
1982 	save_item(NAME(m_suzy.accumulate));
1983 	save_item(NAME(m_suzy.accumulate_overflow));
1984 	// save mikey variables
1985 	save_item(NAME(m_mikey.data));
1986 	save_item(NAME(m_mikey.disp_addr));
1987 	save_item(NAME(m_mikey.vb_rest));
1988 	// save uart variables
1989 	save_item(NAME(m_uart.serctl));
1990 	save_item(NAME(m_uart.data_received));
1991 	save_item(NAME(m_uart.data_to_send));
1992 	save_item(NAME(m_uart.buffer));
1993 	save_item(NAME(m_uart.received));
1994 	save_item(NAME(m_uart.sending));
1995 	save_item(NAME(m_uart.buffer_loaded));
1996 
1997 	machine().save().register_postload(save_prepost_delegate(FUNC(lynx_state::lynx_postload), this));
1998 
1999 	m_bank_fe00->configure_entry(0, memregion("maincpu")->base() + 0x0000);
2000 	m_bank_fe00->configure_entry(1, m_mem_fe00);
2001 	m_bank_fffa->configure_entry(0, memregion("maincpu")->base() + 0x01fa);
2002 	m_bank_fffa->configure_entry(1, m_mem_fffa);
2003 
2004 	for (int i = 0; i < NR_LYNX_TIMERS; i++)
2005 		lynx_timer_init(i);
2006 }
2007 
2008 
2009 /****************************************
2010 
2011     Image handling
2012 
2013 ****************************************/
2014 
lynx_verify_cart(char * header,int kind)2015 image_verify_result lynx_state::lynx_verify_cart(char *header, int kind)
2016 {
2017 	if (kind)
2018 	{
2019 		if (strncmp("BS93", &header[6], 4))
2020 		{
2021 			logerror("This is not a valid Lynx image\n");
2022 			return image_verify_result::FAIL;
2023 		}
2024 	}
2025 	else
2026 	{
2027 		if (strncmp("LYNX", &header[0], 4))
2028 		{
2029 			if (!strncmp("BS93", &header[6], 4))
2030 			{
2031 				logerror("This image is probably a Quickload image with .lnx extension\n");
2032 				logerror("Try to load it with -quickload\n");
2033 			}
2034 			else
2035 				logerror("This is not a valid Lynx image\n");
2036 			return image_verify_result::FAIL;
2037 		}
2038 	}
2039 
2040 	return image_verify_result::PASS;
2041 }
2042 
DEVICE_IMAGE_LOAD_MEMBER(lynx_state::cart_load)2043 DEVICE_IMAGE_LOAD_MEMBER(lynx_state::cart_load)
2044 {
2045 	/* Lynx carts have 19 address lines, the upper 8 used for bank select. The lower
2046 	11 bits are used to address data within the selected bank. Valid bank sizes are 256,
2047 	512, 1024 or 2048 bytes. Commercial roms use all 256 banks.*/
2048 	uint32_t size = m_cart->common_get_size("rom");
2049 	uint16_t gran = 0;
2050 
2051 	if (!image.loaded_through_softlist())
2052 	{
2053 		// check for lnx header
2054 		if (image.is_filetype("lnx"))
2055 		{
2056 			// 64 byte header
2057 			// LYNX
2058 			// intelword lower counter size
2059 			// 0 0 1 0
2060 			// 32 chars name
2061 			// 22 chars manufacturer
2062 			uint8_t header[0x40];
2063 			image.fread(header, 0x40);
2064 
2065 			// Check the image
2066 			if (lynx_verify_cart((char*)header, LYNX_CART) != image_verify_result::PASS)
2067 				return image_init_result::FAIL;
2068 
2069 			/* 2008-10 FP: According to Handy source these should be page_size_bank0. Are we using
2070 			 it correctly in MESS? Moreover, the next two values should be page_size_bank1. We should
2071 			 implement this as well */
2072 			gran = header[4] | (header[5] << 8);
2073 
2074 			logerror ("%s %dkb cartridge with %dbyte granularity from %s\n", header + 10, size / 1024, gran, header + 42);
2075 			size -= 0x40;
2076 		}
2077 	}
2078 
2079 	m_cart->rom_alloc(size, GENERIC_ROM8_WIDTH, ENDIANNESS_LITTLE);
2080 	m_cart->common_load_rom(m_cart->get_rom_base(), size, "rom");
2081 
2082 	// set-up granularity
2083 	if (!image.loaded_through_softlist())
2084 	{
2085 		if (image.is_filetype("lnx"))     // from header
2086 			m_granularity = gran;
2087 		else if (image.is_filetype("lyx"))
2088 		{
2089 			/* 2008-10 FP: FIXME: .lyx file don't have an header, hence they miss "lynx_granularity"
2090 			(see above). What if bank 0 has to be loaded elsewhere? And what about bank 1?
2091 			These should work with most .lyx files, but we need additional info on raw cart images */
2092 			if (size == 0x20000)
2093 				m_granularity = 0x0200;
2094 			else if (size == 0x80000)
2095 				m_granularity = 0x0800;
2096 			else
2097 				m_granularity = 0x0400;
2098 		}
2099 	}
2100 	else
2101 	{
2102 		if (size > 0xffff) // 64,128,256,512k cartridges
2103 			m_granularity = size >> 8;
2104 		else
2105 			m_granularity = 0x400; // Homebrew roms not using all 256 banks (T-Tris) (none currently in softlist)
2106 	}
2107 
2108 	// set-up rotation from softlist
2109 	if (image.loaded_through_softlist())
2110 	{
2111 		const char *rotate = image.get_feature("rotation");
2112 		m_rotate = 0;
2113 		if (rotate)
2114 		{
2115 			if (!core_stricmp(rotate, "RIGHT"))
2116 				m_rotate = 1;
2117 			else if (!core_stricmp(rotate, "LEFT"))
2118 				m_rotate = 2;
2119 		}
2120 
2121 	}
2122 
2123 	return image_init_result::PASS;
2124 }
2125