2009-05-03 01:02:35 +02:00
|
|
|
/*
|
2009-06-03 07:32:00 +02:00
|
|
|
* Copyright (C) 2002-2009 The DOSBox Team
|
2009-05-03 01:02:35 +02:00
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2009-06-03 07:32:00 +02:00
|
|
|
/* $Id: vga_tseng.cpp,v 1.5 2009/05/27 09:15:41 qbix79 Exp $ */
|
2009-05-03 01:02:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
#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) {
|
|
|
|
Bitu target = modeData->vtotal*8*modeData->htotal*60;
|
|
|
|
Bitu best = 1;
|
|
|
|
Bits dist = 100000000;
|
|
|
|
for (Bitu i=0; i<16; i++) {
|
|
|
|
if (abs(target-et4k.clockFreq[i]) < dist) {
|
|
|
|
best = i;
|
|
|
|
dist = abs(target-et4k.clockFreq[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
Bitu target = modeData->vtotal*8*modeData->htotal*60;
|
|
|
|
Bitu best = 1;
|
|
|
|
Bits dist = 100000000;
|
|
|
|
for (Bitu i=0; i<8; i++) {
|
|
|
|
if (abs(target-et3k.clockFreq[i]) < dist) {
|
|
|
|
best = i;
|
|
|
|
dist = abs(target-et3k.clockFreq[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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,' ');
|
|
|
|
}
|