First post, by aitotat

User metadata
Rank Member

Many of the older General Midi games are hardcoded to use port 330h. It is not a problem if you have only one MIDI device. But if you do have more, wouldn't it be nice if you could choose what device to use?

MIDIto tries to do just that. It uses EMM386 or QEMM to trap ports and redirect data elsewhere, to user defined destination. EMM386 and QEMM have limitations, such as this approach does not work with protected mode games. Fortunately many of them allow to define MIDI port (but not all).

MIDIto can do other things as well. It can add delays for OPL port accesses so OPL2 cards could be used on faster systems. MIDIto can also make your CMS equipped Sound Blaster 1.x and 2.0 cards to be fully Game Blaster compatible when needed. And last but not least MIDIto can lock Sound Blaster Pro and 16 mixer so games cannot mess with volumes and inputs.

How to use MIDIto? I tried to make things as simple as possible:

MIDIto TSR - Release 7 - 2021.11.25
(C) 2021 by Tomi Tilli
Released under GNU GPL v2
Uses assembly libraries from XTIDE Universal BIOS and Associated Tools
Copyright (C) 2009-2010 by Tomi Tilli, 2011-2021 by XTIDE Universal BIOS Team.

Redirect MIDI data meant for port 330h to another MIDI device you like better.
Fix OPL2 for faster systems. Make SB with CMS fully Game Blaster compatible.
Lock Sound Blaster Pro/16 mixer against unwanted changes.


Optional destination MIDI port. Accepted values are 100 - FFFE
-a or /a: Adlib - Trap Adlib ports with minimum I/O delay
-a=##### or /a=#####: Adlib - Trap Adlib ports with ##### number of extra
jumps when minimum delay is not enough. Values up to 65535 are accepted
-a=off or /a=off: Adlib - Disable Adlib ports
-cms or /cms: Emulate CT-1302 for full CMS / Game Blaster support
-lock or /lock: Lock Sound Blaster Pro/16 mixer. See readme for more info
-u or /u: Uninstall - Uninstalls MIDIto TSR from memory
-v or /v: Verbose - Prints extra install or uninstall information

More detailed description about the options

-a=##### or /a=##### Traps Adlib ports 388h (index) and 389h (data). OPL ports
from Sound Blaster address space are also trapped if Sound Blaster is detected.
Trapped ports are determined by Sound Blaster DSP version:

DSP versions 1.x and 2.x can only appear on Sound Blaster 1.0, 1.5 and 2.0
cards and they might have CMS chips and always have one OPL2 chip. It is not
possible to detect if the CMS chips are actually present. For those cards
MIDIto traps OPL2 ports at Sound Blaster base+8h and base+9h in addition to the
Adlib ports.

Sound Blasters with DSP version 3.x (Sound Blaster pro 1 and 2) and 4.x (Sound
Blaster 16/32/AWE) cannot have CMS chips. Instead, the CMS address space
(base+0...base+3) is used by OPL3 (or two OPL2 chips for SB Pro 1). For these
cards, MIDIto traps all the Adlib and SB OPL2 ports mentioned above plus the
OPL3 ports.

##### is used to specify number of extra jumps after the port output. OPL2 is
very speed sensitive and you normally start having problems on a 386-33 and
faster systems. Some games are less sensitive than others since some games use
longer delays between port accesses. This also means that if you find a good
delay value for one game, it may not necessarily be enough for some other game.
The extra jumps are implemented by a single LOOP instruction that loops
specified number of times after output to port.

Extra jumps only affect output. For OPL2 there should be a delay of 23us after
data register write and 3.3us after index register write. MIDIto uses full
number of jumps only after output to data ports. 1/8 of extra jumps are used
after output to index port. No extra delay is used for port input.

If number of extra jumps is zero or number is not specified at all, then MIDIto
Show last 142 lines
is set for minimum delay. Only data ports will be trapped without any extra
jumps. Index ports remain untrapped.

-a=off or /a=off will disable ports completely by ignoring outputs. For inputs
FFh is always returned to indicate that nothing was at that port. Disabling OPL
ports is necessary for some games to be forced to CMS/Game Blaster mode. Sound
Blaster DSP can be accessed normally and the card will be detected as a Sound
Blaster and not as a Game Blaster.

