1 // license:BSD-3-Clause
2 // copyright-holders:Jean-Francois DEL NERO
3
4 /*********************************************************************
5
6 ef9365.c
7
8 Thomson EF9365/EF9366 video controller emulator code
9
10 The EF9365/EF9366 is a video controller driving a frame buffer
11 and having built-in vectors and characters drawing engines.
12 This is natively a "black and white" chip (1 bitplane),
13 but it is possible to add more bitplanes to have colors with a
14 hardware trick. The system don't have direct access to the video
15 memory, but indirect access is possible through the 0x0F command
16 and some hardware glue logics.
17 The current implementation emulate the main functions :
18
19 Video modes supported (Hardware implementation dependent):
20 - 256 x 256 (EF9365 with 4 bits shifters per bitplane and FMAT to VSS)
21 - 512 x 512 interlaced (EF9365 with 8 bits shifters per bitplane and FMAT to VCC)
22 - 512 x 256 non interlaced (EF9366 with 8 bits shifters per bitplane)
23 - 128 x 128 (EF9365 with 2 bits shifters per bitplane and FMAT to VSS)
24 - 64 x 64 (EF9365 with FMAT to VSS)
25
26 - 1 bitplane up to 8 bitplanes hardware configuration.
27 - 2 up to 256 colors fixed palette.
28
29 Character & block drawing :
30 - Normal / Titled mode
31 - Horizontal / Vertical orientation
32 - P & Q Zoom factors (1 up to 16)
33
34 Vector drawing :
35 - Normal / Dotted / Dashed / Dotted-Dashed mode
36 - All directions and size supported.
37
38 General :
39 - Clear Screen
40 - Fill Screen
41 - Clear X & Y registers
42 - Video RAM readback supported (Command 0x0F)
43
44 What is NOT yet currently implemented:
45 - Light pen support
46 (To be done when i will find a software using the lightpen)
47
48 What is implemented but not really tested:
49 - Interrupts output.
50 My target system (Squale Apollo 7) doesn't use the interruption
51 for this chip. So i add the interrupt line support, but
52 bug(s) is possible.
53
54 To see how to use this driver, have a look to the Squale machine
55 driver (squale.cpp).
56 If you have any question, don't hesitate to contact me at the email
57 present on this website : http://hxc2001.free.fr/
58
59 12/29/2015
60 Jean-Francois DEL NERO
61 *********************************************************************/
62
63 #include "emu.h"
64 #include "ef9365.h"
65
66 #include "screen.h"
67
68 //#define VERBOSE 1
69 #include "logmacro.h"
70
71
72 namespace {
73
74 #define EF936X_REG_STATUS 0x00
75 #define EF936X_REG_CMD 0x00
76 #define EF936X_REG_CTRL1 0x01
77 #define EF936X_REG_CTRL2 0x02
78 #define EF936X_REG_CSIZE 0x03
79 #define EF936X_REG_DELTAX 0x05
80 #define EF936X_REG_DELTAY 0x07
81 #define EF936X_REG_X_MSB 0x08
82 #define EF936X_REG_X_LSB 0x09
83 #define EF936X_REG_Y_MSB 0x0A
84 #define EF936X_REG_Y_LSB 0x0B
85 #define EF936X_REG_XLP 0x0C
86 #define EF936X_REG_YLP 0x0D
87
88
89 //-------------------------------------------------
90 // Some debug mode const strings
91 // to trace the commands and registers accesses.
92 //-------------------------------------------------
93
94 // Registers list
95 const char *const register_names[]=
96 {
97 "0x00 - CMD / STATUS",
98 "0x01 - CTRL 1 ",
99 "0x02 - CTRL 2 ",
100 "0x03 - CSIZE ",
101 "0x04 - RESERVED ",
102 "0x05 - DELTA X ",
103 "0x06 - RESERVED ",
104 "0x07 - DELTA Y ",
105 "0x08 - X MSBs ",
106 "0x09 - X LSBs ",
107 "0x0A - Y MSBs ",
108 "0x0B - Y LSBs ",
109 "0x0C - XLP ",
110 "0x0D - YLP ",
111 "0x0E - RESERVED ",
112 "0x0F - RESERVED "
113 };
114
115 // Commands list
116 const char *const commands_names[]=
117 {
118 "0x00 - Set bit 1 of CTRL1 : Pen selection",
119 "0x01 - Clear bit 1 of CTRL1 : Eraser selection",
120 "0x02 - Set bit 0 of CTRL1 : Pen/Eraser down selection",
121 "0x03 - Clear bit 0 of CTRL1 : Pen/Eraser up selection",
122 "0x04 - Clear screen",
123 "0x05 - X and Y registers reset to 0",
124 "0x06 - X and Y registers reset to 0 and clear screen",
125 "0x07 - Clear screen, set CSIZE to code \"minsize\". All other registers reset to 0",
126 "0x08 - Light-pen initialization (/White forced low)",
127 "0x09 - Light-pen initialization",
128 "0x0A - 5x8 block drawing (size according to CSIZE)",
129 "0x0B - 4x4 block drawing (size according to CSIZE)",
130 "0x0C - Screen scanning : pen or Eraser as defined by CTRL1",
131 "0x0D - X reset to 0",
132 "0x0E - Y reset to 0",
133 "0x0F - Direct image memory access request for the next free cycle.",
134 "0x10<>0x17 - Vector generation",
135 "0x18<>0x1F - Special direction vectors",
136 "0x20<>0x7F - Character Drawing",
137 "0x80<>0xFF - Small vector generation",
138 };
139
140 } // anonymous namespace
141
142
143 // devices
144 DEFINE_DEVICE_TYPE(EF9365, ef9365_device, "ef9365", "Thomson EF9365")
145
ROM_START(ef9365)146 ROM_START( ef9365 )
147 ROM_REGION( 0x1E0, "ef9365", 0 )
148 ROM_LOAD( "charset_ef9365.rom", 0x0000, 0x01E0, CRC(8d3053be) SHA1(0f9a64d217a0f7f04ee0720d49c5b680ad0ae359) )
149 ROM_END
150
151 //-------------------------------------------------
152 // rom_region - return a pointer to the device's
153 // internal ROM region
154 //-------------------------------------------------
155
156 const tiny_rom_entry *ef9365_device::device_rom_region() const
157 {
158 return ROM_NAME( ef9365 );
159 }
160
161 //-------------------------------------------------
162 // default address map
163 // Up to 512*512 per bitplane, 8 bitplanes max.
164 //-------------------------------------------------
ef9365(address_map & map)165 void ef9365_device::ef9365(address_map &map)
166 {
167 if (!has_configured_map(0))
168 map(0x00000, ef9365_device::BITPLANE_MAX_SIZE * ef9365_device::MAX_BITPLANES - 1).ram();
169 }
170
171 //-------------------------------------------------
172 // memory_space_config - return a description of
173 // any address spaces owned by this device
174 //-------------------------------------------------
175
memory_space_config() const176 device_memory_interface::space_config_vector ef9365_device::memory_space_config() const
177 {
178 return space_config_vector {
179 std::make_pair(0, &m_space_config)
180 };
181 }
182
183 //**************************************************************************
184 // INLINE HELPERS
185 //**************************************************************************
186
187
188 //**************************************************************************
189 // live device
190 //**************************************************************************
191
192 //-------------------------------------------------
193 // ef9365_device - constructor
194 //-------------------------------------------------
195
ef9365_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)196 ef9365_device::ef9365_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
197 device_t(mconfig, EF9365, tag, owner, clock),
198 device_memory_interface(mconfig, *this),
199 device_video_interface(mconfig, *this),
200 m_space_config("videoram", ENDIANNESS_LITTLE, 8, 18, 0, address_map_constructor(FUNC(ef9365_device::ef9365), this)),
201 m_charset(*this, "ef9365"),
202 m_palette(*this, finder_base::DUMMY_TAG),
203 m_irq_handler(*this)
204 {
205 clock_freq = clock;
206 }
207
208 //-------------------------------------------------
209 // set_nb_of_bitplanes: Set the number of bitplanes
210 //-------------------------------------------------
211
set_nb_bitplanes(int nb_bitplanes)212 void ef9365_device::set_nb_bitplanes(int nb_bitplanes)
213 {
214 if( nb_bitplanes > 0 && nb_bitplanes <= 8 )
215 {
216 nb_of_bitplanes = nb_bitplanes;
217 nb_of_colors = pow(2, nb_bitplanes);
218 }
219 }
220
221 //-------------------------------------------------
222 // set_display_mode: Set the display mode
223 //-------------------------------------------------
224
set_display_mode(int display_mode)225 void ef9365_device::set_display_mode(int display_mode)
226 {
227 switch(display_mode)
228 {
229 case DISPLAY_MODE_256x256:
230 bitplane_xres = 256;
231 bitplane_yres = 256;
232 vsync_scanline_pos = 250;
233 overflow_mask_x = 0xFF00;
234 overflow_mask_y = 0xFF00;
235 break;
236 case DISPLAY_MODE_512x512:
237 bitplane_xres = 512;
238 bitplane_yres = 512;
239 vsync_scanline_pos = 506;
240 overflow_mask_x = 0xFE00;
241 overflow_mask_y = 0xFE00;
242 break;
243 case DISPLAY_MODE_512x256:
244 bitplane_xres = 512;
245 bitplane_yres = 256;
246 vsync_scanline_pos = 250;
247 overflow_mask_x = 0xFE00;
248 overflow_mask_y = 0xFF00;
249 break;
250 case DISPLAY_MODE_128x128:
251 bitplane_xres = 128;
252 bitplane_yres = 128;
253 vsync_scanline_pos = 124;
254 overflow_mask_x = 0xFF80;
255 overflow_mask_y = 0xFF80;
256 break;
257 case DISPLAY_MODE_64x64:
258 bitplane_xres = 64;
259 bitplane_yres = 64;
260 vsync_scanline_pos = 62;
261 overflow_mask_x = 0xFFC0;
262 overflow_mask_y = 0xFFC0;
263 break;
264 default:
265 logerror("Invalid EF9365 Display mode: %02x\n", display_mode);
266 bitplane_xres = 256;
267 bitplane_yres = 256;
268 vsync_scanline_pos = 250;
269 overflow_mask_x = 0xFF00;
270 overflow_mask_y = 0xFF00;
271 break;
272 }
273 }
274
275 //-------------------------------------------------
276 // set_color_entry: Set the color value
277 // into the palette
278 //-------------------------------------------------
279
set_color_entry(int index,uint8_t r,uint8_t g,uint8_t b)280 void ef9365_device::set_color_entry( int index, uint8_t r, uint8_t g, uint8_t b )
281 {
282 if( index < nb_of_colors )
283 {
284 m_palette->set_pen_color(index, rgb_t(r, g, b));
285 }
286 else
287 {
288 logerror("Invalid EF9365 Palette entry : %02x\n", index);
289 }
290 }
291
292 //-------------------------------------------------
293 // set_color_filler: Set the color number
294 // used by the chip to draw/fill the memory
295 //-------------------------------------------------
296
set_color_filler(uint8_t color)297 void ef9365_device::set_color_filler( uint8_t color )
298 {
299 m_current_color = color;
300 }
301
302 //-------------------------------------------------
303 // device_start - device-specific startup
304 //-------------------------------------------------
305
device_start()306 void ef9365_device::device_start()
307 {
308 m_irq_handler.resolve_safe();
309
310 m_busy_timer = timer_alloc(BUSY_TIMER);
311
312 m_videoram = &space(0);
313 m_current_color = 0x00;
314
315 m_irq_vb = 0;
316 m_irq_lb = 0;
317 m_irq_rdy = 0;
318 m_irq_state = 0;
319
320 m_screen_out.allocate( bitplane_xres, screen().height() );
321
322 save_item(NAME(m_border));
323 save_item(NAME(m_registers));
324 save_item(NAME(m_bf));
325 save_item(NAME(m_state));
326
327 save_item(NAME(m_irq_state));
328 save_item(NAME(m_irq_vb));
329 save_item(NAME(m_irq_lb));
330 save_item(NAME(m_irq_rdy));
331
332 save_item(NAME(m_screen_out));
333 }
334
335 //-------------------------------------------------
336 // device_reset - device-specific reset
337 //-------------------------------------------------
338
device_reset()339 void ef9365_device::device_reset()
340 {
341 m_state = 0;
342
343 m_bf = 0;
344 m_irq_state = 0;
345 m_irq_vb = 0;
346 m_irq_lb = 0;
347 m_irq_rdy = 0;
348
349 memset(m_registers, 0, sizeof(m_registers));
350 memset(m_border, 0, sizeof(m_border));
351
352 m_screen_out.fill(0);
353
354 set_video_mode();
355 screen_scanning(1);
356
357 m_irq_handler(false);
358 }
359
360 //-------------------------------------------------
361 // update_interrupts
362 //-------------------------------------------------
update_interrupts()363 void ef9365_device::update_interrupts()
364 {
365 int new_state = ( m_irq_vb && (m_registers[EF936X_REG_CTRL1] & 0x20) )
366 || ( m_irq_rdy && (m_registers[EF936X_REG_CTRL1] & 0x40) )
367 || ( m_irq_lb && (m_registers[EF936X_REG_CTRL1] & 0x10) );
368
369 if (new_state != m_irq_state)
370 {
371 m_irq_state = new_state;
372 m_irq_handler(m_irq_state);
373 }
374 }
375
376 //-------------------------------------------------
377 // device_timer - handler timer events
378 //-------------------------------------------------
379
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)380 void ef9365_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
381 {
382 switch(id)
383 {
384 case BUSY_TIMER:
385 m_bf = 0;
386
387 if( m_registers[EF936X_REG_CTRL1] & 0x40 )
388 {
389 m_irq_rdy = 1;
390 }
391
392 update_interrupts();
393
394 break;
395 }
396 }
397
398 //-------------------------------------------------
399 // set_busy_flag: set busy flag and
400 // timer to clear it
401 //-------------------------------------------------
402
set_busy_flag(int period)403 void ef9365_device::set_busy_flag(int period)
404 {
405 m_bf = 1;
406 m_busy_timer->adjust(attotime::from_usec(period));
407 }
408
409 //-------------------------------------------------
410 // get_x_reg: Get the X register value
411 //-------------------------------------------------
412
get_x_reg()413 uint16_t ef9365_device::get_x_reg()
414 {
415 return ((m_registers[EF936X_REG_X_MSB] & 0x0F)<<8) | m_registers[EF936X_REG_X_LSB];
416 }
417
418 //-------------------------------------------------
419 // get_y_reg: Get the Y register value
420 //-------------------------------------------------
421
get_y_reg()422 uint16_t ef9365_device::get_y_reg()
423 {
424 return ((m_registers[EF936X_REG_Y_MSB] & 0x0F)<<8) | m_registers[EF936X_REG_Y_LSB];
425 }
426
427 //-------------------------------------------------
428 // set_x_reg: Set the X register value
429 //-------------------------------------------------
430
set_x_reg(uint16_t x)431 void ef9365_device::set_x_reg(uint16_t x)
432 {
433 m_registers[EF936X_REG_X_MSB] = ( x >> 8 ) & 0x0F;
434 m_registers[EF936X_REG_X_LSB] = x & 0xFF;
435 }
436
437 //-------------------------------------------------
438 // set_y_reg: Set the Y register value
439 //-------------------------------------------------
440
set_y_reg(uint16_t y)441 void ef9365_device::set_y_reg(uint16_t y)
442 {
443 m_registers[EF936X_REG_Y_MSB] = ( y >> 8 ) & 0x0F;
444 m_registers[EF936X_REG_Y_LSB] = y & 0xFF;
445 }
446
447 //-------------------------------------------------
448 // set_video_mode: Set output screen format
449 //-------------------------------------------------
450
set_video_mode(void)451 void ef9365_device::set_video_mode(void)
452 {
453 uint16_t new_width = bitplane_xres;
454
455 if (screen().width() != new_width)
456 {
457 rectangle visarea = screen().visible_area();
458 visarea.max_x = new_width - 1;
459
460 screen().configure(new_width, screen().height(), visarea, screen().frame_period().attoseconds());
461 }
462
463 //border color
464 memset(m_border, 0, sizeof(m_border));
465 }
466
467 //-------------------------------------------------
468 // get_last_readback_word: Read back the latched
469 // bitplane words
470 //-------------------------------------------------
471
get_last_readback_word(int bitplane_number,int * pixel_offset)472 uint8_t ef9365_device::get_last_readback_word(int bitplane_number, int * pixel_offset)
473 {
474 if( pixel_offset )
475 *pixel_offset = m_readback_latch_pix_offset;
476
477 if( bitplane_number < nb_of_bitplanes )
478 {
479 return m_readback_latch[bitplane_number];
480 }
481 else
482 {
483 return 0x00;
484 }
485 }
486
487 //-------------------------------------------------
488 // draw_border: Draw the left and right borders
489 // ( No border for the moment ;) )
490 //-------------------------------------------------
491
draw_border(uint16_t line)492 void ef9365_device::draw_border(uint16_t line)
493 {
494 }
495
496 //-------------------------------------------------
497 // plot: Plot a pixel to the bitplanes
498 // at the x & y position with the m_current_color color
499 //-------------------------------------------------
500
plot(int x_pos,int y_pos)501 void ef9365_device::plot(int x_pos,int y_pos)
502 {
503 int p;
504
505 if( ( x_pos >= 0 && y_pos >= 0 ) && ( x_pos < bitplane_xres && y_pos < bitplane_yres ) )
506 {
507 if ( m_registers[EF936X_REG_CTRL1] & 0x01 )
508 {
509 y_pos = ( (bitplane_yres - 1) - y_pos );
510
511 if( (m_registers[EF936X_REG_CTRL1] & 0x02) )
512 {
513 // Pen
514 for( p = 0 ; p < nb_of_bitplanes ; p++ )
515 {
516 if( m_current_color & (0x01 << p) )
517 m_videoram->write_byte ( (BITPLANE_MAX_SIZE*p) + (((y_pos*bitplane_xres) + x_pos)>>3), m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (((y_pos*bitplane_xres) + x_pos)>>3)) | (0x80 >> (((y_pos*bitplane_xres) + x_pos)&7) ) );
518 else
519 m_videoram->write_byte ( (BITPLANE_MAX_SIZE*p) + (((y_pos*bitplane_xres) + x_pos)>>3), m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (((y_pos*bitplane_xres) + x_pos)>>3)) & ~(0x80 >> (((y_pos*bitplane_xres) + x_pos)&7) ) );
520 }
521 }
522 else
523 {
524 // Eraser
525 for( p = 0 ; p < nb_of_bitplanes ; p++ )
526 {
527 m_videoram->write_byte ( (BITPLANE_MAX_SIZE*p) + (((y_pos*bitplane_xres) + x_pos)>>3), m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (((y_pos*bitplane_xres) + x_pos)>>3)) | (0x80 >> (((y_pos*bitplane_xres) + x_pos)&7) ) );
528 }
529 }
530 }
531 }
532 }
533
534
535 const static unsigned int vectortype_code[][8] =
536 {
537 {0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // Continuous drawing
538 {0x82,0x02,0x00,0x00,0x00,0x00,0x00,0x00}, // Dotted - 2 dots on, 2 dots off
539 {0x84,0x04,0x00,0x00,0x00,0x00,0x00,0x00}, // Dashed - 4 dots on, 4 dots off
540 {0x8A,0x02,0x82,0x02,0x00,0x00,0x00,0x00} // Dotted-Dashed - 10 dots on, 2 dots off, 2 dots on, 2 dots off
541 };
542
543 //-------------------------------------------------
544 // draw_vector: Vector drawing function
545 // from the start_x & start_y position to the start_x+delta_x & start_y+delta_y position
546 // with the m_current_color color
547 // (Bresenham's line algorithm)
548 //-------------------------------------------------
549
draw_vector(uint16_t start_x,uint16_t start_y,short delta_x,short delta_y)550 int ef9365_device::draw_vector(uint16_t start_x,uint16_t start_y,short delta_x,short delta_y)
551 {
552 int dx;
553 int dy,t;
554 int e;
555 int x,y,dest_x,dest_y,end_x,end_y;
556 int incy;
557 int diago,horiz;
558 unsigned char c1;
559
560 int pen_state;
561 unsigned int state_counter;
562 int dot_code_ptr;
563 int compute_cycles;
564
565 LOG("EF9365 draw_vector : Start=(%d,%d) End=(%d,%d)\n", start_x,start_y,start_x+delta_x,start_y+delta_y);
566
567 compute_cycles = 0;
568
569 dest_x = start_x + delta_x;
570 dest_y = start_y + delta_y;
571
572 end_x = dest_x;
573 end_y = dest_y;
574
575 c1=0;
576 incy=1;
577
578 dot_code_ptr = 0;
579 state_counter = vectortype_code[m_registers[EF936X_REG_CTRL2] & 0x3][dot_code_ptr&7];
580 if(state_counter&0x80)
581 pen_state = 1;
582 else
583 pen_state = 0;
584 state_counter &= ~0x80;
585
586 if( dest_x > start_x )
587 dx = dest_x - start_x;
588 else
589 dx = start_x - dest_x;
590
591 if( dest_y > start_y )
592 dy = dest_y - start_y;
593 else
594 dy = start_y - dest_y;
595
596 if( dy > dx )
597 {
598 t = dest_y;
599 dest_y = dest_x;
600 dest_x = t;
601
602 t = start_y;
603 start_y = start_x;
604 start_x = t;
605
606 t = dx;
607 dx = dy;
608 dy = t;
609
610 c1 = 1;
611 }
612
613 if( start_x > dest_x )
614 {
615 t = dest_y;
616 dest_y = start_y;
617 start_y = t;
618
619 t = start_x;
620 start_x = dest_x;
621 dest_x = t;
622 }
623
624 horiz = dy<<1;
625 diago = ( dy - dx )<<1;
626 e = ( dy<<1 ) - dx;
627
628 if( start_y <= dest_y )
629 incy = 1;
630 else
631 incy = -1;
632
633 x = start_x;
634 y = start_y;
635
636 if(c1)
637 {
638 do
639 {
640 if(pen_state)
641 plot(y % bitplane_xres, x % bitplane_yres);
642
643 compute_cycles++;
644
645 set_x_reg(y);
646 set_y_reg(x);
647
648 state_counter--;
649
650 if( !state_counter )
651 {
652 dot_code_ptr++;
653
654 state_counter = vectortype_code[m_registers[EF936X_REG_CTRL2] & 0x3][dot_code_ptr&7];
655
656 if(!state_counter)
657 {
658 dot_code_ptr = 0;
659 state_counter = vectortype_code[m_registers[EF936X_REG_CTRL2] & 0x3][dot_code_ptr&7];
660 }
661
662 if( state_counter & 0x80 )
663 {
664 pen_state = 1;
665 }
666 else
667 {
668 pen_state = 0;
669 }
670
671 state_counter &= ~0x80;
672 }
673
674 if( e > 0 )
675 {
676 y = y + incy;
677 e = e + diago;
678 }
679 else
680 {
681 e = e + horiz;
682 }
683
684 x++;
685
686 } while (x <= dest_x);
687 }
688 else
689 {
690 do
691 {
692 if(pen_state)
693 plot(x % bitplane_xres, y % bitplane_yres);
694
695 compute_cycles++;
696
697 set_x_reg(x);
698 set_y_reg(y);
699
700 state_counter--;
701
702 if( !state_counter )
703 {
704 dot_code_ptr++;
705
706 state_counter = vectortype_code[m_registers[EF936X_REG_CTRL2] & 0x3][dot_code_ptr&7];
707
708 if(!state_counter)
709 {
710 dot_code_ptr = 0;
711 state_counter = vectortype_code[m_registers[EF936X_REG_CTRL2] & 0x3][dot_code_ptr&7];
712 }
713
714 if( state_counter & 0x80 )
715 {
716 pen_state = 1;
717 }
718 else
719 {
720 pen_state = 0;
721 }
722
723 state_counter &= ~0x80;
724 }
725
726 if( e > 0 )
727 {
728 y = y + incy;
729 e = e + diago;
730 }
731 else
732 {
733 e = e + horiz;
734 }
735
736 x++;
737
738 } while (x <= dest_x);
739 }
740
741 set_x_reg(end_x);
742 set_y_reg(end_y);
743
744 return compute_cycles;
745 }
746
747 //-------------------------------------------------
748 // get_char_pix: Get a character pixel state
749 // from the charset.
750 //-------------------------------------------------
751
get_char_pix(unsigned char c,int x,int y)752 int ef9365_device::get_char_pix( unsigned char c, int x, int y )
753 {
754 int char_base,char_pix;
755
756 if(c<96)
757 {
758 if( x < 5 && y < 8 )
759 {
760 char_base = c * 5;
761 char_pix = ( y * 5 ) + x;
762
763 if ( m_charset[char_base + (char_pix>>3)] & ( 0x80 >> (char_pix&7)) )
764 return 1;
765 else
766 return 0;
767 }
768 }
769
770 return 0;
771 }
772
773 //-------------------------------------------------
774 // draw_character: Character and block drawing function
775 // Set smallblock to draw a 4x4 block
776 // Set block to draw a 5x8 block
777 //-------------------------------------------------
778
draw_character(unsigned char c,int block,int smallblock)779 int ef9365_device::draw_character( unsigned char c, int block, int smallblock )
780 {
781 int x_char,y_char;
782 unsigned int x, y;
783 int x_char_res,y_char_res;
784 int p_factor,q_factor,p,q;
785 int compute_cycles;
786
787 x = get_x_reg();
788 y = get_y_reg();
789
790 x_char_res = 5;
791 y_char_res = 8;
792
793 if( smallblock )
794 {
795 block = 1;
796 x_char_res = 4;
797 y_char_res = 4;
798 }
799
800 p_factor = (m_registers[EF936X_REG_CSIZE] >> 4);
801 if(!p_factor)
802 p_factor = 16;
803
804 q_factor = (m_registers[EF936X_REG_CSIZE] & 0xF);
805 if(!q_factor)
806 q_factor = 16;
807
808 compute_cycles = ( ( x_char_res + 1 ) * p_factor ) * ( y_char_res * q_factor );
809
810 if(c<96)
811 {
812 for( x_char=0 ; x_char < x_char_res ; x_char++ )
813 {
814 for( y_char = y_char_res - 1 ; y_char >= 0 ; y_char-- )
815 {
816 if ( block || get_char_pix( c, x_char, ( (y_char_res - 1) - y_char ) ) )
817 {
818 if( m_registers[EF936X_REG_CTRL2] & 0x04) // Tilted character
819 {
820 for(q = 0; q < q_factor; q++)
821 {
822 for(p = 0; p < p_factor; p++)
823 {
824 if( !(m_registers[EF936X_REG_CTRL2] & 0x08) )
825 { // Tilted - Horizontal orientation
826 plot(
827 x + ( (y_char*q_factor) + q ) + ( (x_char*p_factor) + p ),
828 y + ( (y_char*q_factor) + q )
829 );
830 }
831 else
832 { // Tilted - Vertical orientation
833 plot(
834 x - ( (y_char*q_factor)+ q ),
835 y + ( (x_char*p_factor)+ p ) - ( ( ( (y_char_res - 1 ) - y_char) * q_factor ) + ( q_factor - q ) )
836 );
837 }
838 }
839 }
840 }
841 else
842 {
843 for(q = 0; q < q_factor; q++)
844 {
845 for(p = 0; p < p_factor; p++)
846 {
847 if( !(m_registers[EF936X_REG_CTRL2] & 0x08) )
848 { // Normal - Horizontal orientation
849 plot(
850 x + ( (x_char*p_factor) + p ),
851 y + ( (y_char*q_factor) + q )
852 );
853 }
854 else
855 { // Normal - Vertical orientation
856 plot(
857 x - ( (y_char*q_factor) + q ),
858 y + ( (x_char*p_factor) + p )
859 );
860 }
861 }
862 }
863 }
864 }
865 }
866 }
867
868 if(!(m_registers[EF936X_REG_CTRL2] & 0x08))
869 {
870 x = x + ( (x_char_res + 1 ) * p_factor ) ;
871 set_x_reg(x);
872 }
873 else
874 {
875 y = y + ( (x_char_res + 1 ) * p_factor ) ;
876 set_y_reg(y);
877 }
878 }
879
880 return compute_cycles;
881 }
882
883 //-------------------------------------------------
884 // cycles_to_us: Convert a number of clock cycles to us
885 //-------------------------------------------------
886
cycles_to_us(int cycles)887 int ef9365_device::cycles_to_us(int cycles)
888 {
889 return ( (float)cycles * ( (float)1000000 / (float)clock_freq ) );
890 }
891
892 //-------------------------------------------------
893 // dump_bitplanes_word: Latch the bitplane words
894 // pointed by the x & y registers
895 // (Memory read back function)
896 //-------------------------------------------------
897
dump_bitplanes_word()898 void ef9365_device::dump_bitplanes_word()
899 {
900 int p;
901 int pixel_ptr;
902
903 pixel_ptr = ( ( ( ( bitplane_yres - 1 ) - ( get_y_reg() & ( bitplane_yres - 1 ) ) ) * bitplane_xres ) + ( get_x_reg() & ( bitplane_xres - 1 ) ) );
904
905 LOG("dump : x = %d , y = %d\n", get_x_reg() ,get_y_reg());
906
907 for( p = 0; p < nb_of_bitplanes ; p++ )
908 {
909 if( pixel_ptr & 0x4 )
910 {
911 m_readback_latch[p] = ( m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (pixel_ptr>>3) ) ) & 0xF ;
912 }
913 else
914 {
915 m_readback_latch[p] = ( m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (pixel_ptr>>3) ) >> 4 ) & 0xF ;
916 }
917
918 }
919
920 m_readback_latch_pix_offset = pixel_ptr & 0x3;
921 }
922
923 //-------------------------------------------------
924 // screen_scanning: Fill / Clear framebuffer memory
925 //-------------------------------------------------
926
screen_scanning(int force_clear)927 void ef9365_device::screen_scanning( int force_clear )
928 {
929 int x,y,p;
930
931 if( (m_registers[EF936X_REG_CTRL1] & 0x02) && !force_clear )
932 {
933 for( y = 0; y < bitplane_yres; y++ )
934 {
935 for( x = 0; x < bitplane_xres; x++ )
936 {
937 for( p = 0 ; p < nb_of_bitplanes ; p++ )
938 {
939 if( m_current_color & (0x01 << p) )
940 m_videoram->write_byte ( (BITPLANE_MAX_SIZE*p) + (((y*bitplane_xres) + x)>>3), m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (((y*bitplane_xres) + x)>>3)) | (0x80 >> (((y*bitplane_xres) + x)&7) ) );
941 else
942 m_videoram->write_byte ( (BITPLANE_MAX_SIZE*p) + (((y*bitplane_xres) + x)>>3), m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (((y*bitplane_xres) + x)>>3)) & ~(0x80 >> (((y*bitplane_xres) + x)&7) ) );
943 }
944 }
945 }
946 }
947 else
948 {
949 for( y = 0; y < bitplane_yres; y++)
950 {
951 for( x = 0; x < bitplane_xres; x++)
952 {
953 for( p = 0 ; p < nb_of_bitplanes ; p++ )
954 {
955 m_videoram->write_byte ( (BITPLANE_MAX_SIZE*p) + (((y*bitplane_xres) + x)>>3), m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (((y*bitplane_xres) + x)>>3)) | (0x80 >> (((y*bitplane_xres) + x)&7) ) );
956 }
957 }
958 }
959 }
960 }
961
962 //-------------------------------------------------
963 // ef9365_exec: EF936X Command decoder and execution
964 //-------------------------------------------------
965
ef9365_exec(uint8_t cmd)966 void ef9365_device::ef9365_exec(uint8_t cmd)
967 {
968 int tmp_delta_x,tmp_delta_y;
969 int busy_cycles = 0;
970 m_state = 0;
971
972 if( ( cmd>>4 ) == 0 )
973 {
974 LOG("EF9365 Command : %s\n", commands_names[cmd & 0xF]);
975
976 switch(cmd & 0xF)
977 {
978 case 0x0: // Set bit 1 of CTRL1 : Pen Selection
979 m_registers[EF936X_REG_CTRL1] |= 0x02;
980 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
981 break;
982 case 0x1: // Clear bit 1 of CTRL1 : Eraser Selection
983 m_registers[EF936X_REG_CTRL1] &= (~0x02);
984 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
985 break;
986 case 0x2: // Set bit 0 of CTRL1 : Pen/Eraser down selection
987 m_registers[EF936X_REG_CTRL1] |= 0x01;
988 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
989 break;
990 case 0x3: // Clear bit 0 of CTRL1 : Pen/Eraser up selection
991 m_registers[EF936X_REG_CTRL1] &= (~0x01);
992 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
993 break;
994 case 0x4: // Clear screen
995 screen_scanning(1);
996 set_busy_flag( cycles_to_us( bitplane_xres*bitplane_yres ) ); // Timing to check on the real hardware
997 break;
998 case 0x5: // X and Y registers reset to 0
999 set_x_reg(0);
1000 set_y_reg(0);
1001 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
1002 break;
1003 case 0x6: // X and Y registers reset to 0 and clear screen
1004 set_x_reg(0);
1005 set_y_reg(0);
1006 screen_scanning(1);
1007 set_busy_flag( cycles_to_us( bitplane_xres*bitplane_yres ) ); // Timing to check on the real hardware
1008 break;
1009 case 0x7: // Clear screen, set CSIZE to code "minsize". All other registers reset to 0
1010 m_registers[EF936X_REG_CSIZE] = 0x11;
1011 screen_scanning(1);
1012 set_busy_flag( cycles_to_us( bitplane_xres*bitplane_yres ) ); // Timing to check on the real hardware
1013 break;
1014 case 0x8: // Light-pen initialization (/White forced low)
1015 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
1016 break;
1017 case 0x9: // Light-pen initialization
1018 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
1019 break;
1020 case 0xA: // 5x8 block drawing (size according to CSIZE)
1021 busy_cycles = draw_character( 0x00 , 1 , 0 );
1022 set_busy_flag( cycles_to_us( busy_cycles ) );
1023 break;
1024 case 0xB: // 4x4 block drawing (size according to CSIZE)
1025 busy_cycles = draw_character( 0x00 , 1 , 1 );
1026 set_busy_flag( cycles_to_us( busy_cycles ) );
1027 break;
1028 case 0xC: // Screen scanning : pen or Eraser as defined by CTRL1
1029 screen_scanning(0);
1030 set_busy_flag( cycles_to_us( bitplane_xres*bitplane_yres ) ); // Timing to check on the real hardware
1031 break;
1032 case 0xD: // X reset to 0
1033 set_x_reg(0);
1034 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
1035 break;
1036 case 0xE: // Y reset to 0
1037 set_y_reg(0);
1038 set_busy_flag( cycles_to_us( 4 ) ); // Timing to check on the real hardware
1039 break;
1040 case 0xF: // Direct image memory access request for the next free cycle.
1041 set_busy_flag( cycles_to_us( 64 ) ); // Timing to check on the real hardware
1042 dump_bitplanes_word();
1043 break;
1044 default:
1045 logerror("Unemulated EF9365 cmd: %02x\n", cmd);
1046 }
1047 }
1048 else
1049 {
1050 if ( ( cmd>>4 ) == 1 )
1051 {
1052 if( cmd & 0x08 )
1053 LOG("EF9365 Command : [0x%.2X] %s\n", cmd, commands_names[0x11]);
1054 else
1055 LOG("EF9365 Command : [0x%.2X] %s\n", cmd, commands_names[0x10]);
1056
1057 tmp_delta_x = m_registers[EF936X_REG_DELTAX];
1058 tmp_delta_y = m_registers[EF936X_REG_DELTAY];
1059
1060 if( cmd & 0x08 )
1061 {
1062 if(tmp_delta_x > tmp_delta_y )
1063 tmp_delta_y = tmp_delta_x;
1064 if(tmp_delta_y > tmp_delta_x )
1065 tmp_delta_y = tmp_delta_x;
1066 }
1067
1068 // Vector / Special direction vector generation
1069 switch ( cmd & 0x7 ) // Direction code
1070 {
1071 case 0x1:
1072 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), tmp_delta_x, tmp_delta_y );
1073 break;
1074 case 0x3:
1075 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), -tmp_delta_x, tmp_delta_y );
1076 break;
1077 case 0x5:
1078 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), tmp_delta_x, -tmp_delta_y );
1079 break;
1080 case 0x7:
1081 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), -tmp_delta_x, -tmp_delta_y );
1082 break;
1083
1084 case 0x0:
1085 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), tmp_delta_x, 0 );
1086 break;
1087 case 0x2:
1088 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), 0, tmp_delta_y );
1089 break;
1090 case 0x4:
1091 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), 0, -tmp_delta_y );
1092 break;
1093 case 0x6:
1094 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), -tmp_delta_x , 0 );
1095 break;
1096 }
1097 set_busy_flag( cycles_to_us( busy_cycles ) );
1098 }
1099 else
1100 {
1101 if( ( cmd>>4 ) >= 0x8 )
1102 {
1103 LOG("EF9365 Command : [0x%.2X] %s\n", cmd, commands_names[0x13]);
1104
1105 tmp_delta_x = ( cmd >> 5 ) & 3;
1106 tmp_delta_y = ( cmd >> 3 ) & 3;
1107
1108 // Small vector.
1109 switch ( cmd & 0x7 ) // Direction code
1110 {
1111 case 0x1:
1112 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), tmp_delta_x, tmp_delta_y );
1113 break;
1114 case 0x3:
1115 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), -tmp_delta_x, tmp_delta_y );
1116 break;
1117 case 0x5:
1118 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), tmp_delta_x, -tmp_delta_y );
1119 break;
1120 case 0x7:
1121 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), -tmp_delta_x, -tmp_delta_y );
1122 break;
1123
1124 case 0x0:
1125 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), tmp_delta_x, 0 );
1126 break;
1127 case 0x2:
1128 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), 0, tmp_delta_y );
1129 break;
1130 case 0x4:
1131 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), 0, -tmp_delta_y );
1132 break;
1133 case 0x6:
1134 busy_cycles = draw_vector ( get_x_reg(), get_y_reg(), -tmp_delta_x, 0 );
1135 break;
1136 }
1137
1138 set_busy_flag( cycles_to_us( busy_cycles ) );
1139 }
1140 else
1141 {
1142 // Draw character
1143
1144 LOG("EF9365 Command : [0x%.2X] %s\n", cmd, commands_names[0x12]);
1145
1146 busy_cycles = draw_character( cmd - 0x20, 0 , 0 );
1147 set_busy_flag( cycles_to_us( busy_cycles ) );
1148 }
1149 }
1150 }
1151 }
1152
1153 //-------------------------------------------------
1154 // screen_update: Framebuffer video output
1155 //-------------------------------------------------
1156
screen_update(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)1157 uint32_t ef9365_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
1158 {
1159 for(int j=0;j<bitplane_yres;j++)
1160 {
1161 for(int i=0;i<bitplane_xres;i++)
1162 {
1163 unsigned char color_index = 0x00;
1164
1165 int ptr = ( bitplane_xres * j ) + i;
1166
1167 for(int p = 0; p < nb_of_bitplanes; p++)
1168 {
1169 if( m_videoram->read_byte( (BITPLANE_MAX_SIZE*p) + (ptr>>3)) & (0x80>>(ptr&7)))
1170 {
1171 color_index |= (0x01<<p);
1172 }
1173 }
1174
1175 m_screen_out.pix(j, i) = m_palette->pen( color_index );
1176 }
1177 }
1178
1179 copybitmap(bitmap, m_screen_out, 0, 0, 0, 0, cliprect);
1180 return 0;
1181 }
1182
1183 //-------------------------------------------------
1184 // update_scanline: Scanline callback
1185 //-------------------------------------------------
1186
update_scanline(uint16_t scanline)1187 void ef9365_device::update_scanline(uint16_t scanline)
1188 {
1189 if (scanline == vsync_scanline_pos)
1190 {
1191 m_state |= (0x02); // vsync
1192 if( m_registers[EF936X_REG_CTRL1] & 0x20 )
1193 {
1194 m_irq_vb = 1;
1195 }
1196
1197 update_interrupts();
1198 }
1199
1200 if (scanline == 0)
1201 {
1202 m_state &= (~0x02);
1203 draw_border(0);
1204 }
1205 }
1206
1207 //-------------------------------------------------
1208 // data_r: Registers read access callback
1209 //-------------------------------------------------
1210
data_r(offs_t offset)1211 uint8_t ef9365_device::data_r(offs_t offset)
1212 {
1213 unsigned char return_value;
1214
1215 switch(offset & 0xF)
1216 {
1217 case EF936X_REG_STATUS:
1218 if (m_bf)
1219 m_state &= (~0x04);
1220 else
1221 m_state |= 0x04;
1222
1223 if ( ( overflow_mask_x & get_x_reg() ) || ( overflow_mask_y & get_y_reg() ) )
1224 {
1225 m_state |= 0x08;
1226 }
1227
1228 if( m_irq_vb || m_irq_lb || m_irq_rdy )
1229 {
1230 m_state |= 0x80;
1231 }
1232
1233 if( m_irq_lb )
1234 {
1235 m_state |= 0x10;
1236 m_irq_lb = 0;
1237 }
1238
1239 if( m_irq_vb )
1240 {
1241 m_state |= 0x20;
1242 m_irq_vb = 0;
1243 }
1244
1245 if( m_irq_rdy )
1246 {
1247 m_state |= 0x40;
1248 m_irq_rdy = 0;
1249 }
1250
1251 update_interrupts();
1252
1253 return_value = m_state;
1254 break;
1255 case EF936X_REG_CTRL1:
1256 return_value = m_registers[EF936X_REG_CTRL1] & 0x7F;
1257 break;
1258 case EF936X_REG_CTRL2:
1259 return_value = m_registers[EF936X_REG_CTRL2] & 0x0F;
1260 break;
1261 case EF936X_REG_CSIZE:
1262 return_value = m_registers[EF936X_REG_CSIZE];
1263 break;
1264 case EF936X_REG_DELTAX:
1265 return_value = m_registers[EF936X_REG_DELTAX];
1266 break;
1267 case EF936X_REG_DELTAY:
1268 return_value = m_registers[EF936X_REG_DELTAY];
1269 break;
1270 case EF936X_REG_X_MSB:
1271 return_value = m_registers[EF936X_REG_X_MSB] & 0x0F;
1272 break;
1273 case EF936X_REG_X_LSB:
1274 return_value = m_registers[EF936X_REG_X_LSB];
1275 break;
1276 case EF936X_REG_Y_MSB:
1277 return_value = m_registers[EF936X_REG_Y_MSB] & 0x0F;
1278 break;
1279 case EF936X_REG_Y_LSB:
1280 return_value = m_registers[EF936X_REG_Y_LSB];
1281 break;
1282 case EF936X_REG_XLP:
1283 return_value = m_registers[EF936X_REG_XLP] & 0xFD;
1284 break;
1285 case EF936X_REG_YLP:
1286 return_value = m_registers[EF936X_REG_YLP];
1287 break;
1288 default:
1289 return_value = 0xFF;
1290 break;
1291 }
1292
1293 LOG("EF9365 [ %s ] RD> [ 0x%.2X ] - %s\n", register_names[offset&0xF],return_value, machine().describe_context() );
1294
1295 return return_value;
1296 }
1297
1298 //-------------------------------------------------
1299 // data_w: Registers write access callback
1300 //-------------------------------------------------
1301
data_w(offs_t offset,uint8_t data)1302 void ef9365_device::data_w(offs_t offset, uint8_t data)
1303 {
1304 LOG("EF9365 [ %s ] <WR [ 0x%.2X ] - %s\n", register_names[offset&0xF],data, machine().describe_context() );
1305
1306 switch(offset & 0xF)
1307 {
1308 case EF936X_REG_CMD:
1309 ef9365_exec( data & 0xff);
1310 break;
1311 case EF936X_REG_CTRL1:
1312 m_registers[EF936X_REG_CTRL1] = data & 0x7F;
1313 break;
1314 case EF936X_REG_CTRL2:
1315 m_registers[EF936X_REG_CTRL2] = data & 0x0F;
1316 break;
1317 case EF936X_REG_CSIZE:
1318 m_registers[EF936X_REG_CSIZE] = data;
1319 break;
1320 case EF936X_REG_DELTAX:
1321 m_registers[EF936X_REG_DELTAX] = data;
1322 break;
1323 case EF936X_REG_DELTAY:
1324 m_registers[EF936X_REG_DELTAY] = data;
1325 break;
1326 case EF936X_REG_X_MSB:
1327 m_registers[EF936X_REG_X_MSB] = data & 0x0F;
1328 break;
1329 case EF936X_REG_X_LSB:
1330 m_registers[EF936X_REG_X_LSB] = data;
1331 break;
1332 case EF936X_REG_Y_MSB:
1333 m_registers[EF936X_REG_Y_MSB] = data & 0x0F;
1334 break;
1335 case EF936X_REG_Y_LSB:
1336 m_registers[EF936X_REG_Y_LSB] = data;
1337 break;
1338 case EF936X_REG_XLP:
1339 break;
1340 case EF936X_REG_YLP:
1341 break;
1342 default:
1343 break;
1344 }
1345 }
1346