3 * 8254 programmable interrupt timer control library.
4 * (C) 2008-2012 Jonathan Campbell.
5 * Hackipedia DOS library.
7 * This code is licensed under the LGPL.
8 * <insert LGPL legal text here>
10 * Compiles for intended target environments:
11 * - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
13 * The 8254 Programmable Interrupt Timer is used on the PC platform for
14 * several purposes. 3 Timers are provided, which are used as:
16 * Timer 0 - System timer. This is tied to IRQ 0 and under normal operation
17 * provides the usual IRQ 0 18.2 ticks per second. DOS programs that
18 * need higher resolution reprogram this timer to get it.
20 * Timer 1 - Misc, varies. On older PC hardware this was tied to DRAM refresh.
21 * Modern hardware cycles it for whatever purpose. Don't assume it's function.
23 * Timer 2 - PC Speaker. This timer is configured to run as a square wave and it's
24 * output is gated through the 8042 before going directly to a speaker in the
25 * PC's computer case. When used, this timer allows your program to generate
28 * On modern hardware this chip is either integrated into the core motherboard chipset or
29 * emulated for backwards compatibility.
33 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
36 #include "src/lib/doslib/8254.h"
38 uint32_t t8254_counter[3] = {0x10000,0x10000,0x10000};
39 int8_t probed_8254_result = -1;
42 if (probed_8254_result >= 0)
43 return (int)probed_8254_result;
45 /* NTS: Reading port 0x43 does nothing. Intel's datasheet even mentions this in one of the tables.
46 * Actual hardware experience tells me some motherboards DO return something but the correct
47 * response (as seen in emulators in DOSBox) is to ignore, returning 0xFF */
49 /* read timer 0 and see if it comes back non-0xFF */
50 /* NTS: We MUST use the read_8254 function to read it in order. The previous
51 * version of this code read it byte-wise. For some reason it seems,
52 * some emulators including DOSBox don't take that well and they fail
53 * to reset the MSB/LSB flip-flop. When that happens our timer counter
54 * readings come out byte-swapped and we cannot provide proper timing
55 * and sleep routines. Symptoms: A DOS program using our timing code
56 * would have proper timing only on every other run. */
57 unsigned int patience = 128,cc;
61 if (c == 0xFFFF) c = read_8254(0);
62 if (c != 0xFFFF) break;
63 for (cc=0;cc != 0xFFFFU;) cc++; /* delay */
64 } while (patience-- > 0);
67 return (probed_8254_result=0);
70 return (probed_8254_result=1);
73 unsigned long t8254_us2ticks(unsigned long a) {
74 /* FIXME: can you write a version that doesn't require 64-bit integers? */
77 b = (uint64_t)a * (uint64_t)T8254_REF_CLOCK_HZ;
78 return (unsigned long)(b / 1000000ULL);
81 unsigned long t8254_us2ticksr(unsigned long a,unsigned long *rem) {
82 /* FIXME: can you write a version that doesn't require 64-bit integers? */
85 b = (uint64_t)a * (uint64_t)T8254_REF_CLOCK_HZ;
86 *rem = (unsigned long)(b % 1000000ULL);
87 return (unsigned long)(b / 1000000ULL);
90 void t8254_wait(unsigned long ticks) {
93 if (ticks <= 1) return;
100 dec = (pr + (uint16_t)t8254_counter[0] - cr);
105 } while ((signed long)ticks >= 0L);