VOGONS


Reply 20 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Anyone knows how the horizontal and vertical wrappings affect addresses and counters used when starting or continuing a new BitBlt transfer?

This is what I've gotten so far:

Details
void Tseng4k_startAccelerator()
{
uint_32 horizontalwrappings,horizontalsize;
//Start the accelerator's function.
//Load all internal precalcs required and initialize all local required CPU-readonly variables.
Tseng4k_decodeAcceleratorRegisters(); //Load all registers into the accelerator's precalcs!
//TODO: Load the read-only variables for the accelerator to start using. Also depends on the reloadPatternAddress and reloadSourceAddress ACL settings.
if (et34k(getActiveVGA())->W32_ACLregs.reloadPatternAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress = et34k(getActiveVGA())->W32_ACLregs.patternmapaddress; //Pattern address reload!
}
if (et34k(getActiveVGA())->W32_ACLregs.reloadSourceAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress = et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress; //Pattern address reload!
}
//Perform what wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xpatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Ypatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xsourcewrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Ysourcewrap]; //What horizontal wrapping to use!
//Perform wrapping of the inputs!
//First, wrap pattern!
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition; //Don't wrap our idea of X!
et34k(getActiveVGA())->W32_ACLregs.patternmap_y = et34k(getActiveVGA())->W32_ACLregs.Yposition; //Don't wrap our idea of Y!
/*
horizontalwrappings = 0; //Default: nothing is horizontally wrapped!
horizontalsize = (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1); //How large is the horizontal row?
if (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x!=(uint_32)~0) //X wrapping?
{
horizontalwrappings = et34k(getActiveVGA())->W32_ACLregs.Xposition / (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x + 1); //How many times is X wrapped?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition & et34k(getActiveVGA())->W32_ACLregs.patternwrap_x; //Wrap our idea of X!
}
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1); //How many times is X wrapped, adding to Y at the address level?
if (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y!=(uint_32)~0) //Y wrapping?
{
horizontalwrappings -= (et34k(getActiveVGA())->W32_ACLregs.patternmap_y / (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y + 1)) * (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y + 1); //How much is wrapped in the Y coordinate!
et34k(getActiveVGA())->W32_ACLregs.patternmap_y = et34k(getActiveVGA())->W32_ACLregs.patternmap_y & et34k(getActiveVGA())->W32_ACLregs.patternwrap_y; //Wrap our idea of Y!
}
else
{
horizontalwrappings = 0; //Nothing to wrap!
}
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1); //How many times is X wrapped, adding to Y at the address level?
*/
//Next, wrap source!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition; //Don't wrap our idea of X!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_y = et34k(getActiveVGA())->W32_ACLregs.Yposition; //Don't wrap our idea of Y!
/*
horizontalwrappings = 0; //Default: nothing is horizontally wrapped!
horizontalsize = (et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1); //How large is the horizontal row?
if (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x != (uint_32)~0) //X wrapping?
{
horizontalwrappings = et34k(getActiveVGA())->W32_ACLregs.Xposition / (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x + 1); //How many times is X wrapped?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition & et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x; //Wrap our idea of X!
}
if (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y != (uint_32)~0) //Y wrapping?
{
horizontalwrappings -= (et34k(getActiveVGA())->W32_ACLregs.sourcemap_y / (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y + 1)) * (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y + 1); //How much is wrapped in the Y coordinate!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_y = et34k(getActiveVGA())->W32_ACLregs.sourcemap_y & et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y; //Wrap our idea of Y!
}
Show last 14 lines
	et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1); //How many times is X wrapped, adding to Y at the address level?
*/

//Backup the original values before starting!
et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup = et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup = et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Backup!
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup = et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress; //Backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup = et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress; //Backup!
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Backup!
if ((et34k(getActiveVGA())->W32_MMUregisters[1][0x9C] & 7) == 0) //No CPU version?
{
et34k(getActiveVGA())->W32_acceleratorleft = 1; //Always more left until finishing! This keeps us running!
}
}

Each x and y step it simply wraps around the x multiple and y multiple (adding or substracting a multiple of the Y map offset for the source or pattern maps.

Details
byte et4k_stepy()
{
++et34k(getActiveVGA())->W32_ACLregs.Yposition;
et34k(getActiveVGA())->W32_ACLregs.destinationaddress = et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup; //Make sure that we're jumping from the original!
if (et34k(getActiveVGA())->W32_ACLregs.XYdirection&2) //Negative Y?
{
et34k(getActiveVGA())->W32_ACLregs.destinationaddress -= et34k(getActiveVGA())->W32_ACLregs.destinationYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.patternmap_y;
if (et34k(getActiveVGA())->W32_ACLregs.patternmap_y == ((uint_32)~0)) //Overflow?
{
if (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y != (uint_32)~0) //Wrapping Y?
{
et34k(getActiveVGA())->W32_ACLregs.patternmap_y = et34k(getActiveVGA())->W32_ACLregs.patternwrap_y; //Returning to the bottom!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress += ((et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1) * (((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternwrap_y) + 1)); //Apply Y address wrap!
}
}
--et34k(getActiveVGA())->W32_ACLregs.sourcemap_y;
if (et34k(getActiveVGA())->W32_ACLregs.sourcemap_y == ((uint_32)~0)) //Overflow?
{
if (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y != (uint_32)~0) //Wrapping Y?
{
et34k(getActiveVGA())->W32_ACLregs.sourcemap_y = et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y; //Returning to the bottom!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress += ((et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1) * (((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y) + 1)); //Apply Y address wrap!
}
}
}
else //Positive Y?
{
et34k(getActiveVGA())->W32_ACLregs.destinationaddress += et34k(getActiveVGA())->W32_ACLregs.destinationYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress += et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress += et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.patternmap_y;
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_y > (uint_64)et34k(getActiveVGA())->W32_ACLregs.patternwrap_y) //Wrapping point reached?
{
et34k(getActiveVGA())->W32_ACLregs.patternmap_y = 0; //Reset!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1) * (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y + 1); //Go back to the backup address!
}
++et34k(getActiveVGA())->W32_ACLregs.sourcemap_y;
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_y > (uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y) //Wrapping point reached?
{
et34k(getActiveVGA())->W32_ACLregs.sourcemap_y = 0; //Reset!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= (et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1) * (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y + 1); //Go back to the backup address!
}
}
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Save the new line on the destination address to jump back to!
if (et34k(getActiveVGA())->W32_ACLregs.Yposition>et34k(getActiveVGA())->W32_ACLregs.Ycount)
{
//Leave Y position and addresses alone!
return 2; //Y count reached!
}
return 0; //No overflow!
}

