VOGONS


Reply 40 of 55, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Edit: Fixed it by fixing the counter handling. 8088 MPH's volume still moves up and down constantly during normal mode 3(I think) square wave sound generation(until the credits), though.

What volume you mean? Do you mean high tones have less amplitude than low tones? After low pass filter?

What does a low pass filter do? It attenuates high frequencies, and does not attenuate low frequencies.
Therefore, after low pass filtering, high tones will have less amplitude than low tones. Right?

Reply 41 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

The low pass filter uses (as well as the low pass of the MIDI renderer and the high pass 1Hz used before sending the full mixed output of all emulated hardware to SDL) a simple RC filter:

OPTINLINE static float calcSoundHighpassFilter(float cutoff_freq, float samplerate, float currentsample, float previoussample, float previousresult)
{
float RC = 1.0 / (cutoff_freq * 2 * 3.14);
float dt = 1.0 / samplerate;
float alpha = RC / (RC + dt);
return alpha * (previousresult + currentsample - previoussample);
}

OPTINLINE static float calcSoundLowpassFilter(float cutoff_freq, float samplerate, float currentsample, float previousresult)
{
float RC = (float)1.0f / (cutoff_freq * (float)2 * (float)3.14);
float dt = (float)1.0f / samplerate;
float alpha = dt / (RC + dt);
return previousresult + (alpha*(currentsample - previousresult));
}

void applySoundHighpassFilter(float cutoff_freq, float samplerate, float *currentsample, float *sound_last_result, float *sound_last_sample, byte *isFirstSample)
{
//We're using a high pass filter!
if (*isFirstSample) //No last? Only executed once when starting playback!
{
*sound_last_result = *sound_last_sample = *currentsample; //Save the current sample!
*isFirstSample = 0; //Not the first sample anymore!
return; //Abort: don't filter the first sample!
}
*sound_last_result = calcSoundHighpassFilter(cutoff_freq, samplerate, *currentsample, *sound_last_sample, *sound_last_result); //High pass filter!
*sound_last_sample = *currentsample; //The last sample that was processed!
*currentsample = *sound_last_result; //Give the new result!
}

void applySoundLowpassFilter(float cutoff_freq, float samplerate, float *currentsample, float *sound_last_result, float *sound_last_sample, byte *isFirstSample)
{
//We're using a low pass filter!
if (*isFirstSample) //No last? Only executed once when starting playback!
{
*sound_last_result = *sound_last_sample = *currentsample; //Save the current sample!
*isFirstSample = 0; //Not the first sample anymore!
return; //Abort: don't filter the first sample!
}
*sound_last_result = calcSoundLowpassFilter(cutoff_freq, samplerate, *currentsample, *sound_last_result); //Low pass filter!
*sound_last_sample = *currentsample; //The last sample that was processed!
*currentsample = *sound_last_result; //Give the new result!
}

