VOGONS


UniPCemu VGA BIOS troubleshooting?

Topic actions

First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've made some progress in the VGA POSTing, but something is still going very wrong on the IBM VGA BIOS it seems?

I see some basic card setup (some POS register stuff being written, as well as port 3C3), followed by some basic RAM checks, then some DAC palette setup and some weird loop reading the Input Status #0 register's Switch Sense bit during horizontal retrace over and over again till erroring out?

0003 Starting POST
00CE subsystems setup done

210: vretrace gone high
214 reached
23B: VRetrace started

0266 point ES to VRAM? Retrace test completed. Store ones in VRAM
0279 Select attribute register 32h. =12h
Set it to 0Fh.
Read 3DAh.
0282 DAC output bits came high?

0295 DAC readback came high during blanking after vertical retrace and went low again?

029D All values in the DAC test register parsed.

00D9 reached at the end of said routine. Some memory test routine?
02EF RAM test OK? Start DAC palette setup.
0369 DAC palette setup done.
036E DAC palette OK?
0373 DAC palette written? Performing readback.
0376->0381 DAC readback completed.
Clear PEL mask register.
038C palette cleared again. Reset sequencer.
0397 some call after re-enabling sequencer?
Reads Input Status 0 register?
04A7 reading switch 0 over and over again? This is reached on horizontal retrace starting.
Write PEL write mode
0487 Vertical retrace starting.
049E waiting for horizontal retrace
04BF: Return from function reading Input Status 0's Switch Sense.
0423: Check the sense bit against some table in ROM?
0427: Match found.

03DF: Unknown routine started. It's some kind of PIT manipulation routine?

0686 seems to be some kind of beep routine?

Some sound processing occurs? It's beeping the PC speaker.

00E9: POST seems to continue in some form?
0578: Read Input Status #0 and extract feature bits 0&1.
0583: Write feature control 02h. Then read the result back.
00EC: Feature bits read into AL top nibble. Then write to EGA switches byte in BDA.
00F1: CALL 14B
0152: Monochrome monitor bit set.
0182: Set BIOS equipment word to bit 5 set in the monitor ID for the BIOS ROM.
0186: Clear SW switches in 488. And OR in 9h to get the color monitor switches.
00F4: Reached. CALL 337.
03DC ???

Write feature control 1 register value 01h
Read switch sense?
Eventually sets the display monitor to color (9 in the switches in the BDA, due to not detecting a monochrome monitor).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2025-12-29, 07:43:

some weird loop reading the Input Status #0 register's Switch Sense bit during horizontal retrace over and over again till erroring out?

The VGA does your standard horizontal and vertical refresh timing checks, so make sure that your timings are spot on.
Besides that though, the IBM VGA apparently has some sort of mux bit or something tied to the first color register. I don't understand what it is doing, but I reverse-engineered what it wants to see.

const SWITCH_SENSE_LUT: [[u8; 3]; 6] = [
[0x14, 0x14, 0x14],
[0x04, 0x12, 0x04],
[0x00, 0x00, 0x00],
[0x04, 0x12, 0x04],
[0x12, 0x12, 0x12],
[0x04, 0x04, 0x04],
];

If the first color register has any of these values when the input status register is read, set the switch status bit to 1.
If anybody knows what is actually going on here I'd love to know.

Here's the disassembly of this particular part:

seg000:240C do_stuff_with_sws proc near             ; CODE XREF: sub_2364+33↑p
seg000:240C ; sub_2364+3E↑p ...
seg000:240C mov al, cs:[si] ; read 3 values from table (12)
seg000:240F mov ah, cs:[si+1] ; (12)
seg000:2413 mov bl, cs:[si+2] ; (12)
seg000:2417 push cx
seg000:2418 call pal_and_sws_stuff
seg000:241B pop cx
seg000:241C add si, 4 ; next entry in table
seg000:241F cmp al, cs:[si-1] ; compare sws bit from func to this value (10)
seg000:2423 jnz short switch_no_match
seg000:2425 loop do_stuff_with_sws
seg000:2427
seg000:2427 switch_no_match: ; CODE XREF: do_stuff_with_sws+17↑j
seg000:2427 retn
seg000:2427 do_stuff_with_sws endp
seg000:2427
seg000:2427 ; ---------------------------------------------------------------------------
seg000:2428 db 14h, 14h, 14h, 10h
seg000:242C db 2Dh, 14h, 14h, 0
seg000:2430 db 14h, 2Dh, 14h, 0
seg000:2434 db 14h, 14h, 2Dh, 0
seg000:2438 db 2Dh, 2Dh, 2Dh, 0
seg000:243C db 4, 12h, 4, 10h
seg000:2440 db 1Eh, 12h, 4, 0
seg000:2444 db 4, 2Dh, 4, 0
seg000:2448 db 4, 16h, 15h, 0
seg000:244C db 0, 0, 0, 10h
seg000:2450 db 4, 12h, 4, 10h
seg000:2454 db 12h, 12h, 12h, 10h
seg000:2458 db 4, 4, 4, 10h
seg000:245C db 10h, 4, 4, 0
seg000:2460 db 4, 10h, 4, 0
seg000:2464 db 4, 4, 10h, 0
seg000:2468 db 10h, 10h, 10h, 0

The fourth byte in each table entry is the expected state of the SWS bit, so you can see it sets some palette values and then expects the SWS bit to be off as well, but I didn't see any point in storing the values that don't set it.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 2 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie

Looking into the calling context a bit more I suspect this is some sort of monitor detection logic.

seg000:2381 palette_test_ok:                        ; CODE XREF: sub_2364+12↑j
seg000:2381 mov dx, 3C6h ; PEL Mask Register
seg000:2384 mov al, 0
seg000:2386 out dx, al ; Write 0 to PEL Mask
seg000:2387 mov ah, 0
seg000:2389 call color_reg_write_test ; Set palettte regs back to 0
seg000:238C mov ah, 0
seg000:238E call screen_on
seg000:2391 mov si, 454h
seg000:2394 mov cx, 1
seg000:2397 call do_stuff_with_sws
seg000:239A jz short have_color
seg000:239C mov si, 450h
seg000:239F mov cx, 1
seg000:23A2 call do_stuff_with_sws
seg000:23A5 jz short have_mono
seg000:23A7 or ds:byte_0+487h, 8 ; 40:87 video system inactive bit
seg000:23AC or ds:byte_0+489h, 6 ; mda bits on
seg000:23B1 mov si, 458h
seg000:23B4 mov cx, 5
seg000:23B7 call do_stuff_with_sws
seg000:23BA jmp short beep_if_nz
seg000:23BC ; ---------------------------------------------------------------------------
seg000:23BC
seg000:23BC have_color: ; CODE XREF: sub_2364+36↑j
seg000:23BC and ds:byte_0+489h, 0FBh ; clear 'using monochrome monitor' bit
seg000:23BC ; in BIOS data area 40:0089
seg000:23C1 mov si, 428h ; set table index to +428
seg000:23C4 mov cx, 5
seg000:23C7 call do_stuff_with_sws
seg000:23CA jmp short beep_if_nz
seg000:23CC ; ---------------------------------------------------------------------------
seg000:23CC
seg000:23CC have_mono: ; CODE XREF: sub_2364+41↑j
seg000:23CC or ds:byte_0+489h, 6 ; set grayscale & mono monitor bits in
seg000:23CC ; BIOS data area 40:0089
seg000:23D1 mov si, 43Ch
seg000:23D4 mov cx, 5
seg000:23D7 call do_stuff_with_sws
seg000:23DA
seg000:23DA beep_if_nz: ; CODE XREF: sub_2364+56↑j
seg000:23DA ; sub_2364+66↑j
seg000:23DA jz short locret_23E5
seg000:23DC mov dx, 103h
seg000:23DF call beep
seg000:23E2 add bp, 1
seg000:23E5
seg000:23E5 locret_23E5: ; CODE XREF: sub_2364:beep_if_nz↑j
seg000:23E5 retn
seg000:23E5 sub_2364 endp