byte et4k_stepx()
{
byte result;
++et34k(getActiveVGA())->W32_ACLregs.Xposition;
if (et34k(getActiveVGA())->W32_ACLregs.XYdirection&1) //Negative X?
Show last 30 lines
	{
--et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Next address!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_x == ((uint_32)~0)) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x += (uint_32)(et34k(getActiveVGA())->W32_ACLregs.patternwrap_x + 1); //Wrap it!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_x == ((uint_32)~0)) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x += (uint_32)(et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x + 1); //Wrap it!
}
else //Positive X?
{
++et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Next address!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_x > (uint_64)et34k(getActiveVGA())->W32_ACLregs.patternwrap_x) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x -= (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x+1); //Wrap it!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_x > (uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x -= (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x+1); //Wrap it!
}
if (et34k(getActiveVGA())->W32_ACLregs.Xposition>et34k(getActiveVGA())->W32_ACLregs.Xcount)
{
//X overflow? Step vertically!
et34k(getActiveVGA())->W32_ACLregs.Xposition = 0; //Reset
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup; //Restore the backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup; //Restore the backup!
result = 1 | (et4k_stepy()); //Step Y! X count reached!
return result; //Give the result!
}
return 0; //No overflow!
}

So theoretically, the X and Y pattern steps and wrapping should be correctly applied during execution.
The main issue is with the initialization during startAccelerator(), where it tries to determine the X and Y coordinate and map displacement to start with?
Anyone?

Btw WhatVGA's unwrapped transfer works like a charm. It's the wrapped transfer that somehow gives weird results?
Edit: I currently just base the X and Y wrapping initial locations and values on a combination of the wrap settings and the given X and Y positions(MMU registers 94h and 96h), combined applied to said setting (simply wrapping around the X and Y coordinates using division and modulo) to obtain the vertical displacement based on wrapping(and performing the horizontal wrapping by performing a simple modulo)? Then it applies the vertical wrappings to the start address in memory just like the normal Y stepping does.
FInally, it performs the same using a division for the Y divisor(added to the address) and Y modulo(used as the Y row to start counting at)?
Would that be correct behaviour?

Details
uint_32 Tseng4k_wrap_x[8] = { 0,0,3,7,0xF,0x1F,0x3F,~0 }; //X wrapping masks
uint_32 Tseng4k_wrap_y[8] = { 1,2,4,8,~0,~0,~0,~0 }; //Y wrapping masks

void Tseng4k_startAccelerator()
{
int_64 horizontalwrappings,horizontalsize;
//Start the accelerator's function.
//Load all internal precalcs required and initialize all local required CPU-readonly variables.
Tseng4k_decodeAcceleratorRegisters(); //Load all registers into the accelerator's precalcs!
//TODO: Load the read-only variables for the accelerator to start using. Also depends on the reloadPatternAddress and reloadSourceAddress ACL settings.
if (et34k(getActiveVGA())->W32_ACLregs.reloadPatternAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress = et34k(getActiveVGA())->W32_ACLregs.patternmapaddress; //Pattern address reload!
}
if (et34k(getActiveVGA())->W32_ACLregs.reloadSourceAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress = et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress; //Pattern address reload!
}
//Perform what wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xpatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Ypatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xsourcewrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Ysourcewrap]; //What horizontal wrapping to use!
//Perform wrapping of the inputs!
//First, wrap pattern!
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition; //Don't wrap our idea of X!
et34k(getActiveVGA())->W32_ACLregs.patternmap_y = et34k(getActiveVGA())->W32_ACLregs.Yposition; //Don't wrap our idea of Y!
horizontalwrappings = 0; //Default: nothing is horizontally wrapped!
horizontalsize = (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1); //How large is the horizontal row?
if (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x!=(uint_32)~0) //X wrapping?
{
horizontalwrappings = (int_64)(et34k(getActiveVGA())->W32_ACLregs.Xposition / (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x + 1)); //How many times is X wrapped?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition & et34k(getActiveVGA())->W32_ACLregs.patternwrap_x; //Wrap our idea of X!
}
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1); //How many times is X wrapped, adding to Y at the address level?
if (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y!=(uint_32)~0) //Y wrapping?
{
horizontalwrappings -= (int_64)((et34k(getActiveVGA())->W32_ACLregs.patternmap_y / (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y + 1)) * (et34k(getActiveVGA())->W32_ACLregs.patternwrap_y + 1)); //How much is wrapped in the Y coordinate!
et34k(getActiveVGA())->W32_ACLregs.patternmap_y = et34k(getActiveVGA())->W32_ACLregs.patternmap_y & et34k(getActiveVGA())->W32_ACLregs.patternwrap_y; //Wrap to our idea of Y!
}
else
{
horizontalwrappings = 0; //Nothing to wrap!
}
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1); //How many times is X wrapped, adding to Y at the address level?
//Next, wrap source!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition; //Don't wrap our idea of X!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_y = et34k(getActiveVGA())->W32_ACLregs.Yposition; //Don't wrap our idea of Y!
horizontalwrappings = 0; //Default: nothing is horizontally wrapped!
horizontalsize = (et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1); //How large is the horizontal row?
if (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x != (uint_32)~0) //X wrapping?
{
horizontalwrappings = (int_64)(et34k(getActiveVGA())->W32_ACLregs.Xposition / (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x + 1)); //How many times is X wrapped?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.Xposition & et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x; //Wrap our idea of X!
}
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1); //How many times is X wrapped, adding to Y at the address level?
if (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y != (uint_32)~0) //Y wrapping?
{
horizontalwrappings -= (int_64)((et34k(getActiveVGA())->W32_ACLregs.sourcemap_y / (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y + 1)) * (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y + 1)); //How much is wrapped in the Y coordinate!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_y = et34k(getActiveVGA())->W32_ACLregs.sourcemap_y & et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y; //Wrap to our idea of Y!
Show last 18 lines
	}
else
{
horizontalwrappings = 0; //Nothing to wrap!
}
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= horizontalwrappings * (et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1); //How many times is X wrapped, adding to Y at the address level?

//Backup the original values before starting!
et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup = et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup = et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Backup!
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup = et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress; //Backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup = et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress; //Backup!
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Backup!
if ((et34k(getActiveVGA())->W32_MMUregisters[1][0x9C] & 7) == 0) //No CPU version?
{
et34k(getActiveVGA())->W32_acceleratorleft = 1; //Always more left until finishing! This keeps us running!
}
}

