VOGONS


PCIe - PCI - ISA Bridge; IT FREAKING WORKS!

Topic actions

First post, by dartfrog

User metadata
Rank Newbie
Rank
Newbie

A place for me to post about my card and driver development without spamming other's threads. I have recently made a huge win. Real data from a PicoGus on Windows 10!

---

For those unaware, I've been working on a weird but promising setup: using an IT8888 PCI-to-ISA bridge behind an IT8893 PCIe-to-PCI bridge, under Windows 10, with the goal of eventually supporting ISA DMA / DDMA-style access for things like PicoGUS. Specifically my goal is to support old CNC ISA cards, which is a much bigger task than something like a GUS.

I have been asked why several times in email, in person, and comments, I will reiterate my reasoning here even though it's explicitly stated above.

My goal is to bridge real ISA hardware to modern Windows. DOSBox/DOSBox-X would run the old DOS software and drivers, while my card and kernel driver let that software talk to the physical ISA card. The real target is legacy industrial hardware, like CNC machines that still rely on DOS-era ISA cards. ISA sound cards are just a practical test case because they are easy to bench test and require broad ISA support. ISA sound cards are not the end goal; they are a test case for a broader ISA-to-modern-Windows bridge. Also because it's fun.

The hardware topology currently looks like this:

Lenovo ThinkCentre M93p / Intel i7-4790 / Chipset Intel Q87
Intel PCIe Root Port
-> ITE IT8893 PCIe-to-PCI bridge (on motherboard)
-> ITE IT8888 PCI-to-ISA bridge (my PCI-ISA card)
-> ISA slot
-> PicoGUS / ISA POST card

The IT8888 is detected by Windows as a PCI device. I wrote a KMDF diagnostic driver and a user-mode tool, "it8888ctl.exe", to poke at PCI config space, IT8888 config registers, port I/O, DDMA registers, traces, and a DMA common buffer.

At first, the IT8888 itself looked alive: PCI config reads worked, the device started, and I could program its positive decode windows. For example, the IT8888 was configured with classic legacy-style I/O decode windows:

cfg58 = e4000220   ; SB-ish 0x220 window
cfg5c = e3000330 ; MPU/GUS-ish 0x330 window
cfg60 = e2000388 ; OPL 0x388 window

But actual ISA I/O reads from "0x220", "0x330", and "0x388" all returned "0xFF". At first I thought maybe the IT8888 was not initialized correctly, or maybe the ISA device was not responding.

The real issue turned out to be upstream bridge forwarding.

I added a "pci-dumpcfg" command to dump arbitrary PCI config space by bus/device/function. That showed the IT8893 bridge and the Intel root port both had their I/O forwarding windows effectively disabled/inverted:

I/O base = 0xf0/f1
I/O limit = 0x00/0x01
decoded as base 0xf000, limit 0x0fff

So even though the IT8888 was configured to decode "0x220", the upstream PCIe/PCI bridge chain was not forwarding those low I/O cycles to it.

I then added raw CF8/CFC PCI config access to the driver, because Windows/HAL config writes were failing for the bridge registers. With that, I could manually open the bridge I/O windows.

For example, opening both bridges to forward "0x8000–0x8FFF" now works:

bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0404 new=0x0405
io base new=0x80
io limit new=0x80

bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base new=0x80
io limit new=0x80

"pci-dumpcfg" then confirms:

I/O window decoded: 0x00008000-0x00008fff
contains 0x8390: YES

I tried two approaches after that.

First, I tried mapping legacy ISA ports into the high forwarded window:

0x220 -> 0x8220
0x330 -> 0x8330
0x388 -> 0x8788

The IT8888 accepted these high decode values:

cfg58 = e4008220
cfg5c = e3008330
cfg60 = e2008788

Port I/O to those addresses now reaches the driver and is forwarded by the bridges, but plain reads from an ISA POST card naturally just return "0xFF" because a POST card mostly cares about writes to port "0x80".

I also tried opening the bridge windows to "0x0000–0x0FFF" for true legacy I/O. That technically works, but it is dangerous and started causing hangs after some commands. That makes sense: forwarding the whole low legacy I/O page through a PCI bridge can collide with chipset/PIC/PIT/DMA/ACPI/VGA legacy regions. So I am avoiding that except for very short controlled tests.

The big breakthrough was with PicoGUS.

PicoGUS has its own management/control protocol at:

control: 0x1D0
data low: 0x1D1
data high: 0x1D2

So I mapped that into the safe high bridge window: IT8888 cfg60 = e20081d0

Then I opened both upstream bridges to "0x8000–0x8FFF" and talked to PicoGUS at "0x81D0–0x81D2".

This worked.

Protocol read:

out 0x81D0 <- 0xCC
out 0x81D0 <- 0x01
in 0x81D2 -> 0x03

Firmware string read:

out 0x81D0 <- 0xCC
out 0x81D0 <- 0x02
in 0x81D2 -> 0x70
in 0x81D2 -> 0x69
in 0x81D2 -> 0x63
in 0x81D2 -> 0x6f
in 0x81D2 -> 0x67
in 0x81D2 -> 0x75
in 0x81D2 -> 0x73
in 0x81D2 -> 0x2d

Which decodes to:

picogus-

This text is actually from the PicoGUS, specifically it's part of the "picogus-gus vX.X.X" text where X.X.X is the version number. pgusinit tells you this text and version number when it detects a PicoGUS. The fact we can see this in Win10 is a major step. After 2 years of messing about, I finally got provable real data from an ISA card on Windows 10 on a modern-ish PC (4th gen chipset Q87).

So the full path is proven:

Windows 10 kernel driver
-> Intel PCIe root port
-> IT8893 PCIe-to-PCI bridge
-> IT8888 PCI-to-ISA bridge
-> ISA bus
-> PicoGUS

This is a pretty big milestone. It means the card is reachable through the bridge chain if the bridge I/O windows and IT8888 decode windows are configured carefully.

Current lessons learned:

The IT8888 was not the only problem.
The upstream IT8893/root-port I/O forwarding windows matter.
Windows does not necessarily allocate/enable legacy I/O forwarding for this bridge chain.
Raw PCI config writes via CF8/CFC were needed to force bridge I/O windows open.
Forwarding 0x0000–0x0FFF is risky and can hang the system.
A safer strategy is forwarding 0x8000–0x8FFF and mapping IT8888 decode windows into that range.
PicoGUS management/control protocol works through a high alias, proven by reading the firmware string.

The next step is to add proper PicoGUS helper commands to "it8888ctl", instead of manually doing byte pokes. Something like:

