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  *   O2 Video Display Controller 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 "config.h"
23 #include "keyboard.h"
24 #include "cset.h"
25 #include "timefunc.h"
26 #include "cpu.h"
27 #include "vpp.h"
28 #include "vdc.h"
29 #include "allegro.h"
30 
31 
32 #define COL_SP0   0x01
33 #define COL_SP1   0x02
34 #define COL_SP2   0x04
35 #define COL_SP3   0x08
36 #define COL_VGRID 0x10
37 #define COL_HGRID 0x20
38 #define COL_VPP   0x40
39 #define COL_CHAR  0x80
40 
41 #define X_START		8
42 #define Y_START		24
43 
44 
45 static long colortable[2][16]={
46 	/* O2 palette */
47 	{0x000000, 0x0e3dd4, 0x00981b, 0x00bbd9, 0xc70008, 0xcc16b3, 0x9d8710, 0xe1dee1,
48 	 0x5f6e6b, 0x6aa1ff, 0x3df07a, 0x31ffff, 0xff4255, 0xff98ff, 0xd9ad5d, 0xffffff},
49 	/* VP+ G7400 palette */
50 	{0x000000, 0x0000b6, 0x00b600, 0x00b6b6, 0xb60000, 0xb600b6, 0xb6b600, 0xb6b6b6,
51 	 0x494949, 0x4949ff, 0x49ff49, 0x49ffff, 0xff4949, 0xff49ff, 0xffff49, 0xffffff}
52 
53 };
54 
55 
56 /* Collision buffer */
57 static Byte *col = NULL;
58 
59 static PALETTE colors,oldcol;
60 
61 /* The pointer to the graphics buffer */
62 static Byte *vscreen = NULL;
63 
64 static BITMAP *bmp, *bmpcache;
65 static int cached_lines[MAXLINES];
66 
67 Byte coltab[256];
68 
69 long clip_low;
70 long clip_high;
71 
72 int show_fps=0;
73 
74 int wsize;
75 
76 static void create_cmap(void);
77 static void draw_char(Byte ypos,Byte xpos,Byte chr,Byte col);
78 static void draw_grid(void);
79 INLINE void mputvid(unsigned int ad, unsigned int len, Byte d, Byte c);
80 
81 
draw_region(void)82 void draw_region(void){
83 	int i;
84 
85 	if (regionoff == 0xffff)
86 		i = master_clk/(LINECNT-1)-5;
87 	else
88 		i = master_clk/22+regionoff;
89 
90 	i = snapline(i, VDCwrite[0xA0], 0);
91 
92 	if (i<0) i=0;
93  	clip_low = last_line * (long)BMPW;
94 	clip_high = i * (long)BMPW;
95 	if (clip_high > BMPW*BMPH) clip_high = BMPW*BMPH;
96 	if (clip_low < 0) clip_low=0;
97 	if (clip_low < clip_high) draw_display();
98 	last_line=i;
99 }
100 
101 
create_cmap(void)102 static void create_cmap(void){
103 	int i;
104 
105 	/* Initialise parts of the colors array */
106 	for (i = 0; i < 16; i++) {
107 		/* Use the color values from the color table */
108 		colors[i+32].r = colors[i].r = (colortable[app_data.vpp?1:0][i] & 0xff0000) >> 18;
109 		colors[i+32].g = colors[i].g = (colortable[app_data.vpp?1:0][i] & 0x00ff00) >> 10;
110 		colors[i+32].b = colors[i].b = (colortable[app_data.vpp?1:0][i] & 0x0000ff) >> 2;
111 	}
112 
113 	for (i = 16; i < 32; i++) {
114 		/* Half-bright colors for the 50% scanlines */
115 		colors[i+32].r = colors[i].r = colors[i-16].r/2;
116 		colors[i+32].g = colors[i].g = colors[i-16].g/2;
117 		colors[i+32].b = colors[i].b = colors[i-16].b/2;
118 	}
119 
120 	for (i = 64; i < 256; i++) colors[i].r = colors[i].g = colors[i].b = 0;
121 }
122 
123 
grmode(void)124 void grmode(void){
125 
126 	set_color_depth(8);
127 
128 	wsize = app_data.wsize;
129 
130 	if (app_data.fullscreen){
131 		if (app_data.scanlines){
132 			wsize = 2;
133 			if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0)){
134 				wsize = 1;
135 				if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 320, 240, 0, 0)) {
136 					fprintf(stderr,"Error: could not create screen.\n");
137 					exit(EXIT_FAILURE);
138 				}
139 			}
140 		} else {
141 			#ifdef ALLEGRO_DOS
142 			wsize = 1;
143 			if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 320, 240, 0, 0)){
144 				wsize = 2;
145 				if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0)){
146 					fprintf(stderr,"Error: could not create screen.\n");
147 					exit(EXIT_FAILURE);
148 				}
149 			}
150 			#else
151 			wsize = 2;
152 			if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 640, 480, 0, 0)){
153 				wsize = 1;
154 				if (set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, 320, 240, 0, 0)){
155 					fprintf(stderr,"Error: could not create screen.\n");
156 					exit(EXIT_FAILURE);
157 				}
158 			}
159 			#endif
160 		}
161 	} else {
162 		if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, WNDW*wsize, WNDH*wsize, 0, 0)){
163 			wsize = 2;
164 			if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, WNDW*2, WNDH*2, 0, 0)){
165 				if (set_gfx_mode(GFX_AUTODETECT, WNDW*2, WNDH*2, 0, 0)){
166 					fprintf(stderr,"Error: could not create window.\n");
167 					exit(EXIT_FAILURE);
168 				}
169 			}
170 			#ifndef ALLEGRO_DOS
171 			printf("Could not set the requested window size\n");
172 			#endif
173 		}
174 	}
175 
176 	if ((app_data.scanlines) && (wsize==1)) {
177 		#ifndef ALLEGRO_DOS
178 		printf("Could not set scanlines\n");
179 		#endif
180 	}
181 
182 	set_palette(colors);
183 	set_window_title(app_data.window_title);
184 	clearscr();
185 	set_display_switch_mode(SWITCH_PAUSE);
186 }
187 
188 
set_textmode(void)189 void set_textmode(void){
190 	set_palette(oldcol);
191 	set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
192 	if (new_int) Set_Old_Int9();
193 }
194 
195 
clearscr(void)196 void clearscr(void){
197 	acquire_screen();
198 	clear(screen);
199 	release_screen();
200 	clear(bmpcache);
201 }
202 
203 
mputvid(unsigned int ad,unsigned int len,Byte d,Byte c)204 INLINE void mputvid(unsigned int ad, unsigned int len, Byte d, Byte c){
205 	if ((ad > (unsigned long)clip_low) && (ad < (unsigned long)clip_high)) {
206 		unsigned int i;
207 		if (((len & 3)==0) && (sizeof(unsigned long) == 4)) {
208 			unsigned long dddd = (((unsigned long)d) & 0xff) | ((((unsigned long)d) & 0xff) << 8) | ((((unsigned long)d) & 0xff) << 16) | ((((unsigned long)d) & 0xff) << 24);
209 			unsigned long cccc = (((unsigned long)c) & 0xff) | ((((unsigned long)c) & 0xff) << 8) | ((((unsigned long)c) & 0xff) << 16) | ((((unsigned long)c) & 0xff) << 24);
210 			for (i=0; i<len>>2; i++) {
211 				*((unsigned long*)(vscreen+ad)) = dddd;
212 				cccc |= *((unsigned long*)(col+ad));
213 				*((unsigned long*)(col+ad)) = cccc;
214 				coltab[c] |= ((cccc | (cccc >> 8) | (cccc >> 16) | (cccc >> 24)) & 0xff);
215 				ad += 4;
216 			}
217 		} else {
218 			for (i=0; i<len; i++) {
219 				vscreen[ad]=d;
220 				col[ad] |= c;
221 				coltab[c] |= col[ad++];
222 			}
223 		}
224 	}
225 }
226 
227 
draw_grid(void)228 static void draw_grid(void){
229 	unsigned int pnt, pn1;
230 	Byte mask,d;
231 	int j,i,x,w;
232 	Byte color;
233 
234 	if (VDCwrite[0xA0] & 0x40) {
235 		for(j=0; j<9; j++) {
236 			pnt = (((j*24)+24) * BMPW);
237 			for (i=0; i<9; i++) {
238 				pn1 = pnt + (i * 32) + 20;
239 				color = ColorVector[j*24+24];
240 				mputvid(pn1, 4, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
241 				color = ColorVector[j*24+25];
242 				mputvid(pn1+BMPW, 4, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
243 				color = ColorVector[j*24+26];
244 				mputvid(pn1+BMPW*2, 4, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
245 			}
246 		}
247 	}
248 
249 	mask=0x01;
250 	for(j=0; j<9; j++) {
251 		pnt = (((j*24)+24) * BMPW);
252 		for (i=0; i<9; i++) {
253 			pn1 = pnt + (i * 32) + 20;
254 			if ((pn1+BMPW*3 >= (unsigned long)clip_low) && (pn1 <= (unsigned long)clip_high)) {
255 				d=VDCwrite[0xC0 + i];
256 				if (j == 8) {
257 					d=VDCwrite[0xD0+i];
258 					mask=1;
259 				}
260 				if (d & mask)	{
261 					color = ColorVector[j*24+24];
262 					mputvid(pn1, 36, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
263 					color = ColorVector[j*24+25];
264 					mputvid(pn1+BMPW, 36, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
265 					color = ColorVector[j*24+26];
266 					mputvid(pn1+BMPW*2, 36, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
267 				}
268 			}
269 		}
270 		mask = mask << 1;
271 	}
272 
273 	mask=0x01;
274 	w=4;
275 	if (VDCwrite[0xA0] & 0x80) w=32;
276 	for(j=0; j<10; j++) {
277 		pnt=(j*32);
278 		mask=0x01;
279 		d=VDCwrite[0xE0+j];
280 		for (x=0; x<8; x++) {
281 			pn1 = pnt + (((x*24)+24) * BMPW) + 20;
282 			if (d & mask) {
283 				for(i=0; i<24; i++) {
284 					if ((pn1 >= (unsigned long)clip_low) && (pn1 <= (unsigned long)clip_high)) {
285 						color = ColorVector[x*24+24+i];
286 						mputvid(pn1, w, (color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_VGRID);
287 					}
288 					pn1+=BMPW;
289 				}
290 			}
291 			mask = mask << 1;
292 		}
293 	}
294 
295 }
296 
297 
finish_display(void)298 void finish_display(void){
299 	int x,y,sn;
300 	static int cache_counter=0;
301 
302 	vpp_finish_bmp(vscreen, 9, 5, BMPW-9, BMPH-5, bmp->w, bmp->h);
303 
304 	if (show_fps) {
305 		static long last=-1, index=0, curr=0, t=0;
306 		if (last<0) last=gettimeticks();
307 		index = (index+1)%200;
308 		if (!index) {
309 			t=gettimeticks();
310 			curr=t-last;
311 			last=t;
312 		}
313 		if (curr) {
314 			text_mode(0);
315 			textprintf(bmp, font, 20 , 4, 7, "FPS: %3d",(int)((200.0*TICKSPERSEC)/curr+0.5));
316 		}
317 	}
318 
319 	for (y=0; y<bmp->h; y++){
320 		cached_lines[y] = !memcmp(bmpcache->line[y], bmp->line[y], bmp->w);
321 		if (!cached_lines[y]) memcpy(bmpcache->line[y], bmp->line[y], bmp->w);
322 	}
323 
324 	for (y=0; y<10; y++) cached_lines[(y+cache_counter) % bmp->h] = 0;
325 	cache_counter = (cache_counter+10) % bmp->h;
326 
327 	acquire_screen();
328 
329 	sn = ((wsize>1) && (app_data.scanlines)) ? 1 : 0;
330 
331 	for (y=0; y<WNDH; y++){
332 		if (!cached_lines[y+2])
333 			stretch_blit(bmp,screen,7,2+y,WNDW,1,0,y*wsize,WNDW*wsize,wsize-sn);
334 	}
335 
336 	if (sn){
337 		for (y=0; y<WNDH; y++) {
338 			if (!cached_lines[y+2]) {
339 				for (x=0; x<bmp->w; x++) bmp->line[y+2][x] += 16;
340 				stretch_blit(bmp,screen,7,2+y,WNDW,1,0,(y+1)*wsize-1,WNDW*wsize,1);
341 				memcpy(bmp->line[y+2], bmpcache->line[y+2], bmp->w);
342 			}
343 		}
344 	}
345 
346 	release_screen();
347 
348 }
349 
350 
clear_collision(void)351 void clear_collision(void){
352 	load_colplus(col);
353 	coltab[0x01]=coltab[0x02]=0;
354 	coltab[0x04]=coltab[0x08]=0;
355 	coltab[0x10]=coltab[0x20]=0;
356 	coltab[0x40]=coltab[0x80]=0;
357 }
358 
359 
draw_display(void)360 void draw_display(void){
361 	int i,j,x,sm,t;
362 	Byte y,xt,yt,b,d1,cl,c;
363 	unsigned int pnt,pnt2;
364 
365 	for (i=clip_low/BMPW; i<clip_high/BMPW; i++) memset(vscreen+i*BMPW, ((ColorVector[i] & 0x38) >> 3) | (ColorVector[i] & 0x80 ? 0 : 8), BMPW);
366 
367 	if (VDCwrite[0xA0] & 0x08) draw_grid();
368 
369 	if (useforen && (!(VDCwrite[0xA0] & 0x20))) return;
370 
371 	for(i=0x10; i<0x40; i+=4) draw_char(VDCwrite[i],VDCwrite[i+1],VDCwrite[i+2],VDCwrite[i+3]);
372 
373 	pnt=0x40;
374 	for(i=0; i<4; i++) {
375 		x=y=248;
376 		for (j=0; j<4; j++){
377 			xt = VDCwrite[pnt+j*4+1];
378 			yt = VDCwrite[pnt+j*4];
379 			if ((xt<240) && (yt<240)){
380 				x=xt;
381 				y=yt;
382 				break;
383 			}
384 		}
385 		for(j=0; j<4; j++) {
386 			draw_char(y,x,VDCwrite[pnt+2],VDCwrite[pnt+3]);
387 			x+=16;
388 			pnt+=4;
389 		}
390 	}
391 	c=8;
392 	for (i=12; i>=0; i -=4) {
393 		pnt2 = 0x80 + (i * 2);
394 		y = VDCwrite[i];
395 		x = VDCwrite[i+1]-8;
396 		t = VDCwrite[i+2];
397 		cl = ((t & 0x38) >> 3);
398 		cl = ((cl&2) | ((cl&1)<<2) | ((cl&4)>>2)) + 8;
399 		if ((x<164) && (y>0) && (y<232)) {
400 			pnt = y * BMPW + (x * 2) + 20 + sproff;
401 			if (t & 4) {
402 				if ((pnt+BMPW*32 >= (unsigned long)clip_low) && (pnt <= (unsigned long)clip_high)) {
403 					for (j=0; j<8; j++) {
404 						sm = (((j%2==0) && (((t>>1) & 1) != (t & 1))) || ((j%2==1) && (t & 1))) ? 1 : 0;
405 						d1 = VDCwrite[pnt2++];
406 						for (b=0; b<8; b++) {
407 							if (d1 & 0x01) {
408 								if ((x+b+sm < 159) && (y+j < 247)) {
409 									mputvid(sm+pnt,4,cl,c);
410 									mputvid(sm+pnt+BMPW,4,cl,c);
411 									mputvid(sm+pnt+2*BMPW,4,cl,c);
412 									mputvid(sm+pnt+3*BMPW,4,cl,c);
413 								}
414 							}
415 							pnt += 4;
416 							d1 = d1 >> 1;
417 						}
418 						pnt += BMPW*4-32;
419 					}
420 				}
421 			} else {
422 				if ((pnt+BMPW*16 >= (unsigned long)clip_low) && (pnt <= (unsigned long)clip_high)) {
423 					for (j=0; j<8; j++) {
424 						sm = (((j%2==0) && (((t>>1) & 1) != (t & 1))) || ((j%2==1) && (t & 1))) ? 1 : 0;
425 						d1 = VDCwrite[pnt2++];
426 						for (b=0; b<8; b++) {
427 							if (d1 & 0x01) {
428 								if ((x+b+sm<160) && (y+j<249)) {
429 									mputvid(sm+pnt,2,cl,c);
430 									mputvid(sm+pnt+BMPW,2,cl,c);
431 								}
432 							}
433 							pnt += 2;
434 							d1 = d1 >> 1;
435 						}
436 						pnt += BMPW*2-16;
437 					}
438 				}
439 			}
440 		}
441 		c = c >> 1;
442 	}
443 }
444 
445 
draw_char(Byte ypos,Byte xpos,Byte chr,Byte col)446 void draw_char(Byte ypos,Byte xpos,Byte chr,Byte col){
447 	int j,c;
448 	Byte cl,d1;
449 	int y,b,n;
450 	unsigned int pnt;
451 
452 	y=(ypos & 0xFE);
453 	pnt = y * BMPW + ((xpos-8) * 2)+20;
454 
455 	ypos = ypos >> 1;
456 	n = 8 - (ypos % 8) - (chr % 8);
457 	if (n < 3) n = n + 7;
458 
459 	if ((pnt+BMPW*2*n >= (unsigned long)clip_low) && (pnt <= (unsigned long)clip_high)) {
460 
461 		c=(int)chr + ypos;
462 		if (col & 0x01) c+=256;
463 		if (c > 511) c=c-512;
464 
465 		cl = ((col & 0x0E) >> 1);
466 		cl = ((cl&2) | ((cl&1)<<2) | ((cl&4)>>2)) + 8;
467 
468 		if ((y>0) && (y<232) && (xpos<157)) {
469 			for (j=0; j<n; j++) {
470 				d1 = cset[c+j];
471 				for (b=0; b<8; b++) {
472 					if (d1 & 0x80) {
473 						if ((xpos-8+b < 160) && (y+j < 240)) {
474 							mputvid(pnt,2,cl,COL_CHAR);
475 							mputvid(pnt+BMPW,2,cl,COL_CHAR);
476 						}
477 					}
478 					pnt+=2;
479 					d1 = d1 << 1;
480 				}
481 				pnt += BMPW*2-16;
482 			}
483 		}
484 	}
485 }
486 
487 
close_display(void)488 void close_display(void) {
489 	free(vscreen);
490 	free(col);
491 }
492 
493 
window_close_hook(void)494 void window_close_hook(void){
495 	key_debug=0;
496 	key_done=1;
497 }
498 
499 
txtmsg(int x,int y,int c,const char * s)500 static void txtmsg(int x, int y, int c, const char *s){
501 	text_mode(-1);
502 	textout_centre(bmp, font, s, x+1 , y+1, 32);
503 	textout_centre(bmp, font, s, x , y, c);
504 }
505 
506 
display_info(void)507 void display_info(void){
508 	char *ver;
509 
510 	rectfill(bmp,20,72,311,158,9+32);
511 	line(bmp,20,72,311,72,15+32);
512 	line(bmp,20,72,20,158,15+32);
513 	line(bmp,21,158,311,158,1+32);
514 	line(bmp,311,158,311,72,1+32);
515 
516 	#if defined(ALLEGRO_WINDOWS)
517 	ver = "Ueba!!!";
518 	#elif defined(ALLEGRO_DOS)
519 	ver = "DOS version";
520 	#elif defined(ALLEGRO_LINUX)
521 	ver = "Linux version";
522 	#elif defined(ALLEGRO_BEOS)
523 	ver = "BEOS version";
524 	#elif defined(ALLEGRO_QNX)
525 	ver = "QNX version";
526 	#elif defined(ALLEGRO_UNIX)
527 	ver = "UNIX version";
528 	#elif defined(ALLEGRO_MPW)
529 	ver = "MacOS version";
530 	#else
531 	ver = "Unknown platform";
532 	#endif
533 
534 	txtmsg(166,76,15+32,"O2EM v" O2EM_VERSION "  " RELEASE_DATE);
535 	txtmsg(166,90,15+32,"Free Odyssey2 / VP+ Emulator");
536 	txtmsg(166,104,15+32,ver);
537 	txtmsg(166,118,15+32,"Developed by Andre de la Rocha");
538 	txtmsg(166,132,15+32,"Copyright 1996/1998 by Daniel Boris");
539 	txtmsg(166,148,15+32,"Press F1 to continue");
540 
541 	finish_display();
542 }
543 
544 
init_display(void)545 void init_display(void) {
546 
547 	get_palette(oldcol);
548 	create_cmap();
549 
550 	bmp = create_bitmap(BMPW,BMPH);
551 	bmpcache = create_bitmap(BMPW,BMPH);
552 
553 	if ((!bmp) || (!bmpcache)) {
554 		fprintf(stderr,"Could not allocate memory for screen buffer.\n");
555 		exit(EXIT_FAILURE);
556 	}
557 
558 	vscreen = (Byte *) bmp->dat;
559 
560 	clear(bmp);
561 	clear(bmpcache);
562 
563 	col = (Byte *)malloc(BMPW*BMPH);
564 	if (!col) {
565 		fprintf(stderr,"Could not allocate memory for collision buffer.\n");
566 		free(vscreen);
567 		exit(EXIT_FAILURE);
568 	}
569 	memset(col,0,BMPW*BMPH);
570 
571 	if (!app_data.debug) {
572 		grmode();
573 		init_keyboard();
574 	}
575 
576 	set_window_close_button(TRUE);
577 	set_window_close_hook(window_close_hook);
578 
579 }
580 
581