Anyone?

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

Reply 21 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Also nice to note: Windows 95 now actually seems to detect the ET4000/W32 instead of a plain ET4000AX!

Although that's probably because more of the chipset is now emulated(pretty much all but the CRTC/Sprite outputs to the DAC, which has it's registers implemented but not the functionality behind most of them(All the CRTC/Sprite rendering stuff isn't implemented, nor is it ever reported as being busy in the Input Status registers(also not implemented yet))). So if software will try to use it, it will see that the CRTC is never displaying itself (the same for the Sprite). The other new functionalities (ACL and IMA) should theoretically work, although the ACL still has some wrapping issues and the IMA functionality is untested(It just moves the data received in the specified address using the specified row offset into VRAM at the specified VRAM offset, nothing more, nothing less, straight VRAM copy being done there(the reverse for read operations, but using the same linear-ish algorithm of translating the host address straight to a VRAM address and simply writing/reading said byte))).

Would that IMA functionality be correct? Is it just a linear straight input address at specified pitch to VRAM at specified pitch conversion, without any other processing? I've just exploited the high VRAM address mapping with an added pitch conversion(simple modulo and divide, then multiply on the output address) without any other processing being done? Is it always just forwarding single bytes to and from VRAM?

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

Reply 22 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmmm... After rebooting, Windows 95 "C"/OSR 2.5 seems to wait for the BFF36 bit 0 to clear, but the CRTC register 36h is 0x43 and register 37h is 0x0F?

So the MMU registers aren't enabled, but the CPU tries to access them anyways?
It would need to have bit 5 of CRTC register 36h set, but it isn't set?

Edit: Are reads also protected by the "KEY"? (The KEY is the 0x03 to 3BF, Bits 7&5 set to 3B8/3D8, another to clear the KEY(3B8/3D8 bits 7&5 not both set, according to ET4000/W32))
PCem seems to not apply any protection at all for the W32i and up, while Dosbox seems to protect both reads and writes(reads returning 0 without the KEY)?

The ET4000/W32p documentation seems to imply only writes are protected, while reads succeed always(unprotected)?

Also, the ET4000/W32i seems to imply the checks for the KEY are more simple (only checking bits 7&5 to enable, clearing either of those bits at any time disables the extensions? Where the normal ET4000AX checks the entire byte on both setting and clearing the KEY and requires a 0x3BF=1 write before writing the mode control to clear it as well?)

Hmmm... The last time setting the key doesn't even address any of the extended registers? The CRTC index is left off at index 11h?

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

Reply 23 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... Does the ET4000/W32 respond to segment A000-BFFF addresses when it isn't setup to use B000-BFFF or B800-BFFF? Like always reading 0 for those addresses when setup for segment A000-AFFF and B000-B7FF? Of course when the accelerator MMU map is disabled in register 36h? Anyone?

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

Reply 24 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Windows 95 keeps hanging because of the unmapped MMU register area, even with proper low memory area unmapping during linear address, 64K memory limit when disabling the Extended RAM bit in the Sequencer Mode Control Register(bit 1 of index 04h) instead of the old VGA wrapping on the ET4000/W32 for CPU accesses(no wrapping of any kind when reading from hardware), improved Sequencer Memory Mode register initialization(setting bit 2) and Misc Output Register(setting bits 1 and 5), all memory accesses by the CPU passing through a byte memory read/write function that handles all wrapping etc. with hardware accesses not wrapping at all(except the full memory wrapping of the connected VRAM address itself(simply limiting to the amount of bits the installed VRAM has(a power of 2))).

The BIOS still detects only 1MB(both with 2MB and 4MB installed), while Windows 95 still hangs polling the BFF36h address with the MMU memory-mapped registers disabled in the Video Configuration 1 register?

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

Reply 25 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... Implementing https://github.com/86Box/86Box/blob/master/sr … vid_et4000w32.c 's algorithm for X/Y wrapping into UniPCemu's W32's wrapping (although using function calls for each wrap of pattern and source, as well as the precalcs) seems to improve it a bit and destroy another part.

Horizontal timings seem to work, but the center area drawn using a simple unwrapped BitBlt in UniPCemu causes the area to vertically have all kinds of weird pattern imprinted in white and black on it?

Initialization of a new transfer:

Details
void Tseng4k_decodeAcceleratorRegisters()
{
//TODO: Load and decode all accelerator registers into easy to use variables in the accelerator
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress = (getTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0x80]) & 0x3FFFFF); //Pattern map address
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress = (getTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0x84]) & 0x3FFFFF); //Source map address
et34k(getActiveVGA())->W32_ACLregs.patternYoffset = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x88]) & 0xFFF); //Pattern Y offset
et34k(getActiveVGA())->W32_ACLregs.sourceYoffset = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x8A]) & 0xFFF); //Source Y offset
et34k(getActiveVGA())->W32_ACLregs.destinationYoffset = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x8C]) & 0xFFF); //Destination Y offset
et34k(getActiveVGA())->W32_ACLregs.virtualbussize = (et34k(getActiveVGA())->W32_MMUregisters[1][0x8E]&3); //Virtual bus size, powers of 2! 0=1, 1=2, 2=4, 3=Reserved
et34k(getActiveVGA())->W32_ACLregs.XYdirection = (et34k(getActiveVGA())->W32_MMUregisters[1][0x8F] & 3); //0=+1,+1; 1=-1,+1; 2=+1,-1; 3=-1,-1. Essentially bit 0=X direction, bit 1=Y direction. Set=Decreasing, Cleared=Increasing
et34k(getActiveVGA())->W32_ACLregs.Xpatternwrap = (et34k(getActiveVGA())->W32_MMUregisters[1][0x90] & 7); //Power of 2. more than 64 or less than 4 is none.
et34k(getActiveVGA())->W32_ACLregs.Ypatternwrap = ((et34k(getActiveVGA())->W32_MMUregisters[1][0x90] >> 4) & 7); //Power of 2. more than 8 is none.
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6 = ((et34k(getActiveVGA())->W32_MMUregisters[1][0x90] >> 6) & 1); //Bit 6 only
et34k(getActiveVGA())->W32_ACLregs.Xsourcewrap = (et34k(getActiveVGA())->W32_MMUregisters[1][0x92] & 7); //See pattern wrap
et34k(getActiveVGA())->W32_ACLregs.Ysourcewrap = ((et34k(getActiveVGA())->W32_MMUregisters[1][0x92] >> 4) & 7); //See pattern wrap
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6 = ((et34k(getActiveVGA())->W32_MMUregisters[1][0x92] >> 6) & 1); //See pattern wrap
et34k(getActiveVGA())->W32_ACLregs.Xposition = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x94]) & 0xFFF); //X position
et34k(getActiveVGA())->W32_ACLregs.Yposition = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x96]) & 0xFFF); //Y position
et34k(getActiveVGA())->W32_ACLregs.Xcount = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x98]) & 0xFFF); //X count
et34k(getActiveVGA())->W32_ACLregs.Ycount = (getTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x9A]) & 0xFFF); //Y count
et34k(getActiveVGA())->W32_ACLregs.reloadPatternAddress = GETBITS(et34k(getActiveVGA())->W32_MMUregisters[1][0x9D],1,1); //Reload of pattern address. When 1, use the internal pattern address register.
et34k(getActiveVGA())->W32_ACLregs.reloadSourceAddress = GETBITS(et34k(getActiveVGA())->W32_MMUregisters[1][0x9D],0,1); //Reload of source address. When 1, use the internal source address register.
et34k(getActiveVGA())->W32_ACLregs.BGFG_RasterOperation[0] = et34k(getActiveVGA())->W32_MMUregisters[1][0x9E]; //Index 0=BG, 1=FG
et34k(getActiveVGA())->W32_ACLregs.BGFG_RasterOperation[1] = et34k(getActiveVGA())->W32_MMUregisters[1][0x9F]; //Index 0=BG, 1=FG
et34k(getActiveVGA())->W32_ACLregs.destinationaddress = (getTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0xA0]) & 0x3FFFFF); //Destination address
}

