7

The 1989 2nd edition of the DOS PROGRAMMER'S REFERENCE says:

Midnight is determined as the number of ticks in a complete day of 86400 seconds (1573040 ticks of the clock, for a total elapsed time of 86399.9121 seconds). A flag byte in RAM is set to 1 when midnight passes, ...

The 1981 IBM 5150 Technical Reference manual shows next code on page A-77 (271/393):

  INC TIMER_LOW
  JNZ T4
  INC TIMER_HIGH
T4:
  CMP TIMER_HIGH, 018H
  JNZ T5
  CMP TIMER_LOW, 0B0H
  JNZ T5
  MOV TIMER_HIGH, 0
  MOV TIMER_LOW, 0
  MOV TIMER_OFL, 1
T5:

I have never given this a second thought before, but recently I ran a test on some of my older computers. I compiled next list:

BIOS Year TotalTicks
Award Modular BIOS v4.51 PG 1996 1573040
Phoenix BIOS 4.00 Release 6.00 1999 1573041
Compaq System ROM 686P9 v1.11 2002 1573040
Phoenix Technologies LTD v1.23 2007 1573041
AMI BIOS 2010 1573040

It appears that some BIOSes do add 1 extra tick to the count-of-day variable.


The 2007 Phoenix Technologies BIOS uses next code:

  add word [006Ch], 1
  adc word [006Eh], 0
  cmp word [006Eh], 24
  jne T1
  cmp word [006Ch], 00B1h
  jne T1
  xor ax, ax
  mov [006Ch], ax
  mov [006Eh], ax
  inc byte [0070h]
T1:

Other than using 'better' assembly code, this snippet shows 2 differences from the original:

  • 1800B1h instead of 1800B0h
  • incrementing instead of setting the midnight flag
9
  • 1
    Have you checked for the jump instructions they use? Are they as well using JNE, or some other condition? Incrementing a compare value by 1 is typical if one want's to use a JBE instead of a JNE. In that case using1573041 will work exactly like using 1573040. Which BTW also avoids any circumstance of an increment being missed due whatever reason.
    – Raffzahn
    Commented Dec 14, 2022 at 21:01
  • @Raffzahn I have added their relevant code to the question. There's definitely an extra tick.
    – Sep Roland
    Commented Dec 14, 2022 at 21:14
  • 2
    @supercat When IBM invented this scheme with wraparound at 1573040, computers typically didn't have real time clocks. The timer tick was used to give the time of day and needed the 24 hours wraparound for advancing the date. Is that "about 10 days, 15 hours, and 58 minutes" based on a 3-byte value that naturally wraps at 16MB?
    – Sep Roland
    Commented Dec 14, 2022 at 21:50
  • 1
    @ecm That's correct. I was going to leave a comment there linking to this question.
    – Sep Roland
    Commented Dec 14, 2022 at 21:57
  • 2
    @SepRoland: Converting a tick value into days+hours+minutes+seconds isn't really any worse than hours+minutes+seconds. Alternatively, things could have been simplified by using a timer-tick interval of either 33 or 44ms, and having a timer-tick handler add 33 or 44 to a 32-bit milliseconds counter, since a divisor that's a multiple of 13,125 will yield a time which, given a perfect crystal, would be an exact multiple of 11ms. Displaying a time as milliseconds would then have been very easy.
    – supercat
    Commented Dec 14, 2022 at 22:28

2 Answers 2

5

In short, it might be for avoiding the same value for copyright reasons, or simply because some BIOS vendors calculated the value with a timer frequency value that has more precision than what IBM used.

Of course we can start guessing why because we can't be sure why different tick count was used by different BIOS vendors.

Based on IBM BIOS source code comments, they used simply the 8253 timer clock rate as integer, truncated to 1193180. This makes 86400 seconds to equal 1573040.04 ticks, so rounding that to 1573040 is a logical choise for the day length in timer ticks.

In theory, the exact timer clock rate is 105/88 MHz, an endless decimal number 1193181.8181... MHz based on NTSC colorburst frequency. Based on that frequency, 86400 seconds is 1573042.436 timer ticks - actually two counts more.

Other BIOS vendors may have simply used a truncated integer of 1193181 in calculations, which does end up being rounded 1573041.

In practical world, that difference of two ticks differs the timekeeping only by 40 seconds in 365 days. That's less than 2 parts per million (ppm) of error, so the software is already more accurate in timekeeping than the tolerance of the NTSC-based clock crystal ticking on the motherboard.

While the error is insignificant for us humans, this obviously comes in with a sort of compatibility issue, as the user program cannot determine what the actual largest timer tick value is before it wraps around.

Some piece of code could do unexpected things if it expects the tick counter value to be in the IBM defined range.

But then again, the BIOS tick counter is mainly used for timing BIOS things like turning floppy motors off after some timeout of no activity.

And technically, the only interface to access the counter and overflow flag is BIOS time services interrupt 1Ah, which reads the counter and overflow flag, and clears the flag.

Since DOS uses that same interface anyway for reading the count, no program should directly use that interface to not confuse DOS by clearìng the flag and preventing DOS from seeing the overflow.

So as a system tick, it does just that, and DOS can utilize it for timekeeping. And based on the question, DOS has clearly defined the tick count to be the same value what IBM uses.

User programs requiring wall clock time should anyway ask the time from DOS, and leave DOS to handle the BIOS provided system timer tick. Let DOS track the wall clock time, and ask the DOS time in 0.01 second resolution.

Surely DOS will handle weird special cases, and it really does not matter if last second of a day is about 55 or 110 milliseconds longer, as it is good enough.

Of course programs can read the timer count directly for timing purposes so then it is a problem, if a certain maximum value is assumed or the wraparound to zero is not expected.

But, why would a program read the counter directly or assume something about it's range? It will be far better to just hook the timer interrupt by installing a custom interrupt handler, which can keep track of time with any overflow value required, and as a bonus, the user handled counter will not get suddenly written with a new value at supposed midnight. Either that or build in some tolerance.

1

Maybe some points to considere:

Was this a wide-spread phenomena for which one should take precautions when creating algorithms that convert the tick count into the corresponding time, so as to avoid showing "24:00:00"?

Increasing the tick number might rather create that issue than remove it - then again that all depends on how the tick number is to be converted by a caller, so outside the control of that number/function.

[...] incrementing instead of setting the midnight flag

Which is a common behaviour of modern BIOSes to reduce the change of skipping a day. In fact, later DOS versions (IIRC starting with 5.0) do check for values greater then one and act accordingly (*1).

Also we can eliminate a correction as reason as the BIOS clock isn't great to start with. Depending on model crystals used are off up to 50 ppm and still considered 'in spec' - plus moving up to 10 ppm per year. So BIOS clocks may differ between machines up to +/- 6 seconds per day and change that by up to 1.5 seconds over the course of a year.


*1 - IBM also tried to fix this on BIOS level with the PS/2 machines, but instead of incrementing, they introduced a day field (in the former cassette area IIRC).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .