2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
25 /* frequency of the oscillator driving the 8254 timer */
26 #define OSC_FREQ_HZ 1193182
28 /* macro to divide and round to the nearest integer */
29 #define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2))
31 /* I/O ports connected to the 8254 */
32 #define PORT_DATA0 0x40
33 #define PORT_DATA1 0x41
34 #define PORT_DATA2 0x42
39 #define CMD_CHAN1 (1 << 6)
40 #define CMD_CHAN2 (2 << 6)
41 #define CMD_RDBACK (3 << 6)
44 #define CMD_ACCESS_LOW (1 << 4)
45 #define CMD_ACCESS_HIGH (2 << 4)
46 #define CMD_ACCESS_BOTH (3 << 4)
48 #define CMD_OP_INT_TERM 0
49 #define CMD_OP_ONESHOT (1 << 1)
50 #define CMD_OP_RATE (2 << 1)
51 #define CMD_OP_SQWAVE (3 << 1)
52 #define CMD_OP_SOFT_STROBE (4 << 1)
53 #define CMD_OP_HW_STROBE (5 << 1)
55 #define CMD_MODE_BIN 0
56 #define CMD_MODE_BCD 1
58 volatile unsigned long nticks;
61 int dt; /* remaining ticks delta from the previous event */
63 struct timer_event *next;
66 static void timer_handler(int inum);
68 static struct timer_event *evlist;
73 /* calculate the reload count: round(osc / freq) */
74 int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
76 /* set the mode to square wave for channel 0, both low
77 * and high reload count bytes will follow...
79 outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
81 /* write the low and high bytes of the reload count to the
84 outp(PORT_DATA0, reload_count & 0xff);
85 outp(PORT_DATA0, (reload_count >> 8) & 0xff);
87 /* set the timer interrupt handler */
88 interrupt(IRQ_TO_INTR(0), timer_handler);
92 void set_alarm(unsigned long msec, void (*func)(void))
94 int ticks, tsum, iflag;
95 struct timer_event *ev, *node;
97 if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
101 if(!(ev = malloc(sizeof *ev))) {
102 panic("failed to allocate timer event");
107 iflag = get_intr_flag();
110 if(!evlist || ticks < evlist->dt) {
111 /* insert at the begining */
117 ev->next->dt -= ticks;
123 while(node->next && ticks > tsum + node->next->dt) {
124 tsum += node->next->dt;
128 ev->next = node->next;
131 /* fix the relative times */
132 ev->dt = ticks - tsum;
134 ev->next->dt -= ev->dt;
138 set_intr_flag(iflag);
141 void cancel_alarm(void (*func)(void))
144 struct timer_event *ev, *node;
145 struct timer_event dummy;
147 iflag = get_intr_flag();
154 if(ev->func == func) {
157 ev->next->dt += ev->dt;
159 node->next = ev->next;
166 set_intr_flag(iflag);
169 static void timer_handler(int inum)
176 while(evlist && evlist->dt <= 0) {
177 struct timer_event *ev = evlist;
178 evlist = evlist->next;