void Tseng4k_encodeAcceleratorRegisters()
{
//TODO: Save and encode all changable accelerator registers that can be modified into the accelerator registers for the CPU to read.
setTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0x80], (et34k(getActiveVGA())->W32_ACLregs.patternmapaddress & 0x3FFFFF)); //Internal Pattern address
setTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0x84], (et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress & 0x3FFFFF)); //Internal Source address
setTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0xA4], (et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress & 0x3FFFFF)); //Internal Pattern address
setTsengLE24(&et34k(getActiveVGA())->W32_MMUregisters[1][0xA8], (et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress & 0x3FFFFF)); //Internal Source address
setTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x94], (et34k(getActiveVGA())->W32_ACLregs.Xposition) & 0xFFF); //X position
setTsengLE16(&et34k(getActiveVGA())->W32_MMUregisters[1][0x96], (et34k(getActiveVGA())->W32_ACLregs.Yposition) & 0xFFF); //Y position
}

uint_32 Tseng4k_wrap_x[8] = { 0,0,3,7,0xF,0x1F,0x3F,~0 }; //X wrapping masks
uint_32 Tseng4k_wrap_y[8] = { 1,2,4,8,~0,~0,~0,~0 }; //Y wrapping masks

void Tseng4k_calcPatternSourceXY(uint_32* patternsourcex, uint_32* patternsourcex_backup, uint_32* patternsourcey, uint_32 *patternsourceaddress, uint_32 *patternsourceaddress_backup, uint_32 patternsourcewrapx, uint_32 patternsourcewrapy, byte patternsourcewrapbit6)
{
*patternsourcex = *patternsourcey = 0; //Init!
//Handle horizontal first!
if (patternsourcewrapx) //Wrapping horizontally?
{
*patternsourcex = *patternsourceaddress & patternsourcewrapx;
*patternsourceaddress &= ~patternsourcewrapx; //Wrap!
}
*patternsourceaddress_backup = *patternsourceaddress;
//Now, handle vertical wrap!
if (!patternsourcewrapbit6) //Bit 6 not set?
{
*patternsourcey = (*patternsourceaddress / (patternsourcewrapx + 1)) & (patternsourcewrapy - 1);
*patternsourceaddress_backup &= ~(((patternsourcewrapx + 1) * patternsourcewrapy) - 1);
}
*patternsourcex_backup = *patternsourcex;
}

Show last 48 lines
void Tseng4k_startAccelerator()
{
int_64 horizontalwrappings,horizontalsize;
//Start the accelerator's function.
//Load all internal precalcs required and initialize all local required CPU-readonly variables.
Tseng4k_decodeAcceleratorRegisters(); //Load all registers into the accelerator's precalcs!
//TODO: Load the read-only variables for the accelerator to start using. Also depends on the reloadPatternAddress and reloadSourceAddress ACL settings.
if (et34k(getActiveVGA())->W32_ACLregs.reloadPatternAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress = et34k(getActiveVGA())->W32_ACLregs.patternmapaddress; //Pattern address reload!
}
if (et34k(getActiveVGA())->W32_ACLregs.reloadSourceAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress = et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress; //Pattern address reload!
}
//Perform what wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xpatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Ypatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xsourcewrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Ysourcewrap]; //What horizontal wrapping to use!
//Perform wrapping of the inputs!
//First, wrap pattern!
//Next, wrap source!
Tseng4k_calcPatternSourceXY(&et34k(getActiveVGA())->W32_ACLregs.patternmap_x,
&et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup,
&et34k(getActiveVGA())->W32_ACLregs.patternmap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress,
&et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6 //Unsupported paramter on the ET4000/W32?
);
Tseng4k_calcPatternSourceXY(&et34k(getActiveVGA())->W32_ACLregs.sourcemap_x,
&et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup,
&et34k(getActiveVGA())->W32_ACLregs.sourcemap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress,
&et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6 //Unsupported paramter on the ET4000/W32?
);
//Backup the original values before starting!
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Backup!
if ((et34k(getActiveVGA())->W32_MMUregisters[1][0x9C] & 7) == 0) //No CPU version?
{
et34k(getActiveVGA())->W32_acceleratorleft = 1; //Always more left until finishing! This keeps us running!
}
}

Applying of wrapping during runtime:

Details
void et4k_dowrappatternsourceyinc(uint_32 *patternsourcey, uint_32 *patternsourceaddress, byte patternsourcewrapbit6, uint_32 patternsourceaddress_backup, uint_32 patternsourcewrapy, uint_32 patternsourcewrapx)
{
++*patternsourcey;
if (*patternsourcey == patternsourcewrapy)
{
*patternsourcey = 0;
*patternsourceaddress = patternsourceaddress_backup;
}
}

