VOGONS


CGA on VGA emulation in x86EMU?

Topic actions

Reply 120 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

Apparently it's not entirely 50%:

Snap 2016-04-12 at 16.02.34.png
Filename
Snap 2016-04-12 at 16.02.34.png
File size
167 KiB
Views
735 views
File comment
50% image faded in.
File license
Fair use/fair dealing exception
Snap 2016-04-12 at 16.03.51.png
Filename
Snap 2016-04-12 at 16.03.51.png
File size
435.43 KiB
Views
735 views
File comment
And once it starts fading out.
File license
Fair use/fair dealing exception

Here's a good image of what happens. Since the other effects don't have this, they stay ~50% the entire time.

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

Reply 121 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

It just occurred to me: my CGA-on-VGA applies the start address by simply multiplying the CGA start address by 2(SHL 1) to obtain the VGA Start Address. This might be the cause? Only multiply by 2 when in word mode(not mode 6h)? If it's monochrome(byte) mode, the start address is to be used directly: the VRAM is linear to the CPU(Both the CPU memory and rendering memory(plane 0 to be exact) are mapped 1:1 in this mode, thus no need to multiply by 2 in that case. Else row 100+ would end up rendering line 200+ in increments of 2 lines for each scanline)

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

Reply 122 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

It just occurred to me: my CGA-on-VGA applies the start address by simply multiplying the CGA start address by 2(SHL 1) to obtain the VGA Start Address. This might be the cause? Only multiply by 2 when in word mode(not mode 6h)? If it's monochrome(byte) mode, the start address is to be used directly: the VRAM is linear to the CPU(Both the CPU memory and rendering memory(plane 0 to be exact) are mapped 1:1 in this mode, thus no need to multiply by 2 in that case. Else row 100+ would end up rendering line 200+ in increments of 2 lines for each scanline)

The fact that the top half of the picture is correct makes this seem unlikely to me - if you were shifting it wrong the picture would be compressed or stretched. The CGA's start address is always measured in 2-byte words, but the VGA's start address is (IIUC) always measured in bytes.

Reply 123 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just verified that by trying to convert it into byte quantities in VGA's byte mode and word quantities in VGA's word mode. It immediately messes up some parts of the demo. And the half black screens stay(it only makes it worse).

Maybe the VGA's CRTC AW bit is the cause? This shifts bit 13/15 into bit 1 of the adresses?

OPTINLINE word patch_map1314(VGA_Type *VGA, word addresscounter) //Patch full VRAM address!
{ //Check this!
word memoryaddress = addresscounter; //New row scan to use!
SEQ_DATA *Sequencer;
Sequencer = (SEQ_DATA *)VGA->Sequencer; //The sequencer!

word rowscancounter = Sequencer->rowscancounter; //The row scan counter we use!

register word bit; //Load row scan counter!
if (!VGA->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13) //a13=Bit 0 of the row scan counter!
{
//Row scan counter bit 1 is placed on the memory bus bit 14 during active display time.
//Bit 1, placed on memory address bit 14 has the effect of quartering the memory.
bit = rowscancounter; //Current row scan counter!
bit &= 1; //Bit0 only!
bit <<= 13; //Shift to our position (bit 13)!
memoryaddress &= 0xDFFF; //Clear bit13!
memoryaddress |= bit; //Set bit13 if needed!
}

if (!VGA->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP14) //a14<=Bit 1 of the row scan counter!
{
bit = rowscancounter; //Current row scan counter!
bit &= 2; //Bit1 only!
bit <<= 13; //Shift to our position (bit 14)!
memoryaddress &= 0xBFFF; //Clear bit14;
memoryaddress |= bit; //Set bit14 if needed!
}

return memoryaddress; //Give the linear address!
}

