pcem/src/vid_pc200.c
SarahW 1f5fc45c04 Amstrad changes :
- Add PPC512/640 emulation
- Add TV display type to Sinclair PC200
- Add font selection to PC1512
- Add disc change line to PC200
Patch from JohnElliott
2019-04-15 19:48:41 +01:00

1120 lines
38 KiB
C

/*PC200/PPC video emulation. */
#include <stdlib.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "mem.h"
#include "nmi.h"
#include "timer.h"
#include "video.h"
#include "vid_cga.h"
#include "vid_mda.h"
#include "vid_pc200.h"
/* #define PC200LOG(x) pclog x */
#define PC200LOG(x)
/* Display type */
#define PC200_CGA 0 /* CGA monitor */
#define PC200_MDA 1 /* MDA monitor */
#define PC200_TV 2 /* Television */
#define PC200_LCDC 3 /* PPC512 LCD as CGA*/
#define PC200_LCDM 4 /* PPC512 LCD as MDA*/
int pc200_is_mda;
static uint32_t blue, green;
static uint32_t lcdcols[256][2][2];
typedef struct pc200_t
{
mem_mapping_t cga_mapping;
mem_mapping_t mda_mapping;
cga_t cga;
mda_t mda;
int vidtime;
uint8_t emulation; /* Which display are we emulating? */
uint8_t dipswitches; /* DIP switches 1-3 */
uint8_t crtc_index; /* CRTC index readback
* Bit 7: CGA control port written
* Bit 6: Operation control port written
* Bit 5: CRTC register written
* Bits 0-4: Last CRTC register selected */
uint8_t operation_ctrl;
uint8_t reg_3df;
} pc200_t;
static uint8_t crtcmask[32] =
{
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static void setvidtime(pc200_t *pc200)
{
switch (pc200->emulation)
{
case PC200_CGA:
case PC200_TV:
case PC200_LCDC:
pc200->vidtime = pc200->cga.vidtime;
break;
case PC200_MDA:
case PC200_LCDM:
pc200->vidtime = pc200->mda.vidtime;
break;
}
}
/* LCD colour mappings
*
* 0 => solid green
* 1 => blue on green
* 2 => green on blue
* 3 => solid blue
*/
static unsigned char mapping1[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/*00*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/*10*/ 2, 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
/*20*/ 2, 2, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1,
/*30*/ 2, 2, 2, 0, 2, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1,
/*40*/ 2, 2, 1, 1, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1,
/*50*/ 2, 2, 1, 1, 2, 0, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1,
/*60*/ 2, 2, 2, 2, 2, 2, 0, 1, 2, 2, 2, 2, 2, 2, 1, 1,
/*70*/ 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1,
/*80*/ 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
/*90*/ 2, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1,
/*A0*/ 2, 2, 2, 1, 2, 2, 1, 1, 2, 2, 0, 1, 2, 2, 1, 1,
/*B0*/ 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 0, 2, 2, 1, 1,
/*C0*/ 2, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 0, 1, 1, 1,
/*D0*/ 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 0, 1, 1,
/*E0*/ 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 1,
/*F0*/ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0,
};
static unsigned char mapping2[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/*00*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/*10*/ 1, 3, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2,
/*20*/ 1, 1, 3, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2,
/*30*/ 1, 1, 1, 3, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 2, 2,
/*40*/ 1, 1, 2, 2, 3, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2,
/*50*/ 1, 1, 2, 2, 1, 3, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2,
/*60*/ 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 2, 2,
/*70*/ 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2,
/*80*/ 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
/*90*/ 1, 1, 2, 2, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2,
/*A0*/ 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 3, 2, 1, 1, 2, 2,
/*B0*/ 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 3, 1, 1, 2, 2,
/*C0*/ 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 3, 2, 2, 2,
/*D0*/ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 3, 2, 2,
/*E0*/ 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2,
/*F0*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
};
static void set_lcd_cols(uint8_t mode_reg)
{
unsigned char *mapping = (mode_reg & 0x80) ? mapping2 : mapping1;
int c;
for (c = 0; c < 256; c++)
{
switch (mapping[c])
{
case 0:
lcdcols[c][0][0] = lcdcols[c][1][0] = green;
lcdcols[c][0][1] = lcdcols[c][1][1] = green;
break;
case 1:
lcdcols[c][0][0] = lcdcols[c][1][0] =
lcdcols[c][1][1] = green;
lcdcols[c][0][1] = blue;
break;
case 2:
lcdcols[c][0][0] = lcdcols[c][1][0] =
lcdcols[c][1][1] = blue;
lcdcols[c][0][1] = green;
break;
case 3:
lcdcols[c][0][0] = lcdcols[c][1][0] = blue;
lcdcols[c][0][1] = lcdcols[c][1][1] = blue;
break;
}
}
}
void pc200_out(uint16_t addr, uint8_t val, void *p)
{
pc200_t *pc200 = (pc200_t *)p;
cga_t *cga = &pc200->cga;
mda_t *mda = &pc200->mda;
uint8_t old;
PC200LOG(("pc200_out(0x%04x), %02x\n", addr, val));
/* MDA writes ============================================================== */
switch (addr)
{
case 0x3b1:
case 0x3b3:
case 0x3b5:
case 0x3b7:
/* Writes banned to CRTC registers 0-11? */
if (!(pc200->operation_ctrl & 0x40) && mda->crtcreg <= 11)
{
pc200->crtc_index = 0x20 | (mda->crtcreg & 0x1f);
if (pc200->operation_ctrl & 0x80)
{
PC200LOG(("NMI raised from CRTC register access: reg=0x%02x val=0x%02x nmi_mask=%d\n", mda->crtcreg & 0x1F, val, nmi_mask));
nmi = 1;
}
pc200->reg_3df = val;
return;
}
PC200LOG(("CRTM[%02x]:=%02x\n", mda->crtcreg, val));
old = mda->crtc[mda->crtcreg];
mda->crtc[mda->crtcreg] = val & crtcmask[mda->crtcreg];
if (old != val)
{
if (mda->crtcreg < 0xe || mda->crtcreg > 0x10)
{
fullchange = changeframecount;
mda_recalctimings(mda);
setvidtime(pc200);
}
}
return;
case 0x3b8:
old = mda->ctrl;
mda->ctrl = val;
if ((mda->ctrl ^ old) & 3)
{
mda_recalctimings(mda);
setvidtime(pc200);
}
pc200->crtc_index &= 0x1F;
pc200->crtc_index |= 0x80;
if (pc200->operation_ctrl & 0x80)
{
PC200LOG(("NMI raised from mode control access: val=0x%02x nmi_mask=%d\n", val, nmi_mask));
nmi = 1;
}
return;
/* CGA writes ============================================================== */
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
/* Writes banned to CRTC registers 0-11? */
if (!(pc200->operation_ctrl & 0x40) && cga->crtcreg <= 11)
{
pc200->crtc_index = 0x20 | (cga->crtcreg & 0x1f);
if (pc200->operation_ctrl & 0x80)
{
PC200LOG(("NMI raised from CRTC register access: reg=0x%02x val=0x%02x nmi_mask=%d\n", cga->crtcreg & 0x1F, val, nmi_mask));
nmi = 1;
}
pc200->reg_3df = val;
return;
}
PC200LOG(("CRTC[%02x]:=%02x\n", cga->crtcreg, val));
old = cga->crtc[cga->crtcreg];
cga->crtc[cga->crtcreg] = val & crtcmask[cga->crtcreg];
if (old != val)
{
if (cga->crtcreg < 0xe || cga->crtcreg > 0x10)
{
fullchange = changeframecount;
cga_recalctimings(cga);
setvidtime(pc200);
}
}
return;
case 0x3d8:
old = cga->cgamode;
cga->cgamode = val;
if ((cga->cgamode ^ old) & 3)
{
cga_recalctimings(cga);
setvidtime(pc200);
}
pc200->crtc_index &= 0x1F;
pc200->crtc_index |= 0x80;
if (pc200->operation_ctrl & 0x80)
{
PC200LOG(("NMI raised from mode control access: val=0x%02x nmi_mask=%d\n", val, nmi_mask));
nmi = 1;
}
else set_lcd_cols(val);
return;
/* PC200 control port writes ============================================== */
case 0x3de:
pc200->crtc_index = 0x1f;
/* NMI only seems to be triggered if the value being written has the high
* bit set (enable NMI). So it only protects writes to this port if you
* let it? */
if (val & 0x80)
{
PC200LOG(("NMI raised from operation control access: val=0x%02x nmi_mask=%d\n", val, nmi_mask));
pc200->operation_ctrl = val;
pc200->crtc_index |= 0x40;
nmi = 1;
return;
}
pc200->operation_ctrl = val;
/* Bits 0 and 1 control emulation and output mode */
if (val & 1) /* Monitor */
{
pc200->emulation = (val & 2) ? PC200_MDA : PC200_CGA;
}
else if (romset == ROM_PPC512)
{
pc200->emulation = (val & 2) ? PC200_LCDM : PC200_LCDC;
}
else
{
pc200->emulation = PC200_TV;
}
PC200LOG(("Video emulation set to %d\n", pc200->emulation));
/* Bit 2 disables the IDA. We don't support dynamic enabling
* and disabling of the IDA (instead, PCEM disconnects the
* IDA from the bus altogether) so don't implement this */
/* Enable the appropriate memory ranges depending whether
* the IDA is configured as MDA or CGA */
if (pc200->emulation == PC200_MDA ||
pc200->emulation == PC200_LCDM)
{
mem_mapping_disable(&pc200->cga_mapping);
mem_mapping_enable(&pc200->mda_mapping);
}
else
{
mem_mapping_disable(&pc200->mda_mapping);
mem_mapping_enable(&pc200->cga_mapping);
}
return;
}
if (addr >= 0x3D0 && addr <= 0x3DF)
{
cga_out(addr, val, cga);
}
if (addr >= 0x3B0 && addr <= 0x3BB)
{
mda_out(addr, val, mda);
}
}
uint8_t pc200_in(uint16_t addr, void *p)
{
pc200_t *pc200 = (pc200_t *)p;
cga_t *cga = &pc200->cga;
mda_t *mda = &pc200->mda;
uint8_t temp;
PC200LOG(("pc200_in(0x%04x)\n", addr));
switch (addr)
{
case 0x3B8:
PC200LOG((" = %02x\n", mda->ctrl));
return mda->ctrl;
case 0x3D8:
PC200LOG((" = %02x\n", cga->cgamode));
return cga->cgamode;
case 0x3DD:
temp = pc200->crtc_index; /* Read NMI reason */
pc200->crtc_index &= 0x1f; /* Reset NMI reason */
nmi = 0; /* And reset NMI flag */
PC200LOG((" = %02x\n", temp));
return temp;
case 0x3DE:
PC200LOG((" = %02x\n", ((pc200->operation_ctrl & 0xc7) | pc200->dipswitches)));
return ((pc200->operation_ctrl & 0xc7) | pc200->dipswitches);
case 0x3DF:
return pc200->reg_3df;
}
if (addr >= 0x3D0 && addr <= 0x3DF)
{
return cga_in(addr, cga);
}
if (addr >= 0x3B0 && addr <= 0x3BB)
{
return mda_in(addr, mda);
}
return 0xFF;
}
void lcd_draw_char_80(pc200_t *pc200, uint32_t *buffer, uint8_t chr,
uint8_t attr, int drawcursor, int blink, int sc,
int mode160, uint8_t control)
{
int c;
uint8_t bits = fontdat[chr + pc200->cga.fontbase][sc];
uint8_t bright = 0;
uint16_t mask;
if (attr & 8) /* bright */
{
/* The brightness algorithm appears to be: replace any bit sequence 011
* with 001 (assuming an extra 0 to the left of the byte).
*/
bright = bits;
for (c = 0, mask = 0x100; c < 7; c++, mask >>= 1)
{
if (((bits & mask) == 0) &&
((bits & (mask >> 1)) != 0) &&
((bits & (mask >> 2)) != 0))
{
bright &= ~(mask >> 1);
}
}
bits = bright;
}
if (drawcursor) bits ^= 0xFF;
for (c = 0, mask = 0x80; c < 8; c++, mask >>= 1)
{
if (mode160)
buffer[c] = (attr & mask) ? blue : green;
else if (control & 0x20) /* blinking */
buffer[c] = lcdcols[attr & 0x7F][blink][(bits & mask) ? 1 : 0];
else
buffer[c] = lcdcols[attr][blink][(bits & mask) ? 1 : 0];
}
}
void lcd_draw_char_40(pc200_t *pc200, uint32_t *buffer, uint8_t chr,
uint8_t attr, int drawcursor, int blink, int sc,
uint8_t control)
{
int c;
uint8_t bits = fontdat[chr + pc200->cga.fontbase][sc];
uint8_t mask = 0x80;
if (attr & 8) /* bright */
{
bits = bits & (bits >> 1);
}
if (drawcursor) bits ^= 0xFF;
for (c = 0; c < 8; c++, mask >>= 1)
{
if (control & 0x20)
{
buffer[c*2] = buffer[c*2+1] =
lcdcols[attr & 0x7F][blink][(bits & mask) ? 1 : 0];
}
else
{
buffer[c*2] = buffer[c*2+1] =
lcdcols[attr][blink][(bits & mask) ? 1 : 0];
}
}
}
void lcdm_poll(pc200_t *pc200)
{
mda_t *mda = &pc200->mda;
uint16_t ca = (mda->crtc[15] | (mda->crtc[14] << 8)) & 0x3fff;
int drawcursor;
int x;
int oldvc;
uint8_t chr, attr;
int oldsc;
int blink;
if (!mda->linepos)
{
pc200->vidtime += mda->dispofftime;
mda->stat |= 1;
mda->linepos = 1;
oldsc = mda->sc;
if ((mda->crtc[8] & 3) == 3)
mda->sc = (mda->sc << 1) & 7;
if (mda->dispon)
{
if (mda->displine < mda->firstline)
{
mda->firstline = mda->displine;
video_wait_for_buffer();
}
mda->lastline = mda->displine;
for (x = 0; x < mda->crtc[1]; x++)
{
chr = mda->vram[(mda->ma << 1) & 0xfff];
attr = mda->vram[((mda->ma << 1) + 1) & 0xfff];
drawcursor = ((mda->ma == ca) && mda->con && mda->cursoron);
blink = ((mda->blink & 16) && (mda->ctrl & 0x20) && (attr & 0x80) && !drawcursor);
lcd_draw_char_80(pc200, &((uint32_t *)(buffer32->line[mda->displine]))[x * 8], chr, attr, drawcursor, blink, mda->sc, 0, mda->ctrl);
mda->ma++;
}
}
mda->sc = oldsc;
if (mda->vc == mda->crtc[7] && !mda->sc)
{
mda->stat |= 8;
// printf("VSYNC on %i %i\n",vc,sc);
}
mda->displine++;
if (mda->displine >= 500)
mda->displine=0;
}
else
{
pc200->vidtime += mda->dispontime;
if (mda->dispon) mda->stat&=~1;
mda->linepos=0;
if (mda->vsynctime)
{
mda->vsynctime--;
if (!mda->vsynctime)
{
mda->stat&=~8;
// printf("VSYNC off %i %i\n",vc,sc);
}
}
if (mda->sc == (mda->crtc[11] & 31) || ((mda->crtc[8] & 3) == 3 && mda->sc == ((mda->crtc[11] & 31) >> 1)))
{
mda->con = 0;
mda->coff = 1;
}
if (mda->vadj)
{
mda->sc++;
mda->sc &= 31;
mda->ma = mda->maback;
mda->vadj--;
if (!mda->vadj)
{
mda->dispon = 1;
mda->ma = mda->maback = (mda->crtc[13] | (mda->crtc[12] << 8)) & 0x3fff;
mda->sc = 0;
}
}
else if (mda->sc == mda->crtc[9] || ((mda->crtc[8] & 3) == 3 && mda->sc == (mda->crtc[9] >> 1)))
{
mda->maback = mda->ma;
mda->sc = 0;
oldvc = mda->vc;
mda->vc++;
mda->vc &= 127;
if (mda->vc == mda->crtc[6])
mda->dispon=0;
if (oldvc == mda->crtc[4])
{
// printf("Display over at %i\n",displine);
mda->vc = 0;
mda->vadj = mda->crtc[5];
if (!mda->vadj) mda->dispon = 1;
if (!mda->vadj) mda->ma = mda->maback = (mda->crtc[13] | (mda->crtc[12] << 8)) & 0x3fff;
if ((mda->crtc[10] & 0x60) == 0x20) mda->cursoron = 0;
else mda->cursoron = mda->blink & 16;
}
if (mda->vc == mda->crtc[7])
{
mda->dispon = 0;
mda->displine = 0;
mda->vsynctime = 16;
if (mda->crtc[7])
{
// printf("Lastline %i Firstline %i %i\n",lastline,firstline,lastline-firstline);
x = mda->crtc[1] * 8;
mda->lastline++;
if (x != xsize || (mda->lastline - mda->firstline) != ysize)
{
xsize = x;
ysize = mda->lastline - mda->firstline;
// printf("Resize to %i,%i - R1 %i\n",xsize,ysize,crtcm[1]);
if (xsize < 64) xsize = 656;
if (ysize < 32) ysize = 200;
updatewindowsize(xsize, ysize << 1);
}
video_blit_memtoscreen(0, mda->firstline, 0, ysize, xsize, ysize);
frames++;
video_res_x = mda->crtc[1];
video_res_y = mda->crtc[6];
video_bpp = 0;
}
mda->firstline = 1000;
mda->lastline = 0;
mda->blink++;
}
}
else
{
mda->sc++;
mda->sc &= 31;
mda->ma = mda->maback;
}
if ((mda->sc == (mda->crtc[10] & 31) || ((mda->crtc[8] & 3) == 3 && mda->sc == ((mda->crtc[10] & 31) >> 1))))
{
mda->con = 1;
// printf("Cursor on - %02X %02X %02X\n",crtcm[8],crtcm[10],crtcm[11]);
}
}
}
void lcdc_poll(pc200_t *pc200)
{
cga_t *cga = &pc200->cga;
int drawcursor;
int x, c;
int oldvc;
uint8_t chr, attr;
uint16_t dat;
int oldsc;
uint16_t ca;
int blink;
ca = (cga->crtc[15] | (cga->crtc[14] << 8)) & 0x3fff;
if (!cga->linepos)
{
pc200->vidtime += cga->dispofftime;
cga->cgastat |= 1;
cga->linepos = 1;
oldsc = cga->sc;
if ((cga->crtc[8] & 3) == 3)
cga->sc = ((cga->sc << 1) + cga->oddeven) & 7;
if (cga->cgadispon)
{
if (cga->displine < cga->firstline)
{
cga->firstline = cga->displine;
video_wait_for_buffer();
// printf("Firstline %i\n",firstline);
}
cga->lastline = cga->displine;
if (cga->cgamode & 1)
{
for (x = 0; x < cga->crtc[1]; x++)
{
chr = cga->charbuffer[x << 1];
attr = cga->charbuffer[(x << 1) + 1];
drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
blink = ((cga->cgablink & 16) && (cga->cgamode & 0x20) && (attr & 0x80) && !drawcursor);
lcd_draw_char_80(pc200, &((uint32_t *)(buffer32->line[cga->displine]))[x * 8], chr, attr, drawcursor, blink, cga->sc, cga->cgamode & 0x40, cga->cgamode);
cga->ma++;
}
}
else if (!(cga->cgamode & 2))
{
for (x = 0; x < cga->crtc[1]; x++)
{
chr = cga->vram[((cga->ma << 1) & 0x3fff)];
attr = cga->vram[(((cga->ma << 1) + 1) & 0x3fff)];
drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
blink = ((cga->cgablink & 16) && (cga->cgamode & 0x20) && (attr & 0x80) && !drawcursor);
lcd_draw_char_40(pc200, &((uint32_t *)(buffer32->line[cga->displine]))[x * 16], chr, attr, drawcursor, blink, cga->sc, cga->cgamode);
cga->ma++;
}
}
else /* Graphics mode */
{
for (x = 0; x < cga->crtc[1]; x++)
{
dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1];
cga->ma++;
for (c = 0; c < 16; c++)
{
((uint32_t *)buffer32->line[cga->displine])[(x << 4) + c] = (dat & 0x8000) ? blue : green;
dat <<= 1;
}
}
}
}
else
{
if (cga->cgamode & 1) hline(buffer32, 0, cga->displine, (cga->crtc[1] << 3), green);
else hline(buffer32, 0, cga->displine, (cga->crtc[1] << 4), green);
}
if (cga->cgamode & 1) x = (cga->crtc[1] << 3);
else x = (cga->crtc[1] << 4);
cga->sc = oldsc;
if (cga->vc == cga->crtc[7] && !cga->sc)
cga->cgastat |= 8;
cga->displine++;
if (cga->displine >= 360)
cga->displine = 0;
}
else
{
pc200->vidtime += cga->dispontime;
cga->linepos = 0;
if (cga->vsynctime)
{
cga->vsynctime--;
if (!cga->vsynctime)
cga->cgastat &= ~8;
}
if (cga->sc == (cga->crtc[11] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[11] & 31) >> 1)))
{
cga->con = 0;
cga->coff = 1;
}
if ((cga->crtc[8] & 3) == 3 && cga->sc == (cga->crtc[9] >> 1))
cga->maback = cga->ma;
if (cga->vadj)
{
cga->sc++;
cga->sc &= 31;
cga->ma = cga->maback;
cga->vadj--;
if (!cga->vadj)
{
cga->cgadispon = 1;
cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
cga->sc = 0;
}
}
else if (cga->sc == cga->crtc[9])
{
cga->maback = cga->ma;
cga->sc = 0;
oldvc = cga->vc;
cga->vc++;
cga->vc &= 127;
if (cga->vc == cga->crtc[6])
cga->cgadispon = 0;
if (oldvc == cga->crtc[4])
{
cga->vc = 0;
cga->vadj = cga->crtc[5];
if (!cga->vadj) cga->cgadispon = 1;
if (!cga->vadj) cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
if ((cga->crtc[10] & 0x60) == 0x20) cga->cursoron = 0;
else cga->cursoron = cga->cgablink & 8;
}
if (cga->vc == cga->crtc[7])
{
cga->cgadispon = 0;
cga->displine = 0;
cga->vsynctime = 16;
if (cga->crtc[7])
{
if (cga->cgamode & 1) x = (cga->crtc[1] << 3);
else x = (cga->crtc[1] << 4);
cga->lastline++;
if (x != xsize || (cga->lastline - cga->firstline) != ysize)
{
xsize = x;
ysize = cga->lastline - cga->firstline;
if (xsize < 64) xsize = 656;
if (ysize < 32) ysize = 200;
updatewindowsize(xsize, (ysize << 1));
}
video_blit_memtoscreen(0, cga->firstline, 0, (cga->lastline - cga->firstline), xsize, (cga->lastline - cga->firstline));
frames++;
video_res_x = xsize - 16;
video_res_y = ysize;
if (cga->cgamode & 1)
{
video_res_x /= 8;
video_res_y /= cga->crtc[9] + 1;
video_bpp = 0;
}
else if (!(cga->cgamode & 2))
{
video_res_x /= 16;
video_res_y /= cga->crtc[9] + 1;
video_bpp = 0;
}
else if (!(cga->cgamode & 16))
{
video_res_x /= 2;
video_bpp = 2;
}
else
{
video_bpp = 1;
}
}
cga->firstline = 1000;
cga->lastline = 0;
cga->cgablink++;
cga->oddeven ^= 1;
}
}
else
{
cga->sc++;
cga->sc &= 31;
cga->ma = cga->maback;
}
if (cga->cgadispon)
cga->cgastat &= ~1;
if ((cga->sc == (cga->crtc[10] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[10] & 31) >> 1))))
cga->con = 1;
if (cga->cgadispon && (cga->cgamode & 1))
{
for (x = 0; x < (cga->crtc[1] << 1); x++)
cga->charbuffer[x] = cga->vram[(((cga->ma << 1) + x) & 0x3fff)];
}
}
}
void pc200_poll(void *p)
{
pc200_t *pc200 = (pc200_t *)p;
switch (pc200->emulation)
{
case PC200_CGA:
case PC200_TV:
pc200->cga.vidtime = pc200->vidtime;
cga_poll(&pc200->cga);
pc200->vidtime = pc200->cga.vidtime;
return;
case PC200_MDA:
pc200->mda.vidtime = pc200->vidtime;
mda_poll(&pc200->mda);
pc200->vidtime = pc200->mda.vidtime;
return;
case PC200_LCDM:
lcdm_poll(pc200);
return;
case PC200_LCDC:
lcdc_poll(pc200);
return;
}
}
void *pc200_init()
{
int display_type, contrast;
pc200_t *pc200 = malloc(sizeof(pc200_t));
memset(pc200, 0, sizeof(pc200_t));
pc200->emulation = device_get_config_int("video_emulation");
display_type = device_get_config_int("display_type");
contrast = device_get_config_int("contrast");
/* Default to CGA */
pc200->dipswitches = 0x10;
/* DIP switches for PC200. Switches 2,3 give video emulation.
* Switch 1 is 'swap floppy drives' (not implemented) */
if (romset == ROM_PC200) switch (pc200->emulation)
{
case PC200_CGA: pc200->dipswitches = 0x10; break;
case PC200_MDA: pc200->dipswitches = 0x30; break;
case PC200_TV: pc200->dipswitches = 0x00; break;
/* The other combination is 'IDA disabled' (0x20) - see
* amstrad.c */
}
/* DIP switches for PPC512. Switch 1 is CRT/LCD. Switch 2
* is MDA / CGA. Switch 3 disables IDA, not implemented. */
else switch (pc200->emulation)
{
case PC200_CGA: pc200->dipswitches = 0x08; break;
case PC200_MDA: pc200->dipswitches = 0x18; break;
case PC200_LCDC: pc200->dipswitches = 0x00; break;
case PC200_LCDM: pc200->dipswitches = 0x10; break;
}
/* Flag whether the card is behaving like a CGA or an MDA, so that
* DIP switches are reported correctly */
pc200_is_mda = (pc200->emulation == PC200_MDA ||
pc200->emulation == PC200_LCDM);
PC200LOG(("pc200: DIP switches = %02x\n", pc200->dipswitches));
pc200->cga.vram = pc200->mda.vram = malloc(0x4000);
cga_init(&pc200->cga);
mda_init(&pc200->mda);
/* Attribute 8 is white on black (on a real MDA it's black on black) */
mda_setcol(0x08, 0, 1, 15);
mda_setcol(0x88, 0, 1, 15);
/* Attribute 64 is black on black (on a real MDA it's white on black) */
mda_setcol(0x40, 0, 1, 0);
mda_setcol(0xC0, 0, 1, 0);
pc200->cga.fontbase = (device_get_config_int("codepage") & 3) * 256;
timer_add(pc200_poll, &pc200->vidtime, TIMER_ALWAYS_ENABLED, pc200);
mem_mapping_add(&pc200->mda_mapping, 0xb0000, 0x08000, mda_read, NULL, NULL, mda_write, NULL, NULL, NULL, 0, &pc200->mda);
mem_mapping_add(&pc200->cga_mapping, 0xb8000, 0x08000, cga_read, NULL, NULL, cga_write, NULL, NULL, NULL, 0, &pc200->cga);
io_sethandler(0x03d0, 0x0010, pc200_in, NULL, NULL, pc200_out, NULL, NULL, pc200);
io_sethandler(0x03b0, 0x000c, pc200_in, NULL, NULL, pc200_out, NULL, NULL, pc200);
green = makecol(0x1C, 0x71, 0x31);
blue = makecol(0x0f, 0x21, 0x3f);
cgapal_rebuild(display_type, contrast);
set_lcd_cols(0);
return pc200;
}
void pc200_close(void *p)
{
pc200_t *pc200 = (pc200_t *)p;
free(pc200->cga.vram);
free(pc200);
}
void pc200_speed_changed(void *p)
{
pc200_t *pc200 = (pc200_t *)p;
cga_recalctimings(&pc200->cga);
mda_recalctimings(&pc200->mda);
setvidtime(pc200);
}
device_config_t pc200_config[] =
{
{
.name = "video_emulation",
.description = "Display type",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "CGA monitor",
.value = PC200_CGA,
},
{
.description = "MDA monitor",
.value = PC200_MDA,
},
{
.description = "Television",
.value = PC200_TV,
},
{
.description = ""
}
},
.default_int = PC200_CGA,
},
{
.name = "display_type",
.description = "Monitor type",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "RGB",
.value = DISPLAY_RGB
},
{
.description = "RGB (no brown)",
.value = DISPLAY_RGB_NO_BROWN
},
{
.description = "Green Monochrome",
.value = DISPLAY_GREEN
},
{
.description = "Amber Monochrome",
.value = DISPLAY_AMBER
},
{
.description = "White Monochrome",
.value = DISPLAY_WHITE
},
{
.description = ""
}
},
.default_int = DISPLAY_RGB
},
{
.name = "codepage",
.description = "Hardware font",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "US English",
.value = 3
},
{
.description = "Portugese",
.value = 2
},
{
.description = "Norwegian",
.value = 1
},
{
.description = "Greek",
.value = 0
},
{
.description = ""
}
},
.default_int = 3
},
{
.type = -1
}
};
device_config_t ppc512_config[] =
{
{
.name = "video_emulation",
.description = "Display type",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "CGA monitor",
.value = PC200_CGA,
},
{
.description = "MDA monitor",
.value = PC200_MDA,
},
{
.description = "LCD (CGA mode)",
.value = PC200_LCDC,
},
{
.description = "LCD (MDA mode)",
.value = PC200_LCDM,
},
{
.description = ""
}
},
.default_int = PC200_LCDC,
},
{
.name = "display_type",
.description = "External monitor",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "RGB",
.value = DISPLAY_RGB
},
{
.description = "RGB (no brown)",
.value = DISPLAY_RGB_NO_BROWN
},
{
.description = "Green Monochrome",
.value = DISPLAY_GREEN
},
{
.description = "Amber Monochrome",
.value = DISPLAY_AMBER
},
{
.description = "White Monochrome",
.value = DISPLAY_WHITE
},
{
.description = ""
}
},
.default_int = DISPLAY_RGB
},
{
.name = "codepage",
.description = "Hardware font",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "US English",
.value = 3
},
{
.description = "Portugese",
.value = 2
},
{
.description = "Norwegian",
.value = 1
},
{
.description = "Greek",
.value = 0
},
{
.description = ""
}
},
.default_int = 3
},
{
.type = -1
}
};
device_t pc200_device =
{
"Amstrad PC200 (video)",
0,
pc200_init,
pc200_close,
NULL,
pc200_speed_changed,
NULL,
NULL,
pc200_config
};
device_t ppc512_device =
{
"Amstrad PPC512 (video)",
0,
pc200_init,
pc200_close,
NULL,
pc200_speed_changed,
NULL,
NULL,
ppc512_config
};