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