VOGONS


Aweutil /EM option - speed sensitive

Topic actions

Reply 20 of 22, by OttoPS

User metadata
Rank Newbie
Rank
Newbie

Hi there! Sorry for the necroposting! But I bring an explanation and a workaround for the AWEUTIL bug on fast CPUs when using GM or MT-32 emulation.

To explain the bug, it is also necessary to explain how the emulation architecture works, using a mixed hardware/software system...

AWEUTIL uses a very dirty low-level trick: when the sound card receives a data byte through the MIDI port (usually 0x330), it generates an NMI, a non-maskable CPU interrupt. This means it can interrupt normal CPU execution even with maskable interrupts disabled. This way, the card requests that the byte be read as soon as possible.

Inside the NMI handler, the process is fairly simple. It checks whether a byte is available through MIDI base + 1 (usually 0x331), then reads the byte through MIDI base (usually 0x330) and generates the corresponding commands for the EMU8000.

In theory, this should work without problems, but there appears to be a timing bug: the NMI may reach the handler before the MIDI feedback data is available for reading. In fact, looking at the AWEUTIL binary, Creative hardcoded five consecutive attempts to check whether data is available. But this mediocre solution depends on CPU and bus timing.

The problem gets worse because, instead of silently discarding the NMI and potentially losing a MIDI byte, it chains to the original NMI handler. On a typical DOS PC, this is a BIOS hardware-error handler, which may stop the machine.

My software workaround is simple. First, increase the number of attempts from 5 to 50. Second, instead of chaining to the original NMI handler when the attempts run out, silently clear the NMI source, restore the execution context and leave the handler with IRET. Luckily, Creative already provided that last part in its own NMI handler.

This patch does not fix CPU speed problems in the games themselves or compatibility problems with protected-mode games. Solutions for those cases already exist.

The offsets and machine code below apply to the AWEUTIL binary I examined. Check the original bytes before applying the patch to another version.

ORIGINAL DATA

; DX = MIDI base + 1, usually 0x331

in al, dx
test al, 80h
jz 13EFh ; attempt #1: check whether MIDI data is available

in al, dx
test al, 80h
jz 13EFh ; attempt #2

in al, dx
test al, 80h
jz 13EFh ; attempt #3

in al, dx
test al, 80h
jz 13EFh ; attempt #4

in al, dx
test al, 80h
jnz 1441h ; attempt #5: if unavailable, chain to original NMI handler

Binary machine code at file offset 0x12D6:

EC A8 80 74 14 EC A8 80 74 0F EC A8 80 74 0A EC A8 80 74 05 EC A8 80 75 52

PATCH DATA

Assembly at file offset 0x12D6 (runtime offset 0x13D6):

push cx
mov cx, 0032h ; 50 attempts, decimal

wait_midi_data:
in al, dx
test al, 80h
loopnz wait_midi_data

pop cx
jz 13EFh ; data available: process
jmp 1421h ; timeout: clear source, restore context, IRET

Binary machine code at file offset 0x12D6:

51 B9 32 00 EC A8 80 E0 FB 59 74 0D EB 3D

The patch occupies 14 bytes. The remaining original bytes do not need to be overwritten because neither execution path reaches them.

Reply 21 of 22, by Riikcakirds

User metadata
Rank Member
Rank
Member
OttoPS wrote on 2026-10-14, 23:59:

Hi there! Sorry for the necroposting! But I bring an explanation and a workaround for the AWEUTIL bug on fast CPUs when using GM or MT-32 emulation.

OttoPS, I just used your patch on AWEUTIL V1.36 and it worked!. Good work. I didn't need to disable L1 cache. I tested on the game Chips Challenge.
One thing i wasn't quite clear of after reading your info about the problem, does that mean now under certain cases midi notes will simply be dropped and skipped by the music played instead of the previous "I/O CHANNEL CHECK - CHECKING FOR SEGMENT" error message.

Last edited by Riikcakirds on 2026-10-17, 18:16. Edited 1 time in total.

Reply 22 of 22, by OttoPS

User metadata
Rank Newbie
Rank
Newbie

I’m glad to hear it was helpful!

Regarding your question: I have tested this extensively, and not a single MIDI byte has been lost during testing. In fact, the 50-retry limit—used while waiting for the byte that triggered the NMI—is very conservative. That said, in a worst-case scenario, the chip responsible for the transaction (the CT1741) could potentially encounter an issue, causing a MIDI byte to be lost or overwritten by a new one after the NMI has been generated; this is because the data read from port 331h is not buffered in any way on the sound card. However, since that same chip (the CT1741) also manages the protocol for the external MIDI port, any such issue would result in data loss on that interface as well. Therefore, I don’t believe this is likely to happen in practice (if it were a real issue, many people would be complaining about it regarding their integrated MPU-401 interfaces).