pgus-protocol
pgus-fwstring
pgus-read <cmd> <count>
pgus-write8 <cmd> <value>
pgus-write16 <cmd> <value>
pgus-get-sb
pgus-set-sb 0x220 irq dma type

After that, the next milestone is configuring PicoGUS into SB mode and testing SB DSP reset through an aliased SB window:

IT8888 cfg58 = e4008220   ; SB alias
IT8888 cfg5c = e3008330 ; MPU alias
IT8888 cfg60 = e2008788 ; OPL alias

Then test:

out 0x8226 <- 1
out 0x8226 <- 0
in 0x822E
in 0x822A

The hoped-for result is the classic SB reset response:

0x822A -> 0xAA

There is still a lot left, like supporting real CNC ISA cards, especially real DMA/DDMA behavior. But at this point I have proven that real ISA hardware behind a PCIe-to-PCI-to-ISA chain can be reached from Windows 10, as long as the bridge and IT8888 decode windows are configured manually.

All my hardware, code, docs, and tests are in the repo in my signature, and specifically for this thread, the folder contains the code and driver in "DartFrogTek/PCIe-PCI-ISA/DFT_VMDA8/it8888vdma_win10_predosbox"

What i'm currently working on:

Stop manually poking bytes and add real it8888ctl PicoGUS helpers, including real DMA/DDMA work.

---

Below is a section to talk about why im doing this on Win10, there is method to the madness.

Why Windows 10? wrote:
I'm doing this under Windows 10 first because it is the harshest and most useful test environment for this project. […]
Show full quote

I'm doing this under Windows 10 first because it is the harshest and most useful test environment for this project.

In DOS or Windows 9x, software can usually hit legacy ISA I/O ports directly and the system firmware/chipset may already have some legacy behavior enabled. That makes it easier to get a sound card or POST card to respond, but it also hides which part of the bridge chain is actually working.

Windows 10 is much stricter. It does not automatically make this PCIe -> PCI -> ISA chain behave like a real 1990s ISA bus. The OS did not give the upstream bridges useful legacy I/O forwarding windows, and normal low ISA ports like `0x220`, `0x330`, and `0x388` were not forwarded to the IT8888 at all. I had to prove and manually configure each layer:

PCI config access works
IT8893/root port I/O forwarding works
IT8888 positive decode works
kernel port I/O reaches the bridge
real ISA hardware responds

That is why Windows 10 is useful here: if I can make the chain work from a modern protected OS with manually configured bridge windows, then I have proven the actual hardware path is functional instead of relying on BIOS magic.

It also makes DOS and older Windows easier to reason about later. Once the correct IT8893 bridge window and IT8888 decode settings are known, I can reproduce those settings from a DOS tool, Windows 9x VxD/WDM driver, or even an option-ROM/init utility. The working Windows 10 driver gives me a known-good register recipe.

So this is not "Windows 10 because I expect Win10 to be the final ISA gaming environment." It is more like a diagnostic proving ground. If PicoGUS answers through this chain on Windows 10, then the same bridge/decode configuration should be portable to DOS or older Windows, where the software stack is actually more compatible with ISA sound hardware.

The current PicoGUS result is exactly that kind of proof:

Windows 10 driver
-> manually opens root port + IT8893 I/O window
-> programs IT8888 decode window
-> performs port I/O
-> PicoGUS returns firmware string bytes: "picogus-"

That means the electrical and bridge path is real. The next job is packaging the same initialization into a cleaner tool/driver so DOS, Win9x, or other older environments can set the bridges up before using the ISA device normally.

---

A small section about possible address-translation fallback via FPGA incase some legacy ISA devices require the IT8888 or ISA bus to see the original low addresses exactly, an FPGA could translate the address phase of PCI transactions:

Possible FPGA address-translation fallback wrote:
One possible fallback is a small FPGA/CPLD interposer that performs address translation on the PCI side before the IT8888 sees t […]
Show full quote

One possible fallback is a small FPGA/CPLD interposer that performs address translation on the PCI side before the IT8888 sees the cycle.

The current software approach is to keep the upstream bridges forwarding a safe high I/O window, such as:

0x8000–0x8FFF

and then program the IT8888 to decode high aliases like:

0x81D0 for PicoGUS control
0x8220 for Sound Blaster
0x8330 for MPU-401
0x8788 for OPL

This already works for PicoGUS control access, so an FPGA is not needed for that part. However, if some legacy ISA devices require the IT8888 or ISA bus to see the original low addresses exactly, an FPGA could translate the address phase of PCI transactions:

0x8220 -> 0x0220
0x8330 -> 0x0330
0x8788 -> 0x0388

while leaving data phases unchanged.

That would let the host and upstream bridges use a safe high I/O window, while the IT8888/ISA side sees traditional legacy addresses. This is not the first choice because modifying PCI address/data lines in real time is timing-sensitive and should be done with proper glue logic, not a microcontroller bit-bang hack. A small CPLD/FPGA would be the right tool if it becomes necessary.

For now, the better result is that PicoGUS has already responded through a pure software-configured high alias. The FPGA idea remains a fallback only if certain ISA devices cannot tolerate high-address aliasing or if real SB/DMA behavior requires exact low-address presentation.

Last edited by dartfrog on 2026-05-22, 11:42. Edited 2 times in total.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 1 of 24, by weedeewee

User metadata
Rank l33t
Rank
l33t

Very nice. Good work.
A question about the bridge window. While setting it from 0x0 to 0xFFF is indeed problematic. Could it be set from 0x200 to 0xFFF ?

Right to repair is fundamental. You own it, you're allowed to fix it.
How To Ask Questions The Smart Way
Do not ask Why !
https://www.vogonswiki.com/index.php/Serial_port

Reply 2 of 24, by rasteri

User metadata
Rank Oldbie
Rank
Oldbie

Wow neat!

As you say DMA may be a challenge, but here's hoping!

Reply 3 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie
weedeewee wrote on 2026-05-17, 15:52:

Very nice. Good work.
A question about the bridge window. While setting it from 0x0 to 0xFFF is indeed problematic. Could it be set from 0x200 to 0xFFF ?

Thanks! Unfortunately, no, not with the normal Type-1 PCI bridge I/O window. The bridge I/O base/limit registers are 4 KB granular. The register only carries address bits 15:12; the lower 12 bits are implied. Roughly:

base  = (IO_BASE  & 0xF0) << 8;
limit = ((IO_LIMIT & 0xF0) << 8) | 0x0FFF;

