SandboXP

Форк
0
/
rtc.js 
352 строки · 9.6 Кб
1
"use strict";
2

3
/** @const */ var CMOS_RTC_SECONDS = 0x00;
4
/** @const */ var CMOS_RTC_SECONDS_ALARM = 0x01;
5
/** @const */ var CMOS_RTC_MINUTES = 0x02;
6
/** @const */ var CMOS_RTC_MINUTES_ALARM = 0x03;
7
/** @const */ var CMOS_RTC_HOURS = 0x04;
8
/** @const */ var CMOS_RTC_HOURS_ALARM = 0x05;
9
/** @const */ var CMOS_RTC_DAY_WEEK = 0x06;
10
/** @const */ var CMOS_RTC_DAY_MONTH = 0x07;
11
/** @const */ var CMOS_RTC_MONTH = 0x08;
12
/** @const */ var CMOS_RTC_YEAR = 0x09;
13
/** @const */ var CMOS_STATUS_A = 0x0a;
14
/** @const */ var CMOS_STATUS_B = 0x0b;
15
/** @const */ var CMOS_STATUS_C = 0x0c;
16
/** @const */ var CMOS_STATUS_D = 0x0d;
17
/** @const */ var CMOS_RESET_CODE = 0x0f;
18

19
/** @const */ var CMOS_FLOPPY_DRIVE_TYPE = 0x10;
20
/** @const */ var CMOS_DISK_DATA = 0x12;
21
/** @const */ var CMOS_EQUIPMENT_INFO = 0x14;
22
/** @const */ var CMOS_MEM_BASE_LOW = 0x15;
23
/** @const */ var CMOS_MEM_BASE_HIGH = 0x16;
24
/** @const */ var CMOS_MEM_OLD_EXT_LOW = 0x17;
25
/** @const */ var CMOS_MEM_OLD_EXT_HIGH = 0x18;
26
/** @const */ var CMOS_DISK_DRIVE1_TYPE = 0x19;
27
/** @const */ var CMOS_DISK_DRIVE2_TYPE = 0x1a;
28
/** @const */ var CMOS_DISK_DRIVE1_CYL = 0x1b;
29
/** @const */ var CMOS_DISK_DRIVE2_CYL = 0x24;
30
/** @const */ var CMOS_MEM_EXTMEM_LOW = 0x30;
31
/** @const */ var CMOS_MEM_EXTMEM_HIGH = 0x31;
32
/** @const */ var CMOS_CENTURY = 0x32;
33
/** @const */ var CMOS_MEM_EXTMEM2_LOW = 0x34;
34
/** @const */ var CMOS_MEM_EXTMEM2_HIGH = 0x35;
35
/** @const */ var CMOS_BIOS_BOOTFLAG1 = 0x38;
36
/** @const */ var CMOS_BIOS_DISKTRANSFLAG = 0x39;
37
/** @const */ var CMOS_BIOS_BOOTFLAG2 = 0x3d;
38
/** @const */ var CMOS_MEM_HIGHMEM_LOW = 0x5b;
39
/** @const */ var CMOS_MEM_HIGHMEM_MID = 0x5c;
40
/** @const */ var CMOS_MEM_HIGHMEM_HIGH = 0x5d;
41
/** @const */ var CMOS_BIOS_SMP_COUNT = 0x5f;
42

43

44
/**
45
 * RTC (real time clock) and CMOS
46
 * @constructor
47
 * @param {CPU} cpu
48
 */
49
function RTC(cpu)
50
{
51
    /** @const @type {CPU} */
52
    this.cpu = cpu;
53

54
    this.cmos_index = 0;
55
    this.cmos_data = new Uint8Array(128);
56

57
    // used for cmos entries
58
    this.rtc_time = Date.now();
59
    this.last_update = this.rtc_time;
60

61
    // used for periodic interrupt
62
    this.next_interrupt = 0;
63

64
    // next alarm interrupt
65
    this.next_interrupt_alarm = 0;
66

67
    this.periodic_interrupt = false;
68

69
    // corresponds to default value for cmos_a
70
    this.periodic_interrupt_time = 1000 / 1024;
71

72
    this.cmos_a = 0x26;
73
    this.cmos_b = 2;
74
    this.cmos_c = 0;
75

76
    this.nmi_disabled = 0;
77

78
    cpu.io.register_write(0x70, this, function(out_byte)
79
    {
80
        this.cmos_index = out_byte & 0x7F;
81
        this.nmi_disabled = out_byte >> 7;
82
    });
83

84
    cpu.io.register_write(0x71, this, this.cmos_port_write);
85
    cpu.io.register_read(0x71, this, this.cmos_port_read);
86
}
87

