~ubuntu-branches/ubuntu/oneiric/seabios/oneiric

« back to all changes in this revision

Viewing changes to .pc/0028-Don-t-use-RTC-to-time-boot-menu-delay.patch/src/clock.c

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2010-10-22 11:04:31 UTC
  • Revision ID: james.westby@ubuntu.com-20101022110431-fnfj73ra6xkq623n
Tags: 0.6.0-0ubuntu2
Add all patches which were included in qemu-0.13.0-rc2 (per
commit on Jul 13, 2010).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// 16bit code to handle system clocks.
 
2
//
 
3
// Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
 
4
// Copyright (C) 2002  MandrakeSoft S.A.
 
5
//
 
6
// This file may be distributed under the terms of the GNU LGPLv3 license.
 
7
 
 
8
#include "biosvar.h" // SET_BDA
 
9
#include "util.h" // debug_enter
 
10
#include "disk.h" // floppy_tick
 
11
#include "cmos.h" // inb_cmos
 
12
#include "pic.h" // eoi_pic1
 
13
#include "bregs.h" // struct bregs
 
14
#include "biosvar.h" // GET_GLOBAL
 
15
#include "usb-hid.h" // usb_check_event
 
16
 
 
17
// RTC register flags
 
18
#define RTC_A_UIP 0x80
 
19
 
 
20
#define RTC_B_SET  0x80
 
21
#define RTC_B_PIE  0x40
 
22
#define RTC_B_AIE  0x20
 
23
#define RTC_B_UIE  0x10
 
24
#define RTC_B_BIN  0x04
 
25
#define RTC_B_24HR 0x02
 
26
#define RTC_B_DSE  0x01
 
27
 
 
28
 
 
29
// Bits for PORT_PS2_CTRLB
 
30
#define PPCB_T2GATE (1<<0)
 
31
#define PPCB_SPKR   (1<<1)
 
32
#define PPCB_T2OUT  (1<<5)
 
33
 
 
34
// Bits for PORT_PIT_MODE
 
35
#define PM_SEL_TIMER0   (0<<6)
 
36
#define PM_SEL_TIMER1   (1<<6)
 
37
#define PM_SEL_TIMER2   (2<<6)
 
38
#define PM_SEL_READBACK (3<<6)
 
39
#define PM_ACCESS_LATCH  (0<<4)
 
40
#define PM_ACCESS_LOBYTE (1<<4)
 
41
#define PM_ACCESS_HIBYTE (2<<4)
 
42
#define PM_ACCESS_WORD   (3<<4)
 
43
#define PM_MODE0 (0<<1)
 
44
#define PM_MODE1 (1<<1)
 
45
#define PM_MODE2 (2<<1)
 
46
#define PM_MODE3 (3<<1)
 
47
#define PM_MODE4 (4<<1)
 
48
#define PM_MODE5 (5<<1)
 
49
#define PM_CNT_BINARY (0<<0)
 
50
#define PM_CNT_BCD    (1<<0)
 
51
 
 
52
 
 
53
/****************************************************************
 
54
 * TSC timer
 
55
 ****************************************************************/
 
56
 
 
57
#define CALIBRATE_COUNT 0x800   // Approx 1.7ms
 
58
 
 
59
u32 cpu_khz VAR16VISIBLE;
 
60
 
 
61
static void
 
62
calibrate_tsc(void)
 
63
{
 
64
    // Setup "timer2"
 
65
    u8 orig = inb(PORT_PS2_CTRLB);
 
66
    outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB);
 
67
    /* binary, mode 0, LSB/MSB, Ch 2 */
 
68
    outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE);
 
69
    /* LSB of ticks */
 
70
    outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2);
 
71
    /* MSB of ticks */
 
72
    outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2);
 
73
 
 
74
    u64 start = rdtscll();
 
75
    while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0)
 
76
        ;
 
77
    u64 end = rdtscll();
 
78
 
 
79
    // Restore PORT_PS2_CTRLB
 
80
    outb(orig, PORT_PS2_CTRLB);
 
81
 
 
82
    // Store calibrated cpu khz.
 
83
    u64 diff = end - start;
 
84
    dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n"
 
85
            , (u32)start, (u32)end, (u32)diff);
 
86
    u32 hz = diff * PIT_TICK_RATE / CALIBRATE_COUNT;
 
87
    SET_GLOBAL(cpu_khz, hz / 1000);
 