-cms or /cms will place Sound Blaster 1.x and 2.0 to a full Game Blaster
emulation mode. It does all the -a=off parameter does but in addition CT-1302
will be emulated. It is a chip on a Game Blaster that is used to detect the
card (since CMS chips are write only and cannot be detected). Some games will
require CT-1302 for CMS sounds. CT-1302 conflicts with Sound Blaster DSP so
Sound Blaster cannot be accessed when Game Blaster emulation mode is enabled.
Some games also require that the Game Blaster is at port 220h. That is not
necessary anymore since MIDIto will always map the virtual Game Blaster to 220h
even if you have a real Game Blaster on another address. This way you can use
real Game Blaster at any address and use Sound Blaster at port 220h without
loss of game compatibility. If you have CMS capable Sound Blaster and a Game
Blaster on a same system, MIDIto will always prefer real Game Blaster.
One more thing to note is that MIDIto will always trap CMS ports (so it can be
relocated to 220h when necessary). Port trapping will always have some delay.
That is likely a good thing since MIDIto requires a 386 and CMS games are
usually made for slower systems.

Some games will require the GBC-CMS.COM (for Game Blaster) or SBC-CMS.COM
(for Sound Blaster) driver. You must use the GBC-CMS.COM since the Sound
Blaster will be detected as a real Game Blaster.

-lock or /lock will lock Sound Blaster Pro and 16 mixer to prevent unwanted
changes. Some games modify volumes and even disables inputs and leaves them
like that after game quits. These modified volumes might even cause clipping so
it affect the audio quality as well. Mixer locking will fix these issues.
By default, master, CD, Line In, MIC and PC-speaker (SB16 PC-speaker input)
are locked to whatever levels they happen to be when you start MIDIto.
Voice (DAC) and FM/MIDI volumes are not locked by default since games might use
those for effects (like stereo sound in Wolfenstein 3D).

MIDIto stores all mixer values during initialization and unlocked mixer values
are restored when game does mixer reset (normally that would restore hardware
defaults) or game exits. The latter is important since locking will not work
for protected mode games but mixer values can be restored after protected mode
game quits.

You can customize what is locked with -lock=XX,YY,ZZ. Just separate locks with
commas and you can speficy as many as you need. For example -lock=MA,LI,LPE
would lock MAster volume, LIne in volume and force Low Pass filter Enabled.

Here are all the locks:
DEF Sets all default locks (MA, CD, LI, MIC, SP)
MA Locks MAster Volume
VO Locks VOice (DAC)
MI Locks MIdi volume (same as FM)
FM Locks FM volume (same as MI)
CD Locks CD volume
LI Locks Line In volume
MIC Locks MICrophone volume
SP Locks pc SPeaker volume (SB16 PC speaker input)
LPE Forces output Low Pass filter Enabled (only on Sound Blaster Pro)
LPD Forces output Low Pass filter Disabled (only on Sound Blaster Pro)
YMF Enables Yamaha YMF71x Master Volume fix

Low Pass Filter is not available on Sound Blaster 16/32/AWE cards. It is also
not available on some SB Pro compatibles but some do have it.

FM and MI are the same since that is how it is implemented on Sound Blaster 16:
FM volume and Waveblaster MIDI shares the same volume control. In some future
release of MIDIto that can be separated. Since games either use MIDI or FM,
it would be possible to set one volume when midi port (defined by SET BLASTER
P3xx) is accessed and switch to other volume level when FM port is accessed.

YMF enables fix for Yamaha YMF71x based cards. It's unknown if all YMF71x chips
require this fix but some have hardware bug where setting SB Pro master volume
to anything above 1 breaks volume controls. You can't hear stereo sounds on
Wolfenstein 3D for example. YMF-lock locks SB Master volume to 1 and redirects
SB master volume control to master volume available on YMF71x control ports.
This way you can have working volume controls. YMF-lock alone won't lock volume
controls. It justs sets the fix by hiding the problematic real SB master volume
control. If you also want to lock the controls, just specify the locks you want
or use "-lock=YMF, DEF" to set up default locks.

By default the YMF-fix uses control port 370h that is the default. If you have
set the control port to another address, you need to set R### to the end of
SET BLASTER string on autoexec.bat. This is the Unisound way to set the control
port so if you use Unisound to initialize the card, then nothing needs to be
done. For example to set the control port to 380h, make sure SET BLASTER string
contains R380.

Version information

