1 /*CGA emulation*/
2 #include <stdlib.h>
3 #include <math.h>
4 #include "ibm.h"
5 #include "device.h"
6 #include "io.h"
7 #include "mem.h"
8 #include "timer.h"
9 #include "video.h"
10 #include "vid_cga.h"
11 #include "dosbox/vid_cga_comp.h"
12 
13 #define COMPOSITE_OLD 0
14 #define COMPOSITE_NEW 1
15 
16 static uint8_t crtcmask[32] =
17 {
18         0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
19         0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
20 };
21 
22 void cga_recalctimings(cga_t *cga);
23 
cga_out(uint16_t addr,uint8_t val,void * p)24 void cga_out(uint16_t addr, uint8_t val, void *p)
25 {
26         cga_t *cga = (cga_t *)p;
27         uint8_t old;
28 //        pclog("CGA_OUT %04X %02X\n", addr, val);
29         switch (addr)
30         {
31                 case 0x3D4:
32                 cga->crtcreg = val & 31;
33                 return;
34                 case 0x3D5:
35                 old = cga->crtc[cga->crtcreg];
36                 cga->crtc[cga->crtcreg] = val & crtcmask[cga->crtcreg];
37                 if (old != val)
38                 {
39                         if (cga->crtcreg < 0xe || cga->crtcreg > 0x10)
40                         {
41                                 fullchange = changeframecount;
42                                 cga_recalctimings(cga);
43                         }
44                 }
45                 return;
46                 case 0x3D8:
47                 if (((cga->cgamode ^ val) & 5) != 0)
48                 {
49                         cga->cgamode = val;
50                         update_cga16_color(cga->cgamode);
51                 }
52                 cga->cgamode = val;
53                 return;
54                 case 0x3D9:
55                 cga->cgacol = val;
56                 return;
57         }
58 }
59 
cga_in(uint16_t addr,void * p)60 uint8_t cga_in(uint16_t addr, void *p)
61 {
62         cga_t *cga = (cga_t *)p;
63 //        pclog("CGA_IN %04X\n", addr);
64         switch (addr)
65         {
66                 case 0x3D4:
67                 return cga->crtcreg;
68                 case 0x3D5:
69                 return cga->crtc[cga->crtcreg];
70                 case 0x3DA:
71                 return cga->cgastat;
72         }
73         return 0xFF;
74 }
75 
cga_write(uint32_t addr,uint8_t val,void * p)76 void cga_write(uint32_t addr, uint8_t val, void *p)
77 {
78         cga_t *cga = (cga_t *)p;
79 //        pclog("CGA_WRITE %04X %02X\n", addr, val);
80         cga->vram[addr & 0x3fff] = val;
81         if (cga->snow_enabled)
82         {
83                 cga->charbuffer[ ((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc] = val;
84                 cga->charbuffer[(((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc) | 1] = val;
85         }
86         egawrites++;
87         cycles -= 4;
88 }
89 
cga_read(uint32_t addr,void * p)90 uint8_t cga_read(uint32_t addr, void *p)
91 {
92         cga_t *cga = (cga_t *)p;
93         cycles -= 4;
94         if (cga->snow_enabled)
95         {
96                 cga->charbuffer[ ((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc] = cga->vram[addr & 0x3fff];
97                 cga->charbuffer[(((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc) | 1] = cga->vram[addr & 0x3fff];
98         }
99         egareads++;
100 //        pclog("CGA_READ %04X\n", addr);
101         return cga->vram[addr & 0x3fff];
102 }
103 
cga_recalctimings(cga_t * cga)104 void cga_recalctimings(cga_t *cga)
105 {
106         double disptime;
107 	double _dispontime, _dispofftime;
108         pclog("Recalc - %i %i %i\n", cga->crtc[0], cga->crtc[1], cga->cgamode & 1);
109         if (cga->cgamode & 1)
110         {
111                 disptime = cga->crtc[0] + 1;
112                 _dispontime = cga->crtc[1];
113         }
114         else
115         {
116                 disptime = (cga->crtc[0] + 1) << 1;
117                 _dispontime = cga->crtc[1] << 1;
118         }
119         _dispofftime = disptime - _dispontime;
120 //        printf("%i %f %f %f  %i %i\n",cgamode&1,disptime,dispontime,dispofftime,crtc[0],crtc[1]);
121         _dispontime *= CGACONST;
122         _dispofftime *= CGACONST;
123 //        printf("Timings - on %f off %f frame %f second %f\n",dispontime,dispofftime,(dispontime+dispofftime)*262.0,(dispontime+dispofftime)*262.0*59.92);
124 	cga->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT));
125 	cga->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT));
126 }
127 
cga_poll(void * p)128 void cga_poll(void *p)
129 {
130         cga_t *cga = (cga_t *)p;
131         uint16_t ca = (cga->crtc[15] | (cga->crtc[14] << 8)) & 0x3fff;
132         int drawcursor;
133         int x, c;
134         int oldvc;
135         uint8_t chr, attr;
136         uint16_t dat;
137         uint32_t cols[4];
138         int col;
139         int oldsc;
140 
141         if (!cga->linepos)
142         {
143                 cga->vidtime += cga->dispofftime;
144                 cga->cgastat |= 1;
145                 cga->linepos = 1;
146                 oldsc = cga->sc;
147                 if ((cga->crtc[8] & 3) == 3)
148                    cga->sc = ((cga->sc << 1) + cga->oddeven) & 7;
149                 if (cga->cgadispon)
150                 {
151                         if (cga->displine < cga->firstline)
152                         {
153                                 cga->firstline = cga->displine;
154                                 video_wait_for_buffer();
155 //                                printf("Firstline %i\n",firstline);
156                         }
157                         cga->lastline = cga->displine;
158 
159                         cols[0] = ((cga->cgamode & 0x12) == 0x12) ? 0 : (cga->cgacol & 15);
160                         for (c = 0; c < 8; c++)
161                         {
162                                 ((uint32_t *)buffer32->line[cga->displine])[c] = cols[0];
163                                 if (cga->cgamode & 1)
164                                         ((uint32_t *)buffer32->line[cga->displine])[c + (cga->crtc[1] << 3) + 8] = cols[0];
165                                 else
166                                         ((uint32_t *)buffer32->line[cga->displine])[c + (cga->crtc[1] << 4) + 8] = cols[0];
167                         }
168                         if (cga->cgamode & 1)
169                         {
170                                 for (x = 0; x < cga->crtc[1]; x++)
171                                 {
172                                         if (cga->cgamode & 8)
173                                         {
174                                                 chr = cga->charbuffer[x << 1];
175                                                 attr = cga->charbuffer[(x << 1) + 1];
176                                         }
177                                         else
178                                                 chr = attr = 0;
179                                         drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
180                                         if (cga->cgamode & 0x20)
181                                         {
182                                                 cols[1] = attr & 15;
183                                                 cols[0] = (attr >> 4) & 7;
184                                                 if ((cga->cgablink & 8) && (attr & 0x80) && !cga->drawcursor)
185                                                         cols[1] = cols[0];
186                                         }
187                                         else
188                                         {
189                                                 cols[1] = attr & 15;
190                                                 cols[0] = attr >> 4;
191                                         }
192                                         if (drawcursor)
193                                         {
194                                                 for (c = 0; c < 8; c++)
195                                                         ((uint32_t *)buffer32->line[cga->displine])[(x << 3) + c + 8] = cols[(fontdat[chr + cga->fontbase][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 0xffffff;
196                                         }
197                                         else
198                                         {
199                                                 for (c = 0; c < 8; c++)
200                                                         ((uint32_t *)buffer32->line[cga->displine])[(x << 3) + c + 8] = cols[(fontdat[chr + cga->fontbase][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
201                                         }
202                                         cga->ma++;
203                                 }
204                         }
205                         else if (!(cga->cgamode & 2))
206                         {
207                                 for (x = 0; x < cga->crtc[1]; x++)
208                                 {
209                                         if (cga->cgamode & 8)
210                                         {
211                                                 chr  = cga->vram[((cga->ma << 1) & 0x3fff)];
212                                                 attr = cga->vram[(((cga->ma << 1) + 1) & 0x3fff)];
213                                         }
214                                         else
215                                                 chr = attr = 0;
216                                         drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
217                                         if (cga->cgamode & 0x20)
218                                         {
219                                                 cols[1] = attr & 15;
220                                                 cols[0] = (attr >> 4) & 7;
221                                                 if ((cga->cgablink & 8) && (attr & 0x80) && !cga->drawcursor)
222                                                         cols[1] = cols[0];
223                                         }
224                                         else
225                                         {
226                                                 cols[1] = attr & 15;
227                                                 cols[0] = attr >> 4;
228                                         }
229                                         cga->ma++;
230                                         if (drawcursor)
231                                         {
232                                                 for (c = 0; c < 8; c++)
233                                                         ((uint32_t *)buffer32->line[cga->displine])[(x << 4)+(c << 1) + 8] =
234                                                         ((uint32_t *)buffer32->line[cga->displine])[(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr + cga->fontbase][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 0xffffff;
235                                         }
236                                         else
237                                         {
238                                                 for (c = 0; c < 8; c++)
239                                                         ((uint32_t *)buffer32->line[cga->displine])[(x << 4) + (c << 1) + 8] =
240                                                         ((uint32_t *)buffer32->line[cga->displine])[(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr + cga->fontbase][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
241                                         }
242                                 }
243                         }
244                         else if (!(cga->cgamode & 16))
245                         {
246                                 cols[0] = cga->cgacol & 15;
247                                 col = (cga->cgacol & 16) ? 8 : 0;
248                                 if (cga->cgamode & 4)
249                                 {
250                                         cols[1] = col | 3;
251                                         cols[2] = col | 4;
252                                         cols[3] = col | 7;
253                                 }
254                                 else if (cga->cgacol & 32)
255                                 {
256                                         cols[1] = col | 3;
257                                         cols[2] = col | 5;
258                                         cols[3] = col | 7;
259                                 }
260                                 else
261                                 {
262                                         cols[1] = col | 2;
263                                         cols[2] = col | 4;
264                                         cols[3] = col | 6;
265                                 }
266                                 for (x = 0; x < cga->crtc[1]; x++)
267                                 {
268                                         if (cga->cgamode & 8)
269                                                 dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1];
270                                         else
271                                                 dat = 0;
272                                         cga->ma++;
273                                         for (c = 0; c < 8; c++)
274                                         {
275                                                 ((uint32_t *)buffer32->line[cga->displine])[(x << 4) + (c << 1) + 8] =
276                                                   ((uint32_t *)buffer32->line[cga->displine])[(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14];
277                                                 dat <<= 2;
278                                         }
279                                 }
280                         }
281                         else
282                         {
283                                 cols[0] = 0;
284                                 cols[1] = cga->cgacol & 15;
285                                 for (x = 0; x < cga->crtc[1]; x++)
286                                 {
287                                         if (cga->cgamode & 8)
288                                                 dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1];
289                                         else
290                                                 dat = 0;
291                                         cga->ma++;
292                                         for (c = 0; c < 16; c++)
293                                         {
294                                                 ((uint32_t *)buffer32->line[cga->displine])[(x << 4) + c + 8] = cols[dat >> 15];
295                                                 dat <<= 1;
296                                         }
297                                 }
298                         }
299                 }
300                 else
301                 {
302                         cols[0] = ((cga->cgamode & 0x12) == 0x12) ? 0 : (cga->cgacol & 15);
303                         if (cga->cgamode & 1) hline(buffer32, 0, cga->displine, (cga->crtc[1] << 3) + 16, cols[0]);
304                         else                  hline(buffer32, 0, cga->displine, (cga->crtc[1] << 4) + 16, cols[0]);
305                 }
306 
307                 if (cga->cgamode & 1) x = (cga->crtc[1] << 3) + 16;
308                 else                  x = (cga->crtc[1] << 4) + 16;
309 
310                 if (cga->composite)
311                 {
312 			for (c = 0; c < x; c++)
313 				buffer32->line[cga->displine][c] = ((uint32_t *)buffer32->line[cga->displine])[c] & 0xf;
314 
315 			Composite_Process(cga->cgamode, 0, x >> 2, buffer32->line[cga->displine]);
316                 }
317                 else
318                 {
319 			for (c = 0; c < x; c++)
320 				((uint32_t *)buffer32->line[cga->displine])[c] = cgapal[((uint32_t *)buffer32->line[cga->displine])[c] & 0xf];
321                 }
322 
323                 cga->sc = oldsc;
324                 if (cga->vc == cga->crtc[7] && !cga->sc)
325                    cga->cgastat |= 8;
326                 cga->displine++;
327                 if (cga->displine >= 360)
328                         cga->displine = 0;
329         }
330         else
331         {
332                 cga->vidtime += cga->dispontime;
333                 cga->linepos = 0;
334                 if (cga->vsynctime)
335                 {
336                         cga->vsynctime--;
337                         if (!cga->vsynctime)
338                            cga->cgastat &= ~8;
339                 }
340                 if (cga->sc == (cga->crtc[11] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[11] & 31) >> 1)))
341                 {
342                         cga->con = 0;
343                         cga->coff = 1;
344                 }
345                 if ((cga->crtc[8] & 3) == 3 && cga->sc == (cga->crtc[9] >> 1))
346                    cga->maback = cga->ma;
347                 if (cga->vadj)
348                 {
349                         cga->sc++;
350                         cga->sc &= 31;
351                         cga->ma = cga->maback;
352                         cga->vadj--;
353                         if (!cga->vadj)
354                         {
355                                 cga->cgadispon = 1;
356                                 cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
357                                 cga->sc = 0;
358                         }
359                 }
360                 else if (cga->sc == cga->crtc[9])
361                 {
362                         cga->maback = cga->ma;
363                         cga->sc = 0;
364                         oldvc = cga->vc;
365                         cga->vc++;
366                         cga->vc &= 127;
367 
368                         if (cga->vc == cga->crtc[6])
369                                 cga->cgadispon = 0;
370 
371                         if (oldvc == cga->crtc[4])
372                         {
373                                 cga->vc = 0;
374                                 cga->vadj = cga->crtc[5];
375                                 if (!cga->vadj) cga->cgadispon = 1;
376                                 if (!cga->vadj) cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
377                                 if ((cga->crtc[10] & 0x60) == 0x20) cga->cursoron = 0;
378                                 else                                cga->cursoron = cga->cgablink & 8;
379                         }
380 
381                         if (cga->vc == cga->crtc[7])
382                         {
383                                 cga->cgadispon = 0;
384                                 cga->displine = 0;
385                                 cga->vsynctime = 16;
386                                 if (cga->crtc[7])
387                                 {
388                                         if (cga->cgamode & 1) x = (cga->crtc[1] << 3) + 16;
389                                         else                  x = (cga->crtc[1] << 4) + 16;
390                                         cga->lastline++;
391                                         if (x != xsize || (cga->lastline - cga->firstline) != ysize)
392                                         {
393                                                 xsize = x;
394                                                 ysize = cga->lastline - cga->firstline;
395                                                 if (xsize < 64) xsize = 656;
396                                                 if (ysize < 32) ysize = 200;
397                                                 updatewindowsize(xsize, (ysize << 1) + 16);
398                                         }
399 
400                                         video_blit_memtoscreen(0, cga->firstline - 4, 0, (cga->lastline - cga->firstline) + 8, xsize, (cga->lastline - cga->firstline) + 8);
401                                         frames++;
402 
403                                         video_res_x = xsize - 16;
404                                         video_res_y = ysize;
405                                         if (cga->cgamode & 1)
406                                         {
407                                                 video_res_x /= 8;
408                                                 video_res_y /= cga->crtc[9] + 1;
409                                                 video_bpp = 0;
410                                         }
411                                         else if (!(cga->cgamode & 2))
412                                         {
413                                                 video_res_x /= 16;
414                                                 video_res_y /= cga->crtc[9] + 1;
415                                                 video_bpp = 0;
416                                         }
417                                         else if (!(cga->cgamode & 16))
418                                         {
419                                                 video_res_x /= 2;
420                                                 video_bpp = 2;
421                                         }
422                                         else
423                                         {
424                                                 video_bpp = 1;
425                                         }
426                                 }
427                                 cga->firstline = 1000;
428                                 cga->lastline = 0;
429                                 cga->cgablink++;
430                                 cga->oddeven ^= 1;
431                         }
432                 }
433                 else
434                 {
435                         cga->sc++;
436                         cga->sc &= 31;
437                         cga->ma = cga->maback;
438                 }
439                 if (cga->cgadispon)
440                         cga->cgastat &= ~1;
441                 if ((cga->sc == (cga->crtc[10] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[10] & 31) >> 1))))
442                         cga->con = 1;
443                 if (cga->cgadispon && (cga->cgamode & 1))
444                 {
445                         for (x = 0; x < (cga->crtc[1] << 1); x++)
446                             cga->charbuffer[x] = cga->vram[(((cga->ma << 1) + x) & 0x3fff)];
447                 }
448         }
449 }
450 
cga_init(cga_t * cga)451 void cga_init(cga_t *cga)
452 {
453         cga->composite = 0;
454 }
455 
cga_standalone_init()456 void *cga_standalone_init()
457 {
458         int display_type, contrast;
459         cga_t *cga = malloc(sizeof(cga_t));
460         memset(cga, 0, sizeof(cga_t));
461 
462         display_type = device_get_config_int("display_type");
463         cga->composite = (display_type == DISPLAY_COMPOSITE);
464         cga->revision = device_get_config_int("composite_type");
465         cga->snow_enabled = device_get_config_int("snow_enabled");
466         contrast = device_get_config_int("contrast");
467 
468         cga->vram = malloc(0x4000);
469 
470 	cga_comp_init(cga->revision);
471         timer_add(cga_poll, &cga->vidtime, TIMER_ALWAYS_ENABLED, cga);
472         mem_mapping_add(&cga->mapping, 0xb8000, 0x08000, cga_read, NULL, NULL, cga_write, NULL, NULL,  NULL, MEM_MAPPING_EXTERNAL, cga);
473         io_sethandler(0x03d0, 0x0010, cga_in, NULL, NULL, cga_out, NULL, NULL, cga);
474 
475         cgapal_rebuild(display_type, contrast);
476 
477         return cga;
478 }
479 
cga_close(void * p)480 void cga_close(void *p)
481 {
482         cga_t *cga = (cga_t *)p;
483 
484         free(cga->vram);
485         free(cga);
486 }
487 
cga_speed_changed(void * p)488 void cga_speed_changed(void *p)
489 {
490         cga_t *cga = (cga_t *)p;
491 
492         cga_recalctimings(cga);
493 }
494 
495 device_config_t cga_config[] =
496 {
497         {
498                 .name = "display_type",
499                 .description = "Display type",
500                 .type = CONFIG_SELECTION,
501                 .selection =
502                 {
503                         {
504                                 .description = "RGB",
505                                 .value = DISPLAY_RGB
506                         },
507                         {
508                                 .description = "RGB (no brown)",
509                                 .value = DISPLAY_RGB_NO_BROWN
510                         },
511                         {
512                                 .description = "Green Monochrome",
513                                 .value = DISPLAY_GREEN
514                         },
515                         {
516                                 .description = "Amber Monochrome",
517                                 .value = DISPLAY_AMBER
518                         },
519                         {
520                                 .description = "White Monochrome",
521                                 .value = DISPLAY_WHITE
522                         },
523                         {
524                                 .description = "Composite",
525                                 .value = DISPLAY_COMPOSITE
526                         },
527                         {
528                                 .description = ""
529                         }
530                 },
531                 .default_int = DISPLAY_RGB
532         },
533         {
534                 .name = "composite_type",
535                 .description = "Composite type",
536                 .type = CONFIG_SELECTION,
537                 .selection =
538                 {
539                         {
540                                 .description = "Old",
541                                 .value = COMPOSITE_OLD
542                         },
543                         {
544                                 .description = "New",
545                                 .value = COMPOSITE_NEW
546                         },
547                         {
548                                 .description = ""
549                         }
550                 },
551                 .default_int = COMPOSITE_OLD
552         },
553         {
554                 .name = "snow_enabled",
555                 .description = "Snow emulation",
556                 .type = CONFIG_BINARY,
557                 .default_int = 1
558         },
559         {
560                 .name = "contrast",
561                 .description = "Alternate monochrome contrast",
562                 .type = CONFIG_BINARY,
563                 .default_int = 0
564         },
565         {
566                 .type = -1
567         }
568 };
569 
570 device_t cga_device =
571 {
572         "CGA",
573         0,
574         cga_standalone_init,
575         cga_close,
576         NULL,
577         cga_speed_changed,
578         NULL,
579         NULL,
580         cga_config
581 };
582