After some more testing, I found some weird things going on with the OPL2 KSL ROM that was taken from Dosbox or something like that I think (I don't remember which) together with the other volume envelope logic.
Somehow, when I move the KSL lookup table outside said function (which is displaced by 8 apparently, based on the ROMs), the sound for OPL2/OPL3 goes haywire for some OPL3 testing songs I used? Can't seem to figure out why that happens.
Managed to get NT 4.0 workstation booting properly again during setup (phase 2, so after the formatting and copying the OS files over for the graphical stage of the boot). 😁
Weird... When executing opcode 86h from a MS-DOS 6.22 install disk 1's scandisk when aborting while performing a surface scan (screen cleared due to returning to MS-DOS), the BIU managed to enter an invalid, bus locked state. There is a request still pending to be executed in the inbound request buffer, but it's never retrieved to be started it seems, causing the EU to lock up, permanently waiting for the result.
Edit: It seems to have been an issue with the IPS clocking mode handling, where it thought the operation was finished (and thus removed the handler from any pending state as it should), but it wasn't.
After more testing, I found another bus lock contention.
In this case, somehow the CPU managed to lock the bus, while a DMA transfer had ownership of the bus, which obviously shouldn't happen.
This caused both the DMA controller and CPU to lock up, waiting for ownership of the bus (the CPU due to a normal instruction or hardware imposed lock(protected mode etc.), the hardware (DMA) due to trying to complete a transfer).
Once again took a look at the bus lock policies (which are now entirely owned by the BIU in the lastest commits). Adjusted the HLDA mechanics to properly detect an inactive bus (instead of just checking for DMA, checking for any kind of active bus counts there now). The HLDA output now also takes into account if a bus isn't locked before granting the bus.
Eventually reached the point in WIndows NT 4.0 setup where it's writing the recovery disk (after formatting it using floppy disk controller format commands). Somehow it manages to error out on writing the first sector it tries to write somehow?
I've added some bugfixes to force seeking during such an access, but I'm not sure it worked until I get setup back to said point again (which has to restart from the first reboot step each time I test it for WIndows NT 4.0).
Last time, the bus lock was hanging the system (due to multiple locks being held at the same time that shouldn't), so I couldn't continue even if I wanted to. Currently waiting for it to reach that section again to verify if it works now (it should in theory, but I'm not sure).
Fixing a floppy disk scanning (sector ID reading) issue (during read/write/read ID commands), the floppy now properly writes the NT4 ERD again during setup as it did before. 😁
Odd... While testing my emulator, all of a sudden my display started erroring out (Windows 10 Pro) and the OS requested me to reboot. Looking at the hardware list in my PC (although 7-10 years old and upgraded a bit (except the motherboard itself and the GPU) through the device manager showed that somehow the APIC, DMA and some onboard components required a reboot? Normal SDL3 applications shouldn't cause such issues, or do they?
Windows seems to be up-to-date, except the 2025-07 update that's optional.
Implemented some more bugfixes, including:
- Improved time support (properly supporting negative Unix timestamps internally now!)
- Emulator time protection. Somehow, sometimes (like during paging trashing on the host machine) the time elapsed that's reported on the application can make a big jump (on the high precision timing support). This caused the emulator to wait for a very very long time. When the main loop detects this case, it will now simply discard the elapsed time to synchronize it back up, after which it runs on a normal speed again. This case usually doesn't happen, but will cause possible speedups, which can't be avoided (the alternative being having to wait for a very long time that effectively didn't happen, which happened before the bugfix).
Improved x86 task switching and interrupts handling of the EFLAGS register.
It now supports conditionally setting, clearing and unaffecting the EFLAGS resume flag (RF).
Now, when detecting the interrupt to launch, it will itself trigger a set of the resume flag on the stack (or TSS for task switches), if it's not a debug interrupt (interrupt number 01h, triggered by a debugging cause).
Thus it should behave just like Bochs and the documentation on the resume flag (either setting it or leaving it as the instruction left it during the time the interrupt was launched).
Since said flags storage are always based upon the committed state of the EFLAGS by default (if not affected), the documentation for said flag should behave properly now (instead of always setting it during INT1 debug interrupts (except the INT1 instruction of course, which is a trap, not a fault)).
So the version is set to 02h, which might need to be 00h?
Edit: Continuing anyways, I get:
1kd> g 2HAL: An invalid V86 opcode was encountered at address 2000:f76 3Break instruction exception - code 80000003 (first chance) 4halapic!HalpOpcodeInvalid+13: 580191983 cc int 3 6kd> g 7Break instruction exception - code 80000003 (first chance) 8 9A fatal system error has occurred. 10 11******************************************************************************* 12* * 13* Bugcheck Analysis * 14* * 15******************************************************************************* 16 17Use !analyzebugcheck -v to get more information. 18 19BugCheck 7B, {fc90e63c, c0000034, 0, 0} 20*** Bugcheck Analysis may not be correct, please followup with the following. 21Followup : MachineOwner 22 23ntkrnlmp!RtlpBreakWithStatusInstruction: 24807e984c cc int 3
Some more interesting stuff, but first need to patch the revision of the hardware to 00h instead of 02h.
Edit: Also interesting, once that V86 opcode issue was encountered, the screen of the installation turned red? Maybe it was trying to perform a red screen of death?
OK. So the basic PCI device seems alright. I've managed to get the same running on Virtualbox (although downgraded to version 7.1.4 due to crashing on Windows 10's latest version with it's most recent updates).
Running the PIC commands give me this:
1Windows XP Kernel Version 2600 MP (1 procs) Free x86 compatible 2Built by: 2600.xpclient.010817-1148 3Kernel base = 0x807c2000 PsLoadedModuleList = 0x8083ce28 4System Uptime: not available 5ntkrnlmp!DebugService2+e: 68081b01c cc int 3 7WARNING: Process directory table base 00000000 doesn't match CR3 00039000 8WARNING: Process directory table base 00000000 doesn't match CR3 00039000 9kd> !pcitree 10Error retrieving address of PciFdoExtensionListHead 11kd> !pci 15 0 1 1 12PCI Bus 0 1301:1 8086:7111.01 Cmd[0001:i.....] Sts[0000:.....] Device Class:1:1:8a 14 cf8:80000900 IntPin:0 IntLine:0 Rom:0 cis:0 cap:0 15 IO[4]:d001 16 00000000: 86 80 11 71 01 00 00 00-01 8a 01 01 00 00 00 00 *...q............* 17 00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 18 00000020: 01 d0 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 19 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 20
Interestingly enough, it has a class of 1:1:8a (byte 9 is 8A, thus it indicates it's capable of switching to PCI mode, which shouldn't be possible, as there's only one BAR available (at 20h, which is the DMA controller BAR).
Running !pcitree gives me the following, just as happens in UniPCemu:
1kd> !pcitree 2Error retrieving address of PciFdoExtensionListHead
Letting it continue (inside Virtualbox), I run the !pcitree command again and get:
1Bus 0x0 (FDO Ext 812a00d8) 2 0600 12378086 (d=0, f=0) devext 812a0ef8 Bridge/HOST to PCI 3 0601 70008086 (d=1, f=0) devext 812a0d08 Bridge/PCI to ISA 4 0101 71118086 (d=1, f=1) devext 812a0b18 Mass Storage Controller/IDE 5 0300 beef80ee (d=2, f=0) devext 812a07d0 Display Controller/VGA 6 0200 10048086 (d=3, f=0) devext 812a0488 Network Controller/Ethernet 7 0880 cafe80ee (d=4, f=0) devext 812310e8 Base System Device/'Other' base system device
So at least in Virtualbox, it managed to initialize the PCI tree structure and recognise the device as a "Mass Storage Controller/IDE". But in UniPCemu it never reaches said point, either crashing before that or incorrectly initializing the PCI tree structure?
Edit: Hmmm.. Maybe use an I/O breakpoint at CFC to view if it's reading the I/O ports at all?
1 0: kd> ba i4 0xcfc 2 0: kd> bl 3 1 e 00000000`00000cfc i 4 0001 (0001)
Edit: Unfortunately, I got no guest OS support of the breakpoints:
1kd> ba i4 0xcfc 2I/O breakpoints not enabled 3 ^ Syntax error in 'ba i4 0xcfc'
But what I can do, is modifiy the DR0 and DR7 register to implement it manually (as the implemented CPU supports it).
Also need to modify CR4 to support said feature though (setting bit 3).
So I get in the DR registers:
DR0=CFC
DR7=E0203 (I/O trap on breakpoint 0, 4 bytes wide, global and local enable)
Luckily I don't need to take the 386-only bits 8/9 (local enable/global enable) into account (unimplemented as it's unknown behaviour). But I didn't seem to matter in this case anyway (something called 'exact reporting' of debugging addresses).
The debugger also doesn't seem to clear the debugger registers unless I give a command to do so, so the manual I/O breakpoints work without any issue 😁
The debugger doesn't clear the fault, so I can continue using the 'unsupported' breakpoint anyways. 😁
I eventually see it triggering a breakpoint trigger accessing the point, at halapic!READ_PORT_UNLONG, called by:
HalpPCIReadUlongType1+0x29
HalpPCIConfig+0x4b
HalpReadPCIConfig+0x46
HalpGetPCIBridgeConfig+0x84
HalpInitializePCIBus+0xd6
HalReportResourceUsage+0x15
Adding another breakpoint for CF8, I can track the kernel's PCI accesses in this way.
(Logic being:
OK. The WinDBG debugger handling in my emulator is functional at least. I can manually set I/O breakpoints (using the debugger registers directly, as the Windows XP kernel doesn't support it) as well as normal execution and data breakpoints on instruction and memory addresses.
So far I've gotten a breakpoint set at 8019563f which is within HalpPCIReadUlongType1 to read a PCI register using the HAL. So I can then see at the EAX register what PCI register it's trying to read.
And at 8019579c is where it's read a full PCI configuration space into memory it looks like.
OK. With the latest changes, somehow a V86 mode INT 1Ah reflection goes bad?
Instead of reaching interrupt 1Ah, it somehow ends up at interrupt 06h instead, which doesn't recognise the INT 1Ah's next instruction (a conditional jump back to wait for time to elapse using BX/DX counters it's supposed to have read?). The reflection makes it end up at INT 06h (BIOS #UD handler) instead of INT 1Ah (BIOS date/time of day handler).
It reads the opcode of the instruction where the fault's EIP points to and finds opcode 3B. But it's supposed to be opcode CDh (2 bytes before that). SI is loaded with the EIP that was pushed on the stack by the #GP fault handling of the interrupt. But instead of properly containing opcode CDh (2 bytes before the fault), it's detecting opcode 3Bh (the next instruction after the faulting INT instruction!). So it probably doesn't recognise said instruction, so at 0048:000005DB it jumps to a generic point from the fault handling, which apparently is the #UD fault handler in the protected mode handling, which eventually causes the original CDh INT instruction to cause an #UD fault instead of properly executing the interrupt!
Stepping through UniPCemu's interrupt handling, during interrupt startup, it sets the executed fault EIP/CS to the next instruction for interrupts to return to, but it shouldn't do that (it should just use the current executing instruction return point, or it's last known one to be valid (if post-instruction execution))!
Edit: Having fixed that to work properly again, the EMM386 driver boots properly again.
Improved protected mode debugger register state when triggering delayed faults (due to being inhibited by POP SS, MOV SS or any blocking instruction, like STI).
Now UniPCemu will record any debugger faults, to trigger firing them once the instruction combination terminates.
This includes detected execution breakpoints and data breakpoints.
All breakpoints triggering will upon triggering clear the pending breakpoints and move their pending status into DR6 appropriately (as they've fired when possible).
Recording those debugger faults can now also trigger when still inhibited, only triggering once the inhibition ends (at the end of an instruction or when starting the next instruction while an execution breakpoint is pending). Otherwise it's triggering after an instruction.
So, basically, unless interrupts are inhibted, the single step interrupt will occur immediately. If interrupts are inhibited (CLI/STI or MOV to SS is executing), the inhibition still occurs (keeping the single-step pending internally, not firing or registering anything). Then, once the debugger checks for an instruction completed on the next instruction and the pending breakpoint is detected (which can't be inhibited anymore, due to inhibition mechanics only applying to a single instruction), the pending breakpoint is detected and the interrupt is fired (which sets the DR6 bits accordingly for this case).
If it fails to raise the fault at this point (exception error), the fault is now simply recorded into DR6 and discarded.
Edit: Moved all that common logic into the single-step exception start handling. Now, whenever the CPU tries to invoke the single-step exception (and it doesn't trigger a double or triple fault), the exception status for any breakpoint registers is moved into the DR6 register low bits properly at this point. So this happens whenever a #DB exception occurs successfully (not during double/triple fault).
The same now also happens whenever a #BP exception occurs (it left the status cleared on each instruction start before).
So the exceptions will keep pending until they aren't blocked for handling anymore, triggering on the very first occasion it can. So now you can properly single-step interrupt inhabiting instructions (even MOV SS and STI/CLI), which will inhibit the #BP exception until the next instruction finishes executing.
Improved the (80486) TLB test registers a bit.
It will now try to report the 'LRU' from the set specified by the 2 bits in the DR7 register.
It will simply try the free list first, and if no free item (no actual LRU there to be used) is present, it will display the LRU of the used entries instead (as it's the first that's allocated when requesting a new item to be allocated using the LRU allocation algorithm).
Added a little improvement to the Direct Input mode.
When you press RALT-F8, it will trigger the direct input mode release for the mode that was originally enabled (middle mouse button, both mouse buttons or RALT-F10).
It's more for if you forget what kind of mode originally triggered it, causing an ungrab for whatever method you used to originally enter Direct Input mode.