blob: ce39233976aefdae21da90bab8c5c1f57cfa87d9 [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'Connor2d2fa312013-09-14 21:55:26 -04009#include "bregs.h" // struct bregs
Kevin O'Connor5d369d82013-09-02 20:48:46 -040010#include "hw/pic.h" // pic_eoi1
Kevin O'Connorf318c072014-11-12 12:15:34 -050011#include "hw/ps2port.h" // ps2_check_event
Kevin O'Connor8b7861c2013-09-15 02:29:06 -040012#include "hw/rtc.h" // rtc_read
Kevin O'Connor5d369d82013-09-02 20:48:46 -040013#include "hw/usb-hid.h" // usb_check_event
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "output.h" // debug_enter
Kevin O'Connor3df600b2013-09-14 19:28:55 -040015#include "stacks.h" // yield
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040016#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040017#include "util.h" // clock_setup
Kevin O'Connor4b60c002008-02-25 22:29:55 -050018
Kevin O'Connor5be04902008-05-18 17:12:06 -040019
Kevin O'Connor5be04902008-05-18 17:12:06 -040020/****************************************************************
21 * Init
22 ****************************************************************/
23
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040024static u32
25bcd2bin(u8 val)
26{
27 return (val & 0xf) + ((val >> 4) * 10);
28}
29
Kevin O'Connor0f6198a2013-02-07 23:41:53 -050030u8 Century VARLOW;
31
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040032void
Kevin O'Connorc6e8c072013-07-20 10:51:58 -040033clock_setup(void)
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040034{
Kevin O'Connor35192dd2008-06-08 19:18:33 -040035 dprintf(3, "init timer\n");
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040036 pit_setup();
37
Kevin O'Connord83c87b2013-01-21 01:14:12 -050038 rtc_setup();
Kevin O'Connor4e6c9702008-12-13 10:45:50 -050039 rtc_updating();
Kevin O'Connor8b7861c2013-09-15 02:29:06 -040040 u32 seconds = bcd2bin(rtc_read(CMOS_RTC_SECONDS));
41 u32 minutes = bcd2bin(rtc_read(CMOS_RTC_MINUTES));
42 u32 hours = bcd2bin(rtc_read(CMOS_RTC_HOURS));
Kevin O'Connor69013372013-07-20 12:08:48 -040043 u32 ticks = ticks_from_ms(((hours * 60 + minutes) * 60 + seconds) * 1000);
Kevin O'Connorb7ab1782013-07-20 13:06:35 -040044 SET_BDA(timer_counter, ticks % TICKS_PER_DAY);
Kevin O'Connorf54c1502008-06-14 15:56:16 -040045
Kevin O'Connor0f6198a2013-02-07 23:41:53 -050046 // Setup Century storage
47 if (CONFIG_QEMU) {
Kevin O'Connor8b7861c2013-09-15 02:29:06 -040048 Century = rtc_read(CMOS_CENTURY);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -050049 } else {
50 // Infer current century from the year.
Kevin O'Connor8b7861c2013-09-15 02:29:06 -040051 u8 year = rtc_read(CMOS_RTC_YEAR);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -050052 if (year > 0x80)
53 Century = 0x19;
54 else
55 Century = 0x20;
56 }
57
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -040058 enable_hwirq(0, FUNC16(entry_08));
Kevin O'Connorbc46ebe2015-08-13 11:43:27 -040059 if (CONFIG_RTC_TIMER)
60 enable_hwirq(8, FUNC16(entry_70));
Kevin O'Connore6eb3f52008-04-13 17:37:41 -040061}
62
Kevin O'Connor5be04902008-05-18 17:12:06 -040063
64/****************************************************************
65 * Standard clock functions
66 ****************************************************************/
67
Kevin O'Connor4b60c002008-02-25 22:29:55 -050068// get current clock count
69static void
70handle_1a00(struct bregs *regs)
71{
Kevin O'Connor68c51392010-03-13 22:23:44 -050072 yield();
Kevin O'Connor4b60c002008-02-25 22:29:55 -050073 u32 ticks = GET_BDA(timer_counter);
74 regs->cx = ticks >> 16;
75 regs->dx = ticks;
76 regs->al = GET_BDA(timer_rollover);
77 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor6c781222008-03-09 12:19:23 -040078 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -050079}
80
81// Set Current Clock Count
82static void
83handle_1a01(struct bregs *regs)
84{
85 u32 ticks = (regs->cx << 16) | regs->dx;
86 SET_BDA(timer_counter, ticks);
87 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor15157a32008-12-13 11:10:37 -050088 // XXX - should use set_code_success()?
Kevin O'Connor4b60c002008-02-25 22:29:55 -050089 regs->ah = 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -040090 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -050091}
92
93// Read CMOS Time
94static void
95handle_1a02(struct bregs *regs)
96{
97 if (rtc_updating()) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -050098 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -050099 return;
100 }
101
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400102 regs->dh = rtc_read(CMOS_RTC_SECONDS);
103 regs->cl = rtc_read(CMOS_RTC_MINUTES);
104 regs->ch = rtc_read(CMOS_RTC_HOURS);
105 regs->dl = rtc_read(CMOS_STATUS_B) & RTC_B_DSE;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500106 regs->ah = 0;
107 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400108 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500109}
110
111// Set CMOS Time
112static void
113handle_1a03(struct bregs *regs)
114{
115 // Using a debugger, I notice the following masking/setting
116 // of bits in Status Register B, by setting Reg B to
117 // a few values and getting its value after INT 1A was called.
118 //
119 // try#1 try#2 try#3
120 // before 1111 1101 0111 1101 0000 0000
121 // after 0110 0010 0110 0010 0000 0010
122 //
123 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
124 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
125 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500126 rtc_setup();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500127 // fall through as if an update were not in progress
128 }
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400129 rtc_write(CMOS_RTC_SECONDS, regs->dh);
130 rtc_write(CMOS_RTC_MINUTES, regs->cl);
131 rtc_write(CMOS_RTC_HOURS, regs->ch);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500132 // Set Daylight Savings time enabled bit to requested value
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400133 u8 val8 = ((rtc_read(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE))
Kevin O'Connorf3587592009-02-15 13:02:56 -0500134 | RTC_B_24HR | (regs->dl & RTC_B_DSE));
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400135 rtc_write(CMOS_STATUS_B, val8);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500136 regs->ah = 0;
137 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400138 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500139}
140
141// Read CMOS Date
142static void
143handle_1a04(struct bregs *regs)
144{
145 regs->ah = 0;
146 if (rtc_updating()) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500147 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500148 return;
149 }
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400150 regs->cl = rtc_read(CMOS_RTC_YEAR);
151 regs->dh = rtc_read(CMOS_RTC_MONTH);
152 regs->dl = rtc_read(CMOS_RTC_DAY_MONTH);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500153 regs->ch = GET_LOW(Century);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500154 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400155 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500156}
157
158// Set CMOS Date
159static void
160handle_1a05(struct bregs *regs)
161{
162 // Using a debugger, I notice the following masking/setting
163 // of bits in Status Register B, by setting Reg B to
164 // a few values and getting its value after INT 1A was called.
165 //
166 // try#1 try#2 try#3 try#4
167 // before 1111 1101 0111 1101 0000 0010 0000 0000
168 // after 0110 1101 0111 1101 0000 0010 0000 0000
169 //
170 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
171 // My assumption: RegB = (RegB & 01111111b)
172 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500173 rtc_setup();
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500174 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500175 return;
176 }
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400177 rtc_write(CMOS_RTC_YEAR, regs->cl);
178 rtc_write(CMOS_RTC_MONTH, regs->dh);
179 rtc_write(CMOS_RTC_DAY_MONTH, regs->dl);
Kevin O'Connor0f6198a2013-02-07 23:41:53 -0500180 SET_LOW(Century, regs->ch);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400181 // clear halt-clock bit
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400182 u8 val8 = rtc_read(CMOS_STATUS_B) & ~RTC_B_SET;
183 rtc_write(CMOS_STATUS_B, val8);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500184 regs->ah = 0;
185 regs->al = val8; // AL = val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400186 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500187}
188
189// Set Alarm Time in CMOS
190static void
191handle_1a06(struct bregs *regs)
192{
193 // Using a debugger, I notice the following masking/setting
194 // of bits in Status Register B, by setting Reg B to
195 // a few values and getting its value after INT 1A was called.
196 //
197 // try#1 try#2 try#3
198 // before 1101 1111 0101 1111 0000 0000
199 // after 0110 1111 0111 1111 0010 0000
200 //
201 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
202 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400203 u8 val8 = rtc_read(CMOS_STATUS_B); // Get Status Reg B
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500204 regs->ax = 0;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500205 if (val8 & RTC_B_AIE) {
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500206 // Alarm interrupt enabled already
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500207 set_invalid(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500208 return;
209 }
210 if (rtc_updating()) {
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500211 rtc_setup();
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500212 // fall through as if an update were not in progress
213 }
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400214 rtc_write(CMOS_RTC_SECONDS_ALARM, regs->dh);
215 rtc_write(CMOS_RTC_MINUTES_ALARM, regs->cl);
216 rtc_write(CMOS_RTC_HOURS_ALARM, regs->ch);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500217 // enable Status Reg B alarm bit, clear halt clock bit
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400218 rtc_write(CMOS_STATUS_B, (val8 & ~RTC_B_SET) | RTC_B_AIE);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400219 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500220}
221
222// Turn off Alarm
223static void
224handle_1a07(struct bregs *regs)
225{
226 // Using a debugger, I notice the following masking/setting
227 // of bits in Status Register B, by setting Reg B to
228 // a few values and getting its value after INT 1A was called.
229 //
230 // try#1 try#2 try#3 try#4
231 // before 1111 1101 0111 1101 0010 0000 0010 0010
232 // after 0100 0101 0101 0101 0000 0000 0000 0010
233 //
234 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
235 // My assumption: RegB = (RegB & 01010111b)
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400236 u8 val8 = rtc_read(CMOS_STATUS_B); // Get Status Reg B
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500237 // clear clock-halt bit, disable alarm bit
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400238 rtc_write(CMOS_STATUS_B, val8 & ~(RTC_B_SET|RTC_B_AIE));
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500239 regs->ah = 0;
240 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400241 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500242}
243
Stefan Berger5aa2a752015-03-23 14:22:17 -0400244static void
245handle_1abb(struct bregs *regs)
246{
247 if (!CONFIG_TCGBIOS)
248 return;
249
250 dprintf(DEBUG_tcg, "16: Calling tpm_interrupt_handler\n");
Kevin O'Connorb4cca862015-10-09 11:53:02 -0400251 call32(tpm_interrupt_handler32, MAKE_FLATPTR(GET_SEG(SS), regs), 0);
Stefan Berger5aa2a752015-03-23 14:22:17 -0400252}
253
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500254// Unsupported
255static void
256handle_1aXX(struct bregs *regs)
257{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500258 set_unimplemented(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500259}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500260
261// INT 1Ah Time-of-day Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500262void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500263handle_1a(struct bregs *regs)
264{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400265 debug_enter(regs, DEBUG_HDL_1a);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500266 switch (regs->ah) {
267 case 0x00: handle_1a00(regs); break;
268 case 0x01: handle_1a01(regs); break;
269 case 0x02: handle_1a02(regs); break;
270 case 0x03: handle_1a03(regs); break;
271 case 0x04: handle_1a04(regs); break;
272 case 0x05: handle_1a05(regs); break;
273 case 0x06: handle_1a06(regs); break;
274 case 0x07: handle_1a07(regs); break;
Stefan Berger5aa2a752015-03-23 14:22:17 -0400275 case 0xbb: handle_1abb(regs); break;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500276 default: handle_1aXX(regs); break;
277 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500278}
279
Kevin O'Connorbd5f6c72015-08-10 16:14:48 -0400280// Update main tick counter
281static void
282clock_update(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500283{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500284 u32 counter = GET_BDA(timer_counter);
285 counter++;
286 // compare to one days worth of timer ticks at 18.2 hz
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400287 if (counter >= TICKS_PER_DAY) {
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500288 // there has been a midnight rollover at this point
289 counter = 0;
290 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
291 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500292 SET_BDA(timer_counter, counter);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500293
Kevin O'Connor3e641f22013-03-02 18:19:31 -0500294 // Check for internal events.
295 floppy_tick();
Kevin O'Connor0e885762010-05-01 22:14:40 -0400296 usb_check_event();
Kevin O'Connorf318c072014-11-12 12:15:34 -0500297 ps2_check_event();
Gerd Hoffmannd6728f32017-09-18 10:47:23 +0200298 sercon_check_event();
Kevin O'Connorbd5f6c72015-08-10 16:14:48 -0400299}
300
301// INT 08h System Timer ISR Entry Point
302void VISIBLE16
303handle_08(void)
304{
305 debug_isr(DEBUG_ISR_08);
306 clock_update();
Kevin O'Connor114592f2009-09-28 21:32:08 -0400307
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500308 // chain to user timer tick INT #0x1c
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400309 struct bregs br;
310 memset(&br, 0, sizeof(br));
311 br.flags = F_IF;
312 call16_int(0x1c, &br);
Kevin O'Connored128492008-03-11 11:14:59 -0400313
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400314 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500315}
316
Kevin O'Connorbd5f6c72015-08-10 16:14:48 -0400317u32 last_timer_check VARLOW;
318
319// Simulate timer irq on machines without hardware irqs
320void
321clock_poll_irq(void)
322{
323 if (CONFIG_HARDWARE_IRQ)
324 return;
325 if (!timer_check(GET_LOW(last_timer_check)))
326 return;
327 SET_LOW(last_timer_check, timer_calc(ticks_to_ms(1)));
328 clock_update();
329}
330
Kevin O'Connor5be04902008-05-18 17:12:06 -0400331
332/****************************************************************
Kevin O'Connor9fcd1992013-09-15 01:50:00 -0400333 * IRQ based timer
334 ****************************************************************/
335
336// Calculate the timer value at 'count' number of full timer ticks in
337// the future.
338u32
339irqtimer_calc_ticks(u32 count)
340{
341 return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY;
342}
343
344// Return the timer value that is 'msecs' time in the future.
345u32
346irqtimer_calc(u32 msecs)
347{
348 if (!msecs)
349 return GET_BDA(timer_counter);
350 return irqtimer_calc_ticks(ticks_from_ms(msecs));
351}
352
353// Check if the given timer value has passed.
354int
355irqtimer_check(u32 end)
356{
357 return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY)
358 < (TICKS_PER_DAY/2));
359}
360
361
362/****************************************************************
Kevin O'Connor5be04902008-05-18 17:12:06 -0400363 * Periodic timer
364 ****************************************************************/
365
366static int
Kevin O'Connor72743f12008-05-24 23:04:09 -0400367set_usertimer(u32 usecs, u16 seg, u16 offset)
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500368{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400369 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
370 return -1;
371
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500372 // Interval not already set.
373 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400374 SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset));
Kevin O'Connor72743f12008-05-24 23:04:09 -0400375 SET_BDA(user_wait_timeout, usecs);
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400376 rtc_use();
Kevin O'Connor5be04902008-05-18 17:12:06 -0400377 return 0;
378}
379
380static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500381clear_usertimer(void)
Kevin O'Connor5be04902008-05-18 17:12:06 -0400382{
Kevin O'Connorad901592009-12-13 11:25:25 -0500383 if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING))
384 return;
Kevin O'Connor5be04902008-05-18 17:12:06 -0400385 // Turn off status byte.
386 SET_BDA(rtc_wait_flag, 0);
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400387 rtc_release();
Kevin O'Connor5be04902008-05-18 17:12:06 -0400388}
389
Kevin O'Connor5be04902008-05-18 17:12:06 -0400390#define RET_ECLOCKINUSE 0x83
391
Kevin O'Connord21c0892008-11-26 17:02:43 -0500392// Wait for CX:DX microseconds
Kevin O'Connor5be04902008-05-18 17:12:06 -0400393void
394handle_1586(struct bregs *regs)
395{
Kevin O'Connorbc46ebe2015-08-13 11:43:27 -0400396 if (!CONFIG_RTC_TIMER) {
397 set_code_unimplemented(regs, RET_EUNSUPPORTED);
398 return;
399 }
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500400 // Use the rtc to wait for the specified time.
401 u8 statusflag = 0;
402 u32 count = (regs->cx << 16) | regs->dx;
403 int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
404 if (ret) {
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500405 set_code_invalid(regs, RET_ECLOCKINUSE);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500406 return;
407 }
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500408 while (!statusflag)
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400409 yield_toirq();
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500410 set_success(regs);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400411}
412
413// Set Interval requested.
414static void
415handle_158300(struct bregs *regs)
416{
417 int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
418 if (ret)
419 // Interval already set.
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500420 set_code_invalid(regs, RET_EUNSUPPORTED);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400421 else
422 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500423}
424
425// Clear interval requested
426static void
427handle_158301(struct bregs *regs)
428{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400429 clear_usertimer();
430 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500431}
432
433static void
434handle_1583XX(struct bregs *regs)
435{
Kevin O'Connordfefeb52009-12-13 13:04:17 -0500436 set_code_unimplemented(regs, RET_EUNSUPPORTED);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500437 regs->al--;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500438}
439
440void
441handle_1583(struct bregs *regs)
442{
Kevin O'Connorbc46ebe2015-08-13 11:43:27 -0400443 if (!CONFIG_RTC_TIMER) {
444 handle_1583XX(regs);
445 return;
446 }
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500447 switch (regs->al) {
448 case 0x00: handle_158300(regs); break;
449 case 0x01: handle_158301(regs); break;
450 default: handle_1583XX(regs); break;
451 }
452}
453
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400454#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024)
455
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500456// int70h: IRQ8 - CMOS RTC
Kevin O'Connor19786762008-03-05 21:09:59 -0500457void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400458handle_70(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500459{
Kevin O'Connorbc46ebe2015-08-13 11:43:27 -0400460 if (!CONFIG_RTC_TIMER)
461 return;
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400462 debug_isr(DEBUG_ISR_70);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500463
464 // Check which modes are enabled and have occurred.
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400465 u8 registerB = rtc_read(CMOS_STATUS_B);
466 u8 registerC = rtc_read(CMOS_STATUS_C);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500467
Kevin O'Connor5be04902008-05-18 17:12:06 -0400468 if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500469 goto done;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500470 if (registerC & RTC_B_AIE) {
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500471 // Handle Alarm Interrupt.
Kevin O'Connorecdc6552012-05-28 14:25:15 -0400472 struct bregs br;
473 memset(&br, 0, sizeof(br));
474 br.flags = F_IF;
475 call16_int(0x4a, &br);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500476 }
Kevin O'Connorf3587592009-02-15 13:02:56 -0500477 if (!(registerC & RTC_B_PIE))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500478 goto done;
479
480 // Handle Periodic Interrupt.
481
Kevin O'Connorad901592009-12-13 11:25:25 -0500482 check_preempt();
483
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500484 if (!GET_BDA(rtc_wait_flag))
485 goto done;
486
487 // Wait Interval (Int 15, AH=83) active.
488 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400489 if (time < USEC_PER_RTC) {
Kevin O'Connor5be04902008-05-18 17:12:06 -0400490 // Done waiting - write to specified flag byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400491 struct segoff_s segoff = GET_BDA(user_wait_complete_flag);
Helge Deller70b2f072022-05-26 11:43:57 +0200492#if CONFIG_X86
Kevin O'Connor9f985422009-09-09 11:34:39 -0400493 u16 ptr_seg = segoff.seg;
Helge Deller70b2f072022-05-26 11:43:57 +0200494#endif
Kevin O'Connor9f985422009-09-09 11:34:39 -0400495 u8 *ptr_far = (u8*)(segoff.offset+0);
496 u8 oldval = GET_FARVAR(ptr_seg, *ptr_far);
497 SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400498
499 clear_usertimer();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500500 } else {
501 // Continue waiting.
Kevin O'Connor6aee52d2009-09-27 20:07:40 -0400502 time -= USEC_PER_RTC;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500503 SET_BDA(user_wait_timeout, time);
504 }
505
506done:
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400507 pic_eoi2();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500508}