VOGONS


First post, by superfury

User metadata
Rank l33t
Rank
l33t

According to Tseng's VDIAG, there's a problem with the Write Mode 3 emulation? It gives me an Error 7 about it.

This is the current code of applying the write modes:

OPTINLINE uint_32 LogicalOperation(uint_32 input)
{
switch (GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER,3,3))
{
case 0x00: /* None */
return input; //Unmodified
case 0x01: /* AND */
return input & LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch);
case 0x02: /* OR */
return input | LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch);
case 0x03: /* XOR */
return input ^ LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch);
default:
break;
};
return input; //Unknown, just give the input!
}

OPTINLINE uint_32 BitmaskOperation(uint_32 input, byte bitmaskregister)
{
INLINEREGISTER uint_32 result = 0; //The result built!
INLINEREGISTER uint_32 mask,inputdata; //Latch and extended mask!
//Load the mask to use, extend to all four planes!
mask = getActiveVGA()->ExpandTable[bitmaskregister]; //Load the current mask(one plane) expanded!
//Convert the value&latch to result using the mask!
inputdata = input; //Load the input to process!
inputdata &= mask; //Apply the mask to get the bits to turn on!
result |= inputdata; //Apply the bits to turn on to the result!
mask ^= 0xFFFFFFFF; //Flip the mask bits to get the bits to retrieve from the latch!
inputdata = LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch); //Load the latch!
inputdata &= mask; //Apply the mask to get the bits to turn on!
result |= inputdata; //Apply the bits to turn on to the result!
return result; //Give the resulting value!
}

/*

Core read/write operations!

*/

typedef uint_32 (*VGA_WriteMode)(uint_32 data);

uint_32 VGA_WriteModeLinear(uint_32 data) //Passthrough operation!
{
data = getActiveVGA()->ExpandTable[data]; //Make sure the data is on the all planes!
return data; //Give the resulting data!
}

uint_32 VGA_WriteMode0(uint_32 data) //Read-Modify-Write operation!
{
INLINEREGISTER byte curplane;
data = (byte)ror((byte)data, GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER,0,7)); //Rotate it! Keep 8-bit data!
data = getActiveVGA()->ExpandTable[data]; //Make sure the data is on the all planes!

curplane = 1; //Process all 4 plane bits!
do
{
if (GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.ENABLESETRESETREGISTER,0,0xF)&curplane) //Enable set/reset? (Mode 3 ignores this flag)
{
Show last 30 lines
			data = (data&(~getActiveVGA()->FillTable[curplane])) | getActiveVGA()->FillTable[GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.SETRESETREGISTER,0,0xF)&curplane]; //Turn all those bits off, and the set/reset plane ON=0xFF for the plane and OFF=0x00!
}
curplane <<= 1; //Next plane!
} while (curplane!=0x10); //Only the 4 planes are used!
data = LogicalOperation(data); //Execute the logical operation!
data = BitmaskOperation(data, getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER); //Execute the bitmask operation!
return data; //Give the resulting data!
}

uint_32 VGA_WriteMode1(uint_32 data) //Video-to-video transfer
{
return LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch); //Use the latch!
}

uint_32 VGA_WriteMode2(uint_32 data) //Write color to all pixels in the source address byte of VRAM. Use Bit Mask Register.
{
data = getActiveVGA()->FillTable[data&0xF]; //Replicate across all 4 planes to 8 bits set or cleared of their respective planes. The upper 4 bits of the CPU input are unused.
data = LogicalOperation(data); //Execute the logical operation!
data = BitmaskOperation(data, getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER); //Execute the bitmask operation fully!
return data;
}

uint_32 VGA_WriteMode3(uint_32 data) //Ignore enable set reset register!
{
data = ror(data, GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER,0,7)); //Rotate it! Keep 8-bit data!
data &= getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER; //AND with the Bit Mask field.
data = BitmaskOperation(LogicalOperation(getActiveVGA()->FillTable[GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.SETRESETREGISTER,0,0xF)]), data); //Use the generated data on the Set/Reset register
return data;
}

It's a simple 8-bits input, 32-bits output for the four planes.
Can anyone see what's going wrong?
I'm looking at Dosbox-X's peculiar way of handling said modes (https://github.com/joncampbell123/dosbox-x/bl … /vga_memory.cpp), but it's kind of difficult to see where the exact error lies?

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

Reply 1 of 4, by superfury

User metadata
Rank l33t
Rank
l33t

I've just modified it to function more like Dosbox's code:

OPTINLINE uint_32 ALUMaskLatchOperation(uint_32 input, uint_32 bitmask)
{
switch (GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER, 3, 3))
{
case 0x00: /* None */
return ((input&bitmask) | (LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch) &(~bitmask))); //Only apply the bitmask!
case 0x01: /* AND */
return ((input|(~bitmask)) & (LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch)));
case 0x02: /* OR */
return ((input&bitmask) | LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch));
case 0x03: /* XOR */
return ((input&bitmask) ^ LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch));
default:
//Shouldn't happen!
break;
};
return input; //Shouldn't happen!
}

/*

Core read/write operations!

*/

typedef uint_32 (*VGA_WriteMode)(uint_32 data);

uint_32 VGA_WriteModeLinear(uint_32 data) //Passthrough operation!
{
data = getActiveVGA()->ExpandTable[data]; //Make sure the data is on the all planes!
return data; //Give the resulting data!
}

uint_32 VGA_WriteMode0(uint_32 data) //Read-Modify-Write operation!
{
INLINEREGISTER byte curplane;
data = (byte)ror((byte)data, GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER,0,7)); //Rotate it! Keep 8-bit data!
data &= 0xFF; //Prevent overflow!
data = getActiveVGA()->ExpandTable[data]; //Make sure the data is on the all planes!

