diff --git a/CHANGELOG b/CHANGELOG index 22401e1dc81..4fc66da10ac 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,11 @@ NEXT + - S3 emulation: Follow S3 Trio64 documentation and mask the Linear + Window Position bits according to the size of the memory. You + can only place it on a multiple of the memory size. This silences + warnings caused by Line Wars II which appears to modify only the + upper byte but not the lower when modifying this register, which + then leaves 0xA0 in the lower half. That lower half would be + ignored for 1MB or larger video memory sizes. (joncampbell123). - S3 XGA emulation: Add XGA acceleration for 4bpp packed 16-color SVGA modes. A CAD package has S3 support drivers that use the 16-color 4bpp packed modes and expects XGA acceleration to work. diff --git a/src/hardware/vga_memory.cpp b/src/hardware/vga_memory.cpp index 9bead49688d..efae49f756c 100644 --- a/src/hardware/vga_memory.cpp +++ b/src/hardware/vga_memory.cpp @@ -2945,30 +2945,40 @@ void VGA_StartUpdateLFB(void) { // FIXME: What about the 8MB window? } - /* The LFB register has an enable bit */ - if (!(vga.s3.reg_58 & 0x10)) { - vga.lfb.page = (unsigned int)vga.s3.la_window << 4u; - vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u; - vga.lfb.handler = NULL; - MEM_SetLFB(0,0,NULL,NULL); - } - /* if the DOS application or Windows 3.1 driver attempts to put the linear framebuffer + /* NTS: 64KB winsz = 0x10000 => winmsk = 0xFFFF0000 => 0xFFFF + * 1MB winsz = 0x100000 => winmsk = 0xFFF00000 => 0xFFF0 + * 2MB winsz = 0x200000 => winmsk = 0xFFE00000 => 0xFFE0 + * and so on. + * + * From the S3 Trio32/Trio64 documentation regarding the Linear Address Window Position Registers: + * "The Linear Address Window resides on a 64KB, 1MB, 2MB, or 4MB (Trio64 only) memory boundary (size-aligned) ... + * Some LSBs of this register are ignored because of the size-aligned boundary scheme" */ + const unsigned int la_winmsk = ~((winsz - 1u) >> 16u); /* Register holds the upper 16 bits of the linear address */ + + /* The LFB register has an enable bit */ + if (!(vga.s3.reg_58 & 0x10)) { + vga.lfb.page = (unsigned int)(vga.s3.la_window & la_winmsk) << 4u; + vga.lfb.addr = (unsigned int)(vga.s3.la_window & la_winmsk) << 16u; + vga.lfb.handler = NULL; + MEM_SetLFB(0,0,NULL,NULL); + } + /* if the DOS application or Windows 3.1 driver attempts to put the linear framebuffer * below the top of memory, then we're probably entering a DOS VM and it's probably * a 64KB window. If it's not a 64KB window then print a warning. */ - else if ((unsigned long)(vga.s3.la_window << 4UL) < (unsigned long)MEM_TotalPages()) { + else if ((unsigned long)(vga.s3.la_window << 4UL) < (unsigned long)MEM_TotalPages()) { if (winsz != 0x10000) // 64KB window normal for entering a DOS VM in Windows 3.1 or legacy bank switching in DOS LOG(LOG_MISC,LOG_WARN)("S3 warning: Window size != 64KB and address conflict with system RAM!"); - vga.lfb.page = (unsigned int)vga.s3.la_window << 4u; - vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u; + vga.lfb.page = (unsigned int)(vga.s3.la_window & la_winmsk) << 4u; + vga.lfb.addr = (unsigned int)(vga.s3.la_window & la_winmsk) << 16u; vga.lfb.handler = NULL; MEM_SetLFB(0,0,NULL,NULL); } else { - vga.lfb.page = (unsigned int)vga.s3.la_window << 4u; - vga.lfb.addr = (unsigned int)vga.s3.la_window << 16u; + vga.lfb.page = (unsigned int)(vga.s3.la_window & la_winmsk) << 4u; + vga.lfb.addr = (unsigned int)(vga.s3.la_window & la_winmsk) << 16u; vga.lfb.handler = &vgaph.lfb; - MEM_SetLFB((unsigned int)vga.s3.la_window << 4u,(unsigned int)vga.mem.memsize/4096u, vga.lfb.handler, &vgaph.mmio); + MEM_SetLFB((unsigned int)(vga.s3.la_window & la_winmsk) << 4u,(unsigned int)vga.mem.memsize/4096u, vga.lfb.handler, &vgaph.mmio); } }