blob: fc91db77d9a02381e95d8ca27532d8a6f26531e2 [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'Connorfa9c66a2013-09-14 19:10:40 -040017#include "string.h" // memset
Kevin O'Connor4b60c002008-02-25 22:29:55 -050018
Kevin O'Connor5be04902008-05-18 17:12:06 -040019// RTC register flags
20#define RTC_A_UIP 0x80
Kevin O'Connorf3587592009-02-15 13:02:56 -050021
22#define RTC_B_SET 0x80
23#define RTC_B_PIE 0x40
24#define RTC_B_AIE 0x20
25#define RTC_B_UIE 0x10
26#define RTC_B_BIN 0x04
27#define RTC_B_24HR 0x02
28#define RTC_B_DSE 0x01
29
Kevin O'Connor5be04902008-05-18 17:12:06 -040030
Kevin O'Connor5be04902008-05-18 17:12:06 -040031/****************************************************************
32 * Init
33 ****************************************************************/
34
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050035static int
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050036rtc_updating(void)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050037{
38 // This function checks to see if the update-in-progress bit
39 // is set in CMOS Status Register A. If not, it returns 0.
40 // If it is set, it tries to wait until there is a transition
41 // to 0, and will return 0 if such a transition occurs. A -1
42 // is returned only after timing out. The maximum period
Kevin O'Connor4f5586c2009-02-16 10:14:10 -050043 // that this bit should be set is constrained to (1984+244)
Kevin O'Connor11cc6622010-03-13 23:04:41 -050044 // useconds, but we wait for longer just to be sure.
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050045
Kevin O'Connorf3587592009-02-15 13:02:56 -050046 if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050047 return 0;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040048 u32 end = timer_calc(15);
Kevin O'Connor11cc6622010-03-13 23:04:41 -050049 for (;;) {
Kevin O'Connorf3587592009-02-15 13:02:56 -050050 if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050051 return 0;
Kevin O'Connor018bdd72013-07-20 18:22:57 -040052 if (timer_check(end))
Kevin O'Connor11cc6622010-03-13 23:04:41 -050053 // update-in-progress never transitioned to 0
54 return -1;
55 yield();
56 }
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050057}
58
Kevin O'Connor4b60c002008-02-25 22:29:55 -050059static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050060pit_setup(void)
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040061{
62 // timer0: binary count, 16bit count, mode 2
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050063 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE);
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040064 // maximum count of 0000H = 18.2Hz
65 outb(0x0, PORT_PIT_COUNTER0);
66 outb(0x0, PORT_PIT_COUNTER0);
67}
68
Kevin O'Connorf3587592009-02-15 13:02:56 -050069static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -050070rtc_setup(void)
Kevin O'Connorf3587592009-02-15 13:02:56 -050071{
Kevin O'Connor4f5586c2009-02-16 10:14:10 -050072 outb_cmos(0x26, CMOS_STATUS_A); // 32,768Khz src, 976.5625us updates
Kevin O'Connorf3587592009-02-15 13:02:56 -050073 u8 regB = inb_cmos(CMOS_STATUS_B);
74 outb_cmos((regB & RTC_B_DSE) | RTC_B_24HR, CMOS_STATUS_B);
75 inb_cmos(CMOS_STATUS_C);
76 inb_cmos(CMOS_STATUS_D);
77}
78
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040079static u32
80bcd2bin(u8 val)
81{
82 return (val & 0xf) + ((val >> 4) * 10);
83}
84
Kevin O'Connor0f6198a2013-02-07 23:41:53 -050085u8 Century VARLOW;
86
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040087void
Kevin O'Connorc6e8c072013-07-20 10:51:58 -040088clock_setup(void)
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040089{
Kevin O'Connor35192dd2008-06-08 19:18:33 -040090 dprintf(3, "init timer\n");
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040091 pit_setup();
92
Kevin O'Connord83c87b2013-01-21 01:14:12 -050093 rtc_setup();
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050094 rtc_updating();
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040095 u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040096 u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040097 u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
Kevin O'Connor69013372013-07-20 12:08:48 -040098 u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000);
Kevin O'Connorb7ab1782013-07-20 13:06:35 -040099 SET_BDA(timer_counter, ticks % TICKS_PER_DAY);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400100
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500101 // Setup Century storage
102 if (CONFIG_QEMU) {
103 Century = inb_cmos(CMOS_CENTURY);
104 } else {
105 // Infer current century from the year.
106 u8 year = inb_cmos(CMOS_RTC_YEAR);
107 if (year > 0x80)
108 Century = 0x19;
109 else
110 Century = 0x20;
111 }
112
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400113 enable_hwirq(0, FUNC16(entry_08));
114 enable_hwirq(8, FUNC16(entry_70));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400115}
116
Kevin O'Connor5be04902008-05-18 17:12:06 -0400117
118/****************************************************************
119 * Standard clock functions
120 ****************************************************************/
121
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500122// get current clock count
123static void
124handle_1a00(struct bregs *regs)
125{
Kevin O'Connor68c51392010-03-13 22:23:44 -0500126 yield();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500127 u32 ticks = GET_BDA(timer_counter);
128 regs->cx = ticks >> 16;
129 regs->dx = ticks;
130 regs->al = GET_BDA(timer_rollover);
131 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor6c781222008-03-09 12:19:23 -0400132 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500133}
134
135// Set Current Clock Count
136static void
137handle_1a01(struct bregs *regs)
138{
139 u32 ticks = (regs->cx << 16) | regs->dx;
140 SET_BDA(timer_counter, ticks);
141 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor15157a32008-12-13 11:10:37 -0500142 // XXX - should use set_code_success()?
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500143 regs->ah = 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400144 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500145}
146
147// Read CMOS Time
148static void
149handle_1a02(struct bregs *regs)
150{
151 if (rtc_updating()) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500152 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500153 return;
154 }
155
156 regs->dh = inb_cmos(CMOS_RTC_SECONDS);
157 regs->cl = inb_cmos(CMOS_RTC_MINUTES);
158 regs->ch = inb_cmos(CMOS_RTC_HOURS);
Kevin O'Connorf3587592009-02-15 13:02:56 -0500159 regs->dl = inb_cmos(CMOS_STATUS_B) & RTC_B_DSE;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500160 regs->ah = 0;
161 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400162 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500163}
164
165// Set CMOS Time
166static void
167handle_1a03(struct bregs *regs)
168{
169 // Using a debugger, I notice the following masking/setting
170 // of bits in Status Register B, by setting Reg B to
171 // a few values and getting its value after INT 1A was called.
172 //
173 // try#1 try#2 try#3
174 // before 1111 1101 0111 1101 0000 0000
175 // after 0110 0010 0110 0010 0000 0010
176 //
177 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
178 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
179 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500180 rtc_setup();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500181 // fall through as if an update were not in progress
182 }
183 outb_cmos(regs->dh, CMOS_RTC_SECONDS);
184 outb_cmos(regs->cl, CMOS_RTC_MINUTES);
185 outb_cmos(regs->ch, CMOS_RTC_HOURS);
186 // Set Daylight Savings time enabled bit to requested value
Kevin O'Connorf3587592009-02-15 13:02:56 -0500187 u8 val8 = ((inb_cmos(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE))
188 | RTC_B_24HR | (regs->dl & RTC_B_DSE));
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500189 outb_cmos(val8, CMOS_STATUS_B);
190 regs->ah = 0;
191 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400192 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500193}
194
195// Read CMOS Date
196static void
197handle_1a04(struct bregs *regs)
198{
199 regs->ah = 0;
200 if (rtc_updating()) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500201 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500202 return;
203 }
204 regs->cl = inb_cmos(CMOS_RTC_YEAR);
205 regs->dh = inb_cmos(CMOS_RTC_MONTH);
206 regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500207 regs->ch = GET_LOW(Century);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500208 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400209 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500210}
211
212// Set CMOS Date
213static void
214handle_1a05(struct bregs *regs)
215{
216 // Using a debugger, I notice the following masking/setting
217 // of bits in Status Register B, by setting Reg B to
218 // a few values and getting its value after INT 1A was called.
219 //
220 // try#1 try#2 try#3 try#4
221 // before 1111 1101 0111 1101 0000 0010 0000 0000
222 // after 0110 1101 0111 1101 0000 0010 0000 0000
223 //
224 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
225 // My assumption: RegB = (RegB & 01111111b)
226 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500227 rtc_setup();
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500228 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500229 return;
230 }
231 outb_cmos(regs->cl, CMOS_RTC_YEAR);
232 outb_cmos(regs->dh, CMOS_RTC_MONTH);
233 outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500234 SET_LOW(Century, regs->ch);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400235 // clear halt-clock bit
236 u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500237 outb_cmos(val8, CMOS_STATUS_B);
238 regs->ah = 0;
239 regs->al = val8; // AL = val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400240 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500241}
242
243// Set Alarm Time in CMOS
244static void
245handle_1a06(struct bregs *regs)
246{
247 // Using a debugger, I notice the following masking/setting
248 // of bits in Status Register B, by setting Reg B to
249 // a few values and getting its value after INT 1A was called.
250 //
251 // try#1 try#2 try#3
252 // before 1101 1111 0101 1111 0000 0000
253 // after 0110 1111 0111 1111 0010 0000
254 //
255 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
256 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
257 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
258 regs->ax = 0;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500259 if (val8 & RTC_B_AIE) {
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500260 // Alarm interrupt enabled already
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500261 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500262 return;
263 }
264 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500265 rtc_setup();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500266 // fall through as if an update were not in progress
267 }
268 outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
269 outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
270 outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500271 // enable Status Reg B alarm bit, clear halt clock bit
Kevin O'Connor5be04902008-05-18 17:12:06 -0400272 outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400273 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500274}
275
276// Turn off Alarm
277static void
278handle_1a07(struct bregs *regs)
279{
280 // Using a debugger, I notice the following masking/setting
281 // of bits in Status Register B, by setting Reg B to
282 // a few values and getting its value after INT 1A was called.
283 //
284 // try#1 try#2 try#3 try#4
285 // before 1111 1101 0111 1101 0010 0000 0010 0010
286 // after 0100 0101 0101 0101 0000 0000 0000 0010
287 //
288 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
289 // My assumption: RegB = (RegB & 01010111b)
290 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
291 // clear clock-halt bit, disable alarm bit
Kevin O'Connor5be04902008-05-18 17:12:06 -0400292 outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500293 regs->ah = 0;
294 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400295 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500296}
297
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500298// Unsupported
299static void
300handle_1aXX(struct bregs *regs)
301{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500302 set_unimplemented(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500303}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500304
305// INT 1Ah Time-of-day Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500306void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500307handle_1a(struct bregs *regs)
308{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400309 debug_enter(regs, DEBUG_HDL_1a);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500310 switch (regs->ah) {
311 case 0x00: handle_1a00(regs); break;
312 case 0x01: handle_1a01(regs); break;
313 case 0x02: handle_1a02(regs); break;
314 case 0x03: handle_1a03(regs); break;
315 case 0x04: handle_1a04(regs); break;
316 case 0x05: handle_1a05(regs); break;
317 case 0x06: handle_1a06(regs); break;
318 case 0x07: handle_1a07(regs); break;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500319 default: handle_1aXX(regs); break;
320 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500321}
322
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500323// INT 08h System Timer ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500324void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400325handle_08(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500326{
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400327 debug_isr(DEBUG_ISR_08);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500328
Kevin O'Connor3e641f22013-03-02 18:19:31 -0500329 // Update counter
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500330 u32 counter = GET_BDA(timer_counter);
331 counter++;
332 // compare to one days worth of timer ticks at 18.2 hz
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400333 if (counter >= TICKS_PER_DAY) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500334 // there has been a midnight rollover at this point
335 counter = 0;
336 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
337 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500338 SET_BDA(timer_counter, counter);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500339
Kevin O'Connor3e641f22013-03-02 18:19:31 -0500340 // Check for internal events.
341 floppy_tick();
Kevin O'Connor0e885762010-05-01 22:14:40 -0400342 usb_check_event();
Kevin O'Connor114592f2009-09-28 21:32:08 -0400343
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500344 // chain to user timer tick INT #0x1c
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400345 struct bregs br;
346 memset(&br, 0, sizeof(br));
347 br.flags = F_IF;
348 call16_int(0x1c, &br);
Kevin O'Connored128492008-03-11 11:14:59 -0400349
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400350 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500351}
352
Kevin O'Connor5be04902008-05-18 17:12:06 -0400353
354/****************************************************************
355 * Periodic timer
356 ****************************************************************/
357
Kevin O'Connor9d254d42012-05-13 12:18:36 -0400358int RTCusers VARLOW;
359
Kevin O'Connorad901592009-12-13 11:25:25 -0500360void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500361useRTC(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500362{
Kevin O'Connor9d254d42012-05-13 12:18:36 -0400363 int count = GET_LOW(RTCusers);
364 SET_LOW(RTCusers, count+1);
Kevin O'Connorad901592009-12-13 11:25:25 -0500365 if (count)
366 return;
367 // Turn on the Periodic Interrupt timer
368 u8 bRegister = inb_cmos(CMOS_STATUS_B);
369 outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
370}
371
372void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500373releaseRTC(void)
Kevin O'Connorad901592009-12-13 11:25:25 -0500374{
Kevin O'Connor9d254d42012-05-13 12:18:36 -0400375 int count = GET_LOW(RTCusers);
376 SET_LOW(RTCusers, count-1);
Kevin O'Connorad901592009-12-13 11:25:25 -0500377 if (count != 1)
378 return;
379 // Clear the Periodic Interrupt.
380 u8 bRegister = inb_cmos(CMOS_STATUS_B);
381 outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
382}
383
Kevin O'Connor5be04902008-05-18 17:12:06 -0400384static int
Kevin O'Connor72743f12008-05-24 23:04:09 -0400385set_usertimer(u32 usecs, u16 seg, u16 offset)
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500386{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400387 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
388 return -1;
389
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500390 // Interval not already set.
391 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400392 SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset));
Kevin O'Connor72743f12008-05-24 23:04:09 -0400393 SET_BDA(user_wait_timeout, usecs);
Kevin O'Connorad901592009-12-13 11:25:25 -0500394 useRTC();
Kevin O'Connor5be04902008-05-18 17:12:06 -0400395 return 0;
396}
397
398static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500399clear_usertimer(void)
Kevin O'Connor5be04902008-05-18 17:12:06 -0400400{
Kevin O'Connorad901592009-12-13 11:25:25 -0500401 if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING))
402 return;
Kevin O'Connor5be04902008-05-18 17:12:06 -0400403 // Turn off status byte.
404 SET_BDA(rtc_wait_flag, 0);
Kevin O'Connorad901592009-12-13 11:25:25 -0500405 releaseRTC();
Kevin O'Connor5be04902008-05-18 17:12:06 -0400406}
407
Kevin O'Connor5be04902008-05-18 17:12:06 -0400408#define RET_ECLOCKINUSE 0x83
409
Kevin O'Connord21c0892008-11-26 17:02:43 -0500410// Wait for CX:DX microseconds
Kevin O'Connor5be04902008-05-18 17:12:06 -0400411void
412handle_1586(struct bregs *regs)
413{
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500414 // Use the rtc to wait for the specified time.
415 u8 statusflag = 0;
416 u32 count = (regs->cx << 16) | regs->dx;
417 int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
418 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500419 set_code_invalid(regs, RET_ECLOCKINUSE);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500420 return;
421 }
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500422 while (!statusflag)
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400423 yield_toirq();
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500424 set_success(regs);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400425}
426
427// Set Interval requested.
428static void
429handle_158300(struct bregs *regs)
430{
431 int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
432 if (ret)
433 // Interval already set.
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500434 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400435 else
436 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500437}
438
439// Clear interval requested
440static void
441handle_158301(struct bregs *regs)
442{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400443 clear_usertimer();
444 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500445}
446
447static void
448handle_1583XX(struct bregs *regs)
449{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500450 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500451 regs->al--;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500452}
453
454void
455handle_1583(struct bregs *regs)
456{
457 switch (regs->al) {
458 case 0x00: handle_158300(regs); break;
459 case 0x01: handle_158301(regs); break;
460 default: handle_1583XX(regs); break;
461 }
462}
463
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400464#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024)
465
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500466// int70h: IRQ8 - CMOS RTC
Kevin O'Connor19786762008-03-05 21:09:59 -0500467void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400468handle_70(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500469{
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400470 debug_isr(DEBUG_ISR_70);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500471
472 // Check which modes are enabled and have occurred.
473 u8 registerB = inb_cmos(CMOS_STATUS_B);
474 u8 registerC = inb_cmos(CMOS_STATUS_C);
475
Kevin O'Connor5be04902008-05-18 17:12:06 -0400476 if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500477 goto done;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500478 if (registerC & RTC_B_AIE) {
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500479 // Handle Alarm Interrupt.
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400480 struct bregs br;
481 memset(&br, 0, sizeof(br));
482 br.flags = F_IF;
483 call16_int(0x4a, &br);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500484 }
Kevin O'Connorf3587592009-02-15 13:02:56 -0500485 if (!(registerC & RTC_B_PIE))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500486 goto done;
487
488 // Handle Periodic Interrupt.
489
Kevin O'Connorad901592009-12-13 11:25:25 -0500490 check_preempt();
491
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500492 if (!GET_BDA(rtc_wait_flag))
493 goto done;
494
495 // Wait Interval (Int 15, AH=83) active.
496 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400497 if (time < USEC_PER_RTC) {
Kevin O'Connor5be04902008-05-18 17:12:06 -0400498 // Done waiting - write to specified flag byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400499 struct segoff_s segoff = GET_BDA(user_wait_complete_flag);
500 u16 ptr_seg = segoff.seg;
501 u8 *ptr_far = (u8*)(segoff.offset+0);
502 u8 oldval = GET_FARVAR(ptr_seg, *ptr_far);
503 SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400504
505 clear_usertimer();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500506 } else {
507 // Continue waiting.
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400508 time -= USEC_PER_RTC;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500509 SET_BDA(user_wait_timeout, time);
510 }
511
512done:
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400513 pic_eoi2();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500514}