void et4k_dowrappatternsourceydec(uint_32* patternsourcey, uint_32* patternsourceaddress, byte patternsourcewrapbit6, uint_32 patternsourceaddress_backup, uint_32 patternsourcewrapy, uint_32 patternsourcewrapx)
{
--*patternsourcey;
if ((*patternsourcey == (uint_32)(~0)) && !patternsourcewrapbit6)
{
*patternsourcey = patternsourcewrapy - 1;
*patternsourceaddress = patternsourceaddress_backup + (patternsourcewrapx * (patternsourcewrapy - 1));
}
}

byte et4k_stepy()
{
++et34k(getActiveVGA())->W32_ACLregs.Yposition;
et34k(getActiveVGA())->W32_ACLregs.destinationaddress = et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup; //Make sure that we're jumping from the original!
if (et34k(getActiveVGA())->W32_ACLregs.XYdirection&2) //Negative Y?
{
et34k(getActiveVGA())->W32_ACLregs.destinationaddress -= et34k(getActiveVGA())->W32_ACLregs.destinationYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1; //Next address!
et4k_dowrappatternsourceydec(
&et34k(getActiveVGA())->W32_ACLregs.patternmap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x
);
et4k_dowrappatternsourceydec(
&et34k(getActiveVGA())->W32_ACLregs.sourcemap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x
);
}
else //Positive Y?
{
et34k(getActiveVGA())->W32_ACLregs.destinationaddress += et34k(getActiveVGA())->W32_ACLregs.destinationYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress += et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress += et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1; //Next address!
et4k_dowrappatternsourceyinc(
&et34k(getActiveVGA())->W32_ACLregs.patternmap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x
);
et4k_dowrappatternsourceyinc(
Show last 52 lines
			&et34k(getActiveVGA())->W32_ACLregs.sourcemap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x
);
}
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Save the new line on the destination address to jump back to!
if (et34k(getActiveVGA())->W32_ACLregs.Yposition>et34k(getActiveVGA())->W32_ACLregs.Ycount)
{
//Leave Y position and addresses alone!
return 2; //Y count reached!
}
return 0; //No overflow!
}

byte et4k_stepx()
{
byte result;
++et34k(getActiveVGA())->W32_ACLregs.Xposition;
if (et34k(getActiveVGA())->W32_ACLregs.XYdirection&1) //Negative X?
{
--et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Next address!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_x == ((uint_32)~0)) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x += (uint_32)(et34k(getActiveVGA())->W32_ACLregs.patternwrap_x + 1); //Wrap it!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_x == ((uint_32)~0)) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x += (uint_32)(et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x + 1); //Wrap it!
}
else //Positive X?
{
++et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Next address!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_x > (uint_64)et34k(getActiveVGA())->W32_ACLregs.patternwrap_x) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x -= (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x+1); //Wrap it!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_x > (uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x -= (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x+1); //Wrap it!
}
if (et34k(getActiveVGA())->W32_ACLregs.Xposition>et34k(getActiveVGA())->W32_ACLregs.Xcount)
{
//X overflow? Step vertically!
et34k(getActiveVGA())->W32_ACLregs.Xposition = 0; //Reset
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup; //Restore the backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup; //Restore the backup!
result = 1 | (et4k_stepy()); //Step Y! X count reached!
return result; //Give the result!
}
return 0; //No overflow!
}

Edit: Improved it somewhat:
Initialization:

Details
uint_32 Tseng4k_wrap_x[8] = { 0,0,3,7,0xF,0x1F,0x3F,~0 }; //X wrapping masks
uint_32 Tseng4k_wrap_y[8] = { 1,2,4,8,~0,~0,~0,~0 }; //Y wrapping masks

void Tseng4k_calcPatternSourceXY(uint_32* patternsourcex, uint_32* patternsourcex_backup, uint_32* patternsourcey, uint_32 *patternsourceaddress, uint_32 *patternsourceaddress_backup, uint_32 patternsourcewrapx, uint_32 patternsourcewrapy, byte patternsourcewrapbit6)
{
*patternsourcex = *patternsourcey = 0; //Init!
//Handle horizontal first!
if (patternsourcewrapx) //Wrapping horizontally?
{
*patternsourcex = *patternsourceaddress & patternsourcewrapx;
*patternsourceaddress &= ~patternsourcewrapx; //Wrap!
}
*patternsourceaddress_backup = *patternsourceaddress;
//Now, handle vertical wrap!
if (!patternsourcewrapbit6) //Bit 6 not set?
{
*patternsourcey = (*patternsourceaddress / (patternsourcewrapx + 1)) & (patternsourcewrapy - 1);
*patternsourceaddress_backup &= ~(((patternsourcewrapx + 1) * patternsourcewrapy) - 1);
}
*patternsourcex_backup = *patternsourcex;
}

void Tseng4k_startAccelerator()
{
int_64 horizontalwrappings,horizontalsize;
//Start the accelerator's function.
//Load all internal precalcs required and initialize all local required CPU-readonly variables.
Tseng4k_decodeAcceleratorRegisters(); //Load all registers into the accelerator's precalcs!
//TODO: Load the read-only variables for the accelerator to start using. Also depends on the reloadPatternAddress and reloadSourceAddress ACL settings.
if (et34k(getActiveVGA())->W32_ACLregs.reloadPatternAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress = et34k(getActiveVGA())->W32_ACLregs.patternmapaddress; //Pattern address reload!
}
if (et34k(getActiveVGA())->W32_ACLregs.reloadSourceAddress == 0) //To reload the pattern address?
{
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress = et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress; //Pattern address reload!
}
//Perform what wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xpatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y = Tseng4k_wrap_y[et34k(getActiveVGA())->W32_ACLregs.Ypatternwrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x = Tseng4k_wrap_x[et34k(getActiveVGA())->W32_ACLregs.Xsourcewrap]; //What horizontal wrapping to use!
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y = Tseng4k_wrap_y[et34k(getActiveVGA())->W32_ACLregs.Ysourcewrap]; //What horizontal wrapping to use!
//Perform wrapping of the inputs!
//First, wrap pattern!
//Next, wrap source!
Tseng4k_calcPatternSourceXY(&et34k(getActiveVGA())->W32_ACLregs.patternmap_x,
&et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup,
&et34k(getActiveVGA())->W32_ACLregs.patternmap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress,
&et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6 //Unsupported paramter on the ET4000/W32?
);
Tseng4k_calcPatternSourceXY(&et34k(getActiveVGA())->W32_ACLregs.sourcemap_x,
&et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup,
&et34k(getActiveVGA())->W32_ACLregs.sourcemap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress,
&et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x,
Show last 10 lines
		et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6 //Unsupported paramter on the ET4000/W32?
);
//Backup the original values before starting!
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Backup!
if ((et34k(getActiveVGA())->W32_MMUregisters[1][0x9C] & 7) == 0) //No CPU version?
{
et34k(getActiveVGA())->W32_acceleratorleft = 1; //Always more left until finishing! This keeps us running!
}
}

