PCem
view src/vid_cga.c @ 154:d0d530adce12
Initial port to Linux (using Allegro).
64-bit fixes.
Some changes to aid portability.
A few other tweaks.
| author | TomW |
|---|---|
| date | Thu Sep 04 21:07:24 2014 +0100 |
| parents | b53e148867e5 |
| children |
line source
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"
12 static int i_filt[8],q_filt[8];
14 static uint8_t crtcmask[32] =
15 {
16 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
17 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
18 };
20 void cga_recalctimings(cga_t *cga);
22 void cga_out(uint16_t addr, uint8_t val, void *p)
23 {
24 cga_t *cga = (cga_t *)p;
25 uint8_t old;
26 // pclog("CGA_OUT %04X %02X\n", addr, val);
27 switch (addr)
28 {
29 case 0x3D4:
30 cga->crtcreg = val & 31;
31 return;
32 case 0x3D5:
33 old = cga->crtc[cga->crtcreg];
34 cga->crtc[cga->crtcreg] = val & crtcmask[cga->crtcreg];
35 if (old != val)
36 {
37 if (cga->crtcreg < 0xe || cga->crtcreg > 0x10)
38 {
39 fullchange = changeframecount;
40 cga_recalctimings(cga);
41 }
42 }
43 return;
44 case 0x3D8:
45 cga->cgamode = val;
46 return;
47 case 0x3D9:
48 cga->cgacol = val;
49 return;
50 }
51 }
53 uint8_t cga_in(uint16_t addr, void *p)
54 {
55 cga_t *cga = (cga_t *)p;
56 // pclog("CGA_IN %04X\n", addr);
57 switch (addr)
58 {
59 case 0x3D4:
60 return cga->crtcreg;
61 case 0x3D5:
62 return cga->crtc[cga->crtcreg];
63 case 0x3DA:
64 return cga->cgastat;
65 }
66 return 0xFF;
67 }
69 void cga_write(uint32_t addr, uint8_t val, void *p)
70 {
71 cga_t *cga = (cga_t *)p;
72 // pclog("CGA_WRITE %04X %02X\n", addr, val);
73 cga->vram[addr & 0x3fff] = val;
74 cga->charbuffer[ ((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc] = val;
75 cga->charbuffer[(((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc) | 1] = val;
76 egawrites++;
77 cycles -= 4;
78 }
80 uint8_t cga_read(uint32_t addr, void *p)
81 {
82 cga_t *cga = (cga_t *)p;
83 cycles -= 4;
84 cga->charbuffer[ ((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc] = cga->vram[addr & 0x3fff];
85 cga->charbuffer[(((int)(((cga->dispontime - cga->vidtime) * 2) / CGACONST)) & 0xfc) | 1] = cga->vram[addr & 0x3fff];
86 egareads++;
87 // pclog("CGA_READ %04X\n", addr);
88 return cga->vram[addr & 0x3fff];
89 }
91 void cga_recalctimings(cga_t *cga)
92 {
93 double disptime;
94 double _dispontime, _dispofftime;
95 pclog("Recalc - %i %i %i\n", cga->crtc[0], cga->crtc[1], cga->cgamode & 1);
96 if (cga->cgamode & 1)
97 {
98 disptime = cga->crtc[0] + 1;
99 _dispontime = cga->crtc[1];
100 }
101 else
102 {
103 disptime = (cga->crtc[0] + 1) << 1;
104 _dispontime = cga->crtc[1] << 1;
105 }
106 _dispofftime = disptime - _dispontime;
107 // printf("%i %f %f %f %i %i\n",cgamode&1,disptime,dispontime,dispofftime,crtc[0],crtc[1]);
108 _dispontime *= CGACONST;
109 _dispofftime *= CGACONST;
110 // printf("Timings - on %f off %f frame %f second %f\n",dispontime,dispofftime,(dispontime+dispofftime)*262.0,(dispontime+dispofftime)*262.0*59.92);
111 cga->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT));
112 cga->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT));
113 }
115 static int ntsc_col[8][8]=
116 {
117 {0,0,0,0,0,0,0,0}, /*Black*/
118 {0,0,1,1,1,1,0,0}, /*Blue*/
119 {1,0,0,0,0,1,1,1}, /*Green*/
120 {0,0,0,0,1,1,1,1}, /*Cyan*/
121 {1,1,1,1,0,0,0,0}, /*Red*/
122 {0,1,1,1,1,0,0,0}, /*Magenta*/
123 {1,1,0,0,0,0,1,1}, /*Yellow*/
124 {1,1,1,1,1,1,1,1} /*White*/
125 };
127 void cga_poll(void *p)
128 {
129 cga_t *cga = (cga_t *)p;
130 uint16_t ca = (cga->crtc[15] | (cga->crtc[14] << 8)) & 0x3fff;
131 int drawcursor;
132 int x, c;
133 int oldvc;
134 uint8_t chr, attr;
135 uint16_t dat;
136 int cols[4];
137 int col;
138 int oldsc;
139 int y_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}, y_val, y_tot;
140 int i_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}, i_val, i_tot;
141 int q_buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}, q_val, q_tot;
142 int r, g, b;
143 if (!cga->linepos)
144 {
145 cga->vidtime += cga->dispofftime;
146 cga->cgastat |= 1;
147 cga->linepos = 1;
148 oldsc = cga->sc;
149 if ((cga->crtc[8] & 3) == 3)
150 cga->sc = ((cga->sc << 1) + cga->oddeven) & 7;
151 if (cga->cgadispon)
152 {
153 if (cga->displine < cga->firstline)
154 {
155 cga->firstline = cga->displine;
156 // printf("Firstline %i\n",firstline);
157 }
158 cga->lastline = cga->displine;
159 for (c = 0; c < 8; c++)
160 {
161 if ((cga->cgamode & 0x12) == 0x12)
162 {
163 buffer->line[cga->displine][c] = 0;
164 if (cga->cgamode & 1) buffer->line[cga->displine][c + (cga->crtc[1] << 3) + 8] = 0;
165 else buffer->line[cga->displine][c + (cga->crtc[1] << 4) + 8] = 0;
166 }
167 else
168 {
169 buffer->line[cga->displine][c] = (cga->cgacol & 15) + 16;
170 if (cga->cgamode & 1) buffer->line[cga->displine][c + (cga->crtc[1] << 3) + 8] = (cga->cgacol & 15) + 16;
171 else buffer->line[cga->displine][c + (cga->crtc[1] << 4) + 8] = (cga->cgacol & 15) + 16;
172 }
173 }
174 if (cga->cgamode & 1)
175 {
176 for (x = 0; x < cga->crtc[1]; x++)
177 {
178 chr = cga->charbuffer[x << 1];
179 attr = cga->charbuffer[(x << 1) + 1];
180 drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
181 if (cga->cgamode & 0x20)
182 {
183 cols[1] = (attr & 15) + 16;
184 cols[0] = ((attr >> 4) & 7) + 16;
185 if ((cga->cgablink & 8) && (attr & 0x80) && !cga->drawcursor)
186 cols[1] = cols[0];
187 }
188 else
189 {
190 cols[1] = (attr & 15) + 16;
191 cols[0] = (attr >> 4) + 16;
192 }
193 if (drawcursor)
194 {
195 for (c = 0; c < 8; c++)
196 buffer->line[cga->displine][(x << 3) + c + 8] = cols[(fontdat[chr][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15;
197 }
198 else
199 {
200 for (c = 0; c < 8; c++)
201 buffer->line[cga->displine][(x << 3) + c + 8] = cols[(fontdat[chr][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
202 }
203 cga->ma++;
204 }
205 }
206 else if (!(cga->cgamode & 2))
207 {
208 for (x = 0; x < cga->crtc[1]; x++)
209 {
210 chr = cga->vram[((cga->ma << 1) & 0x3fff)];
211 attr = cga->vram[(((cga->ma << 1) + 1) & 0x3fff)];
212 drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
213 if (cga->cgamode & 0x20)
214 {
215 cols[1] = (attr & 15) + 16;
216 cols[0] = ((attr >> 4) & 7) + 16;
217 if ((cga->cgablink & 8) && (attr & 0x80)) cols[1] = cols[0];
218 }
219 else
220 {
221 cols[1] = (attr & 15) + 16;
222 cols[0] = (attr >> 4) + 16;
223 }
224 cga->ma++;
225 if (drawcursor)
226 {
227 for (c = 0; c < 8; c++)
228 buffer->line[cga->displine][(x << 4)+(c << 1) + 8] = buffer->line[cga->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15;
229 }
230 else
231 {
232 for (c = 0; c < 8; c++)
233 buffer->line[cga->displine][(x << 4) + (c << 1) + 8] = buffer->line[cga->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][cga->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
234 }
235 }
236 }
237 else if (!(cga->cgamode & 16))
238 {
239 cols[0] = (cga->cgacol & 15) | 16;
240 col = (cga->cgacol & 16) ? 24 : 16;
241 if (cga->cgamode & 4)
242 {
243 cols[1] = col | 3;
244 cols[2] = col | 4;
245 cols[3] = col | 7;
246 }
247 else if (cga->cgacol & 32)
248 {
249 cols[1] = col | 3;
250 cols[2] = col | 5;
251 cols[3] = col | 7;
252 }
253 else
254 {
255 cols[1] = col | 2;
256 cols[2] = col | 4;
257 cols[3] = col | 6;
258 }
259 for (x = 0; x < cga->crtc[1]; x++)
260 {
261 dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1];
262 cga->ma++;
263 for (c = 0; c < 8; c++)
264 {
265 buffer->line[cga->displine][(x << 4) + (c << 1) + 8] =
266 buffer->line[cga->displine][(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14];
267 dat <<= 2;
268 }
269 }
270 }
271 else
272 {
273 cols[0] = 0; cols[1] = (cga->cgacol & 15) + 16;
274 for (x = 0; x < cga->crtc[1]; x++)
275 {
276 dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1];
277 cga->ma++;
278 for (c = 0; c < 16; c++)
279 {
280 buffer->line[cga->displine][(x << 4) + c + 8] = cols[dat >> 15];
281 dat <<= 1;
282 }
283 }
284 }
285 }
286 else
287 {
288 cols[0] = ((cga->cgamode & 0x12) == 0x12) ? 0 : (cga->cgacol & 15) + 16;
289 if (cga->cgamode & 1) hline(buffer, 0, cga->displine, (cga->crtc[1] << 3) + 16, cols[0]);
290 else hline(buffer, 0, cga->displine, (cga->crtc[1] << 4) + 16, cols[0]);
291 }
293 if (cga->cgamode & 1) x = (cga->crtc[1] << 3) + 16;
294 else x = (cga->crtc[1] << 4) + 16;
295 if (cga_comp)
296 {
297 for (c = 0; c < x; c++)
298 {
299 y_buf[(c << 1) & 6] = ntsc_col[buffer->line[cga->displine][c] & 7][(c << 1) & 6] ? 0x6000 : 0;
300 y_buf[(c << 1) & 6] += (buffer->line[cga->displine][c] & 8) ? 0x3000 : 0;
301 i_buf[(c << 1) & 6] = y_buf[(c << 1) & 6] * i_filt[(c << 1) & 6];
302 q_buf[(c << 1) & 6] = y_buf[(c << 1) & 6] * q_filt[(c << 1) & 6];
303 y_tot = y_buf[0] + y_buf[1] + y_buf[2] + y_buf[3] + y_buf[4] + y_buf[5] + y_buf[6] + y_buf[7];
304 i_tot = i_buf[0] + i_buf[1] + i_buf[2] + i_buf[3] + i_buf[4] + i_buf[5] + i_buf[6] + i_buf[7];
305 q_tot = q_buf[0] + q_buf[1] + q_buf[2] + q_buf[3] + q_buf[4] + q_buf[5] + q_buf[6] + q_buf[7];
307 y_val = y_tot >> 10;
308 if (y_val > 255) y_val = 255;
309 y_val <<= 16;
310 i_val = i_tot >> 12;
311 if (i_val > 39041) i_val = 39041;
312 if (i_val < -39041) i_val = -39041;
313 q_val = q_tot >> 12;
314 if (q_val > 34249) q_val = 34249;
315 if (q_val < -34249) q_val = -34249;
317 r = (y_val + 249*i_val + 159*q_val) >> 16;
318 g = (y_val - 70*i_val - 166*q_val) >> 16;
319 b = (y_val - 283*i_val + 436*q_val) >> 16;
321 y_buf[((c << 1) & 6) + 1] = ntsc_col[buffer->line[cga->displine][c] & 7][((c << 1) & 6) + 1] ? 0x6000 : 0;
322 y_buf[((c << 1) & 6) + 1] += (buffer->line[cga->displine][c] & 8) ? 0x3000 : 0;
323 i_buf[((c << 1) & 6) + 1] = y_buf[((c << 1) & 6) + 1] * i_filt[((c << 1) & 6) + 1];
324 q_buf[((c << 1) & 6) + 1] = y_buf[((c << 1) & 6) + 1] * q_filt[((c << 1) & 6) + 1];
325 y_tot = y_buf[0] + y_buf[1] + y_buf[2] + y_buf[3] + y_buf[4] + y_buf[5] + y_buf[6] + y_buf[7];
326 i_tot = i_buf[0] + i_buf[1] + i_buf[2] + i_buf[3] + i_buf[4] + i_buf[5] + i_buf[6] + i_buf[7];
327 q_tot = q_buf[0] + q_buf[1] + q_buf[2] + q_buf[3] + q_buf[4] + q_buf[5] + q_buf[6] + q_buf[7];
329 y_val = y_tot >> 10;
330 if (y_val > 255) y_val = 255;
331 y_val <<= 16;
332 i_val = i_tot >> 12;
333 if (i_val > 39041) i_val = 39041;
334 if (i_val < -39041) i_val = -39041;
335 q_val = q_tot >> 12;
336 if (q_val > 34249) q_val = 34249;
337 if (q_val < -34249) q_val = -34249;
339 r = (y_val + 249*i_val + 159*q_val) >> 16;
340 g = (y_val - 70*i_val - 166*q_val) >> 16;
341 b = (y_val - 283*i_val + 436*q_val) >> 16;
342 if (r > 511) r = 511;
343 if (g > 511) g = 511;
344 if (b > 511) b = 511;
346 ((uint32_t *)buffer32->line[cga->displine])[c] = makecol32(r / 2, g / 2, b / 2);
347 }
348 }
350 cga->sc = oldsc;
351 if (cga->vc == cga->crtc[7] && !cga->sc)
352 cga->cgastat |= 8;
353 cga->displine++;
354 if (cga->displine >= 360)
355 cga->displine = 0;
356 }
357 else
358 {
359 cga->vidtime += cga->dispontime;
360 if (cga->cgadispon) cga->cgastat &= ~1;
361 cga->linepos = 0;
362 if (cga->vsynctime)
363 {
364 cga->vsynctime--;
365 if (!cga->vsynctime)
366 cga->cgastat &= ~8;
367 }
368 if (cga->sc == (cga->crtc[11] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[11] & 31) >> 1)))
369 {
370 cga->con = 0;
371 cga->coff = 1;
372 }
373 if ((cga->crtc[8] & 3) == 3 && cga->sc == (cga->crtc[9] >> 1))
374 cga->maback = cga->ma;
375 if (cga->vadj)
376 {
377 cga->sc++;
378 cga->sc &= 31;
379 cga->ma = cga->maback;
380 cga->vadj--;
381 if (!cga->vadj)
382 {
383 cga->cgadispon = 1;
384 cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
385 cga->sc = 0;
386 }
387 }
388 else if (cga->sc == cga->crtc[9])
389 {
390 cga->maback = cga->ma;
391 cga->sc = 0;
392 oldvc = cga->vc;
393 cga->vc++;
394 cga->vc &= 127;
396 if (cga->vc == cga->crtc[6])
397 cga->cgadispon = 0;
399 if (oldvc == cga->crtc[4])
400 {
401 cga->vc = 0;
402 cga->vadj = cga->crtc[5];
403 if (!cga->vadj) cga->cgadispon = 1;
404 if (!cga->vadj) cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
405 if ((cga->crtc[10] & 0x60) == 0x20) cga->cursoron = 0;
406 else cga->cursoron = cga->cgablink & 8;
407 }
409 if (cga->vc == cga->crtc[7])
410 {
411 cga->cgadispon = 0;
412 cga->displine = 0;
413 cga->vsynctime = (cga->crtc[3] >> 4) + 1;
414 if (cga->crtc[7])
415 {
416 if (cga->cgamode & 1) x = (cga->crtc[1] << 3) + 16;
417 else x = (cga->crtc[1] << 4) + 16;
418 cga->lastline++;
419 if (x != xsize || (cga->lastline - cga->firstline) != ysize)
420 {
421 xsize = x;
422 ysize = cga->lastline - cga->firstline;
423 if (xsize < 64) xsize = 656;
424 if (ysize < 32) ysize = 200;
425 updatewindowsize(xsize, (ysize << 1) + 16);
426 }
428 startblit();
429 if (cga_comp)
430 video_blit_memtoscreen(0, cga->firstline - 4, 0, (cga->lastline - cga->firstline) + 8, xsize, (cga->lastline - cga->firstline) + 8);
431 else
432 video_blit_memtoscreen_8(0, cga->firstline - 4, xsize, (cga->lastline - cga->firstline) + 8);
433 frames++;
434 endblit();
435 video_res_x = xsize - 16;
436 video_res_y = ysize;
437 if (cga->cgamode & 1)
438 {
439 video_res_x /= 8;
440 video_res_y /= cga->crtc[9] + 1;
441 video_bpp = 0;
442 }
443 else if (!(cga->cgamode & 2))
444 {
445 video_res_x /= 16;
446 video_res_y /= cga->crtc[9] + 1;
447 video_bpp = 0;
448 }
449 else if (!(cga->cgamode & 16))
450 {
451 video_res_x /= 2;
452 video_bpp = 2;
453 }
454 else
455 {
456 video_bpp = 1;
457 }
458 }
459 cga->firstline = 1000;
460 cga->lastline = 0;
461 cga->cgablink++;
462 cga->oddeven ^= 1;
463 }
464 }
465 else
466 {
467 cga->sc++;
468 cga->sc &= 31;
469 cga->ma = cga->maback;
470 }
471 if ((cga->sc == (cga->crtc[10] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[10] & 31) >> 1))))
472 cga->con = 1;
473 if (cga->cgadispon && (cga->cgamode & 1))
474 {
475 for (x = 0; x < (cga->crtc[1] << 1); x++)
476 cga->charbuffer[x] = cga->vram[(((cga->ma << 1) + x) & 0x3fff)];
477 }
478 }
479 }
481 void cga_init(cga_t *cga)
482 {
483 }
485 void *cga_standalone_init()
486 {
487 int c;
488 int cga_tint = -2;
489 cga_t *cga = malloc(sizeof(cga_t));
490 memset(cga, 0, sizeof(cga_t));
492 cga->vram = malloc(0x4000);
494 for (c = 0; c < 8; c++)
495 {
496 i_filt[c] = 512.0 * cos((3.14 * (cga_tint + c * 4) / 16.0) - 33.0 / 180.0);
497 q_filt[c] = 512.0 * sin((3.14 * (cga_tint + c * 4) / 16.0) - 33.0 / 180.0);
498 }
499 timer_add(cga_poll, &cga->vidtime, TIMER_ALWAYS_ENABLED, cga);
500 mem_mapping_add(&cga->mapping, 0xb8000, 0x08000, cga_read, NULL, NULL, cga_write, NULL, NULL, NULL, 0, cga);
501 io_sethandler(0x03d0, 0x0010, cga_in, NULL, NULL, cga_out, NULL, NULL, cga);
502 return cga;
503 }
505 void cga_close(void *p)
506 {
507 cga_t *cga = (cga_t *)p;
509 free(cga->vram);
510 free(cga);
511 }
513 void cga_speed_changed(void *p)
514 {
515 cga_t *cga = (cga_t *)p;
517 cga_recalctimings(cga);
518 }
520 device_t cga_device =
521 {
522 "CGA",
523 0,
524 cga_standalone_init,
525 cga_close,
526 NULL,
527 cga_speed_changed,
528 NULL,
529 NULL
530 };
