VOGONS


First post, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Hi all,

I am having some issues with the IBM PC 5162 (XT/286) BIOS in my emulator. It sets the 8253 channel 1 to mode 0 then repeatedly reads the 4th bit of port 0x61 from the 8042 (since the 286/XT comes with an 8042 and not a PPI) and waits for it to flip. According to Bochs port list

bit 4	 toggles with each refresh request

I assume that means RAM refresh. The problem with mode 0 in 8253 is that it is an "interrupt on terminal count". So it gets fired once and then...no more. That means I think that there is no more RAM fresh. And no more flipping of bit 4 on status word of port 0x61 of the 8042. And...the BIOS eventually hangs with a HALT.

So, am I misunderstanding how "interrupt on terminal count" works or maybe how RAM refresh and timer channel 1 works?

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 1 of 6, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Argh!

From VCF:

The PC and XT uses the actual DMA Channel 0 to refresh memory, while The AT has a memory-refresher based on discrete logic components.

From 286/XT IBM Manual:

The refresh controller steps one refresh address every 15 
microseconds. Each refresh cycle requires eight clock cycles to
refresh all of the system's dynamic memory; 256 refresh cycles
are required every 4 milliseconds, but the system hardware
refreshes every 3.84ms.

So separate HW. It also uses 9% of the bandwidth to refresh memory.

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 2 of 6, by Scali

User metadata
Rank l33t
Rank
l33t
vladstamate wrote:

So separate HW. It also uses 9% of the bandwidth to refresh memory.

Yes, as far as I know, the timer is not connected to the DMA controller at all, so you can't use timer channel 1 for DMA. I think DMA channel 0 is also a 'dummy' channel on AT and newer. So you have to change this behaviour in your emulator as well, and 'disconnect' it (I think this is so software doesn't 'accidentally' program the DMA for refresh anyway).
The chipset has its own refresh circuit built in. Many clone chipsets allow you to tweak the refresh rate somewhat.
So basically you need to steal bus cycles periodically just like with the PC/XT. The only difference is that you have to introduce some new 'magic' component that does this, the 'refresh controller', rather than just emulating the timer and DMA controller, and letting the BIOS take care of it by programming the PIT and DMA to refresh every 18 ticks.

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

Reply 3 of 6, by superfury

User metadata
Rank l33t++
Rank
l33t++
vladstamate wrote:
Hi all, […]
Show full quote

Hi all,

I am having some issues with the IBM PC 5162 (XT/286) BIOS in my emulator. It sets the 8253 channel 1 to mode 0 then repeatedly reads the 4th bit of port 0x61 from the 8042 (since the 286/XT comes with an 8042 and not a PPI) and waits for it to flip. According to Bochs port list

bit 4	 toggles with each refresh request

I assume that means RAM refresh. The problem with mode 0 in 8253 is that it is an "interrupt on terminal count". So it gets fired once and then...no more. That means I think that there is no more RAM fresh. And no more flipping of bit 4 on status word of port 0x61 of the 8042. And...the BIOS eventually hangs with a HALT.

So, am I misunderstanding how "interrupt on terminal count" works or maybe how RAM refresh and timer channel 1 works?

The mode 0 is identical to mode 1, except that:
- Mode 0 only ticks when the gate is high.
- Mode 0 doesn't wait for the gate to have a rising edge before starting to tick.

				case 0: //Interrupt on Terminal Count? Is One-Shot without Gate Input?
case 1: //One-shot mode?
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!
PITchannels[channel].reloadlistening = 1; //We're listening to reloads!
if (PITchannels[channel].reload)
{
PITchannels[channel].gatelistening = mode; //We're listening to gate with mode 1!
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].gatelistening = 0; //We're not listening to gate with mode 1 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! The other channels are tied 1!
wrapPITticker(channel); //Wrap us correctly!
if ((!PITchannels[channel].ticker) && oldvalue) //Timeout when ticking? We're done!
{
PITchannels[channel].channel_status = 1; //We're high again!
}
break;
default: //Unsupported! Ignore any input!
break;
}
break;

That code was based on the articles on OSDev wiki.

Mode 0 will continue ticking on(none of the modes actually 'stop' counting), although indeed no more interrupts are triggered(as the output will keep staying 1 until a new mode is set or the reload value is overwritten).

Is it maybe testing that it's counting once and then setting something else up expecting that to happen(which isn't emulated), but it times out because of it not receiving a response?

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

Reply 4 of 6, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Mode 0 will continue ticking on(none of the modes actually 'stop' counting), although indeed no more interrupts are triggered(as the output will keep staying 1 until a new mode is set or the reload value is overwritten).

Thank you Superfury and Scali. So the CPU interrupts from 8253 are edge triggered and not level triggered?

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 5 of 6, by Scali

User metadata
Rank l33t
Rank
l33t
vladstamate wrote:

So the CPU interrupts from 8253 are edge triggered and not level triggered?

Yes, I mean no, I mean, don't care.
Early PCs have edge triggered interrupts.
IBM switched to level-trigger in the PS/2 (which afaik doesn't use physical 8253/4 or 8259 chips, but integrates equivalent logic in its custom chipset, so they probably also modified the timer to work properly in level-triggered mode).

When EISA came around, they figured that level-triggering is indeed the better option. However, ISA was still edge-triggered.
Since by this time physical 82xx chips were no longer used, they just virtualized the edge/level setting of the circuit.
They introduced a separate register where you could select for each interrupt whether it would be edge-triggered (so for use with legacy ISA hardware) or level-triggered (for EISA hardware).
These are called Edge/Level Control Register (ELCR). The master PIC ELCR is at 0x4D0, and the slave PIC ELCR is at 0x4D1.

They kept using that system for PCI. I guess it is no longer relevant, because the physical ISA bus has long disappeared, and with it, edge-triggered interrupts.

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

Reply 6 of 6, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Thank you for the explanation Scali.

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/