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