88
 
 
89
    dprintf(1, "CPU Mhz=%u\n", hz / 1000000);
 
90
}
 
91
 
 
92
static void
 
93
tscdelay(u64 diff)
 
94
{
 
95
    u64 start = rdtscll();
 
96
    u64 end = start + diff;
 
97
    while (!check_tsc(end))
 
98
        cpu_relax();
 
99
}
 
100
 
 
101
static void
 
102
tscsleep(u64 diff)
 
103
{
 
104
    u64 start = rdtscll();
 
105
    u64 end = start + diff;
 
106
    while (!check_tsc(end))
 
107
        yield();
 
108
}
 
109
 
 
110
void ndelay(u32 count) {
 
111
    tscdelay(count * GET_GLOBAL(cpu_khz) / 1000000);
 
112
}
 
113
void udelay(u32 count) {
 
114
    tscdelay(count * GET_GLOBAL(cpu_khz) / 1000);
 
115
}
 
116
void mdelay(u32 count) {
 
117
    tscdelay(count * GET_GLOBAL(cpu_khz));
 
118
}
 
119
 
 
120
void nsleep(u32 count) {
 
121
    tscsleep(count * GET_GLOBAL(cpu_khz) / 1000000);
 
122
}
 
123
void usleep(u32 count) {
 
124
    tscsleep(count * GET_GLOBAL(cpu_khz) / 1000);
 
125
}
 
126
void msleep(u32 count) {
 
127
    tscsleep(count * GET_GLOBAL(cpu_khz));
 
128
}
 
129
 
 
130
// Return the TSC value that is 'msecs' time in the future.
 
131
u64
 
132
calc_future_tsc(u32 msecs)
 
133
{
 
134
    u32 khz = GET_GLOBAL(cpu_khz);
 
135
    return rdtscll() + ((u64)khz * msecs);
 
136
}
 
137
u64
 
138
calc_future_tsc_usec(u32 usecs)
 
139
{
 
140
    u32 khz = GET_GLOBAL(cpu_khz);
 
141
    return rdtscll() + ((u64)(khz/1000) * usecs);
 
142
}
 
143
 
 
144
 
 
145
/****************************************************************
 
146
 * Init
 
147
 ****************************************************************/
 
148
 
 
149
static int
 
150
rtc_updating(void)
 
151
{
 
152
    // This function checks to see if the update-in-progress bit
 
153
    // is set in CMOS Status Register A.  If not, it returns 0.
 
154
    // If it is set, it tries to wait until there is a transition
 
155
    // to 0, and will return 0 if such a transition occurs.  A -1
 
156
    // is returned only after timing out.  The maximum period
 
157
    // that this bit should be set is constrained to (1984+244)
 
158
    // useconds, but we wait for longer just to be sure.
 
159
 
 
160
    if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
 
161
        return 0;
 
162
    u64 end = calc_future_tsc(15);
 
163
    for (;;) {
 
164
        if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
 
165
            return 0;
 
166
        if (check_tsc(end))
 
167
            // update-in-progress never transitioned to 0
 
168
            return -1;
 
169
        yield();
 
170
    }
 
171
}
 
172
 
 
173
static void
 
174
pit_setup(void)
 
175
{
 
176
    // timer0: binary count, 16bit count, mode 2
 
177
    outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE);
 
178
    // maximum count of 0000H = 18.2Hz
 
179
    outb(0x0, PORT_PIT_COUNTER0);
 
180
    outb(0x0, PORT_PIT_COUNTER0);
 
181
}
 
182
 
 
183
static void
 
184
init_rtc(void)
 
185
{
 
186
    outb_cmos(0x26, CMOS_STATUS_A);    // 32,768Khz src, 976.5625us updates
 
187
    u8 regB = inb_cmos(CMOS_STATUS_B);
 
188
    outb_cmos((regB & RTC_B_DSE) | RTC_B_24HR, CMOS_STATUS_B);
 
189
    inb_cmos(CMOS_STATUS_C);
 
190
    inb_cmos(CMOS_STATUS_D);
 
191
}
 
192
 
 
193
static u32
 
194
bcd2bin(u8 val)
 
195
{
 
196
    return (val & 0xf) + ((val >> 4) * 10);
 
197
}
 
198
 
 
199
void
 
200
timer_setup(void)
 
