Files
86Box/src/video/vid_ega.c

1124 lines
30 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Emulation of the EGA and Chips & Technologies SuperEGA
* graphics cards.
*
* Version: @(#)vid_ega.c 1.0.23 2019/11/19
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2008-2019 Sarah Walker.
* Copyright 2016-2019 Miran Grca.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../86box.h"
#include "../cpu/cpu.h"
#include "../io.h"
#include "../timer.h"
#include "../pit.h"
#include "../mem.h"
#include "../rom.h"
#include "../device.h"
#include "video.h"
#include "vid_ega.h"
#include "vid_ega_render.h"
void ega_doblit(int y1, int y2, int wx, int wy, ega_t *ega);
#define BIOS_IBM_PATH L"roms/video/ega/ibm_6277356_ega_card_u44_27128.bin"
#define BIOS_CPQ_PATH L"roms/video/ega/108281-001.bin"
#define BIOS_SEGA_PATH L"roms/video/ega/lega.vbi"
enum {
EGA_IBM = 0,
EGA_COMPAQ,
EGA_SUPEREGA
};
static video_timings_t timing_ega = {VIDEO_ISA, 8, 16, 32, 8, 16, 32};
static uint8_t ega_rotate[8][256];
static uint32_t pallook16[256], pallook64[256];
static int old_overscan_color = 0;
extern uint8_t edatlookup[4][4];
/* 3C2 controls default mode on EGA. On VGA, it determines monitor type (mono or colour):
7=CGA mode (200 lines), 9=EGA mode (350 lines), 8=EGA mode (200 lines). */
int egaswitchread, egaswitches=9;
int update_overscan = 0;
void
ega_out(uint16_t addr, uint8_t val, void *p)
{
ega_t *ega = (ega_t *)p;
int c;
uint8_t o, old;
if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(ega->miscout & 1))
addr ^= 0x60;
switch (addr) {
case 0x3c0: case 0x3c1:
if (!ega->attrff) {
ega->attraddr = val & 31;
if ((val & 0x20) != ega->attr_palette_enable) {
fullchange = 3;
ega->attr_palette_enable = val & 0x20;
ega_recalctimings(ega);
}
} else {
o = ega->attrregs[ega->attraddr & 31];
ega->attrregs[ega->attraddr & 31] = val;
if (ega->attraddr < 16)
fullchange = changeframecount;
if (ega->attraddr == 0x10 || ega->attraddr == 0x14 || ega->attraddr < 0x10) {
for (c = 0; c < 16; c++) {
if (ega->attrregs[0x10] & 0x80) ega->egapal[c] = (ega->attrregs[c] & 0xf) | ((ega->attrregs[0x14] & 0xf) << 4);
else ega->egapal[c] = (ega->attrregs[c] & 0x3f) | ((ega->attrregs[0x14] & 0xc) << 4);
}
}
/* Recalculate timings on change of attribute register 0x11
(overscan border color) too. */
if (ega->attraddr == 0x10) {
if (o != val)
ega_recalctimings(ega);
} else if (ega->attraddr == 0x11) {
ega->overscan_color = ega->vres ? pallook16[val & 0x0f] : pallook64[val & 0x3f];
if (o != val)
ega_recalctimings(ega);
} else if (ega->attraddr == 0x12)
ega->plane_mask = val & 0xf;
}
ega->attrff ^= 1;
break;
case 0x3c2:
o = ega->miscout;
egaswitchread = (val & 0xc) >> 2;
ega->vres = !(val & 0x80);
ega->pallook = ega->vres ? pallook16 : pallook64;
ega->vidclock = val & 4;
ega->miscout = val;
ega->overscan_color = ega->vres ? pallook16[ega->attrregs[0x11] & 0x0f] : pallook64[ega->attrregs[0x11] & 0x3f];
if ((o ^ val) & 0x80)
ega_recalctimings(ega);
break;
case 0x3c4:
ega->seqaddr = val;
break;
case 0x3c5:
o = ega->seqregs[ega->seqaddr & 0xf];
ega->seqregs[ega->seqaddr & 0xf] = val;
if (o != val && (ega->seqaddr & 0xf) == 1)
ega_recalctimings(ega);
switch (ega->seqaddr & 0xf) {
case 1:
if (ega->scrblank && !(val & 0x20))
fullchange = 3;
ega->scrblank = (ega->scrblank & ~0x20) | (val & 0x20);
break;
case 2:
ega->writemask = val & 0xf;
break;
case 3:
ega->charsetb = (((val >> 2) & 3) * 0x10000) + 2;
ega->charseta = ((val & 3) * 0x10000) + 2;
break;
case 4:
ega->chain2_write = !(val & 4);
break;
}
break;
case 0x3ce:
ega->gdcaddr = val;
break;
case 0x3cf:
ega->gdcreg[ega->gdcaddr & 15] = val;
switch (ega->gdcaddr & 15) {
case 2:
ega->colourcompare = val;
break;
case 4:
ega->readplane = val & 3;
break;
case 5:
ega->writemode = val & 3;
ega->readmode = val & 8;
ega->chain2_read = val & 0x10;
break;
case 6:
switch (val & 0xc) {
case 0x0: /*128k at A0000*/
mem_mapping_set_addr(&ega->mapping, 0xa0000, 0x20000);
break;
case 0x4: /*64k at A0000*/
mem_mapping_set_addr(&ega->mapping, 0xa0000, 0x10000);
break;
case 0x8: /*32k at B0000*/
mem_mapping_set_addr(&ega->mapping, 0xb0000, 0x08000);
break;
case 0xC: /*32k at B8000*/
mem_mapping_set_addr(&ega->mapping, 0xb8000, 0x08000);
break;
}
break;
case 7:
ega->colournocare = val;
break;
}
break;
case 0x3d0: case 0x3d4:
ega->crtcreg = val & 31;
return;
case 0x3d1: case 0x3d5:
if ((ega->crtcreg < 7) && (ega->crtc[0x11] & 0x80))
return;
if ((ega->crtcreg == 7) && (ega->crtc[0x11] & 0x80))
val = (ega->crtc[7] & ~0x10) | (val & 0x10);
old = ega->crtc[ega->crtcreg];
ega->crtc[ega->crtcreg] = val;
if (old != val) {
if (ega->crtcreg < 0xe || ega->crtcreg > 0x10) {
fullchange = changeframecount;
ega_recalctimings(ega);
}
}
break;
}
}
uint8_t ega_in(uint16_t addr, void *p)
{
ega_t *ega = (ega_t *)p;
uint8_t ret = 0xff;
if (((addr & 0xfff0) == 0x3d0 || (addr & 0xfff0) == 0x3b0) && !(ega->miscout & 1))
addr ^= 0x60;
switch (addr) {
case 0x3c0:
ret = ega->attraddr | ega->attr_palette_enable;
break;
case 0x3c1:
ret = ega->attrregs[ega->attraddr];
break;
case 0x3c2:
ret = (egaswitches & (8 >> egaswitchread)) ? 0x10 : 0x00;
break;
case 0x3c4:
ret = ega->seqaddr;
break;
case 0x3c5:
ret = ega->seqregs[ega->seqaddr & 0xf];
break;
case 0x3c8:
ret = 2;
break;
case 0x3cc:
ret = ega->miscout;
break;
case 0x3ce:
ret = ega->gdcaddr;
break;
case 0x3cf:
ret = ega->gdcreg[ega->gdcaddr & 0xf];
break;
case 0x3d0: case 0x3d4:
ret = ega->crtcreg;
break;
case 0x3d1:
case 0x3d5:
ret = ega->crtc[ega->crtcreg];
break;
case 0x3da:
ega->attrff = 0;
ega->stat ^= 0x30; /*Fools IBM EGA video BIOS self-test*/
ret = ega->stat;
break;
}
return ret;
}
void
ega_recalctimings(ega_t *ega)
{
double _dispontime, _dispofftime, disptime;
double crtcconst;
ega->vtotal = ega->crtc[6];
ega->dispend = ega->crtc[0x12];
ega->vsyncstart = ega->crtc[0x10];
ega->split = ega->crtc[0x18];
if (ega->crtc[7] & 1) ega->vtotal |= 0x100;
if (ega->crtc[7] & 32) ega->vtotal |= 0x200;
ega->vtotal += 2;
if (ega->crtc[7] & 2) ega->dispend |= 0x100;
if (ega->crtc[7] & 64) ega->dispend |= 0x200;
ega->dispend++;
if (ega->crtc[7] & 4) ega->vsyncstart |= 0x100;
if (ega->crtc[7] & 128) ega->vsyncstart |= 0x200;
ega->vsyncstart++;
if (ega->crtc[7] & 0x10) ega->split |= 0x100;
if (ega->crtc[9] & 0x40) ega->split |= 0x200;
ega->split++;
ega->hdisp = ega->crtc[1];
ega->hdisp++;
ega->rowoffset = ega->crtc[0x13];
ega->linedbl = ega->crtc[9] & 0x80;
ega->rowcount = ega->crtc[9] & 0x1f;
if (ega->vidclock) crtcconst = (ega->seqregs[1] & 1) ? MDACONST : (MDACONST * (9.0 / 8.0));
else crtcconst = (ega->seqregs[1] & 1) ? CGACONST : (CGACONST * (9.0 / 8.0));
ega->interlace = 0;
ega->ma_latch = (ega->crtc[0xc] << 8) | ega->crtc[0xd];
ega->render = ega_render_blank;
if (!ega->scrblank && ega->attr_palette_enable) {
if (!(ega->gdcreg[6] & 1)) {
if (ega->seqregs[1] & 8) {
ega->render = ega_render_text_40;
ega->hdisp *= (ega->seqregs[1] & 1) ? 16 : 18;
} else {
ega->render = ega_render_text_80;
ega->hdisp *= (ega->seqregs[1] & 1) ? 8 : 9;
}
ega->hdisp_old = ega->hdisp;
} else {
ega->hdisp *= (ega->seqregs[1] & 8) ? 16 : 8;
ega->hdisp_old = ega->hdisp;
switch (ega->gdcreg[5] & 0x20) {
case 0x00:
if (ega->seqregs[1] & 8)
ega->render = ega_render_4bpp_lowres;
else
ega->render = ega_render_4bpp_highres;
break;
case 0x20:
if (ega->seqregs[1] & 8)
ega->render = ega_render_2bpp_lowres;
else
ega->render = ega_render_2bpp_highres;
break;
}
}
}
if (enable_overscan) {
overscan_y = (ega->rowcount + 1) << 1;
if (overscan_y < 16)
overscan_y = 16;
}
overscan_x = (ega->seqregs[1] & 1) ? 16 : 18;
if (ega->seqregs[1] & 8)
overscan_x <<= 1;
ega->y_add = (overscan_y >> 1) - (ega->crtc[8] & 0x1f);
ega->x_add = (overscan_x >> 1);
if (ega->seqregs[1] & 8) {
disptime = (double) ((ega->crtc[0] + 2) << 1);
_dispontime = (double) ((ega->crtc[1] + 1) << 1);
} else {
disptime = (double) (ega->crtc[0] + 2);
_dispontime = (double) (ega->crtc[1] + 1);
}
_dispofftime = disptime - _dispontime;
_dispontime = _dispontime * crtcconst;
_dispofftime = _dispofftime * crtcconst;
ega->dispontime = (uint64_t)(_dispontime);
ega->dispofftime = (uint64_t)(_dispofftime);
}
void
ega_poll(void *p)
{
ega_t *ega = (ega_t *)p;
int x, old_ma;
int wx = 640, wy = 350;
uint32_t blink_delay;
if (!ega->linepos) {
timer_advance_u64(&ega->timer, ega->dispofftime);
ega->stat |= 1;
ega->linepos = 1;
if (ega->dispon) {
ega->hdisp_on = 1;
ega->ma &= ega->vrammask;
if (ega->firstline == 2000) {
ega->firstline = ega->displine;
video_wait_for_buffer();
}
if (ega->vres) {
old_ma = ega->ma;
ega->displine <<= 1;
ega->y_add <<= 1;
ega->render(ega);
ega->x_add = (overscan_x >> 1);
ega_render_overscan_left(ega);
ega_render_overscan_right(ega);
ega->x_add = (overscan_x >> 1) - ega->scrollcache;
ega->displine++;
ega->ma = old_ma;
ega_render_overscan_left(ega);
ega->render(ega);
ega_render_overscan_right(ega);
ega->y_add >>= 1;
ega->displine >>= 1;
} else {
ega_render_overscan_left(ega);
ega->render(ega);
ega_render_overscan_right(ega);
}
if (ega->lastline < ega->displine)
ega->lastline = ega->displine;
}
ega->displine++;
if (ega->interlace)
ega->displine++;
if ((ega->stat & 8) && ((ega->displine & 15) == (ega->crtc[0x11] & 15)) && ega->vslines)
ega->stat &= ~8;
ega->vslines++;
if (ega->displine > 500)
ega->displine = 0;
} else {
timer_advance_u64(&ega->timer, ega->dispontime);
if (ega->dispon)
ega->stat &= ~1;
ega->hdisp_on = 0;
ega->linepos = 0;
if ((ega->sc == (ega->crtc[11] & 31)) || (ega->sc == ega->rowcount))
ega->con = 0;
if (ega->dispon) {
if (ega->linedbl && !ega->linecountff) {
ega->linecountff = 1;
ega->ma = ega->maback;
} if (ega->sc == (ega->crtc[9] & 31)) {
ega->linecountff = 0;
ega->sc = 0;
ega->maback += (ega->rowoffset << 3);
if (ega->interlace)
ega->maback += (ega->rowoffset << 3);
ega->maback &= ega->vrammask;
ega->ma = ega->maback;
} else {
ega->linecountff = 0;
ega->sc++;
ega->sc &= 31;
ega->ma = ega->maback;
}
}
ega->vc++;
ega->vc &= 1023;
if (ega->vc == ega->split) {
ega->ma = ega->maback = 0;
ega->sc = 0;
if (ega->attrregs[0x10] & 0x20) {
ega->scrollcache = 0;
ega->x_add = (overscan_x >> 1);
}
}
if (ega->vc == ega->dispend) {
ega->dispon = 0;
blink_delay = (ega->crtc[11] & 0x60) >> 5;
if (ega->crtc[10] & 0x20)
ega->cursoron = 0;
else if (blink_delay == 2)
ega->cursoron = ((ega->blink % 96) >= 48);
else
ega->cursoron = ega->blink & (16 + (16 * blink_delay));
if (!(ega->gdcreg[6] & 1) && !(ega->blink & 15))
fullchange = 2;
ega->blink = (ega->blink + 1) & 0x7f;
if (fullchange)
fullchange--;
}
if (ega->vc == ega->vsyncstart) {
ega->dispon = 0;
ega->stat |= 8;
x = ega->hdisp;
if (ega->interlace && !ega->oddeven)
ega->lastline++;
if (ega->interlace && ega->oddeven)
ega->firstline--;
wx = x;
if (ega->vres) {
wy = (ega->lastline - ega->firstline) << 1;
ega_doblit(ega->firstline_draw << 1, (ega->lastline_draw + 1) << 1, wx, wy, ega);
} else {
wy = ega->lastline - ega->firstline;
ega_doblit(ega->firstline_draw, ega->lastline_draw + 1, wx, wy, ega);
}
frames++;
ega->firstline = 2000;
ega->lastline = 0;
ega->firstline_draw = 2000;
ega->lastline_draw = 0;
ega->oddeven ^= 1;
changeframecount = ega->interlace ? 3 : 2;
ega->vslines = 0;
if (ega->interlace && ega->oddeven)
ega->ma = ega->maback = ega->ma_latch + (ega->rowoffset << 1);
else
ega->ma = ega->maback = ega->ma_latch;
ega->ca = (ega->crtc[0xe] << 8) | ega->crtc[0xf];
ega->ma <<= 2;
ega->maback <<= 2;
ega->ca <<= 2;
}
if (ega->vc == ega->vtotal) {
ega->vc = 0;
ega->sc = 0;
ega->dispon = 1;
ega->displine = (ega->interlace && ega->oddeven) ? 1 : 0;
ega->scrollcache = (ega->attrregs[0x13] & 0x0f);
if (!(ega->gdcreg[6] & 1) && !(ega->attrregs[0x10] & 1)) { /*Text mode*/
if (ega->seqregs[1] & 1)
ega->scrollcache &= 0x07;
else {
ega->scrollcache++;
if (ega->scrollcache > 8)
ega->scrollcache = 0;
}
} else
ega->scrollcache &= 0x07;
if (ega->seqregs[1] & 8)
ega->scrollcache <<= 1;
ega->x_add = (overscan_x >> 1) - ega->scrollcache;
ega->linecountff = 0;
}
if (ega->sc == (ega->crtc[10] & 31))
ega->con = 1;
}
}
void
ega_doblit(int y1, int y2, int wx, int wy, ega_t *ega)
{
int y_add = (enable_overscan) ? overscan_y : 0;
int x_add = (enable_overscan) ? overscan_x : 0;
int y_start = (enable_overscan) ? 0 : (overscan_y >> 1);
int x_start = (enable_overscan) ? 0 : (overscan_x >> 1);
int bottom = (overscan_y >> 1) + (ega->crtc[8] & 0x1f);
uint32_t *p;
int i, j;
int xs_temp, ys_temp;
if (ega->vres) {
y_add <<= 1;
y_start <<= 1;
bottom <<= 1;
}
if ((wx <= 0) || (wy <= 0)) {
video_blit_memtoscreen(x_start, y_start, 0, 0, 0, 0);
return;
}
if (y1 > y2) {
video_blit_memtoscreen(x_start, y_start, 0, 0, xsize + x_add, ysize + y_add);
return;
}
if (ega->vres)
ega->y_add <<= 1;
xs_temp = wx;
ys_temp = wy + 1;
if (ega->vres)
ys_temp++;
if (xs_temp < 64)
xs_temp = 640;
if (ys_temp < 32)
ys_temp = 200;
if ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get()) {
/* Screen res has changed.. fix up, and let them know. */
xsize = xs_temp;
ysize = ys_temp;
if ((xsize > 1984) || (ysize > 2016)) {
/* 2048x2048 is the biggest safe render texture, to account for overscan,
we suppress overscan starting from x 1984 and y 2016. */
x_add = 0;
y_add = 0;
suppress_overscan = 1;
} else
suppress_overscan = 0;
set_screen_size(xsize + x_add, ysize + y_add);
if (video_force_resize_get())
video_force_resize_set(0);
}
if ((wx >= 160) && ((wy + 1) >= 120)) {
/* Draw (overscan_size - scroll size) lines of overscan on top and bottom. */
for (i = 0; i < ega->y_add; i++) {
p = &buffer32->line[i & 0x7ff][0];
for (j = 0; j < (xsize + x_add); j++)
p[j] = ega->overscan_color;
}
for (i = 0; i < bottom; i++) {
p = &buffer32->line[(ysize + ega->y_add + i) & 0x7ff][0];
for (j = 0; j < (xsize + x_add); j++)
p[j] = ega->overscan_color;
}
}
video_blit_memtoscreen(x_start, y_start, y1, y2 + y_add, xsize + x_add, ysize + y_add);
if (ega->vres)
ega->y_add >>= 1;
}
void
ega_write(uint32_t addr, uint8_t val, void *p)
{
ega_t *ega = (ega_t *)p;
uint8_t vala, valb, valc, vald;
int writemask2 = ega->writemask;
egawrites++;
sub_cycles(video_timing_write_b);
if (addr >= 0xB0000) addr &= 0x7fff;
else addr &= 0xffff;
if (ega->chain2_write) {
writemask2 &= ~0xa;
if (addr & 1)
writemask2 <<= 1;
addr &= ~1;
if (addr & 0x4000)
addr |= 1;
addr &= ~0x4000;
}
addr <<= 2;
if (addr >= ega->vram_limit)
return;
if (!(ega->gdcreg[6] & 1))
fullchange = 2;
switch (ega->writemode) {
case 1:
if (writemask2 & 1) ega->vram[addr] = ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = ega->ld;
break;
case 0:
if (ega->gdcreg[3] & 7)
val = ega_rotate[ega->gdcreg[3] & 7][val];
if ((ega->gdcreg[8] == 0xff) && !(ega->gdcreg[3] & 0x18) && !ega->gdcreg[1]) {
if (writemask2 & 1) ega->vram[addr] = val;
if (writemask2 & 2) ega->vram[addr | 0x1] = val;
if (writemask2 & 4) ega->vram[addr | 0x2] = val;
if (writemask2 & 8) ega->vram[addr | 0x3] = val;
} else {
if (ega->gdcreg[1] & 1) vala = (ega->gdcreg[0] & 1) ? 0xff : 0;
else vala = val;
if (ega->gdcreg[1] & 2) valb = (ega->gdcreg[0] & 2) ? 0xff : 0;
else valb = val;
if (ega->gdcreg[1] & 4) valc = (ega->gdcreg[0] & 4) ? 0xff : 0;
else valc = val;
if (ega->gdcreg[1] & 8) vald = (ega->gdcreg[0] & 8) ? 0xff : 0;
else vald = val;
switch (ega->gdcreg[3] & 0x18) {
case 0: /*Set*/
if (writemask2 & 1) ega->vram[addr] = (vala & ega->gdcreg[8]) | (ega->la & ~ega->gdcreg[8]);
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb & ega->gdcreg[8]) | (ega->lb & ~ega->gdcreg[8]);
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc & ega->gdcreg[8]) | (ega->lc & ~ega->gdcreg[8]);
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald & ega->gdcreg[8]) | (ega->ld & ~ega->gdcreg[8]);
break;
case 8: /*AND*/
if (writemask2 & 1) ega->vram[addr] = (vala | ~ega->gdcreg[8]) & ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb | ~ega->gdcreg[8]) & ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc | ~ega->gdcreg[8]) & ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald | ~ega->gdcreg[8]) & ega->ld;
break;
case 0x10: /*OR*/
if (writemask2 & 1) ega->vram[addr] = (vala & ega->gdcreg[8]) | ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb & ega->gdcreg[8]) | ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc & ega->gdcreg[8]) | ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald & ega->gdcreg[8]) | ega->ld;
break;
case 0x18: /*XOR*/
if (writemask2 & 1) ega->vram[addr] = (vala & ega->gdcreg[8]) ^ ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb & ega->gdcreg[8]) ^ ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc & ega->gdcreg[8]) ^ ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald & ega->gdcreg[8]) ^ ega->ld;
break;
}
}
break;
case 2:
if (!(ega->gdcreg[3] & 0x18) && !ega->gdcreg[1]) {
if (writemask2 & 1) ega->vram[addr] = (((val & 1) ? 0xff : 0) & ega->gdcreg[8]) | (ega->la & ~ega->gdcreg[8]);
if (writemask2 & 2) ega->vram[addr | 0x1] = (((val & 2) ? 0xff : 0) & ega->gdcreg[8]) | (ega->lb & ~ega->gdcreg[8]);
if (writemask2 & 4) ega->vram[addr | 0x2] = (((val & 4) ? 0xff : 0) & ega->gdcreg[8]) | (ega->lc & ~ega->gdcreg[8]);
if (writemask2 & 8) ega->vram[addr | 0x3] = (((val & 8) ? 0xff : 0) & ega->gdcreg[8]) | (ega->ld & ~ega->gdcreg[8]);
} else {
vala = ((val & 1) ? 0xff : 0);
valb = ((val & 2) ? 0xff : 0);
valc = ((val & 4) ? 0xff : 0);
vald = ((val & 8) ? 0xff : 0);
switch (ega->gdcreg[3] & 0x18) {
case 0: /*Set*/
if (writemask2 & 1) ega->vram[addr] = (vala & ega->gdcreg[8]) | (ega->la & ~ega->gdcreg[8]);
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb & ega->gdcreg[8]) | (ega->lb & ~ega->gdcreg[8]);
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc & ega->gdcreg[8]) | (ega->lc & ~ega->gdcreg[8]);
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald & ega->gdcreg[8]) | (ega->ld & ~ega->gdcreg[8]);
break;
case 8: /*AND*/
if (writemask2 & 1) ega->vram[addr] = (vala | ~ega->gdcreg[8]) & ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb | ~ega->gdcreg[8]) & ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc | ~ega->gdcreg[8]) & ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald | ~ega->gdcreg[8]) & ega->ld;
break;
case 0x10: /*OR*/
if (writemask2 & 1) ega->vram[addr] = (vala & ega->gdcreg[8]) | ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb & ega->gdcreg[8]) | ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc & ega->gdcreg[8]) | ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald & ega->gdcreg[8]) | ega->ld;
break;
case 0x18: /*XOR*/
if (writemask2 & 1) ega->vram[addr] = (vala & ega->gdcreg[8]) ^ ega->la;
if (writemask2 & 2) ega->vram[addr | 0x1] = (valb & ega->gdcreg[8]) ^ ega->lb;
if (writemask2 & 4) ega->vram[addr | 0x2] = (valc & ega->gdcreg[8]) ^ ega->lc;
if (writemask2 & 8) ega->vram[addr | 0x3] = (vald & ega->gdcreg[8]) ^ ega->ld;
break;
}
}
break;
}
}
uint8_t
ega_read(uint32_t addr, void *p)
{
ega_t *ega = (ega_t *)p;
uint8_t temp, temp2, temp3, temp4;
int readplane = ega->readplane;
egareads++;
sub_cycles(video_timing_read_b);
if (addr >= 0xb0000) addr &= 0x7fff;
else addr &= 0xffff;
if (ega->chain2_read) {
readplane = (readplane & 2) | (addr & 1);
addr &= ~1;
if (addr & 0x4000)
addr |= 1;
addr &= ~0x4000;
}
addr <<= 2;
if (addr >= ega->vram_limit)
return 0xff;
ega->la = ega->vram[addr];
ega->lb = ega->vram[addr | 0x1];
ega->lc = ega->vram[addr | 0x2];
ega->ld = ega->vram[addr | 0x3];
if (ega->readmode) {
temp = ega->la;
temp ^= (ega->colourcompare & 1) ? 0xff : 0;
temp &= (ega->colournocare & 1) ? 0xff : 0;
temp2 = ega->lb;
temp2 ^= (ega->colourcompare & 2) ? 0xff : 0;
temp2 &= (ega->colournocare & 2) ? 0xff : 0;
temp3 = ega->lc;
temp3 ^= (ega->colourcompare & 4) ? 0xff : 0;
temp3 &= (ega->colournocare & 4) ? 0xff : 0;
temp4 = ega->ld;
temp4 ^= (ega->colourcompare & 8) ? 0xff : 0;
temp4 &= (ega->colournocare & 8) ? 0xff : 0;
return ~(temp | temp2 | temp3 | temp4);
}
return ega->vram[addr | readplane];
}
void
ega_init(ega_t *ega, int monitor_type, int is_mono)
{
int c, d, e;
ega->vram = malloc(0x40000);
ega->vrammask = 0x3ffff;
for (c = 0; c < 256; c++) {
e = c;
for (d = 0; d < 8; d++) {
ega_rotate[d][c] = e;
e = (e >> 1) | ((e & 1) ? 0x80 : 0);
}
}
for (c = 0; c < 4; c++) {
for (d = 0; d < 4; d++) {
edatlookup[c][d] = 0;
if (c & 1) edatlookup[c][d] |= 1;
if (d & 1) edatlookup[c][d] |= 2;
if (c & 2) edatlookup[c][d] |= 0x10;
if (d & 2) edatlookup[c][d] |= 0x20;
}
}
if (is_mono) {
for (c = 0; c < 256; c++) {
if (((c >> 3) & 3) == 0)
pallook64[c] = pallook16[c] = makecol32(0, 0, 0);
else switch (monitor_type >> 4) {
case DISPLAY_GREEN:
switch ((c >> 3) & 3) {
case 1:
pallook64[c] = pallook16[c] = makecol32(0x08, 0xc7, 0x2c);
break;
case 2:
pallook64[c] = pallook16[c] = makecol32(0x04, 0x8a, 0x20);
break;
case 3:
pallook64[c] = pallook16[c] = makecol32(0x34, 0xff, 0x5d);
break;
}
break;
case DISPLAY_AMBER:
switch ((c >> 3) & 3) {
case 1:
pallook64[c] = pallook16[c] = makecol32(0xef, 0x79, 0x00);
break;
case 2:
pallook64[c] = pallook16[c] = makecol32(0xb2, 0x4d, 0x00);
break;
case 3:
pallook64[c] = pallook16[c] = makecol32(0xff, 0xe3, 0x34);
break;
}
break;
case DISPLAY_WHITE: default:
switch ((c >> 3) & 3) {
case 1:
pallook64[c] = pallook16[c] = makecol32(0xaf, 0xb3, 0xb0);
break;
case 2:
pallook64[c] = pallook16[c] = makecol32(0x7a, 0x81, 0x83);
break;
case 3:
pallook64[c] = pallook16[c] = makecol32(0xff, 0xfd, 0xed);
break;
}
break;
}
}
} else {
for (c = 0; c < 256; c++) {
pallook64[c] = makecol32(((c >> 2) & 1) * 0xaa, ((c >> 1) & 1) * 0xaa, (c & 1) * 0xaa);
pallook64[c] += makecol32(((c >> 5) & 1) * 0x55, ((c >> 4) & 1) * 0x55, ((c >> 3) & 1) * 0x55);
pallook16[c] = makecol32(((c >> 2) & 1) * 0xaa, ((c >> 1) & 1) * 0xaa, (c & 1) * 0xaa);
pallook16[c] += makecol32(((c >> 4) & 1) * 0x55, ((c >> 4) & 1) * 0x55, ((c >> 4) & 1) * 0x55);
if ((c & 0x17) == 6)
pallook16[c] = makecol32(0xaa, 0x55, 0);
}
}
ega->pallook = pallook16;
egaswitches = monitor_type & 0xf;
ega->vram_limit = 256 * 1024;
ega->vrammask = ega->vram_limit - 1;
old_overscan_color = 0;
ega->miscout |= 0x22;
ega->oddeven_page = 0;
ega->seqregs[4] |= 2;
ega->extvram = 1;
update_overscan = 0;
ega->crtc[0] = 63;
ega->crtc[6] = 255;
timer_add(&ega->timer, ega_poll, ega, 1);
}
static void *
ega_standalone_init(const device_t *info)
{
ega_t *ega = malloc(sizeof(ega_t));
int monitor_type, c;
memset(ega, 0, sizeof(ega_t));
video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_ega);
overscan_x = 16;
overscan_y = 28;
ega->x_add = 8;
ega->y_add = 14;
switch(info->local) {
case EGA_IBM:
default:
rom_init(&ega->bios_rom, BIOS_IBM_PATH,
0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
break;
case EGA_COMPAQ:
rom_init(&ega->bios_rom, BIOS_CPQ_PATH,
0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
break;
case EGA_SUPEREGA:
rom_init(&ega->bios_rom, BIOS_SEGA_PATH,
0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
break;
}
if ((ega->bios_rom.rom[0x3ffe] == 0xaa) && (ega->bios_rom.rom[0x3fff] == 0x55)) {
for (c = 0; c < 0x2000; c++) {
uint8_t temp = ega->bios_rom.rom[c];
ega->bios_rom.rom[c] = ega->bios_rom.rom[0x3fff - c];
ega->bios_rom.rom[0x3fff - c] = temp;
}
}
monitor_type = device_get_config_int("monitor_type");
ega_init(ega, monitor_type, (monitor_type & 0x0F) == 0x0B);
ega->vram_limit = device_get_config_int("memory") * 1024;
ega->vrammask = ega->vram_limit - 1;
mem_mapping_add(&ega->mapping, 0xa0000, 0x20000, ega_read, NULL, NULL, ega_write, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, ega);
io_sethandler(0x03a0, 0x0040, ega_in, NULL, NULL, ega_out, NULL, NULL, ega);
return ega;
}
static int
ega_standalone_available(void)
{
return rom_present(BIOS_IBM_PATH);
}
static int
cpqega_standalone_available(void)
{
return rom_present(BIOS_CPQ_PATH);
}
static int
sega_standalone_available(void)
{
return rom_present(BIOS_SEGA_PATH);
}
static void
ega_close(void *p)
{
ega_t *ega = (ega_t *)p;
free(ega->vram);
free(ega);
}
static void
ega_speed_changed(void *p)
{
ega_t *ega = (ega_t *)p;
ega_recalctimings(ega);
}
/* SW1 SW2 SW3 SW4
OFF OFF ON OFF Monochrome (5151) 1011 0x0B
ON OFF OFF ON Color 40x25 (5153) 0110 0x06
OFF OFF OFF ON Color 80x25 (5153) 0111 0x07
ON ON ON OFF Enhanced Color - Normal Mode (5154) 1000 0x08
OFF ON ON OFF Enhanced Color - Enhanced Mode (5154) 1001 0x09
0 = Switch closed (ON);
1 = Switch open (OFF). */
static const device_config_t ega_config[] =
{
{
"memory", "Memory size", CONFIG_SELECTION, "", 256,
{
{
"64 kB", 64
},
{
"128 kB", 128
},
{
"256 kB", 256
},
{
""
}
}
},
{
.name = "monitor_type",
.description = "Monitor type",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "Monochrome (5151/MDA) (white)",
.value = 0x0B | (DISPLAY_WHITE << 4)
},
{
.description = "Monochrome (5151/MDA) (green)",
.value = 0x0B | (DISPLAY_GREEN << 4)
},
{
.description = "Monochrome (5151/MDA) (amber)",
.value = 0x0B | (DISPLAY_AMBER << 4)
},
{
.description = "Color 40x25 (5153/CGA)",
.value = 0x06
},
{
.description = "Color 80x25 (5153/CGA)",
.value = 0x07
},
{
.description = "Enhanced Color - Normal Mode (5154/ECD)",
.value = 0x08
},
{
.description = "Enhanced Color - Enhanced Mode (5154/ECD)",
.value = 0x09
},
{
.description = ""
}
},
.default_int = 9
},
{
.type = -1
}
};
const device_t ega_device =
{
"EGA",
DEVICE_ISA,
EGA_IBM,
ega_standalone_init, ega_close, NULL,
ega_standalone_available,
ega_speed_changed,
NULL,
ega_config
};
const device_t cpqega_device =
{
"Compaq EGA",
DEVICE_ISA,
EGA_COMPAQ,
ega_standalone_init, ega_close, NULL,
cpqega_standalone_available,
ega_speed_changed,
NULL,
ega_config
};
const device_t sega_device =
{
"SuperEGA",
DEVICE_ISA,
EGA_SUPEREGA,
ega_standalone_init, ega_close, NULL,
sega_standalone_available,
ega_speed_changed,
NULL,
ega_config
};