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