1 /*Olivetti M24 video emulation
2 Essentially double-res CGA*/
3 #include <stdlib.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_olivetti_m24.h"
11
12 typedef struct m24_t
13 {
14 mem_mapping_t mapping;
15
16 uint8_t crtc[32];
17 int crtcreg;
18
19 uint8_t *vram;
20 uint8_t charbuffer[256];
21
22 uint8_t ctrl;
23 uint32_t base;
24
25 uint8_t cgamode, cgacol;
26 uint8_t stat;
27
28 int linepos, displine;
29 int sc, vc;
30 int con, coff, cursoron, blink;
31 int vsynctime, vadj;
32 int lineff;
33 uint16_t ma, maback;
34 int dispon;
35
36 int dispontime, dispofftime, vidtime;
37
38 int firstline, lastline;
39 } m24_t;
40
41 static uint8_t crtcmask[32] =
42 {
43 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
44 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
45 };
46
47 void m24_recalctimings(m24_t *m24);
48
49
m24_out(uint16_t addr,uint8_t val,void * p)50 void m24_out(uint16_t addr, uint8_t val, void *p)
51 {
52 m24_t *m24 = (m24_t *)p;
53 uint8_t old;
54 // pclog("m24_out %04X %02X\n", addr, val);
55 switch (addr)
56 {
57 case 0x3d4:
58 m24->crtcreg = val & 31;
59 return;
60 case 0x3d5:
61 old = m24->crtc[m24->crtcreg];
62 m24->crtc[m24->crtcreg] = val & crtcmask[m24->crtcreg];
63 if (old != val)
64 {
65 if (m24->crtcreg < 0xe || m24->crtcreg > 0x10)
66 {
67 fullchange = changeframecount;
68 m24_recalctimings(m24);
69 }
70 }
71 return;
72 case 0x3d8:
73 m24->cgamode = val;
74 return;
75 case 0x3d9:
76 m24->cgacol = val;
77 return;
78 case 0x3de:
79 m24->ctrl = val;
80 m24->base = (val & 0x08) ? 0x4000 : 0;
81 return;
82 }
83 }
84
m24_in(uint16_t addr,void * p)85 uint8_t m24_in(uint16_t addr, void *p)
86 {
87 m24_t *m24 = (m24_t *)p;
88 switch (addr)
89 {
90 case 0x3d4:
91 return m24->crtcreg;
92 case 0x3d5:
93 return m24->crtc[m24->crtcreg];
94 case 0x3da:
95 return m24->stat;
96 }
97 return 0xff;
98 }
99
m24_write(uint32_t addr,uint8_t val,void * p)100 void m24_write(uint32_t addr, uint8_t val, void *p)
101 {
102 m24_t *m24 = (m24_t *)p;
103 m24->vram[addr & 0x7FFF]=val;
104 m24->charbuffer[ ((int)(((m24->dispontime - m24->vidtime) * 2) / (CGACONST / 2))) & 0xfc] = val;
105 m24->charbuffer[(((int)(((m24->dispontime - m24->vidtime) * 2) / (CGACONST / 2))) & 0xfc) | 1] = val;
106 }
107
m24_read(uint32_t addr,void * p)108 uint8_t m24_read(uint32_t addr, void *p)
109 {
110 m24_t *m24 = (m24_t *)p;
111 return m24->vram[addr & 0x7FFF];
112 }
113
m24_recalctimings(m24_t * m24)114 void m24_recalctimings(m24_t *m24)
115 {
116 double _dispontime, _dispofftime, disptime;
117 if (m24->cgamode & 1)
118 {
119 disptime = m24->crtc[0] + 1;
120 _dispontime = m24->crtc[1];
121 }
122 else
123 {
124 disptime = (m24->crtc[0] + 1) << 1;
125 _dispontime = m24->crtc[1] << 1;
126 }
127 _dispofftime = disptime - _dispontime;
128 // printf("%i %f %f %f %i %i\n",cgamode&1,disptime,dispontime,dispofftime,crtc[0],crtc[1]);
129 _dispontime *= CGACONST / 2;
130 _dispofftime *= CGACONST / 2;
131 // printf("Timings - on %f off %f frame %f second %f\n",dispontime,dispofftime,(dispontime+dispofftime)*262.0,(dispontime+dispofftime)*262.0*59.92);
132 m24->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT));
133 m24->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT));
134 }
135
m24_poll(void * p)136 void m24_poll(void *p)
137 {
138 m24_t *m24 = (m24_t *)p;
139 uint16_t ca = (m24->crtc[15] | (m24->crtc[14] << 8)) & 0x3fff;
140 int drawcursor;
141 int x, c;
142 int oldvc;
143 uint8_t chr, attr;
144 uint16_t dat, dat2;
145 uint32_t cols[4];
146 int col;
147 int oldsc;
148 if (!m24->linepos)
149 {
150 // pclog("Line poll %i %i %i %i - %04X %i %i %i\n", m24_lineff, vc, sc, vadj, ma, firstline, lastline, displine);
151 m24->vidtime += m24->dispofftime;
152 m24->stat |= 1;
153 m24->linepos = 1;
154 oldsc = m24->sc;
155 if ((m24->crtc[8] & 3) == 3)
156 m24->sc = (m24->sc << 1) & 7;
157 if (m24->dispon)
158 {
159 pclog("dispon %i\n", m24->linepos);
160 if (m24->displine < m24->firstline)
161 {
162 m24->firstline = m24->displine;
163 // printf("Firstline %i\n",firstline);
164 }
165 m24->lastline = m24->displine;
166 for (c = 0; c < 8; c++)
167 {
168 if ((m24->cgamode & 0x12) == 0x12)
169 {
170 ((uint32_t *)buffer32->line[m24->displine])[c] = cgapal[0];
171 if (m24->cgamode & 1) ((uint32_t *)buffer32->line[m24->displine])[c + (m24->crtc[1] << 3) + 8] = cgapal[0];
172 else ((uint32_t *)buffer32->line[m24->displine])[c + (m24->crtc[1] << 4) + 8] = cgapal[0];
173 }
174 else
175 {
176 ((uint32_t *)buffer32->line[m24->displine])[c] = cgapal[m24->cgacol & 15];
177 if (m24->cgamode & 1) ((uint32_t *)buffer32->line[m24->displine])[c + (m24->crtc[1] << 3) + 8] = cgapal[m24->cgacol & 15];
178 else ((uint32_t *)buffer32->line[m24->displine])[c + (m24->crtc[1] << 4) + 8] = cgapal[m24->cgacol & 15];
179 }
180 }
181 if (m24->cgamode & 1)
182 {
183 for (x = 0; x < m24->crtc[1]; x++)
184 {
185 chr = m24->charbuffer[ x << 1];
186 attr = m24->charbuffer[(x << 1) + 1];
187 drawcursor = ((m24->ma == ca) && m24->con && m24->cursoron);
188 if (m24->cgamode & 0x20)
189 {
190 cols[1] = cgapal[attr & 15];
191 cols[0] = cgapal[(attr >> 4) & 7];
192 if ((m24->blink & 16) && (attr & 0x80) && !drawcursor)
193 cols[1] = cols[0];
194 }
195 else
196 {
197 cols[1] = cgapal[attr & 15];
198 cols[0] = cgapal[attr >> 4];
199 }
200 if (drawcursor)
201 {
202 for (c = 0; c < 8; c++)
203 ((uint32_t *)buffer32->line[m24->displine])[(x << 3) + c + 8] = cols[(fontdatm[chr][((m24->sc & 7) << 1) | m24->lineff] & (1 << (c ^ 7))) ? 1 : 0] ^ 0xffffff;
204 }
205 else
206 {
207 for (c = 0; c < 8; c++)
208 ((uint32_t *)buffer32->line[m24->displine])[(x << 3) + c + 8] = cols[(fontdatm[chr][((m24->sc & 7) << 1) | m24->lineff] & (1 << (c ^ 7))) ? 1 : 0];
209 }
210 m24->ma++;
211 }
212 }
213 else if (!(m24->cgamode & 2))
214 {
215 for (x = 0; x < m24->crtc[1]; x++)
216 {
217 chr = m24->vram[((m24->ma << 1) & 0x3fff) + m24->base];
218 attr = m24->vram[(((m24->ma << 1) + 1) & 0x3fff) + m24->base];
219 drawcursor = ((m24->ma == ca) && m24->con && m24->cursoron);
220 if (m24->cgamode & 0x20)
221 {
222 cols[1] = cgapal[attr & 15];
223 cols[0] = cgapal[(attr >> 4) & 7];
224 if ((m24->blink & 16) && (attr & 0x80))
225 cols[1] = cols[0];
226 }
227 else
228 {
229 cols[1] = cgapal[attr & 15];
230 cols[0] = cgapal[attr >> 4];
231 }
232 m24->ma++;
233 if (drawcursor)
234 {
235 for (c = 0; c < 8; c++)
236 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + (c << 1) + 8] =
237 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + (c << 1) + 1 + 8] = cols[(fontdatm[chr][((m24->sc & 7) << 1) | m24->lineff] & (1 << (c ^ 7))) ? 1 : 0] ^ 0xffffff;
238 }
239 else
240 {
241 for (c = 0; c < 8; c++)
242 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + (c << 1) + 8] =
243 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + (c << 1) + 1 + 8] = cols[(fontdatm[chr][((m24->sc & 7) << 1) | m24->lineff] & (1 << (c ^ 7))) ? 1 : 0];
244 }
245 }
246 }
247 else if (!(m24->cgamode & 16))
248 {
249 cols[0] = cgapal[m24->cgacol & 15];
250 col = (m24->cgacol & 16) ? 8 : 0;
251 if (m24->cgamode & 4)
252 {
253 cols[1] = cgapal[col | 3];
254 cols[2] = cgapal[col | 4];
255 cols[3] = cgapal[col | 7];
256 }
257 else if (m24->cgacol & 32)
258 {
259 cols[1] = cgapal[col | 3];
260 cols[2] = cgapal[col | 5];
261 cols[3] = cgapal[col | 7];
262 }
263 else
264 {
265 cols[1] = cgapal[col | 2];
266 cols[2] = cgapal[col | 4];
267 cols[3] = cgapal[col | 6];
268 }
269 for (x = 0; x < m24->crtc[1]; x++)
270 {
271 dat = (m24->vram[((m24->ma << 1) & 0x1fff) + ((m24->sc & 1) * 0x2000) + m24->base] << 8) |
272 m24->vram[((m24->ma << 1) & 0x1fff) + ((m24->sc & 1) * 0x2000) + 1 + m24->base];
273 m24->ma++;
274 for (c = 0; c < 8; c++)
275 {
276 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + (c << 1) + 8] =
277 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14];
278 dat <<= 2;
279 }
280 }
281 }
282 else
283 {
284 if (m24->ctrl & 1)
285 {
286 dat2 = ((m24->sc & 1) * 0x4000) | (m24->lineff * 0x2000);
287 cols[0] = cgapal[0];
288 cols[1] = cgapal[15];
289 }
290 else
291 {
292 dat2 = (m24->sc & 1) * 0x2000;
293 cols[0] = cgapal[0];
294 cols[1] = cgapal[m24->cgacol & 15];
295 }
296 for (x = 0; x < m24->crtc[1]; x++)
297 {
298 dat = (m24->vram[((m24->ma << 1) & 0x1fff) + dat2] << 8) | m24->vram[((m24->ma << 1) & 0x1fff) + dat2 + 1];
299 m24->ma++;
300 for (c = 0; c < 16; c++)
301 {
302 ((uint32_t *)buffer32->line[m24->displine])[(x << 4) + c + 8] = cols[dat >> 15];
303 dat <<= 1;
304 }
305 }
306 }
307 }
308 else
309 {
310 cols[0] = cgapal[((m24->cgamode & 0x12) == 0x12) ? 0 : (m24->cgacol & 15)];
311 if (m24->cgamode & 1) hline(buffer32, 0, m24->displine, (m24->crtc[1] << 3) + 16, cols[0]);
312 else hline(buffer32, 0, m24->displine, (m24->crtc[1] << 4) + 16, cols[0]);
313 }
314
315 if (m24->cgamode & 1) x = (m24->crtc[1] << 3) + 16;
316 else x = (m24->crtc[1] << 4) + 16;
317
318 m24->sc = oldsc;
319 if (m24->vc == m24->crtc[7] && !m24->sc)
320 m24->stat |= 8;
321 m24->displine++;
322 if (m24->displine >= 720) m24->displine = 0;
323 }
324 else
325 {
326 // pclog("Line poll %i %i %i %i\n", m24_lineff, vc, sc, vadj);
327 m24->vidtime += m24->dispontime;
328 if (m24->dispon) m24->stat &= ~1;
329 m24->linepos = 0;
330 m24->lineff ^= 1;
331 if (m24->lineff)
332 {
333 m24->ma = m24->maback;
334 }
335 else
336 {
337 if (m24->vsynctime)
338 {
339 m24->vsynctime--;
340 if (!m24->vsynctime)
341 m24->stat &= ~8;
342 }
343 if (m24->sc == (m24->crtc[11] & 31) || ((m24->crtc[8] & 3) == 3 && m24->sc == ((m24->crtc[11] & 31) >> 1)))
344 {
345 m24->con = 0;
346 m24->coff = 1;
347 }
348 if (m24->vadj)
349 {
350 m24->sc++;
351 m24->sc &= 31;
352 m24->ma = m24->maback;
353 m24->vadj--;
354 if (!m24->vadj)
355 {
356 m24->dispon = 1;
357 m24->ma = m24->maback = (m24->crtc[13] | (m24->crtc[12] << 8)) & 0x3fff;
358 m24->sc = 0;
359 }
360 }
361 else if (m24->sc == m24->crtc[9] || ((m24->crtc[8] & 3) == 3 && m24->sc == (m24->crtc[9] >> 1)))
362 {
363 m24->maback = m24->ma;
364 m24->sc = 0;
365 oldvc = m24->vc;
366 m24->vc++;
367 m24->vc &= 127;
368
369 if (m24->vc == m24->crtc[6])
370 m24->dispon=0;
371
372 if (oldvc == m24->crtc[4])
373 {
374 m24->vc = 0;
375 m24->vadj = m24->crtc[5];
376 if (!m24->vadj) m24->dispon = 1;
377 if (!m24->vadj) m24->ma = m24->maback = (m24->crtc[13] | (m24->crtc[12] << 8)) & 0x3fff;
378 if ((m24->crtc[10] & 0x60) == 0x20) m24->cursoron = 0;
379 else m24->cursoron = m24->blink & 16;
380 }
381
382 if (m24->vc == m24->crtc[7])
383 {
384 m24->dispon = 0;
385 m24->displine = 0;
386 m24->vsynctime = (m24->crtc[3] >> 4) + 1;
387 if (m24->crtc[7])
388 {
389 if (m24->cgamode & 1) x = (m24->crtc[1] << 3) + 16;
390 else x = (m24->crtc[1] << 4) + 16;
391 m24->lastline++;
392 if (x != xsize || (m24->lastline - m24->firstline) != ysize)
393 {
394 xsize = x;
395 ysize = m24->lastline - m24->firstline;
396 if (xsize < 64) xsize = 656;
397 if (ysize < 32) ysize = 200;
398 updatewindowsize(xsize, ysize + 16);
399 }
400
401 video_blit_memtoscreen(0, m24->firstline - 8, 0, (m24->lastline - m24->firstline) + 16, xsize, (m24->lastline - m24->firstline) + 16);
402 frames++;
403
404 video_res_x = xsize - 16;
405 video_res_y = ysize;
406 if (m24->cgamode & 1)
407 {
408 video_res_x /= 8;
409 video_res_y /= (m24->crtc[9] + 1) * 2;
410 video_bpp = 0;
411 }
412 else if (!(m24->cgamode & 2))
413 {
414 video_res_x /= 16;
415 video_res_y /= (m24->crtc[9] + 1) * 2;
416 video_bpp = 0;
417 }
418 else if (!(m24->cgamode & 16))
419 {
420 video_res_x /= 2;
421 video_res_y /= 2;
422 video_bpp = 2;
423 }
424 else if (!(m24->ctrl & 1))
425 {
426 video_res_y /= 2;
427 video_bpp = 1;
428 }
429 }
430 m24->firstline = 1000;
431 m24->lastline = 0;
432 m24->blink++;
433 }
434 }
435 else
436 {
437 m24->sc++;
438 m24->sc &= 31;
439 m24->ma = m24->maback;
440 }
441 if ((m24->sc == (m24->crtc[10] & 31) || ((m24->crtc[8] & 3) == 3 && m24->sc == ((m24->crtc[10] & 31) >> 1))))
442 m24->con = 1;
443 }
444 if (m24->dispon && (m24->cgamode & 1))
445 {
446 for (x = 0; x < (m24->crtc[1] << 1); x++)
447 m24->charbuffer[x] = m24->vram[(((m24->ma << 1) + x) & 0x3fff) + m24->base];
448 }
449 }
450 }
451
m24_init()452 void *m24_init()
453 {
454 m24_t *m24 = malloc(sizeof(m24_t));
455 memset(m24, 0, sizeof(m24_t));
456
457 m24->vram = malloc(0x8000);
458
459 timer_add(m24_poll, &m24->vidtime, TIMER_ALWAYS_ENABLED, m24);
460 mem_mapping_add(&m24->mapping, 0xb8000, 0x08000, m24_read, NULL, NULL, m24_write, NULL, NULL, NULL, 0, m24);
461 io_sethandler(0x03d0, 0x0010, m24_in, NULL, NULL, m24_out, NULL, NULL, m24);
462 return m24;
463 }
464
m24_close(void * p)465 void m24_close(void *p)
466 {
467 m24_t *m24 = (m24_t *)p;
468
469 free(m24->vram);
470 free(m24);
471 }
472
m24_speed_changed(void * p)473 void m24_speed_changed(void *p)
474 {
475 m24_t *m24 = (m24_t *)p;
476
477 m24_recalctimings(m24);
478 }
479
480 device_t m24_device =
481 {
482 "Olivetti M24 (video)",
483 0,
484 m24_init,
485 m24_close,
486 NULL,
487 m24_speed_changed,
488 NULL,
489 NULL
490 };
491