2 256boss - bootable launcher for 256b intros
3 Copyright (C) 2018-2019 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
60 int dt; /* remaining ticks delta from the previous event */
62 struct timer_event *next;
65 static void timer_handler(int inum);
67 static struct timer_event *evlist;
72 /* calculate the reload count: round(osc / freq) */
73 int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
75 /* set the mode to square wave for channel 0, both low
76 * and high reload count bytes will follow...
78 outb(CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE, PORT_CMD);
80 /* write the low and high bytes of the reload count to the
83 outb(reload_count & 0xff, PORT_DATA0);
84 outb((reload_count >> 8) & 0xff, PORT_DATA0);
86 /* set the timer interrupt handler */
87 interrupt(IRQ_TO_INTR(0), timer_handler);
90 void set_alarm(unsigned long msec, void (*func)(void))
92 int ticks, tsum, iflag;
93 struct timer_event *ev, *node;
95 if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
99 if(!(ev = malloc(sizeof *ev))) {
100 panic("failed to allocate timer event");
105 iflag = get_intr_flag();
108 if(!evlist || ticks < evlist->dt) {
109 /* insert at the begining */
115 ev->next->dt -= ticks;
121 while(node->next && ticks > tsum + node->next->dt) {
122 tsum += node->next->dt;
126 ev->next = node->next;
129 /* fix the relative times */
130 ev->dt = ticks - tsum;
132 ev->next->dt -= ev->dt;
136 set_intr_flag(iflag);
139 void cancel_alarm(void (*func)(void))
142 struct timer_event *ev, *node;
143 struct timer_event dummy;
145 iflag = get_intr_flag();
152 if(ev->func == func) {
155 ev->next->dt += ev->dt;
157 node->next = ev->next;
164 set_intr_flag(iflag);
167 static void timer_handler(int inum)
174 while(evlist && evlist->dt <= 0) {
175 struct timer_event *ev = evlist;
176 evlist = evlist->next;