88
RTC.prototype.get_state = function()
89
{
90
    var state = [];
91

92
    state[0] = this.cmos_index;
93
    state[1] = this.cmos_data;
94
    state[2] = this.rtc_time;
95
    state[3] = this.last_update;
96
    state[4] = this.next_interrupt;
97
    state[5] = this.next_interrupt_alarm;
98
    state[6] = this.periodic_interrupt;
99
    state[7] = this.periodic_interrupt_time;
100
    state[8] = this.cmos_a;
101
    state[9] = this.cmos_b;
102
    state[10] = this.cmos_c;
103
    state[11] = this.nmi_disabled;
104

105
    return state;
106
};
107

108
RTC.prototype.set_state = function(state)
109
{
110
    this.cmos_index = state[0];
111
    this.cmos_data = state[1];
112
    this.rtc_time = state[2];
113
    this.last_update = state[3];
114
    this.next_interrupt = state[4];
115
    this.next_interrupt_alarm = state[5];
116
    this.periodic_interrupt = state[6];
117
    this.periodic_interrupt_time = state[7];
118
    this.cmos_a = state[8];
119
    this.cmos_b = state[9];
120
    this.cmos_c = state[10];
121
    this.nmi_disabled = state[11];
122
};
123

124
RTC.prototype.timer = function(time, legacy_mode)
125
{
126
    time = Date.now(); // XXX
127
    this.rtc_time += time - this.last_update;
128
    this.last_update = time;
129

130
    if(this.periodic_interrupt && this.next_interrupt < time)
131
    {
132
        this.cpu.device_raise_irq(8);
133
        this.cmos_c |= 1 << 6 | 1 << 7;
134

135
        this.next_interrupt += this.periodic_interrupt_time *
136
                Math.ceil((time - this.next_interrupt) / this.periodic_interrupt_time);
137
    }
138
    else if(this.next_interrupt_alarm && this.next_interrupt_alarm < time)
139
    {
140
        this.cpu.device_raise_irq(8);
141
        this.cmos_c |= 1 << 5 | 1 << 7;
142

143
        this.next_interrupt_alarm = 0;
144
    }
145

146
    let t = 100;
147

148
    if(this.periodic_interrupt && this.next_interrupt)
149
    {
150
        t = Math.min(t, Math.max(0, this.next_interrupt - time));
151
    }
152
    if(this.next_interrupt_alarm)
153
    {
154
        t = Math.min(t, Math.max(0, this.next_interrupt_alarm - time));
155
    }
156

157
    return t;
158
};
159

160
RTC.prototype.bcd_pack = function(n)
161
{
162
    var i = 0,
163
        result = 0,
164
        digit;
165

166
    while(n)
167
    {
168
        digit = n % 10;
169

170
        result |= digit << (4 * i);
171
        i++;
172
        n = (n - digit) / 10;
173
    }
174

175
    return result;
176
};
177

178
RTC.prototype.bcd_unpack = function(n)
179
{
180
    const low = n & 0xF;
181
    const high = n >> 4 & 0xF;
182

183
    dbg_assert(n < 0x100);
184
    dbg_assert(low < 10);
185
    dbg_assert(high < 10);
186

187
    return low + 10 * high;
188
};
189

190
RTC.prototype.encode_time = function(t)
191
{
192
    if(this.cmos_b & 4)
193
    {
194
        // binary mode
195
        return t;
196
    }
197
    else
198
    {
199
        return this.bcd_pack(t);
200
    }
201
};
202

203
RTC.prototype.decode_time = function(t)
204
{
205
    if(this.cmos_b & 4)
206
    {
207
        // binary mode
208
        return t;
209
    }
210
    else
211
    {
212
        return this.bcd_unpack(t);
213
    }
214
};
215

