VOGONS


First post, by markot

User metadata
Rank Member
Rank
Member

In Turbo Pascal there is the divide error bug. On website are ways to fix it. But there is said the ticks 1193180 per second. When I tested this on some computers, a 10 second delay using the functions below gave only a 5 second delay.

On other website (http://www.delphigroups.info/2/d2/16758.html) I found out that following comment:

{ =========================================================== } { Wait specified number of 1.193180 MHz clock tics to elapse. } […]
Show full quote

{ =========================================================== }
{ Wait specified number of 1.193180 MHz clock tics to elapse. }
{ The system timer normally runs in mode 3, which counts down }
{ by twos, so Delay(1193180) may be only a half a second. }
{ =========================================================== }

Where could I find out more information about this thing? What is system timer "mode 3"?

http://mtech.dk/thomsen/program/pasbug.php

procedure ShortDelay(Interval: Word); assembler;
{ Interval = number of ticks
Note: About 1193180 ticks/s }
asm
push ax
push bx
cmp Interval,0FFFFh { otherwise 0FFFFh will end in an infinite loop }
jne @start
dec Interval
@start:
in al,040h { save initial time in bx }
mov bl,al
in al,040h
mov bh,al
@delayloop:
in al,040h { get current time }
xchg al,ah
in al,040h
xchg al,ah
sub ax,bx { calculate the difference }
neg ax
cmp ax,Interval { are we done? }
jb @delayloop
pop bx
pop ax
end;

procedure delay(ms: Word);
{ identical to the faulty Borland delay procedure }
var
A : Word;
begin
for A := 1 to ms do
ShortDelay(1193); { pause for 1 ms }
end;

Reply 1 of 5, by markot

User metadata
Rank Member
Rank
Member

One addition to the initial question. When running this is a virtual machine, e.g. a 10 second delay will be 10 seconds.

But running the same on real hardware, will only delay for 5 seconds.

How do I fix it?

Reply 2 of 5, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie

For example http://wiki.osdev.org/Programmable_Interval_Timer or 8254 datasheet.

This is normal. When in square wave mode (3), the timer counts only with even numbers, but it counts one period twice, to get square wave out. It counts once for high period and once for low period, so to the software reading the count it looks like it counts twice as fast and you get half the delay you requested.

When in rate generator mode (2), the timer counts normally, but then the output pulse is very short (one tick of 1.19MHz clock), which may or may not be suitable for every use.

Most BIOSes normally use timer 0 in mode 3 to trigger interrupts, I have previously used mode 2 and on my PCs it has worked, but I can't guarantee it, so nowadays I use mode 3.

If you want, you could change the timer mode so the code works, or keep the timer mode and request double amount of ticks for delays. The problem is you don't know which mode the BIOS is using. Unless you use the readback command, but it is not available on PCs and XTs because they use 8253 timer, while AT and later use 8254 where it is available. You could also detect the mode by checking the timer count say 10000 times, and if it is mostly even, then it is in square wave mode.

Reply 3 of 5, by markot

User metadata
Rank Member
Rank
Member

I'm not sure if I have understood things correctly. If I want to get the mode for counter 0 and I have set the bits for the read back command, this just seems to return some random value when then reading from 40h.

Own example code:

function readpitmode:Word; assembler;
asm
mov al, 11000010b
out 43h, al
mov ah, 0
in al, 40h
end;

Reply 4 of 5, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
markot wrote:
I'm not sure if I have understood things correctly. If I want to get the mode for counter 0 and I have set the bits for the read […]
Show full quote

I'm not sure if I have understood things correctly. If I want to get the mode for counter 0 and I have set the bits for the read back command, this just seems to return some random value when then reading from 40h.

Own example code:

function readpitmode:Word; assembler;
asm
mov al, 11000010b
out 43h, al
mov ah, 0
in al, 40h
end;

Well, that should work, but you latch both status and count, but read only one byte (which should be status).
Try sending a command to latch status only, but no count, and read the one byte back.

Which virtual machine and which real PC hardware you are using?
It might be that VMs don't emulate PIT so accurately, and if your hardware predates AT then it has the older 8253 timer which does not have readback command.

Reply 5 of 5, by markot

User metadata
Rank Member
Rank
Member

Here is my new code. Maybe I did something right this time. According to the 8254 datasheet and the osdev link you gave, it should be correct now.

The results are the following when running on virtual machines:

    DOSBox: 00110110
Virtual PC: 10110100
VirtualBox: 00110100

Bits 1-3 are 011 for DOSBox. Datasheet says this is Mode 3.
Bits 1-3 are 010 for Virtual PC and VirtualBox. Datasheet says Mode 2.


function readpitmode:byte; assembler;
asm
mov al, 11100010b
out 43h, al
mov ah, 0
in al, 40h
end;

function bytetobin(b:byte):string;
var s:string;
i:integer;
begin
s:='';
for i:=0 to 7 do
begin
if (((1 shl i) and b)>0) then s:='1'+s else s:='0'+s;
end;
bytetobin:=s;
end;

begin
writeln(bytetobin(readpitmode));
readln;
end.