201
{
 
202
    dprintf(3, "init timer\n");
 
203
    calibrate_tsc();
 
204
    pit_setup();
 
205
 
 
206
    init_rtc();
 
207
    rtc_updating();
 
208
    u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
 
209
    u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
 
210
    u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
 
211
    u32 ticks = (hours * 60 + minutes) * 60 + seconds;
 
212
    ticks = ((u64)ticks * PIT_TICK_RATE) / PIT_TICK_INTERVAL;
 
213
    SET_BDA(timer_counter, ticks);
 
214
    SET_BDA(timer_rollover, 0);
 
215
 
 
216
    enable_hwirq(0, entry_08);
 
217
    enable_hwirq(8, entry_70);
 
218
}
 
219
 
 
220
 
 
221
/****************************************************************
 
222
 * Standard clock functions
 
223
 ****************************************************************/
 
224
 
 
225
#define TICKS_PER_DAY (u32)((u64)60*60*24*PIT_TICK_RATE / PIT_TICK_INTERVAL)
 
226
 
 
227
// Calculate the timer value at 'count' number of full timer ticks in
 
228
// the future.
 
229
u32
 
230
calc_future_timer_ticks(u32 count)
 
231
{
 
232
    return (GET_BDA(timer_counter) + count + 1) % TICKS_PER_DAY;
 
233
}
 
234
// Return the timer value that is 'msecs' time in the future.
 
235
u32
 
236
calc_future_timer(u32 msecs)
 
237
{
 
238
    u32 kticks = DIV_ROUND_UP((u64)(msecs * PIT_TICK_RATE), PIT_TICK_INTERVAL);
 
239
    u32 ticks = DIV_ROUND_UP(kticks, 1000);
 
240
    return calc_future_timer_ticks(ticks);
 
241
}
 
242
// Check if the given timer value has passed.
 
243
int
 
244
check_timer(u32 end)
 
245
{
 
246
    return (((GET_BDA(timer_counter) + TICKS_PER_DAY - end) % TICKS_PER_DAY)
 
247
            < (TICKS_PER_DAY/2));
 
248
}
 
249
 
 
250
// get current clock count
 
251
static void
 
252
handle_1a00(struct bregs *regs)
 
253
{
 
254
    yield();
 
255
    u32 ticks = GET_BDA(timer_counter);
 
256
    regs->cx = ticks >> 16;
 
257
    regs->dx = ticks;
 
258
    regs->al = GET_BDA(timer_rollover);
 
259
    SET_BDA(timer_rollover, 0); // reset flag
 
260
    set_success(regs);
 
261
}
 
262
 
 
263
// Set Current Clock Count
 
264
static void
 
265
handle_1a01(struct bregs *regs)
 
266
{
 
267
    u32 ticks = (regs->cx << 16) | regs->dx;
 
268
    SET_BDA(timer_counter, ticks);
 
269
    SET_BDA(timer_rollover, 0); // reset flag
 
270
    // XXX - should use set_code_success()?
 
271
    regs->ah = 0;
 
272
    set_success(regs);
 
273
}
 
274
 
 
275
// Read CMOS Time
 
276
static void
 
277
handle_1a02(struct bregs *regs)
 
278
{
 
279
    if (rtc_updating()) {
 
280
        set_invalid(regs);
 
281
        return;
 
282
    }
 
283
 
 
284
    regs->dh = inb_cmos(CMOS_RTC_SECONDS);
 
285
    regs->cl = inb_cmos(CMOS_RTC_MINUTES);
 
286
    regs->ch = inb_cmos(CMOS_RTC_HOURS);
 
287
    regs->dl = inb_cmos(CMOS_STATUS_B) & RTC_B_DSE;
 
288
    regs->ah = 0;
 
289
    regs->al = regs->ch;
 
290
    set_success(regs);
 
291
}
 
292
 
 
293
// Set CMOS Time
 
294
static void
 
295
handle_1a03(struct bregs *regs)
 