So if the window needs to include 0x220, the smallest normal bridge I/O window is: 0x0000-0x0FFF. You can't express 0x0200-0x0FFF with the standard bridge I/O base/limit registers.

Here's a useful reference for PCI configuration space / Type 1 bridge headers for anyone interested.
https://web.archive.org/web/20260412184036/ht … tlk/dd/pci.html

rasteri wrote on 2026-05-17, 17:43:

Wow neat!

As you say DMA may be a challenge, but here's hoping!

Neat indeed! Thanks again for your work. We are so very close now. The encouraging part is the bridge I/O forwarding and PicoGUS control path working. I still need to finish the driver-side DMA/DDMA path and test whether the IT8888/PicoGUS handshake behaves the way we want, but if the industrial motherboards containing IT8888's are any evidence, I would say there's no real unsolved problems left. There is also a possibility that IRQ works differently from I/O and DMA paths, but I don't see either DMA or IRQ paths as major problems, just hurdles to jump now. My current expectation is that normal I/O should keep the IT8888 acting as a PCI target/slave, while DMA/DDMA will require the IT8888/DDMA engine to become a PCI bus master only during the actual transfer. IRQ may also have its own quirks. So I would not call those completely solved yet, but the fact that real ISA hardware is responding is a very good sign.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 4 of 24, by EduBat

User metadata
Rank Member
Rank
Member
dartfrog wrote on 2026-05-17, 10:05:

Then I opened both upstream bridges to "0x8000–0x8FFF" and talked to PicoGUS at "0x81D0–0x81D2".

This worked.

Awesome work. This is a great discovery, it means that the sound card is ignoring bit A15 of the I/O access address and responding to accesses to it.
Can't wait for more developments...

Reply 5 of 24, by NeoG_

User metadata
Rank Oldbie
Rank
Oldbie
EduBat wrote on 2026-05-17, 22:56:

Awesome work. This is a great discovery, it means that the sound card is ignoring bit A15 of the I/O access address and responding to accesses to it.
Can't wait for more developments...

My understanding is that the card interacts on 0x1D0 natively via the ISA bus, the bridge is translating the addresses bi-directionally into and from the 0x8000 range. So only the controller software side (currently being done with primitive reads/writes) needs to be on 0x8000.

98/DOS Rig: BabyAT AladdinV, K6-2+/550, V3 2000, 128MB PC100, 20GB HDD, 128GB SD2IDE, SB Live!, SB16-SCSI, PicoGUS, WP32 McCake, iNFRA CD, ZIP100
XP Rig: Lian Li PC-10 ATX, Gigabyte X38-DQ6, Core2Duo E6850, ATi HD5870, 2GB DDR2, 2TB HDD, X-Fi XtremeGamer

Reply 6 of 24, by EduBat

User metadata
Rank Member
Rank
Member

We are both describing something that is also known as I/O aliasing (see attached, chapter 3.1)

The attachment 318244.pdf is no longer available

Also, I hope this cheat sheet is useful. I did this some time ago as a fun exercise, it may contain errors...

The attachment DMA.txt is no longer available

Reply 7 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie
EduBat wrote on 2026-05-17, 22:56:

Awesome work. This is a great discovery, it means that the sound card is ignoring bit A15 of the I/O access address and responding to accesses to it.
Can't wait for more developments...

NeoG_ wrote on 2026-05-17, 23:30:

My understanding is that the card interacts on 0x1D0 natively via the ISA bus, the bridge is translating the addresses bi-directionally into and from the 0x8000 range. So only the controller software side (currently being done with primitive reads/writes) needs to be on 0x8000.

EduBat wrote on 2026-05-18, 00:55:

We are both describing something that is also known as I/O aliasing (see attached, chapter 3.1)

I think both descriptions are pointing at the same practical effect, but I want to be careful about where the claim for aliasing is happening. What I have proven so far is:

host I/O 0x81D0/0x81D2
-> Intel root port forwards 0x8000-0x8FFF
-> IT8893 forwards 0x8000-0x8FFF
-> IT8888 positive-decodes 0x81D0
-> PicoGUS answers with:
CMD_PROTOCOL -> 0x03
CMD_FWSTRING -> "picogus-"

We know this high window path is working. Whether that means "PicoGUS ignores A15" specifically, or whether the IT8888 is effectively translating/truncating the ISA-side address, I do not want to claim either without probing the ISA address lines with a logic analyzer. However functionally it behaves like I/O aliasing: the Windows side software can talk to a high alias in 0x8000-0x8FFF and still reach the legacy PicoGUS control path.

The Intel paper's section on I/O aliasing is relevant here: many ISA devices only decode a limited number of I/O address bits, so upper address bits become "don't care bits" and the same device can answer at multiple aliases. The practical result is that I do not need to forward the dangerous 0x0000-0x0FFF bridge window for this path. I can keep the bridges forwarding 0x8000-0x8FFF and use IT8888 decode aliases like 0x81D0, 0x8220, 0x8330, 0x8788.

Personally I'd avoid saying "the bridge is translating bidirectionally into and from the 0x8000 range" unless by "bridge" you mean the whole root port/IT8893/IT8888 chain. A normal PCI to PCI bridge is not translating the port number, it is forwarding a window. The main take away is that the IT8888 decode plus ISA side aliasing is what makes the high address useful.

There might be some problems with I/O aliasing though, if an ISA device does actually decode upper address bits, then that FPGA workaround might be needed for that kind of card. I suspect some devices might behave differently than what is the letter of the spec and datasheets actually say

---

Some more info on the IRQ and DMA side, we are seemingly covered by some of the most common PCIe-PCI bridges and the IT8888 PCI -> ISA bridge

IRQ:
mostly yes, translated up through PCI INTx -> PCIe INTx messages, assuming IT8888 IRQ routing is configured.

DMA/DDMA:
yes in principle, but only after IT8888 DDMA is explicitly armed and a real ISA side DRQ/DACK transaction happens. Specifically IT8893 can forward the resulting PCI bus-master traffic. This is also true for other PCIe-PCI bridges, but i have no idea about all of them, just the ones I read their docs for seem to agree it's possible, so that's a good sign.

The bridge chip is designed to carry downstream PCI master transactions upstream. That is exactly the kind of mechanism DDMA needs. The part that is not automatic is IT8888 DDMA. We still have to configure/arm the IT8888 DDMA windows and channel behavior. My current driver already has the IT8888 command register, I/O, memory, and bus-master enable set (cmd/status 0x02800007), and the DDMA base windows are programmed at 0x8380, 0x8390, 0x83a0, etc. But bus-master enable just gives permission; it does not start a transfer by itself.

