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