VOGONS


First post, by carlostex

User metadata
Rank l33t
Rank
l33t

Hi there!

Tandy 3 voice sound on an AT class computer (286+) always faced the second DMA controller problem. As you know, on the IBM AT and following clones a second Intel 8237 DMA controller was placed on port 0C0h, right where IBM PC/Jr and Tandy 1000 PC's ha the 3 voice chip. So AT users with homebrew Tandy cards, had commonly their cards set to port 2C0h. Port 1E0h, as with later Tandy PC's, which the Tandy 1000 RSX and the 2500XL, kept the PSSJ but relocated to port 1E0h. Later Tandy PC's thus had their compatibility broken with older software.

Enter the software solution. Through the years we got redirection utilities, for both the ISA cards and the TNDLPT device. Recently, i was discussing here on VOGONS about repatching for the INT C0h trick. This is a better approach than the TSR method as port trapping is made completely unnecessary, no need for QEMM or EMM386! However, i started to be a bit more ambitious... Why not patch for a 2 byte port? Directly to port 2C0h... Possible? Why is this difficult?

As some of you might know with only 2 bytes you can do this:

E6 C0 -> OUT C0, AL

With only 2 bytes you spit out the data that is on AL right directly to the Tandy chip I/O port.
Now for a 2 byte port like 2c0h?

52 -> PUSH DX
BAC002 -> MOV DX, 2c0
EE -> OUT DX, AL
5A -> POP DX

Ouch... that's 6 bytes... One can try and remove saving DX into the stack but it's still 4 bytes and there's situations you can't simply destroy what's on DX, as it might be needed later. So the options:

Option 1:
Find some empty space within the segment where we can jump and create our patch code. Short jumps are 2 bytes long which fit but 99% of the time there's no space around the +- 128 bytes. So better do a CALL, but that's 3 bytes. So that requires this, like the following example:

8AC4
E6C0

We now have 4 bytes to do a CALL and there will be a NOP left over:

E8 xx xx -> CALL to .... (wherever we find the space without effing up)
90

and the patched code will look like this:

8AC4
52
BAC002
EE
5A
C3 -> RETURN BACK to the caller, will land on that stray NOP (which is fine)

Above is fine as long as it's within the same segment, or you're not overwriting reserved space for something else.

Option 2:
Now this one is a LOT more work, but it's ALWAYS better. It's common, (at least a lot of games have them) to find multiple 2 byte SHIFT instructions like so:

D0E0 -> SHL AL, 1
D0E0 -> SHL AL, 1
D0E0 -> SHL AL, 1
D0E0 -> SHL AL, 1

Why not SHL AL, 4?

C0E004 -> SHL AL, 4

and like that we save 5 bytes!!!! OK this will require the 186 instruction set but who cares? XT systems can use port 0C0h anyway....
We're not out of the woods yet, so i need to carefully rewrite ALL instructions realign jumps and calls in between always cross referencing every instruction!
But it works, and soon enough i reach the E6 C0 instruction and i have enough space to patch! Sometimes i have to use a combination of both methods!

Honestly, it's a lot of work...but i don't mind. I'm actually enjoying doing this. A bit masochistic, but it has been an enriching experience. I still face some difficulties though...dreaded ENCRYPTION. Some files are packed/encrypted and it's impossible to patch otherwise. For EA stuff with their LIB files, or DYNAMIX VOL files, SSI with their DAX files, all of those use some kind of compression / encryption that makes it impossible to patch. I have found an unpacking utility for EA LIB files for instance, but the game is expecting to find the packed file during runtime and it completely ignores the unpacked file in the directory. So this would need either some tool to encrypt back the file after i had it patched. These ones are super frustrating because without any encryption it is possible to patch 95% of the time.

So what is the progress at the moment? Well... I have quite a few games patched, i would say around 30 titles or so, which include the classics most everyone likes.

The patches are available at the OLDSKOOL FTP, exactly where my old patches that defeat detection could be found. At the root, you'll now find a folder called ISA with 2 subfolders. One for port 1E0 (great for the AT Tandy PC's) and port 2C0. Some readme's are also included where i thought it would make sense. This is still a WIP, and i guarantee more games will be patched. It is absolutely great to just launch a game not having to depend on any TSR's or redirection tricks, installing INT handlers etc... Just run and go!

I honestly hope that in the future we will see an ISA card that consolidates other similar standards, a single ISA card that would tackle Tandy, CMS, Innovation and Covox Sound Master.

Reply 1 of 7, by carlostex

User metadata
Rank l33t
Rank
l33t

As an addendum, during the patching process i found something i never knew. So LUCASARTS dropped Tandy support for the 256 color version of Indiana Jones and the Last Crusade right?? Er... well they dropped the "ts" command line option from the EXE but the sound routines and the data are intact!!! So there's a patched version on the FTP that allows you to play Indy 256 color with Tandy 3 voice sound! Was just a matter of defeating Adlib detection + making it believe it's running on a Tandy PC and 3 voice Tandy will play!

Reply 2 of 7, by digger

User metadata
Rank Oldbie
Rank
Oldbie

Cool hackery. Thanks for sharing! 🙂