This is what I'm working on atm, get/force the ISA side to initiate a transfer. I'm optimistic it will just work because the glue/configuration layer is all done in my kernel level driver and it8888ctl.exe. Which is why KYA's whole idea on VirtualDMA was such a ground breaking idea for me. By both using something like VDMA and keeping normal I/O target/slave based, while only enabling bus mastering for DDMA if/when the DDMA engine needs it. It's allowed me to rebuild the missing machinery virtually. I vaguely remember us talking about something similar to VDMA/kernel driver in rasteri's thread, but IIRC I dismissed it too early because I had not yet discovered/accounted for the I/O aliasing effect and also misunderstanding the nature of master/slave on the IT8888. You both are right to focus on the aliasing effect, because without it I am now certain the high window approach would not be possible without the extra FPGA hardware. Keeping the FPGA out of the chain is a real desirable goal as any timing addition might make issues and a soundcard will be first to sound the problems, pun intended.

---

One minor note: I have gotten a question in my email about what the folder name "it8888vdma_win10_predosbox" means, specifically what does "predosbox" mean in the repo. Well, if this approach works fully, I will move on to try and patch DOSBOX to use real ISA hardware through my driver. Which is what will be required for my goal of actually supporting old CNC ISA cards on a modern OS, since most drivers/software do not exist past DOS. Obviously this would mean you too could use your ISA cards with DOSBOX. That's my end goal: Real ISA hardware communicating with DOSBOX on Win10+.

---

Anyways I'll get back to work, as I'm sure some of you are just screaming at your monitor: "GOSH DARN IT, DF! JUST TELL ME ALREADY! DOES THE GUS PLAY DOOM MUSIC YET??!?!"

I will try my best making this work. I have not and will not rest until every possible avenue is explored.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 8 of 24, by NeoG_

User metadata
Rank Oldbie
Rank
Oldbie
dartfrog wrote on 2026-05-18, 04:11:

Personally I'd avoid saying "the bridge is translating bidirectionally into and from the 0x8000 range" unless by "bridge" you mean the whole root port/IT8893/IT8888 chain.

Yes I was inaccurately referring to the steps in between as the bridge - Logically truncation can only work in one direction, once the data has to return via the same path the alias needs to be added. So something in the chain is actively managing the process of going between 0x8000 and the original addresses on the ISA bus side which is why I would lean on calling it translation.

98/DOS Rig: BabyAT AladdinV, K6-2+/550, V3 2000, 128MB PC100, 20GB HDD, 128GB SD2IDE, SB Live!, SB16-SCSI, PicoGUS, WP32 McCake, iNFRA CD, ZIP100
XP Rig: Lian Li PC-10 ATX, Gigabyte X38-DQ6, Core2Duo E6850, ATi HD5870, 2GB DDR2, 2TB HDD, X-Fi XtremeGamer

Reply 9 of 24, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie
NeoG_ wrote on 2026-05-18, 04:31:

So something in the chain is actively managing the process of going between 0x8000 and the original addresses on the ISA bus side which is why I would lean on calling it translation.

Most ISA cards only look at the lowest 10 bits of the I/O addresses. There's no "translation" as such, using the same ports on an authentic ISA bus would work the same.

Reply 10 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie

A quick update: I fixed a few things and now I get a full version number from my it8888ctl.exe tool.

.\it8888ctl.exe pgus-fwstring 32

pgus string cmd=0x02 ctrl=0x81d0 count=32
hex: 70 69 63 6f 67 75 73 2d 67 75 73 20 76 34 2e 31 2e 31 00
ascii: picogus-gus v4.1.1.

Now it fully responds with the entire "picogus-gus v4.1.1." string (also just updated the firmware on it)

This included a bunch of stability fixes for the kernel driver. If you've never done kernel driver work, you can imagine how annoying it is to reboot constantly from random total freezes and BSOD, 🤣.

---

I also got the GUS side DMA control register to react. The PicoGUS accepted the GUS DMA command and changed the DMA control /status state.

armed=1 ch=1 dir=2 base=8390 logical=0xdcf1e000 count=4096 flags=000002c0 cmd=08 mode=49 status=00 pci=0280 complete=0 errors=0

GUS DMA control 0x41 = 0x00
GUS timer control 0x45 = 0x00
# zero on these are exactly the idle state we want

cfg5C = e3008340 # GUS +0x100/global register window
cfg60 = e2008240 # GUS base window

gus-reg-probe 0x8240 -> scratch=0xdd OK
gus-reg-reset-safe 0x8240 -> master-reset-readback=0x01 OK

My DDMA side armed cleanly though, GUS reg 0x41 reacted to the write, changing from 0x00 to 0x40, but DDMA stayed complete=0, so the GUS side DMA setup is incomplete. The IT8888 did not complete anything yet. It may be setting an internal DMA-active/status bit or error/status bit, but not asserting DRQ. This is not a big problem, as I need more code on my side to fully force a DMA, or rather force it in the right way. Here's what is likely what I have to do now

GUS DMA address register 0x42 needs full/word style programming, not just low byte 0x00.
GUS DMA control 0x41 likely needs a different bit pattern.
PicoGUS GUS DMA channel expectation may not match IT8888 DDMA ch1 behavior yet.
The IT8888 DDMA register window is armed, but the GUS firmware may not be asserting ISA DRQ because GUS side DMA setup is incomplete.

I need to add the proper 16bit GUS reg write via a new "gus-reg-write16/read16" and "gus-dma-kick/sweep" commands to my it8888ctl.exe tool, so the DMA setup is not half byte only. I need to look at DOSBOX-X and look at it's GUS stuff and see how it does IRQ/DMA on GUS and verify i'm doing things correctly. As im unsure how GUS works exactly, does it assert ISA DRQ from just register pokes? I think I need a full DRAM DMA sequence. I'll have to try and find that sequence somewhere.

However this is a good result. This was quite a bit of research and work to get here from the original post.

---

EDIT!!! I got the ctrl=0x00 to change to ctrl=0x01 so it's changing the DDMA state enough that unrelated DDMA window reads hang. This is super useful as it means the PicoGUS is doing something DMA-like but my tool is looking at the wrong DDMA window and killing the machine. SUPER SUPER EXCITING!

EDIT2!!!: The new dma-like pic : the GUS side did not just accept a register write. Its DMA address advanced from 0x0000 to 0x00ff, and the DDMA window changed state. ddma-status still shows complete=0 because the driver is probably waiting for an IRQ/completion path that is not firing, but the hardware state changed.