OPTINLINE word addresswrap(VGA_Type *VGA, word memoryaddress) //Wraps memory arround 64k!
{
register word address2; //Load the initial value for calculating!
register word result;
register byte temp;
if (VGA->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 |= (VGA->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!
}

Also one little thing: do MAP13, MAP14 and/or Address Wrap affect the memory addressed by the CPU itself on a VGA? To imitate the effect the CGA and MDA have with their repeating 16K and 4K VRAM window?

Edit: Tried running the CGA Compatiblity Tester again:
The part that scrolls vertically does scroll vertically, but gives a bit odd output:
The first window is correct.
The second window is displaced about 60% to the right.
The third window is displaced about 30% to the left.
The fourth window is displaced about 30% to the right.
This repeats infinitely in that order.

The part that scrolls horizontally seems to shift left pretty correctly (besides tearing up borders and crosses) and moving up and down 1 pixel every screen scrolled it seems.

Btw it's CGA monitor tests give correct output now! 😁 Except of course the calibration screen only showing 50%.

Snap 2016-04-12 at 18.04.45.png
Filename
Snap 2016-04-12 at 18.04.45.png
File size
122.37 KiB
Views
714 views
File comment
CGA Compatibility Tester aspect ratio screen.
File license
Fair use/fair dealing exception

Edit: I've moved the memory wrapping of CGA/MDA to the VGA_MMU module. It will now wrap around 0x4000 on CGA and 0x1000 on MDA(simply by anding the address of the CPU, after substracting B8000, with 0x3FFF(CGA) or 0xFFF(MDA)).

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

Reply 124 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Also one little thing: do MAP13, MAP14 and/or Address Wrap affect the memory addressed by the CPU itself on a VGA? To imitate the effect the CGA and MDA have with their repeating 16K and 4K VRAM window?

I think they just affect the CRTC/RAM relationship - IIUC the CPU/RAM relationship on VGA is always linear (times the 4 bitplanes).

superfury wrote:

Btw it's CGA monitor tests give correct output now! 😁 Except of course the calibration screen only showing 50%.

So it sounds like everything is working correctly when the start address is 0 - it's just when the CGA start address is >=0x1000 (i.e. VGA start address is >=0x2000, i.e. the second 8kB region) that you're getting this weird blanking.

Reply 125 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've edited the way the CGA/MDA handle the MAP13 and Address Word values. They now look at the CGA Interlace register and CGA Mode Control register to determine the 'correct' values:

void updateCGAmapping()
{
switch (getActiveVGA()->registers->CGARegisters[8]&3) //What mode?
{
case 0:
case 2: //Straight?
if (getActiveVGA()->registers->Compatibility_CGAModeControl&2) //Graphics mode?
{
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 0; //Graphics enables CGA graphics MAP13, else text!
}
else //Text mode?
{
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 1; //Graphics enables CGA graphics MAP13, else text!
}
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 1; //CGA mapping is done by the renderer mapping CGA!
break;
case 1: //Interleaved?
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 0; //Graphics enables CGA graphics MAP13, else text!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 0; //CGA mapping is done by the renderer mapping CGA!
break;
case 3: //Interleaved video?
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 0; //Graphics enables CGA graphics MAP13, else text!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 1; //CGA mapping is done by the renderer mapping CGA!
break;
}
}

This function is called when either the CGA Mode Control register updates the mode or when the Interlace register is updated.

Now most of the demo shows up without many errors (the faces still being horizontally displaced, but they're vertically whole again. Also the flower girl has flashes in the middle of the screen (I think the image is reduced to about 20 pixels high in the center of the screen about one frame per second, or something close to that. It looks like turning the monitor on again once a second)).

So the only problems currently left is the flower girl 'blinking', the Kefrens Bars blanking completely and the final heads going wrong horizontally(over the edge of the active display? Wrapping?).

Anyone knows what the cause might be? In the meantime I'll make a little video of the current rendering again:)

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

Reply 126 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Now most of the demo shows up without many errors (the faces still being horizontally displaced, but they're vertically whole again. Also the flower girl has flashes in the middle of the screen (I think the image is reduced to about 20 pixels high in the center of the screen about one frame per second, or something close to that. It looks like turning the monitor on again once a second)).

Sounds like the CPU is getting out of sync with the raster beam, which suggests a problem with the status register (but we already went over that). Maybe if you can make a dump of 40ms or so worth of CGA register writes and status register reads during the effect I can tell what's going on from that.

superfury wrote:

the Kefrens Bars blanking completely

This effect is never going to display correctly without emulating the CPU instruction timings correctly. The vertical overscan/retrace showing up during what should be active video time is a typical symptom of this.

superfury wrote:

and the final heads going wrong horizontally(over the edge of the active display? Wrapping?).

This is a standard 80-column text mode horizontally. The one thing that's slightly unusual is that it doesn't use an "int 10" to go into this mode - it just programs all the CGA registers directly. So if there's some VGA register (9/8 Dot Mode?) that you're accidentally depending on getting set correctly and your "int 10" handler normally does this, that could account for what you're seeing.

Reply 127 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

The sync part is already obvious by now, else the effect wouldn't be happening. The emulator doesn't do anything but listen to the CGA Mode Control/pallette registers/CRTC register changing. Those should be fully translated to VGA-compatible (in the case of the normal registers). With of course the CGA CRTC values (display, blanking, retrace and total) being used and calculated instead of the normal VGA registers. All other CRTC registers are simply translated to VGA values (when needed) and stored in their VGA equivalents for the renderer to see.

The CGA mode control updates the palettes when needed(just as the palette register does when written to) and updates some CRT timing in the VGA to be compatible to the mode selected. The CGA CRT register translations take care of the rest of the timing etc. required.

//Foreground colors: Red green yellow(not set), Magenta cyan white(set), Black red cyan white on a color monitor(RGB)!
byte CGA_lowcolors[3][4] = {{0,0x2,0x4,0x6},{0,0x3,0x5,0x7},{0,0x3,0x4,0x7}};
extern byte CGA_RGB; //Are we a RGB monitor(1) or Composite monitor(0)?

void setCGAMDAColors(byte isGraphics, byte GraphicsMode)
{
byte i,color;
if ((!isGraphics) && GraphicsMode) //MDA enabled?
{
//Apply the MDA palette registers!
for (i=0;i<0x10;i++) //Process all colours!
{
color = 0; //Default to black!
if (i&0x8) //Bright(8-F)?
{
color |= 2; //Set bright attribute!
}
if (i&0x7) //On (1-7 and 9-15)?
{
color |= 1; //Set on attribute!
}
switch (color) //What color to map?
{
default:
case 0: //Black=Black!
case 3: //Bright foreground=Bright foreground!
break;
case 1: //Normal on?
color = 2; //Bright!
break;
case 2: //Bright background!
color = 1; //Lighter!
break;
}
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.PALETTEREGISTERS[i].DATA = color; //Make us equal!
}
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.OVERSCANCOLORREGISTER = 0; //This forces black overscan! We don't have overscan!
}
//Apply the new CGA palette register?
else if (isGraphics) //Graphics mode?
{
if (!GraphicsMode) //High resolution graphics mode(640 pixels)?
{
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.OVERSCANCOLORREGISTER = 0; //Black overscan!
}

for (i=0;i<0x10;i++) //Process all colours!
{
color = i; //Default to the normal color!
if (GraphicsMode) //Color mode? 320x200!
{
if (!i) //Background color?
{
color = (getActiveVGA()->registers->Compatibility_CGAPaletteRegister&0xF); //Use the specified background color!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.OVERSCANCOLORREGISTER = color; //It also applies to the overscan!
}
else //Three foreground colors?
{
if (getActiveVGA()->registers->Compatibility_CGAModeControl&0x4) //Disabling the color burst bit applies 3rd palette on RGB monitor?
{
Show last 271 lines
						color = CGA_lowcolors[2][i&3]; //Use the RGB-specific 3rd palette!
}
else //Normal palettes?
{
color = CGA_lowcolors[(getActiveVGA()->registers->Compatibility_CGAPaletteRegister&0x20)?1:0][i&3]; //Don't use the RGB palette, use the normal palettes!
}
if (getActiveVGA()->registers->Compatibility_CGAPaletteRegister&0x10) //Display in high intensity?
{
color |= 0x8; //Display in high intensity!
}
}
}
else //B/W mode?
{
if (i) //We're on?
{
color = (getActiveVGA()->registers->Compatibility_CGAPaletteRegister&0xF); //Use the specified ON color!
}
}
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.PALETTEREGISTERS[i].DATA = color; //Make us the specified value!
}
}
else //Text mode?
{
for (i=0;i<0x10;i++) //Process all colours!
{
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.PALETTEREGISTERS[i].DATA = i; //Make us equal!
}
/*if (getActiveVGA()->registers->Compatibility_CGAModeControl&0x10) //High resolution graphics mode(640 pixels)?
{
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.OVERSCANCOLORREGISTER = 0; //This forces black overscan!
}
else //Use overscan!
{*/
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.OVERSCANCOLORREGISTER = (getActiveVGA()->registers->Compatibility_CGAPaletteRegister&0xF);
//}
}

VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_ALL_SECTION|WHEREUPDATED_ATTRIBUTECONTROLLER); //We have been updated(whole attribute controller mode)!
RENDER_updateCGAColors(); //Update the NTSC color translation if required!
}

//Compatibility handling on both writes and reads to compatibility registers!
void applyCGAMDAPaletteRegisters()
{
if (getActiveVGA()->registers->specialMDAflags&1) //MDA mode?
{
setCGAMDAColors(0,1); //MDA text mode!
}
else if (getActiveVGA()->registers->Compatibility_CGAModeControl&0x2) //Graphics mode?
{
if (getActiveVGA()->registers->Compatibility_CGAModeControl&0x10) //2 colour graphics?
{
setCGAMDAColors(1,0); //Set up basic 2-color graphics!
}
else //4 colour?
{
setCGAMDAColors(1,1); //Set up basic 4-color graphics!
}
}
else //Text mode?
{
setCGAMDAColors(0,0); //CGA Text mode!
}
}

void applyCGAPaletteRegister() //Update the CGA colors!
{
applyCGAMDAPaletteRegisters(); //Apply the palette registers!
}

void updateCGAmapping()
{
switch (getActiveVGA()->registers->CGARegisters[8]&3) //What mode?
{
case 0:
case 2: //Straight?
if (getActiveVGA()->registers->Compatibility_CGAModeControl&2) //Graphics mode?
{
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 0; //Graphics enables CGA graphics MAP13, else text!
}
else //Text mode?
{
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 1; //Graphics enables CGA graphics MAP13, else text!
}
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 1; //CGA mapping is done by the renderer mapping CGA!
break;
case 1: //Interleaved?
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 0; //Graphics enables CGA graphics MAP13, else text!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 0; //CGA mapping is done by the renderer mapping CGA!
break;
case 3: //Interleaved video?
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP13 = 0; //Graphics enables CGA graphics MAP13, else text!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 1; //CGA mapping is done by the renderer mapping CGA!
break;
}
}

//useGraphics: 0 for text mode, 1 for graphics mode! GraphicsMode: 0=B/W graphics or CGA text mode, 1=4 color graphics or MDA text mode
void setCGAMDAMode(byte useGraphics, byte GraphicsMode, byte blink) //Rendering mode set!
{
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.ShiftRegisterInterleaveMode = ((useGraphics && GraphicsMode)?1:0);
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.MISCGRAPHICSREGISTER.AlphaNumericModeDisable = useGraphics;
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.COLORPLANEENABLEREGISTER.DATA = (useGraphics&&!GraphicsMode)?0x1:0xF; //CGA: enable all color planes!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.AttributeControllerGraphicsEnable = useGraphics; //Text mode!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.MonochromeEmulation = ((!useGraphics) && GraphicsMode); //MDA attributes!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.BlinkEnable = ((!useGraphics) && blink)?1:0; //Use blink when not using graphics and blink is enabled!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.UNDERLINELOCATIONREGISTER.UnderlineLocation = ((!useGraphics) && GraphicsMode)?0xC:0x1F; //Monochrome emulation applies MDA-compatible underline, simple detection by character height!
updateCGAmapping(); //Update the rendering mapping by the CGA!
}

void applyCGAMemoryMap(byte useGraphics, byte GraphicsMode) //Apply the current CGA memory map!
{
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.MISCGRAPHICSREGISTER.MemoryMapSelect = ((!useGraphics) && GraphicsMode)?2:3; //Use map B000(MDA) or B800(CGA), depending on the adapter used!
if (useGraphics && (!GraphicsMode)) //Special case? CGA monochrome mode?
{
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.OddEvenMode = 0; //Don't force odd/even mode!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.READMAPSELECTREGISTER.ReadMapSelect = 0; //Only read map #0!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.COLORDONTCAREREGISTER.ColorCare = 0; //Care about this only!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.MISCGRAPHICSREGISTER.EnableOddEvenMode = 0; //Disable chaining!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.SEQUENCERMEMORYMODEREGISTER.OEDisabled = 1; //Disable odd/even mode!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.MAPMASKREGISTER.MemoryPlaneWriteEnable = 1; //Write to plane 0 only, since we're emulating CGA!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.UseByteMode = 1; //CGA byte mode!
}
else //Revert to normal memory mode!
{
if (getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.UseByteMode) //We were using data that now becomes junk data?
{
uint_32 x;
for (x=1;x<0x10000;) //Clear all odd data we've used in monochrome mode!
{
writeVRAMplane(getActiveVGA(),0,x,0,0); //Clear the byte of data at byte offsets only!
++x; //Next byte is skipped(even plane)!
++x; //We skip 2 bytes for the next offset to use!
}
}
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.OddEvenMode = 1; //Force odd/even mode!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.READMAPSELECTREGISTER.ReadMapSelect = 0; //Only read map #0!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.COLORDONTCAREREGISTER.ColorCare |= 0xF; //Care about this only!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.MISCGRAPHICSREGISTER.EnableOddEvenMode = 1; //Enable chaining!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.SEQUENCERMEMORYMODEREGISTER.OEDisabled = 0; //Disable odd/even mode!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.MAPMASKREGISTER.MemoryPlaneWriteEnable = 3; //Write to planes 0/1 only, since we're emulating CGA!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.UseByteMode = 0; //CGA word mode!
}
VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_CGACRTCONTROLLER_HORIZONTAL|0x1); //The horizontal size might have been updated!
}

void applyCGAModeControl()
{
//Apply the new CGA mode control register?
if (getActiveVGA()->registers->Compatibility_CGAModeControl&8) //Video enabled on the CGA?
{
if (getActiveVGA()->registers->Compatibility_MDAModeControl&8) //MDA also enabled?
{
getActiveVGA()->registers->Compatibility_MDAModeControl &= ~8; //Disable the MDA!
}
}
if (getActiveVGA()->registers->Compatibility_CGAModeControl&0x2) //Graphics mode?
{
if (getActiveVGA()->registers->Compatibility_CGAModeControl&0x10) //2 colour?
{
setCGAMDAMode(1,0,(getActiveVGA()->registers->Compatibility_CGAModeControl&0x20)); //Set up basic 2-color graphics!
applyCGAMemoryMap(1,0);
}
else //4 colour?
{
setCGAMDAMode(1,1,(getActiveVGA()->registers->Compatibility_CGAModeControl&0x20)); //Set up basic 4-color graphics!
applyCGAMemoryMap(1,1);
}
}
else //Text mode?
{
setCGAMDAMode(0,0,(getActiveVGA()->registers->Compatibility_CGAModeControl&0x20)); //Text mode!
applyCGAMemoryMap(0,0);
}
getActiveVGA()->registers->SequencerRegisters.REGISTERS.CLOCKINGMODEREGISTER.ScreenDisable = (getActiveVGA()->registers->Compatibility_CGAModeControl&8)?0:1; //Disable the screen when requested! Interpret the RAM as zeroes(black) when disabled!
applyCGAMDAPaletteRegisters(); //Apply the palette registers according to our settings!
VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_ALL); //We have been updated!
}

void applyMDAModeControl()
{
//Apply the new MDA mode control register?
if (getActiveVGA()->registers->Compatibility_MDAModeControl&8) //Video enabled on the MDA?
{
if (getActiveVGA()->registers->Compatibility_CGAModeControl&8) //CGA also enabled?
{
getActiveVGA()->registers->Compatibility_CGAModeControl &= ~8; //Disable the CGA!
}
setCGAMDAMode(0,1,(getActiveVGA()->registers->Compatibility_MDAModeControl&0x20)); //Text mode!
applyCGAMemoryMap(0,1);
}
applyCGAMDAPaletteRegisters(); //Apply the palette registers according to our settings!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.CLOCKINGMODEREGISTER.ScreenDisable = (getActiveVGA()->registers->Compatibility_MDAModeControl&8)?0:1; //Disable the screen when requested! Interpret the RAM as zeroes(black) when disabled!
VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_ALL); //We have been updated!
}