296
{
 
297
    // Using a debugger, I notice the following masking/setting
 
298
    // of bits in Status Register B, by setting Reg B to
 
299
    // a few values and getting its value after INT 1A was called.
 
300
    //
 
301
    //        try#1       try#2       try#3
 
302
    // before 1111 1101   0111 1101   0000 0000
 
303
    // after  0110 0010   0110 0010   0000 0010
 
304
    //
 
305
    // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 
306
    // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
 
307
    if (rtc_updating()) {
 
308
        init_rtc();
 
309
        // fall through as if an update were not in progress
 
310
    }
 
311
    outb_cmos(regs->dh, CMOS_RTC_SECONDS);
 
312
    outb_cmos(regs->cl, CMOS_RTC_MINUTES);
 
313
    outb_cmos(regs->ch, CMOS_RTC_HOURS);
 
314
    // Set Daylight Savings time enabled bit to requested value
 
315
    u8 val8 = ((inb_cmos(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE))
 
316
               | RTC_B_24HR | (regs->dl & RTC_B_DSE));
 
317
    outb_cmos(val8, CMOS_STATUS_B);
 
318
    regs->ah = 0;
 
319
    regs->al = val8; // val last written to Reg B
 
320
    set_success(regs);
 
321
}
 
322
 
 
323
// Read CMOS Date
 
324
static void
 
325
handle_1a04(struct bregs *regs)
 
326
{
 
327
    regs->ah = 0;
 
328
    if (rtc_updating()) {
 
329
        set_invalid(regs);
 
330
        return;
 
331
    }
 
332
    regs->cl = inb_cmos(CMOS_RTC_YEAR);
 
333
    regs->dh = inb_cmos(CMOS_RTC_MONTH);
 
334
    regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
 
335
    if (CONFIG_COREBOOT) {
 
336
        if (regs->cl > 0x80)
 
337
            regs->ch = 0x19;
 
338
        else
 
339
            regs->ch = 0x20;
 
340
    } else {
 
341
        regs->ch = inb_cmos(CMOS_CENTURY);
 
342
    }
 
343
    regs->al = regs->ch;
 
344
    set_success(regs);
 
345
}
 
346
 
 
347
// Set CMOS Date
 
348
static void
 
349
handle_1a05(struct bregs *regs)
 
350
{
 
351
    // Using a debugger, I notice the following masking/setting
 
352
    // of bits in Status Register B, by setting Reg B to
 
353
    // a few values and getting its value after INT 1A was called.
 
354
    //
 
355
    //        try#1       try#2       try#3       try#4
 
356
    // before 1111 1101   0111 1101   0000 0010   0000 0000
 
357
    // after  0110 1101   0111 1101   0000 0010   0000 0000
 
358
    //
 
359
    // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 
360
    // My assumption: RegB = (RegB & 01111111b)
 
361
    if (rtc_updating()) {
 
362
        init_rtc();
 
363
        set_invalid(regs);
 
364
        return;
 
365
    }
 
366
    outb_cmos(regs->cl, CMOS_RTC_YEAR);
 
367
    outb_cmos(regs->dh, CMOS_RTC_MONTH);
 
368
    outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
 
369
    if (!CONFIG_COREBOOT)
 
370
        outb_cmos(regs->ch, CMOS_CENTURY);
 
371
    // clear halt-clock bit
 
372
    u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
 
373
    outb_cmos(val8, CMOS_STATUS_B);
 
374
    regs->ah = 0;
 
375
    regs->al = val8; // AL = val last written to Reg B
 
376
    set_success(regs);
 
377
}
 
378
 
 
379
// Set Alarm Time in CMOS
 
380
static void
 
381
handle_1a06(struct bregs *regs)
 
382
{
 
383
    // Using a debugger, I notice the following masking/setting
 
384
    // of bits in Status Register B, by setting Reg B to
 
385
    // a few values and getting its value after INT 1A was called.
 
386
    //
 
387
    //        try#1       try#2       try#3
 
388
    // before 1101 1111   0101 1111   0000 0000
 
389
    // after  0110 1111   0111 1111   0010 0000
 
390
    //
 
391
    // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 
392
    // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
 
393
    u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
 
394
    regs->ax = 0;
 
395
    if (val8 & RTC_B_AIE) {
 
396
        // Alarm interrupt enabled already
 
397
        set_invalid(regs);
 
398
        return;
 
399
    }
 
400
    if (rtc_updating()) {
 
401
        init_rtc();
 
402
        // fall through as if an update were not in progress
 
403
    }
 
404
    outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
 
405
    outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
 
406
    outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
 
407
    // enable Status Reg B alarm bit, clear halt clock bit
 
408
    outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
 
409
    set_success(regs);
 
410
}
 
411
 
 
412
// Turn off Alarm
 
413
static void
 