curplane = 1; //Process all 4 plane bits!
do
{
if (GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.ENABLESETRESETREGISTER,0,0xF)&curplane) //Enable set/reset? (Mode 3 ignores this flag)
{
data = (data&(~getActiveVGA()->FillTable[curplane])) | getActiveVGA()->FillTable[GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.SETRESETREGISTER,0,0xF)&curplane]; //Turn all those bits off, and the set/reset plane ON=0xFF for the plane and OFF=0x00!
}
curplane <<= 1; //Next plane!
} while (curplane!=0x10); //Only the 4 planes are used!
data = ALUMaskLatchOperation(data, getActiveVGA()->ExpandTable[getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER]); //Execute the bitmask operation!
return data; //Give the resulting data!
}

uint_32 VGA_WriteMode1(uint_32 data) //Video-to-video transfer
{
return LE32(getActiveVGA()->registers->ExternalRegisters.DATALATCH.latch); //Use the latch!
}

uint_32 VGA_WriteMode2(uint_32 data) //Write color to all pixels in the source address byte of VRAM. Use Bit Mask Register.
{
Show last 46 lines
	data = getActiveVGA()->FillTable[data&0xF]; //Replicate across all 4 planes to 8 bits set or cleared of their respective planes. The upper 4 bits of the CPU input are unused.
data = ALUMaskLatchOperation(data, getActiveVGA()->ExpandTable[getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER]); //Execute the bitmask operation fully!
return data;
}

uint_32 VGA_WriteMode3(uint_32 data) //Ignore enable set reset register!
{
data = ror(data, GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER,0,7)); //Rotate it! Keep 8-bit data!
data &= 0xFF; //Prevent overflow!
data = ALUMaskLatchOperation(getActiveVGA()->FillTable[GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.SETRESETREGISTER,0,0xF)], getActiveVGA()->ExpandTable[data]&getActiveVGA()->ExpandTable[getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER]); //Use the generated data on the Set/Reset register
return data;
}

uint_32 readbank = 0, writebank = 0; //Banked VRAM support!
byte VGA_forcelinearmode = 0; //Forcing linear mode?

OPTINLINE void VGA_WriteModeOperation(byte planes, uint_32 offset, byte val)
{
static const VGA_WriteMode VGA_WRITE[4] = {VGA_WriteMode0,VGA_WriteMode1,VGA_WriteMode2,VGA_WriteMode3}; //All write modes!
INLINEREGISTER byte curplane; //For plane loops!
INLINEREGISTER uint_32 data; //Default to the value given!
if (VGA_forcelinearmode) //Forced linear mode?
{
data = VGA_WriteModeLinear((uint_32)val); //What write mode?
}
else //Normal mode?
{
data = VGA_WRITE[GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER, 0, 3)]((uint_32)val); //What write mode?
}

byte planeenable = GETBITS(getActiveVGA()->registers->SequencerRegisters.REGISTERS.MAPMASKREGISTER,0,0xF); //What planes to try to write to!
if (((getActiveVGA()->precalcs.linearmode & 5) == 5) || (getActiveVGA()->precalcs.linearmode&8) || VGA_forcelinearmode) planeenable = 0xF; //Linear memory ignores this? Or are we to ignore the Write Plane Mask(linear byte mode)?
planeenable &= planes; //The actual planes to write to!
byte curplanemask=1;
curplane = 0;
do //Process all planes!
{
if (planeenable&curplanemask) //Modification of the plane?
{
writeVRAMplane(getActiveVGA(),curplane,offset,writebank,data&0xFF,1); //Write the plane from the data!
}
data >>= 8; //Shift to the next plane!
curplanemask <<= 1; //Next plane!
} while (++curplane!=4);
}

Together with the read mode 1 fix based on said code(just the way the color compare register is applied to the result by being anded with the color don't care register instead of flipping said bits to be compared for non-equality).
That seems to fix the VGAtest2 graphics reading of text tests.

VDIAG still reports the 'problem' with write mode 3 somehow?

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

Reply 3 of 4, by superfury

User metadata
Rank l33t
Rank
l33t
ripsaw8080 wrote on 2021-03-28, 06:11:

I notice that the rotate on write mode 3 isn't casted to byte as it is on write mode 0, but I don't know if it matters in the context of your code.

I don't know. Just added anyways.
Thinking about it, the ror macro takes variable size into account. If the size isn't correct (of the first parameter), the ror will fail (wrong shifts because of wrong variable size (in bytes) beging detected).

//Easy rotating!
#define ror(x,moves) ((x >> moves) | (x << (sizeof(x)*8 - moves)))
#define rol(x,moves) ((x << moves) | (x >> (sizeof(x)*8 - moves)))
uint_32 VGA_WriteMode3(uint_32 data) //Ignore enable set reset register!
{
data = (byte)ror((byte)data, GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER,0,7)); //Rotate it! Keep 8-bit data!
data &= 0xFF; //Prevent overflow!
data = ALUMaskLatchOperation(getActiveVGA()->FillTable[GETBITS(getActiveVGA()->registers->GraphicsRegisters.REGISTERS.SETRESETREGISTER,0,0xF)], getActiveVGA()->ExpandTable[data]&getActiveVGA()->ExpandTable[getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER]); //Use the generated data on the Set/Reset register
return data;
}

I remember adding the &= 0xFF after the ror because it was overflowing in the write mode 3 operation past 8-bit size(causing the ExpandTable index to overflow it's 256 entry size.
This is probably related to that.

So mainly the (byte)data in the ror() macro that's called was going wrong there?

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

Reply 4 of 4, by superfury

User metadata
Rank l33t
Rank
l33t

Hmmm.... The above bugfix causes VDIAG to no longer complain about the write mode 3 problem 😁

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