VGA's nonplanar memory layout?

Anyone knows what the VGA's real (256k) memory is like? My emulator currently reads the VRAM as a 65k times 4 array of bytes with each plane byte following the other planes (256k byte index = planar index times 4 plus selected plane). So plane:index=256k index:
0:0=0
1:0=1
2:0=2
3:0=3
0:1=4
1:1=5
2:1=6
3:1=7
etc.

This 256k index is also used during the mode 13h rendering, the pixels and rows being sequentially during virtual width=real width.

For some reason the mode 06h is giving junk output (all other modes work fine). When I implement the register modes used by this mode (CRTC mode control register bits 0, 1 & 5), I can't get it to work. Anyone knows what happens in a real VGA on the above plane, index and 256k index when toggling these bits? A scanline counter is also being used to determine the above 256k index.
Last edited on
Last edited on
It is called "chained mode" because the planes are "chained" together.

The underlying data, IIRC, is actually interleaved byte by byte across the four planes.

pixel,plane,index
0,0,0
1,1,0
2,2,0
3,3,0
4,0,1
5,1,1
etc

I'm pretty sure that's right, but I could be wrong, though. It has been many years, and from the application programmer's POV it doesn't matter.

[edit]
BTW, you still don't seem have taken the advice to just get yourself an old VGA card and hack at it. You should be able to change mode without clearing the display memory.

Set it to 13H, write sequential values into memory, change it to any planar mode you want, and read back what's there.
Last edited on
@Duoas, so in fact (if you look to the VRAM addresses as 32-bit integers (uint_32)),