414
handle_1a07(struct bregs *regs)
 
415
{
 
416
    // Using a debugger, I notice the following masking/setting
 
417
    // of bits in Status Register B, by setting Reg B to
 
418
    // a few values and getting its value after INT 1A was called.
 
419
    //
 
420
    //        try#1       try#2       try#3       try#4
 
421
    // before 1111 1101   0111 1101   0010 0000   0010 0010
 
422
    // after  0100 0101   0101 0101   0000 0000   0000 0010
 
423
    //
 
424
    // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 
425
    // My assumption: RegB = (RegB & 01010111b)
 
426
    u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
 
427
    // clear clock-halt bit, disable alarm bit
 
428
    outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
 
429
    regs->ah = 0;
 
430
    regs->al = val8; // val last written to Reg B
 
431
    set_success(regs);
 
432
}
 
433
 
 
434
// Unsupported
 
435
static void
 
436
handle_1aXX(struct bregs *regs)
 
437
{
 
438
    set_unimplemented(regs);
 
439
}
 
440
 
 
441
// INT 1Ah Time-of-day Service Entry Point
 
442
void VISIBLE16
 
443
handle_1a(struct bregs *regs)
 
444
{
 
445
    debug_enter(regs, DEBUG_HDL_1a);
 
446
    switch (regs->ah) {
 
447
    case 0x00: handle_1a00(regs); break;
 
448
    case 0x01: handle_1a01(regs); break;
 
449
    case 0x02: handle_1a02(regs); break;
 
450
    case 0x03: handle_1a03(regs); break;
 
451
    case 0x04: handle_1a04(regs); break;
 
452
    case 0x05: handle_1a05(regs); break;
 
453
    case 0x06: handle_1a06(regs); break;
 
454
    case 0x07: handle_1a07(regs); break;
 
455
    case 0xb1: handle_1ab1(regs); break;
 
456
    default:   handle_1aXX(regs); break;
 
457
    }
 
458
}
 
459
 
 
460
// INT 08h System Timer ISR Entry Point
 
461
void VISIBLE16
 
462
handle_08(void)
 
463
{
 
464
    debug_isr(DEBUG_ISR_08);
 
465
 
 
466
    floppy_tick();
 
467
 
 
468
    u32 counter = GET_BDA(timer_counter);
 
469
    counter++;
 
470
    // compare to one days worth of timer ticks at 18.2 hz
 
471
    if (counter >= TICKS_PER_DAY) {
 
472
        // there has been a midnight rollover at this point
 
473
        counter = 0;
 
474
        SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
 
475
    }
 
476
 
 
477
    SET_BDA(timer_counter, counter);
 
478
 
 
479
    usb_check_event();
 
480
 
 
481
    // chain to user timer tick INT #0x1c
 
482
    u32 eax=0, flags;
 
483
    call16_simpint(0x1c, &eax, &flags);
 
484
 
 
485
    eoi_pic1();
 
486
}
 
487
 
 
488
 
 
489
/****************************************************************
 
490
 * Periodic timer
 
491
 ****************************************************************/
 
492
 
 
493
void
 
494
useRTC(void)
 
495
{
 
496
    u16 ebda_seg = get_ebda_seg();
 
497
    int count = GET_EBDA2(ebda_seg, RTCusers);
 
498
    SET_EBDA2(ebda_seg, RTCusers, count+1);
 
499
    if (count)
 
500
        return;
 
501
    // Turn on the Periodic Interrupt timer
 
502
    u8 bRegister = inb_cmos(CMOS_STATUS_B);
 
503
    outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
 
504
}
 
505
 
 
506
void
 
507
releaseRTC(void)
 
508
{
 
509
    u16 ebda_seg = get_ebda_seg();
 
510
    int count = GET_EBDA2(ebda_seg, RTCusers);
 
511
    SET_EBDA2(ebda_seg, RTCusers, count-1);
 
512
    if (count != 1)
 
513
        return;
 
514
    // Clear the Periodic Interrupt.
 
515
    u8 bRegister = inb_cmos(CMOS_STATUS_B);
 
516
    outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
 
517
}
 
518
 
 
519
static int
 
520
set_usertimer(u32 usecs, u16 seg, u16 offset)
 
521
{
 
522
    if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
 
523
        return -1;
 
524
 
 
525
    // Interval not already set.
 
526
    SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING);  // Set status byte.
 