Mainly fixed the Y wrapping precalcs to be correct(they were being 0-based, which shouldn't happen for vertical precalcs, since performing -1 to get a base of 0 will cause 1-1=row 0 to instead turn into 0-1=ffffffffh, which is an invalid line number altogether for wrapping operations!).

And x/y stepping:

Details
void et4k_dowrappatternsourceyinc(uint_32 *patternsourcey, uint_32 *patternsourceaddress, byte patternsourcewrapbit6, uint_32 patternsourceaddress_backup, uint_32 patternsourcewrapy, uint_32 patternsourcewrapx)
{
++*patternsourcey;
if (*patternsourcey == patternsourcewrapy)
{
*patternsourcey = 0;
*patternsourceaddress = patternsourceaddress_backup;
}
}

void et4k_dowrappatternsourceydec(uint_32* patternsourcey, uint_32* patternsourceaddress, byte patternsourcewrapbit6, uint_32 patternsourceaddress_backup, uint_32 patternsourcewrapy, uint_32 patternsourcewrapx)
{
--*patternsourcey;
if ((*patternsourcey == (uint_32)(~0)) && (!patternsourcewrapbit6))
{
*patternsourcey = patternsourcewrapy - 1;
*patternsourceaddress = patternsourceaddress_backup + ((patternsourcewrapx+1) * (patternsourcewrapy - 1));
}
}

byte et4k_stepy()
{
++et34k(getActiveVGA())->W32_ACLregs.Yposition;
et34k(getActiveVGA())->W32_ACLregs.destinationaddress = et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup; //Make sure that we're jumping from the original!
if (et34k(getActiveVGA())->W32_ACLregs.XYdirection&2) //Negative Y?
{
et34k(getActiveVGA())->W32_ACLregs.destinationaddress -= et34k(getActiveVGA())->W32_ACLregs.destinationYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress -= et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress -= et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1; //Next address!
et4k_dowrappatternsourceydec(
&et34k(getActiveVGA())->W32_ACLregs.patternmap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x
);
et4k_dowrappatternsourceydec(
&et34k(getActiveVGA())->W32_ACLregs.sourcemap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x
);
}
else //Positive Y?
{
et34k(getActiveVGA())->W32_ACLregs.destinationaddress += et34k(getActiveVGA())->W32_ACLregs.destinationYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress += et34k(getActiveVGA())->W32_ACLregs.patternYoffset + 1; //Next address!
et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress += et34k(getActiveVGA())->W32_ACLregs.sourceYoffset + 1; //Next address!
et4k_dowrappatternsourceyinc(
&et34k(getActiveVGA())->W32_ACLregs.patternmap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalpatternaddress,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.patternmapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_y,
et34k(getActiveVGA())->W32_ACLregs.patternwrap_x
);
et4k_dowrappatternsourceyinc(
Show last 52 lines
			&et34k(getActiveVGA())->W32_ACLregs.sourcemap_y,
&et34k(getActiveVGA())->W32_ACLregs.internalsourceaddress,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_bit6,
et34k(getActiveVGA())->W32_ACLregs.sourcemapaddress_backup,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_y,
et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x
);
}
et34k(getActiveVGA())->W32_ACLregs.destinationaddress_backup = et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Save the new line on the destination address to jump back to!
if (et34k(getActiveVGA())->W32_ACLregs.Yposition>et34k(getActiveVGA())->W32_ACLregs.Ycount)
{
//Leave Y position and addresses alone!
return 2; //Y count reached!
}
return 0; //No overflow!
}

byte et4k_stepx()
{
byte result;
++et34k(getActiveVGA())->W32_ACLregs.Xposition;
if (et34k(getActiveVGA())->W32_ACLregs.XYdirection&1) //Negative X?
{
--et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Next address!
--et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Next address!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_x == ((uint_32)~0)) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x += (uint_32)(et34k(getActiveVGA())->W32_ACLregs.patternwrap_x + 1); //Wrap it!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_x == ((uint_32)~0)) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x += (uint_32)(et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x + 1); //Wrap it!
}
else //Positive X?
{
++et34k(getActiveVGA())->W32_ACLregs.destinationaddress; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.patternmap_x; //Next address!
++et34k(getActiveVGA())->W32_ACLregs.sourcemap_x; //Next address!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.patternmap_x > (uint_64)et34k(getActiveVGA())->W32_ACLregs.patternwrap_x) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.patternmap_x -= (et34k(getActiveVGA())->W32_ACLregs.patternwrap_x+1); //Wrap it!
if ((uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcemap_x > (uint_64)et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x) //X overflow according to wrapping?
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x -= (et34k(getActiveVGA())->W32_ACLregs.sourcewrap_x+1); //Wrap it!
}
if (et34k(getActiveVGA())->W32_ACLregs.Xposition>et34k(getActiveVGA())->W32_ACLregs.Xcount)
{
//X overflow? Step vertically!
et34k(getActiveVGA())->W32_ACLregs.Xposition = 0; //Reset
et34k(getActiveVGA())->W32_ACLregs.patternmap_x = et34k(getActiveVGA())->W32_ACLregs.patternmap_x_backup; //Restore the backup!
et34k(getActiveVGA())->W32_ACLregs.sourcemap_x = et34k(getActiveVGA())->W32_ACLregs.sourcemap_x_backup; //Restore the backup!
result = 1 | (et4k_stepy()); //Step Y! X count reached!
return result; //Give the result!
}
return 0; //No overflow!
}

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

Reply 26 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just fixed a few little things:
- Don't apply n+1 for the wrapping's y coordinate addition on x during execution (thus taking the size in bits(power of 2)-1 for the line size in VRAM?).
- Don't update the source and pattern registers with the finished pattern/source map addresses after a transfer finishes?

I still see the weird pattern(it looks like some pattern inputs are going wrong?) for what looks like every 2nd scanline drawn in the pallette in the center of the screen?

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

Reply 27 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm.... When I look at what WhatVGA is writing when setting up the 640x480x8 mode for the BitBlt test, I see that it writes MMU register 0x90 with 0x12 and register 0x92 with 0x77. So Pattern wrap is wrapping around 2 lines(which seems to work itself), but the source wrap isn't wrapping at all(as it's indeed supposed to)?