gus-dma-kick ...
addr_before=0x0000 ctrl_before=0x00
addr_after =0x00ff ctrl_after =0x40

So after ctrl=0x01, the PicoGUS GUS DMA address advanced to 0x00ff. That strongly suggests the PicoGUS did consume a DMA burst or at least ran its DMA state machine. The IT8888 driver did not report complete, but the GUS-side state moved. Also this changed:

before: ddma-ch1 cmd/status=0x00 request=0x00 mode=0x00 mask=0x00
after : ddma-ch1 cmd/status=0xff request=0x00 mode=0x00 mask=0x0f

So ch1 is definitely being affected after the GUS DMA kick. I'm getting a lot of hangs now, messing with DMA.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 11 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie

Sorry for the bad quality, however I present to you: PCM Audio.

https://www.youtube.com/watch?v=vemvmwXZD8I

If you don't care about anything but hearing it play a clip of PCM DOOM music, skip to 0:32 seconds.

Still working on DMA/DDMA/IRQ. Soon...soon.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 12 of 24, by rasteri

User metadata
Rank Oldbie
Rank
Oldbie

Ha, super cool!

Reply 13 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie
rasteri wrote on 2026-05-19, 21:07:

Ha, super cool!

Yeah! Now I just need to figure out the proper DDMA communication sequence and we should be golden.

The PCM audio test was a nice break from fighting the DMA hangs/crashes, but it was also a big sanity check. I had promising data going back and forth in the console, but I was still nervous the hardware path was not really doing anything useful. Sending a converted WAV to the PicoGUS and hearing it play correctly (at least correct enough) totally put that concern to rest.

I spent all last night getting the PCM side working. The main trick was getting the GF1 sequence/config right, making sure the PicoGUS was in the correct nonmirrored memory mode, and using xor80. Without that it was either garbage noise or terrible audio quality. I could keep polishing CPU driven PCM streaming and faster memory blanking/loading, but DDMA/IRQ is the real target now. Time to man up and deal with the hangs/crashes, haha.

---

While this PCM audio tangent was super fun to me. DDMA is still a separate beast. Port IO proves the card can be talked to, but DMA adds the 8237/DDMA handshake, IRQ routing, transfer timing, and bridge specific sequencing. What I'm working on:

  • 8237 style DMA request/ack behavior
  • DDMA register programming sequence
  • IRQ routing / acknowledgement
  • Memory window or transfer address semantics
  • Timing expectations
  • Bridge specific DMA enable bits
  • Potential bus-master / subtractive decode weirdness

A lot of that should happen on the IT8888 side, which is exactly why the working port I/O test is encouraging.

So roughly here's the full sequence delineation for anyone curious:

ISA card side:

  • asserts DRQ
  • waits for DACK
  • transfers byte/word data
  • may raise IRQ when done
  • behaves like it is on an old ISA bus

IT8888 / bridge side:

  • sees ISA DRQ/DACK-style activity
  • maps that to DDMA / PCI-side behavior
  • performs or coordinates memory movement
  • handles DMA channel config semantics
  • routes/bridges IRQ behavior
  • exposes config/control registers to host driver

Windows driver / host side:

  • programs the bridge
  • allocates/maps suitable buffers
  • sets up DDMA registers
  • enables decode/routing
  • services IRQ/completion

I've already done a bunch of ground work above, so really it's just the right incantation needed without leaving the bridge or ISA card stuck waiting on a DMA state that never completes.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 14 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie
DDMA WORKS!!!! Read the console output if you want:

PS C:\Users\Neptune\Documents\GitHub\PCIe-PCI-ISA\DFT_VMDA8\it8888vdma_win10_predosbox> powershell -ExecutionPolicy Bypass -File .\gus_ddma_upload_4k_reinit_safe_v3.ps1 `
>> -Exe .\dist\Debug_x64\it8888ctl.exe `
>> -Base 0x8240 `
>> -Wav .\test.wav `
>> -StartDram 0x000100 `
>> -TotalSamples 32768 `
>> -ChunkSamples 4096 `
>> -Gain 400 `
>> -Freq 0x03c0 `
>> -PlayWholeRegion `
>> -PlayMsWhole 3000
============================================================
GUS DDMA 4KiB reinit-safe uploader/player v3
Exe: .\dist\Debug_x64\it8888ctl.exe
Base: 0x8240
WAV: .\test.wav
StartDram: 0x000100
TotalSamples: 32768
ChunkSamples: 4096
Chunks: 8
Gain: 400
Freq: 0x03c0
DmaCtrl: 0x01
SettleMs: 1500
PlayWholeRegion: True
PlayMsWhole: 3000
PlayEachChunk: False
PlayMs/chunk: 140
============================================================

============================================================
INITIAL BRIDGE SETUP
============================================================

=== INITIAL BRIDGE SETUP : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== INITIAL BRIDGE SETUP : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== INITIAL BRIDGE SETUP : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== INITIAL BRIDGE SETUP : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== INITIAL BRIDGE SETUP : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DMA FREE ===
.\dist\Debug_x64\it8888ctl.exe dma-free

=== DMA ALLOC LOW ===
.\dist\Debug_x64\it8888ctl.exe dma-alloc-low 65536
LOW buf 1 size 65536 logical 0xff0000 kva 0xffff9500ce328000 OK<16M

=== DDMA CLEAR BEFORE CHUNK 0 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 0 src=0 dram=0x000100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x000100 0 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x000100 src_off=0 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=87bce7ba first bytes: fc fc fc fc fc f8 f8 00 04 0c 10 f8 d4 b4 b8 ec
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=87bce7ba bad=0 first bytes: fc fc fc fc fc f8 f8 00 04 0c 10 f8 d4 b4 b8 ec
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x000100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 1
============================================================

=== RE-INIT BEFORE CHUNK 1 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 1 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 1 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 1 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 1 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 1 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 1 src=4096 dram=0x001100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x001100 4096 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x001100 src_off=4096 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=0e4b2eab first bytes: c4 d4 e0 e4 d8 cc d0 dc e0 e0 dc d4 c8 cc d8 e8
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=0e4b2eab bad=0 first bytes: c4 d4 e0 e4 d8 cc d0 dc e0 e0 dc d4 c8 cc d8 e8
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x001100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 2
============================================================

=== RE-INIT BEFORE CHUNK 2 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 2 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 2 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 2 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 2 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 2 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 2 src=8192 dram=0x002100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x002100 8192 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x002100 src_off=8192 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=523df25b first bytes: fc fc fc f4 f0 f4 f8 f8 fc 00 04 00 00 04 0c 0c
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=523df25b bad=0 first bytes: fc fc fc f4 f0 f4 f8 f8 fc 00 04 00 00 04 0c 0c
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x002100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 3
============================================================

