1 /* pro_vid.c: video board
2
3 Copyright (c) 1997-2003, Tarik Isani (xhomer@isani.org)
4
5 This file is part of Xhomer.
6
7 Xhomer is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10
11 Xhomer is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Xhomer; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21
22 /* TBD:
23 -implement appropriate vbi bits
24 */
25
26 #ifdef PRO
27 #include "pdp11_defs.h"
28 #include "sim_defs.h" /* For sim_gtime() */
29
30 /* XXX */
31
32 struct iolink {
33 int low;
34 int high;
35 int (*read)();
36 int (*write)(); };
37
38 extern struct iolink iotable[]; /* from pdp11_cpu */
39
40 /* 3 planes are allocated even if EBO is not present. The two additional
41 blank planes are referenced during screen refresh. */
42
43 unsigned short PRO_VRAM[3][PRO_VID_MEMSIZE/2];
44
45 unsigned char pro_vid_mvalid[(PRO_VID_MEMSIZE/2) >> PRO_VID_CLS];
46
47 int pro_vid_csr;
48 int pro_vid_p1c;
49 int pro_vid_scl;
50
51 int pro_vid_y_offset; /* address offset for all vram accesses */
52
53 LOCAL int pro_vid_opc;
54 LOCAL int pro_vid_x;
55 LOCAL int pro_vid_y;
56 LOCAL int pro_vid_cnt;
57 LOCAL int pro_vid_pat;
58 LOCAL int pro_vid_mbr;
59
60 LOCAL int pro_vid_mem_base;
61
62 LOCAL int pro_vid_plane_en; /* This is a composite of the 3 enable bits */
63
64 LOCAL int pro_vid_skip; /* window update cycle skip counter */
65
66
67 /* Vertical retrace event scheduler */
68
pro_vid_sched()69 LOCAL void pro_vid_sched ()
70 {
71 int interval;
72
73 if ((pro_vid_csr & PRO_VID_LM) == 0)
74 interval = PRO_EQ_NTSC;
75 else
76 interval = PRO_EQ_PAL;
77
78 pro_eq_sched(PRO_EVENT_VID, interval);
79 }
80
81
82 /* Vertical retrace event handler */
83
pro_vid_eq()84 void pro_vid_eq ()
85 {
86 /* Trigger end of frame interrupt, if enabled */
87
88 /* XXX use better int name */
89
90 if ((pro_vid_csr & PRO_VID_EFIE) != 0)
91 pro_int_set(PRO_INT_2A);
92
93
94 /* Refresh display window, if skip count has been reached */
95
96 if (pro_vid_skip == PRO_VID_SKIP)
97 {
98 pro_screen_update();
99
100 pro_vid_skip = 0;
101 }
102 else
103 pro_vid_skip++;
104
105
106 /* Schedule next event */
107
108 pro_vid_sched();
109 }
110
111
112 /* Command done event handler */
113
pro_vid_cmd_eq()114 void pro_vid_cmd_eq ()
115 {
116 /* Trigger command done interrupt, if enabled */
117
118 /* XXX use better int name */
119
120 if ((pro_vid_csr & PRO_VID_DIE) != 0)
121 pro_int_set(PRO_INT_2B);
122
123 /* Set done bit */
124
125 pro_vid_csr = pro_vid_csr | PRO_VID_TD;
126
127 /* Clear count */
128
129 pro_vid_cnt = 0;
130 }
131
132
133 /* Video memory */
134
pro_vram_rd(int * data,int pa,int access)135 int pro_vram_rd (int *data, int pa, int access)
136 {
137 int vindex;
138
139 if ((pa & 017700000) == pro_vid_mem_base)
140 {
141 vindex = vmem((pa-pro_vid_mem_base) >> 1);
142
143 switch (pro_vid_plane_en)
144 {
145 case 00:
146 /* XXX all disabled - return 0? */
147
148 *data = PRO_NOREG;
149 break;
150
151 case 01:
152 case 03:
153 case 05:
154 case 07:
155 *data = PRO_VRAM[0][vindex];
156 break;
157
158 case 02:
159 case 06:
160 *data = PRO_VRAM[1][vindex];
161 break;
162
163 case 04:
164 *data = PRO_VRAM[2][vindex];
165 break;
166
167 default:
168 *data = PRO_NOREG;
169 break;
170 }
171
172 /* XXX
173 printf("Video memory READ!!!!!!!!!!\r\n");
174 */
175 }
176 else
177 {
178 *data = 0;
179 printf("%10f Warning: video memory (R) not at default location!\r\n", sim_gtime());
180 }
181
182 return SCPE_OK;
183 }
184
185
pro_vram_wr(int data,int pa,int access)186 int pro_vram_wr (int data, int pa, int access)
187 {
188 int vindex;
189
190 if ((pa & 017700000) == pro_vid_mem_base)
191 {
192 vindex = vmem((pa-pro_vid_mem_base) >> 1);
193
194 /* XXX perform byte enable check */
195
196 /*
197 if (access == WRITEB)
198 printf("%10f Warning: video memory byte write!\r\n", sim_gtime());
199 */
200
201 if ((pro_vid_plane_en & 01) != 0)
202 {
203 WRITE_WB(PRO_VRAM[0][vindex], 0177777, access);
204 }
205
206 if ((pro_vid_plane_en & 02) != 0)
207 {
208 WRITE_WB(PRO_VRAM[1][vindex], 0177777, access);
209 }
210
211 if ((pro_vid_plane_en & 04) != 0)
212 {
213 WRITE_WB(PRO_VRAM[2][vindex], 0177777, access);
214 }
215
216 /* XXX cache is currently cleared even if no memory planes are enabled */
217
218 pro_vid_mvalid[cmem(vindex)] = 0;
219
220 /* XXX
221 printf("Video memory WRITE!!!!!!!!!!\r\n");
222 */
223 }
224 else
225 {
226 printf("%10f Warning: video memory (W) not at default location!\r\n", sim_gtime());
227 }
228
229 return SCPE_OK;
230 }
231
232
233 /* Pixel drawing operations */
234
pro_vid_pixop(int pnum,int x,int y,int data,int pixop)235 LOCAL void pro_vid_pixop(int pnum, int x, int y, int data, int pixop)
236 {
237 int vindex, vbit, vmask, vdata, bit;
238
239 /* Calculate starting memory index and bit offset */
240
241 vindex = vmem((y << 6) | ((x >> 4) & 077));
242
243 vbit = x & 017;
244
245 vmask = ~(01 << vbit) & 0177777;
246
247 bit = data << vbit;
248
249 vdata = PRO_VRAM[pnum][vindex];
250
251 /* Perform operation */
252
253 switch (pixop)
254 {
255 case PRO_VID_PIX_XOR: /* xor pixel */
256 vdata = vdata ^ bit;
257 break;
258
259 case PRO_VID_PIX_SET: /* set pixel */
260 vdata = vdata | bit;
261 break;
262
263 case PRO_VID_PIX_CLR: /* clear pixel */
264 vdata = vdata & (~bit & 0177777);
265 break;
266
267 case PRO_VID_PIX_REP: /* replace pixel */
268 vdata = (vdata & vmask) | bit;
269 break;
270
271 default:
272 break;
273 }
274
275 PRO_VRAM[pnum][vindex] = vdata;
276
277 pro_vid_mvalid[cmem(vindex)] = 0;
278 }
279
280
pro_clear_mvalid()281 void pro_clear_mvalid ()
282 {
283 memset(&pro_vid_mvalid, 0, sizeof(pro_vid_mvalid));
284 }
285
286
287 /* Video registers */
288
pro_vid_rd(int pa)289 int pro_vid_rd (int pa)
290 {
291 int data;
292
293 switch (pa & 017777776)
294 {
295 case 017774400:
296 data = PRO_ID_VID; /* IDR */
297 break;
298
299 case 017774404:
300 data = pro_vid_csr;
301 break;
302
303 case 017774406:
304 data = pro_vid_p1c;
305 break;
306
307 /* EBO */
308 case 017774410:
309 data = pro_vid_opc;
310 break;
311
312 case 017774414:
313 data = pro_vid_scl;
314 break;
315
316 case 017774416:
317 data = pro_vid_x;
318 break;
319
320 case 017774420:
321 data = pro_vid_y;
322 break;
323
324 /* XXX The following are write-only bits. The real PRO returns these values */
325
326 case 017774422:
327 data = 0177577;
328 break;
329
330 case 017774424:
331 data = 0177577;
332 break;
333
334 case 017774426:
335 data = 0177577;
336 break;
337
338 /* EBO */
339 case 017774600:
340 data = PRO_ID_EBO;
341 break;
342
343 default:
344 data = PRO_NOREG;
345 break;
346 }
347
348 return data;
349 }
350
pro_vid_wr(int data,int pa,int access)351 void pro_vid_wr (int data, int pa, int access)
352 {
353 int x, y, pixop, vplane, vindex, vcount, vdata, vpixpat, vwordpat, vsi, old_csr, old_scl;
354 int pixdata = 0; /* assign dummy value to silence incorrect gcc warning */
355
356 int vop[3];
357 int voc1, voc0;
358
359 switch (pa & 017777776)
360 {
361 case 017774404:
362 old_csr = pro_vid_csr;
363
364 WRITE_WB(pro_vid_csr, PRO_VID_CSR_W, access);
365
366 /* XXX this might not be correct, but the ROM seems
367 to imply this behavior */
368
369 /* Maybe it should only happen the first time after reset */
370
371 /* Generate TD interrupt if TD enabled and done bit set */
372
373 if (((pro_vid_csr & PRO_VID_DIE) != 0)
374 && ((old_csr & PRO_VID_DIE) == 0)
375 && ((pro_vid_csr & PRO_VID_TD) != 0))
376 pro_int_set(PRO_INT_2B);
377
378 /* Check if mapped mode changed */
379
380 if ((pro_vid_csr & PRO_VID_CME) != (old_csr & PRO_VID_CME))
381 pro_mapchange();
382
383 break;
384
385 case 017774406:
386 WRITE_W(pro_vid_p1c, PRO_VID_P1C_W);
387 pro_vid_plane_en = ((pro_vid_opc & PRO_VID_P3_VME) >> 11)
388 | ((pro_vid_opc & PRO_VID_P2_VME) >> 4)
389 | ((pro_vid_p1c & PRO_VID_P1_VME) >> 5);
390 break;
391
392 /* EBO */
393 case 017774410:
394 WRITE_W(pro_vid_opc, PRO_VID_OPC_W);
395 pro_vid_plane_en = ((pro_vid_opc & PRO_VID_P3_VME) >> 11)
396 | ((pro_vid_opc & PRO_VID_P2_VME) >> 4)
397 | ((pro_vid_p1c & PRO_VID_P1_VME) >> 5);
398 break;
399
400 /* EBO */
401 case 017774412:
402 /* Write to colormap */
403
404 pro_colormap_write(((data & PRO_VID_INDEX) >> 8), (data & PRO_VID_RGB));
405
406 break;
407
408 case 017774414:
409 old_scl = pro_vid_scl;
410
411 WRITE_W(pro_vid_scl, PRO_VID_SCL_W);
412
413 /* Call scroll routine if scl changed */
414
415 if (old_scl != pro_vid_scl)
416 pro_scroll();
417
418 /* Update memory offset */
419
420 pro_vid_y_offset = (pro_vid_scl << 6) & 037700;
421
422 break;
423
424 case 017774416:
425 WRITE_W(pro_vid_x, PRO_VID_X_W);
426 break;
427
428 case 017774420:
429 WRITE_W(pro_vid_y, PRO_VID_Y_W);
430 break;
431
432 case 017774422:
433 WRITE_W(pro_vid_cnt, PRO_VID_CNT_W);
434
435 /* Clear the done bit */
436
437 pro_vid_csr = pro_vid_csr & ~PRO_VID_TD;
438
439 /* XXX kludge - assuming plane, enabled, etc. */
440
441 if (pro_vid_cnt != 0)
442 {
443 /* Determine operation types for all planes */
444
445 vop[0] = pro_vid_p1c & PRO_VID_P1_OP;
446 vop[1] = pro_vid_opc & PRO_VID_P2_OP;
447 vop[2] = (pro_vid_opc & PRO_VID_P3_OP) >> 8;
448
449 /* Operation class */
450
451 /* XXX Make these text macros for speed */
452
453 voc1 = pro_vid_csr & PRO_VID_OC1; /* bit mode */
454
455 voc0 = pro_vid_csr & PRO_VID_OC0; /* left to right */
456
457 for(vplane=0; vplane<PRO_VID_NUMPLANES; vplane++)
458 {
459 x = pro_vid_x;
460 y = pro_vid_y;
461
462 /* Calculate memory index (for word mode) */
463
464 vindex = vmem((y << 6) | ((x >> 4) & 077));
465
466 /* Calculate pattern for word mode */
467
468 /* XXX
469 vwordpat = pro_vid_pat;
470 */
471 if ((pro_vid_pat & 01) == 0)
472 vwordpat = 0;
473 else
474 vwordpat = 0177777;
475
476 /* Set initial pattern for pixel mode */
477
478 vpixpat = pro_vid_pat;
479
480 /* Set initial shift-in pattern for word screen shift */
481
482 vsi = vwordpat;
483
484 for(vcount = 0; vcount < pro_vid_cnt; vcount++)
485 {
486 if (voc1 == 0) /* bit mode */
487 {
488 switch (vop[vplane])
489 {
490 case PRO_VID_BM_NOP:
491 pixop = PRO_VID_PIX_NOP;
492 break;
493
494 case PRO_VID_BM_XOR:
495 pixop = PRO_VID_PIX_XOR;
496 pixdata = vpixpat & 01;
497 vpixpat = (vpixpat >> 1) | ((vpixpat << 15) & 0100000);
498 break;
499
500 case PRO_VID_BM_MOVE:
501 pixop = PRO_VID_PIX_REP;
502 pixdata = vpixpat & 01;
503 vpixpat = (vpixpat >> 1) | ((vpixpat << 15) & 0100000);
504 break;
505
506 case PRO_VID_BM_NMOVE:
507 pixop = PRO_VID_PIX_REP;
508 pixdata = ~vpixpat & 01;
509 vpixpat = (vpixpat >> 1) | ((vpixpat << 15) & 0100000);
510 break;
511
512 case PRO_VID_BM_SETPAT:
513 pixop = PRO_VID_PIX_SET;
514 pixdata = vpixpat & 01;
515 vpixpat = (vpixpat >> 1) | ((vpixpat << 15) & 0100000);
516 break;
517
518 case PRO_VID_BM_CLRPAT:
519 pixop = PRO_VID_PIX_CLR;
520 pixdata = vpixpat & 01;
521 vpixpat = (vpixpat >> 1) | ((vpixpat << 15) & 0100000);
522 break;
523
524 case PRO_VID_BM_CLRBIT:
525 pixop = PRO_VID_PIX_CLR;
526 pixdata = 01;
527 break;
528
529 case PRO_VID_BM_SETBIT:
530 pixop = PRO_VID_PIX_SET;
531 pixdata = 01;
532 break;
533
534 default:
535 pixop = PRO_VID_PIX_NOP;
536 break;
537 }
538
539 /* Perform the pixel operation */
540
541 if (pixop != PRO_VID_PIX_NOP)
542 pro_vid_pixop(vplane, x, y, pixdata, pixop);
543
544 /* Update x & y */
545
546 if (voc0 == 0) /* left to right */
547 {
548 x++;
549
550 if (x == PRO_VID_MEMWIDTH)
551 {
552 x = 0;
553 y++;
554
555 if (y == PRO_VID_MEMHEIGHT)
556 y = 0;
557 }
558 }
559 else /* top to bottom */
560 {
561 y++;
562
563 if (y == PRO_VID_MEMHEIGHT)
564 {
565 y = 0;
566 x++;
567
568 if (x == PRO_VID_MEMWIDTH)
569 x = 0;
570 }
571 }
572
573 }
574 else /* word mode */
575 {
576 switch (vop[vplane])
577 {
578 case PRO_VID_WM_NOP:
579 break;
580
581 case PRO_VID_WM_COM:
582 PRO_VRAM[vplane][vindex] = (PRO_VRAM[vplane][vindex] ^ 0177777) & 0177777;
583 break;
584
585 case PRO_VID_WM_MOVE:
586 PRO_VRAM[vplane][vindex] = vwordpat;
587 break;
588
589 case PRO_VID_WM_NMOVE:
590 PRO_VRAM[vplane][vindex] = (~vwordpat) & 0177777;
591 break;
592
593 /* XXX combine these three */
594
595 case PRO_VID_WM_S1:
596 vdata = PRO_VRAM[vplane][vindex];
597 if (voc0 == 0) /* left to right */
598 PRO_VRAM[vplane][vindex] = ((PRO_VRAM[vplane][vindex] << 1) & 0177777) | ((vsi >> 15) & 01);
599 else /* right to left */
600 PRO_VRAM[vplane][vindex] = ((PRO_VRAM[vplane][vindex] >> 1) & 0177777) | ((vsi << 15) & 0100000);
601 vsi = vdata;
602 break;
603
604 case PRO_VID_WM_S2:
605 vdata = PRO_VRAM[vplane][vindex];
606 if (voc0 == 0) /* left to right */
607 PRO_VRAM[vplane][vindex] = ((PRO_VRAM[vplane][vindex] << 2) & 0177777) | ((vsi >> 14) & 03);
608 else /* right to left */
609 PRO_VRAM[vplane][vindex] = ((PRO_VRAM[vplane][vindex] >> 2) & 0177777) | ((vsi << 14) & 0140000);
610 vsi = vdata;
611 break;
612
613 case PRO_VID_WM_S4:
614 vdata = PRO_VRAM[vplane][vindex];
615 if (voc0 == 0) /* left to right */
616 PRO_VRAM[vplane][vindex] = ((PRO_VRAM[vplane][vindex] << 4) & 0177777) | ((vsi >> 12) & 017);
617 else /* right to left */
618 PRO_VRAM[vplane][vindex] = ((PRO_VRAM[vplane][vindex] >> 4) & 0177777) | ((vsi << 12) & 0170000);
619 vsi = vdata;
620 break;
621
622 default:
623 break;
624 }
625
626 pro_vid_mvalid[cmem(vindex)] = 0;
627
628 /* Update memory index */
629
630 /* XXX is wrap-around handled correctly? */
631
632 if (voc0 == 0) /* left to right */
633 vindex = (vindex + 1) & PRO_VID_VADDR_MASK;
634 else /* right to left */
635 vindex = (vindex - 1) & PRO_VID_VADDR_MASK;
636
637 }
638
639 }
640 }
641
642 /* Update pattern register, once all three plane operations
643 have completed */
644
645 /* XXX Is this the correct behavior? */
646
647 pro_vid_pat = vpixpat;
648 }
649
650
651 /* Schedule completion event */
652
653 pro_eq_sched(PRO_EVENT_VID_CMD, PRO_EQ_CMD);
654
655 break;
656
657 /* XXX This should only be writeable if the done bit is set */
658
659 case 017774424:
660 WRITE_W(pro_vid_pat, PRO_VID_PAT_W);
661 break;
662
663 case 017774426:
664 WRITE_W(pro_vid_mbr, PRO_VID_MBR_W);
665 pro_vid_mem_base = pro_vid_mbr << 15; /* video mem base address */
666
667 /* Update CPU memory decoder */
668
669 iotable[0].low = pro_vid_mem_base;
670 iotable[0].high = pro_vid_mem_base + PRO_VID_MEMSIZE - 1;
671
672 break;
673
674 default:
675 break;
676 }
677 }
678
679
pro_vid_reset()680 void pro_vid_reset ()
681 {
682 /* Clear Video memory buffers */
683
684 memset(&PRO_VRAM, 0, sizeof(PRO_VRAM));
685
686 /* XXX some undefined bits have different values in the real PRO */
687
688 pro_vid_csr = PRO_VID_CSR | PRO_VID_TD; /* indicate command completed */
689 pro_vid_p1c = 0;
690 pro_vid_opc = 0;
691 pro_vid_scl = 0177400;
692 pro_vid_x = 0;
693 pro_vid_y = 0;
694 pro_vid_cnt = 0;
695 pro_vid_pat = 0;
696 pro_vid_mbr = 0;
697
698 pro_vid_mem_base = 0;
699 pro_vid_plane_en = 0;
700 pro_vid_y_offset = 0;
701 pro_vid_skip = 0;
702
703
704 /* Start up vertical retrace event */
705
706 pro_vid_sched();
707
708 /* Reset display subsystem */
709
710 pro_screen_reset();
711
712 /* Open display window */
713
714 if (pro_screen_init() == PRO_FAIL)
715 pro_exit();
716 }
717
718
719 /* Exit routine */
720
pro_vid_exit()721 void pro_vid_exit ()
722 {
723 pro_screen_close();
724 }
725 #endif
726