blob: cc962e47fba28f6398572e3ae32cec87db9a723b [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to handle system clocks.
2//
Kevin O'Connorabf31d32010-07-26 22:33:54 -04003// Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05004// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "biosvar.h" // SET_BDA
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05009#include "util.h" // debug_enter
10#include "disk.h" // floppy_tick
Kevin O'Connor5d369d82013-09-02 20:48:46 -040011#include "hw/cmos.h" // inb_cmos
12#include "hw/pic.h" // pic_eoi1
13#include "hw/pit.h" // PM_SEL_TIMER0
Kevin O'Connor9521e262008-07-04 13:04:29 -040014#include "bregs.h" // struct bregs
Kevin O'Connor15157a32008-12-13 11:10:37 -050015#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor5d369d82013-09-02 20:48:46 -040016#include "hw/usb-hid.h" // usb_check_event
Kevin O'Connor4b60c002008-02-25 22:29:55 -050017
Kevin O'Connor5be04902008-05-18 17:12:06 -040018// RTC register flags
19#define RTC_A_UIP 0x80
Kevin O'Connorf3587592009-02-15 13:02:56 -050020
21#define RTC_B_SET 0x80
22#define RTC_B_PIE 0x40
23#define RTC_B_AIE 0x20
24#define RTC_B_UIE 0x10
25#define RTC_B_BIN 0x04
26#define RTC_B_24HR 0x02
27#define RTC_B_DSE 0x01
28
Kevin O'Connor5be04902008-05-18 17:12:06 -040029
Kevin O'Connor5be04902008-05-18 17:12:06 -040030/****************************************************************
31 * Init
32 ****************************************************************/
33
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050034static int
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050035rtc_updating(void)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050036{
37 // This function checks to see if the update-in-progress bit
38 // is set in CMOS Status Register A. If not, it returns 0.
39 // If it is set, it tries to wait until there is a transition
40 // to 0, and will return 0 if such a transition occurs. A -1
41 // is returned only after timing out. The maximum period
Kevin O'Connor4f5586c2009-02-16 10:14:10 -050042 // that this bit should be set is constrained to (1984+244)
Kevin O'Connor11cc6622010-03-13 23:04:41 -050043 // useconds, but we wait for longer just to be sure.
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050044
Kevin O'Connorf3587592009-02-15 13:02:56 -050045 if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050046 return 0;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040047 u32 end = timer_calc(15);
Kevin O'Connor11cc6622010-03-13 23:04:41 -050048 for (;;) {
Kevin O'Connorf3587592009-02-15 13:02:56 -050049 if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050050 return 0;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040051 if (timer_check(end))
Kevin O'Connor11cc6622010-03-13 23:04:41 -050052 // update-in-progress never transitioned to 0
53 return -1;
54 yield();
55 }
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050056}
57
Kevin O'Connor4b60c002008-02-25 22:29:55 -050058static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050059pit_setup(void)
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040060{
61 // timer0: binary count, 16bit count, mode 2
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050062 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE);
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040063 // maximum count of 0000H = 18.2Hz
64 outb(0x0, PORT_PIT_COUNTER0);
65 outb(0x0, PORT_PIT_COUNTER0);
66}
67
Kevin O'Connorf3587592009-02-15 13:02:56 -050068static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -050069rtc_setup(void)
Kevin O'Connorf3587592009-02-15 13:02:56 -050070{
Kevin O'Connor4f5586c2009-02-16 10:14:10 -050071 outb_cmos(0x26, CMOS_STATUS_A); // 32,768Khz src, 976.5625us updates
Kevin O'Connorf3587592009-02-15 13:02:56 -050072 u8 regB = inb_cmos(CMOS_STATUS_B);
73 outb_cmos((regB & RTC_B_DSE) | RTC_B_24HR, CMOS_STATUS_B);
74 inb_cmos(CMOS_STATUS_C);
75 inb_cmos(CMOS_STATUS_D);
76}
77
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040078static u32
79bcd2bin(u8 val)
80{
81 return (val & 0xf) + ((val >> 4) * 10);
82}
83
Kevin O'Connor0f6198a2013-02-07 23:41:53 -050084u8 Century VARLOW;
85
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040086void
Kevin O'Connorc6e8c072013-07-20 10:51:58 -040087clock_setup(void)
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040088{
Kevin O'Connor35192dd2008-06-08 19:18:33 -040089 dprintf(3, "init timer\n");
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040090 pit_setup();
91
Kevin O'Connord83c87b2013-01-21 01:14:12 -050092 rtc_setup();
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050093 rtc_updating();
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040094 u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040095 u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040096 u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
Kevin O'Connor69013372013-07-20 12:08:48 -040097 u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000);
Kevin O'Connorb7ab1782013-07-20 13:06:35 -040098 SET_BDA(timer_counter, ticks % TICKS_PER_DAY);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040099
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500100 // Setup Century storage
101 if (CONFIG_QEMU) {
102 Century = inb_cmos(CMOS_CENTURY);
103 } else {
104 // Infer current century from the year.
105 u8 year = inb_cmos(CMOS_RTC_YEAR);
106 if (year > 0x80)
107 Century = 0x19;
108 else
109 Century = 0x20;
110 }
111
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400112 enable_hwirq(0, FUNC16(entry_08));
113 enable_hwirq(8, FUNC16(entry_70));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400114}
115
Kevin O'Connor5be04902008-05-18 17:12:06 -0400116
117/****************************************************************
118 * Standard clock functions
119 ****************************************************************/
120
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500121// get current clock count
122static void
123handle_1a00(struct bregs *regs)
124{
Kevin O'Connor68c51392010-03-13 22:23:44 -0500125 yield();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500126 u32 ticks = GET_BDA(timer_counter);
127 regs->cx = ticks >> 16;
128 regs->dx = ticks;
129 regs->al = GET_BDA(timer_rollover);
130 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor6c781222008-03-09 12:19:23 -0400131 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500132}
133
134// Set Current Clock Count
135static void
136handle_1a01(struct bregs *regs)
137{
138 u32 ticks = (regs->cx << 16) | regs->dx;
139 SET_BDA(timer_counter, ticks);
140 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor15157a32008-12-13 11:10:37 -0500141 // XXX - should use set_code_success()?
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500142 regs->ah = 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400143 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500144}
145
146// Read CMOS Time
147static void
148handle_1a02(struct bregs *regs)
149{
150 if (rtc_updating()) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500151 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500152 return;
153 }
154
155 regs->dh = inb_cmos(CMOS_RTC_SECONDS);
156 regs->cl = inb_cmos(CMOS_RTC_MINUTES);
157 regs->ch = inb_cmos(CMOS_RTC_HOURS);
Kevin O'Connorf3587592009-02-15 13:02:56 -0500158 regs->dl = inb_cmos(CMOS_STATUS_B) & RTC_B_DSE;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500159 regs->ah = 0;
160 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400161 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500162}
163
164// Set CMOS Time
165static void
166handle_1a03(struct bregs *regs)
167{
168 // Using a debugger, I notice the following masking/setting
169 // of bits in Status Register B, by setting Reg B to
170 // a few values and getting its value after INT 1A was called.
171 //
172 // try#1 try#2 try#3
173 // before 1111 1101 0111 1101 0000 0000
174 // after 0110 0010 0110 0010 0000 0010
175 //
176 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
177 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
178 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500179 rtc_setup();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500180 // fall through as if an update were not in progress
181 }
182 outb_cmos(regs->dh, CMOS_RTC_SECONDS);
183 outb_cmos(regs->cl, CMOS_RTC_MINUTES);
184 outb_cmos(regs->ch, CMOS_RTC_HOURS);
185 // Set Daylight Savings time enabled bit to requested value
Kevin O'Connorf3587592009-02-15 13:02:56 -0500186 u8 val8 = ((inb_cmos(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE))
187 | RTC_B_24HR | (regs->dl & RTC_B_DSE));
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500188 outb_cmos(val8, CMOS_STATUS_B);
189 regs->ah = 0;
190 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400191 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500192}
193
194// Read CMOS Date
195static void
196handle_1a04(struct bregs *regs)
197{
198 regs->ah = 0;
199 if (rtc_updating()) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500200 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500201 return;
202 }
203 regs->cl = inb_cmos(CMOS_RTC_YEAR);
204 regs->dh = inb_cmos(CMOS_RTC_MONTH);
205 regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500206 regs->ch = GET_LOW(Century);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500207 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400208 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500209}
210
211// Set CMOS Date
212static void
213handle_1a05(struct bregs *regs)
214{
215 // Using a debugger, I notice the following masking/setting
216 // of bits in Status Register B, by setting Reg B to
217 // a few values and getting its value after INT 1A was called.
218 //
219 // try#1 try#2 try#3 try#4
220 // before 1111 1101 0111 1101 0000 0010 0000 0000
221 // after 0110 1101 0111 1101 0000 0010 0000 0000
222 //
223 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
224 // My assumption: RegB = (RegB & 01111111b)
225 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500226 rtc_setup();
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500227 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500228 return;
229 }
230 outb_cmos(regs->cl, CMOS_RTC_YEAR);
231 outb_cmos(regs->dh, CMOS_RTC_MONTH);
232 outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500233 SET_LOW(Century, regs->ch);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400234 // clear halt-clock bit
235 u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500236 outb_cmos(val8, CMOS_STATUS_B);
237 regs->ah = 0;
238 regs->al = val8; // AL = val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400239 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500240}
241
242// Set Alarm Time in CMOS
243static void
244handle_1a06(struct bregs *regs)
245{
246 // Using a debugger, I notice the following masking/setting
247 // of bits in Status Register B, by setting Reg B to
248 // a few values and getting its value after INT 1A was called.
249 //
250 // try#1 try#2 try#3
251 // before 1101 1111 0101 1111 0000 0000
252 // after 0110 1111 0111 1111 0010 0000
253 //
254 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
255 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
256 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
257 regs->ax = 0;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500258 if (val8 & RTC_B_AIE) {
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500259 // Alarm interrupt enabled already
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500260 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500261 return;
262 }
263 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500264 rtc_setup();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500265 // fall through as if an update were not in progress
266 }
267 outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
268 outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
269 outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500270 // enable Status Reg B alarm bit, clear halt clock bit
Kevin O'Connor5be04902008-05-18 17:12:06 -0400271 outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400272 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500273}
274
275// Turn off Alarm
276static void
277handle_1a07(struct bregs *regs)
278{
279 // Using a debugger, I notice the following masking/setting
280 // of bits in Status Register B, by setting Reg B to
281 // a few values and getting its value after INT 1A was called.
282 //
283 // try#1 try#2 try#3 try#4
284 // before 1111 1101 0111 1101 0010 0000 0010 0010
285 // after 0100 0101 0101 0101 0000 0000 0000 0010
286 //
287 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
288 // My assumption: RegB = (RegB & 01010111b)
289 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
290 // clear clock-halt bit, disable alarm bit
Kevin O'Connor5be04902008-05-18 17:12:06 -0400291 outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500292 regs->ah = 0;
293 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400294 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500295}
296
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500297// Unsupported
298static void
299handle_1aXX(struct bregs *regs)
300{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500301 set_unimplemented(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500302}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500303
304// INT 1Ah Time-of-day Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500305void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500306handle_1a(struct bregs *regs)
307{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400308 debug_enter(regs, DEBUG_HDL_1a);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500309 switch (regs->ah) {
310 case 0x00: handle_1a00(regs); break;
311 case 0x01: handle_1a01(regs); break;
312 case 0x02: handle_1a02(regs); break;
313 case 0x03: handle_1a03(regs); break;
314 case 0x04: handle_1a04(regs); break;
315 case 0x05: handle_1a05(regs); break;
316 case 0x06: handle_1a06(regs); break;
317 case 0x07: handle_1a07(regs); break;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500318 default: handle_1aXX(regs); break;
319 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500320}
321
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500322// INT 08h System Timer ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500323void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400324handle_08(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500325{
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400326 debug_isr(DEBUG_ISR_08);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500327
Kevin O'Connor3e641f22013-03-02 18:19:31 -0500328 // Update counter
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500329 u32 counter = GET_BDA(timer_counter);
330 counter++;
331 // compare to one days worth of timer ticks at 18.2 hz
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400332 if (counter >= TICKS_PER_DAY) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500333 // there has been a midnight rollover at this point
334 counter = 0;
335 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
336 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500337 SET_BDA(timer_counter, counter);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500338
Kevin O'Connor3e641f22013-03-02 18:19:31 -0500339 // Check for internal events.
340 floppy_tick();
Kevin O'Connor0e885762010-05-01 22:14:40 -0400341 usb_check_event();
Kevin O'Connor114592f2009-09-28 21:32:08 -0400342
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500343 // chain to user timer tick INT #0x1c
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400344 struct bregs br;
345 memset(&br, 0, sizeof(br));
346 br.flags = F_IF;
347 call16_int(0x1c, &br);
Kevin O'Connored128492008-03-11 11:14:59 -0400348
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400349 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500350}
351
Kevin O'Connor5be04902008-05-18 17:12:06 -0400352
353/****************************************************************
354 * Periodic timer
355 ****************************************************************/
356
Kevin O'Connor9d254d42012-05-13 12:18:36 -0400357int RTCusers VARLOW;
358
Kevin O'Connorad901592009-12-13 11:25:25 -0500359void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500360useRTC(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500361{
Kevin O'Connor9d254d42012-05-13 12:18:36 -0400362 int count = GET_LOW(RTCusers);
363 SET_LOW(RTCusers, count+1);
Kevin O'Connorad901592009-12-13 11:25:25 -0500364 if (count)
365 return;
366 // Turn on the Periodic Interrupt timer
367 u8 bRegister = inb_cmos(CMOS_STATUS_B);
368 outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
369}
370
371void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500372releaseRTC(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500373{
Kevin O'Connor9d254d42012-05-13 12:18:36 -0400374 int count = GET_LOW(RTCusers);
375 SET_LOW(RTCusers, count-1);
Kevin O'Connorad901592009-12-13 11:25:25 -0500376 if (count != 1)
377 return;
378 // Clear the Periodic Interrupt.
379 u8 bRegister = inb_cmos(CMOS_STATUS_B);
380 outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
381}
382
Kevin O'Connor5be04902008-05-18 17:12:06 -0400383static int
Kevin O'Connor72743f12008-05-24 23:04:09 -0400384set_usertimer(u32 usecs, u16 seg, u16 offset)
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500385{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400386 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
387 return -1;
388
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500389 // Interval not already set.
390 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400391 SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset));
Kevin O'Connor72743f12008-05-24 23:04:09 -0400392 SET_BDA(user_wait_timeout, usecs);
Kevin O'Connorad901592009-12-13 11:25:25 -0500393 useRTC();
Kevin O'Connor5be04902008-05-18 17:12:06 -0400394 return 0;
395}
396
397static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500398clear_usertimer(void)
Kevin O'Connor5be04902008-05-18 17:12:06 -0400399{
Kevin O'Connorad901592009-12-13 11:25:25 -0500400 if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING))
401 return;
Kevin O'Connor5be04902008-05-18 17:12:06 -0400402 // Turn off status byte.
403 SET_BDA(rtc_wait_flag, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500404 releaseRTC();
Kevin O'Connor5be04902008-05-18 17:12:06 -0400405}
406
Kevin O'Connor5be04902008-05-18 17:12:06 -0400407#define RET_ECLOCKINUSE 0x83
408
Kevin O'Connord21c0892008-11-26 17:02:43 -0500409// Wait for CX:DX microseconds
Kevin O'Connor5be04902008-05-18 17:12:06 -0400410void
411handle_1586(struct bregs *regs)
412{
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500413 // Use the rtc to wait for the specified time.
414 u8 statusflag = 0;
415 u32 count = (regs->cx << 16) | regs->dx;
416 int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
417 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500418 set_code_invalid(regs, RET_ECLOCKINUSE);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500419 return;
420 }
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500421 while (!statusflag)
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400422 yield_toirq();
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500423 set_success(regs);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400424}
425
426// Set Interval requested.
427static void
428handle_158300(struct bregs *regs)
429{
430 int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
431 if (ret)
432 // Interval already set.
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500433 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400434 else
435 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500436}
437
438// Clear interval requested
439static void
440handle_158301(struct bregs *regs)
441{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400442 clear_usertimer();
443 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500444}
445
446static void
447handle_1583XX(struct bregs *regs)
448{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500449 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500450 regs->al--;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500451}
452
453void
454handle_1583(struct bregs *regs)
455{
456 switch (regs->al) {
457 case 0x00: handle_158300(regs); break;
458 case 0x01: handle_158301(regs); break;
459 default: handle_1583XX(regs); break;
460 }
461}
462
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400463#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024)
464
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500465// int70h: IRQ8 - CMOS RTC
Kevin O'Connor19786762008-03-05 21:09:59 -0500466void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400467handle_70(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500468{
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400469 debug_isr(DEBUG_ISR_70);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500470
471 // Check which modes are enabled and have occurred.
472 u8 registerB = inb_cmos(CMOS_STATUS_B);
473 u8 registerC = inb_cmos(CMOS_STATUS_C);
474
Kevin O'Connor5be04902008-05-18 17:12:06 -0400475 if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500476 goto done;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500477 if (registerC & RTC_B_AIE) {
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500478 // Handle Alarm Interrupt.
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400479 struct bregs br;
480 memset(&br, 0, sizeof(br));
481 br.flags = F_IF;
482 call16_int(0x4a, &br);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500483 }
Kevin O'Connorf3587592009-02-15 13:02:56 -0500484 if (!(registerC & RTC_B_PIE))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500485 goto done;
486
487 // Handle Periodic Interrupt.
488
Kevin O'Connorad901592009-12-13 11:25:25 -0500489 check_preempt();
490
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500491 if (!GET_BDA(rtc_wait_flag))
492 goto done;
493
494 // Wait Interval (Int 15, AH=83) active.
495 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400496 if (time < USEC_PER_RTC) {
Kevin O'Connor5be04902008-05-18 17:12:06 -0400497 // Done waiting - write to specified flag byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400498 struct segoff_s segoff = GET_BDA(user_wait_complete_flag);
499 u16 ptr_seg = segoff.seg;
500 u8 *ptr_far = (u8*)(segoff.offset+0);
501 u8 oldval = GET_FARVAR(ptr_seg, *ptr_far);
502 SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400503
504 clear_usertimer();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500505 } else {
506 // Continue waiting.
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400507 time -= USEC_PER_RTC;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500508 SET_BDA(user_wait_timeout, time);
509 }
510
511done:
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400512 pic_eoi2();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500513}