=== RE-INIT BEFORE CHUNK 3 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 3 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 3 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 3 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 3 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 3 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 3 src=12288 dram=0x003100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x003100 12288 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x003100 src_off=12288 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=0a675c25 first bytes: e0 e0 e0 e4 e8 e8 ec f0 f0 f4 f8 00 04 08 0c 10
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=0a675c25 bad=0 first bytes: e0 e0 e0 e4 e8 e8 ec f0 f0 f4 f8 00 04 08 0c 10
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x003100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 4
============================================================

=== RE-INIT BEFORE CHUNK 4 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 4 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 4 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 4 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 4 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 4 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 4 src=16384 dram=0x004100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x004100 16384 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x004100 src_off=16384 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=cc7f41e1 first bytes: b8 bc bc bc bc b8 b4 b0 ac ac ac ac b0 b0 b0 b4
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=cc7f41e1 bad=0 first bytes: b8 bc bc bc bc b8 b4 b0 ac ac ac ac b0 b0 b0 b4
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x004100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 5
============================================================

=== RE-INIT BEFORE CHUNK 5 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 5 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 5 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 5 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 5 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 5 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 5 src=20480 dram=0x005100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x005100 20480 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x005100 src_off=20480 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=86eb8605 first bytes: f8 fc fc fc 00 00 04 04 08 08 0c 0c 0c 0c 0c 0c
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=86eb8605 bad=0 first bytes: f8 fc fc fc 00 00 04 04 08 08 0c 0c 0c 0c 0c 0c
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x005100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 6
============================================================

=== RE-INIT BEFORE CHUNK 6 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 6 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 6 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 6 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 6 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 6 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 6 src=24576 dram=0x006100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x006100 24576 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x006100 src_off=24576 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=04d60f55 first bytes: f4 f4 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=04d60f55 bad=0 first bytes: f4 f4 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x006100 bytes=4096

============================================================
RE-INIT BEFORE CHUNK 7
============================================================

=== RE-INIT BEFORE CHUNK 7 : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE CHUNK 7 : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 7 : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE CHUNK 7 : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE CHUNK 7 : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

=== DDMA CLEAR BEFORE CHUNK 7 ===
.\dist\Debug_x64\it8888ctl.exe ddma-clear

=== UPLOAD CHUNK 7 src=28672 dram=0x007100 count=4096 ===
.\dist\Debug_x64\it8888ctl.exe gus-ddma-wav-load-low-safe 0x8240 .\test.wav 0x007100 28672 4096 400 0x03c0 0x01 1500 0
gus-ddma-wav-load-low-safe base=0x8240 wav=.\test.wav dram=0x007100 src_off=28672 samples_req=4096 gain=400 freq=0x03c0 ctrl=0x01 settle=1500ms play_ms=0
dma-info: logical=0xff0000 size=65536 kva=0xffff9500ce328000
wav: rate=44100 channels=1 bits=8 data=477141 block_align=1
encoded samples=4096 hash=c21b3b45 first bytes: 24 24 20 20 20 1c 1c 18 14 0c 08 00 f8 f4 ec e8
host low DMA buffer written with encoded WAV bytes
skipping target preclear; final exact DDMA verify is authoritative
ddma armed ch=1 dir=2 base=0x8390 logical=0xff0000 count=4096 cmd=0x00 mode=0x49 flags=0x00000280
kicking GUS DMA write-only...
verify attempt 1: hash=c21b3b45 bad=0 first bytes: 24 24 20 20 20 1c 1c 18 14 0c 08 00 f8 f4 ec e8
verify OK total_bad=0 bytes=4096 attempts=1
loaded only; pass play_ms > 0 to audition from dram=0x007100 bytes=4096

============================================================
DUMP CHUNK STARTS
============================================================

