dosbox-wii/src/hardware/vga_tseng.cpp
2021-02-06 16:06:31 +01:00

808 lines
24 KiB
C++

/*
* Copyright (C) 2002-2019 The DOSBox Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "dosbox.h"
#include "setup.h"
#include "vga.h"
#include "inout.h"
#include "mem.h"
#include <cstdlib>
// Tseng ET4K data
typedef struct {
Bit8u extensionsEnabled;
// Stored exact values of some registers. Documentation only specifies some bits but hardware checks may
// expect other bits to be preserved.
Bitu store_3d4_31;
Bitu store_3d4_32;
Bitu store_3d4_33;
Bitu store_3d4_34;
Bitu store_3d4_35;
Bitu store_3d4_36;
Bitu store_3d4_37;
Bitu store_3d4_3f;
Bitu store_3c0_16;
Bitu store_3c0_17;
Bitu store_3c4_06;
Bitu store_3c4_07;
Bitu clockFreq[16];
Bitu biosMode;
} SVGA_ET4K_DATA;
static SVGA_ET4K_DATA et4k = { 1,0,0,0,0,0,0,0,0, 0,0, 0,0,
{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0 };
#define STORE_ET4K(port, index) \
case 0x##index: \
et4k.store_##port##_##index = val; \
break;
#define RESTORE_ET4K(port, index) \
case 0x##index: \
return et4k.store_##port##_##index;
// Tseng ET4K implementation
void write_p3d5_et4k(Bitu reg,Bitu val,Bitu iolen) {
if(!et4k.extensionsEnabled && reg!=0x33)
return;
switch(reg) {
/*
3d4h index 31h (R/W): General Purpose
bit 0-3 Scratch pad
6-7 Clock Select bits 3-4. Bits 0-1 are in 3C2h/3CCh bits 2-3.
*/
STORE_ET4K(3d4, 31);
// 3d4h index 32h - RAS/CAS Configuration (R/W)
// No effect on emulation. Should not be written by software.
STORE_ET4K(3d4, 32);
case 0x33:
// 3d4 index 33h (R/W): Extended start Address
// 0-1 Display Start Address bits 16-17
// 2-3 Cursor start address bits 16-17
// Used by standard Tseng ID scheme
et4k.store_3d4_33 = val;
vga.config.display_start = (vga.config.display_start & 0xffff) | ((val & 0x03)<<16);
vga.config.cursor_start = (vga.config.cursor_start & 0xffff) | ((val & 0x0c)<<14);
break;
/*
3d4h index 34h (R/W): 6845 Compatibility Control Register
bit 0 Enable CS0 (alternate clock timing)
1 Clock Select bit 2. Bits 0-1 in 3C2h bits 2-3, bits 3-4 are in 3d4h
index 31h bits 6-7
2 Tristate ET4000 bus and color outputs if set
3 Video Subsystem Enable Register at 46E8h if set, at 3C3h if clear.
4 Enable Translation ROM for reading CRTC and MISCOUT if set
5 Enable Translation ROM for writing CRTC and MISCOUT if set
6 Enable double scan in AT&T compatibility mode if set
7 Enable 6845 compatibility if set
*/
// TODO: Bit 6 may have effect on emulation
STORE_ET4K(3d4, 34);
case 0x35:
/*
3d4h index 35h (R/W): Overflow High
bit 0 Vertical Blank Start Bit 10 (3d4h index 15h).
1 Vertical Total Bit 10 (3d4h index 6).
2 Vertical Display End Bit 10 (3d4h index 12h).
3 Vertical Sync Start Bit 10 (3d4h index 10h).
4 Line Compare Bit 10 (3d4h index 18h).
5 Gen-Lock Enabled if set (External sync)
6 (4000) Read/Modify/Write Enabled if set. Currently not implemented.
7 Vertical interlace if set. The Vertical timing registers are
programmed as if the mode was non-interlaced!!
*/
et4k.store_3d4_35 = val;
vga.config.line_compare = (vga.config.line_compare & 0x3ff) | ((val&0x10)<<6);
// Abusing s3 ex_ver_overflow field. This is to be cleaned up later.
{
Bit8u s3val =
((val & 0x01) << 2) | // vbstart
((val & 0x02) >> 1) | // vtotal
((val & 0x04) >> 1) | // vdispend
((val & 0x08) << 1) | // vsyncstart (?)
((val & 0x10) << 2); // linecomp
if ((s3val ^ vga.s3.ex_ver_overflow) & 0x3) {
vga.s3.ex_ver_overflow=s3val;
VGA_StartResize();
} else vga.s3.ex_ver_overflow=s3val;
}
break;
// 3d4h index 36h - Video System Configuration 1 (R/W)
// VGADOC provides a lot of info on this register, Ferraro has significantly less detail.
// This is unlikely to be used by any games. Bit 4 switches chipset into linear mode -
// that may be useful in some cases if there is any software actually using it.
// TODO (not near future): support linear addressing
STORE_ET4K(3d4, 36);
// 3d4h index 37 - Video System Configuration 2 (R/W)
// Bits 0,1, and 3 provides information about memory size:
// 0-1 Bus width (1: 8 bit, 2: 16 bit, 3: 32 bit)
// 3 Size of RAM chips (0: 64Kx, 1: 256Kx)
// Other bits have no effect on emulation.
case 0x37:
if (val != et4k.store_3d4_37) {
et4k.store_3d4_37 = val;
vga.vmemwrap = ((64*1024)<<((val&8)>>2))<<((val&3)-1);
VGA_SetupHandlers();
}
break;
case 0x3f:
/*
3d4h index 3Fh (R/W):
bit 0 Bit 8 of the Horizontal Total (3d4h index 0)
2 Bit 8 of the Horizontal Blank Start (3d4h index 3)
4 Bit 8 of the Horizontal Retrace Start (3d4h index 4)
7 Bit 8 of the CRTC offset register (3d4h index 13h).
*/
// The only unimplemented one is bit 7
et4k.store_3d4_3f = val;
// Abusing s3 ex_hor_overflow field which very similar. This is
// to be cleaned up later
if ((val ^ vga.s3.ex_hor_overflow) & 3) {
vga.s3.ex_hor_overflow=(val&0x15);
VGA_StartResize();
} else vga.s3.ex_hor_overflow=(val&0x15);
break;
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET4K:Write to illegal index %2X", reg);
break;
}
}
Bitu read_p3d5_et4k(Bitu reg,Bitu iolen) {
if (!et4k.extensionsEnabled && reg!=0x33)
return 0x0;
switch(reg) {
RESTORE_ET4K(3d4, 31);
RESTORE_ET4K(3d4, 32);
RESTORE_ET4K(3d4, 33);
RESTORE_ET4K(3d4, 34);
RESTORE_ET4K(3d4, 35);
RESTORE_ET4K(3d4, 36);
RESTORE_ET4K(3d4, 37);
RESTORE_ET4K(3d4, 3f);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET4K:Read from illegal index %2X", reg);
break;
}
return 0x0;
}
void write_p3c5_et4k(Bitu reg,Bitu val,Bitu iolen) {
switch(reg) {
/*
3C4h index 6 (R/W): TS State Control
bit 1-2 Font Width Select in dots/character
If 3C4h index 4 bit 0 clear:
0: 9 dots, 1: 10 dots, 2: 12 dots, 3: 6 dots
If 3C4h index 5 bit 0 set:
0: 8 dots, 1: 11 dots, 2: 7 dots, 3: 16 dots
Only valid if 3d4h index 34h bit 3 set.
*/
// TODO: Figure out if this has any practical use
STORE_ET4K(3c4, 06);
// 3C4h index 7 (R/W): TS Auxiliary Mode
// Unlikely to be used by games (things like ROM enable/disable and emulation of VGA vs EGA)
STORE_ET4K(3c4, 07);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET4K:Write to illegal index %2X", reg);
break;
}
}
Bitu read_p3c5_et4k(Bitu reg,Bitu iolen) {
switch(reg) {
RESTORE_ET4K(3c4, 06);
RESTORE_ET4K(3c4, 07);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET4K:Read from illegal index %2X", reg);
break;
}
return 0x0;
}
/*
3CDh (R/W): Segment Select
bit 0-3 64k Write bank number (0..15)
4-7 64k Read bank number (0..15)
*/
void write_p3cd_et4k(Bitu port,Bitu val,Bitu iolen) {
vga.svga.bank_write = val & 0x0f;
vga.svga.bank_read = (val>>4) & 0x0f;
VGA_SetupHandlers();
}
Bitu read_p3cd_et4k(Bitu port,Bitu iolen) {
return (vga.svga.bank_read<<4)|vga.svga.bank_write;
}
void write_p3c0_et4k(Bitu reg,Bitu val,Bitu iolen) {
switch(reg) {
// 3c0 index 16h: ATC Miscellaneous
// VGADOC provides a lot of information, Ferarro documents only two bits
// and even those incompletely. The register is used as part of identification
// scheme.
// Unlikely to be used by any games but double timing may be useful.
// TODO: Figure out if this has any practical use
STORE_ET4K(3c0, 16);
/*
3C0h index 17h (R/W): Miscellaneous 1
bit 7 If set protects the internal palette ram and redefines the attribute
bits as follows:
Monochrome:
bit 0-2 Select font 0-7
3 If set selects blinking
4 If set selects underline
5 If set prevents the character from being displayed
6 If set displays the character at half intensity
7 If set selects reverse video
Color:
bit 0-1 Selects font 0-3
2 Foreground Blue
3 Foreground Green
4 Foreground Red
5 Background Blue
6 Background Green
7 Background Red
*/
// TODO: Figure out if this has any practical use
STORE_ET4K(3c0, 17);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET4K:Write to illegal index %2X", reg);
break;
}
}
Bitu read_p3c1_et4k(Bitu reg,Bitu iolen) {
switch(reg) {
RESTORE_ET4K(3c0, 16);
RESTORE_ET4K(3c0, 17);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET4K:Read from illegal index %2X", reg);
break;
}
return 0x0;
}
/*
These ports are used but have little if any effect on emulation:
3BFh (R/W): Hercules Compatibility Mode
3CBh (R/W): PEL Address/Data Wd
3CEh index 0Dh (R/W): Microsequencer Mode
3CEh index 0Eh (R/W): Microsequencer Reset
3d8h (R/W): Display Mode Control
3DEh (W); AT&T Mode Control Register
*/
static Bitu get_clock_index_et4k() {
// Ignoring bit 4, using "only" 16 frequencies. Looks like most implementations had only that
return ((vga.misc_output>>2)&3) | ((et4k.store_3d4_34<<1)&4) | ((et4k.store_3d4_31>>3)&8);
}
static void set_clock_index_et4k(Bitu index) {
// Shortwiring register reads/writes for simplicity
IO_Write(0x3c2, (vga.misc_output&~0x0c)|((index&3)<<2));
et4k.store_3d4_34 = (et4k.store_3d4_34&~0x02)|((index&4)>>1);
et4k.store_3d4_31 = (et4k.store_3d4_31&~0xc0)|((index&8)<<3); // (index&0x18) if 32 clock frequencies are to be supported
}
void FinishSetMode_ET4K(Bitu crtc_base, VGA_ModeExtraData* modeData) {
et4k.biosMode = modeData->modeNo;
IO_Write(0x3cd, 0x00); // both banks to 0
// Reinterpret hor_overflow. Curiously, three bits out of four are
// in the same places. Input has hdispend (not supported), output
// has CRTC offset (also not supported)
Bit8u et4k_hor_overflow =
(modeData->hor_overflow & 0x01) |
(modeData->hor_overflow & 0x04) |
(modeData->hor_overflow & 0x10);
IO_Write(crtc_base,0x3f);IO_Write(crtc_base+1,et4k_hor_overflow);
// Reinterpret ver_overflow
Bit8u et4k_ver_overflow =
((modeData->ver_overflow & 0x01) << 1) | // vtotal10
((modeData->ver_overflow & 0x02) << 1) | // vdispend10
((modeData->ver_overflow & 0x04) >> 2) | // vbstart10
((modeData->ver_overflow & 0x10) >> 1) | // vretrace10 (tseng has vsync start?)
((modeData->ver_overflow & 0x40) >> 2); // line_compare
IO_Write(crtc_base,0x35);IO_Write(crtc_base+1,et4k_ver_overflow);
// Clear remaining ext CRTC registers
IO_Write(crtc_base,0x31);IO_Write(crtc_base+1,0);
IO_Write(crtc_base,0x32);IO_Write(crtc_base+1,0);
IO_Write(crtc_base,0x33);IO_Write(crtc_base+1,0);
IO_Write(crtc_base,0x34);IO_Write(crtc_base+1,0);
IO_Write(crtc_base,0x36);IO_Write(crtc_base+1,0);
IO_Write(crtc_base,0x37);IO_Write(crtc_base+1,0x0c|(vga.vmemsize==1024*1024?3:vga.vmemsize==512*1024?2:1));
// Clear ext SEQ
IO_Write(0x3c4,0x06);IO_Write(0x3c5,0);
IO_Write(0x3c4,0x07);IO_Write(0x3c5,0);
// Clear ext ATTR
IO_Write(0x3c0,0x16);IO_Write(0x3c0,0);
IO_Write(0x3c0,0x17);IO_Write(0x3c0,0);
// Select SVGA clock to get close to 60Hz (not particularly clean implementation)
if (modeData->modeNo > 0x13) {
Bits target = static_cast<Bits>(modeData->vtotal * 8 * modeData->htotal * 60);
Bitu best = 1;
int dist = 100000000;
for (Bitu i = 0; i < 16; i++) {
int cdiff = abs( static_cast<int>(target - static_cast<Bits>(et4k.clockFreq[i])) );
if (cdiff < dist) {
best = i;
dist = cdiff;
}
}
set_clock_index_et4k(best);
}
if(svga.determine_mode)
svga.determine_mode();
// Verified (on real hardware and in a few games): Tseng ET4000 used chain4 implementation
// different from standard VGA. It was also not limited to 64K in regular mode 13h.
vga.config.compatible_chain4 = false;
vga.vmemwrap = vga.vmemsize;
VGA_SetupHandlers();
}
void DetermineMode_ET4K() {
// Close replica from the base implementation. It will stay here
// until I figure a way to either distinguish M_VGA and M_LIN8 or
// merge them.
if (vga.attr.mode_control & 1) {
if (vga.gfx.mode & 0x40) VGA_SetMode((et4k.biosMode<=0x13)?M_VGA:M_LIN8); // Ugly...
else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
else VGA_SetMode((et4k.biosMode<=0x13)?M_EGA:M_LIN4);
} else {
VGA_SetMode(M_TEXT);
}
}
void SetClock_ET4K(Bitu which,Bitu target) {
et4k.clockFreq[which]=1000*target;
VGA_StartResize();
}
Bitu GetClock_ET4K() {
return et4k.clockFreq[get_clock_index_et4k()];
}
bool AcceptsMode_ET4K(Bitu mode) {
return VideoModeMemSize(mode) < vga.vmemsize;
// return mode != 0x3d;
}
void SVGA_Setup_TsengET4K(void) {
svga.write_p3d5 = &write_p3d5_et4k;
svga.read_p3d5 = &read_p3d5_et4k;
svga.write_p3c5 = &write_p3c5_et4k;
svga.read_p3c5 = &read_p3c5_et4k;
svga.write_p3c0 = &write_p3c0_et4k;
svga.read_p3c1 = &read_p3c1_et4k;
svga.set_video_mode = &FinishSetMode_ET4K;
svga.determine_mode = &DetermineMode_ET4K;
svga.set_clock = &SetClock_ET4K;
svga.get_clock = &GetClock_ET4K;
svga.accepts_mode = &AcceptsMode_ET4K;
// From the depths of X86Config, probably inexact
VGA_SetClock(0,CLK_25);
VGA_SetClock(1,CLK_28);
VGA_SetClock(2,32400);
VGA_SetClock(3,35900);
VGA_SetClock(4,39900);
VGA_SetClock(5,44700);
VGA_SetClock(6,31400);
VGA_SetClock(7,37500);
VGA_SetClock(8,50000);
VGA_SetClock(9,56500);
VGA_SetClock(10,64900);
VGA_SetClock(11,71900);
VGA_SetClock(12,79900);
VGA_SetClock(13,89600);
VGA_SetClock(14,62800);
VGA_SetClock(15,74800);
IO_RegisterReadHandler(0x3cd,read_p3cd_et4k,IO_MB);
IO_RegisterWriteHandler(0x3cd,write_p3cd_et4k,IO_MB);
// Default to 1M of VRAM
if (vga.vmemsize == 0)
vga.vmemsize = 1024*1024;
if (vga.vmemsize < 512*1024)
vga.vmemsize = 256*1024;
else if (vga.vmemsize < 1024*1024)
vga.vmemsize = 512*1024;
else
vga.vmemsize = 1024*1024;
// Tseng ROM signature
PhysPt rom_base=PhysMake(0xc000,0);
phys_writeb(rom_base+0x0075,' ');
phys_writeb(rom_base+0x0076,'T');
phys_writeb(rom_base+0x0077,'s');
phys_writeb(rom_base+0x0078,'e');
phys_writeb(rom_base+0x0079,'n');
phys_writeb(rom_base+0x007a,'g');
phys_writeb(rom_base+0x007b,' ');
}
// Tseng ET3K implementation
typedef struct {
// Stored exact values of some registers. Documentation only specifies some bits but hardware checks may
// expect other bits to be preserved.
Bitu store_3d4_1b;
Bitu store_3d4_1c;
Bitu store_3d4_1d;
Bitu store_3d4_1e;
Bitu store_3d4_1f;
Bitu store_3d4_20;
Bitu store_3d4_21;
Bitu store_3d4_23; // note that 22 is missing
Bitu store_3d4_24;
Bitu store_3d4_25;
Bitu store_3c0_16;
Bitu store_3c0_17;
Bitu store_3c4_06;
Bitu store_3c4_07;
Bitu clockFreq[8];
Bitu biosMode;
} SVGA_ET3K_DATA;
static SVGA_ET3K_DATA et3k = { 0,0,0,0,0,0,0,0,0,0, 0,0, 0,0, {0,0,0,0,0,0,0,0}, 0 };
#define STORE_ET3K(port, index) \
case 0x##index: \
et3k.store_##port##_##index = val; \
break;
#define RESTORE_ET3K(port, index) \
case 0x##index: \
return et3k.store_##port##_##index;
void write_p3d5_et3k(Bitu reg,Bitu val,Bitu iolen) {
switch(reg) {
// 3d4 index 1bh-21h: Hardware zoom control registers
// I am not sure if there was a piece of software that used these.
// Not implemented and will probably stay this way.
STORE_ET3K(3d4, 1b);
STORE_ET3K(3d4, 1c);
STORE_ET3K(3d4, 1d);
STORE_ET3K(3d4, 1e);
STORE_ET3K(3d4, 1f);
STORE_ET3K(3d4, 20);
STORE_ET3K(3d4, 21);
case 0x23:
/*
3d4h index 23h (R/W): Extended start ET3000
bit 0 Cursor start address bit 16
1 Display start address bit 16
2 Zoom start address bit 16
7 If set memory address 8 is output on the MBSL pin (allowing access to
1MB), if clear the blanking signal is output.
*/
// Only bits 1 and 2 are supported. Bit 2 is related to hardware zoom, bit 7 is too obscure to be useful
et3k.store_3d4_23 = val;
vga.config.display_start = (vga.config.display_start & 0xffff) | ((val & 0x02)<<15);
vga.config.cursor_start = (vga.config.cursor_start & 0xffff) | ((val & 0x01)<<16);
break;
/*
3d4h index 24h (R/W): Compatibility Control
bit 0 Enable Clock Translate if set
1 Clock Select bit 2. Bits 0-1 are in 3C2h/3CCh.
2 Enable tri-state for all output pins if set
3 Enable input A8 of 1MB DRAMs from the INTL output if set
4 Reserved
5 Enable external ROM CRTC translation if set
6 Enable Double Scan and Underline Attribute if set
7 Enable 6845 compatibility if set.
*/
// TODO: Some of these may be worth implementing.
STORE_ET3K(3d4, 24);
case 0x25:
/*
3d4h index 25h (R/W): Overflow High
bit 0 Vertical Blank Start bit 10
1 Vertical Total Start bit 10
2 Vertical Display End bit 10
3 Vertical Sync Start bit 10
4 Line Compare bit 10
5-6 Reserved
7 Vertical Interlace if set
*/
et3k.store_3d4_25 = val;
vga.config.line_compare = (vga.config.line_compare & 0x3ff) | ((val&0x10)<<6);
// Abusing s3 ex_ver_overflow field. This is to be cleaned up later.
{
Bit8u s3val =
((val & 0x01) << 2) | // vbstart
((val & 0x02) >> 1) | // vtotal
((val & 0x04) >> 1) | // vdispend
((val & 0x08) << 1) | // vsyncstart (?)
((val & 0x10) << 2); // linecomp
if ((s3val ^ vga.s3.ex_ver_overflow) & 0x3) {
vga.s3.ex_ver_overflow=s3val;
VGA_StartResize();
} else vga.s3.ex_ver_overflow=s3val;
}
break;
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET3K:Write to illegal index %2X", reg);
break;
}
}
Bitu read_p3d5_et3k(Bitu reg,Bitu iolen) {
switch(reg) {
RESTORE_ET3K(3d4, 1b);
RESTORE_ET3K(3d4, 1c);
RESTORE_ET3K(3d4, 1d);
RESTORE_ET3K(3d4, 1e);
RESTORE_ET3K(3d4, 1f);
RESTORE_ET3K(3d4, 20);
RESTORE_ET3K(3d4, 21);
RESTORE_ET3K(3d4, 23);
RESTORE_ET3K(3d4, 24);
RESTORE_ET3K(3d4, 25);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:CRTC:ET3K:Read from illegal index %2X", reg);
break;
}
return 0x0;
}
void write_p3c5_et3k(Bitu reg,Bitu val,Bitu iolen) {
switch(reg) {
// Both registers deal mostly with hardware zoom which is not implemented. Other bits
// seem to be useless for emulation with the exception of index 7 bit 4 (font select)
STORE_ET3K(3c4, 06);
STORE_ET3K(3c4, 07);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET3K:Write to illegal index %2X", reg);
break;
}
}
Bitu read_p3c5_et3k(Bitu reg,Bitu iolen) {
switch(reg) {
RESTORE_ET3K(3c4, 06);
RESTORE_ET3K(3c4, 07);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:SEQ:ET3K:Read from illegal index %2X", reg);
break;
}
return 0x0;
}
/*
3CDh (R/W): Segment Select
bit 0-2 64k Write bank number
3-5 64k Read bank number
6-7 Segment Configuration.
0 128K segments
1 64K segments
2 1M linear memory
NOTES: 1M linear memory is not supported
*/
void write_p3cd_et3k(Bitu port,Bitu val,Bitu iolen) {
vga.svga.bank_write = val & 0x07;
vga.svga.bank_read = (val>>3) & 0x07;
vga.svga.bank_size = (val&0x40)?64*1024:128*1024;
VGA_SetupHandlers();
}
Bitu read_p3cd_et3k(Bitu port,Bitu iolen) {
return (vga.svga.bank_read<<3)|vga.svga.bank_write|((vga.svga.bank_size==128*1024)?0:0x40);
}
void write_p3c0_et3k(Bitu reg,Bitu val,Bitu iolen) {
// See ET4K notes.
switch(reg) {
STORE_ET3K(3c0, 16);
STORE_ET3K(3c0, 17);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET3K:Write to illegal index %2X", reg);
break;
}
}
Bitu read_p3c1_et3k(Bitu reg,Bitu iolen) {
switch(reg) {
RESTORE_ET3K(3c0, 16);
RESTORE_ET3K(3c0, 17);
default:
LOG(LOG_VGAMISC,LOG_NORMAL)("VGA:ATTR:ET3K:Read from illegal index %2X", reg);
break;
}
return 0x0;
}
/*
These ports are used but have little if any effect on emulation:
3B8h (W): Display Mode Control Register
3BFh (R/W): Hercules Compatibility Mode
3CBh (R/W): PEL Address/Data Wd
3CEh index 0Dh (R/W): Microsequencer Mode
3CEh index 0Eh (R/W): Microsequencer Reset
3d8h (R/W): Display Mode Control
3D9h (W): Color Select Register
3dAh (W): Feature Control Register
3DEh (W); AT&T Mode Control Register
*/
static Bitu get_clock_index_et3k() {
return ((vga.misc_output>>2)&3) | ((et3k.store_3d4_24<<1)&4);
}
static void set_clock_index_et3k(Bitu index) {
// Shortwiring register reads/writes for simplicity
IO_Write(0x3c2, (vga.misc_output&~0x0c)|((index&3)<<2));
et3k.store_3d4_24 = (et3k.store_3d4_24&~0x02)|((index&4)>>1);
}
void FinishSetMode_ET3K(Bitu crtc_base, VGA_ModeExtraData* modeData) {
et3k.biosMode = modeData->modeNo;
IO_Write(0x3cd, 0x40); // both banks to 0, 64K bank size
// Tseng ET3K does not have horizontal overflow bits
// Reinterpret ver_overflow
Bit8u et4k_ver_overflow =
((modeData->ver_overflow & 0x01) << 1) | // vtotal10
((modeData->ver_overflow & 0x02) << 1) | // vdispend10
((modeData->ver_overflow & 0x04) >> 2) | // vbstart10
((modeData->ver_overflow & 0x10) >> 1) | // vretrace10 (tseng has vsync start?)
((modeData->ver_overflow & 0x40) >> 2); // line_compare
IO_Write(crtc_base,0x25);IO_Write(crtc_base+1,et4k_ver_overflow);
// Clear remaining ext CRTC registers
for (Bitu i=0x16; i<=0x21; i++) {
IO_Write(crtc_base,i);
IO_Write(crtc_base+1,0);
}
IO_Write(crtc_base,0x23);IO_Write(crtc_base+1,0);
IO_Write(crtc_base,0x24);IO_Write(crtc_base+1,0);
// Clear ext SEQ
IO_Write(0x3c4,0x06);IO_Write(0x3c5,0);
IO_Write(0x3c4,0x07);IO_Write(0x3c5,0x40); // 0 in this register breaks WHATVGA
// Clear ext ATTR
IO_Write(0x3c0,0x16);IO_Write(0x3c0,0);
IO_Write(0x3c0,0x17);IO_Write(0x3c0,0);
// Select SVGA clock to get close to 60Hz (not particularly clean implementation)
if (modeData->modeNo > 0x13) {
Bits target = static_cast<Bits>(modeData->vtotal * 8 * modeData->htotal * 60);
Bitu best = 1;
int dist = 100000000;
for (Bitu i = 0; i < 8; i++) {
int cdiff = abs( static_cast<Bit32s>(target - static_cast<Bits>(et3k.clockFreq[i])) );
if (cdiff < dist) {
best = i;
dist = cdiff;
}
}
set_clock_index_et3k(best);
}
if (svga.determine_mode)
svga.determine_mode();
// Verified on functioning (at last!) hardware: Tseng ET3000 is the same as ET4000 when
// it comes to chain4 architecture
vga.config.compatible_chain4 = false;
vga.vmemwrap = vga.vmemsize;
VGA_SetupHandlers();
}
void DetermineMode_ET3K() {
// Close replica from the base implementation. It will stay here
// until I figure a way to either distinguish M_VGA and M_LIN8 or
// merge them.
if (vga.attr.mode_control & 1) {
if (vga.gfx.mode & 0x40) VGA_SetMode((et3k.biosMode<=0x13)?M_VGA:M_LIN8); // Ugly...
else if (vga.gfx.mode & 0x20) VGA_SetMode(M_CGA4);
else if ((vga.gfx.miscellaneous & 0x0c)==0x0c) VGA_SetMode(M_CGA2);
else VGA_SetMode((et3k.biosMode<=0x13)?M_EGA:M_LIN4);
} else {
VGA_SetMode(M_TEXT);
}
}
void SetClock_ET3K(Bitu which,Bitu target) {
et3k.clockFreq[which]=1000*target;
VGA_StartResize();
}
Bitu GetClock_ET3K() {
return et3k.clockFreq[get_clock_index_et3k()];
}
bool AcceptsMode_ET3K(Bitu mode) {
return mode <= 0x37 && mode != 0x2f && VideoModeMemSize(mode) < vga.vmemsize;
}
void SVGA_Setup_TsengET3K(void) {
svga.write_p3d5 = &write_p3d5_et3k;
svga.read_p3d5 = &read_p3d5_et3k;
svga.write_p3c5 = &write_p3c5_et3k;
svga.read_p3c5 = &read_p3c5_et3k;
svga.write_p3c0 = &write_p3c0_et3k;
svga.read_p3c1 = &read_p3c1_et3k;
svga.set_video_mode = &FinishSetMode_ET3K;
svga.determine_mode = &DetermineMode_ET3K;
svga.set_clock = &SetClock_ET3K;
svga.get_clock = &GetClock_ET3K;
svga.accepts_mode = &AcceptsMode_ET3K;
VGA_SetClock(0,CLK_25);
VGA_SetClock(1,CLK_28);
VGA_SetClock(2,32400);
VGA_SetClock(3,35900);
VGA_SetClock(4,39900);
VGA_SetClock(5,44700);
VGA_SetClock(6,31400);
VGA_SetClock(7,37500);
IO_RegisterReadHandler(0x3cd,read_p3cd_et3k,IO_MB);
IO_RegisterWriteHandler(0x3cd,write_p3cd_et3k,IO_MB);
vga.vmemsize = 512*1024; // Cannot figure how this was supposed to work on the real card
// Tseng ROM signature
PhysPt rom_base=PhysMake(0xc000,0);
phys_writeb(rom_base+0x0075,' ');
phys_writeb(rom_base+0x0076,'T');
phys_writeb(rom_base+0x0077,'s');
phys_writeb(rom_base+0x0078,'e');
phys_writeb(rom_base+0x0079,'n');
phys_writeb(rom_base+0x007a,'g');
phys_writeb(rom_base+0x007b,' ');
}