]> 4ch.mooo.com Git - 16.git/blob - src/lib/dl/8254.c
meh did some cleanings and i will work on mapread to mm thingy sometime soon! oops...
[16.git] / src / lib / dl / 8254.c
1 /* 8254.c
2  *
3  * 8254 programmable interrupt timer control library.
4  * (C) 2008-2012 Jonathan Campbell.
5  * Hackipedia DOS library.
6  *
7  * This code is licensed under the LGPL.
8  * <insert LGPL legal text here>
9  *
10  * Compiles for intended target environments:
11  *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
12  *
13  * The 8254 Programmable Interrupt Timer is used on the PC platform for
14  * several purposes. 3 Timers are provided, which are used as:
15  *
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.
19  *
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.
22  *
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
26  *            audible beeps.
27  *
28  * On modern hardware this chip is either integrated into the core motherboard chipset or
29  * emulated for backwards compatibility.
30  */
31
32 #include <stdio.h>
33 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
34 #include <stdlib.h>
35
36 #include "src/lib/doslib/8254.h"
37
38 uint32_t t8254_counter[3] = {0x10000,0x10000,0x10000};
39 int8_t probed_8254_result = -1;
40
41 int probe_8254() {
42         if (probed_8254_result >= 0)
43                 return (int)probed_8254_result;
44
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 */
48         {
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;
58                 unsigned short c;
59                 do {
60                         c = read_8254(0);
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);
65
66                 if (c == 0xFF)
67                         return (probed_8254_result=0);
68         }
69
70         return (probed_8254_result=1);
71 }
72
73 unsigned long t8254_us2ticks(unsigned long a) {
74         /* FIXME: can you write a version that doesn't require 64-bit integers? */
75         uint64_t b;
76         if (a == 0) return 0;
77         b = (uint64_t)a * (uint64_t)T8254_REF_CLOCK_HZ;
78         return (unsigned long)(b / 1000000ULL);
79 }
80
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? */
83         uint64_t b;
84         if (a == 0) return 0;
85         b = (uint64_t)a * (uint64_t)T8254_REF_CLOCK_HZ;
86         *rem = (unsigned long)(b % 1000000ULL);
87         return (unsigned long)(b / 1000000ULL);
88 }
89
90 void t8254_wait(unsigned long ticks) {
91         uint16_t dec;
92         t8254_time_t pr,cr;
93         if (ticks <= 1) return;
94         ticks--;
95         cr = read_8254(0);
96         do {
97                 pr = cr;
98                 cr = read_8254(0);
99                 if (cr > pr)
100                         dec = (pr + (uint16_t)t8254_counter[0] - cr);
101                 else
102                         dec = (pr - cr);
103
104                 ticks -= dec;
105         } while ((signed long)ticks >= 0L);
106 }
107