=== DUMP CHUNK 0 dram=0x000100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x000100 64
gus-dram-dump base=0x8240 addr=0x000100 count=64
000100: fc fc fc fc fc f8 f8 00 04 0c 10 f8 d4 b4 b8 ec |................|
000110: 2c 44 48 4c 30 08 20 54 4c 28 24 24 30 58 74 78 |,DHL0. TL($$0Xtx|
000120: 78 54 1c fc 20 6c 7f 60 4c 78 7f 7f 7f 7f 6c 28 |xT.. l.`Lx....l(|
000130: f8 ec e4 ec 18 2c 0c ec e8 ec f8 f8 04 0c 10 24 |.....,.........$|

=== DUMP CHUNK 1 dram=0x001100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x001100 64
gus-dram-dump base=0x8240 addr=0x001100 count=64
001100: c4 d4 e0 e4 d8 cc d0 dc e0 e0 dc d4 c8 cc d8 e8 |................|
001110: ec e8 dc d4 e4 f0 e8 e4 ec ec e8 e8 ec f0 e8 e4 |................|
001120: f0 fc f0 d8 dc ec e8 dc e0 ec ec d8 d0 e4 ec dc |................|
001130: d4 e8 f4 f0 f0 e4 d8 d4 d8 e0 ec e8 d8 c8 c8 cc |................|

=== DUMP CHUNK 2 dram=0x002100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x002100 64
gus-dram-dump base=0x8240 addr=0x002100 count=64
002100: fc fc fc f4 f0 f4 f8 f8 fc 00 04 00 00 04 0c 0c |................|
002110: 10 14 14 10 0c 08 04 04 00 00 04 0c 10 14 10 10 |................|
002120: 18 20 24 24 24 24 24 20 24 28 28 28 2c 2c 24 24 |. $$$$$ $(((,,$$|
002130: 28 28 24 20 28 30 34 2c 28 28 28 28 2c 34 34 30 |(($ (04,((((,440|

=== DUMP CHUNK 3 dram=0x003100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x003100 64
gus-dram-dump base=0x8240 addr=0x003100 count=64
003100: e0 e0 e0 e4 e8 e8 ec f0 f0 f4 f8 00 04 08 0c 10 |................|
003110: 0c 08 04 04 00 fc fc fc f8 f8 f8 f8 f8 fc fc 00 |................|
003120: 00 fc fc fc fc 00 04 04 04 00 04 08 08 08 0c 0c |................|
003130: 0c 08 08 08 04 00 00 fc fc fc 00 04 08 0c 14 18 |................|

=== DUMP CHUNK 4 dram=0x004100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x004100 64
gus-dram-dump base=0x8240 addr=0x004100 count=64
004100: b8 bc bc bc bc b8 b4 b0 ac ac ac ac b0 b0 b0 b4 |................|
004110: b4 b4 b8 b8 bc c0 c8 d0 dc e4 ec f4 f8 f8 f8 f8 |................|
004120: fc fc 00 04 08 0c 0c 04 00 fc f4 f4 f4 f4 f4 f4 |................|
004130: f4 f4 f0 e8 e4 e4 e0 dc dc dc dc d8 d8 d4 d4 d0 |................|

=== DUMP CHUNK 5 dram=0x005100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x005100 64
gus-dram-dump base=0x8240 addr=0x005100 count=64
005100: f8 fc fc fc 00 00 04 04 08 08 0c 0c 0c 0c 0c 0c |................|
005110: 08 08 08 08 04 04 04 04 04 04 04 04 08 08 08 08 |................|
005120: 08 08 04 04 04 04 04 04 04 04 04 04 08 08 08 08 |................|
005130: 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c |................|

=== DUMP CHUNK 6 dram=0x006100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x006100 64
gus-dram-dump base=0x8240 addr=0x006100 count=64
006100: f4 f4 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 |................|
006110: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f4 |................|
006120: f4 f4 f4 f4 f4 f0 f0 f0 f0 f4 f4 f0 f0 f0 f0 f0 |................|
006130: f0 f0 ec ec ec ec ec ec ec ec ec ec ec ec e8 ec |................|

=== DUMP CHUNK 7 dram=0x007100 ===
.\dist\Debug_x64\it8888ctl.exe gus-dram-dump 0x8240 0x007100 64
gus-dram-dump base=0x8240 addr=0x007100 count=64
007100: 24 24 20 20 20 1c 1c 18 14 0c 08 00 f8 f4 ec e8 |$$ ...........|
007110: e8 e4 e4 e4 e8 e8 e8 e8 e4 e4 e0 e0 dc d8 d4 d4 |................|
007120: d0 d0 d0 d0 d0 d0 d0 d0 d4 d4 d4 d4 d0 d0 d0 d0 |................|
007130: d0 d4 d4 d4 d4 d8 d8 dc e0 e4 ec f0 f8 fc 00 04 |................|

============================================================
RE-INIT BEFORE PLAYBACK
============================================================

=== RE-INIT BEFORE PLAYBACK : init ===
.\dist\Debug_x64\it8888ctl.exe init

=== RE-INIT BEFORE PLAYBACK : root bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 0 28 3 0x8000 0x8fff
bridge-iowin 0:28.3 0x8000-0x8fff
command old=0x0405 new=0x0405
io base old=0x80 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x80 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE PLAYBACK : it8888 bridge iowin ===
.\dist\Debug_x64\it8888ctl.exe bridge-iowin 3 0 0 0x8000 0x8fff
bridge-iowin 3:0.0 0x8000-0x8fff
command old=0x0407 new=0x0407
io base old=0x81 upper=0x0000 new=0x80 upper=0x0000
io limit old=0x81 upper=0x0000 new=0x80 upper=0x0000
status=3320

=== RE-INIT BEFORE PLAYBACK : cfgwrite 5C ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x5C 4 0xe3008340

=== RE-INIT BEFORE PLAYBACK : cfgwrite 60 ===
.\dist\Debug_x64\it8888ctl.exe cfgwrite 0x60 4 0xe2008240

============================================================
PLAY WHOLE LOADED REGION
============================================================

=== PLAY WHOLE REGION dram=0x000100 bytes=32768 ===
.\dist\Debug_x64\it8888ctl.exe gus-play-loaded-region-safe 0x8240 0x000100 32768 0x03c0 3000
gus-play-loaded-region-safe base=0x8240 dram=0x000100 bytes=32768 freq=0x03c0 play_ms=3000
playing DDMA-loaded region using known-good GF1 voice path: dram=0x000100 count=32768 freq=0x03c0 play_ms=3000
gus-enable-dac reset-readback=0x07
GF1 addr pack: start byte=0x000100 -> 0002:0000, end byte=0x0080ff -> 0101:fe00
auto-stopped voice 0

============================================================
DONE
============================================================

Yep, DDMA works. It's a bit slow for loading, but when it finally loads (lots of safety/analysis on my side), the test.wav (a small section of e1m1) plays correctly. You can just keep playing the pre-loaded ram so yeah it's working well. The only thing it is not doing yet is live MIDI synthesis on the GUS; where .mid note events load patches and trigger GF1 voices in real time. That's a bit more complicated than loading WAV into memory and playing it back, but I can work on it in the near future. I'm sure everyone wants to hear the full e1m1. I think I need a well needed break for a bit. Finally though it's proven pathway.

There is still a ton of work todo. But it freaking works.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 15 of 24, by rasteri

User metadata
Rank Oldbie
Rank
Oldbie
dartfrog wrote on 2026-05-22, 05:41:

Yep, DDMA works. It's a bit slow for loading, but when it finally loads (lots of safety/analysis on my side), the test.wav (a small section of e1m1) plays correctly. You can just keep playing the pre-loaded ram so yeah it's working well. The only thing it is not doing yet is live MIDI synthesis on the GUS; where .mid note events load patches and trigger GF1 voices in real time. That's a bit more complicated than loading WAV into memory and playing it back, but I can work on it in the near future. I'm sure everyone wants to hear the full e1m1. I think I need a well needed break for a bit. Finally though it's proven pathway.

There is still a ton of work todo. But it freaking works.

Holy crap!!

How easy is it going to be to get this running in DOS?

Reply 16 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie
rasteri wrote on 2026-05-22, 08:16:

Holy crap!!

I know. I never would have thought I would get this far this fast.

rasteri wrote on 2026-05-22, 08:16:

How easy is it going to be to get this running in DOS?

I'll go over each OS in case anyone is curious.

For port I/O, easy as pie on everything from DOS through Win95/98 to 2000/XP. In fact, since a lot of ISA cards do not even need DDMA/IRQ, it is mostly just configuration of each bridge.

For DDMA/IRQ, same idea, just slightly harder because each platform wants a different driver model: DOS utility/TSR, VxD for 95/98, WDM for 2000/XP, KMDF/WDM for modern NT. But that is "harder" in the sense of needing platform specific driver knowledge.

Funnily enough, DOS is probably the easiest one for DDMA/IRQ. Since the port I/O path works and the DDMA path is now proven, all DOS really needs is a small hardware utility rather than a complex driver. Basically it's just a direct PCI config, direct in/out port access, low memory DMA buffer, configure the IT8888, program the ISA card if needed, done.

Also, since DOS already has drivers for ISA hardware, my Win10 driver code is mostly useful as a reference for the broad structure and register sequence. The actual DOS side should be much, much simpler.

XP/2000 would be a full WDM rewrite of the current Win10 driver logic. Not conceptually hard, but still a total rewrite.

95/98 is probably the weirdest one because of VxD/WDM era differences, but VxD is also some of my favorite kind of work, so I’d call it easy/medium for me.

So yeah, DOS should probably be the easiest target.

Win10 is by far the most complex target because you have to deal with the modern Windows driver stack, memory/DMA rules, signing, and all the extra safety layers. The full chain working here with a modern KMDF driver is probably the most shocking development, honestly.

---

The DOSBox/DOSBox-X layer is probably the trickiest support target, weirdly enough. DOSBox/DOSBox-X is different because now I have to bridge an emulator's internal view of port I/O, DMA, IRQs, timing, and memory to real hardware through the host OS. That means it is not just a DOS problem and not just a Windows driver problem; it is an emulator integration problem too. So ironically DOS itself may be one of the easiest targets, while DOSBox passthrough may be one of the hardest.

I will definitely be tackling DOS before the DOSBox/DOSBox-X layer for sure.

---

I should add that I'll try to support as many PCIe-to-PCI bridges as I reasonably can.

Right now my setup is working with the IT8893 PCIe-to-PCI bridge feeding the IT8888 PCI-to-ISA bridge. Other PCIe-to-PCI bridges may work too, as long as they support the ingredients this needs: forwarding the right I/O windows, proper PCI bus mastering(which is needed for ISA DMA like behavior) and enough configuration control to keep the legacy path alive.

In practice, adding support for another bridge mostly comes down to having its datasheet and then patching the software side bridge setup sequence. The IT8888 side is the important PCI-to-ISA/DDMA piece; the PCIe-to-PCI bridge just has to pass the traffic correctly.

From what I have read online, a lot of PCIe-to-PCI bridges have bus mastering which is the critical thing for DMA, the real crux will be if the bridge has enough configuration control though most datasheets I've looked at seem to have that level of control.

There is plenty of bandwidth headroom compared to ISA, since ISA is around 8.33 MHz and conventional PCI is usually 33 MHz, but bandwidth is not the only issue. The IT8888 is the part that isolates the ISA side and implements the PCI-to-ISA/DDMA behavior. The upstream PCIe-to-PCI bridge does not need to understand ISA DMA at all; it just has to forward the PCI-side traffic cleanly enough that the IT8888 can do its job. So the possible dragons are more on the PCI side: posted writes, buffering, ordering, bridge latency, bus-master forwarding, and whether the bridge exposes enough configuration control. If those behave, the IT8888 should handle the legacy ISA/DDMA side as if it were actually on the motherboard.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 17 of 24, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie
dartfrog wrote on 2026-05-22, 11:39:

So yeah, DOS should probably be the easiest target.

But aren't you still required to use an IO port alias rather than the original port? So under DOS you will either need a DPMI host to remap port access or patch existing games to use different ports than standard...

Reply 18 of 24, by dartfrog

User metadata
Rank Newbie
Rank
Newbie
jmarsh wrote on 2026-05-22, 12:20:

But aren't you still required to use an IO port alias rather than the original port? So under DOS you will either need a DPMI host to remap port access or patch existing games to use different ports than standard...

That's actually a very good question. This is one reason why I'd like to eventually have a DOSBox bridge on a modern OS, among other reasons like having CAD/CAM on modern OS with the CNC machines running through that DOSBox bridge. I can just use the alias like I have been and the DOSBox bridge can handle the aliasing.

Under real DOS, my hope is to configure the bridge chain so the original legacy ports, like 0x240 for GUS, are forwarded directly. If that works, existing DOS software can keep using the standard ports and no DPMI port remapper or game patching would be needed for plain port I/O.

Just to be clear, on Win10 I used a high PCI I/O alias like 0x8240 because it avoids conflicts with the OS/resource manager/PnP and makes the bridge path explicit. The ISA card still sees its normal ISA-side address behind the IT8888. So the alias is on the host/PCI side, not what the ISA card sees.

I understand the immediate hesitation that DOS might be no different from Win10 in this regard. But based on the testing I did before this new VDMA/DDMA work, each OS and platform claims and exposes the bridge chain a little differently. That makes me think the high alias may not be required on real DOS or possibly even 95/98. It is very likely hardware and BIOS dependent.

Under real DOS there is no modern Windows style resource manager sitting in the way, so it may be possible to configure the bridge chain to forward the legacy ports directly. That still needs testing, though, because yaah the chipset/BIOS/PCI bridge setup may already be using, blocking, or reserving some legacy ranges. I expect there may be platform specific weirdness. For example, if the bridge chain starts forwarding legacy ranges too aggressively, it could potentially interfere with onboard legacy, USB, or chipset compatibility logic. I do not see this as impossible to solve, but it is definitely something I will need to test carefully. Though some of that hardware is configurable after boot like bridges, so it might just need some extra configuration details worked out.

The fallback would be using the high alias, in which case yes, software would need to be configured, patched, or trapped to use that alias. But I do not think that is necessarily required under real DOS.

Potential PCIe-to-PCI-to-ISA pathway repository: https://github.com/DartFrogTek/PCIe-PCI-ISA
Using KMDF driver on Win10 PicoGUS PLAYS DOOM SAMPLES VIA PORT IO & DMA!

Reply 19 of 24, by myne

User metadata
Rank l33t
Rank
l33t

Congratulations!
As I said in pm, I always knew this was possible, it's all 1s and 0s. It was always a question of how much effort it took.

I haven't looked into your exact memory address scheme at the binary level, but it occurs to me that the pci space is likely to be broadly compatible. Iirc that's somewhere near 4gb.
We're talking kilobytes in a 32+gb world. Even if you incidentally reserve/block off an absurd size range, almost no one would notice or care.
Eg assuming the pci space is 1 bit below 4gb 01000000 00000000 xxxxxxxx xxxxxxxx
would map cleanly. No?
Then you just reserve the appropriate spare pci space and drop the first 16 bits. Modern and legacy both happy.

I built:
Convert old ASUS ASC boardviews to KICAD PCB!
Re: A comprehensive guide to install and play MechWarrior 2 on new versions on Windows.
Dos+Windows 3.11+tcp+vbe_svga auto-install iso template
Script to backup Win9x\ME drivers from a working install
Re: The thing no one asked for: KICAD 440bx reference schematic