//Support for the I/O updating!
void updateCGAMDAflags()
{
if ((getActiveVGA()->registers->specialCGAflags!=getActiveVGA()->precalcs.LastCGAFlags) || (getActiveVGA()->registers->specialMDAflags!=getActiveVGA()->precalcs.LastMDAFlags)) //CGA/MDA flags updated?
{
getActiveVGA()->precalcs.LastCGAFlags = getActiveVGA()->registers->specialCGAflags; //Update the last value used!
getActiveVGA()->precalcs.LastMDAFlags = getActiveVGA()->registers->specialMDAflags; //Update the last value used!
VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_ALL_SECTION|WHEREUPDATED_CRTCONTROLLER); //We have been updated! Update the whole section, as we don't know anything about the exact registers affected by the special action!
}
}

void applyCGAMDAMode() //Apply VGA to CGA/MDA Mode conversion(setup defaults for all registers not used by the CGA/MDA emulation)!
{
//Now, setup defaults for the CGA/MDA to be used(normal data)! Clear any unused registers!
//Graphics Controller: Fully set!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.SETRESETREGISTER.SetReset = 0; //Disable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.ENABLESETRESETREGISTER.EnableSetReset = 0; //No set/reset used!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER.RotateCount = 0; //No special operation!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.DATAROTATEREGISTER.LogicalOperation = 0; //No special operation!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.COLORCOMPAREREGISTER.ColorCompare = 0; //Disable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.READMAPSELECTREGISTER.ReadMapSelect = 0; //Disable/Plane 0!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.WriteMode = 0; //Disable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.ReadMode = 0; //Disable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.OddEvenMode = 1; //Enable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.GRAPHICSMODEREGISTER.Color256ShiftMode = 0; //Disable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.MISCGRAPHICSREGISTER.EnableOddEvenMode = 1; //Enable!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.COLORDONTCAREREGISTER.ColorCare |= 0xF; //Care about this only!
getActiveVGA()->registers->GraphicsRegisters.REGISTERS.BITMASKREGISTER = 0xFF; //Use all bits supplied by the CPU!
//Sequencer: Fully set!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.RESETREGISTER.AR = 1; //Start the sequencer!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.RESETREGISTER.SR = 1; //Start the sequencer!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.CLOCKINGMODEREGISTER.S4 = 0; //CGA display!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.CLOCKINGMODEREGISTER.DCR = 0; //CGA display! Single pixels only!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.CLOCKINGMODEREGISTER.SLR = 0; //CGA display! Single load rate!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.CLOCKINGMODEREGISTER.DotMode8 = 1; //CGA display! 8 dots/character!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.MAPMASKREGISTER.MemoryPlaneWriteEnable = 3; //Write to planes 0/1 only, since we're emulating CGA!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.SEQUENCERMEMORYMODEREGISTER.ExtendedMemory = 0; //Write to planes 0/1 only, since we're emulating CGA! We're wrapping around 16KB, so only 64K applied(16K with double 4 bytes per index=64K memory).
getActiveVGA()->registers->SequencerRegisters.REGISTERS.SEQUENCERMEMORYMODEREGISTER.OEDisabled = 0; //Write to planes 0/1 only, since we're emulating CGA!
getActiveVGA()->registers->SequencerRegisters.REGISTERS.SEQUENCERMEMORYMODEREGISTER.Chain4Enable = 0; //Write to planes 0/1 only, since we're emulating CGA!
//CRT Controller: Fully set!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.UNDERLINELOCATIONREGISTER.DW = 0; //CGA normal mode!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.UNDERLINELOCATIONREGISTER.DIV4 = 0; //CGA normal mode!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.LINECOMPAREREGISTER = 0xFF; //Maximum line compare: no split screen!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.OVERFLOWREGISTER.LineCompare8 = 1; //Maximum line compare: no split screen!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.MAXIMUMSCANLINEREGISTER.LineCompare9 = 1; //Maximum line compare: no split screen!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CURSORENDREGISTER.CursorSkew = 0; //Don't use any cursor skewing: we're direct line numbers for the scanline within the character!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.VERTICALRETRACEENDREGISTER.VerticalInterrupt_Disabled = 1; //Disable the VRetrace interrupts!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.SLDIV = 0; //CGA no scanline division!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.DIV2 = 0; //CGA normal mode?
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.SE = 1; //CGA enable CRT rendering HSYNC/VSYNC!
//Memory mapping special: always map like a CGA!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.UseByteMode = 0; //CGA word mode!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.AW = 0; //CGA mapping is done by the renderer mapping CGA!
getActiveVGA()->registers->CRTControllerRegisters.REGISTERS.CRTCMODECONTROLREGISTER.MAP14 = 1; //CGA mapping!
//Attribute Controller: Fully set!
VGA_3C0_PAL = 1; //Enable the palette!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.LineGraphicsEnable = 1; //CGA line graphics!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.PixelPanningMode = 0; //CGA pixel panning mode!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.ColorEnable8Bit = 0; //Not using 8-bit colors!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.ATTRIBUTEMODECONTROLREGISTER.PaletteBits54Select = 0; //Not using the high Palette bits!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.COLORPLANEENABLEREGISTER.DATA |= 0xF; //CGA: enable all color planes!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.HORIZONTALPIXELPANNINGREGISTER.PixelShiftCount = 0; //Don't shift!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.COLORSELECTREGISTER.ColorSelect54 = 0; //Don't use!
getActiveVGA()->registers->AttributeControllerRegisters.REGISTERS.COLORSELECTREGISTER.ColorSelect76 = 0; //Don't use!
//External registers: Fully set!
getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER.IO_AS = (getActiveVGA()->registers->specialCGAflags&1)?1:0; //CGA/MDA address!
getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER.RAM_Enable = 1; //CGA!
getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER.OE_HighPage = 0; //CGA!
getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER.HSyncP = 0; //CGA has positive polarity!
getActiveVGA()->registers->ExternalRegisters.MISCOUTPUTREGISTER.VSyncP = 0; //CGA has positive polarity!
getActiveVGA()->registers->ExternalRegisters.FEATURECONTROLREGISTER.FC0 = 0; //CGA!
getActiveVGA()->registers->ExternalRegisters.FEATURECONTROLREGISTER.FC1 = 1; //CGA!
VGA_calcprecalcs(getActiveVGA(),WHEREUPDATED_ALL); //We have been updated(full VGA)!
}