- The pixels for mode 13h appear sequentially in VRAM (pixel 0, 1, 2, 3, 4, 5 .. scanline size, line 2 pixel 0, 1, 2, 3, 4, 5 etc).
- The planes appear interleaved, so each uint_32 contains 4 planes, from index 0-65535. (uint_32 fourplanes[0x10000]; Where each fourplanes item contains four planes. (or byte planedata[0x10000][4]; where planedata[index][plane] contains the data).

This is exactly how the VRAM in my emulator is build atm (although working with 32-bit values reversed byte order in order to make mode 13h work correctly).

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
extern uint_32 rowscanaddress; //Row scan address!
extern uint_32 rowscancounter; //Row scan counter!

byte LOG_VRAM_WRITES = 0;
extern byte is_loadchartable; //Loading character table, so no logging?


//Allow wrap is used to determine the hardware rendering!
inline uint_32 determinewrap(VGA_Type *VGA, uint_32 addr,byte is_renderer)
{
	return addr; //Disable changes: just take the default!
	uint_32 result = addr; //Default: take address itself!
	if (is_renderer) //Hardware side?
	{
		result -= rowscanaddress; //Substract the row scan address to get the base!
		rowscanaddress = addresswrap(VGA,rowscanaddress); //Wrap if needed!
		result += rowscanaddress; //Apply new row scan address!
		result = patch_map1314(VGA,rowscanaddress,rowscanaddress,rowscancounter); //Allow patching of MAP13&14!	
	}
	return result; //New offset!
}

inline byte getVRAM(VGA_Type *VGA,uint_32 fulloffset, byte is_renderer)
{
	if (!VGA->VRAM_size) return 0; //No size!
	if ((is_renderer&2)|(is_renderer&4)) //Allow double buffering?
	{
		if (!(VGA->VRAM_size&0x3)) //Bufferable VRAM (we need to be an exact multiple of 4 bytes)?
		{
			uint_32 multivals[4] = {0xFF000000,0x00FF0000,0x0000FF00,0x000000FF}; //Multiple values position!
			byte multipos[4] = {24,16,8,0}; //The positions to look for!

			if (is_renderer&2) //First buffer?
			{
				static uint_32 lastblock; //Last block of 4 values!
				static uint_32 lastvals; //Last values!
				if ((fulloffset&0xFFFFFFFB)!=lastblock) //Different block as last?
				{
					lastblock = (fulloffset&0xFFFFFFFB); //New block!
					uint_32 *newval = (uint_32 *)&VGA->VRAM[SAFEMODUINT32(lastblock,VGA->VRAM_size)]; //The new value to read!
					lastvals = *newval; //Read vals, making sure we loop arround at 4 bytes border!
				}
				return (((lastvals&multivals[fulloffset&3])>>multipos[fulloffset&3])&0xFF); //Take the buffered last block!
			}
			else if (is_renderer&4) //Second buffer?
			{
				static uint_32 lastblock2; //Last block of 4 values!
				static uint_32 lastvals2; //Last values!
				if ((fulloffset&0xFFFFFFFB)!=lastblock2) //Different block as last?
				{
					lastblock2 = (fulloffset&0xFFFFFFFB); //New block!
					uint_32 *newval = (uint_32 *)&VGA->VRAM[SAFEMODUINT32(lastblock2,VGA->VRAM_size)]; //Read vals, making sure we loop arround at 4 bytes border!
					lastvals2 = *newval; //Get!
				}
				return (((lastvals2&multivals[fulloffset&3])>>multipos[fulloffset&3])&0xFF); //Take the buffered last block!
			}
		}
	}
	return VGA->VRAM[SAFEMODUINT32(fulloffset,VGA->VRAM_size)]; //Give the data!
}

inline void putVRAM(VGA_Type *VGA,uint_32 fulloffset,byte value)
{
	if (!VGA->VRAM_size) return; //No size!
	VGA->VRAM[SAFEMODUINT32(fulloffset,VGA->VRAM_size)] = value; //Set the data!
}

//Direct access to VRAM, using start and offset!
inline byte readVRAMdirect(VGA_Type *VGA, uint_32 start, uint_32 offset, byte is_renderer) //Used in 256 color mode!
{
	return getVRAM(VGA,determinewrap(VGA,start+offset,is_renderer),is_renderer); //The full protected offset!
}

inline void writeVRAMdirect(VGA_Type *VGA, uint_32 start, uint_32 offset, byte value, byte is_renderer) //See readVRAMdirect above!
{
	putVRAM(VGA,determinewrap(VGA,start+offset,is_renderer),value); //The full protected offset!
}

//Planar access to VRAM, using 
inline byte readVRAMplane(VGA_Type *VGA, uint_32 start, byte plane, uint_32 offset, byte is_renderer) //Read from a VRAM plane!
{
	return getVRAM(VGA,determinewrap(VGA,((start+offset)<<2)|plane,is_renderer),is_renderer);
}

inline void writeVRAMplane(VGA_Type *VGA, uint_32 start, byte plane, uint_32 offset, byte value, byte is_renderer) //Write to a VRAM plane!
{
	putVRAM(VGA,determinewrap(VGA,((start+offset)<<2)|plane,is_renderer),value);
}

//Bit from left to right starts with 0(value 128) ends with 7(value 1)

byte getBitPlaneBit(VGA_Type *VGA, uint_32 start, int plane, uint_32 offset, byte bit, byte is_renderer)
{
	byte bits = readVRAMplane(VGA,start,plane,offset,is_renderer); //Get original bits!
	return GETBIT(bits,7-bit); //Give the bit!
}

void setBitPlaneBit(VGA_Type *VGA, uint_32 start, int plane, uint_32 offset, byte bit, byte on, byte is_renderer)
{
	byte bits = readVRAMplane(VGA,start,plane,offset,is_renderer); //Get original bits!
	if (on) //To turn bit on?
	{
		bits = SETBIT1(bits,7-bit); //Turn bit on!
	}
	else //To turn bit off?
	{
		bits = SETBIT0(bits,7-bit); //Turn bit off!
	}
	writeVRAMplane(VGA,start,plane,offset,bits,is_renderer); //Write the modified value back!
}
Topic archived. No new replies allowed.