216
// TODO
217
// - interrupt on update
218
// - countdown
219
// - letting bios/os set values
220
// (none of these are used by seabios or the OSes we're
221
// currently testing)
222
RTC.prototype.cmos_port_read = function()
223
{
224
    var index = this.cmos_index;
225

226
    //this.cmos_index = 0xD;
227

228
    switch(index)
229
    {
230
        case CMOS_RTC_SECONDS:
231
            return this.encode_time(new Date(this.rtc_time).getUTCSeconds());
232
        case CMOS_RTC_MINUTES:
233
            return this.encode_time(new Date(this.rtc_time).getUTCMinutes());
234
        case CMOS_RTC_HOURS:
235
            // TODO: 12 hour mode
236
            return this.encode_time(new Date(this.rtc_time).getUTCHours());
237
        case CMOS_RTC_DAY_MONTH:
238
            return this.encode_time(new Date(this.rtc_time).getUTCDate());
239
        case CMOS_RTC_MONTH:
240
            return this.encode_time(new Date(this.rtc_time).getUTCMonth() + 1);
241
        case CMOS_RTC_YEAR:
242
            return this.encode_time(new Date(this.rtc_time).getUTCFullYear() % 100);
243

244
        case CMOS_STATUS_A:
245
            return this.cmos_a;
246
        case CMOS_STATUS_B:
247
            //dbg_log("cmos read from index " + h(index));
248
            return this.cmos_b;
249

250
        case CMOS_STATUS_C:
251
            // It is important to know that upon a IRQ 8, Status Register C
252
            // will contain a bitmask telling which interrupt happened.
253
            // What is important is that if register C is not read after an
254
            // IRQ 8, then the interrupt will not happen again.
255
            this.cpu.device_lower_irq(8);
256

257
            dbg_log("cmos reg C read", LOG_RTC);
258
            // Missing IRQF flag
259
            //return cmos_b & 0x70;
260
            var c = this.cmos_c;
261

262
            this.cmos_c &= ~0xF0;
263

264
            return c;
265

266
        case CMOS_STATUS_D:
267
            return 0;
268

269
        case CMOS_CENTURY:
270
            return this.encode_time(new Date(this.rtc_time).getUTCFullYear() / 100 | 0);
271

272
        default:
273
            dbg_log("cmos read from index " + h(index), LOG_RTC);
274
            return this.cmos_data[this.cmos_index];
275
    }
276
};
277

278
RTC.prototype.cmos_port_write = function(data_byte)
279
{
280
    switch(this.cmos_index)
281
    {
282
        case 0xA:
283
            this.cmos_a = data_byte & 0x7F;
284
            this.periodic_interrupt_time = 1000 / (32768 >> (this.cmos_a & 0xF) - 1);
285

286
            dbg_log("Periodic interrupt, a=" + h(this.cmos_a, 2) + " t=" + this.periodic_interrupt_time , LOG_RTC);
287
            break;
288
        case 0xB:
289
            this.cmos_b = data_byte;
290
            if(this.cmos_b & 0x40)
291
            {
292
                this.next_interrupt = Date.now();
293
            }
294

295
            if(this.cmos_b & 0x20)
296
            {
297
                const now = new Date();
298

299
                const seconds = this.decode_time(this.cmos_data[CMOS_RTC_SECONDS_ALARM]);
300
                const minutes = this.decode_time(this.cmos_data[CMOS_RTC_MINUTES_ALARM]);
301
                const hours = this.decode_time(this.cmos_data[CMOS_RTC_HOURS_ALARM]);
302

303
                const alarm_date = new Date(Date.UTC(
304
                    now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
305
                    hours, minutes, seconds
306
                ));
307

308
                const ms_from_now = alarm_date - now;
309
                dbg_log("RTC alarm scheduled for " + alarm_date +
310
                        " hh:mm:ss=" + hours + ":" + minutes + ":" + seconds +
311
                        " ms_from_now=" + ms_from_now, LOG_RTC);
312

313
                this.next_interrupt_alarm = +alarm_date;
314
            }
315

316
            if(this.cmos_b & 0x10) dbg_log("Unimplemented: updated interrupt", LOG_RTC);
317

318
            dbg_log("cmos b=" + h(this.cmos_b, 2), LOG_RTC);
319
            break;
320

321
        case CMOS_RTC_SECONDS_ALARM:
322
        case CMOS_RTC_MINUTES_ALARM:
323
        case CMOS_RTC_HOURS_ALARM:
324
            this.cmos_write(this.cmos_index, data_byte);
325
            break;
326

327
        default:
328
            dbg_log("cmos write index " + h(this.cmos_index) + ": " + h(data_byte), LOG_RTC);
329
    }
330

331
    this.periodic_interrupt = (this.cmos_b & 0x40) === 0x40 && (this.cmos_a & 0xF) > 0;
332
};
333

334
/**
335
 * @param {number} index
336
 */
337
RTC.prototype.cmos_read = function(index)
338
{
339
    dbg_assert(index < 128);
340
    return this.cmos_data[index];
341
};
342

343
/**
344
 * @param {number} index
345
 * @param {number} value
346
 */
347
RTC.prototype.cmos_write = function(index, value)
348
{
349
    dbg_log("cmos " + h(index) + " <- " + h(value), LOG_RTC);
350
    dbg_assert(index < 128);
351
    this.cmos_data[index] = value;
352
};
353

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.