BizHawk/waterbox/o2em/vpp.c

589 lines
11 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
*
*
*
* Videopac+ G7400 emulation
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "types.h"
#include "vmachine.h"
#include "vdc.h"
#include "vpp_cset.h"
#include "vpp.h"
#include "bitmap.h"
static void vpp_draw_char(int x, int y, Byte ch, Byte c0, Byte c1, Byte ext,
Byte dw, Byte dh, Byte ul);
static void vpp_update_screen(void);
static Byte LumReg = 0xff, TraReg = 0xff;
static Byte *colplus = NULL;
static int vppon = 1;
static int vpp_cx = 0;
static int vpp_cy = 0;
static Byte vpp_data = 0;
static int inc_curs = 1;
static int slice = 0;
static int vpp_y0 = 0;
static Byte vpp_r = 0;
Byte dchars[2][960];
Byte vpp_mem[40][32][4];
static int frame_cnt = 0;
static int blink_st = 0;
static int slicemode = 0;
static int need_update = 0;
static Bitmap* vppbmp;
Byte read_PB(Byte p)
{
p &= 0x3;
switch (p)
{
case 0:
return LumReg >> 4;
break;
case 1:
return LumReg & 0xf;
break;
case 2:
return TraReg >> 4;
break;
case 3:
return TraReg & 0xf;
break;
}
return 0;
}
void write_PB(Byte p, Byte val)
{
p &= 0x3;
val &= 0xf;
switch (p)
{
case 0:
LumReg = (val << 4) | (LumReg & 0xf);
break;
case 1:
LumReg = (LumReg & 0xf0) | val;
break;
case 2:
TraReg = (val << 4) | (TraReg & 0xf);
break;
case 3:
TraReg = (TraReg & 0xf0) | val;
break;
}
need_update = 1;
}
Byte vpp_read(ADDRESS adr)
{
Byte t;
static Byte ta = 0;
static Byte tb = 0;
switch (adr)
{
case 4:
return ta;
case 5:
/* get return value from last read */
t = tb;
/* the real VPP starts a read cycle,
* the data gets returned at next read */
if (slicemode)
{
Byte ext, chr;
chr = vpp_mem[vpp_cx][vpp_cy][0];
ext = (vpp_mem[vpp_cx][vpp_cy][1] & 0x80) ? 1 : 0;
if (chr < 0xA0)
{
ta = 0;
fprintf(stderr, "unsupported: CHARROM read %d %d %d\n", chr,
ext, slice);
}
else
{
ta = dchars[ext][(chr - 0xA0) * 10 + slice];
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);
}
tb = 0xff; /* real VPP seems to return junk */
slice = (slice + 1) % 10;
}
else
{
ta = vpp_mem[vpp_cx][vpp_cy][1];
tb = vpp_mem[vpp_cx][vpp_cy][0];
if (inc_curs)
{
vpp_cx++;
if (vpp_cx >= 40)
{
vpp_cx = 0;
vpp_cy++;
if (vpp_cy >= 24)
vpp_cy = 0;
}
}
}
return t;
case 6:
return 0;
default:
return 0;
}
}
void vpp_write(Byte dat, ADDRESS adr)
{
static Byte ta;
switch (adr)
{
case 0:
if (!slicemode)
vpp_mem[vpp_cx][vpp_cy][1] = dat;
else
ta = dat;
break;
case 1:
if (slicemode)
{
Byte ext, chr;
chr = vpp_mem[vpp_cx][vpp_cy][0];
ext = (vpp_mem[vpp_cx][vpp_cy][1] & 0x80) ? 1 : 0;
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);
slice = (slice + 1) % 10;
}
else
{
vpp_mem[vpp_cx][vpp_cy][0] = dat;
if ((dat > 0x7f) && (dat < 0xa0) && (!(vpp_mem[vpp_cx][vpp_cy][1] & 0x80)))
{
vpp_mem[vpp_cx][vpp_cy][2] = dat;
vpp_mem[vpp_cx][vpp_cy][3] = vpp_mem[vpp_cx][vpp_cy][1];
}
else
{
vpp_mem[vpp_cx][vpp_cy][2] = vpp_mem[vpp_cx][vpp_cy][3] = 0;
}
if (inc_curs)
{
vpp_cx++;
if (vpp_cx >= 40)
{
vpp_cx = 0;
vpp_cy++;
if (vpp_cy >= 24)
vpp_cy = 0;
}
}
}
break;
case 2:
vpp_data = dat;
break;
case 3:
switch (dat & 0xe0)
{
case 0x00: /* plus_cmd_brow */
vpp_cy = vpp_data & 0x1f;
vpp_cx = 0;
break;
case 0x20: /* plus_cmd_loady */
vpp_cy = vpp_data & 0x1f;
break;
case 0x40: /* plus_cmd_loadx */
vpp_cx = (vpp_data & 0x3f) % 40;
break;
case 0x60: /* plus_cmd_incc */
vpp_cx++;
if (vpp_cx >= 40)
{
vpp_cx = 0;
vpp_cy++;
if (vpp_cy >= 24)
vpp_cy = 0;
}
break;
case 0x80: /* plus_cmd_loadm */
slicemode = 0;
slice = (vpp_data & 0x1f) % 10;
switch (vpp_data & 0xe0)
{
case 0x00: /* plus_loadm_wr */
inc_curs = 1;
break;
case 0x20: /* plus_loadm_rd */
inc_curs = 1;
break;
case 0x40: /* plus_loadm_wrni */
inc_curs = 0;
break;
case 0x60: /* plus_loadm_rdni */
inc_curs = 0;
break;
case 0x80: /* plus_loadm_wrsl */
slicemode = 1;
break;
case 0xA0: /* plus_loadm_rdsl */
slicemode = 1;
break;
default:
break;
}
break;
case 0xA0: /* plus_cmd_loadr */
vpp_r = vpp_data;
break;
case 0xC0: /* plus_cmd_loady0 */
if (vpp_data & 0x20)
fprintf(stderr, "unsupported: global double height");
vpp_y0 = (vpp_data & 0x1f) % 24;
break;
default:
break;
}
break;
default:
break;
}
need_update = 1;
}
void vpp_finish_bmp(Byte *vmem, int offx, int offy, int w, int h, int totw,
int toth)
{
int i, x, y, t, c, nc, clrx, clry;
int tcol[16], m[8] = {0x01, 0x10, 0x04, 0x40, 0x02, 0x20, 0x08, 0x80};
Byte *pnt, *pnt2, *pnt3;
if (vppon)
{
memset(colplus, 0, BMPW * BMPH);
vppon = 0;
}
if (TraReg == 0xff)
return;
vppon = 1;
frame_cnt--;
if (frame_cnt <= 0)
{
frame_cnt = 100;
blink_st = 1 - blink_st;
need_update = 1;
}
if (need_update)
vpp_update_screen();
for (i = 0; i < 8; i++)
tcol[i] = tcol[i + 8] = !(TraReg & m[i]);
if (w > totw - offx)
w = totw - offx;
if (h > toth - offy)
h = toth - offy;
if (w > vppbmp->w)
w = vppbmp->w;
if (h > vppbmp->h)
h = vppbmp->h;
clrx = clry = 0;
for (i = 0; (!clrx) && (i < totw); i++)
if (tcol[vmem[offy * totw + i] & 7])
clrx = 1;
for (i = 0; (!clry) && (i < toth); i++)
if (tcol[vmem[i * totw + offx] & 7])
clry = 1;
if (clrx)
for (y = 0; y < offy; y++)
for (x = 0; x < totw; x++)
vmem[y * totw + x] = 0;
if (clry)
for (y = 0; y < toth; y++)
for (x = 0; x < offx; x++)
vmem[y * totw + x] = 0;
for (y = 0; y < h; y++)
{
pnt = vmem + (offy + y) * totw + offx;
pnt2 = (Byte *)vppbmp->line[y];
x = 0;
while (x < w)
{
pnt3 = pnt;
c = *pnt++;
t = x++;
if ((((x + offx) & 3) == 0) && (sizeof(unsigned long) == 4))
{
unsigned long cccc, dddd, *p = (unsigned long *)pnt;
int t2 = x, w2 = w - 4;
cccc = (((unsigned long)c) & 0xff) | ((((unsigned long)c) & 0xff) << 8) | ((((unsigned long)c) & 0xff) << 16) | ((((unsigned long)c) & 0xff) << 24);
dddd = *p++;
while ((x < w2) && (dddd == cccc))
{
x += 4;
dddd = *p++;
}
pnt += x - t2;
}
if (c < 16)
{
if (tcol[c])
{
if (app_data.openb)
for (i = 0; i < x - t; i++)
*pnt3++ = *pnt2++ & 0xf;
else
{
memcpy(pnt3, pnt2, x - t);
pnt2 += x - t;
}
}
else
{
for (i = 0; i < x - t; i++)
{
nc = *pnt2++;
if ((nc & 0x10) && app_data.openb)
{
*pnt3++ = nc & 0xf;
}
else if (nc & 8)
{
colplus[pnt3++ - vmem] = 0x40;
}
else
{
pnt3++;
}
}
}
}
}
}
}
static void vpp_draw_char(int x, int y, Byte ch, Byte c0, Byte c1, Byte ext,
Byte dw, Byte dh, Byte ul)
{
int xx, yy, d, m, k;
if ((x > 39) || (y > 24) || (ext > 1))
return;
d = (dh == 2) ? 5 : 0;
for (yy = 0; yy < 10; yy++)
{
if (ul && (d == 9))
k = 255;
else if (ch >= 0xA0)
k = dchars[ext][(ch - 0xA0) * 10 + d];
else if (ch >= 0x80)
k = 255;
else
k = vpp_cset[ext][ch * 10 + d];
m = (dw == 2) ? 0x08 : 0x80;
for (xx = 0; xx < 8; xx++)
{
vppbmp->line[y * 10 + yy][x * 8 + xx] = (k & m) ? c1 : c0;
if ((xx % 2) || (dw == 0))
m >>= 1;
}
if ((yy % 2) || (dh == 0))
d++;
}
}
static void vpp_update_screen(void)
{
int i, x, y, l, chr, attr, ext, c0, c1, dw, dh, hpar, vpar, lvd, lhd,
ser_chr, ser_atr, ul, conc, box, swapcol;
int tlum[8], m[8] = {0x01, 0x10, 0x04, 0x40, 0x02, 0x20, 0x08, 0x80};
clear(vppbmp);
for (i = 0; i < 8; i++)
tlum[i] = (LumReg & m[i]) ? 0 : 8;
vpar = lvd = 0;
for (y = 0; y < 25; y++)
{
vpar = (lvd == 0) ? 0 : 1 - vpar;
l = (y == 0) ? 31 : (y - 1 + vpp_y0) % 24;
c0 = ul = conc = box = 0;
hpar = lhd = 0;
for (x = 0; x < 40; x++)
{
hpar = (lhd == 0) ? 0 : 1 - hpar;
chr = vpp_mem[x][l][0];
attr = vpp_mem[x][l][1];
c1 = attr & 0x7;
c1 = ((c1 & 2) | ((c1 & 1) << 2) | ((c1 & 4) >> 2));
ext = (attr & 0x80) ? 1 : 0;
ser_chr = vpp_mem[x][l][2];
ser_atr = vpp_mem[x][l][3];
if (ser_chr)
{
c0 = (ser_atr >> 4) & 0x7;
c0 = ((c0 & 2) | ((c0 & 1) << 2) | ((c0 & 4) >> 2));
ul = ser_chr & 4;
conc = ser_chr & 1;
box = ser_chr & 2;
}
if (ext)
{
c0 = (attr >> 4) & 0x7;
c0 = ((c0 & 2) | ((c0 & 1) << 2) | ((c0 & 4) >> 2));
dw = dh = 0;
}
else
{
dw = (attr & 0x20) ? (hpar ? 2 : 1) : 0;
dh = (attr & 0x10) ? (vpar ? 2 : 1) : 0;
if (dw)
lhd = 1;
if (dh)
lvd = 1;
}
swapcol = 0;
/* cursor display */
if ((x == vpp_cx) && (l == vpp_cy))
{
/* on cursor position */
if (vpp_r & 0x10)
{
/* cursor display active */
swapcol = !swapcol;
if ((vpp_r & 0x80) && blink_st)
{
/* blinking active */
swapcol = !swapcol;
}
}
}
/* invert attribute */
if ((!ext) && (attr & 0x40))
swapcol = !swapcol;
/* blinking chars */
if ((vpp_r & 0x80) && !(attr & 8) && !blink_st)
{
/* cursor handling is done already */
if (!(vpp_r & 0x10) || (x != vpp_cx) || (l != vpp_cy))
{
c1 = c0;
}
}
if (((y == 0) && (vpp_r & 8)) || ((y != 0) && (vpp_r & 1)))
{
if ((!conc) || (!(vpp_r & 4)))
{
if (box || (!(vpp_r & 2)))
{
if (swapcol)
vpp_draw_char(x, y, chr, c1 | tlum[c1],
c0 | tlum[c0], ext, dw, dh, ul);
else
vpp_draw_char(x, y, chr, c0 | tlum[c0],
c1 | tlum[c1], ext, dw, dh, ul);
}
else
{
vpp_draw_char(x, y, 255, (app_data.openb) ? 16 : 0, 0,
0, 0, 0, 0);
}
}
}
}
}
if (vpp_r & 0x20)
{
for (y = vppbmp->h - 1; y >= 10; y--)
for (x = 0; x < vppbmp->w; x++)
vppbmp->line[y][x] = vppbmp->line[(y - 10) / 2 + 10][x];
}
need_update = 0;
}
void load_colplus(Byte *col)
{
if (vppon)
memcpy(col, colplus, BMPW * BMPH);
else
memset(col, 0, BMPW * BMPH);
}
void init_vpp(void)
{
int i, j, k;
if (!colplus)
colplus = (Byte *)malloc(BMPW * BMPH);
if (!vppbmp)
vppbmp = NewBitmap(BMPW, BMPH);
clear(vppbmp);
memset(colplus, 0, BMPW * BMPH);
LumReg = TraReg = 0xff;
vpp_cx = 0;
vpp_cy = 0;
vpp_y0 = 0;
vpp_r = 0;
inc_curs = 1;
vpp_data = 0;
frame_cnt = 0;
blink_st = 0;
slice = 0;
slicemode = 0;
need_update = 1;
vppon = 1;
for (i = 0; i < 2; i++)
for (j = 0; j < 960; j++)
dchars[i][j] = 0;
for (i = 0; i < 40; i++)
for (j = 0; j < 32; j++)
for (k = 0; k < 4; k++)
vpp_mem[i][j][k] = 0;
}