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 cleanup_timer(void)
94 /* return the timer to the original rate */
95 outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
100 void set_alarm(unsigned long msec, void (*func)(void))
102 int ticks, tsum, iflag;
103 struct timer_event *ev, *node;
105 if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
109 if(!(ev = malloc(sizeof *ev))) {
110 panic("failed to allocate timer event");
115 iflag = get_intr_flag();
118 if(!evlist || ticks < evlist->dt) {
119 /* insert at the begining */
125 ev->next->dt -= ticks;
131 while(node->next && ticks > tsum + node->next->dt) {
132 tsum += node->next->dt;
136 ev->next = node->next;
139 /* fix the relative times */
140 ev->dt = ticks - tsum;
142 ev->next->dt -= ev->dt;
146 set_intr_flag(iflag);
149 void cancel_alarm(void (*func)(void))
152 struct timer_event *ev, *node;
153 struct timer_event dummy;
155 iflag = get_intr_flag();
162 if(ev->func == func) {
165 ev->next->dt += ev->dt;
167 node->next = ev->next;
174 set_intr_flag(iflag);
177 static void timer_handler(int inum)
184 while(evlist && evlist->dt <= 0) {
185 struct timer_event *ev = evlist;
186 evlist = evlist->next;