mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-14 02:17:36 +08:00
Take VBE window and size options, apply to SVGA emulation. Update VBE BIOS to report overridden window size and granularity. Limit video memory to prevent granularity overflow when writing to S3 emulation. Hack S3 CRTC port 6Ah to accept a full 8-bit value if VBE granularity override and less than 64KB, otherwise retain 7-bit behavior. Update all VBE emulation to limit reported video memory based on maximum video memory possible given the granularity. It is now possible for retro developers to test their code against older SVGA hardware with odd bank size vs granularity configurations
This commit is contained in:
@@ -141,6 +141,7 @@
|
||||
#include "mem.h"
|
||||
#include "render.h"
|
||||
#include "jfont.h"
|
||||
#include "bitop.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -165,6 +166,9 @@ void VGA_CaptureStartNextFrame(void);
|
||||
void VGA_CaptureMarkError(void);
|
||||
bool VGA_CaptureValidateCurrentFrame(void);
|
||||
|
||||
unsigned int vbe_window_granularity = 0;
|
||||
unsigned int vbe_window_size = 0;
|
||||
|
||||
/* current dosplay page (controlled by A4h) */
|
||||
unsigned char* pc98_pgraph_current_display_page;
|
||||
/* current CPU page (controlled by A6h) */
|
||||
@@ -624,6 +628,7 @@ VGA_Vsync VGA_Vsync_Decode(const char *vsyncmodestr) {
|
||||
|
||||
bool has_pcibus_enable(void);
|
||||
uint32_t MEM_get_address_bits();
|
||||
uint32_t GetReportedVideoMemorySize(void);
|
||||
|
||||
void VGA_Reset(Section*) {
|
||||
// All non-PC98 video-related config settings are now in the [video] section
|
||||
@@ -918,6 +923,16 @@ void VGA_Reset(Section*) {
|
||||
|
||||
LOG(LOG_VGA,LOG_DEBUG)("VGA memory I/O delay %uns",vga_memio_delay_ns);
|
||||
|
||||
vbe_window_granularity = (unsigned int)section->Get_int("vbe window granularity") << 10u; /* KB to bytes */
|
||||
vbe_window_size = (unsigned int)section->Get_int("vbe window size") << 10u; /* KB to bytes */
|
||||
|
||||
if (vbe_window_granularity != 0 && !bitop::ispowerof2(vbe_window_granularity))
|
||||
LOG(LOG_VGA,LOG_WARN)("User specified VBE window granularity is not a power of 2. May break some programs.");
|
||||
if (vbe_window_size != 0 && !bitop::ispowerof2(vbe_window_size))
|
||||
LOG(LOG_VGA,LOG_WARN)("User specified VBE window size is not a power of 2. May break some programs.");
|
||||
if (vbe_window_size != 0 && vbe_window_granularity != 0 && vbe_window_size < vbe_window_granularity)
|
||||
LOG(LOG_VGA,LOG_WARN)("VBE window size is less than window granularity, which prevents full access to video memory and may break some programs.");
|
||||
|
||||
/* mainline compatible vmemsize (in MB)
|
||||
* plus vmemsizekb for KB-level control.
|
||||
* then we round up a page.
|
||||
@@ -1006,6 +1021,18 @@ void VGA_Reset(Section*) {
|
||||
if (IS_EGA_ARCH && vga.mem.memsize < _KB_bytes(128))
|
||||
LOG_MSG("WARNING: EGA 64KB emulation is very experimental and not well supported");
|
||||
|
||||
/* If video memory is limited by bank switching, then reduce video memory */
|
||||
if (vbe_window_granularity != 0 && !IS_PC98_ARCH) {
|
||||
const uint32_t sz = GetReportedVideoMemorySize();
|
||||
|
||||
LOG(LOG_VGA,LOG_NORMAL)("Video RAM size %uKB exceeds maximum possible %uKB given VBE window granularity, reducing",
|
||||
vga.mem.memsize>>10,(unsigned int)(sz>>10ul));
|
||||
|
||||
/* reduce by half, until video memory is reported or larger but not more than 2x */
|
||||
while (vga.mem.memsize > _KB_bytes(512) && (vga.mem.memsize/2ul) >= sz)
|
||||
vga.mem.memsize /= 2u;
|
||||
}
|
||||
|
||||
// prepare for transition
|
||||
if (want_fm_towns) {
|
||||
if (vga.mem.memsize < _KB_bytes(640)) vga.mem.memsize = _KB_bytes(640); /* "640KB of RAM, 512KB VRAM and 128KB sprite RAM" */
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include "pc98_cg.h"
|
||||
#include "pc98_gdc.h"
|
||||
#include "zipfile.h"
|
||||
#include "src/ints/int10.h"
|
||||
|
||||
unsigned char pc98_pegc_mmio[0x200] = {0}; /* PC-98 memory-mapped PEGC registers at E0000h */
|
||||
uint32_t pc98_pegc_banks[2] = {0x0000,0x0000}; /* bank switching offsets */
|
||||
@@ -44,6 +45,9 @@ extern bool vga_ignore_extended_memory_bit;
|
||||
extern bool enable_pc98_256color_planar;
|
||||
extern bool enable_pc98_256color;
|
||||
|
||||
extern unsigned int vbe_window_granularity;
|
||||
extern unsigned int vbe_window_size;
|
||||
|
||||
#ifndef C_VGARAM_CHECKED
|
||||
#define C_VGARAM_CHECKED 1
|
||||
#endif
|
||||
@@ -2192,6 +2196,11 @@ void MEM_ResetPageHandler_RAM(Bitu phys_page, Bitu pages);
|
||||
|
||||
extern void DISP2_SetPageHandler(void);
|
||||
void VGA_SetupHandlers(void) {
|
||||
/* This code inherited from DOSBox SVN confuses bank size with bank granularity.
|
||||
* This code is enforcing bank granularity. Bank size concerns how much memory is
|
||||
* mapped to A0000-BFFFF. Most cards expose a window of 64KB. Most cards also have
|
||||
* a bank granularity of 64KB, but some, like Paradise and Cirrus, have 64KB windows
|
||||
* and 4KB granularity. */
|
||||
vga.svga.bank_read_full = vga.svga.bank_read*vga.svga.bank_size;
|
||||
vga.svga.bank_write_full = vga.svga.bank_write*vga.svga.bank_size;
|
||||
|
||||
@@ -2375,7 +2384,19 @@ void VGA_SetupHandlers(void) {
|
||||
vgapages.mask = 0xffff & vga.mem.memmask;
|
||||
break;
|
||||
}
|
||||
MEM_SetPageHandler(VGA_PAGE_A0, 32, newHandler );
|
||||
if (CurMode && CurMode->mode >= 0x14/*VESA BIOS or extended mode*/ && vbe_window_size > 0/*user override of window size*/) {
|
||||
unsigned int pages = (vbe_window_size + 0xFFFu) >> 12u; /* bytes to pages, round up */
|
||||
if (pages > 32) pages = 32;
|
||||
assert(pages != 0u);
|
||||
|
||||
/* map only what the window size determines, make the rest empty */
|
||||
MEM_SetPageHandler(VGA_PAGE_A0, pages, newHandler );
|
||||
MEM_SetPageHandler(VGA_PAGE_A0 + pages, 32 - pages, &vgaph.empty );
|
||||
}
|
||||
else {
|
||||
/*full 128KB */
|
||||
MEM_SetPageHandler(VGA_PAGE_A0, 32, newHandler );
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
vgapages.base = VGA_PAGE_A0;
|
||||
@@ -2515,7 +2536,14 @@ void VGA_SetupMemory() {
|
||||
|
||||
vga.svga.bank_read = vga.svga.bank_write = 0;
|
||||
vga.svga.bank_read_full = vga.svga.bank_write_full = 0;
|
||||
vga.svga.bank_size = 0x10000; /* most common bank size is 64K */
|
||||
|
||||
/* obey user override for "bank size", which this code inherited from DOSBox SVN
|
||||
* confuses with "bank granularity". If "bank size" were truly a concern it would
|
||||
* affect how much of the A0000-BFFFF region VGA mapping would expose. */
|
||||
if (vbe_window_granularity > 0)
|
||||
vga.svga.bank_size = vbe_window_granularity; /* allow different sizes for dev testing */
|
||||
else
|
||||
vga.svga.bank_size = 0x10000; /* most common bank size is 64K */
|
||||
|
||||
if (!VGA_Memory_ShutDown_init) {
|
||||
AddExitFunction(AddExitFunctionFuncPair(VGA_Memory_ShutDown));
|
||||
|
@@ -24,6 +24,9 @@
|
||||
#include "inout.h"
|
||||
#include "mem.h"
|
||||
|
||||
extern unsigned int vbe_window_granularity;
|
||||
extern unsigned int vbe_window_size;
|
||||
|
||||
typedef struct SVGA_PVGA1A_DATA_t {
|
||||
Bitu PR0A;
|
||||
Bitu PR0B;
|
||||
@@ -54,7 +57,12 @@ static void bank_setup_pvga1a() {
|
||||
} else {
|
||||
// Single bank config is straightforward
|
||||
vga.svga.bank_read = vga.svga.bank_write = (uint8_t)pvga1a.PR0A;
|
||||
vga.svga.bank_size = 4*1024;
|
||||
|
||||
if (vbe_window_granularity > 0)
|
||||
vga.svga.bank_size = vbe_window_granularity; /* allow different sizes for dev testing */
|
||||
else
|
||||
vga.svga.bank_size = 4*1024;
|
||||
|
||||
VGA_SetupHandlers();
|
||||
}
|
||||
}
|
||||
|
@@ -29,6 +29,9 @@ bool has_pcibus_enable(void);
|
||||
|
||||
extern bool enable_pci_vga;
|
||||
|
||||
extern unsigned int vbe_window_granularity;
|
||||
extern unsigned int vbe_window_size;
|
||||
|
||||
void SVGA_S3_WriteCRTC(Bitu reg,Bitu val,Bitu iolen) {
|
||||
(void)iolen;//UNUSED
|
||||
switch (reg) {
|
||||
@@ -397,7 +400,16 @@ void SVGA_S3_WriteCRTC(Bitu reg,Bitu val,Bitu iolen) {
|
||||
}
|
||||
break;
|
||||
case 0x6a: /* Extended System Control 4 */
|
||||
vga.svga.bank_read=(uint8_t)val & 0x7f;
|
||||
/* S3 cards think only in 64KB bank granularity.
|
||||
* An option was added to emulate smaller amounts of granularity, but
|
||||
* then this 7-bit field causes problems. Since the smaller granularity
|
||||
* breaks accuracy anyway, go ahead and accept the full 8-bit value
|
||||
* in that case as a hack. */
|
||||
if (vbe_window_granularity == 0 || vbe_window_granularity >= (64*1024))
|
||||
vga.svga.bank_read=(uint8_t)val & 0x7f;
|
||||
else
|
||||
vga.svga.bank_read=(uint8_t)val & 0xff;
|
||||
|
||||
vga.svga.bank_write = vga.svga.bank_read;
|
||||
VGA_SetupHandlers();
|
||||
break;
|
||||
@@ -647,8 +659,10 @@ bool SVGA_S3_HWCursorActive(void) {
|
||||
return (vga.s3.hgc.curmode & 0x1) != 0;
|
||||
}
|
||||
|
||||
uint32_t GetReportedVideoMemorySize(void);
|
||||
|
||||
bool SVGA_S3_AcceptsMode(Bitu mode) {
|
||||
return VideoModeMemSize(mode) < vga.mem.memsize;
|
||||
return VideoModeMemSize(mode) < GetReportedVideoMemorySize();
|
||||
}
|
||||
|
||||
extern bool VGA_BIOS_use_rom;
|
||||
|
@@ -30,6 +30,9 @@
|
||||
extern bool vga_enable_3C6_ramdac;
|
||||
extern bool vga_sierra_lock_565;
|
||||
|
||||
extern unsigned int vbe_window_granularity;
|
||||
extern unsigned int vbe_window_size;
|
||||
|
||||
// Tseng ET4K data
|
||||
typedef struct {
|
||||
uint8_t extensionsEnabled;
|
||||
@@ -825,7 +828,12 @@ void write_p3cd_et3k(Bitu port,Bitu val,Bitu iolen) {
|
||||
(void)iolen;//UNUSED
|
||||
vga.svga.bank_write = val & 0x07;
|
||||
vga.svga.bank_read = (val>>3) & 0x07;
|
||||
vga.svga.bank_size = (val&0x40)?64*1024:128*1024;
|
||||
|
||||
if (vbe_window_granularity > 0)
|
||||
vga.svga.bank_size = vbe_window_granularity; /* allow different sizes for dev testing */
|
||||
else
|
||||
vga.svga.bank_size = (val&0x40)?64*1024:128*1024;
|
||||
|
||||
VGA_SetupHandlers();
|
||||
}
|
||||
|
||||
|
@@ -735,7 +735,7 @@ CX 640x480 800x600 1024x768/1280x1024
|
||||
break;
|
||||
case 0x05:
|
||||
if (reg_bh==0) { /* Set CPU Window */
|
||||
reg_ah=VESA_SetCPUWindow(reg_bl,reg_dl);
|
||||
reg_ah=VESA_SetCPUWindow(reg_bl,reg_dx);
|
||||
reg_al=0x4f;
|
||||
} else if (reg_bh == 1) { /* Get CPU Window */
|
||||
reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
|
||||
|
@@ -240,7 +240,7 @@ uint8_t VESA_GetSVGAInformation(uint16_t seg,uint16_t off);
|
||||
uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off);
|
||||
uint8_t VESA_SetSVGAMode(uint16_t mode);
|
||||
uint8_t VESA_GetSVGAMode(uint16_t & mode);
|
||||
uint8_t VESA_SetCPUWindow(uint8_t window,uint8_t address);
|
||||
uint8_t VESA_SetCPUWindow(uint8_t window,uint16_t address);
|
||||
uint8_t VESA_GetCPUWindow(uint8_t window,uint16_t & address);
|
||||
uint8_t VESA_ScanLineLength(uint8_t subcall, uint16_t val, uint16_t & bytes,uint16_t & pixels,uint16_t & lines);
|
||||
uint8_t VESA_SetDisplayStart(uint16_t x,uint16_t y,bool wait);
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "dosbox.h"
|
||||
#include "logging.h"
|
||||
#include "callback.h"
|
||||
#include "regs.h"
|
||||
#include "mem.h"
|
||||
@@ -36,6 +37,9 @@ bool vesa_bank_switch_window_range_check = true;
|
||||
bool vesa_bank_switch_window_mirror = false;
|
||||
bool vesa_zero_on_get_information = true;
|
||||
|
||||
extern unsigned int vbe_window_granularity;
|
||||
extern unsigned int vbe_window_size;
|
||||
|
||||
extern int vesa_mode_width_cap;
|
||||
extern int vesa_mode_height_cap;
|
||||
extern bool enable_vga_8bit_dac;
|
||||
@@ -138,6 +142,34 @@ void VESA_OnReset_Clear_Callbacks(void) {
|
||||
|
||||
extern bool vesa_bios_modelist_in_info;
|
||||
|
||||
uint32_t GetReportedVideoMemorySize(void) {
|
||||
uint32_t sz = vga.mem.memsize;
|
||||
|
||||
/* if the user specified custom window granularity, than
|
||||
* limitations in the interface to program bank offset
|
||||
* can cause problems if the granularity is small enough
|
||||
* that the reported video memory exceeds 128 (if 64KB
|
||||
* banks) or 256 (if not 64KB banks) possible values
|
||||
* of granularity. */
|
||||
if (vbe_window_granularity != 0) {
|
||||
unsigned int banks = (unsigned int)sz / vbe_window_granularity;
|
||||
|
||||
if (vbe_window_granularity >= (64*1024) && banks > 128)
|
||||
banks = 128; /* ref: vga_s3.cpp port 6Ah */
|
||||
else if (banks > 256)
|
||||
banks = 256; /* ref: vga_s3.cpp port 6Ah hack for < 64KB granularity */
|
||||
|
||||
uint32_t maxsz = (uint32_t)banks * (uint32_t)vbe_window_granularity;
|
||||
|
||||
if (vga.svga.bank_size > vbe_window_granularity)
|
||||
maxsz -= (vga.svga.bank_size - vbe_window_granularity);
|
||||
|
||||
if (sz > maxsz) sz = maxsz;
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
uint8_t VESA_GetSVGAInformation(uint16_t seg,uint16_t off) {
|
||||
/* Fill 256 byte buffer with VESA information */
|
||||
PhysPt buffer=PhysMake(seg,off);
|
||||
@@ -209,7 +241,7 @@ uint8_t VESA_GetSVGAInformation(uint16_t seg,uint16_t off) {
|
||||
}
|
||||
|
||||
mem_writed(buffer+0x0a,(enable_vga_8bit_dac ? 1 : 0)); //Capabilities and flags
|
||||
mem_writew(buffer+0x12,(uint16_t)(vga.mem.memsize/(64*1024))); // memory size in 64kb blocks
|
||||
mem_writew(buffer+0x12,(uint16_t)(GetReportedVideoMemorySize()/(64*1024))); // memory size in 64kb blocks
|
||||
return VESA_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -393,11 +425,11 @@ foundit:
|
||||
pageSize &= ~0xFFFFu;
|
||||
}
|
||||
Bitu pages = 0;
|
||||
if (pageSize > vga.mem.memsize || (mblock->special & _USER_DISABLED)) {
|
||||
if (pageSize > GetReportedVideoMemorySize() || (mblock->special & _USER_DISABLED)) {
|
||||
// mode not supported by current hardware configuration
|
||||
modeAttributes &= ~0x1;
|
||||
} else if (pageSize) {
|
||||
pages = (vga.mem.memsize / pageSize)-1;
|
||||
pages = (GetReportedVideoMemorySize() / pageSize)-1;
|
||||
}
|
||||
|
||||
/* VBE 1.0 allows fields "XResolution" and later to be optional.
|
||||
@@ -424,8 +456,16 @@ foundit:
|
||||
var_write(&minfo.YResolution,(uint16_t)mblock->theight);
|
||||
}
|
||||
} else {
|
||||
var_write(&minfo.WinGranularity,64);
|
||||
var_write(&minfo.WinSize,64);
|
||||
if (vbe_window_granularity > 0)
|
||||
var_write(&minfo.WinGranularity,vbe_window_granularity>>10u); /* field is in KB */
|
||||
else
|
||||
var_write(&minfo.WinGranularity,64);
|
||||
|
||||
if (vbe_window_size > 0)
|
||||
var_write(&minfo.WinSize,vbe_window_size>>10u); /* field is in KB */
|
||||
else
|
||||
var_write(&minfo.WinSize,64);
|
||||
|
||||
var_write(&minfo.WinASegment,(uint16_t)0xa000);
|
||||
|
||||
if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */
|
||||
@@ -463,11 +503,23 @@ uint8_t VESA_GetSVGAMode(uint16_t & mode) {
|
||||
return VESA_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t VESA_SetCPUWindow(uint8_t window,uint8_t address) {
|
||||
uint8_t VESA_SetCPUWindow(uint8_t window,uint16_t address) {
|
||||
if (window && !vesa_bank_switch_window_mirror) return VESA_FAIL;
|
||||
if ((!vesa_bank_switch_window_range_check) || (uint32_t)(address)*64*1024<vga.mem.memsize) { /* range check, or silently truncate address depending on dosbox.conf setting */
|
||||
|
||||
/* despite the fact DX in INT 10h AX=4F05h is the window address,
|
||||
* VESA BIOSes probably only look at DL anyway, because cards in the
|
||||
* 1990s didn't have enough memory to necessitate the full 16 bit
|
||||
* value and firmware programmers might take shortcuts anyway either
|
||||
* for performance or ROM space optimization. Perhaps someday if
|
||||
* DOSBox-X emulates a more modern SVGA card it would check the
|
||||
* full DX value. DOSBox SVN and forks achieve equivalent behavior
|
||||
* here by defining this function prototype with an 8-bit "address"
|
||||
* parameter. */
|
||||
address &= 0xFFu;
|
||||
|
||||
if ((!vesa_bank_switch_window_range_check) || (uint32_t)(address)*vga.svga.bank_size<GetReportedVideoMemorySize()) { /* range check, or silently truncate address depending on dosbox.conf setting */
|
||||
IO_Write(0x3d4,0x6a);
|
||||
IO_Write(0x3d5,(uint8_t)address);
|
||||
IO_Write(0x3d5,(uint8_t)address); /* NTS: in vga_s3.cpp this is a 7-bit field, wraparound will occur at address >= 128 but only if emulating a full 64KB bank as normal */
|
||||
return VESA_SUCCESS;
|
||||
} else return VESA_FAIL;
|
||||
}
|
||||
@@ -527,7 +579,7 @@ uint8_t VESA_ScanLineLength(uint8_t subcall,uint16_t val, uint16_t & bytes,uint1
|
||||
// offset register: virtual scanline length
|
||||
Bitu pixels_per_offset;
|
||||
Bitu bytes_per_offset = 8;
|
||||
Bitu vmemsize = vga.mem.memsize;
|
||||
Bitu vmemsize = GetReportedVideoMemorySize();
|
||||
Bitu new_offset = vga.config.scan_len;
|
||||
Bitu screen_height = CurMode->sheight;
|
||||
Bitu max_offset;
|
||||
@@ -749,7 +801,7 @@ uint8_t VESA_GetDisplayStart(uint16_t & x,uint16_t & y) {
|
||||
|
||||
static Bitu VESA_SetWindow(void) {
|
||||
if (reg_bh) reg_ah=VESA_GetCPUWindow(reg_bl,reg_dx);
|
||||
else reg_ah=VESA_SetCPUWindow(reg_bl,(uint8_t)reg_dx);
|
||||
else reg_ah=VESA_SetCPUWindow(reg_bl,reg_dx);
|
||||
reg_al=0x4f;
|
||||
return CBRET_NONE;
|
||||
}
|
||||
|
Reference in New Issue
Block a user