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