1
2 /*
3 * O2EM Free Odyssey2 / Videopac+ Emulator
4 *
5 * Created by Daniel Boris <dboris@comcast.net> (c) 1997,1998
6 *
7 * Developed by Andre de la Rocha <adlroc@users.sourceforge.net>
8 *
9 * http://o2em.sourceforge.net
10 *
11 *
12 *
13 * Videopac+ G7400 emulation
14 */
15
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include "types.h"
21 #include "vmachine.h"
22 #include "vdc.h"
23 #include "vpp_cset.h"
24 #include "vpp.h"
25 #include "allegro.h"
26
27
28 static void vpp_draw_char(int x, int y, Byte ch, Byte c0, Byte c1, Byte ext, Byte dw, Byte dh, Byte ul);
29 static void vpp_update_screen(void);
30
31
32 static Byte LumReg = 0xff, TraReg = 0xff;
33 static BITMAP *vppbmp = NULL;
34 static Byte *colplus = NULL;
35 static int vppon = 1;
36 static int vpp_cx = 0;
37 static int vpp_cy = 0;
38 static Byte vpp_data = 0;
39 static int inc_curs=1;
40 static int slice=0;
41 static int vpp_y0=0;
42 static Byte vpp_r=0;
43 static Byte dchars[2][960];
44 static Byte vpp_mem[40][32][4];
45 static int frame_cnt=0;
46 static int blink_st=0;
47 static int slicemode=0;
48 static int need_update=0;
49
50
read_PB(Byte p)51 Byte read_PB(Byte p){
52 p &= 0x3;
53 switch (p) {
54 case 0:
55 return LumReg >> 4;
56 break;
57 case 1:
58 return LumReg & 0xf;
59 break;
60 case 2:
61 return TraReg >> 4;
62 break;
63 case 3:
64 return TraReg & 0xf;
65 break;
66 }
67 return 0;
68 }
69
70
write_PB(Byte p,Byte val)71 void write_PB(Byte p, Byte val){
72 p &= 0x3;
73 val &= 0xf;
74
75 switch (p) {
76 case 0:
77 LumReg = (val<<4) | (LumReg & 0xf);
78 break;
79 case 1:
80 LumReg = (LumReg & 0xf0) | val;
81 break;
82 case 2:
83 TraReg = (val<<4) | (TraReg & 0xf);
84 break;
85 case 3:
86 TraReg = (TraReg & 0xf0) | val;
87 break;
88 }
89 need_update = 1;
90 }
91
92
vpp_read(ADDRESS adr)93 Byte vpp_read(ADDRESS adr){
94 Byte t;
95 static Byte ta=0;
96 static Byte tb=0;
97
98 switch (adr){
99 case 4:
100 return ta;
101 case 5:
102 /* get return value from last read */
103 t = tb;
104 /* the real VPP starts a read cycle,
105 * the data gets returned at next read */
106 if (slicemode) {
107 Byte ext, chr;
108 chr = vpp_mem[vpp_cx][vpp_cy][0];
109 ext = (vpp_mem[vpp_cx][vpp_cy][1] & 0x80) ? 1 : 0;
110 if (chr < 0xA0) {
111 ta = 0;
112 fprintf(stderr, "unsupported: CHARROM read %d %d %d\n", chr, ext, slice);
113 } else {
114 ta = dchars[ext][(chr-0xA0)*10+slice];
115 ta = ((ta&0x80)>>7) | ((ta&0x40)>>5) | ((ta&0x20)>>3) | ((ta&0x10)>>1) | ((ta&0x08)<<1) | ((ta&0x04)<<3) | ((ta&0x02)<<5) | ((ta&0x01)<<7);
116 }
117 tb = 0xff; /* real VPP seems to return junk */
118 slice = (slice+1) % 10;
119 } else {
120 ta = vpp_mem[vpp_cx][vpp_cy][1];
121 tb = vpp_mem[vpp_cx][vpp_cy][0];
122 if (inc_curs) {
123 vpp_cx++;
124 if (vpp_cx >= 40) {
125 vpp_cx = 0;
126 vpp_cy++;
127 if (vpp_cy >= 24) vpp_cy = 0;
128 }
129 }
130 }
131 return t;
132 case 6:
133 return 0;
134 default:
135 return 0;
136 }
137 }
138
139
vpp_write(Byte dat,ADDRESS adr)140 void vpp_write(Byte dat, ADDRESS adr){
141 static Byte ta;
142
143 switch (adr) {
144 case 0:
145 if (!slicemode) vpp_mem[vpp_cx][vpp_cy][1] = dat;
146 else ta = dat;
147 break;
148 case 1:
149 if (slicemode) {
150 Byte ext, chr;
151 chr = vpp_mem[vpp_cx][vpp_cy][0];
152 ext = (vpp_mem[vpp_cx][vpp_cy][1] & 0x80) ? 1 : 0;
153 if (chr >= 0xA0) dchars[ext][(chr-0xA0)*10+slice] = ((ta&0x80)>>7) | ((ta&0x40)>>5) | ((ta&0x20)>>3) | ((ta&0x10)>>1) | ((ta&0x08)<<1) | ((ta&0x04)<<3) | ((ta&0x02)<<5) | ((ta&0x01)<<7);
154 slice = (slice+1) % 10;
155 } else {
156 vpp_mem[vpp_cx][vpp_cy][0] = dat;
157 if ((dat>0x7f) && (dat<0xa0) && (!(vpp_mem[vpp_cx][vpp_cy][1] & 0x80))) {
158 vpp_mem[vpp_cx][vpp_cy][2] = dat;
159 vpp_mem[vpp_cx][vpp_cy][3] = vpp_mem[vpp_cx][vpp_cy][1];
160 } else {
161 vpp_mem[vpp_cx][vpp_cy][2] = vpp_mem[vpp_cx][vpp_cy][3] = 0;
162 }
163 if (inc_curs) {
164 vpp_cx++;
165 if (vpp_cx >= 40) {
166 vpp_cx = 0;
167 vpp_cy++;
168 if (vpp_cy >= 24) vpp_cy = 0;
169 }
170 }
171 }
172 break;
173 case 2:
174 vpp_data = dat;
175 break;
176 case 3:
177 switch (dat & 0xe0) {
178 case 0x00: /* plus_cmd_brow */
179 vpp_cy = vpp_data & 0x1f;
180 vpp_cx = 0;
181 break;
182 case 0x20: /* plus_cmd_loady */
183 vpp_cy = vpp_data & 0x1f;
184 break;
185 case 0x40: /* plus_cmd_loadx */
186 vpp_cx = (vpp_data & 0x3f) % 40;
187 break;
188 case 0x60: /* plus_cmd_incc */
189 vpp_cx++;
190 if (vpp_cx >= 40) {
191 vpp_cx = 0;
192 vpp_cy++;
193 if (vpp_cy >= 24) vpp_cy = 0;
194 }
195 break;
196 case 0x80: /* plus_cmd_loadm */
197 slicemode = 0;
198 slice = (vpp_data & 0x1f) % 10;
199 switch (vpp_data & 0xe0) {
200 case 0x00: /* plus_loadm_wr */
201 inc_curs = 1;
202 break;
203 case 0x20: /* plus_loadm_rd */
204 inc_curs = 1;
205 break;
206 case 0x40: /* plus_loadm_wrni */
207 inc_curs = 0;
208 break;
209 case 0x60: /* plus_loadm_rdni */
210 inc_curs = 0;
211 break;
212 case 0x80: /* plus_loadm_wrsl */
213 slicemode = 1;
214 break;
215 case 0xA0: /* plus_loadm_rdsl */
216 slicemode = 1;
217 break;
218 default:
219 break;
220 }
221 break;
222 case 0xA0: /* plus_cmd_loadr */
223 vpp_r = vpp_data;
224 break;
225 case 0xC0: /* plus_cmd_loady0 */
226 if (vpp_data & 0x20) fprintf(stderr, "unsupported: global double height");
227 vpp_y0 = (vpp_data & 0x1f) % 24;
228 break;
229 default:
230 break;
231 }
232 break;
233 default:
234 break;
235 }
236
237 need_update = 1;
238 }
239
240
vpp_finish_bmp(Byte * vmem,int offx,int offy,int w,int h,int totw,int toth)241 void vpp_finish_bmp(Byte *vmem, int offx, int offy, int w, int h, int totw, int toth){
242 int i, x, y, t, c, nc, clrx, clry;
243 int tcol[16], m[8] = {0x01, 0x10, 0x04, 0x40, 0x02, 0x20, 0x08, 0x80};
244 Byte *pnt, *pnt2, *pnt3;
245
246 if (vppon) {
247 memset(colplus,0,BMPW*BMPH);
248 vppon=0;
249 }
250
251 if (TraReg == 0xff) return;
252
253 vppon=1;
254
255 frame_cnt--;
256 if (frame_cnt<=0) {
257 frame_cnt = 100;
258 blink_st = 1-blink_st;
259 need_update = 1;
260 }
261
262 if (need_update) vpp_update_screen();
263
264 for (i=0; i<8; i++) tcol[i] = tcol[i+8] = !(TraReg & m[i]);
265
266 if (w > totw-offx) w = totw-offx;
267 if (h > toth-offy) h = toth-offy;
268
269 if (w > vppbmp->w) w = vppbmp->w;
270 if (h > vppbmp->h) h = vppbmp->h;
271
272 clrx = clry = 0;
273 for (i=0; (!clrx) && (i<totw); i++) if (tcol[vmem[offy*totw+i]&7]) clrx=1;
274 for (i=0; (!clry) && (i<toth); i++) if (tcol[vmem[i*totw+offx]&7]) clry=1;
275 if (clrx) for (y=0; y<offy; y++) for (x=0; x<totw; x++) vmem[y*totw+x]=0;
276 if (clry) for (y=0; y<toth; y++) for (x=0; x<offx; x++) vmem[y*totw+x]=0;
277
278 for (y=0; y<h; y++){
279 pnt = vmem+(offy+y)*totw + offx;
280 pnt2 = (Byte *)vppbmp->line[y];
281
282 x=0;
283 while (x < w) {
284 pnt3 = pnt;
285 c = *pnt++;
286 t = x++;
287
288 if ((((x+offx) & 3) == 0) && (sizeof(unsigned long)==4)) {
289 unsigned long cccc, dddd, *p = (unsigned long*) pnt;
290 int t2=x, w2=w-4;
291 cccc = (((unsigned long)c) & 0xff) | ((((unsigned long)c) & 0xff) << 8) | ((((unsigned long)c) & 0xff) << 16) | ((((unsigned long)c) & 0xff) << 24);
292 dddd = *p++;
293 while ((x<w2) && (dddd == cccc)) {
294 x += 4;
295 dddd = *p++;
296 }
297 pnt += x-t2;
298 }
299
300 if (c<16) {
301 if (tcol[c]){
302 if (app_data.openb)
303 for (i=0; i<x-t; i++) *pnt3++ = *pnt2++ & 0xf;
304 else {
305 memcpy(pnt3, pnt2, x-t);
306 pnt2 += x-t;
307 }
308 } else {
309 for (i=0; i<x-t; i++) {
310 nc = *pnt2++;
311 if ((nc & 0x10) && app_data.openb) {
312 *pnt3++ = nc & 0xf;
313 } else if (nc & 8) {
314 colplus[pnt3++ - vmem] = 0x40;
315 } else {
316 pnt3++;
317 }
318 }
319 }
320 }
321
322 }
323
324 }
325
326 }
327
328
vpp_draw_char(int x,int y,Byte ch,Byte c0,Byte c1,Byte ext,Byte dw,Byte dh,Byte ul)329 static void vpp_draw_char(int x, int y, Byte ch, Byte c0, Byte c1, Byte ext, Byte dw, Byte dh, Byte ul){
330 int xx, yy, d, m, k;
331
332 if ((x>39) || (y>24) || (ext>1)) return;
333
334 d = (dh==2) ? 5 : 0;
335
336 for (yy=0; yy<10; yy++) {
337 if (ul && (d==9))
338 k = 255;
339 else if (ch >= 0xA0)
340 k = dchars[ext][(ch-0xA0)*10 + d];
341 else if (ch >= 0x80)
342 k = 255;
343 else
344 k = vpp_cset[ext][ch * 10 + d];
345
346 m = (dw==2) ? 0x08 : 0x80;
347
348 for (xx=0; xx<8; xx++) {
349 vppbmp->line[y*10+yy][x*8+xx] = (k & m) ? c1 : c0;
350 if ((xx%2) || (dw==0)) m >>= 1;
351 }
352 if ((yy%2) || (dh==0)) d++;
353 }
354 }
355
356
vpp_update_screen(void)357 static void vpp_update_screen(void){
358 int i,x,y,l,chr,attr,ext,c0,c1,dw,dh,hpar,vpar,lvd,lhd,ser_chr,ser_atr,ul,conc,box,swapcol;
359 int tlum[8], m[8] = {0x01, 0x10, 0x04, 0x40, 0x02, 0x20, 0x08, 0x80};
360
361 clear(vppbmp);
362
363 for (i=0; i<8; i++) tlum[i] = (LumReg & m[i]) ? 0 : 8;
364
365 vpar = lvd = 0;
366 for (y=0; y<25; y++) {
367
368 vpar = (lvd==0) ? 0 : 1-vpar;
369
370 l = (y==0) ? 31 : (y-1+vpp_y0)%24;
371 c0 = ul = conc = box = 0;
372
373 hpar = lhd = 0;
374 for (x=0; x<40; x++) {
375 hpar = (lhd==0) ? 0 : 1-hpar;
376
377 chr = vpp_mem[x][l][0];
378 attr = vpp_mem[x][l][1];
379 c1 = attr & 0x7;
380 c1 = ((c1&2) | ((c1&1)<<2) | ((c1&4)>>2));
381 ext = (attr & 0x80) ? 1 : 0;
382
383 ser_chr = vpp_mem[x][l][2];
384 ser_atr = vpp_mem[x][l][3];
385 if (ser_chr) {
386 c0 = (ser_atr>>4) & 0x7;
387 c0 = ((c0&2) | ((c0&1)<<2) | ((c0&4)>>2));
388 ul = ser_chr & 4;
389 conc = ser_chr & 1;
390 box = ser_chr & 2;
391 }
392
393 if (ext) {
394 c0 = (attr>>4) & 0x7;
395 c0 = ((c0&2) | ((c0&1)<<2) | ((c0&4)>>2));
396 dw = dh = 0;
397 } else {
398 dw = (attr & 0x20) ? (hpar ? 2 : 1) : 0;
399 dh = (attr & 0x10) ? (vpar ? 2 : 1) : 0;
400 if (dw) lhd=1;
401 if (dh) lvd=1;
402 }
403
404 swapcol = 0;
405
406 /* cursor display */
407 if ((x == vpp_cx) && (l == vpp_cy)) {
408 /* on cursor position */
409 if (vpp_r & 0x10) {
410 /* cursor display active */
411 swapcol = !swapcol;
412 if ((vpp_r & 0x80) && blink_st) {
413 /* blinking active */
414 swapcol = !swapcol;
415 }
416 }
417 }
418
419 /* invert attribute */
420 if ((!ext) && (attr & 0x40)) swapcol = !swapcol;
421
422 /* blinking chars */
423 if ((vpp_r & 0x80) && !(attr & 8) && !blink_st) {
424 /* cursor handling is done already */
425 if (!(vpp_r & 0x10) || (x != vpp_cx) || (l != vpp_cy)) {
426 c1=c0;
427 }
428 }
429
430 if (((y == 0) && (vpp_r & 8)) || ((y != 0) && (vpp_r & 1))) {
431 if ((!conc) || (!(vpp_r & 4))) {
432 if (box || (!(vpp_r & 2))) {
433 if (swapcol)
434 vpp_draw_char(x, y, chr, c1|tlum[c1], c0|tlum[c0], ext, dw, dh, ul);
435 else
436 vpp_draw_char(x, y, chr, c0|tlum[c0], c1|tlum[c1], ext, dw, dh, ul);
437 } else {
438 vpp_draw_char(x, y, 255, (app_data.openb) ? 16 : 0, 0, 0, 0, 0, 0);
439 }
440 }
441 }
442 }
443
444 }
445
446 if (vpp_r & 0x20) {
447 for (y = vppbmp->h-1; y >= 10; y--)
448 for (x = 0; x < vppbmp->w; x++) vppbmp->line[y][x] = vppbmp->line[(y-10)/2+10][x];
449 }
450
451 need_update=0;
452 }
453
454
load_colplus(Byte * col)455 void load_colplus(Byte *col){
456 if (vppon)
457 memcpy(col,colplus,BMPW*BMPH);
458 else
459 memset(col,0,BMPW*BMPH);
460 }
461
462
init_vpp(void)463 void init_vpp(void){
464 int i,j,k;
465
466 if (!vppbmp) vppbmp = create_bitmap(320,250);
467 if (!colplus) colplus = (Byte *)malloc(BMPW*BMPH);
468
469 if ((!vppbmp) || (!colplus)) {
470 fprintf(stderr,"Could not allocate memory for Videopac+ screen buffer.\n");
471 exit(EXIT_FAILURE);
472 }
473
474 clear(vppbmp);
475 memset(colplus,0,BMPW*BMPH);
476
477 LumReg = TraReg = 0xff;
478 vpp_cx = 0;
479 vpp_cy = 0;
480 vpp_y0 = 0;
481 vpp_r = 0;
482 inc_curs = 1;
483 vpp_data = 0;
484 frame_cnt=0;
485 blink_st=0;
486 slice = 0;
487 slicemode=0;
488 need_update = 1;
489 vppon = 1;
490
491 for (i=0; i<2; i++)
492 for (j=0; j<960; j++) dchars[i][j] = 0;
493
494 for (i=0; i<40; i++)
495 for (j=0; j<32; j++)
496 for (k=0; k<4; k++) vpp_mem[i][j][k] = 0;
497 }
498
499