527
    SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset));
 
528
    SET_BDA(user_wait_timeout, usecs);
 
529
    useRTC();
 
530
    return 0;
 
531
}
 
532
 
 
533
static void
 
534
clear_usertimer(void)
 
535
{
 
536
    if (!(GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING))
 
537
        return;
 
538
    // Turn off status byte.
 
539
    SET_BDA(rtc_wait_flag, 0);
 
540
    releaseRTC();
 
541
}
 
542
 
 
543
#define RET_ECLOCKINUSE  0x83
 
544
 
 
545
// Wait for CX:DX microseconds
 
546
void
 
547
handle_1586(struct bregs *regs)
 
548
{
 
549
    // Use the rtc to wait for the specified time.
 
550
    u8 statusflag = 0;
 
551
    u32 count = (regs->cx << 16) | regs->dx;
 
552
    int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
 
553
    if (ret) {
 
554
        set_code_invalid(regs, RET_ECLOCKINUSE);
 
555
        return;
 
556
    }
 
557
    while (!statusflag)
 
558
        wait_irq();
 
559
    set_success(regs);
 
560
}
 
561
 
 
562
// Set Interval requested.
 
563
static void
 
564
handle_158300(struct bregs *regs)
 
565
{
 
566
    int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
 
567
    if (ret)
 
568
        // Interval already set.
 
569
        set_code_invalid(regs, RET_EUNSUPPORTED);
 
570
    else
 
571
        set_success(regs);
 
572
}
 
573
 
 
574
// Clear interval requested
 
575
static void
 
576
handle_158301(struct bregs *regs)
 
577
{
 
578
    clear_usertimer();
 
579
    set_success(regs);
 
580
}
 
581
 
 
582
static void
 
583
handle_1583XX(struct bregs *regs)
 
584
{
 
585
    set_code_unimplemented(regs, RET_EUNSUPPORTED);
 
586
    regs->al--;
 
587
}
 
588
 
 
589
void
 
590
handle_1583(struct bregs *regs)
 
591
{
 
592
    switch (regs->al) {
 
593
    case 0x00: handle_158300(regs); break;
 
594
    case 0x01: handle_158301(regs); break;
 
595
    default:   handle_1583XX(regs); break;
 
596
    }
 
597
}
 
598
 
 
599
#define USEC_PER_RTC DIV_ROUND_CLOSEST(1000000, 1024)
 
600
 
 
601
// int70h: IRQ8 - CMOS RTC
 
602
void VISIBLE16
 
603
handle_70(void)
 
604
{
 
605
    debug_isr(DEBUG_ISR_70);
 
606
 
 
607
    // Check which modes are enabled and have occurred.
 
608
    u8 registerB = inb_cmos(CMOS_STATUS_B);
 
609
    u8 registerC = inb_cmos(CMOS_STATUS_C);
 
610
 
 
611
    if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
 
612
        goto done;
 
613
    if (registerC & RTC_B_AIE) {
 
614
        // Handle Alarm Interrupt.
 
615
        u32 eax=0, flags;
 
616
        call16_simpint(0x4a, &eax, &flags);
 
617
    }
 
618
    if (!(registerC & RTC_B_PIE))
 
619
        goto done;
 
620
 
 
621
    // Handle Periodic Interrupt.
 
622
 
 
623
    check_preempt();
 
624
 
 
625
    if (!GET_BDA(rtc_wait_flag))
 
626
        goto done;
 
627
 
 
628
    // Wait Interval (Int 15, AH=83) active.
 
629
    u32 time = GET_BDA(user_wait_timeout);  // Time left in microseconds.
 
630
    if (time < USEC_PER_RTC) {
 
631
        // Done waiting - write to specified flag byte.
 
632
        struct segoff_s segoff = GET_BDA(user_wait_complete_flag);
 
633
        u16 ptr_seg = segoff.seg;
 
634
        u8 *ptr_far = (u8*)(segoff.offset+0);
 
635
        u8 oldval = GET_FARVAR(ptr_seg, *ptr_far);
 
636
        SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80);
 
637
 
 
638
        clear_usertimer();
 
639
    } else {
 
640
        // Continue waiting.
 
641
        time -= USEC_PER_RTC;
 
642
        SET_BDA(user_wait_timeout, time);
 
643
    }
 
644
 
 
645
done:
 
646
    eoi_pic2();
 
647
}