VOGONS


First post, by EricWinter

User metadata
Rank Newbie
Rank
Newbie

Note on AI use:
I'm Chinese, English is not my first language. This post was originally
written in Chinese and translated with AI assistance (exact translation only,
no content generation).
Regarding the project itself: the reverse engineering, disassembly analysis,
and validation were done manually. AI was used for code translation and
structural assistance, and this is disclosed in the project README.

OK so I've been reverse-engineering NEC's NECOPN.DRV (the Windows 95 OPNA
MIDI driver for the PC-9821) for the past few weeks, and I'm at a point
where I can't figure out what's wrong anymore.

I reimplemented the driver's behavior in Python, and most of it
actually works. The voices sound right, the drum routing is right, the LFO
is right. 56 self-tests pass. But when I play back a real track, some
instruments just disappear at certain points, and it's the same spots every
time. I've compared it against a recording from the real driver, and the
difference is obvious.

What I've tried:
- Reworking the channel allocation logic (seg1:0x0058)
- Different ref_count implementations for key-on
- LFO tick timing (seg3:0x0D59)
- Checking ALG6 mask (found and fixed a real bug there)
- A bunch of other stuff that made zero difference

I don't have a way to trace I/O ports on my setup. NP2's debug build doesn't
have trace. I've tried MAME but couldn't get Win95 running well enough.

If anyone here has a working MAME PC-9821 setup with port tracing, I'd
really appreciate a short trace — like 30-50 writes around one missing note.
Or if you know OPNA well enough to spot what I might have misunderstood,
that would help even more.

Details and audio: https://github.com/EriczjrWinter/necopn-drv-reverse

Thanks.

Reply 1 of 4, by bakemono

User metadata
Rank Oldbie
Rank
Oldbie

Based on the two audio tracks on github, I have to wonder if the instruments that play once and never again are not being keyed-off, and hence can't be keyed-on again. I never heard of NECOPN.DRV before, though now I'm interested to take a look and see what instrument patches it uses. I wonder how it compares to MIDI to VGM translator

What is this comment referring to? "Real hardware is dual OPNA, each using only ch0-2 (6 voices)" What hardware has two YM2608s?

Wreckage reimagined on itch: https://90soft90.itch.io/wreckage-dash

Reply 2 of 4, by EricWinter

User metadata
Rank Newbie
Rank
Newbie
bakemono wrote on 2026-10-07, 23:20:

Based on the two audio tracks on github, I have to wonder if the instruments that play once and never again are not being keyed-off, and hence can't be keyed-on again. I never heard of NECOPN.DRV before, though now I'm interested to take a look and see what instrument patches it uses. I wonder how it compares to MIDI to VGM translator

What is this comment referring to? "Real hardware is dual OPNA, each using only ch0-2 (6 voices)" What hardware has two YM2608s?

Thanks — the first point is very interesting and might be exactly the bug.
If a slot never gets its key-off written, it would stay occupied and later
note-ons would fail to allocate it. That would match the symptom: entire
tracks drop out at fixed positions once something gets stuck.

In my Python side, key_off() only writes 0x28 when a ref_count counter
reaches 0. If a note_off event is dropped or mismatched, ref_count stays
positive and 0x28 never gets written. I'll investigate whether this is
what's happening.

On the second point: you're right, that README line is wrong. The real
hardware is a single YM2608 (OPNA) with 6 FM voices, addressed as two
logical 3-voice units through ports 0x188/0x18A and 0x18C/0x18E. My
Python code uses two YmfmCore instances to model these two port groups,
which is a code structure, not physical hardware. I'll fix the README.

Reply 3 of 4, by EricWinter

User metadata
Rank Newbie
Rank
Newbie
bakemono wrote on 2026-10-07, 23:20:

Based on the two audio tracks on github, I have to wonder if the instruments that play once and never again are not being keyed-off, and hence can't be keyed-on again. I never heard of NECOPN.DRV before, though now I'm interested to take a look and see what instrument patches it uses. I wonder how it compares to MIDI to VGM translator

What is this comment referring to? "Real hardware is dual OPNA, each using only ch0-2 (6 voices)" What hardware has two YM2608s?

AND On what NECOPN.DRV is:

It's NEC's official Windows 95/98 MIDI synthesizer driver for the PC-9821
with the 86 sound board (which carries a Yamaha YM2608 / OPNA chip) When PC-9821 use Windows95/98 with 86 sound board.
Device name in Windows is "MIDI:NEC Synthesizer Driver", version 4.05.00.0129,
dated 1997. It's a 16-bit NE DLL that MMSYSTEM loads as the default MIDI
output device on PC-9821 Windows95 systems.

Reply 4 of 4, by EricWinter

User metadata
Rank Newbie
Rank
Newbie
bakemono wrote on 2026-10-07, 23:20:

Based on the two audio tracks on github, I have to wonder if the instruments that play once and never again are not being keyed-off, and hence can't be keyed-on again. I never heard of NECOPN.DRV before, though now I'm interested to take a look and see what instrument patches it uses. I wonder how it compares to MIDI to VGM translator

What is this comment referring to? "Real hardware is dual OPNA, each using only ch0-2 (6 voices)" What hardware has two YM2608s?

Thanks — I checked this. I added diagnostics to the key-on/key-off/ref_count
path and ran a 7.5-second segment around one of the problem positions
(t=12.3 to t=19.8 in the render).

Result: the key-off path is clean. ref_count stays in {0, 1} on every channel,
every key-on has a matching key-off, 0x28 is written on every key-off, and no
channel gets stuck. There's only one orphan note-off (midi_ch=3 note=63 at
t=12.3) and it's immediately followed by a successful re-allocation. So the
"never keyed off" hypothesis doesn't hold in my implementation — the note
scheduler is fine.

Which means the loss is probably in the voice/register layer, not the
note-on/off layer. The notes are being scheduled and keyed, but some voices
may be writing wrong register values (envelope, TL, etc.) so they don't
actually sound.

That points back to the bit-packing question that Wohlstand mentioned —
Tomsoft GIN format has the same kind of bit-reorder issue in its source
files, and I'm currently re-checking my field extraction against his spec.

If you have any insight into how NECOPN's 64-byte voice record packs its
bits (particularly AR/DR/TL), that would help a lot.