1 /*
2  *  KCemu -- The emulator for the KC85 homecomputer series and much more.
3  *  Copyright (C) 1997-2010 Torsten Paul
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <ctype.h>
21 #include <stdio.h>
22 
23 #include "kc/system.h"
24 
25 #include "kc/kc.h"
26 #include "kc/z80.h"
27 #include "kc/gdc.h"
28 
29 #include "libdbg/dbg.h"
30 
31 using namespace std;
32 
GDC(void)33 GDC::GDC(void) : InterfaceCircuit("VIS")
34 {
35   reset(true);
36   z80->register_ic(this);
37 }
38 
~GDC(void)39 GDC::~GDC(void)
40 {
41   z80->unregister_ic(this);
42 }
43 
44 byte_t
in(word_t addr)45 GDC::in(word_t addr)
46 {
47   byte_t val = 0xff;
48 
49   switch (addr & 0x01)
50     {
51     case 0x00:
52       /*
53        *  status register
54        *
55        *  bit 0 (0x01): 0 not ready, 1 ready ? [17a1h]
56        *  bit 1 (0x02): 0 ready, 1 not ready ? [1fa7h]
57        *  bit 2 (0x04): 0 not ready, 1 ready ? [1797h]
58        *  bit 5 (0x20): 1 while vertical retrace
59        */
60       val = _control;
61       break;
62     case 0x01:
63       if ((_cmd & 0xe4) == 0xa0)
64 	{
65 	  if ((_ridx & 1) == 0)
66 	    val = _mem[_ptr];
67 	  else
68 	    {
69 	      val = _col[_ptr];
70 	      _ptr = (_ptr + 1) & 0xffff;
71 	    }
72 	  //printf("RDAT: %4d, ptr = %04x, val = %02x\n", _ridx, _ptr, val);
73 	}
74       else if (_cmd == 0xe0)
75 	{
76 	  switch (_ridx)
77 	    {
78 	    case 0: val = _ptr & 0xff; break;
79 	    case 1: val = (_ptr >> 8) & 0xff; break;
80 	    default:
81 	      val = 0;
82 	      break;
83 	    }
84 	  //printf("CURD: %4d, ptr = %04x, val = %02x\n", _ridx, _ptr, val);
85 	}
86       else
87 	val = _mem[_ptr];
88 
89       _ridx++;
90       DBG(2, form("KCemu/GDC/in_data",
91 		  "GDC::in():  %04xh cmd = %02x, val = %02x\n",
92 		  addr, _cmd, val));
93     default:
94       break;
95     }
96 
97   DBG(2, form("KCemu/GDC/in",
98               "GDC::in():  %04xh val = %02x\n",
99               addr, val));
100 
101   return val;
102 }
103 
104 void
out(word_t addr,byte_t val)105 GDC::out(word_t addr, byte_t val)
106 {
107   switch (addr & 0x01)
108     {
109     case 0x00:
110       _arg[_idx++] = val;
111       break;
112     case 0x01:
113       info();
114       _idx = 0;
115       _ridx = 0;
116       _cmd = val;
117       break;
118     }
119 
120   DBG(2, form("KCemu/GDC/out",
121               "GDC::out(): %04xh: %04xh val = %02x [%c]\n",
122               z80->getPC(), addr, val, isprint(val) ? val : '.'));
123 
124   if (_cmd == 0x00) // RESET
125     _screen_on = 0;
126 
127   if ((_cmd & 0xfe) == 0x0e) // SYNC
128     _screen_on = _cmd & 1;
129 
130   if ((_cmd & 0xfe) == 0x6e) // VSYNC
131     ;
132 
133   if ((_cmd == 0x4b) && (_idx == 3)) // CCHAR
134     {
135       _nr_of_lines = _arg[0] & 0x1f;
136       _cursor_top = _arg[1] & 0x1f;
137       _cursor_bottom = (_arg[2] & 0xf8) >> 3;
138     }
139 
140   if (_cmd == 0x6b) // START
141     ;
142 
143   if ((_cmd & 0xfe) == 0x0c) // BCTRL
144     _screen_on = _cmd & 1;
145 
146   if (_cmd == 0x46) // ZOOM
147     ;
148 
149   if (_cmd == 0x49) // CURS
150     {
151       if (_idx == 2)
152 	{
153 	  _ptr = ((int)_arg[1] * 256 + _arg[0]);
154 	  _pptr = 0;
155 	}
156       else if (_idx == 3)
157 	{
158 	  _ptr = (_ptr & 0xffff) | ((_arg[2] & 0x03) << 16);
159 	  _pptr = (_arg[2] >> 4) & 0x0f;
160 	  _mask = 1 << _pptr;
161 	  _mask_c = _mask >> 8;
162 	}
163     }
164 
165   if (((_cmd & 0xf0) == 0x70) && (_idx > 0)) // PRAM
166     {
167       int idx = (_cmd & 0x0f) + _idx - 1;
168       if (idx < 16)
169 	_pram[idx] = val;
170     }
171 
172   if (_cmd == 0x47) // PITCH
173     ;
174 
175   if (((_cmd & 0xe4) == 0x20) && (_idx > 0)) // WDAT
176     {
177       switch (_cmd & 0x18)
178 	{
179 	case 0x00: /* type 00 - two byte transfer */
180 	  if (_idx & 1)
181 	    _mem[_ptr] = (_mask & val) | (~_mask & _mem[_ptr]);
182 	  else
183 	    {
184 	      _col[_ptr] = (_mask_c & val) | (~_mask_c & _col[_ptr]);
185 	      _ptr = (_ptr + 1) & 0xffff;
186 	      while ((_idx == 2) && _figs_dc > 0)
187 		{
188 		  _mem[_ptr] = (_mask & _arg[0]) | (~_mask & _mem[_ptr]);
189 		  _col[_ptr] = (_mask_c & _arg[1]) | (~_mask_c & _col[_ptr]);
190 		  _ptr = (_ptr + 1) & 0xffff;
191 		  _figs_dc--;
192 		}
193 	    }
194 	  break;
195 	case 0x08: /* type 01 - invalid */
196 	  break;
197 	case 0x10: /* type 10 - low byte of word */
198 	  _mem[_ptr] = (_mask & val) | (~_mask & _mem[_ptr]);
199 	  _ptr = (_ptr + 1) & 0xffff;
200 	  break;
201 	case 0x18: /* type 11 - high byte of word */
202 	  _col[_ptr] = (_mask_c & val) | (~_mask_c & _col[_ptr]);
203 	  _ptr = (_ptr + 1) & 0xffff;
204 	  break;
205 	}
206     }
207 
208   if ((_cmd == 0x4a) && (_idx > 0)) // MASK
209     {
210       if (_idx == 1)
211 	{
212 	  /*
213 	   *  the z1013 gdc driver only loads the lower byte
214 	   *  into the mask register :-(
215 	   */
216 	  _mask = _arg[0];
217 	}
218       else
219 	{
220 	  _mask = _arg[0] | (_arg[1] << 8);
221 	  _mask_c = _arg[1];
222 	}
223     }
224 
225   if ((_cmd == 0x4c) && (_idx > 2)) // FIGS
226     _figs_dc = _arg[1] | ((_arg[2] & 0x3f) << 8);
227 
228   if (_cmd == 0x6c) // FIGD
229     ;
230 
231   if (_cmd == 0x68) // GCHRD
232     ;
233 
234   if ((_cmd & 0xe4) == 0xa0) // RDAT
235     _figs_dc = 0;
236 
237   if (_cmd == 0xe0) // CURD
238     ;
239 
240   if (_cmd == 0xc0) // LPRD
241     ;
242 
243   if ((_cmd & 0xe4) == 0xa4) // DMAR
244     ;
245 
246   if ((_cmd & 0xe4) == 0x24) // DMAW
247     ;
248 }
249 
250 byte_t
get_mem(int addr)251 GDC::get_mem(int addr)
252 {
253   return _mem[addr & 0xffff];
254 }
255 
256 byte_t
get_col(int addr)257 GDC::get_col(int addr)
258 {
259   return _col[addr & 0xffff];
260 }
261 
262 bool
get_cursor(int addr)263 GDC::get_cursor(int addr)
264 {
265   if (addr != _ptr)
266     return false;
267 
268   return true;
269 }
270 
271 bool
get_cursor(int addr,int line)272 GDC::get_cursor(int addr, int line)
273 {
274   if (addr != _ptr)
275     return false;
276 
277   if (line < _cursor_top)
278     return false;
279 
280   if (line > _cursor_bottom)
281     return false;
282 
283   return true;
284 }
285 
286 int
get_nr_of_lines(void)287 GDC::get_nr_of_lines(void)
288 {
289   return _nr_of_lines;
290 }
291 
292 int
get_screen_on(void)293 GDC::get_screen_on(void)
294 {
295   return _screen_on;
296 }
297 
298 long
get_pram_SAD(int idx)299 GDC::get_pram_SAD(int idx)
300 {
301   if ((idx < 0) || (idx > 3))
302     return 0;
303 
304   return _pram[4 * idx] | (_pram[4 * idx + 1] << 8) | ((_pram[4 * idx + 2] & 3) << 16);
305 }
306 
307 long
get_pram_LEN(int idx)308 GDC::get_pram_LEN(int idx)
309 {
310   if ((idx < 0) || (idx > 3))
311     return 0;
312 
313   return ((_pram[4 * idx + 2] & 0xf0) >> 4) | ((_pram[4 * idx + 3] & 0x3f) << 4);
314 }
315 
316 void
v_retrace(bool value)317 GDC::v_retrace(bool value)
318 {
319   if (value)
320     _control |= 0x20;
321   else
322     _control &= ~0x20;
323 }
324 
325 void
info(void)326 GDC::info(void)
327 {
328   switch (_cmd)
329     {
330     case 0x00:
331       DBG(2, form("KCemu/GDC/RESET",
332 		  "GDC: RESET ------------------------------- %02x\n"
333 		  "GDC: RESET mode                  %s\n"
334 		  "GDC: RESET -------------------------------\n",
335 		  _cmd,
336 		  ((_arg[0] & 0x20) ?
337 		   ((_arg[0] & 0x02) ? "  invalid" : "character") :
338 		   ((_arg[0] & 0x02) ? "  graphic": "    mixed"))
339                   ));
340       break;
341 
342     case 0x0e: case 0x0f:
343       DBG(2, form("KCemu/GDC/SYNC",
344 		  "GDC: SYNC -------------------------------- %02x\n"
345 		  "GDC: SYNC display on/off               %s\n"
346 		  "GDC: SYNC mode (C/G)       %s (%d/%d)\n"
347 		  "GDC: SYNC retrace (I/S)      %s (%d/%d)\n"
348 		  "GDC: SYNC refresh (D)                  %s\n"
349 		  "GDC: SYNC drawing (F)         %s\n"
350 		  "GDC: SYNC --------------------------------\n",
351 		  _cmd,
352 		  (_cmd & 1) ? " on" : "off",
353 
354 		  ((_arg[0] & 0x20) ?
355 		   ((_arg[0] & 0x02) ? "  invalid" : "character") :
356 		   ((_arg[0] & 0x02) ? "  graphic" : "    mixed")),
357                   (_arg[0] & 0x20) >> 5, // C
358 		  (_arg[0] & 0x02) >> 1, // G
359 
360 		  ((_arg[0] & 0x08) ?
361 		   ((_arg[0] & 0x01) ? "zeilensprung" : "doppelbild") :
362 		   ((_arg[0] & 0x01) ? "invalid" : "kein zeilensprung")),
363 		  (_arg[0] & 0x08) >> 3, // I
364 		  (_arg[0] & 0x01),      // S
365 
366 		  ((_arg[0] & 0x04) ? "yes" : " no"),
367 
368 		  ((_arg[0] & 0x10) ? "retrace only" : "      always")
369 		  ));
370       break;
371 
372     case 0x6e: case 0x6f:
373       DBG(2, form("KCemu/GDC/VSYNC",
374 		  "GDC: VSYNC ------------------------------- %02x\n"
375 		  "GDC: VSYNC external sync            %s\n"
376 		  "GDC: VSYNC -------------------------------\n",
377 		  _cmd,
378 		  (_cmd & 1) ? "master" : " slave"));
379       break;
380 
381     case 0x4b:
382       DBG(2, form("KCemu/GDC/CCHAR",
383 		  "GDC: CCHAR ------------------------------- %02x\n"
384 		  "GDC: CCHAR display cursor              %s\n"
385 		  "GDC: CCHAR number of lines           %5d\n"
386 		  "GDC: CCHAR cursor top                %5d\n"
387 		  "GDC: CCHAR cursor bottom             %5d\n"
388 		  "GDC: CCHAR steady cursor               %s\n"
389 		  "GDC: CCHAR cursor blink frequency    %5d\n"
390 		  "GDC: CCHAR -------------------------------\n",
391 		  _cmd,
392 		  (_arg[0] & 0x80) ? " on" : "off",
393 		  _nr_of_lines,
394 		  _cursor_top,
395 		  _cursor_bottom,
396 		  (_arg[1] & 0x20) ? " on" : "off",
397 		  ((_arg[1] & 0xc0) >> 6) | ((_arg[2] & 0x07) << 2)));
398       break;
399 
400     case 0x6b:
401       DBG(2, form("KCemu/GDC/START",
402 		  "GDC: START ------------------------------- %02x\n"
403 		  "GDC: START -------------------------------\n",
404 		  _cmd));
405       break;
406 
407     case 0x0c: case 0x0d:
408       DBG(2, form("KCemu/GDC/BCTRL",
409 		  "GDC: BCTRL ------------------------------- %02x\n"
410 		  "GDC: BCTRL display on/off              %s\n"
411 		  "GDC: BCTRL -------------------------------\n",
412 		  _cmd,
413 		  (_cmd & 1) ? " on" : "off"));
414       break;
415 
416     case 0x46:
417       DBG(2, form("KCemu/GDC/ZOOM",
418 		  "GDC: ZOOM -------------------------------- %02x\n"
419 		  "GDC: ZOOM --------------------------------\n",
420 		  _cmd));
421       break;
422 
423     case 0x49:
424       DBG(2, form("KCemu/GDC/CURS",
425 		  "GDC: CURS -------------------------------- %02x\n"
426 		  "GDC: CURS set cursor to addr:        %05x\n"
427 		  "GDC: CURS point address:                %02x\n"
428 		  "GDC: CURS --------------------------------\n",
429 		  _cmd,
430 		  _ptr,
431 		  _pptr));
432       break;
433 
434     case 0x70: case 0x71: case 0x72: case 0x73:
435     case 0x74: case 0x75: case 0x76: case 0x77:
436     case 0x78: case 0x79: case 0x7a: case 0x7b:
437     case 0x7c: case 0x7d: case 0x7e: case 0x7f:
438       DBG(2, form("KCemu/GDC/PRAM",
439 		  "GDC: PRAM -------------------------------- %02x\n"
440 		  "GDC: PRAM write starting at register    %2d\n"
441 		  "GDC: PRAM register  0: %04x %04x %04x %04x\n"
442 		  "GDC: PRAM register  4: %04x %04x %04x %04x\n"
443 		  "GDC: PRAM register  8: %04x %04x %04x %04x\n"
444 		  "GDC: PRAM register 12: %04x %04x %04x %04x\n"
445 		  "GDC: PRAM SAD/LEN 1  %010x/%010x\n"
446 		  "GDC: PRAM IM/WD 1              %s/%d\n"
447 		  "GDC: PRAM SAD/LEN 2  %010x/%010x\n"
448 		  "GDC: PRAM IM/WD 2              %s/%d\n"
449 		  "GDC: PRAM SAD/LEN 3  %010x/%010x\n"
450 		  "GDC: PRAM IM/WD 3              %s/%d\n"
451 		  "GDC: PRAM SAD/LEN 4  %010x/%010x\n"
452 		  "GDC: PRAM IM/WD 4              %s/%d\n"
453 		  "GDC: PRAM --------------------------------\n",
454 		  _cmd,
455 
456 		  _cmd & 15,
457 		  _pram[ 0], _pram[ 1], _pram[ 2], _pram[ 3],
458 		  _pram[ 4], _pram[ 5], _pram[ 6], _pram[ 7],
459 		  _pram[ 8], _pram[ 9], _pram[10], _pram[11],
460 		  _pram[12], _pram[13], _pram[14], _pram[15],
461 
462 		  _pram[0] | (_pram[1] << 8) | ((_pram[2] & 3) << 16),
463 		  ((_pram[2] & 0xf0) >> 4) | ((_pram[3] & 0x3f) << 4),
464 		  ((_pram[3] & 0x40) >> 6) ? "  graphic" : "character",
465 		  (_pram[3] & 0x80) >> 7,
466 
467 		  _pram[4] | (_pram[5] << 8) | ((_pram[6] & 3) << 16),
468 		  ((_pram[6] & 0xf0) >> 4) | ((_pram[7] & 0x3f) << 4),
469 		  ((_pram[7] & 0x40) >> 6) ? "  graphic" : "character",
470 		  (_pram[7] & 0x80) >> 7,
471 
472 		  _pram[8] | (_pram[9] << 8) | ((_pram[10] & 3) << 16),
473 		  ((_pram[10] & 0xf0) >> 4) | ((_pram[11] & 0x3f) << 4),
474 		  ((_pram[11] & 0x40) >> 6) ? "  graphic" : "character",
475 		  (_pram[11] & 0x80) >> 7,
476 
477 		  _pram[12] | (_pram[13] << 8) | ((_pram[14] & 3) << 16),
478 		  ((_pram[14] & 0xf0) >> 4) | ((_pram[15] & 0x3f) << 4),
479                   ((_pram[15] & 0x40) >> 6) ? "  graphic" : "character",
480 		  (_pram[15] & 0x80) >> 7));
481       break;
482 
483     case 0x47:
484       DBG(2, form("KCemu/GDC/PITCH",
485 		  "GDC: PITCH ------------------------------- %02x\n"
486 		  "GDC: PITCH line width                %5d\n"
487 		  "GDC: PITCH -------------------------------\n",
488 		  _cmd,
489 		  _arg[0]));
490       break;
491 
492     case 0x20: case 0x21: case 0x22: case 0x23:
493     case 0x28: case 0x29: case 0x2a: case 0x2b:
494     case 0x30: case 0x31: case 0x32: case 0x33:
495     case 0x38: case 0x39: case 0x3a: case 0x3b:
496       DBG(2, form("KCemu/GDC/WDAT",
497 		  "GDC: WDAT -------------------------------- %02x\n"
498 		  "GDC: WDAT type                           %d\n"
499 		  "GDC: WDAT MOD                            %d\n"
500 		  "GDC: WDAT arg[0]                      0x%02x\n"
501 		  "GDC: WDAT arg[1]                      0x%02x\n"
502 		  "GDC: WDAT --------------------------------\n",
503 		  _cmd,
504 		  (_cmd & 0x18) >> 3,
505 		  (_cmd & 3),
506 		  _arg[0],
507 		  _arg[1]));
508       break;
509 
510     case 0x4a:
511       DBG(2, form("KCemu/GDC/MASK",
512 		  "GDC: MASK -------------------------------- %02x\n"
513 		  "GDC: MASK mask                        %04x\n"
514 		  "GDC: MASK --------------------------------\n",
515 		  _cmd,
516 		  _mask));
517       break;
518 
519     case 0x4c:
520       DBG(2, form("KCemu/GDC/FIGS",
521 		  "GDC: FIGS -------------------------------- %02x\n"
522 		  "GDC: FIGS count                      %05d\n"
523 		  "GDC: FIGS --------------------------------\n",
524 		  _cmd,
525 		  _figs_dc));
526       break;
527 
528     case 0x6c:
529       DBG(2, form("KCemu/GDC/FIGD",
530 		  "GDC: FIGD -------------------------------- %02x\n"
531 		  "GDC: FIGD --------------------------------\n",
532 		  _cmd));
533       break;
534 
535     case 0x68:
536       DBG(2, form("KCemu/GDC/GCHRD",
537 		  "GDC: GCHRD ------------------------------- %02x\n"
538 		  "GDC: GCHRD -------------------------------\n",
539 		  _cmd));
540       break;
541 
542     case 0xa0: case 0xa1: case 0xa2: case 0xa3:
543     case 0xa8: case 0xa9: case 0xaa: case 0xab:
544     case 0xb0: case 0xb1: case 0xb2: case 0xb3:
545     case 0xb8: case 0xb9: case 0xba: case 0xbb:
546       DBG(2, form("KCemu/GDC/RDAT",
547 		  "GDC: RDAT -------------------------------- %02x\n"
548 		  "GDC: RDAT type                           %d\n"
549 		  "GDC: RDAT MOD                            %d\n"
550 		  "GDC: RDAT --------------------------------\n",
551 		  _cmd,
552 		  (_cmd & 0x18) >> 3,
553 		  (_cmd & 3)));
554       break;
555 
556     case 0xe0:
557       DBG(2, form("KCemu/GDC/CURD",
558 		  "GDC: CURD -------------------------------- %02x\n"
559 		  "GDC: CURD --------------------------------\n",
560 		  _cmd));
561       break;
562 
563     case 0xc0:
564       DBG(2, form("KCemu/GDC/LPRD",
565 		  "GDC: LPRD -------------------------------- %02x\n"
566 		  "GDC: LPRD --------------------------------\n",
567 		  _cmd));
568       break;
569 
570     case 0xa4: case 0xa5: case 0xa6: case 0xa7:
571     case 0xac: case 0xad: case 0xae: case 0xaf:
572     case 0xb4: case 0xb5: case 0xb6: case 0xb7:
573     case 0xbc: case 0xbd: case 0xbe: case 0xbf:
574       DBG(2, form("KCemu/GDC/DMAR",
575 		  "GDC: DMAR -------------------------------- %02x\n"
576 		  "GDC: DMAR type                           %d\n"
577 		  "GDC: DMAR MOD                            %d\n"
578 		  "GDC: DMAR --------------------------------\n",
579 		  _cmd,
580 		  (_cmd & 0x18) >> 3,
581 		  (_cmd & 3)));
582       break;
583 
584     case 0x24: case 0x25: case 0x26: case 0x27:
585     case 0x2c: case 0x2d: case 0x2e: case 0x2f:
586     case 0x34: case 0x35: case 0x36: case 0x37:
587     case 0x3c: case 0x3d: case 0x3e: case 0x3f:
588       DBG(2, form("KCemu/GDC/DMAW",
589 		  "GDC: DMAW -------------------------------- %02x\n"
590 		  "GDC: DMAW type                           %d\n"
591 		  "GDC: DMAW MOD                            %d\n"
592 		  "GDC: DMAW --------------------------------\n",
593 		  _cmd,
594 		  (_cmd & 0x18) >> 3,
595 		  (_cmd & 3)));
596       break;
597 
598     default:
599       cout << "CMD: 0x" << hex << (int)_cmd << " -";
600       for (int a = 0;a < _idx;a++)
601 	cout << " 0x" << hex << (int)_arg[a];
602       cout << endl;
603     }
604 }
605 
606 void
reti(void)607 GDC::reti(void)
608 {
609 }
610 
611 void
irqreq(void)612 GDC::irqreq(void)
613 {
614 }
615 
616 word_t
irqack(void)617 GDC::irqack(void)
618 {
619   return IRQ_NOT_ACK;
620 }
621 
622 void
reset(bool power_on)623 GDC::reset(bool power_on)
624 {
625   _idx = 0;
626   _ptr = 0;
627   _pptr = 0;
628   _mask = 0;
629   _mask_c = 0;
630   _control = 5;
631   _figs_dc = 0;
632   _screen_on = 0;
633   _nr_of_lines = 0;
634   _cursor_top = 1;
635   _cursor_bottom = 0;
636 
637   memset(_pram, 0, 16);
638   memset(_mem, 0x20, 65536); // FIXME: handle screen blanking
639   memset(_col, 0x00, 65536);
640 }
641