The low pass of 20kHz is executed on the 1.19MHz samples before downsampling to 44.1kHz. The high pass of 1kHz is executed on the data that ends up in the rendering buffer (which is at that point mixed with all other hardware(the Adlib, Disney Sound Source and Covox devices(mono and stereo Covox on LPT, based on Dosbox's emulation)) before sending the data to SDL.

About your last post: The volume of all output (even the PC speaker mode 3-like ones(if it's actually square waves it's requested to generate, don't know what 8088 MPH does at that point though)) from the point the big image with the car (first 'real' 1K color image of 8088 MPH, if you don't count the fake CGA text mode comparing C64 vs CGA) is faded off the screen to continue the demo. So after the image is displayed and the sound becoming lower, making the sound of something falling on the ground and becomes quiet. Then when it continues with the next notes, it sounds like the PC speaker's volume (or Windows' volume slider) is being pulled up and down repeatedly. So like a sinus modulating the amplitude, only it's sinus-like and seems to be a bit random in speed(going slower and faster with time).

This problem won't show up in the recordings though. Maybe it's got something to do with the skipping to archieve 44.1kHz from the 1.19Mhz input? Or the high pass RC filter?

Edit: It seems I was right: disabling the 1Hz high pass filter removes the effect and makes the PC speaker music play properly (although unprotected against DC blowing up your speaker and outputting -MAXVOLTAGE with PC speaker off(PIT output is 0 since turned off=>-32878=>output=>-5V DC)). So at least the RC high pass filter isn't correct. The low pass filter might also be wrong, since it's based on the same source code by the same post(and poster/author).

These are the recordings both low pass filtered and raw PIT data stream(converted to SHRT_MIN and SHRT_MAX): http://www.filedropper.com/speakerlog8088mph201603040101

Unfortunately I don't have anything else than that RC filter for high and low pass filtering(I don't know enough to make such a thing myself, not can I seem to find any c code anywhere on how to do it for different playback frequencies and cutoff frequencies, to actually fully cut off that audio). Anyone has a link to code or some code to use instead with correct high&low pass filtering?

The RC filters used are based on the code in the first post of this page: http://stackoverflow.com/questions/13882038/i … ss-filters-in-c

It's essentially the only I could find, which also allows different cutoff frequencies and rendering frequencies as far as I could see. That it's a RC filter might be a problem though.

Btw, this is my latest version of my PC speaker emulation(current commit): https://bitbucket.org/superfury/x86emu/src/ad … pit.c?at=master

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

Reply 42 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

I am wondering though: Besides the filters not working properly(apparently), is the PIT output itself even correct?

	time_ticktiming += timepassed; //Add the amount of time passed to the PIT timing!

//Render 1.19MHz samples for the time that has passed!
length = (uint_32)SAFEDIV(time_ticktiming, time_tick); //How many ticks to tick?
time_ticktiming -= (length*time_tick); //Rest the amount of ticks!

if (length) //Anything to tick at all?
{
for (channel=0;channel<3;channel++)
{
byte mode,outputmask;
mode = PITchannels[channel].mode; //Current mode!
outputmask = (channel==2)?((PCSpeakerPort&2)>>1):1; //Mask output on/off for this timer!

switch (mode) //What mode are we rendering?
{
case 0: //Interrupt on Terminal Count? Is One-Shot without Gate Input?
case 1: //One-shot mode?
for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed!
{
//Length counts the amount of ticks to render!
switch (PITchannels[channel].status) //What status?
{
case 0: //Output goes low/high?
PITchannels[channel].channel_status = mode; //We're high when mode 1, else low with mode 0!
if (PITchannels[channel].reload)
{
PITchannels[channel].status = 1; //Skip to 1: we're ready to run already!
goto mode0_1; //Skip to step 1!
}
break;
case 1: //Wait for next rising edge of gate input?
mode0_1:
if (!mode) //No wait on mode 0?
{
PITchannels[channel].status = 2;
goto mode0_2;
}
else if (PITchannels[channel].gatewenthigh) //Mode 1 waits for gate to become high!
{
PITchannels[channel].gatewenthigh = 0; //Not went high anymore!
PITchannels[channel].status = 2;
goto mode0_2;
}
break;
case 2: //Output goes low and we start counting to rise! After timeout we become 4(inactive) with mode 1!
mode0_2:
if (PITchannels[channel].reload)
{
PITchannels[channel].reload = 0; //Not reloading anymore!
PITchannels[channel].channel_status = 0; //Lower output!
reloadticker(channel); //Reload the counter!
}

oldvalue = PITchannels[channel].ticker; //Save old ticker for checking for overflow!

if (mode) --PITchannels[channel].ticker; //Mode 1 always ticks?
else if ((PCSpeakerPort&1) || (channel<2)) --PITchannels[channel].ticker; //Mode 0 ticks when gate is high!

if ((!PITchannels[channel].ticker) && oldvalue) //Timeout when ticking? We're done!
Show last 173 lines
						{
PITchannels[channel].channel_status = 1; //We're high again!
}
break;
default: //Unsupported! Ignore any input!
break;
}
writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal!
}
break;
case 2: //Also Rate Generator mode?
case 6: //Rate Generator mode?
for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed!
{
//Length counts the amount of ticks to render!
switch (PITchannels[channel].status) //What status?
{
case 0: //Output going high! See below! Wait for reload register to be written!
PITchannels[channel].channel_status = 1; //We're high!
PITchannels[channel].status = 1; //Skip to 1: we're ready to run already!
goto mode2_1; //Skip to step 1!
break;
case 1: //We're starting the count?
mode2_1:
if (PITchannels[channel].reload)
{
reload2:
PITchannels[channel].reload = 0; //Not reloading!
reloadticker(channel); //Reload the counter!
PITchannels[channel].channel_status = 1; //We're high!
PITchannels[channel].status = 2; //Start counting!
}
break;
case 2: //We start counting to rise!!
if (PITchannels[channel].gatewenthigh) //Gate went high?
{
PITchannels[channel].gatewenthigh = 0; //Not anymore!
goto reload2; //Reload and execute!
}
if (((PCSpeakerPort & 1) && (channel==2)) || (channel<2)) //We're high or undefined?
{
--PITchannels[channel].ticker; //Decrement?
switch (PITchannels[channel].ticker) //Two to one? Go low!
{
case 1:
PITchannels[channel].channel_status = 0; //We're going low during this phase!
break;
case 0:
PITchannels[channel].channel_status = 1; //We're going high again during this phase!
reloadticker(channel); //Reload the counter!
break;
default: //No action taken!
break;
}
}
else //We're low? Output=High and wait for reload!
{
PITchannels[channel].channel_status = 1; //We're going high again during this phase!
}
break;
default: //Unsupported! Ignore any input!
break;
}
writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal!
}
break;
//mode 2==6 and mode 3==7.
case 3: //Square Wave mode?
case 7: //Also Square Wave mode?
for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed!
{
//Length counts the amount of ticks to render!
switch (PITchannels[channel].status) //What status?
{
case 0: //Output going high! See below! Wait for reload register to be written!
PITchannels[channel].channel_status = 1; //We're high!
if (PITchannels[channel].reload)
{
PITchannels[channel].reload = 0; //Not reloading!
reloadticker(channel); //Reload the counter!
PITchannels[channel].status = 1; //Next status: we're loaded and ready to run!
goto mode3_1; //Skip to step 1!
}
break;
case 1: //We start counting to rise!!
mode3_1:
if (PITchannels[channel].gatewenthigh)
{
PITchannels[channel].gatewenthigh = 0; //Not anymore!
PITchannels[channel].reload = 0; //Reloaded!
reloadticker(channel); //Gate going high reloads the ticker immediately!
}
if ((PCSpeakerPort&1) || (channel<2)) //To tick at all?
{
PITchannels[channel].ticker -= 2; //Decrement by 2 instead?
switch (PITchannels[channel].ticker)
{
case 0: //Even counts decreased to 0!
case 0xFFFF: //Odd counts decreased to -1/0xFFFF.
PITchannels[channel].channel_status ^= 1; //We're toggling during this phase!
PITchannels[channel].reload = 0; //Reloaded!
reloadticker(channel); //Reload the next value to tick!
break;
default: //No action taken!
break;
}
}
break;
default: //Unsupported! Ignore any input!
break;
}
writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal!
}
break;
case 4: //Software Triggered Strobe?
case 5: //Hardware Triggered Strobe?
for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed!
{
switch (PITchannels[channel].status) //What status?
{
case 0: //Output going high! See below! Wait for reload register to be written!
PITchannels[channel].channel_status = 1; //We're high!
PITchannels[channel].status = 1; //Skip to 1: we're ready to run already!
goto mode4_1; //Skip to step 1!
break;
case 1: //We're starting the count or waiting for rising gate(mode 5)?
mode4_1:
if (PITchannels[channel].reload)
{
pit45_reload: //Reload PIT modes 4&5!
if ((mode == 4) || ((PITchannels[channel].gatewenthigh) && (mode == 5))) //Reload when allowed!
{
PITchannels[channel].gatewenthigh = 0; //Reset gate high flag!
PITchannels[channel].reload = 0; //Not reloading!
reloadticker(channel); //Reload the counter!
PITchannels[channel].status = 2; //Start counting!
}
}
break;
case 2: //We start counting to rise!!
case 3: //We're counting, but ignored overflow?
if (PITchannels[channel].reload || (((mode==5) && PITchannels[channel].gatewenthigh))) //We're reloaded?
{
goto pit45_reload; //Reload when allowed!
}
if (((PCSpeakerPort & 1) && (channel == 2)) || (channel<2)) //We're high or undefined?
{
--PITchannels[channel].ticker; //Decrement?
if (!PITchannels[channel].ticker && (PITchannels[channel].status!=3)) //One to zero? Go low when not overflown already!
{
PITchannels[channel].channel_status = 0; //We're going low during this phase!
PITchannels[channel].status = 3; //We're ignoring any further overflows from now on!
}
else
{
PITchannels[channel].channel_status = 1; //We're going high again any other phase!
}
}
else //We're low? Output=High and wait for reload!
{
PITchannels[channel].channel_status = 1; //We're going high again during this phase!
}
break;
default: //Unsupported mode! Ignore any input!
break;
}
writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal!
}
break;
}
}
}

Is this processed correctly?

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

Reply 43 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've improved my PIT emulation a bit with more accuracy. Although it still doesn't sound fully right.

http://www.filedropper.com/speakerlog201603041603

The current source code: https://bitbucket.org/superfury/x86emu/src/cc … pit.c?at=master

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

Reply 44 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

Also, strange enough, when I disable the high pass 1Hz filter now, I get clear sound when the PC speaker makes music (on the slow PC running at ~10% CPU Speed now on the 2GHz Pentium Dual core P6100 processor) in all recordings, but the output gives some low frequency rumble (about 20-50Hz) without the 1Hz high pass filter. I've enabled the high pass filter for now for release (disabling the Floppy disk Gap length and Density erroring out to be able to boot things like 160K PC booter games on the emulated drive(although the Turbo XT BIOS drives it wrongly in this case?)).

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

Reply 45 of 55, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

You could try the filters separately if you wanted, like read a wav file and filter it and write output to another wav file.

But I think I figured out at least one thing that could cause the problems you see.

You convert the PIT output of 0s and 1s blindly to full scale -32768 and +32767 without thinking about signal amplitudes and midpoints (I did mention earlier this leaves no headroom if the filters act funnily and amplify a bit, but now I realized it is a problem here as well).

Imagine you boot the emulator, and the PIT output is just zeroes, so your raw output is just -32768. When there is a beep, it jumps between 32767 and -32768 as we know, or in other words, the beep has a peak-to-peak amplitude of 65535, so it steps up by 65535 and down by 65535.

Now, when you have the DC-blocking 1Hz high-pass filter there, the -32768 gets slowly filtered away and the output signal is 0. But the beep signal still wants to jump up by 65535 and again down by 65535. But it won't fit into 16-bit signed integer any more, so the signal gets distorted (clips or overflows or whatever you have specified it to do in case it does not fit).

So at least halve the emulated amplitude, or even better, make it somewhat less than half, before filtering. Anyway, full scale 16-bit signal would be loud as hell and my gut feeling says people don't want their PC speakers as loud as their music or effects from other sound devices.

Reply 46 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

Emmm. Jumping up and down by 64K still fits inside 16-bits? Also, the filter only attenuates(lowers volume), not amplifies it?

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

Reply 47 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

I notice that increasing the high pass filter to 18.2Hz removes the echo effect that occurred with 1Hz. Audio sounds fine so far. Still untested on 8088MPH though as the pc I was working on (The 2GHz one) was too slow(CPU now running at 10% due to the PC speaker's heavy low pass filter(1.19M floating point filters/second).

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

Reply 48 of 55, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Emmm. Jumping up and down by 64K still fits inside 16-bits?

signed 16-bits?
Yes if you don't remove DC and start from -32768.
No if you remove DC and start from 0.

superfury wrote:

Also, the filter only attenuates(lowers volume), not amplifies it?

Yes, there is attenuation of low frequencies, yes, it does not amplify it. But still no, because, the DC blocking high pass filter attenuates low frequencies (removes DC) and passes high frequencies as they were (amplitude does not change). A step signal of 65535 into high pass filter will still come out as step signal of 65535, but without previous DC component, so nothing is amplified, but like I explained above, you can feed in a step from -32768 to 32767, but because DC was removed, the output would want to go from 0 to 65535 and it won't fit into signed 16 bit integer.

Reply 49 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

Why from 0 to 65535? The previous signal would be (as given by the PC Speaker) -32768, the new sample +32767, so the output should move as a result from the high pass filter (previous was zero) to 32767, not 65536? Unless you would double the amplitude from the PC Speaker itself(which isn't done)?

DC is only removed as the result of the mixer mixing rendered samples from the PC Speaker and other hardware, not in the PC Speaker renderer itself(which still moves from -32768 to +32767)? Since the previous sample is ignored, the output will move from 0(The DC current) to 32767(the new speaker sample), so nothing is out of limits with the high pass filter?

I don't know what's wrong with the PC Speaker emulation itself yet though.

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

Reply 50 of 55, by gdjacobs

User metadata
Rank l33t++
Rank
l33t++
superfury wrote:

Also, the filter only attenuates(lowers volume), not amplifies it?

I have to think about it wrt high pass filters, but filters in general definitely can. For example, look at the summed low order harmonics of a square wave. So, leaving head room is a very good idea.

squarewave32terms.png

As you can see, if you LPF at full scale you get clipping distortion.

All hail the Great Capacitor Brand Finder

Reply 51 of 55, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Why from 0 to 65535? The previous signal would be (as given by the PC Speaker) -32768, the new sample +32767, so the output should move as a result from the high pass filter (previous was zero) to 32767, not 65536? Unless you would double the amplitude from the PC Speaker itself(which isn't done)?

DC is only removed as the result of the mixer mixing rendered samples from the PC Speaker and other hardware, not in the PC Speaker renderer itself(which still moves from -32768 to +32767)? Since the previous sample is ignored, the output will move from 0(The DC current) to 32767(the new speaker sample), so nothing is out of limits with the high pass filter?

OK so the DC removal filtering is not done at the speaker render any more, but globally.

I am sorry I don't follow your reasoning why you think a 64k sample step at input would become 32k sample step at output.
But here's an electronic version : if you make a RC high pass filter, a step of 10 volts (AC) at the input will create a step of 10 volts (AC) at the output. The actual input DC voltage can be -5V before the jump so it goes from -5V to +5V, but on the output there is no DC so the jump is from 0V to 10V. So, a jump of 65k values on input will be 65k jump values on output, even if the actual DC value at input was -32k and DC on the output was 0.

superfury wrote:

I don't know what's wrong with the PC Speaker emulation itself yet though.

I have only played back the raw dumps and they show some variation in tempo, most likely because some parts are running with music playing in timer interrupt, some parts run music on vertical retrace timing. Pretty good actually.

Reply 52 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

So the actual reason that 8088 MPH is making slow music at some parts of the demo(like the 1K color image, where it shows on the vga only half a screen with the bottom part in text mode with blinking text (1K colors text enlarged) and the top half shaking with the data from the 1K image(1K colors on a IBM CGA I believe it says in the demo). So this isn't caused by the CPU or PIT that's too slow, but actually because the vertical retrace interrupt(IRQ2) isn't firing fast enough because the rendering is too slow(because it's limiting it's VGA resources, sharing 50/50 with the CPU processing timing(it only executes samples for as long as the computer can safely handle it, thus processing below normal VGA speed, thus triggering too few IRQ2 interrupts, causing music to slow down? At least it should do so according to the calculations, if they're correct?

//CPU cycle locked version of VGA rendering!
void updateVGA(double timepassed)
{
uint_64 limitcalc;
double timeprocessed;
VGA_timing += timepassed; //Time has passed!
if ((VGA_timing >= VGA_rendertiming) && VGA_rendertiming) //Might have passed?
{
uint_64 renderings,renderingsbackup;
renderings = (uint_64)(VGA_timing/VGA_rendertiming); //Ammount of times to render!
VGA_timing -= (renderings*VGA_rendertiming); //Rest the amount we can process!

if ((renderings>VGA_limit) && VGA_limit) //Limit broken?
{
renderings = VGA_limit; //Limit the processing to the amount of time specified!
}
if (!renderings) return; //Nothing to render!
timeprocessed = (renderings*VGA_rendertiming); //How much are we processing?
timeprocessed *= 0.50; //We're running too slow at full rendering, so split 50/50!
renderingsbackup = renderings; //Save the backup for comparision!

if (!doVGA_Sequencer()) return; //Don't execute the sequencer if requested to!

SEQ_DATA *Sequencer;
Sequencer = GETSEQUENCER(getActiveVGA()); //Our sequencer!

getnspassed(&VGA_test);
do
{
if (renderings>=20) //20+ optimization?
{
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
VGA_Sequencer(Sequencer); //Tick the VGA once!
renderings -= 19; //We've processed 19 more!
}
VGA_Sequencer(Sequencer); //Tick the VGA once!
} while (--renderings); //Ticks left to tick?
limitcalc = getnspassed(&VGA_test); //How long have we taken?

//timeprocessed=how much time to use, limitcalc=how much time we have taken, renderingsbackup=How many pixels have we processed.
VGA_limit = (uint_64)(((float)renderingsbackup/(float)limitcalc)*timeprocessed); //Don't process any more than we're allowed to (timepassed).
if (limitcalc<=timeprocessed) VGA_limit = 0; //Don't limit if we're running at full speed (we're below time we are allowed to process)!
}
Show last 1 line
}

Where timepassed is the time the CPU emulation has progressed, in nanoseconds. This is essentially based on either the 8086 4.77MHz clock tick time multiplied by the instruction cycles(currently 9 on 80(1)88, 8 on 80(1)86) with default setting used. When a Dosbox count is set, it uses 1ms time units(like Dosbox cycles setting) instead for any instruction, making sure count times 1000 instructions are executed each millisecond, like Dosbox.

Is the calculation made based on used VGA processing time and CPU instruction time(timepassed) correct? Does it actually use full VGA speed(limit of 0) when the host CPU is fast enough to run CPU and VGA 50/50, both at full speed?

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

Reply 53 of 55, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

No, it does not use IRQ2. CGA does not have it.

Besides the video timing is wildly different, using default CGA mode on real CGA adapter draws ~15700 lines per second at ~59.92 frames per second, while emulated CGA mode on VGA adapter draws ~31469 lines per second at ~70.09 frames per second. And who knows what tweaks are used to get possibly non-standard video modes or timings.

So unless you emulate CGA adapter, the timing of drawing scan lines and frames is not what the software expects.

Reply 54 of 55, by Scali

User metadata
Rank l33t
Rank
l33t
Jepael wrote:

And who knows what tweaks are used to get possibly non-standard video modes or timings.

All modes in 8088 MPH should maintain the standard 'near-NTSC' timing of CGA, as in 262 scanlines per field, and 59.92 fields per second. This is because our output is for NTSC-composite devices. So we don't have the flexibility of multisync monitors like you'd have with VGA.

http://scalibq.wordpress.com/just-keeping-it- … ro-programming/

Reply 55 of 55, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've increased the rendering parts speed a bit(VGA and text surfaces). Now 8088 MPH is running up to ~38-40 FPS framerate with ~90-100% CPU speed. Thus audio is a bit faster on those slow parts. It does hurt a little bit on the audio rendering for this reason though(only 90-100% samples rendered each second).

Edir: Once again increased it to ~70-80FPS. But running a simple game like Zool gets unstable framerate atm(Varying from 5-70FPS and back). Even though it locks to a certain rate after testing dynamical speed(old method) for 1000 pixels before locking the limiter(of pixels per handling). So if the host CPU can only handle x% of the pixels in the CPU time, only x% will be locked to until the next mode change(which affects rendering speed) with the new method. The old method won't lock. Full synchronization does neither, instead running full blast(and might be slowing the realtime CPU speed in the case the host isn't fast enough to handle it). Although only half of the actual CPU speed is used atm.

The old method continuously calculates, the new method calculates 1000 pixels and locks, full synchronization runs at full speed without limiting(100% CPU synchronized speed).

Edit: The unstable framerate seems to be fixed by setting the new synchronization setting BEFORE starting the game, as it seems to change the active display settings at that point only?

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