-CGA, PCJr, Tandy: Add video blanking, change display start latch timing, sync pulse width correction,
-PCJr, Tandy: implement vertical retrace interrupt, -PCJr, CGA: do line-by-line video emulation, -PCJr: support on-screen change of color modes 4medium to 16low (used by Ghostbusters booter), -All machines: only update the video timing when needed (Jungle Hunt, others that synchronize to the video screen might profit), only resize the output window when needed, start up the video more quickly at the beginning. Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@3572
This commit is contained in:
parent
b055c7a87f
commit
8912e7ab6f
5 changed files with 137 additions and 76 deletions
|
@ -111,6 +111,12 @@ typedef struct {
|
|||
Bit32u full_enable_and_set_reset;
|
||||
} VGA_Config;
|
||||
|
||||
typedef enum {
|
||||
PART,
|
||||
LINE,
|
||||
//EGALINE
|
||||
} Drawmode;
|
||||
|
||||
typedef struct {
|
||||
bool resizing;
|
||||
Bitu width;
|
||||
|
@ -144,6 +150,7 @@ typedef struct {
|
|||
double hdend, htotal;
|
||||
double parts;
|
||||
} delay;
|
||||
Bitu bpp;
|
||||
double aspect_ratio;
|
||||
bool double_scan;
|
||||
bool doublewidth,doubleheight;
|
||||
|
@ -156,6 +163,7 @@ typedef struct {
|
|||
Bit8u count,delay;
|
||||
Bit8u enabled;
|
||||
} cursor;
|
||||
Drawmode mode;
|
||||
bool vret_triggered;
|
||||
} VGA_Draw;
|
||||
|
||||
|
@ -216,7 +224,7 @@ typedef struct {
|
|||
Bit8u htotal;
|
||||
Bit8u hdend;
|
||||
Bit8u hsyncp;
|
||||
Bit8u syncw;
|
||||
Bit8u hsyncw;
|
||||
Bit8u vtotal;
|
||||
Bit8u vdend;
|
||||
Bit8u vadjust;
|
||||
|
@ -372,8 +380,6 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
VGAModes mode; /* The mode the vga system is in */
|
||||
VGAModes lastmode;
|
||||
Bits screenflip;
|
||||
Bit8u misc_output;
|
||||
VGA_Draw draw;
|
||||
VGA_Config config;
|
||||
|
|
|
@ -41,6 +41,12 @@ Bit32u Expand16Table[4][16];
|
|||
Bit32u FillTable[16];
|
||||
Bit32u ColorTable[16];
|
||||
|
||||
void VGA_SetModeNow(VGAModes mode) {
|
||||
if (vga.mode == mode) return;
|
||||
vga.mode=mode;
|
||||
VGA_SetupHandlers();
|
||||
VGA_StartResize(0);
|
||||
}
|
||||
|
||||
|
||||
void VGA_SetMode(VGAModes mode) {
|
||||
|
@ -85,8 +91,10 @@ void VGA_DetermineMode(void) {
|
|||
void VGA_StartResize(Bitu delay /*=50*/) {
|
||||
if (!vga.draw.resizing) {
|
||||
vga.draw.resizing=true;
|
||||
if (vga.mode==M_ERROR) delay = 5;
|
||||
/* Start a resize after delay (default 50 ms) */
|
||||
PIC_AddEvent(VGA_SetupDrawing,(float)delay);
|
||||
if (delay==0) VGA_SetupDrawing(0);
|
||||
else PIC_AddEvent(VGA_SetupDrawing,(float)delay);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,8 +175,6 @@ void VGA_SetCGA4Table(Bit8u val0,Bit8u val1,Bit8u val2,Bit8u val3) {
|
|||
|
||||
void VGA_Init(Section* sec) {
|
||||
// Section_prop * section=static_cast<Section_prop *>(sec);
|
||||
// vga.screenflip = section->Get_int("screenflip");
|
||||
vga.screenflip = 0;
|
||||
vga.draw.resizing=false;
|
||||
vga.mode=M_ERROR; //For first init
|
||||
SVGA_Setup_Driver();
|
||||
|
|
|
@ -734,6 +734,11 @@ static void VGA_VertInterrupt(Bitu /*val*/) {
|
|||
}
|
||||
}
|
||||
|
||||
static void VGA_Other_VertInterrupt(Bitu val) {
|
||||
if (val) PIC_ActivateIRQ(5);
|
||||
else PIC_DeActivateIRQ(5);
|
||||
}
|
||||
|
||||
static void VGA_DisplayStartLatch(Bitu /*val*/) {
|
||||
vga.config.real_start=vga.config.display_start & (vga.vmemwrap-1);
|
||||
vga.draw.bytes_skip = vga.config.bytes_skip;
|
||||
|
@ -744,43 +749,40 @@ static void VGA_PanningLatch(Bitu /*val*/) {
|
|||
}
|
||||
|
||||
static void VGA_VerticalTimer(Bitu /*val*/) {
|
||||
double error = vga.draw.delay.framestart;
|
||||
vga.draw.delay.framestart = PIC_FullIndex();
|
||||
error = vga.draw.delay.framestart - error - vga.draw.delay.vtotal;
|
||||
PIC_AddEvent( VGA_VerticalTimer, (float)vga.draw.delay.vtotal );
|
||||
//PIC_AddEvent( VGA_VerticalDisplayEnd, (float)vga.draw.delay.vrstart );
|
||||
double flip_offset = vga.screenflip/1000.0 + vga.draw.delay.vrstart;
|
||||
if(flip_offset > vga.draw.delay.vtotal) {
|
||||
|
||||
switch(machine) {
|
||||
case MCH_PCJR:
|
||||
case MCH_TANDY:
|
||||
// PCJr: Vsync is directly connected to the IRQ controller
|
||||
// Some earlier Tandy models are said to have a vsync interrupt too
|
||||
PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrstart, 1);
|
||||
PIC_AddEvent(VGA_Other_VertInterrupt, (float)vga.draw.delay.vrend, 0);
|
||||
// fall-through
|
||||
case MCH_CGA:
|
||||
case MCH_HERC:
|
||||
// MC6845-powered graphics: Loading the display start latch happens somewhere
|
||||
// after vsync off and before first visible scanline, so probably here
|
||||
VGA_DisplayStartLatch(0);
|
||||
} else PIC_AddEvent( VGA_DisplayStartLatch,(float)flip_offset);
|
||||
PIC_AddEvent(VGA_PanningLatch,(float)vga.draw.delay.vrend);
|
||||
|
||||
// EGA: 82c435 datasheet: interrupt happens at display end
|
||||
// VGA: checked with scope
|
||||
// add a little amount of time to make sure the last drawpart has already fired
|
||||
if (IS_EGAVGA_ARCH) PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
|
||||
|
||||
if ( GCC_UNLIKELY( vga.draw.parts_left)) {
|
||||
if (!IS_VGA_ARCH || (svgaCard!=SVGA_None)) {
|
||||
LOG(LOG_VGAMISC,LOG_NORMAL)( "Parts left: %d", vga.draw.parts_left );
|
||||
PIC_RemoveEvents( &VGA_DrawPart );
|
||||
RENDER_EndUpdate();
|
||||
vga.draw.parts_left = 0;
|
||||
}
|
||||
break;
|
||||
case MCH_VGA:
|
||||
case MCH_EGA:
|
||||
PIC_AddEvent(VGA_DisplayStartLatch, (float)vga.draw.delay.vrstart);
|
||||
PIC_AddEvent(VGA_PanningLatch, (float)vga.draw.delay.vrend);
|
||||
// EGA: 82c435 datasheet: interrupt happens at display end
|
||||
// VGA: checked with scope; however disabled by default by jumper on VGA boards
|
||||
// add a little amount of time to make sure the last drawpart has already fired
|
||||
PIC_AddEvent(VGA_VertInterrupt,(float)(vga.draw.delay.vdend + 0.005));
|
||||
break;
|
||||
default:
|
||||
E_Exit("This new machine needs implementation in VGA_VerticalTimer too.");
|
||||
break;
|
||||
}
|
||||
//Check if we can actually render, else skip the rest
|
||||
//Check if we can actually render, else skip the rest (frameskip)
|
||||
if (!RENDER_StartUpdate())
|
||||
return;
|
||||
if ( GCC_UNLIKELY( vga.draw.lines_done < vga.draw.lines_total)) {
|
||||
if (IS_VGA_ARCH && (svgaCard==SVGA_None)) {
|
||||
while(vga.draw.lines_done < vga.draw.lines_total)
|
||||
VGA_DrawSingleLine(0);
|
||||
PIC_RemoveEvents(VGA_DrawSingleLine);
|
||||
}
|
||||
}
|
||||
//TODO Maybe check for an active frame on parts_left and clear that first?
|
||||
vga.draw.parts_left = vga.draw.parts_total;
|
||||
vga.draw.lines_done = 0;
|
||||
|
||||
vga.draw.address_line = vga.config.hlines_skip;
|
||||
if (IS_EGAVGA_ARCH) {
|
||||
vga.draw.split_line = (Bitu)((vga.config.line_compare+1)/vga.draw.lines_scaled);
|
||||
|
@ -862,17 +864,38 @@ static void VGA_VerticalTimer(Bitu /*val*/) {
|
|||
#ifdef VGA_KEEP_CHANGES
|
||||
if (startaddr_changed) VGA_ChangesStart();
|
||||
#endif
|
||||
|
||||
// check if some lines at the top off the screen are blanked
|
||||
float draw_skip = 0.0;
|
||||
if (GCC_UNLIKELY(vga.draw.vblank_skip)) {
|
||||
draw_skip = (float)(vga.draw.delay.htotal * vga.draw.vblank_skip);
|
||||
vga.draw.address += vga.draw.address_add * (vga.draw.vblank_skip/(vga.draw.address_line_total));
|
||||
}
|
||||
|
||||
if ((IS_VGA_ARCH) && (svgaCard==SVGA_None)) PIC_AddEvent(VGA_DrawSingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
|
||||
else PIC_AddEvent(VGA_DrawPart,(float)vga.draw.delay.parts + draw_skip,vga.draw.parts_lines);
|
||||
//VGA_DrawPart( vga.draw.parts_lines );
|
||||
//PIC_AddEvent(VGA_DrawPart,(float)vga.draw.delay.parts,vga.draw.parts_lines);
|
||||
//PIC_AddEvent(VGA_DrawPart,(float)(vga.draw.delay.parts/2),vga.draw.parts_lines); //Else tearline in Tyrian and second reality
|
||||
// add the draw event
|
||||
switch (vga.draw.mode) {
|
||||
case PART:
|
||||
if (GCC_UNLIKELY(vga.draw.parts_left)) {
|
||||
LOG(LOG_VGAMISC,LOG_NORMAL)( "Parts left: %d", vga.draw.parts_left );
|
||||
PIC_RemoveEvents(VGA_DrawPart);
|
||||
RENDER_EndUpdate();
|
||||
}
|
||||
vga.draw.lines_done = 0;
|
||||
vga.draw.parts_left = vga.draw.parts_total;
|
||||
PIC_AddEvent(VGA_DrawPart,(float)vga.draw.delay.parts + draw_skip,vga.draw.parts_lines);
|
||||
break;
|
||||
case LINE:
|
||||
if (GCC_UNLIKELY(vga.draw.lines_done < vga.draw.lines_total)) {
|
||||
LOG(LOG_VGAMISC,LOG_NORMAL)( "Lines left: %d",
|
||||
vga.draw.lines_total-vga.draw.lines_done);
|
||||
PIC_RemoveEvents(VGA_DrawSingleLine);
|
||||
RENDER_EndUpdate();
|
||||
}
|
||||
vga.draw.lines_done = 0;
|
||||
PIC_AddEvent(VGA_DrawSingleLine,(float)(vga.draw.delay.htotal/4.0 + draw_skip));
|
||||
break;
|
||||
//case EGALINE:
|
||||
}
|
||||
}
|
||||
|
||||
void VGA_CheckScanLength(void) {
|
||||
|
@ -949,8 +972,25 @@ void VGA_SetupDrawing(Bitu /*val*/) {
|
|||
PIC_RemoveEvents(VGA_DisplayStartLatch);
|
||||
return;
|
||||
}
|
||||
// set the drawing mode
|
||||
switch (machine) {
|
||||
case MCH_CGA:
|
||||
case MCH_PCJR:
|
||||
vga.draw.mode = LINE;
|
||||
break;
|
||||
case MCH_VGA:
|
||||
if (svgaCard==SVGA_None) {
|
||||
vga.draw.mode = LINE;
|
||||
break;
|
||||
}
|
||||
// fall-through
|
||||
default:
|
||||
vga.draw.mode = PART;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate the FPS for this screen */
|
||||
float fps; Bitu clock;
|
||||
double fps; Bitu clock;
|
||||
Bitu htotal, hdend, hbstart, hbend, hrstart, hrend;
|
||||
Bitu vtotal, vdend, vbstart, vbend, vrstart, vrend;
|
||||
Bitu vblank_skip;
|
||||
|
@ -1050,17 +1090,13 @@ void VGA_SetupDrawing(Bitu /*val*/) {
|
|||
hbstart = hdend;
|
||||
hbend = htotal;
|
||||
hrstart = vga.other.hsyncp;
|
||||
hrend = hrstart + (vga.other.syncw & 0xf) ;
|
||||
hrend = hrstart + vga.other.hsyncw;
|
||||
|
||||
vga.draw.address_line_total = vga.other.max_scanline + 1;
|
||||
vtotal = vga.draw.address_line_total * (vga.other.vtotal+1)+vga.other.vadjust;
|
||||
vdend = vga.draw.address_line_total * vga.other.vdend;
|
||||
vrstart = vga.draw.address_line_total * vga.other.vsyncp;
|
||||
vrend = (vga.other.syncw >> 4);
|
||||
if (!vrend)
|
||||
vrend = vrstart + 0xf + 1;
|
||||
else
|
||||
vrend = vrstart + vrend;
|
||||
vrend = vrstart + 16; // vsync width is fixed to 16 lines on the MC6845 TODO Tandy
|
||||
vbstart = vdend;
|
||||
vbend = vtotal;
|
||||
vga.draw.double_scan=false;
|
||||
|
@ -1088,9 +1124,8 @@ void VGA_SetupDrawing(Bitu /*val*/) {
|
|||
if (!htotal) return;
|
||||
if (!vtotal) return;
|
||||
|
||||
fps=(float)clock/(vtotal*htotal);
|
||||
// The time a complete video frame takes
|
||||
vga.draw.delay.vtotal = (1000.0 * (double)(vtotal*htotal)) / (double)clock;
|
||||
// The screen refresh frequency
|
||||
fps=(double)clock/(vtotal*htotal);
|
||||
// Horizontal total (that's how long a line takes with whistles and bells)
|
||||
vga.draw.delay.htotal = htotal*1000.0/clock; //in milliseconds
|
||||
// Start and End of horizontal blanking
|
||||
|
@ -1411,21 +1446,34 @@ void VGA_SetupDrawing(Bitu /*val*/) {
|
|||
}
|
||||
// LOG_MSG("ht %d vt %d ratio %f", htotal, vtotal, aspect_ratio );
|
||||
|
||||
if (( width != vga.draw.width) || (height != vga.draw.height) ||
|
||||
(aspect_ratio != vga.draw.aspect_ratio) ||
|
||||
(vga.mode != vga.lastmode)) {
|
||||
vga.lastmode = vga.mode;
|
||||
// need to change the vertical timing?
|
||||
if (fabs(vga.draw.delay.vtotal - 1000.0 / fps) > 0.0001) {
|
||||
vga.draw.delay.vtotal = 1000.0 / fps;
|
||||
VGA_KillDrawing();
|
||||
PIC_RemoveEvents(VGA_Other_VertInterrupt);
|
||||
PIC_RemoveEvents(VGA_VerticalTimer);
|
||||
PIC_RemoveEvents(VGA_PanningLatch);
|
||||
PIC_RemoveEvents(VGA_DisplayStartLatch);
|
||||
PIC_RemoveEvents(VGA_DrawPart);
|
||||
PIC_RemoveEvents(VGA_DrawSingleLine);
|
||||
VGA_VerticalTimer(0);
|
||||
}
|
||||
|
||||
// need to resize the output window?
|
||||
if ((width != vga.draw.width) ||
|
||||
(height != vga.draw.height) ||
|
||||
(vga.draw.doublewidth != doublewidth) ||
|
||||
(vga.draw.doubleheight != doubleheight) ||
|
||||
(abs(aspect_ratio - vga.draw.aspect_ratio) > 0.0001) ||
|
||||
(vga.draw.bpp != bpp)) {
|
||||
|
||||
VGA_KillDrawing();
|
||||
|
||||
vga.draw.width = width;
|
||||
vga.draw.height = height;
|
||||
vga.draw.doublewidth = doublewidth;
|
||||
vga.draw.doubleheight = doubleheight;
|
||||
vga.draw.aspect_ratio = aspect_ratio;
|
||||
vga.draw.vblank_skip = vblank_skip;
|
||||
vga.draw.bpp = bpp;
|
||||
if (doubleheight) vga.draw.lines_scaled=2;
|
||||
else vga.draw.lines_scaled=1;
|
||||
#if C_DEBUG
|
||||
|
@ -1433,14 +1481,14 @@ void VGA_SetupDrawing(Bitu /*val*/) {
|
|||
LOG(LOG_VGA,LOG_NORMAL)("%s width, %s height aspect %f",
|
||||
doublewidth ? "double":"normal",doubleheight ? "double":"normal",aspect_ratio);
|
||||
#endif
|
||||
RENDER_SetSize(width,height,bpp,fps,aspect_ratio,doublewidth,doubleheight);
|
||||
vga.draw.delay.framestart = PIC_FullIndex();
|
||||
PIC_AddEvent( VGA_VerticalTimer , (float)vga.draw.delay.vtotal );
|
||||
vga.draw.lines_done = 0;
|
||||
RENDER_SetSize(width,height,bpp,(float)fps,aspect_ratio,doublewidth,doubleheight);
|
||||
}
|
||||
}
|
||||
|
||||
void VGA_KillDrawing(void) {
|
||||
PIC_RemoveEvents(VGA_DrawPart);
|
||||
PIC_RemoveEvents(VGA_DrawSingleLine);
|
||||
vga.draw.parts_left = 0;
|
||||
vga.draw.lines_done = ~0;
|
||||
RENDER_EndUpdate();
|
||||
}
|
||||
|
|
|
@ -49,8 +49,10 @@ static void write_crtc_data_other(Bitu /*port*/,Bitu val,Bitu /*iolen*/) {
|
|||
case 0x02: //Horizontal sync position
|
||||
vga.other.hsyncp=(Bit8u)val;
|
||||
break;
|
||||
case 0x03: //Horizontal and vertical sync width
|
||||
vga.other.syncw=(Bit8u)val;
|
||||
case 0x03: //Horizontal sync width
|
||||
if (machine==MCH_TANDY) vga.other.vsyncw=(Bit8u)(val >> 4);
|
||||
else vga.other.vsyncw = 16; // The MC6845 has a fixed v-sync width of 16 lines
|
||||
vga.other.hsyncw=(Bit8u)(val & 0xf);
|
||||
break;
|
||||
case 0x04: //Vertical total
|
||||
if (vga.other.vtotal ^ val) VGA_StartResize();
|
||||
|
@ -116,7 +118,9 @@ static Bitu read_crtc_data_other(Bitu /*port*/,Bitu /*iolen*/) {
|
|||
case 0x02: //Horizontal sync position
|
||||
return vga.other.hsyncp;
|
||||
case 0x03: //Horizontal and vertical sync width
|
||||
return vga.other.syncw;
|
||||
if (machine==MCH_TANDY)
|
||||
return vga.other.hsyncw | (vga.other.vsyncw << 4);
|
||||
else return vga.other.hsyncw;
|
||||
case 0x04: //Vertical total
|
||||
return vga.other.vtotal;
|
||||
case 0x05: //Vertical display adjust
|
||||
|
@ -278,17 +282,21 @@ static void TANDY_FindMode(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void VGA_SetModeNow(VGAModes mode);
|
||||
|
||||
static void PCJr_FindMode(void) {
|
||||
if (vga.tandy.mode_control & 0x2) {
|
||||
if (vga.tandy.mode_control & 0x10) {
|
||||
/* bit4 of mode control 1 signals 16 colour graphics mode */
|
||||
VGA_SetMode(M_TANDY16);
|
||||
if (vga.mode==M_TANDY4) VGA_SetModeNow(M_TANDY16); // TODO lowres mode only
|
||||
else VGA_SetMode(M_TANDY16);
|
||||
} else if (vga.tandy.gfx_control & 0x08) {
|
||||
/* bit3 of mode control 2 signals 2 colour graphics mode */
|
||||
VGA_SetMode(M_TANDY2);
|
||||
} else {
|
||||
/* otherwise some 4-colour graphics mode */
|
||||
VGA_SetMode(M_TANDY4);
|
||||
if (vga.mode==M_TANDY16) VGA_SetModeNow(M_TANDY4);
|
||||
else VGA_SetMode(M_TANDY4);
|
||||
}
|
||||
write_color_select(vga.tandy.color_select);
|
||||
} else {
|
||||
|
@ -318,6 +326,7 @@ static void write_tandy_reg(Bit8u val) {
|
|||
vga.tandy.mode_control=val;
|
||||
VGA_SetBlinking(val & 0x20);
|
||||
PCJr_FindMode();
|
||||
vga.attr.disabled = (val&0x8)? 0: 1;
|
||||
} else {
|
||||
LOG(LOG_VGAMISC,LOG_NORMAL)("Unhandled Write %2X to tandy reg %X",val,vga.tandy.reg_index);
|
||||
}
|
||||
|
@ -359,6 +368,7 @@ static void write_cga(Bitu port,Bitu val,Bitu /*iolen*/) {
|
|||
switch (port) {
|
||||
case 0x3d8:
|
||||
vga.tandy.mode_control=(Bit8u)val;
|
||||
vga.attr.disabled = (val&0x8)? 0: 1;
|
||||
if (vga.tandy.mode_control & 0x2) {
|
||||
if (vga.tandy.mode_control & 0x10) {
|
||||
if (!(val & 0x4) && machine==MCH_CGA) {
|
||||
|
|
|
@ -826,13 +826,6 @@ void BIOS_ZeroExtendedSize(bool in) {
|
|||
if(other_memsystems < 0) other_memsystems=0;
|
||||
}
|
||||
|
||||
#define RAM_REFRESH_DELAY 16.7f
|
||||
|
||||
static void RAMRefresh_Event(Bitu /*val*/) {
|
||||
PIC_ActivateIRQ(5);
|
||||
PIC_AddEvent(RAMRefresh_Event,RAM_REFRESH_DELAY);
|
||||
}
|
||||
|
||||
void BIOS_SetupKeyboard(void);
|
||||
void BIOS_SetupDisks(void);
|
||||
|
||||
|
@ -1085,8 +1078,6 @@ public:
|
|||
size_extended=IO_Read(0x71);
|
||||
IO_Write(0x70,0x31);
|
||||
size_extended|=(IO_Read(0x71) << 8);
|
||||
|
||||
if (machine==MCH_PCJR) PIC_AddEvent(RAMRefresh_Event,RAM_REFRESH_DELAY);
|
||||
}
|
||||
~BIOS(){
|
||||
/* abort DAC playing */
|
||||
|
|
Loading…
Add table
Reference in a new issue