What's the effect of the VGA Byte/word/doubleword modes?

It seems that there's two byte/word/doubleword settings:

- The CRTC Mode Control's byte/word mode and Underline Location's Doubleword setting combine into byte/word/doubleword mode. The combine into a shift left by 0, 1 or 2 bits during rendering, affecting the final memory address read from VRAM?
- The CRTC Mode Control's Divide Memory Address by 2 and Underline Location's Divide Memory Address by 4. These combine into divide by 1, 2 or 4?

Anyone knows how these two work during CPU reads/writes and how they combine/affect rendering? These two settings, how are they applied in a real VGA? Are they both applied at the rendering, when a character clock loads information from the four memory planes? Or are they applied when the CPU reads and writes to display memory?
Last edited on
Would you please just buy Ferraro's book?

The CRTC Mode Control W/B and AW bits are used to manipulate the CPU's view of VRAM for CGA and HGC emulation modes. They don't affect rendering.

The CRTC Underline Location DW and CB4 bits do affect rendering: they control how VRAM is addressed in chained modes.

Remember, VRAM is internally divided into "planes", which is actually just each DWORD of VRAM split into individual bytes. This allows the hardware to do a single read and multiplex the four bits of each of the eight neighboring pixels to the CRT controllers at the same time.

These register bits turn that behavior off so that each byte can represent a single eight-bit pixel value, necessary for a 256-color mode.

Hope this helps.
At what point does the W/B and AW affect the CPU read/write? My routine is currently like this:

1. Convert the address to VGA base (substract 0xA0000, 0xB0000 or 0xB8000).
2. Apply the addressing mode (Chain 4 > odd/even > planar mode).
This results into an VRAM address and a byte containing the planes for usage with the plane mask.
3. These 2 variables are passed (together with the written value) to a function that applies the read/write mode and writes/reads from VRAM (writes according to map mask register anded with the given plane mask from step 2 and the offset in the plane calculated at step 2).

What happens when:
- The DIV2 bit in the CRTC mode control is set?
- The DIV4 bit in the Underline Location Register is set?
- When both DIV2 and DIV4 are set?

