557 lines
12 KiB
C
557 lines
12 KiB
C
/*
|
|
* O2EM Free Odyssey2 / Videopac+ Emulator
|
|
*
|
|
* Created by Daniel Boris <dboris@comcast.net> (c) 1997,1998
|
|
*
|
|
* Developed by Andre de la Rocha <adlroc@users.sourceforge.net>
|
|
* Arlindo M. de Oliveira <dgtec@users.sourceforge.net>
|
|
*
|
|
* http://o2em.sourceforge.net
|
|
*
|
|
*
|
|
*
|
|
* O2 Video Display Controller emulation
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include "types.h"
|
|
#include "vmachine.h"
|
|
#include "config.h"
|
|
#include "keyboard.h"
|
|
#include "cset.h"
|
|
#include "cpu.h"
|
|
#include "vpp.h"
|
|
#include "vdc.h"
|
|
#include "audio.h"
|
|
#include "voice.h"
|
|
#include "bitmap.h"
|
|
|
|
#define COL_SP0 0x01
|
|
#define COL_SP1 0x02
|
|
#define COL_SP2 0x04
|
|
#define COL_SP3 0x08
|
|
#define COL_VGRID 0x10
|
|
#define COL_HGRID 0x20
|
|
#define COL_VPP 0x40
|
|
#define COL_CHAR 0x80
|
|
|
|
#define X_START 8
|
|
#define Y_START 24
|
|
|
|
static const uint32_t colortable[2][16] = {
|
|
/* O2 palette */
|
|
{0x000000, 0x0e3dd4, 0x00981b, 0x00bbd9, 0xc70008, 0xcc16b3, 0x9d8710,
|
|
0xe1dee1, 0x5f6e6b, 0x6aa1ff, 0x3df07a, 0x31ffff, 0xff4255, 0xff98ff,
|
|
0xd9ad5d, 0xffffff},
|
|
/* VP+ G7400 palette */
|
|
{0x000000, 0x0000b6, 0x00b600, 0x00b6b6, 0xb60000, 0xb600b6, 0xb6b600,
|
|
0xb6b6b6, 0x494949, 0x4949ff, 0x49ff49, 0x49ffff, 0xff4949, 0xff49ff,
|
|
0xffff49, 0xffffff}};
|
|
|
|
static Bitmap* bitmap;
|
|
|
|
/* Collision buffer */
|
|
static Byte *col = NULL;
|
|
|
|
/* The pointer to the graphics buffer */
|
|
static Byte *vscreen = NULL;
|
|
|
|
static int cached_lines[MAXLINES];
|
|
|
|
Byte coltab[256];
|
|
|
|
long clip_low;
|
|
long clip_high;
|
|
|
|
int wsize;
|
|
|
|
static void create_cmap(void);
|
|
static void draw_char(Byte ypos, Byte xpos, Byte chr, Byte col);
|
|
static void draw_quad(Byte ypos, Byte xpos, Byte cp0l, Byte cp0h, Byte cp1l,
|
|
Byte cp1h, Byte cp2l, Byte cp2h, Byte cp3l, Byte cp3h);
|
|
static void draw_grid(void);
|
|
void mputvid(unsigned int ad, unsigned int len, Byte d, Byte c);
|
|
|
|
void draw_region(void)
|
|
{
|
|
int i;
|
|
if (regionoff == 0xffff)
|
|
{
|
|
i = (master_clk / (LINECNT - 1) - 5);
|
|
}
|
|
else
|
|
{
|
|
i = (master_clk / 22 + regionoff);
|
|
}
|
|
i = (snapline(i, VDCwrite[0xA0], 0));
|
|
if (app_data.crc == 0xA7344D1F)
|
|
{
|
|
i = (master_clk / 22 + regionoff) + 6;
|
|
i = (snapline(i, VDCwrite[0xA0], 0) + 6);
|
|
} /*Atlantis*/
|
|
|
|
if (app_data.crc == 0xD0BC4EE6)
|
|
{
|
|
i = (master_clk / 24 + regionoff) - 6;
|
|
i = (snapline(i, VDCwrite[0xA0], 0) + 7);
|
|
} /*Frogger*/
|
|
|
|
if (app_data.crc == 0x26517E77)
|
|
{
|
|
i = (master_clk / 22 + regionoff);
|
|
i = (snapline(i, VDCwrite[0xA0], 0) - 5);
|
|
} /*Comando Noturno*/
|
|
|
|
if (app_data.crc == 0xA57E1724)
|
|
{
|
|
i = (master_clk / (LINECNT - 1) - 5);
|
|
i = (snapline(i, VDCwrite[0xA0], 0) - 3);
|
|
} /*Catch the ball*/
|
|
|
|
if (i < 0)
|
|
i = 0;
|
|
clip_low = last_line * (long)BMPW;
|
|
clip_high = i * (long)BMPW;
|
|
if (clip_high > BMPW * BMPH)
|
|
clip_high = BMPW * BMPH;
|
|
if (clip_low < 0)
|
|
clip_low = 0;
|
|
if (clip_low < clip_high)
|
|
draw_display();
|
|
last_line = i;
|
|
}
|
|
|
|
static uint32_t colors[256];
|
|
|
|
static void create_cmap(void)
|
|
{
|
|
int i;
|
|
|
|
/* Initialise parts of the colors array */
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
/* Use the color values from the color table */
|
|
colors[i] = colortable[app_data.vpp ? 1 : 0][i];
|
|
colors[i + 32] = colors[i];
|
|
}
|
|
|
|
for (i = 16; i < 32; i++)
|
|
colors[i] = 0;
|
|
for (i = 48; i < 256; i++)
|
|
colors[i] = 0;
|
|
}
|
|
|
|
void clearscr(void)
|
|
{
|
|
clear(bitmap);
|
|
}
|
|
|
|
void mputvid(unsigned int ad, unsigned int len, Byte d, Byte c)
|
|
{
|
|
if ((ad > (unsigned long)clip_low) && (ad < (unsigned long)clip_high))
|
|
{
|
|
unsigned int i;
|
|
if (((len & 3) == 0) && (sizeof(unsigned long) == 4))
|
|
{
|
|
unsigned long dddd = (((unsigned long)d) & 0xff) | ((((unsigned long)d) & 0xff) << 8) | ((((unsigned long)d) & 0xff) << 16) | ((((unsigned long)d) & 0xff) << 24);
|
|
unsigned long cccc = (((unsigned long)c) & 0xff) | ((((unsigned long)c) & 0xff) << 8) | ((((unsigned long)c) & 0xff) << 16) | ((((unsigned long)c) & 0xff) << 24);
|
|
for (i = 0; i<len>> 2; i++)
|
|
{
|
|
*((unsigned long *)(vscreen + ad)) = dddd;
|
|
cccc |= *((unsigned long *)(col + ad));
|
|
*((unsigned long *)(col + ad)) = cccc;
|
|
coltab[c] |= ((cccc | (cccc >> 8) | (cccc >> 16) | (cccc >> 24)) & 0xff);
|
|
ad += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
vscreen[ad] = d;
|
|
col[ad] |= c;
|
|
coltab[c] |= col[ad++];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void draw_grid(void)
|
|
{
|
|
unsigned int pnt, pn1;
|
|
Byte mask, d;
|
|
int j, i, x, w;
|
|
Byte color;
|
|
|
|
if (VDCwrite[0xA0] & 0x40)
|
|
{
|
|
for (j = 0; j < 9; j++)
|
|
{
|
|
pnt = (((j * 24) + 24) * BMPW);
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
pn1 = pnt + (i * 32) + 20;
|
|
color = ColorVector[j * 24 + 24];
|
|
mputvid(
|
|
pn1,
|
|
4,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
|
|
color = ColorVector[j * 24 + 25];
|
|
mputvid(
|
|
pn1 + BMPW,
|
|
4,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
|
|
color = ColorVector[j * 24 + 26];
|
|
mputvid(
|
|
pn1 + BMPW * 2,
|
|
4,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
|
|
}
|
|
}
|
|
}
|
|
|
|
mask = 0x01;
|
|
for (j = 0; j < 9; j++)
|
|
{
|
|
pnt = (((j * 24) + 24) * BMPW);
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
pn1 = pnt + (i * 32) + 20;
|
|
if ((pn1 + BMPW * 3 >= (unsigned long)clip_low) && (pn1 <= (unsigned long)clip_high))
|
|
{
|
|
d = VDCwrite[0xC0 + i];
|
|
if (j == 8)
|
|
{
|
|
d = VDCwrite[0xD0 + i];
|
|
mask = 1;
|
|
}
|
|
if (d & mask)
|
|
{
|
|
color = ColorVector[j * 24 + 24];
|
|
mputvid(
|
|
pn1,
|
|
36,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
|
|
color = ColorVector[j * 24 + 25];
|
|
mputvid(
|
|
pn1 + BMPW,
|
|
36,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
|
|
color = ColorVector[j * 24 + 26];
|
|
mputvid(
|
|
pn1 + BMPW * 2,
|
|
36,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_HGRID);
|
|
}
|
|
}
|
|
}
|
|
mask = mask << 1;
|
|
}
|
|
|
|
mask = 0x01;
|
|
w = 4;
|
|
if (VDCwrite[0xA0] & 0x80)
|
|
{
|
|
w = 32;
|
|
}
|
|
for (j = 0; j < 10; j++)
|
|
{
|
|
pnt = (j * 32);
|
|
mask = 0x01;
|
|
d = VDCwrite[0xE0 + j];
|
|
for (x = 0; x < 8; x++)
|
|
{
|
|
pn1 = pnt + (((x * 24) + 24) * BMPW) + 20;
|
|
if (d & mask)
|
|
{
|
|
for (i = 0; i < 24; i++)
|
|
{
|
|
if ((pn1 >= (unsigned long)clip_low) && (pn1 <= (unsigned long)clip_high))
|
|
{
|
|
color = ColorVector[x * 24 + 24 + i];
|
|
mputvid(
|
|
pn1,
|
|
w,
|
|
(color & 0x07) | ((color & 0x40) >> 3) | (color & 0x80 ? 0 : 8), COL_VGRID);
|
|
}
|
|
pn1 += BMPW;
|
|
}
|
|
}
|
|
mask = mask << 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void finish_display(void)
|
|
{
|
|
vpp_finish_bmp(vscreen, 9, 5, BMPW - 9, BMPH - 5, bitmap->w, bitmap->h);
|
|
}
|
|
|
|
void blit(uint32_t* dst)
|
|
{
|
|
uint8_t* src = vscreen;
|
|
src += 5 * BMPW;
|
|
src += 9;
|
|
for (int j = 0; j < 240; j++)
|
|
{
|
|
for (int i = 0; i < 320; i++)
|
|
{
|
|
*dst++ = colors[*src++];
|
|
}
|
|
src += 20;
|
|
}
|
|
}
|
|
|
|
void clear_collision(void)
|
|
{
|
|
load_colplus(col);
|
|
coltab[0x01] = coltab[0x02] = 0;
|
|
coltab[0x04] = coltab[0x08] = 0;
|
|
coltab[0x10] = coltab[0x20] = 0;
|
|
coltab[0x40] = coltab[0x80] = 0;
|
|
}
|
|
|
|
void draw_display(void)
|
|
{
|
|
int i, j, x, sm, t;
|
|
Byte y, b, d1, cl, c;
|
|
|
|
unsigned int pnt, pnt2;
|
|
|
|
for (i = clip_low / BMPW; i < clip_high / BMPW; i++)
|
|
memset(
|
|
vscreen + i * BMPW,
|
|
((ColorVector[i] & 0x38) >> 3) | (ColorVector[i] & 0x80 ? 0 : 8), BMPW);
|
|
|
|
if (VDCwrite[0xA0] & 0x08)
|
|
draw_grid();
|
|
|
|
if (useforen && (!(VDCwrite[0xA0] & 0x20)))
|
|
return;
|
|
|
|
for (i = 0x10; i < 0x40; i += 4)
|
|
draw_char(VDCwrite[i], VDCwrite[i + 1], VDCwrite[i + 2],
|
|
VDCwrite[i + 3]);
|
|
|
|
/* draw quads, position mapping happens in ext_write (vmachine.c)*/
|
|
for (i = 0x40; i < 0x80; i += 0x10)
|
|
draw_quad(VDCwrite[i], VDCwrite[i + 1], VDCwrite[i + 2],
|
|
VDCwrite[i + 3], VDCwrite[i + 6], VDCwrite[i + 7],
|
|
VDCwrite[i + 10], VDCwrite[i + 11], VDCwrite[i + 14],
|
|
VDCwrite[i + 15]);
|
|
|
|
c = 8;
|
|
for (i = 12; i >= 0; i -= 4)
|
|
{
|
|
pnt2 = 0x80 + (i * 2);
|
|
y = VDCwrite[i];
|
|
x = VDCwrite[i + 1] - 8;
|
|
t = VDCwrite[i + 2];
|
|
cl = ((t & 0x38) >> 3);
|
|
cl = ((cl & 2) | ((cl & 1) << 2) | ((cl & 4) >> 2)) + 8;
|
|
/*174*/
|
|
if ((x < 164) && (y > 0) && (y < 232))
|
|
{
|
|
pnt = y * BMPW + (x * 2) + 20 + sproff;
|
|
if (t & 4)
|
|
{
|
|
if ((pnt + BMPW * 32 >= (unsigned long)clip_low) && (pnt <= (unsigned long)clip_high))
|
|
{
|
|
for (j = 0; j < 8; j++)
|
|
{
|
|
sm = (((j % 2 == 0) && (((t >> 1) & 1) != (t & 1))) || ((j % 2 == 1) && (t & 1))) ? 1 : 0;
|
|
d1 = VDCwrite[pnt2++];
|
|
for (b = 0; b < 8; b++)
|
|
{
|
|
if (d1 & 0x01)
|
|
{
|
|
if ((x + b + sm < 159) && (y + j < 247))
|
|
{
|
|
mputvid(sm + pnt, 4, cl, c);
|
|
mputvid(sm + pnt + BMPW, 4, cl, c);
|
|
mputvid(sm + pnt + 2 * BMPW, 4, cl, c);
|
|
mputvid(sm + pnt + 3 * BMPW, 4, cl, c);
|
|
}
|
|
}
|
|
pnt += 4;
|
|
d1 = d1 >> 1;
|
|
}
|
|
pnt += BMPW * 4 - 32;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pnt + BMPW * 16 >= (unsigned long)clip_low) && (pnt <= (unsigned long)clip_high))
|
|
{
|
|
for (j = 0; j < 8; j++)
|
|
{
|
|
sm = (((j % 2 == 0) && (((t >> 1) & 1) != (t & 1))) || ((j % 2 == 1) && (t & 1))) ? 1 : 0;
|
|
d1 = VDCwrite[pnt2++];
|
|
for (b = 0; b < 8; b++)
|
|
{
|
|
if (d1 & 0x01)
|
|
{
|
|
if ((x + b + sm < 160) && (y + j < 249))
|
|
{
|
|
mputvid(sm + pnt, 2, cl, c);
|
|
mputvid(sm + pnt + BMPW, 2, cl, c);
|
|
}
|
|
}
|
|
pnt += 2;
|
|
d1 = d1 >> 1;
|
|
}
|
|
pnt += BMPW * 2 - 16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
c = c >> 1;
|
|
}
|
|
}
|
|
|
|
void draw_char(Byte ypos, Byte xpos, Byte chr, Byte col)
|
|
{
|
|
int j, c;
|
|
Byte cl, d1;
|
|
int y, b, n;
|
|
unsigned int pnt;
|
|
|
|
y = (ypos & 0xFE);
|
|
pnt = y * BMPW + ((xpos - 8) * 2) + 20;
|
|
|
|
ypos = ypos >> 1;
|
|
n = 8 - (ypos % 8) - (chr % 8);
|
|
if (n < 3)
|
|
n = n + 7;
|
|
|
|
if ((pnt + BMPW * 2 * n >= (unsigned long)clip_low) && (pnt <= (unsigned long)clip_high))
|
|
{
|
|
|
|
c = (int)chr + ypos;
|
|
if (col & 0x01)
|
|
c += 256;
|
|
if (c > 511)
|
|
c = c - 512;
|
|
|
|
cl = ((col & 0x0E) >> 1);
|
|
cl = ((cl & 2) | ((cl & 1) << 2) | ((cl & 4) >> 2)) + 8;
|
|
|
|
if ((y > 0) && (y < 232) && (xpos < 157))
|
|
{
|
|
for (j = 0; j < n; j++)
|
|
{
|
|
d1 = cset[c + j];
|
|
for (b = 0; b < 8; b++)
|
|
{
|
|
if (d1 & 0x80)
|
|
{
|
|
if ((xpos - 8 + b < 160) && (y + j < 240))
|
|
{
|
|
mputvid(pnt, 2, cl, COL_CHAR);
|
|
mputvid(pnt + BMPW, 2, cl, COL_CHAR);
|
|
}
|
|
}
|
|
pnt += 2;
|
|
d1 = d1 << 1;
|
|
}
|
|
pnt += BMPW * 2 - 16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This quad drawing routine can display the quad cut off effect used in KTAA.
|
|
* It needs more testing with other games, especially the clipping.
|
|
* This code is quite slow and needs a rewrite by somebody with more experience
|
|
* than I (sgust) have */
|
|
|
|
void draw_quad(Byte ypos, Byte xpos, Byte cp0l, Byte cp0h, Byte cp1l, Byte cp1h,
|
|
Byte cp2l, Byte cp2h, Byte cp3l, Byte cp3h)
|
|
{
|
|
/* char set pointers */
|
|
int chp[4];
|
|
/* colors */
|
|
Byte col[4];
|
|
/* pointer into screen bitmap */
|
|
unsigned int pnt;
|
|
/* offset into current line */
|
|
unsigned int off;
|
|
/* loop variables */
|
|
int i, j, lines;
|
|
|
|
/* get screen bitmap position of quad */
|
|
pnt = (ypos & 0xfe) * BMPW + ((xpos - 8) * 2) + 20;
|
|
/* abort drawing if completely below the bottom clip */
|
|
if (pnt > (unsigned long)clip_high)
|
|
return;
|
|
/* extract and convert char-set offsets */
|
|
chp[0] = cp0l | ((cp0h & 1) << 8);
|
|
chp[1] = cp1l | ((cp1h & 1) << 8);
|
|
chp[2] = cp2l | ((cp2h & 1) << 8);
|
|
chp[3] = cp3l | ((cp3h & 1) << 8);
|
|
for (i = 0; i < 4; i++)
|
|
chp[i] = (chp[i] + (ypos >> 1)) & 0x1ff;
|
|
lines = 8 - (chp[3] + 1) % 8;
|
|
/* abort drawing if completely over the top clip */
|
|
if (pnt + BMPW * 2 * lines < (unsigned long)clip_low)
|
|
return;
|
|
/* extract and convert color information */
|
|
col[0] = (cp0h & 0xe) >> 1;
|
|
col[1] = (cp1h & 0xe) >> 1;
|
|
col[2] = (cp2h & 0xe) >> 1;
|
|
col[3] = (cp3h & 0xe) >> 1;
|
|
for (i = 0; i < 4; i++)
|
|
col[i] = ((col[i] & 2) | ((col[i] & 1) << 2) | ((col[i] & 4) >> 2)) + 8;
|
|
/* now draw the quad line by line controlled by the last quad */
|
|
while (lines-- > 0)
|
|
{
|
|
off = 0;
|
|
/* draw all 4 sub-quads */
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
/* draw sub-quad pixel by pixel, but stay in same line */
|
|
for (j = 0; j < 8; j++)
|
|
{
|
|
if ((cset[chp[i]] & (1 << (7 - j))) && (off < BMPW))
|
|
{
|
|
mputvid(pnt + off, 2, col[i], COL_CHAR);
|
|
mputvid(pnt + off + BMPW, 2, col[i], COL_CHAR);
|
|
}
|
|
/* next pixel */
|
|
off += 2;
|
|
}
|
|
/* space between sub-quads */
|
|
off += 16;
|
|
}
|
|
/* advance char-set pointers */
|
|
for (i = 0; i < 4; i++)
|
|
chp[i] = (chp[i] + 1) & 0x1ff;
|
|
/* advance screen bitmap pointer */
|
|
pnt += BMPW * 2;
|
|
}
|
|
}
|
|
|
|
void init_display(void)
|
|
{
|
|
create_cmap();
|
|
bitmap = NewBitmap(BMPW, BMPH);
|
|
if (!bitmap)
|
|
{
|
|
fprintf(stderr, "Could not allocate memory for screen buffer.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
vscreen = (Byte *)bitmap->dat;
|
|
clear(bitmap);
|
|
col = (Byte *)malloc(BMPW * BMPH);
|
|
if (!col)
|
|
{
|
|
fprintf(stderr, "Could not allocate memory for collision buffer.\n");
|
|
free(vscreen);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
memset(col, 0, BMPW * BMPH);
|
|
}
|