Most Tandy video registers are write-only, yet Troubadours likes to read/modify/write. Add log file message whenever those are read, and return 0x00 for specific registers to help the game and the hand-rolled modesetting (someone doesn't like using INT 10 to do it?) set up 320x200 16-color mode, even though the game crashes anyway [https://github.com/joncampbell123/dosbox-x/issues/5867]

This commit is contained in:
Jonathan Campbell
2025-10-09 08:20:12 -07:00
parent 5a7ad7f9b8
commit 170ff0785b
2 changed files with 50 additions and 2 deletions

View File

@@ -1,5 +1,21 @@
NEXT VERSION
-
- Tandy machine type: Many registers are WRITE ONLY according to SX
documentation, yet "Troubadours" likes to read/modify/write those
registers in what appears to be hand-rolled manual modesetting code
to set up 320x200 16-color mode, so, add code to respond to reads
from those ports with zeros and print a debug message to the log
about how DOS games are not supposed to read those registers. Make
the hand-rolled modesetting work by returning 0x00, rather than 0xFF.
The code will not set up the mode correctly if 0xFF is returned, but
will if 0x00 is returned. A side effect of this is that, since one
of the registers controls the RAM address used as video memory, this
moves it down to 512KB from base memory as well, which of course
easily corrupts the very end of the MCB chain unless you enable more
than 640KB of RAM and ask DOSBox-X to emulate the full Tandy 768KB
of conventional memory. Though the game still crashes very easily,
and in the exact same manner in both DOSBox-X and DOSBox SVN, this
fix does solve the problem where the game was only able to draw
2 out of 4 scanlines on the screen. (joncampbell123).
2025.10.07
- INT 21h AH=4Ah resize memory: Compact free blocks only at or after the

View File

@@ -944,6 +944,27 @@ static void write_tandy_reg(uint8_t val) {
}
}
/* You're not SUPPOSED to read Tandy registers 3D8, 3DE, 3DF, they're documented WRITE ONLY! */
static Bitu read_tandy(Bitu port,Bitu /*iolen*/) {
LOG(LOG_VGAMISC,LOG_DEBUG)("DOS applications are NOT SUPPOSED to read Tandy register %x",(unsigned int)port);
switch (port) {
/* "Troubadours" does not switch into Tandy 320x200 16-color by calling INT 10h.
* Instead, it has it's own hand-written code to modify registers directly.
* For whatever reason, it modifies 3DD and 3DF by reading, modifying, and writing
* the registers EVEN THOUGH Tandy documents the registers as write only.
* Returning the last written value doesn't work, because the modified values
* come out incorrect. 3DFh after modification comes out to 0xFF which maps the
* video RAM to B800h as repeating 16KB banks for example. The game works correctly
* if this code provides 0x00, not 0xFF, when it reads these specific registers. */
case 0x3DD:
case 0x3DF:
return 0;
}
return ~0u;
}
static void write_tandy(Bitu port,Bitu val,Bitu /*iolen*/) {
switch (port) {
case 0x3d8:
@@ -981,7 +1002,6 @@ static void write_tandy(Bitu port,Bitu val,Bitu /*iolen*/) {
// appears to be ORed with CPU A14 (to preserve some sort of
// backwards compatibility?), resulting in odd pages being mapped
// as 2x16kB. Implemented in vga_memory.cpp Tandy handler.
vga.tandy.line_mask = (uint8_t)(val >> 6);
vga.tandy.draw_bank = val & ((vga.tandy.line_mask&2) ? 0x6 : 0x7);
vga.tandy.mem_bank = (val >> 3) & 7;
@@ -1297,6 +1317,18 @@ void VGA_SetupOther(void) {
}
if (machine==MCH_TANDY) {
write_tandy( 0x3df, 0x0, 0 );
// according to Tandy SX documentation a lot of registers are write-only
IO_RegisterReadHandler(0x3d4,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3d5,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3d8,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3d9,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3db,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3dc,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3dd,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3de,read_tandy,IO_MB);
IO_RegisterReadHandler(0x3df,read_tandy,IO_MB);
IO_RegisterWriteHandler(0x3d8,write_tandy,IO_MB);
IO_RegisterWriteHandler(0x3d9,write_tandy,IO_MB);
IO_RegisterWriteHandler(0x3da,write_tandy,IO_MB);