From e837f5a5b6a6cdf3c055d13c39ffadca04165f73 Mon Sep 17 00:00:00 2001 From: Jonathan Campbell Date: Mon, 15 Jul 2024 21:41:56 -0700 Subject: [PATCH] Add pitch value to modelist, add code to VESAMOED to set the pitch, add note about Line Wars II S3 acceleration needing -pitch 1024 -mode 0x203 because of strange assumptions --- CHANGELOG | 10 ++++++++ src/ints/int10.h | 1 + src/ints/int10_modes.cpp | 52 +++++++++++++++++++++++++++++----------- src/ints/int10_vesa.cpp | 30 ++++++++++++----------- 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4fc66da10ac..6076510c67a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,14 @@ NEXT + - INT 10 mode list: Add "pitch" parameter. If nonzero, the value + (in pixels) is how to program the pixels per scanline when + setting the mode. + - VESAMOED: Add -pitch option to control the pitch (pixels per + scanline). The intent is to work around DOS games that assume + nonstandard pixels per scanline by allowing the VESA BIOS mode + to use a different one. For example, Line Wars II has an S3 + acceleration mode that uses 800x600, but for whatever reason, + assumes the video mode is 1024 pixels per scanline, and will + render garbled graphics if that is not the case. (joncampbell123). - 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 diff --git a/src/ints/int10.h b/src/ints/int10.h index 95f60c6a8d7..73c2d00a056 100644 --- a/src/ints/int10.h +++ b/src/ints/int10.h @@ -119,6 +119,7 @@ struct VideoModeBlock { Bitu htotal,vtotal; Bitu hdispend,vdispend; Bitu special; + Bitu pitch = 0; /* bytes/scanline to use instead of normal calculation */ }; extern VideoModeBlock ModeList_VGA[]; diff --git a/src/ints/int10_modes.cpp b/src/ints/int10_modes.cpp index c8c6f3dd02e..371ad402593 100644 --- a/src/ints/int10_modes.cpp +++ b/src/ints/int10_modes.cpp @@ -1738,29 +1738,34 @@ bool INT10_SetVideoMode(uint16_t mode) { } /* Offset Register */ - Bitu offset; + Bitu offset,ouwidth = (CurMode->pitch != 0) ? CurMode->pitch : CurMode->swidth; switch (CurMode->type) { case M_LIN8: - offset = CurMode->swidth/8; + offset = ouwidth/8; break; case M_LIN15: case M_LIN16: - offset = 2 * CurMode->swidth/8; + offset = 2 * ouwidth/8; break; case M_LIN24: - offset = 3 * CurMode->swidth/8; + offset = 3 * ouwidth/8; break; case M_LIN32: - offset = 4 * CurMode->swidth/8; + offset = 4 * ouwidth/8; break; case M_EGA: - if (IS_EGA_ARCH && vga.mem.memsize < 0x20000 && CurMode->vdispend==350) + if (CurMode->pitch != 0) + offset = ouwidth/(2*8); + else if (IS_EGA_ARCH && vga.mem.memsize < 0x20000 && CurMode->vdispend==350) offset = CurMode->hdispend/4; /* = 0x14, See EGA BIOS listing for entry 10h 16K mode [https://ibmmuseum.com/Adapters/Video/EGA/IBM_EGA_Manual.pdf] */ else offset = CurMode->hdispend/2; break; default: - offset = CurMode->hdispend/2; + if (CurMode->pitch != 0) + offset = ouwidth/(2*8); + else + offset = CurMode->hdispend/2; break; } @@ -2265,7 +2270,8 @@ bool INT10_SetVideoMode(uint16_t mode) { case M_LIN32: reg_50|=S3_XGA_32BPP; break; default: break; } - switch(CurMode->swidth) { + unsigned int xgawidth = (CurMode->pitch != 0) ? CurMode->pitch : CurMode->swidth; + switch(xgawidth) { case 640: reg_50|=S3_XGA_640; break; case 800: reg_50|=S3_XGA_800; break; case 1024: reg_50|=S3_XGA_1024; break; @@ -2568,12 +2574,19 @@ void INT10_SetJ3ModeCGA4(uint16_t mode) Bitu INT10_WriteVESAModeList(Bitu max_modes); /* ====================== VESAMOED.COM ====================== */ +/* NOTES: + * + * - Line Wars II: If you use the S3 acceleration mode (800x600) + * use "VESAMOED -mode 0x203 -pitch 1024" to make 800x600 behave + * as the game expects, or else you will get garbled graphics. + */ class VESAMOED : public Program { public: void Run(void) override { size_t array_i = 0; std::string arg,tmp; bool got_opt=false; + int pitch = -1; int mode = -1; int fmt = -1; int w = -1,h = -1; @@ -2626,6 +2639,10 @@ class VESAMOED : public Program { return; } } + else if (arg == "pitch") { + cmd->NextOptArgv(/*&*/tmp); + pitch = (int)strtoul(tmp.c_str(),NULL,0); + } else if (arg == "w") { cmd->NextOptArgv(/*&*/tmp); w = (int)strtoul(tmp.c_str(),NULL,0); @@ -2740,11 +2757,15 @@ class VESAMOED : public Program { return; } - if (!modefind && (w > 0 || h > 0 || fmt >= 0 || ch > 0)) { + if (!modefind && (w > 0 || h > 0 || fmt >= 0 || ch > 0 || pitch >= 0)) { WriteOut("Changing mode 0x%x parameters\n",(unsigned int)ModeList_VGA[array_i].mode); ModeList_VGA[array_i].special |= _USER_MODIFIED; + LOG_MSG("pitch %d",pitch); + if (pitch >= 0) { + ModeList_VGA[array_i].pitch = (unsigned int)pitch; + } if (fmt >= 0) { ModeList_VGA[array_i].type = (VGAModes)fmt; /* will require reprogramming width in some cases! */ @@ -2815,24 +2836,25 @@ class VESAMOED : public Program { /* if the new mode cannot fit in available memory, then mark as disabled */ { unsigned int pitch = 0; + unsigned int cwidth = (ModeList_VGA[array_i].pitch != 0) ? ModeList_VGA[array_i].pitch : ModeList_VGA[array_i].swidth; switch (ModeList_VGA[array_i].type) { case M_LIN4: case M_PACKED4: - pitch = (unsigned int)(ModeList_VGA[array_i].swidth / 8) * 4u; /* not totally accurate but close enough */ + pitch = (unsigned int)(cwidth / 8) * 4u; /* not totally accurate but close enough */ break; case M_LIN8: - pitch = (unsigned int)ModeList_VGA[array_i].swidth; + pitch = (unsigned int)cwidth; break; case M_LIN15: case M_LIN16: - pitch = (unsigned int)ModeList_VGA[array_i].swidth * 2u; + pitch = (unsigned int)cwidth * 2u; break; case M_LIN24: - pitch = (unsigned int)ModeList_VGA[array_i].swidth * 3u; + pitch = (unsigned int)cwidth * 3u; break; case M_LIN32: - pitch = (unsigned int)ModeList_VGA[array_i].swidth * 4u; + pitch = (unsigned int)cwidth * 4u; break; default: break; @@ -2867,6 +2889,8 @@ class VESAMOED : public Program { WriteOut(" -delete Delete video mode\n"); WriteOut(" -disable Disable video mode (list but do not allow setting)\n"); WriteOut(" -enable Enable video mode\n"); + WriteOut(" -pitch Change display pitch (pixels per scanline).\n"); + WriteOut(" A value of zero will restore normal calculation.\n"); } }; diff --git a/src/ints/int10_vesa.cpp b/src/ints/int10_vesa.cpp index 6cfd82f7e43..38fd3e2b174 100644 --- a/src/ints/int10_vesa.cpp +++ b/src/ints/int10_vesa.cpp @@ -292,11 +292,13 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { bool allow_res = allow_vesa_lowres_modes || (ModeList_VGA[i].swidth >= 640 && ModeList_VGA[i].sheight >= 400); + unsigned int cwidth = (mblock->pitch != 0) ? mblock->pitch : mblock->swidth; + switch (mblock->type) { case M_PACKED4: if (!allow_vesa_4bpp_packed && !(ModeList_VGA[i].mode >= 0x202 && ModeList_VGA[i].mode <= 0x208)) return VESA_FAIL;//TODO: New option to disable - pageSize = mblock->sheight * mblock->swidth/2; - var_write(&minfo.BytesPerScanLine,(uint16_t)((((mblock->swidth+15U)/8U)&(~1U))*4)); /* NTS: 4bpp requires even value due to VGA registers, round up */ + pageSize = mblock->sheight * cwidth/2; + var_write(&minfo.BytesPerScanLine,(uint16_t)((((cwidth+15U)/8U)&(~1U))*4)); /* NTS: 4bpp requires even value due to VGA registers, round up */ if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x1); var_write(&minfo.BitsPerPixel,4); @@ -307,8 +309,8 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { break; case M_LIN4: if (!allow_vesa_4bpp) return VESA_FAIL; - pageSize = mblock->sheight * (uint16_t)(((mblock->swidth+15U)/8U)&(~1U)); - var_write(&minfo.BytesPerScanLine,(uint16_t)(((mblock->swidth+15U)/8U)&(~1U))); /* NTS: 4bpp requires even value due to VGA registers, round up */ + pageSize = mblock->sheight * (uint16_t)(((cwidth+15U)/8U)&(~1U)); + var_write(&minfo.BytesPerScanLine,(uint16_t)(((cwidth+15U)/8U)&(~1U))); /* NTS: 4bpp requires even value due to VGA registers, round up */ if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x4); var_write(&minfo.BitsPerPixel,4); // bits per pixel is 4 as specified by VESA BIOS 2.0 specification @@ -318,8 +320,8 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { break; case M_LIN8: if (!allow_vesa_8bpp || !allow_res) return VESA_FAIL; - pageSize = mblock->sheight * mblock->swidth; - var_write(&minfo.BytesPerScanLine,(uint16_t)mblock->swidth); + pageSize = mblock->sheight * cwidth; + var_write(&minfo.BytesPerScanLine,(uint16_t)cwidth); if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x1); var_write(&minfo.BitsPerPixel,8); @@ -330,8 +332,8 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { break; case M_LIN15: if (!allow_vesa_15bpp || !allow_res) return VESA_FAIL; - pageSize = mblock->sheight * mblock->swidth*2; - var_write(&minfo.BytesPerScanLine,(uint16_t)(mblock->swidth*2)); + pageSize = mblock->sheight * cwidth*2; + var_write(&minfo.BytesPerScanLine,(uint16_t)(cwidth*2)); if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x1); var_write(&minfo.BitsPerPixel,15); @@ -350,8 +352,8 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { break; case M_LIN16: if (!allow_vesa_16bpp || !allow_res) return VESA_FAIL; - pageSize = mblock->sheight * mblock->swidth*2; - var_write(&minfo.BytesPerScanLine,(uint16_t)(mblock->swidth*2)); + pageSize = mblock->sheight * cwidth*2; + var_write(&minfo.BytesPerScanLine,(uint16_t)(cwidth*2)); if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x1); var_write(&minfo.BitsPerPixel,16); @@ -369,8 +371,8 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { case M_LIN24: if (!allow_vesa_24bpp || !allow_res) return VESA_FAIL; if (mode >= 0x120 && !allow_explicit_vesa_24bpp) return VESA_FAIL; - pageSize = mblock->sheight * mblock->swidth*3; - var_write(&minfo.BytesPerScanLine,(uint16_t)(mblock->swidth*3)); + pageSize = mblock->sheight * cwidth*3; + var_write(&minfo.BytesPerScanLine,(uint16_t)(cwidth*3)); if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x1); var_write(&minfo.BitsPerPixel,24); @@ -387,8 +389,8 @@ uint8_t VESA_GetSVGAModeInformation(uint16_t mode,uint16_t seg,uint16_t off) { break; case M_LIN32: if (!allow_vesa_32bpp || !allow_res) return VESA_FAIL; - pageSize = mblock->sheight * mblock->swidth*4; - var_write(&minfo.BytesPerScanLine,(uint16_t)(mblock->swidth*4)); + pageSize = mblock->sheight * cwidth*4; + var_write(&minfo.BytesPerScanLine,(uint16_t)(cwidth*4)); if (!int10.vesa_oldvbe10) { /* optional in VBE 1.0 */ var_write(&minfo.NumberOfPlanes,0x1); var_write(&minfo.BitsPerPixel,32);