Why does it use an errnenous pattern wrap around 2 lines? It seems to supposed to be 1 line instead?

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

Reply 28 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just tried running the ET4000/W32 with a W32 Windows 3.1 driver from a floppy disk on a Windows 3.1 setup.

Just like Windows 95, it will hang waiting for the bits to clear while the MMU Memory Mapped Registers mapping is disabled in the ET4000?

Edit: Segment 447 that's hanging doesn't reveal much, other than originating from segment 467h and checking the version of the W32 EC 21xA register as well as addressing some MMU memory-mapped registers (and 3CD paging registers) that are unmapped by the Tseng chipset.

Filename
debugger_segment447.zip
File size
118.47 KiB
Downloads
34 downloads
File comment
Segment 447 running and hanging.
File license
Fair use/fair dealing exception

Edit: Segment 467:

Filename
debugger_segment467.zip
File size
69.63 KiB
Downloads
36 downloads
File comment
Segment 467 running.
File license
Fair use/fair dealing exception

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

Reply 29 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. Changing the version reported to 0xB(11 in WhatVGA's documentation) has no effect on it(other than making the code/driver set a few extra bytes in memory which have no effect on the issue).

So why isn't the ET4000/W32 MMU registers and/or accelerator windows (MMU 0-2) enabled?

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

Reply 30 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... 0467:0261 is called from 037F:4E70 apparently... Time for more digging (segment 37F logging)!
Edit: Yup. Logging a long time. So far already 1.4GB in the log file, not having reached the issue parts yet.
Edit: Might be a main windows process that I'm logging though?
Edit: Said log of segment 467h+36Fh(4.11GB text file, which is segment 467(first 1.1MB) followed by segment 36F(which is almost the entire file)).
https://www.dropbox.com/s/aaevef4cl5vki77/deb … Flogged.7z?dl=0

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

Reply 31 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Originally put the rendering of the second CRTC/Sprite at the MCLK level. That gave wrong output in half clock modes (dot clock / 2 modes).

Then I moved it to the attribute controller level. That caused issues in 8-bit color modes(due to the attribute controller aborting half of the time in said modes).

Finally I moved it to the attribute controller output stage (so it ticks at the DAC input level, together with the DAC). That seems to fix the rendered sprite in all modes.

One weird thing that I notice with the WhatVGA test is that the first line of the rendered sprite acts weird? All other lines act normal, but the first scanline of the sprite seems to have a weird dot at the left side instead of what it's supposed to have in the horizontal center of the sprite? That's right before it reaches the horizontal location of the start of the circle that WhatVGA draws(so a few pixels horizontally left of the circle, on the first scanline it draws a few pixels as foreground 0xFF, followed by transparent pixels until the end of the scanline?). It's probably supposed to draw that at the center of the first scanline instead?

Edit: Fixed a latching (x latching to be exact) issue of the first scanline of the sprites and CRTC. That fixes the display of the first scanline.
The few weird foreground 0xFF pixels seem to be one or two scanlines down somehow? Perhaps an issue with the creation of the exact bitmap used?
Otherwise it's working correctly.

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

Reply 32 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just fixed the rendering of the Sprites. There were a few little things still missing:
- Non-active display current scanline rendering bit support(bit 6 of Input Status 0 register) to properly support the input status 0 register. The same with other non0active display areas(overscan and rendering NOP(for retracing)).
- Bits 6 and 2 of input status 0 register. Bit 2 being in the CRTC window(overlay active?) and bit 6 being if the current scanline will render or has rendered the Sprite/CRTC window as far as I can tell?
- CRTCB interrupt after the last scanline of the CRTC/Sprite window is rendered(is this based on bit 6 of input status 0 register going from high to low?).

Although I currently have nothing to test the CRTCB functionality with yet?

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

Reply 33 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just found a little bug in the ACL Routing Control Register bits 4-5 case 01h never being detected(The "CPU address is Destination address" case). It was always evaluating to false due to a ==1 check instead of a ==0x10 check.

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

Reply 34 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Hmmm... The Sprite window seems to work without issues now 😁

All the rendering by the WhatVGA BitBlt tests fail in much the same way?

1382-WhatVGA BitBlt fails.png
Filename
1382-WhatVGA BitBlt fails.png
File size
5.61 KiB
Views
573 views
File comment
Failing WhatVGA BitBlt operations?
File license
Fair use/fair dealing exception

Edit: Just found a BIG BitBlt bug: it was using the source map x on the reading of the pattern map address and the pattern map x on the source map address! Big Oops (unless it's Monkey Island, where it's a Big Whoop!)! *FACEPALMS*

Edit: After said bug fix, look what WhatVGA does now (showing the test of the 320x200 256 color BitBlt test (mode 13h)):

1383-WhatVGA BitBlt works as intended.png
Filename
1383-WhatVGA BitBlt works as intended.png
File size
6.34 KiB
Views
564 views
File comment
WhatVGA works as intended for the first time!
File license
Fair use/fair dealing exception

😁 😁 😁 😁 😁 😁 😁 😁
At least I think that might be as intended? PCem gives a different blitted result though?
Looking at the 800x600 mode 30h tests, the center and bottom blits seem to be done correctly, but the right, left and top blits only show the first color (red)'s first bar (so the lowest shade horizontally of the red bar originally printed on the left side of the screen) of the four red horizontal bars and a little bit of the second red bar? The remainder seems to be transparent or not blitted at all?

Interesting behaviour?

1384-Weird blitting at 800x600 mode 30h.png
Filename
1384-Weird blitting at 800x600 mode 30h.png
File size
15.56 KiB
Views
561 views
File comment
Weird BitBlt in mode 30h
File license
Fair use/fair dealing exception

Can anyone tell me something about what's happening there?

Although all this still doesn't explain why all Windows versions Tseng drivers seem to have the MMU unmapping issue with the ET4000/W32's MMU and memory mapped registers?
Edit: Btw, the first image in this post has a black rectangle rendered on the left BitBlt copy. That's not actually in video RAM, but simply the WhatVGA's setup of the Sprite that it somehow leaves active(bit 7 of the Image Port register is still set) when it's supposed to deactivate it when returning from the Sprite/cursor tests. But it somehow leaves said bit enabled, thus you see the sprite overlapping all available display modes. It should probably disable the sprite when returning to the menus, but it somehow doesn't?

Edit: Hmmm... Interesting! Forcing (in the precalcs) the ET4000/W32's CRTC register 36h to be interpreted as if bits 3 and 5 are always set causes Windows 95 to continue booting.
The sprite function is actually activated and displaying a normal cursor horizontally interleaved with black lines it seems? I can move the mouse around and it will adjust the position of said sprite accordingly?
Edit: Hmmm... Bits 1 and 0 of MMUregister 36h report being set, while bit 2 is cleared(indicating a transfer isn't being done)? So it has something stored in it's buffer, but isn't starting the transfer?
Edit: Also, the queue from the memory write is filled (value 0xFF is waiting to be processed), while bit 2 of the MMU register 36h is cleared, so it's preventing the transfer from happening?

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

Reply 35 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Anyone knows what happens when the accelerator is written through the MMU apertures(MMU 0-2) while it's setup in the queue'd registers to not be using CPU input (in other words: mode #0 in the routing control register's lower 3 bits)?

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

Reply 36 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just found a bug in the MMU 0-2 apertures: it was applying the offset specified in the MMU 0-2 base address registers incorrectly in some of them:
- MMU area 0 had the MMU 2 aperture offset applied to it (which is incorrect), with the MMU 0 offset applied on top of that for normal VRAM mode (so MMU 2 in virtual space (depending on addressing mode and linear mode) and MMU 0 added to that in raw VRAM addresses).
- All MMU areas didn't have their MMU 0-2 aperture applied to the address during the processing in the Tseng chipset-specific part.

What it does now is use the MMU 0-2 offset for the selected area and add it to the VRAM addresses during normal VRAM mode only.
When in accelerator mode (passthrough to the accelerator), it will give it the address from the start of the MMU aperture together with the aperture number selected. The accelerator itself then does basic checking(like waitstates etc.) and after passing those and starting to buffer the address and data into the queue it will add the MMU aperture's base address to it (to make the accelerator use the correct base address for the aperture, which previously was none(windows 1 and 2) or the wrong(aperture 2's base address instead of aperture 0's base address)).

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

Reply 37 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. Fixing the loading of the destination address when the queue is written to be applied correctly, the Windows 95 start menu already looks somewhat better.

Although there's still a big block of gray color plot on the top-left of the screen?

Although I still need to perform a manual override of the CRTC register 36h bits 3&5 to always be interpreted set, because Windows never seems to properly set those bits (or write to the register at all) before starting the ACL raster operations (otherwise it waits for the 0xFF returned at the location of the status register, which isn't mapped for the CPU to see, thus floating bus and reading 0xFF)?

1385-Windows 95 rendering improved somewhat.png
Filename
1385-Windows 95 rendering improved somewhat.png
File size
64.56 KiB
Views
528 views
File comment
Windows 95 rendering improved somewhat.
File license
Fair use/fair dealing exception

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

Reply 38 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

OK. When fixing the accelerator to properly become idle when input is expected and no input is queued, Windows 95 seems to continue booting the remainder of the desktop it seems (instead of hanging).

Of course all of this is with the special code in place enforcing the enabling of the extended MMU windows (instead of normally using bits 5&3 of CRTC register 36h).

The Sprite cursor is also displaying kind of weird? It has interleaved transparent pixels horizontally on all rows (vertical timing seems fine?) at even intervals (almost like it's reading incorrect pixel data from VRAM?). It also seems to be too wide, with the right half of the cursor seeming to become out of range(past the 64x64 barrier)?
Otherwise, the Sprite-based cursor seems fully functional?

Edit: Hmmm... It looks like stuff like images and icons are displayed correctly (together with selections). Stuff like windows aren't being drawn at all it seems? The same for text (other than the text (which are images) in the sidebar of Windows 95 C/OSR 2.5)? Buttons are also missing?

1387-Windows 95 boot complete with gaps after returning from the shutdown menu.png
Filename
1387-Windows 95 boot complete with gaps after returning from the shutdown menu.png
File size
77.87 KiB
Views
511 views
File comment
Windows 95 booted with the patch on the CRTC 36h register always bits 3&5 set in precalcs.
File license
Fair use/fair dealing exception

Edit: Although the special register CRTC 36h register bits patch is still required (because Windows 3.1 and 95's drivers refuse to set them when they're needed).

Edit: Just tried Windows 3.1 setup with the ET4000/W32 drivers and put in 256 color mode.
It fills the display with a light grey pattern, with every so many lines a black line? And the first lines until the first black horizontal line (until about 5-8 scanlines in?) seems to be the top of the blue background having been rendered?
Other than that, at the right hand side there's a vertical displacement of said pattern? Shifted slightly down/up?

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

Reply 39 of 69, by superfury

User metadata
Rank l33t++
Rank
l33t++

Does anyone know exactly where the ET4000/W32 CRTC/Sprite controller is within the chip? Is it after the attribute controller stage, ticking pixels at the attribute controller output rate?
Or is it before the attribute controller stage somehow? How does the 8-bit/256 color attribute controller mode affect the Sprite/CRTC's pixel output? What about the Dot Clock / 2 setting in the Sequencer register?

In other words: how does the chip determine the sprite's color depth? And when is the sprite data retrieved and ticked to the next pixel of the sprite data?
How do they combine with Dot Clock / 2(Sequencer register 01h) and the attribute controller 8/16-bit setting?

Edit: Just moved the CRTC/Sprite window back to before the attribute controller (hopefully that's correct behaviour? It's replacing the attribute controller in said process or overruling it's result when using the inverted sprite?) and applying inversion after the attribute controller(before sending to the DAC). The CRTC/Sprite thus ticks at the same speed as the attribute controller inputs(thus the dot clock)?

Horizontal zoom currently zooms within the window that's selected? And vertical zoom makes the window larger while duplicating scanlines (since each line is rendered multiple times, according to documentation)?

Edit: Hmmm... The DAC seems to be in 15BPP mode? And the ET4000/W32 is supplying 8BPP pixels which are combined by the DAC into 16BPP pixels?
The basic colors of the mouse cursor seem fine(no weird colouring due to latching of the DAC), but it looks like it gets a transparent pixel every few entries for some reason?
Edit: Hmmm... After switching to what I think is 256 color mode (although I can barely see what I'm doing due to the missing elements in the Windows rendering) the mouse sprite seems to disappear?
Edit: It only contains 0xAA bytes, thus no sprite is visible?

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