I assume that the W/B is applied between steps 2 and 3, and the AW immediately after it?
Last edited on
This is my current VGA rendering plane loader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void VGA_loadcharacterplanes(VGA_Type *VGA, SEQ_DATA *Sequencer, word x) //Load the planes!
{
	//Horizontal logic
	VGA_Sequencer_planedecoder planesdecoder[2] = { VGA_TextDecoder, VGA_GraphicsDecoder }; //Use the correct decoder!
	loadedlocation = x; //X!
	loadedlocation &= 0xFFF; //Limit!
	if (VGA->precalcs.graphicsmode) //Graphics mode?
	{
		loadedlocation >>= 3; //We take portions of 8 pixels, so increase our location every 8 pixels!
	}
	else //Gotten character width? Just here for safety!
	{
		loadedlocation <<= 1; //Multiply by 2 for our index!
		loadedlocation = VGA->CRTC.charcolstatus[loadedlocation]; //Divide by character width in text mode to get the correct character by using the horizontal clock!
	}

	loadedlocation >>= VGA->precalcs.characterclockshift; //Apply VGA shift: the shift is the ammount to move at a time!

	//Row logic
	loadedlocation += Sequencer->charystart; //Apply the line and start map to retrieve!

	//Now calculate and give the planes to be used!
	planesbuffer[0] = readVRAMplane(VGA, 0, loadedlocation, 1); //Read plane 0!
	planesbuffer[1] = readVRAMplane(VGA, 1, loadedlocation, 1); //Read plane 1!
	planesbuffer[2] = readVRAMplane(VGA, 2, loadedlocation, 1); //Read plane 2!
	planesbuffer[3] = readVRAMplane(VGA, 3, loadedlocation, 1); //Read plane 3!
	//Now the buffer is ready to be processed into pixels!

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


And this is the function decoding the CPU window (decodeCPUaddress):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
OPTINLINE uint_32 addresswrap(word memoryaddress) //Wraps memory arround 64k!
{
	register word address2; //Load the initial value for calculating!
	register word result;
	register byte temp;
	if (ActiveVGA->precalcs.BWDModeShift == 1) //Word mode?
	{
		result = memoryaddress; //Default: don't change!
		address2 = memoryaddress; //Load the address for calculating!
		temp = 0xD; //Load default location (13)
		temp |= (ActiveVGA->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW << 1); //MA15 instead of MA13 when set!
		address2 >>= temp; //Apply MA15/MA13 to bit 0!
		address2 &= 1; //Only load bit 0!
		result &= 0xFFFE; //Clear bit 0!
		result |= address2; //Add bit MA15/MA13 at bit 0!
		return result; //Give the result!
	}
	return memoryaddress; //Original address!
}

//decodeCPUaddress(Write from CPU=1; Read from CPU=0, offset (from VRAM start address), planes to read/write (4-bit mask), offset to read/write within the plane(s)).
OPTINLINE void decodeCPUaddress(byte towrite, uint_32 offset, byte *planes, uint_32 *realoffset)
{
	register uint_32 realoffsettmp;
	register byte calcplanes;
	if (ActiveVGA->registers->SequencerRegisters.REGISTERS.SEQUENCERMEMORYMODEREGISTER.Chain4Enable) //Chain 4 mode?
	{
		realoffsettmp = offset; //Original offset to start with!
		realoffsettmp >>= 2; //Rest of the bits. Multiples of 4 wont get written!
		realoffsettmp <<= ActiveVGA->precalcs.BWDModeShift; //Apply B/W/DW mode shift!
		realoffsettmp = addresswrap(realoffsettmp); //Apply address wrap!
		*realoffset = realoffsettmp; //Give the calculated offset!

		offset &= 0x3; //Walk through the planes!
		calcplanes = 1; //The default plane (plane 0)!
		calcplanes <<= offset; //Lower bits, create bitmask!
		return; //Done!
	}
	if (ActiveVGA->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.OddEvenMode) //Odd/Even mode?
	{
		//Odd/even mode used (compatiblity case)?
		//Do the same as VPC!
		realoffsettmp = offset; //Take the default offset!
		realoffsettmp &= 0xFFFE; //Clear bit 0 for our result!
		if (ActiveVGA->registers->GraphicsRegisters.REGISTERS.MISCGRAPHICSREGISTER.EnableOddEvenMode) //Chain using A0 selected? 0=0/2, 1=1/3!
		{
			calcplanes = offset;
			calcplanes &= 1; //Take 1 bit to determine the plane (0/1)!
		}
		else //Normal operations?
		{
			calcplanes = 0; //The plane calculated is always 0!
			realoffsettmp |= (offset & 1); //Linear operations within plane 0!
		}
		realoffsettmp <<= ActiveVGA->precalcs.BWDModeShift; //Apply B/W/DW mode shift!
		realoffsettmp = addresswrap(realoffsettmp); //Apply address wrap!
		*realoffset = realoffsettmp; //Give the calculated offset!

		calcplanes = (0x5 << calcplanes); //Convert to used plane (0/2 or 1/3)!
		*planes = calcplanes; //Load the planes to address!
		return; //Done!
	}

	if (towrite) //Writing access?
	{
		*planes = 0xF; //Write to all planes possible, map mask register does the rest!
	}
	else
	{
		*planes = 1; //Load plane 0!
		*planes <<= ActiveVGA->registers->GraphicsRegisters.REGISTERS.READMAPSELECTREGISTER.ReadMapSelect; //Take this plane!
	}
	realoffsettmp = offset; //Load the offset!
	realoffsettmp <<= ActiveVGA->precalcs.BWDModeShift; //Apply B/W/DW mode shift!
	realoffsettmp = addresswrap(realoffsettmp); //Apply address wrap only!
	//The offset is used directly!
}


Is this correct?

The BWDModeShift is 0(byte), 1(word) or 2(dword) depending on the W/B and DW bits.
The characterclockshift is 0(byte), 1(word) or 2(dword) depending on the DIV2 and CB4 (DIV4?) bits.
Last edited on
Topic archived. No new replies allowed.