Release 7 - 2021.11.26
* Fixed some uninstall error strings that were not correctly displayed
* Added mixer lock to fix Yamaha YMF71x SB Pro Master Volume control
* Fixes mixer locking for Star Trek: Judgment Rites by returning last written
MIC volume so MIC volume appears to be unlocked (but it is really locked).

Release 6 - 2021.11.19
* Optimizations to reduce TSR size, especially for QEMM
* MIDIto now automatically uninstalls if you try to reinstall with new options
* QEMM handler for Adlib delay with extra jumps no longer corrupt AH register.
That was the cause why QEMM was slower than EMM386 on Wolfenstein 3D.
* Added -lock command line parameter for Sound Blaster Pro and 16 mixer locking

Release 5 - 2021.10.20
* Two (instead of four) OPL ports are now trapped from Adlib address 388h
* Added Sound Blaster and Game Blaster detection
* AUTO and NOEMS parameters are now supported for EMM386
* Adlib zero delay now traps only data ports for as little delay as possible
* Specified Adlib delay is now for output only. Full specified delay is for data ports
and index ports will have 1/8 delay. Inputs do not have extra delay.
* Added -cms command line parameter for full Game Blaster emulation to
maximize CMS compatibility

Release 4 - 2021.10.01
* EMM386/QEMM version check now accepts versions with greater major number
but lesser minor number than supported minimum version numbers.
* Adlib delay can now be specified and default value is set to zero. The extra
delay is for Adlib DATA port only. Address/Status port is trapped but
with minimum delay.
* Adlib ports can now be disabled completely

Release 3 - 2021.9.24
* Added support for QEMM 7.03 and later
* Destination MIDI port is now optional
(so it is possible to only trap Adlib ports)

Release 2 - 2021.9.17
* Extended destination port range from 100h-FFEh to 100h-FFFEh

Release 1 - 2021.9.16
* First release

Known issues
* EMM386 cannot be set to OFF (Cannot be fixed. Use AUTO parameter instead)
* Loading MIDIto can fail if VIDE-CDD.SYS CD-ROM driver is used. Reason
unknown. Likely does not affect every system.

I took the same approach as mt32-pi control program. When there are no errors, there won't be any text output. Things just work silently. You can use the -v parameter to display what MIDIto tries to do. If you execute MIDIto without parameters or unsupported parameter, you get the instructions displayed above.

MIDIto can be downloaded here. Source codes are included in the zip file.
Old releases can be found here.

Last edited by aitotat on 2021-11-26, 09:30. Edited 7 times in total.

Reply 1 of 22, by Gmlb256

User metadata
Rank Oldbie

This reminds me of a special version of SoftMPU which does a similar thing with virtual MPU ports and was done by a certain user which I don't want to talk about. Unlike that version though, your software has the source code available. 😀

Nice that you added an AdLib delay option there for people that are nostalgic about using FM synth for MIDI music. 😁

Reply 3 of 22, by Kamerat

User metadata
Rank Oldbie

Make the mapping range four didgit so it also can map into the PCI I/O range. Some PCI sound cards can have their MPU-401 interface accessed that way without even initializing the card, see the spreadsheet in my signature for sound cards that allow this.

DOS Sound Blaster compatibility: PCI sound cards vs. PCI chipsets
YouTube channel

Reply 4 of 22, by Gmlb256

User metadata
Rank Oldbie

I've noticed that MIDIto doesn't get loaded if EMM386 has the NOEMS parameter, is EMS required for port-trapping to work? I'm not using just the AUTO parameter.

It would also be interesting to have QEMM support.

Reply 5 of 22, by aitotat

User metadata
Rank Member

Release 2 now extends port range for PCI cards. I didn't know ports that high would be required but I've avoided PCI cards on DOS so no wonder.

I suppose the NOEMS parameter just can't be used then. EMM386 should be better documented. EMS memory does not have anything to do with port trapping but maybe port trapping functionality is just internally part of the EMS code or something. EMM386 can be strange. If you use the AUTO parameter and try to trap ports it will fail. But if you do mem /c /p (or likely mem without parameters is enough) and try port trapping again then it will work. It took me a lot of time to figure out why port trapping didn't work at the beginning.

I don't use QEMM myself but I'll see what I can do.

Reply 8 of 22, by aitotat

User metadata
Rank Member

I didn't see that edit until now but I already found the bug. Now the version check should work properly.