I consider it once of IBM more frustrating decisions how they the broke their own hardware compatibility when they released the PC/AT and just carelessly repurposed that same I/O address for the second DMA controller.

I know IBM always focused on business use cases and stuck to the lame PC speaker just to save maybe a dollar or two in manufacturing costs, but why not keep that official PCjr I/O address free, and pick one of the many other available ports for the second DMA controller? They could then have added the 3-voice sound chip to their official joystick card, in effect making it a "proto sound card" that would in effect add PCjr sound compatibility back in. Didn't it make sense for the same people who bought joystick cards to also want better sound capabilities?

Or better still, why didn't they just equip the PC/AT with the same PCjr-compatible sound chip on the same I/O address? That machine was more high-end and had a higher profit margin anyway.

Maybe if a third-party joystick card manufacturer had come up with that idea and sold enough of such joystick/sound cards before the PC/AT was released , that would have influenced IBM's design decision.

Reply 3 of 7, by carlostex

User metadata
Rank l33t
Rank
l33t

IBM always wanted to keep away from games for their top of the line PC machines, the PC the XT and the AT were to be seen as "serios machines for real work". Machines for games were commony referred as toys, an IBM wanted to avoid that at all costs. In hindsight, yes it would be great if they kept the SN76489 for the AT, but that's just not how corporate suits thought back then.

I had a chance to keep in touch with Rich Heimlich, and one of the stories he told me involved testing a bunch of software, including games for IBM OS/2. I think Rich tells this story in the Bleep webisode about sound, and basically the IBM top brass didn't care that DOS games didn't have any sound when running through OS/2. Rich refused to go along when OS/2 wasn't fully working and Dave Whittle got fired, which he ultimately blamed on Rich. So Rich told me further that none other than Mike Meisner (MR BIOS) found out the issue was with VDMA.SYS driver. Mike hacked the driver, and suddenly everything worked. Who knows what IBM OS/2 could have been if it had perfect compatibility with DOS games and other software?

Reply 4 of 7, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

maybe do the replace a 2 byte out 0xc0,al with an int call 0xCD 0xXX? thats only two bytes then you can inject a tsr of however many bytes you need..

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--

Reply 5 of 7, by carlostex

User metadata
Rank l33t
Rank
l33t
BloodyCactus wrote on 2025-08-12, 22:13:

maybe do the replace a 2 byte out 0xc0,al with an int call 0xCD 0xXX? thats only two bytes then you can inject a tsr of however many bytes you need..

That defeats the exact purpose of having a binary that does not need a tsr which is all what this is about. It’s nice to have the card in the ISA slot launch the game and it just works, there’s nothing in between.

Besides…encryption is the biggest problem i face, and that makes what I’m doing difficult and it’s just as difficult to patch for INT C0h. So for those games the old port trapping redirection is still the best option, I’m afraid.

Regardless, there’s quite a good number of titles that have been patched already.

Reply 6 of 7, by pdw

User metadata
Rank Newbie
Rank
Newbie

If what annoys you most about a TSR is the memory use, there are ways around that. Your C0h interrupt handler would only be 7 bytes (PUSH DX / MOV DX, 2c0 / OUT DX, AL / POP DX / IRET). There are a lot of spaces where that can be stashed without "using" memory. E.g. two unused interrupt vector slots. Or the "Inter-Application Communication Area" at 0040:00F0.

The nice thing about the INT C0h patches is that it allows for compatibility with any hardware configuration: original hardware, port 2C0h, TNDLPT.

Reply 7 of 7, by carlostex

User metadata
Rank l33t
Rank
l33t
pdw wrote on 2025-08-13, 11:47:

If what annoys you most about a TSR is the memory use, there are ways around that. Your C0h interrupt handler would only be 7 bytes (PUSH DX / MOV DX, 2c0 / OUT DX, AL / POP DX / IRET). There are a lot of spaces where that can be stashed without "using" memory. E.g. two unused interrupt vector slots. Or the "Inter-Application Communication Area" at 0040:00F0.

The nice thing about the INT C0h patches is that it allows for compatibility with any hardware configuration: original hardware, port 2C0h, TNDLPT.

I don't deny that The INT C0h trick isn't without its merits. I do say so in the original post. But the goal here was different. I wanted to challenge myself, because i knew in theory what i wanted to do was possible, so i just rolled my sleeves and started on a game by game basis and things started to progress. Will i be able to patch 100% of games with this method? Of course not, but i was ready for that. I just wanted to have binaries that i could just run and they would work, no need to load any TSR's or reprogram the interrupt handler. So i'm not against it, the more options the better.

Again the biggest issue a project like this isn't even trying to replace and patch instructions, it's encryption. And there are still games in which the Tandy detection eludes and puzzles me. For instance, i have an additional 3 ACCOLADE titles patched for Tandy that i haven't released yet. While these work great in DOSBox set to machine=svga_s3 and tandy=on, they still play with PC Speaker on a real DOS PC. These are Les Manley 2, Elvira: The Jaws of Cerberus and The Games: Winter Challenge. These games seem to share the same type of sound engine, but i couldn't break their detection yet. And this is an issue that will be present for this method or the interrupt handler trick.