applyCGAMDAMode is called when the VGA is switched from VGA to CGA mode (VGA with CGA or VGA with MDA modes). It's called when starting the emulator when Pure CGA or Pure MDA mode is selected. applyMDAModeControl, applyCGAModeControl and applyCGAPaletteRegister are called when those respective registers are written to (by the CPU). Writing to CGA CRTC register 8h(Interlace Register) also calls updateCGAmapping to register the changes in CGA interlace modes.

I've uploaded a video on Youtube with the current build running 8088 MPH better than before:
https://www.youtube.com/watch?v=mSsGUugmRmM

Although the audio is a bit displaced because the emulator has to be starting recording itself and the recording software (Ezvid) doesn't record audio, only video. Also unfortunately Windows 10 refuses to record the application itself using Win+G (Gaming toolbar) on it.

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

Reply 128 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

I've uploaded a video on Youtube with the current build running 8088 MPH better than before:
https://www.youtube.com/watch?v=mSsGUugmRmM

Getting there! The palette change on the intro screen (as it rolls down over the comparison text) is supposed to be synchronized with the rolldown (so the text is in palette 7 and the graphical title screen is in palette 15). The fact that it isn't means that your PIT implementation is not synchronized with your CGA implementation. That could also account for the flickering on the 1K colour screens. There are supposed to be exactly 19912 (=76*262) PIT cycles per CGA frame (i.e. a PIT cycle is 12 hdots).

