VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

Anyone can tell me what the exact relationship is between all counters in the VGA? Although the FreeVGA documents some bits of it, I'm trying to make a picture of how it all relates to each other (input -(divide)> input -(divide)> input -(divide)> output wise).

I know that there's the dot clock, which provides timing for the pixels to be drawn on the screen.
- This dot clock can be divided by 2(Dot Clock Rate) or used without modification to drive the renderer (Sequencer).
- The renderer(Sequencer&CRTC) further divide this into 8 or 9 pixel blocks to drive the character clock. This character clock is directly used with the CRTC registers to provide timing for overscan, active display, blanking and retracing.
- The character clock, in turn, is divided by 1, 2 or 4(Shift/load rate) to drive the Memory Address Clock to load data from VRAM at the start of each 1, 2 or 4 character clocks?

After this, the documentation is a bit strange on the diffent dividers available(other CRTC registers)?
- There's the Scan Doubling and the Divide Scanline clock by 2? Do they do the same(OR'ed with each other), or do they stack on top of each other?
- There's the Byte/Word/Doubleword mode, which after multiplication by 1,2 or 4 with the Memory Address Clock seem to provide the planar address (0-65536) to load a DWORd(planes 0-3) from VRAM?
- There's the Divide Memory Address clock, which divides the Memory Address Clock by 1, 2 or 4 to provide the memory address for the Byte/Word/Doubleword modes?
- The result of the Byte/Word/Doubleword address wraps around 64K. The bits overflowing are shifted back in at the least significant bit? It then modifies the resulting bit 0 to be set to the value in bit 13 or bit 15 depending on the Address Wrap Select? Finally the address bits 14&13 are replaced with bit 0 and 1 of the row scan counter(the row within the character clock) when their respective bit in the CRTC mode control register (bits 1&0) are cleared?

Is this correct? Am I missing anything on that matter? Anyone?

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

Reply 2 of 5, by superfury

User metadata
Rank l33t++
Rank
l33t++

What happens with memory requirements past 64K VRAM DWORD addresses? It says the bits 16-17 that are shifted out are put in memory address 0-1, but this doesn't fix the problem: Since it keeps being a 16-bit address(64K 32-bit addresses for a total of 256K memory). How should this be handled when using more memory? Should the shift be using a bigger range(128K, 256K, 512K or 1024K)? It cannot just be wrapping the addresses that are past 256K(64K addresses with 32-bit VRAM data per address). That would cause the 640x480x256 color mode (which uses about 300K memory) to wrap around too early. What happens in those cases?

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

Reply 3 of 5, by Scali

User metadata
Rank l33t
Rank
l33t

VGA doesn't support more than 256K of memory, and doesn't support 640x480x256.
Only certain SVGA clones support that, and they each have their own ways of dealing with those sitations.
Generally you had to perform some kind of paging/memory remapping to access more memory on early SVGA cards.
Later cards had linear framebuffer support via 32-bit pmode, which avoids the 64K addressing limit altogether.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 4 of 5, by superfury

User metadata
Rank l33t++
Rank
l33t++

Well I'm currently testing 640x480x256 on my ET3000 emulation(which is also at the same time ET4000 code, since they're very similar), which is based on the register writes from the lastest Dosbox SVN. I just modified it to apply it's register data and use my own precalculations written by myself(which is used together with information on the Sierra 16-bit Hicolor DAC of a commit to the Dosbox DAC). The Sierra DAC works without problems (supporting 8-bit, 15-bit, 16 bit colors with and without 2 pixel latching (bit 0x20 being set)). The VRAM is written correctly into the full 512KB range. The only problem is the way the VGA applies address wrapping(The CPU is writing the pixels using the VGA-compatible chained 256-color mode combined with the 0x3CD register to apply the writes to chunks of 64K memory at a time). I see, when dumping the VRAM to a file, that the VRAM is correct(Most of the VRAM just contains 0x7 bytes(256-color gray), which is the background of the Windows 3.0 desktop. So the VRAM is correctly written (The VGA MMU module works correctly). The problem is definitely in the Sequencer taking pixels from VRAM(32-bits at a time).

