This is about the IBM version of the VGA ISA card.
So I am trying to complete my VGA emulation and the VGA bios I am using is ibm_vga.bin from minuszerodegrees. There are a few awkward things about the behavior of the bios and you can see how it shares a lot of code with the IBM EGA bios. For example
1) Apparently the card has dipswitches of some kind. The BIOS reads them via "in al, 3C2h". There seem to be 6 of them. Returning 1,1,0,0,0,0 (which I think means off, off, on, on, on, on) makes the BIOS happy. Otherwise it emits 2 long beeps.
2) It expects the (undocumented) flipping of 2 bits in InputStatusRegister1 (the 3BAh/3DAh register that has "are we in VSync?"). I remember even Sarah Walker had to do this for PCEm's EGA emulation (and so do I) and it seems this carried over to VGA. According to normal VGA documentation the top 4 bits are not used in that register, yet the BIOS will "and" them with 0x30.
3) There are some other issues and strangeness (like setting the character height to 0...) that I am trying to debug.
So questions:
1) Does anyone have a picture of said IBM VGA ISA card? Google is not much of help as I get a lot of VGA-compatible cards, which is not what I am after. I assume it exists right?
2) Does IBMulator and/or UniPCEmu emulate properly ibm_vga.bin BIOS and if so how do you deal with my points above?
I looked at UniPCEmu and I do not see how they deal with points 1) and 2) so my guess is the VGA BIOS fails detection but the normal BIOS will use the card anyway.
Port 3C2h should have both interrupt pending bit (7) and monitor sense switch bit (4) to determine attached display type (color VGA / monochrome VGA). Others should be undefined.
Display type sense works so that if you have a color VGA monitor it will load down RGB outputs with termination resistors, if not, the unterminated channels have higher voltage that can be detected with a comparator. So it basically needs to drive non-black pixels on screen before it can sense if there are unterminated pins or not. I don't know if this is used for detecting between all three possible combinations (disconnected/RGB/grayscale) or just a subset.
Port 3DAh will have both the vertical retrace bit (that somehow relates to VSYNC to monitor) and inverted Data Enable bit that tells you if active area on screen is drawn or non-active area is being drawn, so you can use it for all kinds of tricks such as counting lines where the drawing is occuring to do some tricks or waiting to do some updates between lines. Bits 5 and 4 are diagnostic bits, that give you feedback from attribute controller about what 8-bit pixel data is sent to DAC, and some other bits somewhere else can select which two bits from eight are sent to these two diagnostic bits.
vladstamate wrote:This is about the IBM version of the VGA ISA card. […] Show full quote
This is about the IBM version of the VGA ISA card.
So I am trying to complete my VGA emulation and the VGA bios I am using is ibm_vga.bin from minuszerodegrees. There are a few awkward things about the behavior of the bios and you can see how it shares a lot of code with the IBM EGA bios. For example
1) Apparently the card has dipswitches of some kind. The BIOS reads them via "in al, 3C2h". There seem to be 6 of them. Returning 1,1,0,0,0,0 (which I think means off, off, on, on, on, on) makes the BIOS happy. Otherwise it emits 2 long beeps.
2) It expects the (undocumented) flipping of 2 bits in InputStatusRegister1 (the 3BAh/3DAh register that has "are we in VSync?"). I remember even Sarah Walker had to do this for PCEm's EGA emulation (and so do I) and it seems this carried over to VGA. According to normal VGA documentation the top 4 bits are not used in that register, yet the BIOS will "and" them with 0x30.
3) There are some other issues and strangeness (like setting the character height to 0...) that I am trying to debug.
So questions:
1) Does anyone have a picture of said IBM VGA ISA card? Google is not much of help as I get a lot of VGA-compatible cards, which is not what I am after. I assume it exists right?
2) Does IBMulator and/or UniPCEmu emulate properly ibm_vga.bin BIOS and if so how do you deal with my points above?
I looked at UniPCEmu and I do not see how they deal with points 1) and 2) so my guess is the VGA BIOS fails detection but the normal BIOS will use the card anyway.
UniPCemu:
1) The dipswitches should be fully emulated. It uses inverted input(1=on), which is inverted when the CPU reads it. Color and mono monitors are properly detected on VGA BIOS. EGA previously crashed it's BIOS, haven't tested since implementing it(Might work by now with the many bugfixes).
2. The input status bits are fully emulated according to chip(CGA/EGA/(S)VGA).
Look at the VGA renderer, VGA_precalcs.c and VGA_IO.c for the implementations of 1&2.
3) Character height 0 doesn't exist: the character heights range from 0-31(depending on CGA vs EGA+). Add 1 to that number for the amount of scanlines of a character cel(Thus 1 to 32 lines range on VGA, CGA was 1-8 if I remember correctly. Look at VGA_precalcs.c and VGA_CGA_MDA for the implementation.).
We did, but that was EGA, this is VGA. In CAPE I implement this as two separate pieces of code (mirroring two pieces of HW). Plus the BIOS and underlining HW is different enough. For example I did not expect the VGA to have dipswitches. EGA does yes, but VGA? I have never seen a VGA with switches. But apparently they exist. Hence my questions.
3) Character height 0 doesn't exist: the character heights range from 0-31(depending on CGA vs EGA+). Add 1 to that number for the amount of scanlines of a character cel(Thus 1 to 32 lines range on VGA, CGA was 1-8 if I remember correctly. Look at VGA_precalcs.c and VGA_CGA_MDA for the implementation.).
Yeah, I misspoke, I did not mean actual character height but rather what the bios pokes in the CRTC register. In my traces the VGA BIOS only writes twice to the 9th CRTC register. Once 0 and once 0x40.
1 [VGADevice: port 0x3d4 out 0x9] 2 [VGADevice: port 0x3d5 out 0x40]
Both 0 and 40h mean same thing: character height of 1. Obviously that cannot be correct. I still think the BIOS does not properly detect the board and eventually exits too early before setting proper CRTC values. I wish I had the VGA BIOS listing (like I have the EGA one) as the EGA one helped me tremendously finding emulation bugs. I guess IBM never made it available.
The BIOS as well as software are properly reading the switches and responding to it(CheckIt displays and VGA BIOS initializes to monochrome or color mode accordingly, depending on the monitor setup(Settings menu -> Video -> Monitor setting. Options are Color(0110) and B/W,Brownscale,Grayscale,Greenscale(0010, but on VGA+ only atm, EGA forces 0110)).
You can always try to simply run UniPCemu with the VGA BIOS and dump it's results to compare(using the Common log format). Although register logging also enables the currently unimplemented short(compact) version of memory access logging for proper testing.
For more proof look at the VGA connector pin 11,12,4,15 afaik on eg. wikipedia. Those clearly say ID0-3, until E-DDC.
Last edited by superfury on 2017-11-16, 23:07. Edited 1 time in total.
The BIOS as well as software are properly reading the switches and responding to it(CheckIt displays and VGA BIOS initializes to monochrome or color mode accordingly, depending on the monitor setup(Settings menu -> Video -> Monitor setting. Options are Color(0110) and B/W,Brownscale,Grayscale,Greenscale(0010, but on VGA+ only atm, EGA forces 0110)).
You can always try to simply run UniPCemu with the VGA BIOS and dump it's results to compare(using the Common log format). Although register logging also enables the currently unimplemented short(compact) version of memory access logging for proper testing.
Don't forget to set it to IPS mode(and your emulator as well, if it supports it). That should fix any clock cycle differences between our emulators(which might differ due to different implementations).
Thinking about it: the fixes on the shift might make it(The EGA BIOS) continue: the shr that wasn't affecting the carry flag(properly) back then might have been fixed in more recent builds.
Although that seems to have been fixed(it continues to POST and even boot on EGA now), the misc output register is still 0x00, thus nothing is in VRAM, no fonts nor text/attributes? Can you see what's going wrong by comparing logs on the EGA? The memory is mapped properly at physical address 0xB8000, but it's disabled?
Just found a bug turning EGA clocks to use VGA clocks instead(EGA vs VGA clock determination checked CGA Extension ID 4(Unused) instead of 3(EGA)). But the BIOS still fails setting up VRAM?
Does any one has a VGA vs EGA difference list, listing all differences between EGA and VGA I/O and timings?
I currently have implemented the following differences(the base system is a VGA after all):
- Disable internal video drivers(EGA only).
- ROM is reversed.
- MISC output register starts with bits 1 and 5 set.
- Sequencer register 4 starts with bit 1 set.
- VGA palette is replaced by a constant EGA palette instead(64 colors).
Notes on VGA vs EGA differences:
CRTC index 5h bit 7: Start odd memory address (after horizontal retrace)
CRTC index 11h read: L […] Show full quote
Notes on VGA vs EGA differences:
CRTC index 5h bit 7: Start odd memory address (after horizontal retrace)
CRTC index 11h read: Light pen low
CRTC index 17h: bit 4: Output control. When 1, CRTC output is in high-impedance state.
Port 3CC(w): Graphics 1 position register. Bits 0-1. Usually programmed to 0.
Port 3CA(w): Graphics 2 position register. BIts 0-1. Usually programmed to 1.
Graphics index 5 bit 2: Test condition. When 1, Graphics controller output is placed in high-impedance state for testing.
- Registers have partly been masked off for compatiblity with VGA output.
1byte VGA_RegisterWriteMasks_CRTC[2][0x25] = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, //VGA 2 {0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x3F,0x1F,0x1F,0x1F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0x1F,0xFF,0x1F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}; //EGA 3byte VGA_RegisterWriteMasks_Attribute[2][0x15] = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, //VGA 4 {0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x0F,0x3F,0x3F,0x0F,0x00}}; //EGA 5byte VGA_RegisterWriteMasks_InputStatus0[2] = {0xFF,0x90}; 6byte VGA_RegisterWriteMasks_InputStatus1[2] = {0xFF,0x7F}; 7byte VGA_RegisterWriteMasks_FeatureControl[2] = {0x03,0x03}; 8byte VGA_RegisterWriteMasks_MiscOutput[2] = {0xFF,0xFF}; 9byte VGA_RegisterWriteMasks_Sequencer[2][8] = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, //VGA 10 {0x03,0x0F,0x0F,0x0F,0x07,0x00,0x00,0x00}}; //EGA 11byte VGA_RegisterWriteMasks_Graphics[2][9] = {{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, //VGA 12 {0x0F,0x0F,0x0F,0x3F,0x07,0x3F,0x0F,0x0F,0xFF}}; //EGA 13//Limits are the maximum valid index+1(the total amount of registers that are addressable by index in the index register(including holes)). 14byte VGA_RegisterWriteLimits_CRTC[2] = {0x25,0x19}; 15byte VGA_RegisterWriteLimits_Attribute[2] = {0x15,0x14}; 16byte VGA_RegisterWriteLimits_Sequencer[2] = {8,5}; 17byte VGA_RegisterWriteLimits_Graphics[2] = {9,9}; 18 19byte VGA_RegisterWriteMask_CRTCIndex[2] = {0xFF,0x1F}; 20byte VGA_RegisterWriteMask_AttributeIndex[2] = {0xFF,0x3F}; 21byte VGA_RegisterWriteMask_SequencerIndex[2] = {0xFF,0x1F}; 22byte VGA_RegisterWriteMask_GraphicsIndex[2] = {0xFF,0x0F}; //A.k.a. Graphics 1 and 2 Address register
(Index 1 is the EGA mask, index 0 is the VGA mask)
Port 3D4:
1 case 0x3D4: //CRTC Controller Address Register ADDRESS 2 if (!GETBITS(getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER,0,1)) goto finishinput; //Block: we're a mono mode addressing as color! 3 readcrtaddress: 4 if (getActiveVGA()->enable_SVGA!=3) //Not EGA? 5 { 6 *result = getActiveVGA()->registers->CRTControllerRegisters_Index; //Give! 7 ok = 1; 8 } 9 break;
Port 3D5:
1 case 0x3D5: //CRTC Controller Data Register DATA 2 if (!GETBITS(getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER,0,1)) goto finishinput; //Block: we're a mono mode addressing as color! 3 readcrtvalue: 4 if ((getActiveVGA()->enable_SVGA!=3) || (((getActiveVGA()->registers->CRTControllerRegisters_Index==0x11) || (getActiveVGA()->registers->CRTControllerRegisters_Index==0x12)) && (getActiveVGA()->enable_SVGA==3))) //Not EGA or EGA light pen registers? 5 { 6 *result = PORT_readCRTC_3B5(); //Read port 3B5! 7 ok = 1; 8 } 9 break;
Port 3C0:
1 case 0x3C0: //Attribute Address/Data register ADDRESS/DATA 2 //Do nothing: write only port! Undefined! 3 if (getActiveVGA()->enable_SVGA!=3) //Not EGA? 4 { 5 *result = (VGA_3C0_PALR<<5)|VGA_3C0_INDEXR; //Give the saved information! 6 ok = 1; 7 } 8 break;
Port 3C1:
1 case 0x3C1: //Attribute Data Read Register DATA 2 if (VGA_3C0_INDEXR>=VGA_RegisterWriteLimits_Attribute[(getActiveVGA()->enable_SVGA==3)?1:0]) break; //Out of range! 3 if (getActiveVGA()->enable_SVGA!=3) //Not EGA? 4 { 5 *result = getActiveVGA()->registers->AttributeControllerRegisters.DATA[VGA_3C0_INDEXR]; //Read from current index! 6 ok = 1; 7 } 8 break;
Input status #0 register:
1 case 0x3C2: //Read: Input Status #0 Register DATA 2 //Switch sense: 0=Switch closed(value of the switch being 1) 3 switchval = ((getActiveVGA()->registers->switches)>>GETBITS(getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER,2,3)); //Switch value to set! 4 switchval = ~switchval; //Reverse the switch for EGA+! 5 SETBITS(getActiveVGA()->registers->ExternalRegisters.INPUTSTATUS0REGISTER,4,1,(switchval&1)); //Depends on the switches. This is the reverse of the actual switches used! Originally stuck to 1s, but reported as 0110! 6 *result = getActiveVGA()->registers->ExternalRegisters.INPUTSTATUS0REGISTER; //Give the register! 7 *result &= VGA_RegisterWriteMasks_InputStatus0[(getActiveVGA()->enable_SVGA==3)?1:0]; //Apply the write mask to the data written to the register! 8 ok = 1; 9 break;
Disabled registers(reads):
1 case 0x3C5: //Sequencer Data Register DATA 2 case 0x3C6: //DAC Mask Register? 3 case 0x3C7: //Read: DAC State Register DATA 4 case 0x3C8: //DAC Address Write Mode Register ADDRESS 5 case 0x3C9: //DAC Data Register DATA 6 case 0x3CA: //Read: Feature Control Register DATA 7 case 0x3CC: //Read: Miscellaneous Output Register DATA 8 case 0x3CF: //Graphics Controller Data Register DATA
Input Status #1 register(write):
1 case 0x3DA: //Input Status #1 Register (color) DATA 2 if (GETBITS(getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER,0,1)) //Block: we're a mono mode addressing as color! 3 { 4 readInputStatus1: 5 SETBITS(getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.ATTRIBUTECONTROLLERTOGGLEREGISTER,7,1,0); //Reset flipflop for 3C0! 6 7 *result = getActiveVGA()->registers->ExternalRegisters.INPUTSTATUS1REGISTER; //Give! 8 const static byte bittablelow[2][4] = {{0,4,1,6},{0,4,1,8}}; //Bit 6 is undefined on EGA! 9 const static byte bittablehigh[2][4] = {{2,5,3,7},{2,5,3,8}}; //Bit 7 is undefined on EGA! 10 byte DACOutput = getActiveVGA()->CRTC.DACOutput; //Current DAC output to give! 11 SETBITS(*result,4,1,GETBITS(DACOutput,bittablelow[(getActiveVGA()->enable_SVGA==3)?1:0][GETBITS(getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.COLORPLANEENABLEREGISTER,4,3)],1)); 12 SETBITS(*result,5,1,GETBITS(DACOutput,bittablehigh[(getActiveVGA()->enable_SVGA==3)?1:0][GETBITS(getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.COLORPLANEENABLEREGISTER,4,3)],1)); 13 if (getActiveVGA()->enable_SVGA==3) //EGA has lightpen support here? 14 { 15 SETBITS(*result,1,1,GETBITS(getActiveVGA()->registers->EGA_lightpenstrobeswitch,1,1)); //Light pen has been triggered and stopped pending? Set light pen trigger! 16 SETBITS(*result,2,1,GETBITS(~getActiveVGA()->registers->EGA_lightpenstrobeswitch,2,1)); //Light pen switch is open(not pressed)? 17 } 18 ok = 1; 19 } 20 break;
Disabled writes:
1 case 0x3C6: //DAC Mask Register? 2 case 0x3C7: //Write: DAC Address Read Mode Register ADDRESS 3 case 0x3C8: //DAC Address Write Mode Register ADDRESS 4 case 0x3C9: //DAC Data Register DATA
Graphics controller data register:
1 case 0x3CF: //Graphics Controller Data Register DATA 2 if (getActiveVGA()->registers->GraphicsRegisters_Index>=VGA_RegisterWriteLimits_Graphics[(getActiveVGA()->enable_SVGA==3)?1:0]) break; //Invalid index! 3 value &= VGA_RegisterWriteMasks_Graphics[(getActiveVGA()->enable_SVGA==3)?1:0][getActiveVGA()->registers->GraphicsRegisters_Index]; //Apply the write mask to the data written to the register! 4 getActiveVGA()->registers->GraphicsRegisters.DATA[getActiveVGA()->registers->GraphicsRegisters_Index] = value; //Set! 5 VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_GRAPHICSCONTROLLER|getActiveVGA()->registers->GraphicsRegisters_Index); //We have been updated! 6 ok = 1; 7 break;
Extra EGA support(writes):
1 //EGA compatibility! 2 case 0x3DB: 3 if (getActiveVGA()->enable_SVGA==3) //EGA? 4 { 5 //Light pen latch is to be cleared! 6 getActiveVGA()->registers->EGA_lightpenstrobeswitch &= ~3; //Stop strobe(pending)&measurement ready flag? 7 } 8 break; 9 case 0x3DC: 10 if (getActiveVGA()->enable_SVGA==3) //EGA? 11 { 12 //Light pen latch is to be triggered! 13 getActiveVGA()->registers->EGA_lightpenstrobeswitch |= 1; //Start strobing(pending)? 14 } 15 break;
Precalcs(Misc Output register):
1 if (VGA->enable_SVGA==3) //EGA? 2 { 3 if (VGA->registers->ExternalRegisters.MISCOUTPUTREGISTER&0x10) //Disable internal video drivers? 4 { 5 VGA->precalcs.EGA_DisableInternalVideoDrivers = 1; //Disable it! 6 } 7 else //Enable internal video drivers? 8 { 9 VGA->precalcs.EGA_DisableInternalVideoDrivers = 0; //Enable it! 10 } 11 } 12 13 //Update our dipswitches according to the emulated monitor! 14 //Dipswitch source: https://groups.google.com/d/msg/comp.sys.ibm.pc.classic/O-oivadTYck/kLe4xxf7wDIJ 15 pattern = 0x6; //Pattern 0110: Enhanced Color - Enhanced Mode, 0110 according to Dosbox's VGA 16 //Pattern 0001=CGA, Pattern 0010=MDA 17 if (DAC_Use_BWMonitor(0xFF)) //Are we using a non-color monitor? 18 { 19 pattern = 0x2; //Bit 1=Monochrome?, originally 0010 for Monochrome! 20 } 21 22 if (VGA->enable_SVGA==3) //EGA? 23 { 24 pattern = 0x6; //Start up as a EGA 80x25 color! 25 } 26 27 //Set the dipswitches themselves! 28 VGA->registers->switches = pattern; //Set the pattern to use!
- DAC isn't updated(EGA can't change the DAC, since it has no software DAC). Emulated using unchanging VGA DAC entries&DAC mask register.
- Different clocks:
- Light pen is added on EGA(called the same as CGA):
1void EGA_checklightpen(word currentlocation, byte is_lightpenlocation, byte is_lightpenpressed) //Check the lightpen on the current location! 2{ 3 word lightpenlocation; 4 if (getActiveVGA()->enable_SVGA==3) //EGA is emulated? 5 { 6 if (((getActiveVGA()->registers->EGA_lightpenstrobeswitch&3)==1) || (is_lightpenlocation && ((getActiveVGA()->registers->EGA_lightpenstrobeswitch&2)==0))) //Light pen preset and strobing? Are we the light pen location? 7 { 8 getActiveVGA()->registers->EGA_lightpenstrobeswitch &= ~1; //Clear the preset: we're not set anymore! 9 getActiveVGA()->registers->EGA_lightpenstrobeswitch |= 2; //The light pen register is now set! 10 lightpenlocation = currentlocation; //Load the current location for converting to CGA location! 11 //Now set our lightpen location! 12 getActiveVGA()->registers->lightpen_high = ((lightpenlocation>>8)&0xFF); //Our high bits! 13 getActiveVGA()->registers->lightpen_low = (lightpenlocation&0xFF); //Our low bits! 14 } 15 //Always update the CGA lightpen button(live state)! 16 getActiveVGA()->registers->EGA_lightpenstrobeswitch &= ~4; //Default: clear the pressed switch indicator: we're depressed! 17 getActiveVGA()->registers->EGA_lightpenstrobeswitch |= ((is_lightpenpressed&1)<<2); //Set if we're switched or not! 18 } 19}
Those are all EGA changes I've made to the VGA emulation(and CRT emulation for video adapters). Essentially it's (like CGA/MDA) a (S)VGA with EGA compatibility software patches strapped on(modifying the base VGA behaviour for EGA). CGA/MDA works in much the same way, but customizes various input/output completely, disabling the VGA entirely and using the common rendering subsystem(VGA_renderer.c) to render it's contents.
We did, but that was EGA, this is VGA. In CAPE I implement this as two separate pieces of code (mirroring two pieces of HW). Plus the BIOS and underlining HW is different enough. For example I did not expect the VGA to have dipswitches. EGA does yes, but VGA? I have never seen a VGA with switches. But apparently they exist. Hence my questions.
I don't think any VGA-only board has DIP switches.
And PS/2 Model 50 has onboard VGA chipset that has no monitor ID pins, so those are a later invention. It does however have the monitoring of RGB analog voltages. So just R,G,B,HS,VS are connected, and of course GND on few pins, but that schematic has some errors so I don't trust what pins are GND.