You've also got some lines on the 256 colour plasma. These suggest that you might not have the correct CGA font ROM. If that's not the problem, can I see a still image of the RGBI output for this effect?

For the 100-line (faces) image, I wonder if the start address is taking effect at the wrong time. These registers are only supposed to have an effect at the start of the CRTC frame. It seems like you also have an issue with the vsync position, as the image is supposed to be vertically centered. This effect switches between 1 scanline per row (for the 100 scanlines or so of the actual image) to 2 scanlines per row (for the vertical overscan and sync part of the screen).

Reply 129 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

Well the PIT, CGA and 8086/88 all share the same 14MHz clock. The CGA takes it as pixel clock, the CPU divides by 3 and the PIT divides by 12.

Of course every CPU instruction takes 9 cycles on a 80(1)88 and 8 cycles on a 80(1)86 atm. So PIT ticks might not execute right away when the CPU executes too slow(multiple IRQ0 triggers at once will be missed, only triggering one IRQ0). That will currently cause a little IRQ0 drift/miss with PIT not exactly 8/9 cycle multiples.

The start address is currently reloaded on vertical total(at horizontal(scanline) pixel #0). It will reset the scanline of the buffer, rendering the first pixel of the new(2-line if requested) frame. It also triggers the vertical sync reset signal, which currently has no effect anymore, since it's unneeded(old seperate hsync/vsync processing to reset the hsync/vsync timing, which is currently set to the current pixel instead of using independant timing).

Do I need to load the start register at the start of the frame instead(at Sequencer pixel 0,0)?

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

Reply 130 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

Also, here is the RGBI output of the 256 color plasma (16/256 color part of the demo):

Snap 2016-04-12 at 23.02.24.png
Filename
Snap 2016-04-12 at 23.02.24.png
File size
19.82 KiB
Views
674 views
File comment
Snapshot of the 256 color plasma.
File license
Fair use/fair dealing exception

It's rendered 1:1 inside the window using the emulator's RGBI conversion (just a simple lookup table of 15 RGBI values according to a wiki(wikipedia I think)).

https://en.wikipedia.org/wiki/Color_Graphics_Adapter
It's the one listed as the "Full CGA 16-color palette". Although the color #6's green signal can be switched to full (0xAA) for a dark yellow, or half (0x55) for the normal brown color in my emulator. It's currently set to brown for compatibility though.

The fonts used for CGA and MDA are the Dosbox fonts(Taken from Dosbox-X I think, though I don't know what commit anymore(it was a long time ago I implemented this). And the ROMs read within 8088 MPH are the fonts stored in the Turbo XT BIOS v2.5, don't know if that one's accurate.

Although full PIT synchronization might be impossible currently, since the CPU emulation can only execute the PIT IRQ0 after it's processed that corresponding instruction(which adds cycles to tick) and ticking the PIT&IRQ0 based on that number added. At the same time the PIT is ticked, the VGA/EGA/CGA/MDA also measure timing and process that time.

Since a CPU tick takes 8 cycles, so 4 PIT ticks when it's at it's highest frequency(1.19MHz clock set with a reload counter set to 1), the CPU ticks 8 cycles of time(at ~4.77MHz), the PIT ticks 1 or 2 times(at ~1.19MHz) and the CGA ticks ~23 pixel timings(at ~14MHz). This happens every instruction, thus CPU/CGA timing might be drifting due to cycles taking too much time to process the faster events from the PIT(currently running twice as fast as the CPU(can expire up to 2 times in one CPU instruction, with the CPU instruction cycles being set)). The CPU will only execute the PIT0 after the cycles triggering that IRQ (and all hardware updating status up to the current time) finish and the next instruction is timestamped for execution (the emulator waits for the next instruction time to arrive, after which it checks for interrupts(actually the start of the cycle), updates input status(physical keyboard/mouse/PSP keys)) and then executes the next instruction, at which point the whole thing starts again.

The emulator will of course start with zero time(nothing has been done yet after all). Then the following steps will loop:
1. Interrupts are checked. This includes IRQ0 triggering.
1. The instruction is executed and timing is calculated and added(Default setting is 8/9 cycles) to current time for real time synchronization.
2. Hardware times (PIT, Video adapter(VGA/CGA/MDA) and other hardwares) are updated using the time spend on the instruction.
3. Too slow core loops (they're combined if possible) are aborted(preventing an unresponsive emulator with too high cycle settings).
4. When the main loop ends, physical keyboard&mouse status is updated. Then the emulator waits until the current CPU time is reached in realtime (thus synchronizing CPU time with realtime).
5. Finally, if changed, the display resolution (when using the normal Forced mode combined with Fullscreen stretching, which generates a 1:1 window of the output by the Video adapter) of the SDL display window is updated.
6. SDL is polled for any input (Mouse/Keyboard/Window status/PSP keys/Joystick input(Joystick #0 only)) and the OSK and emulated keyboard/mouse status is updated as needed(though actual processing of input/mouse movement is in the main CPU loop itself, at step 2).

So CGA might always run faster than the CPU, as long as the CPU takes 8 or 9 cycles each instruction.

Last edited by superfury on 2016-04-13, 06:06. Edited 1 time in total.

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

Reply 131 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Also, here is the RGBI output of the 256 color plasma (16/256 color part of the demo):

Snap 2016-04-12 at 23.02.24.png

That looks like it's using 8 scanlines per character row. The effect programs the CRTC for 6 scanlines (r9=5). That could account for the lines.

If your PIT clock is correct then the next most likely problem affecting the palette change in the intro is loading a new PIT count. If you write to port 0x43 to set the mode before the count, then the new count takes effect immediately, but if you don't set the mode (and just write to port 0x40, in this case) the new count should take effect once the current count reaches 0.

Another possible problem is your implementation of the HLT instruction. Once the CPU is halted, the next interrupt should take effect a constant number of cycles after it occurs (no jitter).

Reply 132 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

The reloading of PIT counters is done according to the osdev wiki article.

The CGA Maximum scanline programmed is copird directly into the VGA Maximum scanline register. The renderer character height should the value in the VGA Maximum Scanline Register + 1. In this case 5 is programmed, copied and increased to become 6 scanlines/character in the renderer, if that's indeed what's programmed in the CGA CRTC register #9.

The HLT instruction takes it's defined time(4 cycles I believe?). The delay between interrupt checks(and hardware updates etc.) is 1 CPU cycle during HLT state.

I've also tried moving the Start Address reloading to when the frame ends (the moment the new frame starts). This doesn't seem to have any effect on the demo.

Just verified the value in the CGA&VGA Maximum Scanline registers. Both are set to 5, so 6 scanlines per character should be used during that part. Vertical active display is 264 scanlines high for some reason though (which uses 8 scanlines/character)?

Edit: It looks like it wasn't updating the CRTC timings when the character height was updated. This is now fixed. So far it at least fixes the height of the SALC part (the 3d text tube part at the start of the demo).

The height of the characters in the 16/256 color image is now only a fraction of the old height according to the RGBI monitor.

The faces at the end now flash left and right (moving half a face left and right very fast).

The command prompt still displays at the same row as the final line of 8088MPH. It's disappearing behind the content of 8088MPH(including anything typed, until you reach the next line, which shifts display up one row, using any command prompt method (typing too much or pressing enter)).

Edit: Moving the start address reloading back to the end of vertical total(start of new display) seems to fix the problems introduced with the heads moving left&right. It also fixes the problem introduced in the detection screen with the change in start address logic.

I also notice that the + sign in the middle of the table comparing the C64 and IBM PC is slightly displaced? Although the font won't have any effect on the textmode font (which is probably different), as the font used at that point is in the Turbo XT BIOS itself (it's graphics mode with characters read from the Turbo XT BIOS ROM or in low RAM(0-640K))?

Well, now at least the horizontal timing of the final image showing the faces is correct (thanks to the character height fix):

Snap 2016-04-13 at 14.23.02.png
Filename
Snap 2016-04-13 at 14.23.02.png
File size
118.57 KiB
Views
621 views
File comment
The final faces, not aspect corrected to CGA resolution.
File license
Fair use/fair dealing exception

(Alhough it still isn't centered vertically)

Also, the rotozoomer (Intel logo) part looks more grainy now (less colours displayed).

Btw the CGA Compatibility Tester's "Row reprogramming" option now gives the correct images 😀
The Interlaced mode test gives errors (showing incomplete text on the screen).
Also the informational texts reporting stuff give a little window with, for example:

xt mode.
Press a key to enable in
terlacing; when done vie
wing, press any key to e
xit.
Snap 2016-04-13 at 14.47.36.png
Filename
Snap 2016-04-13 at 14.47.36.png
File size
30.22 KiB
Views
620 views
File comment
Text mode going wrong in CGA Compatibility Tester parts.
File license
Fair use/fair dealing exception

When the Interlaced mode is turned on, it only shows "Intelaced mode on, Press key", with the bottom of the 4th line of the baseline of the "y" being the last scanline seen on the screen.

Snap 2016-04-13 at 14.47.52.png
Filename
Snap 2016-04-13 at 14.47.52.png
File size
111.03 KiB
Views
620 views
File comment
Interlaced mode turned on.
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 133 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

Some pictures of the pixel art etc. of the CGA Compatibility Tester (finally not losing sync anymore:) ):

Snap 2016-04-13 at 14.46.41.png
Filename
Snap 2016-04-13 at 14.46.41.png
File size
236.75 KiB
Views
620 views
File comment
First art image, aspect corrected(NTSC mode).
File license
Fair use/fair dealing exception
Snap 2016-04-13 at 14.47.09.png
Filename
Snap 2016-04-13 at 14.47.09.png
File size
290.27 KiB
Views
620 views
File comment
The ASCII from hell image in NTSC mode.
File license
Fair use/fair dealing exception
Snap 2016-04-13 at 14.48.55.png
Filename
Snap 2016-04-13 at 14.48.55.png
File size
296.04 KiB
Views
620 views
File comment
The ASCII from hell image in RGB mode.
File license
Fair use/fair dealing exception
Snap 2016-04-13 at 15.16.04.png
Filename
Snap 2016-04-13 at 15.16.04.png
File size
297.11 KiB
Views
617 views
File comment
The faces, including aspect correction by x86EMU.
File license
Fair use/fair dealing exception
Snap 2016-04-13 at 15.18.07.png
Filename
Snap 2016-04-13 at 15.18.07.png
File size
456.14 KiB
Views
617 views
File comment
The command prompt disappearing behind the last line of 8088 MPH(one line too early?).
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 134 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

Here's the video of the latest x86EMU build running 8088 MPH on a new-style CGA with 8086 CPU at 8 cycles/instruction:
https://www.youtube.com/watch?v=Dzdp7q05gr8

Most of the demo seems to work now. Only four problems left:
1. The 1K images are flashing (seemingly reduced to small, vertically centered beams of about 16 pixels during at least some of those periods).
2. The Kefrens Bars are flashing completely black (probably a timing problem, which would need the CPU to become 100% accurate to get working properly(which is of course the next step after all other parts of 8088 MPH work)).
3. The MS-DOS prompt at the end is positioned correctly (directly after 8088 MPH), but the screen isn't shifted upwards for the prompt to show? This causes it to 'hide' behind the final line of the credits somehow? Although it's a text line and such a thing SHOULD be impossible(text and older text mixing together)?
4. The faces won't be centered vertically for some reason? They're displayed fine, but the timing for retracing might be off by a quarter screen somehow?

Reenigne? Scali? Do you know anything about these (current) problems?

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

Reply 135 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Reenigne? Scali? Do you know anything about these (current) problems?

Yes, as previously mentioned:

superfury wrote:

1. The 1K images are flashing (seemingly reduced to small, vertically centered beams of about 16 pixels during at least some of those periods).
4. The faces won't be centered vertically for some reason? They're displayed fine, but the timing for retracing might be off by a quarter screen somehow?

If you can make a dump of 40ms or so worth of CGA register writes and status register reads during the effect I can tell what's going on from that.

superfury wrote:

3. The MS-DOS prompt at the end is positioned correctly (directly after 8088 MPH), but the screen isn't shifted upwards for the prompt to show? This causes it to 'hide' behind the final line of the credits somehow? Although it's a text line and such a thing SHOULD be impossible(text and older text mixing together)?

That's most likely a DOS and/or BIOS problem. Try it with the 1501512 BIOS and PC DOS 3.1. If it's correct there, change one of those back to isolate it. The effect uses int 0x10 with ah=2, bh=0, dh=24, dl=0 to move the BIOS's idea of the cursor position to the last line of the screen before the effect starts, and at the end of the effect the start address should be 0.

Reply 136 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

Trying to run the 1501512 BIOS (first revision of the IBM XT BIOS) makes the emulator end up in a HLT instruction somehow? Maybe something is wrong/inaccurate enough in my emulator making it detect wrong/invalid hardware?

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

Reply 137 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Trying to run the 1501512 BIOS (first revision of the IBM XT BIOS) makes the emulator end up in a HLT instruction somehow? Maybe something is wrong/inaccurate enough in my emulator making it detect wrong/invalid hardware?

Probably! What is the address it halts at?

Reply 138 of 187, by superfury

User metadata
Rank l33t++
Rank
l33t++

The debugger (which is now adjusted to log hlt/halt state as well) logs:

0:00:09:29.5.0253: F000:E12E (74424242)<HLT>

0:00:09:29.5.0256: Registers:

0:00:09:29.5.0259: AX: FFFF, BX: 0000, CX: 0008, DX: 0000

0:00:09:29.5.0261: CS: F000, DS: F000, ES: 0000, SS: F000

0:00:09:29.5.0263: SP: E006, BP: 0000, SI: 0000, DI: 0000

0:00:09:29.5.0265: IP: E12E, FLAGS: F013

0:00:09:29.5.0269: FLAGSINFO:C1p0A0zstido1111H

0:00:09:29.5.0274: Interrupt status: 0000000000000010

0:00:09:29.5.0276: VGA@0,0(CRT:0,0)

0:00:09:29.5.0277: Display=0,0

Btw I do remember valuely when trying a IBM PC XT (third revision if I remember correctl) BIOS a few weeks ago, which was endlessly looping when testing the PIT counters (I believe it set up PIT counter 2 and waited it to become 0x40 somehow after some ANDing of the values read from PIT0(port 0x40) or PIT1(port 0x41)).

I'll switch back to the Turbo XT BIOS for now and start dumping those 40ms logs (are you sure you need 40ms and not 40us? 40ms=40/1000th second), so that amounts to around 2.4 frames being logged?

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

Reply 139 of 187, by reenigne

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:
The debugger (which is now adjusted to log hlt/halt state as well) logs: […]
Show full quote

The debugger (which is now adjusted to log hlt/halt state as well) logs:

0:00:09:29.5.0253: F000:E12E (74424242)<HLT>[/quote]

That means you're not implementing the DMA controller properly.

[quote="superfury"]I'll switch back to the Turbo XT BIOS for now and start dumping those 40ms logs (are you sure you need 40ms and not 40us? 40ms=40/1000th second), so that amounts to around 2.4 frames being logged?[/quote]

Yes. If I can see all the CGA register accesses for a couple of frames worth of time I think I'll be able to figure out what's going on.