My current code, which loads data from all character planes(called at the start of every character clock) and increases the memory address counter according to the divider(CRTC mode control's DIV2/Underline location DIV4 bits) every 1, 2 or 4 character clocks(commented parts), finally to call the addresswrap(which applies the byte/word/doubleword modes and applies the Address Wrap bit(all according to the XGA manual "Figure 2-65. CRT Memory Address Mapping"), finally passing that address(which always is 16-bits instead of the 18-bits required to access full VRAM) to patch_map1314(which converts the given 32 or 16-bit address and applies the row scan counter to it, which is disabled(set to 1) in the 640x480x256 mode, thus passing through the address from the addresswrap function).

The problem here is that the addresswrap function always truncates to 16-bits, according to the XGA manual, which has the effect of wrapping the data around 256K in VRAM(64K planar indexes=256K RAM, the upper 2 bits specify the higher regions of RAM in my emulation(up to 512KB or 1MB RAM. Currently 512KB is emulated in ET3000 mode). The high bits end up in bits 0-1 according to the XGA documentation, thus messing up display further.

How does the ET3000/ET4000 graphics cards handle this? I can't see anything in their manuals.

OPTINLINE void VGA_loadcharacterplanes(VGA_Type *VGA, SEQ_DATA *Sequencer, word x) //Load the planes!
{
INLINEREGISTER uint_32 loadedlocation, vramlocation; //The location we load at!
static uint_32 lastlocation; //Last latched location!
//Horizontal logic
VGA_Sequencer_planedecoder planesdecoder[2] = { VGA_TextDecoder, VGA_GraphicsDecoder }; //Use the correct decoder!

//Column logic
if (x) //Past the first position of the line(not the initial loading)?
{
++Sequencer->memoryaddressclockdivider; //Increase the address counter point by one character clock!
++Sequencer->memoryaddress; //Increase the memory address to be loaded!
}
else //First position on the line?
{
Sequencer->memoryaddressclockdivider = Sequencer->memoryaddressclock = Sequencer->memoryaddress = lastlocation = 0; //Address counters are reset!
}

/*if (Sequencer->memoryaddressclockdivider >= (1<<VGA->precalcs.characterclockshift)) //Divide memory address clock by 1/2/4!
{
Sequencer->memoryaddressclockdivider = 0; //Reset!
*/
lastlocation = Sequencer->memoryaddress; //Latch the new location!
//}

loadedlocation = lastlocation; //Load the address to be loaded!
loadedlocation += Sequencer->charystart; //Apply the line and start map to retrieve!
vramlocation = patch_map1314(VGA, addresswrap(VGA, loadedlocation)); //Apply address wrap and MAP13/14?

//Row logic
CGA_checklightpen(loadedlocation); //Check for anything requiring the lightpen on the CGA!

//Now calculate and give the planes to be used!
if (VGA->VRAM==0) goto skipVRAM; //VRAM must exist!
loadedlocation = VGA_VRAMDIRECTPLANAR(VGA,vramlocation); //Load the 4 planes from VRAM, one dword at a time!
skipVRAM: //No VRAM present to display?
planesbuffer[0] = (loadedlocation&0xFF); //Read plane 0!
loadedlocation >>= 8;
planesbuffer[1] = (loadedlocation & 0xFF); //Read plane 1!
loadedlocation >>= 8;
planesbuffer[2] = (loadedlocation & 0xFF); //Read plane 2!
loadedlocation >>= 8;
planesbuffer[3] = (loadedlocation & 0xFF); //Read plane 3!
//Now the buffer is ready to be processed into pixels!

planesdecoder[VGA->precalcs.graphicsmode](VGA,vramlocation); //Use the decoder to get the pixels or characters!

INLINEREGISTER byte lookupprecalcs;
lookupprecalcs = (byte)((SEQ_DATA *)Sequencer)->charinner_y;
lookupprecalcs <<= 1; //Make room!
lookupprecalcs |= CURRENTBLINK(VGA); //Blink!
lookupprecalcs <<= 1; //Make room for the pixelon!
currentattributeinfo.lookupprecalcs = lookupprecalcs; //Save the looked up precalcs, this never changes during a processed block of pixels (both text and graphics modes)!
}

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

Reply 5 of 5, by superfury

User metadata
Rank l33t++
Rank
l33t++

I notice that the first quarter of the screen (top quarter) contains the VRAM data as drawn (all four quarters of the screen op top of each other). The rest of the active display contains garbage.

The attachment 204-Windows 3.0 640x480x256 quarter contains all.png is no longer available

Can you see how I should fix this?

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