Release 4 adds a new feature: It is now possible to disable Adlib ports completely. Idea is to force CMS support for games that do now allow it if Adlib is found. However, only Adlib-ports are disabled. Next version will also trap FM ports from Sound Blaster address space.

Adlib delay value can now be adjusted and it is zero by default. Zero means only the delay caused by port trapping itself. However I think I should improve it by trapping only Adlib Data port.

The adjustable delay could be better as well. Now it will be added to all Adlib port accesses. I think it would be best if specified delay would be added only after data port write. Other writes should have less delay and reads only minimum possible delay. So next version will be even better.

Reply 9 of 22, by jmarsh

User metadata
Rank Oldbie
aitotat wrote on 2021-09-17, 18:18:

I suppose the NOEMS parameter just can't be used then. EMM386 should be better documented. EMS memory does not have anything to do with port trapping but maybe port trapping functionality is just internally part of the EMS code or something.

It sort of makes sense if you consider that EMM386 only has to care about monitoring port accesses for the purposes of catching DMA transfers to/from the "fake" EMS page frame that is actually remapped XMS memory. If there's no EMS it has no reason to trap any ports.

Reply 10 of 22, by aitotat

User metadata
Rank Member

There was an error in release 4 version information (it is fixed now). I had code to add the user defined delay to data port only but I removed it and forgot to update the TXT that I had already written. The implementation was insufficient so I removed it completely for now.

Reply 11 of 22, by Gmlb256

User metadata
Rank Oldbie
jmarsh wrote on 2021-10-01, 14:16:

It sort of makes sense if you consider that EMM386 only has to care about monitoring port accesses for the purposes of catching DMA transfers to/from the "fake" EMS page frame that is actually remapped XMS memory. If there's no EMS it has no reason to trap any ports.

I was able to get QEMM with NOEMS parameter working with the new version of MIDIto. 😉

And SoftMPU works with EMM386 and EMS disabled, however it still doesn't work with MIDIto though.

Reply 12 of 22, by aitotat

User metadata
Rank Member
jmarsh wrote on 2021-10-01, 14:16:

If there's no EMS it has no reason to trap any ports.

That makes perfect sense. I'm sure that is the reason.

I did a lot of testing with EMM386 and port trapping since it is not so well documented. My findings can be read from ResCode.asm if anyone else is interested in port trapping. I compared EMM386 versions 4.46 (DOS 6.22), 4.50 (PC-DOS 2000) and 4.95 (Windows 9x) and the port trap handler works just the same in all versions. There seems to be no way to identify if 8-, 16- or 32-bit I/O call was made. QEMM API is much better, it even tells if the program had interrupts enabled or disabled when port access was made.

But back to EMM386 versions. 4.50 from PC-DOS 2000 is the only one that can trap 32-bit port accesses. MS EMM will crash, even the latest 4.95. The 95 most likely just means Windows 95 so the version number cannot be directly compared. But for port trapping the EMM386 4.50 from PC-DOS 2000 is the best. That is great since PC-DOS 2000 is my favorite DOS version (I would really like FAT32 support however).

One note about QEMM. I have only tested v7.53 and it has slower port trapping than EMM386. I need to test it more and other versions as well.

Reply 13 of 22, by aitotat

User metadata
Rank Member
Gmlb256 wrote on 2021-10-01, 17:54:

And SoftMPU works with EMM386 and EMS disabled, however it still doesn't work with MIDIto though.

Strange. Maybe I should read SoftMPU sources to see if EMM386 port trapping is setup somewhat differently.

Reply 14 of 22, by aitotat

User metadata
Rank Member

New release (5).

EMM386 can now be used with NOEMS and AUTO parameters and the Adlib delay is much better now and Sound Blaster OPL ports are included.

First I decided it would be enough to just read the SET BLASTER string and determine ports from there. But future releases in mind I decided to implement auto detection. Then I started to think more about the -a=0ff option. It is supposed to disable Adlib ports so some games would not force Adlib instead of allowing to use CMS.

I decided I can do better than that and added -cms option. It emulates the Game Blaster detection chip that the Sound Blaster does not have. Some games require it for CMS support. So basically the -cms option now turns your Sound Blaster 1.x or 2.0 to a Game Blaster (and disables the Sound Blaster DSP because of address conflicts).

Reply 15 of 22, by Dominus