By giving various values for si and cx before calling the previous routine, it can test the SWS response for different subsets of the table.
Hard to tell without having a schematic for the VGA, but there might be some sort of voltage comparator on the DAC output and it can determine whether a color or monochrome VGA monitor is connected by trying a few different colors and seeing how the impedance changes on the relevant pins. Just speculation, mind you. I'm terrible with analog stuff.

Note that "color", "mono" and "no monitor" are valid options, but an invalid response will make the BIOS angry and hang with beeps.

this is backed up by IBM's 'technical reference' for the VGA which I never really paid that much attention to since it's bad.

The attachment vga_sws_note.PNG is no longer available

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 3 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've went and disassembled quite a bit of the VGA POST code at C000:0003 and onwards.

I eventually found some strange routine. It sets the DAC palette to 3 specific colors from a lookup table (in SI that's pointing to the VGA ROM) for color number 0, then waits for vertical retrace to start and end, then reads the Switch Sense with the Misc Output register pointing to clock #0 (thus the ID0 pin).

The pel mask is cleared during this readout and setup routine, forcing DAC 0 colors to be output on the RGB output pins.

The value read is masked (to become 10h or 00h) and returned into AL for the caller.

It performs a series of those lookups by performing the DAC palette setup to some values from the current entry of the table (SI, SI+1 and SI+2 and SI-1 having the ) and then matches it's switch sense against the table entries.

The first table used only has one entry at ROM address 454h. If that matches, it checks 5 entries at 428h.
Otherwise, it checks one more entry at 450h. If that matches, it checks the 5 entries at 43Ch, otherwise at 458h.
If an entry result isn't found, at the bottom of that graph, it adds one to BP (on top of this -> ). If the PEL registers couldn't be written, BP has 2 set, otherwise left alone.

So BP contains an error code (2 base if PEL failed, input otherwise. 1 added if the entry couldn't be found). SI is the last entry found, if the zero flag is set upon returning.

It seems to use the DAC color registers as some kind of addressing for the monitor to report the data addressed on it's ID0 pin.

It also seems to just verify that those entries exist, otherwise causing a beep code (1 long 3 short). It doesn't seem to store that index anywhere and destroys the pointer in the next step?

Last edited by superfury on 2025-12-29, 21:32. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 4 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2025-12-29, 20:59:

I've went and disassembled quite a bit of the VGA POST code at C000:0003 and onwards.

I eventually found some strange routine. It sets the DAC palette to 3 specific colors from a lookup table (in SI that's pointing to the VGA ROM) for color number 0, then waits for vertical retrace to start and end, then reads the Switch Sense with the Misc Output register pointing to clock #0 (thus the ID0 pin).

... I am starting to suspect that superfury has me muted. Ah well.

I'm not seeing where a clock source of 0 selects the ID0 pin as the SWS input in the IBM technical reference, if anyone happens to know where that is pointed out I'd be grateful.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 5 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

https://read.seas.harvard.edu/~kohler/class/0 … are/ibm-vga.txt

It literally (as well as any documentation I can find) documents the Switch Sense (which of the ID0-3 pins to report) based on the CS bits of the miscellaneous output register. Thus the clock source.

The polling of that monitor information is at offset 364h in the ROM, with the scanning routine after retrace is at 40C and the monitoring query function starts at 46C.

My current version of disassembling and documenting the VGA BIOS ROM POST:
https://www.dropbox.com/scl/fi/phgqbeo7vabq6v … t=nm0a171q&dl=0

Last edited by superfury on 2025-12-29, 21:55. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 6 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2025-12-29, 21:34:

It literally (as well as any documentation I can find) documents the Switch Sense (which of the ID0-3 pins to report) based on the CS bits of the miscellaneous output register. Thus the clock source.

The VGA/XGA reference doesn't mention that, nor does the PS/2 technical reference. Interesting.

SS |Returns the status of the four sense switches as selected by the CS field of the Miscellaneous Output |Register.

I'm not doubting you but where does it say the "sense switches" are tied to the ID pins on the VGA?
The original VGA connector only has two ID pins , 11, and 12.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 7 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2025-12-29, 21:50:
The VGA/XGA reference doesn't mention that, nor does the PS/2 technical reference. Interesting. […]
Show full quote
superfury wrote on 2025-12-29, 21:34:

It literally (as well as any documentation I can find) documents the Switch Sense (which of the ID0-3 pins to report) based on the CS bits of the miscellaneous output register. Thus the clock source.

The VGA/XGA reference doesn't mention that, nor does the PS/2 technical reference. Interesting.

SS |Returns the status of the four sense switches as selected by the CS field of the Miscellaneous Output |Register.

I'm not doubting you but where does it say the "sense switches" are tied to the ID pins on the VGA?
The original VGA connector only has two ID pins , 11, and 12.

Because the VGA doesn't have switches on the board from what I know, and the ID pins are literally known to be visible to read there. https://upload.wikimedia.org/wikipedia/common … aphics_card.jpg
Edit: Also, for example: https://corbatech.sourceforge.net/appleII/Sec … GARegisters.pdf

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 8 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2025-12-29, 21:58:

Because the VGA doesn't have switches on the board from what I know, and the ID pins are literally known to be visible to read there. https://upload.wikimedia.org/wikipedia/common … aphics_card.jpg
Edit: Also, for example: https://corbatech.sourceforge.net/appleII/Sec … GARegisters.pdf

I'm not trying to be obstinate, but saying it is "literally known" isn't helpful. Maybe this is common knowledge that everyone and their grandma knows about the VGA, I'm just curious where it was ever cited.

I have an original IBM VGA "PS/2" display adapter and I'm looking at it right now. The ID pins go into an LM339N voltage comparator, where they go from there I do not know.

Your other linked reference just says:

The attachment oak_sw_doc.PNG is no longer available

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 9 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie

This is the best I can find from the PS/2 hardware technical reference.

The attachment ps2_id_pins.PNG is no longer available

So if we can read the ID pins via the SWS bit then we can directly tell if we have a color or monochrome monitor.
The question then is what is the purpose of twiddling different colors into the first color register?

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 10 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++
GloriousCow wrote on 2025-12-29, 22:24:
This is the best I can find from the PS/2 hardware technical reference. […]
Show full quote

This is the best I can find from the PS/2 hardware technical reference.

The attachment ps2_id_pins.PNG is no longer available

So if we can read the ID pins via the SWS bit then we can directly tell if we have a color or monochrome monitor.
The question then is what is the purpose of twiddling different colors into the first color register?

Maybe the monitor reports some kind of data on it's 4 ID pins (only ID0 pin according to what I see the BIOS ROM perform, misc output register isn't touched during this) when in active display with the different RGB output voltages as a sort of address select into it's ROM or something like that?

The DAC is setup at that point to always display color #0 (clearing of the DAC mask register). So when the active display starts (not retracing horizontally or vertically), the 3 color values are effectively put on the R,G,B pins. The BIOS seems to just look at what the ID0 pin has at that point, using that information to look through those lookup tables for a match (first the top, then either the bottom or the final table lookup of 5 entries).
Those 5 entries should (from what I can see) contain different RGB values to put onto the DAC's 0th palette entry. The only downside is that AIDA seems to replace multiple zero bytes with dup() statements, making reading harder.

Of course you can just open up the ROM and look what those addresses I mentioned contain. Each entry effectively starts the byte before the offsets I mentioned, with that first byte being the switch sense expected (0x10 or 0x00) and the next 3 bytes being the RGB settings that are applied to the DAC for the readout of the switch sense.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 11 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

The first table contains (SI=454h): Determines color or monochrome monitor to check. Match for color, otherwise monochrome.
12h 12h 12h 10h sum=36h not reached.

450h contains: (first table not matched) This selects between an active and inactive mono monitor. Match for being active.
04h 12h 04h 10h sum=1Ah not reached.

428h contains: (first table match) This validates a color monitor for operation.
14h 14h 14h 10h sum=3Ch not reached.
2Dh 14h 14h 00h sum=55h reached.
14h 2dh 14h 00h sum=55h reached.
14h 14h 2dh 00h sum=55h reached.
2dh 2dh 2dh 00h sum=87h reached.

43Ch contains: (450h match) Monochrome monitor detected. This validates a monochrome monitor for operation.
04h 12h 04h 10h sum=1Ah not reached.
1eh 12h 04h 00h sum=34h reached.
04h 2dh 04h 00h sum=35h reached.
04h 16h 15h 00h sum=2Fh reached.
00h 00h 00h 10h sum=00h not reached.

458h contains: (450h not matched) Monochrome monitor detected, but inactive video subsystem (BDA's address +87h bit 3 set). This validates an inactive monochrome monitor for operation.
04h 04h 04h 10h sum=Ch not reached.
10h 04h 04h 00h sum=18h reached.
04h 10h 04h 00h sum=18h reached.
04h 04h 10h 00h sum=18h reached.
10h 10h 10h 00h sum=30h reached.

Those former 3 numbers are listerally sent to the 0th DAC entry in the DAC in that order. The order is: red,green,blue. The first one is literally the switch sense bit read. Although that C3 values at the start of the 428h table are a bit odd? In fact, it cannot match, so it will always be skipped it seems.

It is however odd that the resulting SI isn't ever stored or used anywhere. It just faults the POST with a beep code if at the final table used no match is found (1 long 3 short beeps).
Edit: My bad, SI points to a 4 entry data block, with the first 3 indeed being the R,G,B values to write (in that order). But instead of the result value to check being at SI-1, it's effectively at SI+3, since SI is first incremented by 4, and then checked at offset SI-1!
I'll rewrite the tables again in a bit...
Edit: Rewritten the tables. It now properly makes sense what's in it.
Edit: Added the R,G,B sum values, in VGA DAC units.
Edit: Added reached for comparitor readback treshold reached.

Last edited by superfury on 2026-01-02, 18:29. Edited 12 times in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 12 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie

Okay, this led me down a little bit of a rabbit hole.

I took a look at the Oak OTI 087 documentation, and they document the SWS bit as being used for monitor detection. Cool.
There's also a schematic - and they don't show the VGA ID pins being connected at all.
http://www.bitsavers.org/components/oakTechno … 087_Feb1993.pdf (see PDF page 65)

So where does the "Sense" pin connect to? The sense pin on the DAC - in this case they use a BT481A RAMDAC which there's a very nice PDF for:

https://www.ifi-ed.tu-clausthal.de/fileadmin/ … C_btl481a_c.pdf
See page 18.

SENSE* is a logical zero if one or more of the IOR, IOG, and IOB outputs have exceeded the internal voltage reference level of the SENSE comparator circuit. This
output determines the presence of a CRT monitor and, with diagnostic code, the difference between a loaded or unloaded RGB line can be discerned.

The IBM VGA card uses an INMOS G171 DAC, which does not have a dedicated "SENSE" pin, but it has a somewhat tangental "IREF" pin that will sink current proportional to the DAC output current. So similar sense logic could be implemented on the IBM VGA using that in conjunction with the LM339N voltage comparitor.

Many monochrome monitors only had the Green pin connected, so by sending red and blue signals the DAC can sense if anything is attached to them... now you'd have to go look at how that table is read and the RGB values set to see if that makes any more sense in context.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 14 of 25, by GloriousCow

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote on 2025-12-29, 23:48:

But if that's true, then why does the VGA allow 4 sources to be selected for the sense 'pin', using the clock select bits in the misc output register?

I don't think it actually does. I honestly think that people have gotten the EGA register description mixed up with the VGA one, and over the years that's polluted various references that get copied over and over. These references all talk about selecting "switches" and as you pointed out earlier, the VGA has no switches.

The EGA used the same bits to select dip switches as it did to select clock sources because that was a side-effect of using an 74LS153. But the VGA doesn't have an 74LS153 on it either.

Consider as evidence that the original VGA connector pinout only has *two* ID pins, the extra two ID pins were introduced with the XGA.
you an also see here for schematics for the original "VGA" monitor. the IBM 8513 , and it only had two ID pins as well. So there would be no reason for the VGA to read four pins if only two of them were even defined at the time.

https://dn721903.ca.archive.org/0/items/ibm_8 … ibm_8513_sm.pdf

also it turns out what i was probing on my VGA card was not the ID pin but the Red pin as I had the pads upside-down. So there's no telling if the IBM VGA card even has the ID pins connected - there are no visible traces running off the d-sub connector except for a ground so they're routed on the inner layers of the board. You either get lucky with your continuity tester and educated guesses, or you sand the board down to see the inner layers and I'm not about to do that 😁

I guess the remaining question is why *not* just use the ID pins if IBM bothered to define them. That I don't know.

EDIT: A good reason might be because although the IBM 8513 was the first "VGA" monitor it wasn't the first monitor that could display VGA.
For example you could use the VGA card, via an adapter, with a NEC JC-1401P3A multisync monitor. That monitor had no ID pins to read. It used a de-9 connector for both digital and analog video signals, and you'd throw a switch right above it to select which you were using. So this manner of DAC-probing was really the only way to detect mono vs color if using an official IBM monitor was not guaranteed. Awfully nice of IBM, I guess.

MartyPC: A cycle-accurate IBM PC/XT emulator | https://github.com/dbalsom/martypc

Reply 15 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

But it just verifies those results and doesn't seem to save it anywhere? It just beeps or doesn't, having no effect on the POST afterwards it seems.

The code afterwards always seems to go to color logic instead of mono logic, no matter what? Is it messing up the monitor ID somehow? I clearly see mono logic being selectable somehow, but it might be depending on a CGA being installed (some Fh CRT register being probed with the value 25h needing to match readback).

Edit: It seems like it will only setup for 'monochrome monitor' mode when the 6845 at address 3D4 cursor low register (0Fh) responds to 1Ah being written and read back and afterwards 25h as well. And if it detects the MDA (3B5h)'s 0F register doing the same, it will clear the mono bit at BDA 87h anyways, forcing the VGA to color mode.

So it seems that the only way to make the BIOS put properly in color/mono mode is by either implementing that single register 0Fh on a 'fake or real' CGA card, or omitting said register?
Edit: In fact, all it needs is for 3D5h to respond to reads or writes, as it doesn't seem to perform any readback checks on 3D4h at all.
Edit: Implementing that 'fake CGA', I already found it hanging on the 3DAh port reads for horizontal retrace. So adding that as well (redirecting it to the VGA's own 3BAh register in the monochrome case), I see it POSTing with monochrome graphics! Looking at the color and attribute controller registers shows it's running in monochrome monitor mode (video mode 07h in MDA/CGA BIOS terms).

The switches byte on the VGA's BDA now report 0x05, meaning 'off on off on'?

Just some glyphs are somehow broken it seems. Perhaps the BIOS is using wrong character height somehow.

Edit: In this way, the system boots properly in monochrome mode. Although the VGA's character sets are partially messed up (and perhaps for the wrong character height somehow?). And in some cases the VRAM isn't cleared at all, leaving garbage in there. It's mainly the lower text (<20h and some other characters that are messed up (so plane 2 isn't exactly fully written properly).

Color mode (where I make it unmap the fake CGA CRTC Fh register) runs without visible issues (and it POSTs and boots properly into color mode, without graphical or text issues).

In color mode, somehow the CheckIt diagnostics software seems to mess up the mode 7h screens? I see the blocks being oddly placed, although in 'color' as specified by the palette bits 3/4 (all others always zeroed). And mode 0Fh gives a black screen somehow, on the color monitor?
Edit: In monochrome mode, the video RAM tests seem to fail? Also pretty much all video memory gets jambled up, text being displayed afterwards, but with lots of static garbage added? Like plane 2 is somehow messed up?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 16 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just took a look at 86Box's implementation of the (S)VGA behaviour.
It seems to report a switch value of 1 (ignoring the misc output register's value), which is of course flipped, reporting 0 (1 if flipped when read) when the sum of the R,G,B pins is 4E or higher, in values of the VGA palette (where each range is 3F). So converting from RGB's 6-bit to 8-bit range, it's shifted right by 2 bits and compare to 4e?

Last edited by superfury on 2026-01-02, 00:10. Edited 2 times in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 17 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. Looked at the ET4000 documentation:
6:R-GND
7: G-GND
8: B-GND
11:ID0->BDB<0>, through resistor R9

15:ID3 N/C

12:ID1->res->BDB<1>, through resistor R10
4:ID2->res->BDB<1>, through resistor R10
Perhaps a different resistor used in those two somehow?

But looking inside the ET4000 chip, I see some 'chip' with:
- 2-bit +CS0 and +CS1 connected to S0/S1 respectively.
- 4 bits into I0-I3. It looks like it's all connected to GND? Although that might be dipswitches? The top-left of it it says 'SW1'
- Output is +SWSE is documented as 'SWSE ... Input status, can be read via input status 0 bit 4

The documentation of Input status 0 register is:

Bit 4 input can be used to determine the default video mode upon power up, or
the type of monitor connected to the system. The Clock Select field (bits 2,3 in
the Miscellaneous Output Registerl setting determines the switch to read.

So apparently Input Status 0 register is behaving as is known in many VGA documentation, using CS in the Misc Output Register as it's source.

I can't figure out where +BDB<0> amd +BDB<1> from the VGA connector go to though? It seems unlikely it's going to the data bus of sorts?
Edit: OK. Found them. It's bit 0 and bit 1 of the inputs to the translation read and write ROMs.
Edit: As well as INMOS 171 pins D0/D1 (data pins).
Edit: Interestingly, the /W32i variant even changes those inputs (misc output register reporting on the switch sense) with an additional 5th bit somehow (feature control register reads bit 2-5 from the MONID bits (monitor ID bits), which are also reported at the input status 0 register for legacy software (the feature control register reads's extended bit (bits 2-5 (MONID) and 7(MC6845 emulation enables NMI generation using this bit).
No clue what the extra MONID4 and MONID5 pins are though.

Edit: Found this. Apparently, on a monochrome VGA monitor, just green is connected and grounded, the R/B signals not connected at all? So perhaps I should simply report the 'ground' values of either RGB(color monitor) or just G(monochrome monitor, effectively not reporting R/B) on the sense pin output?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 18 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. I've found out something when delving through the BIOS code in AIDA:
- When one of those tables is matched, it must match entirely. Any failed matching entries aborts the whole table (returning with the zero flag not set).

- If the first table matches (1 entry) with zero flag, it's always a color monitor. If so, table 428h must match, otherwise error beeps.
- Otherwise, If 450h table matches, it's a mono monitor. But 43Ch table's entries must all match, otherwise error beeps.
- If 450h table doesn't match, it deactivates the video subsystem bit (BDA+87h bit 3 set) and enables grayscale just like if 450h matches. Table 458h must have a match.

So to get monochrome operation, the fist table must fail, 450h must match, as does 43Ch table.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 19 of 25, by superfury

User metadata
Rank l33t++
Rank
l33t++

Looked a bit at what is supposed to happen to reach the monochrome monitor case (reaching address 3CCh):
12 12 12 shouldn't match (first table, 454h) (switch sense 00h)
04 12 04 should match (first choice subtable, 450h) (switch sense 10h)
04 12 04 shouldn't match (table 43Ch first entry, switch sense 00h). But the above just matched in the previous lookup table, so this is Always erroring out?

Looking at 86box, which implements it, it's apparently having a reference of 4Eh in VGA DAC summing terms (range 0-3F being translated to exactly 4 times as high, effectively shifted left by 2 in this calculation). So shifting the resulting DAC lookup back down should give a valid result on that part? Or alternatively, shift 4Eh left by 2 bits for the same effect.

Now, my emulator still effectively grounds the R/B pins right now on monochome mode, thus:
121212 results in 12h, cleared switch sense. So the first entry actually matches, which it shouldn't? It should set the entry's switch sense, thus not matching the minimum value, to reach the monochrome cases.
Edit: OK. So switch sense 00h means 'matched minimum voltage' and 10h means 'not matched minimum voltage', according to 86Box. My emulator does the same, at a VGA-compatible 4Eh sum of the DAC registers (shifted left on readout).
On a color monitor, all channels probably just readout the sum as specified.
So I get (on a color monitor), in SI indexes:
454h getting 0x10 as output, so succeeds.
428h getting 0x10 as output, OK
42Ch getting 0x00 as output, OK
430h getting 0x00 as output, OK
434h getting 0x00 as output, OK
438h getting 0x00 as output, OK
This results in success, since all color monitor results succeeded.

The results of the final byte of each entry are as: 00h: At least total 4Eh DAC input (SHL 2 on RGB scale), 10h: Less than 4Eh DAC input (SHL 2 on RGB scale).

So to get it to do the monochrome monitor:
The first table (12h 12h 12h 10h) should fail. So it shouldn't become at least 4Eh (SHL 2) on the resulting R,G,B inputs.
The second table (04h 12h 04h 10h) should succeed for a proper monochrome VGA monitor that's not disabled. It shouldn't be at least 4Eh (SHL 2) on the resulting R,G,B inputs.
The third table at 43Ch should succeed fully to POST properly without an error code.

My VGA ROM findings so far in IDA:
https://www.dropbox.com/scl/fi/phgqbeo7vabq6v … t=lsykfv1h&dl=0

So by my findings:
- Color: needs a treshold of 55h or more.
- Monochrome: treshold of 1B through 35h.
- Monochrome D/C: treshold of Dh.

So what does the monitor do? Logically, it should be taken this path:
table 454h:
12h 12h 12h 10h sum=36h not reached. Should be reached.
table 450h:
04h 12h 04h 10h sum=1Ah not reached. Should not be reached.
table 43Ch: as indicated. should all match.
04h 12h 04h 10h sum=1Ah not reached.
1eh 12h 04h 00h sum=34h reached.
04h 2dh 04h 00h sum=35h reached.
04h 16h 15h 00h sum=2Fh reached.
00h 00h 00h 10h sum=00h not reached
So it should report treshold reached on 1Bh and higher for monochrome operation?

Edit: It seems to work (implementing the feedback to the sense comparator to give 4Eh in DAC units when the monitor senses that the sum of R,G,B is at least 1Bh)! No more error beeps and the VGA actually POSTs in monochome mode (even according to the BDA 88-89-8A data).
Also no more nasty CGA shadow register work needing to be done for this, as the VGA will simply be a monochrome VGA in this case.
Edit: Although it might officially just convert the 1Bh summed range to the threshold range on the monochrome monitors, using a simple resistor? Either way, it works this way to POST properly.

Although running CheckIt Diagnostics causes it to spew out memory errors when testing the VRAM, as well as all text modes except mode 0Fh failing to display anything (with some text modes somehow using only half a screen somehow).

Edit: The final table seems to be reached with the trigger point being from 18h instead of 1Bh, in terms of the DAC output sum?

Last edited by superfury on 2026-01-03, 14:58. Edited 1 time in total.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io