User metadata
Rank DOSBox Moderator
DOSBox Moderator

Even though I'm never gonna use it as I have no old PC anymore... this is an awesome idea (and I guess implementation) of a program!

Windows 3.1x guide for DOSBox
60 seconds guide to DOSBox
DOSBox SVN snapshot for macOS (10.4-11.x ppc/intel 32/64bit) notarized for gatekeeper

Reply 16 of 22, by aitotat

User metadata
Rank Member

Release 6 now brings optimizations to reduce memory usage especially for QEMM (previously EMM386 code needed to be loaded to resident part of the program when QEMM was used). But this release is mainly about mixer locking.

Please, read the first post of this thread or readme included in the zip file for more details. By default the -lock parameter locks Master audio, CD, Line in, MIC and PC speaker (Sound Blaster 16 input) but what is locked or not can be user defined. It is also possible to force enable or disable Sound Blaster pro Low pass filter (Sound Blaster 16 does not have it).

There is one known issue with mixer locking. Star Trek: Judgment Rites requires MIC to be unlocked or it will not play digital sound. I'll need to investigate. Let me know if you find other problematic games. There is a possible problem since reading locked register will return the locked value. So if a game reads back what it wrote and does a comparison there will be a conflict. I suspect that it the case with Judgment Rites. It can be fixed by storing bytes written to locked register and then returning those stored values when register is read. Maybe the MIC just happens to be part of Judgment Rites SB Pro detection code.

Reply 17 of 22, by badmojo

User metadata
Rank l33t

This looks awesome thankyou! That mixer lock interests me in particular - no more .bat files "fixing the mixer" after those games that crank the volume right up.

I'll give it a try.

Life? Don't talk to me about life.

Reply 18 of 22, by aitotat

User metadata
Rank Member

Release 7 has some bug fixes.

First Star Trek: Judgment Rites with mixer locking has been fixed by storing MIC value it writes and then returning the expected value when read. So MIC volume appears to be unlocked but it is actually locked.

Next there is a workaround for Yamaha YMF71x based cards. It has a hardware bug (not sure if all the YMF71x has it) that SB mixer master volume must be set to 1 or the volume controls won't work like supposed to. For example Wolfenstein 3D plays with mono sounds since mixer does not work.

To enable the fix, MIDIto must be started with "-lock=ymf" parameter. This won't lock the mixers, it will just enable the fix. If you want to lock the mixers, use "-lock=ymf,def". This will enable the YMF fix and set mixer locks to defaults (master, FM, CD, line-in and MIC) . You can specify what it locked or not if needed. More info in readme.

How the YMF fix works? It sets the SB master volume to 1 and locks it. Then any accesses to master volume will be directed to another master volume control available at YMF control port. So you will have fix for the hardware bug and working master volume control at the same time. See the readme if you have set YMF to another control port than the default 370h. I believe it is possible to later implement autodetection and maybe set the fix automatically without the ymf-lock but better to test it first since I don't know if all the YMF71x chips really need the fix or not.

Finally I found and fixed a couple of bugs related to uninstall strings.

But then I found a new issue and there is no fix for it, at least not at the moment: For some reason EMM386 port trapping does not work (tested with EMM386 4.95) if I have vide-cdd.sys CD-ROM driver loaded. I don't know the reason. I use pcem to test the code before testing with real hardware but with my pcem setup vide-cdd.sys did not cause any problems. I'll need to test different emm386 versions next since I have no other ideas at the moment.

Reply 19 of 22, by Falcosoft

User metadata
Rank Oldbie

Can you add ESS cards special AuxB mixer source to locked/preserved mixer sources? The problem is that ESS 1868/1869 etc. accepts Sound Blaster compatible mixer resets and after a reset the AuxB volume is 0. So in games such as Gabriel Knight that first sends a reset and then adjusts SB mixer volumes you cannot hear Midi music from attached Waveblaster compatible cards. Here is a topic about the problem:
Re: Gabriel Knight 1 - General Midi/MT-32 Not Working.

According to ESS documentation the AuxB address is 3Ah (page 55 of attached document).

Currently Midito preserves all other volumes (Master/Voice/FM etc.) but AuxB is still reset to 0 by the game.
Thanks in advance!


Website, Facebook, Youtube
Falcosoft Midi Player + Munt VSTi + BassMidi VSTi topic