SandboXP

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

3
/**
4
 * @const
5
 * In kHz
6
 */
7
var OSCILLATOR_FREQ = 1193.1816666; // 1.193182 MHz
8

9
/**
10
 * @constructor
11
 *
12
 * Programmable Interval Timer
13
 */
14
function PIT(cpu, bus)
15
{
16
    /** @const @type {CPU} */
17
    this.cpu = cpu;
18

19
    this.bus = bus;
20

21
    this.counter_start_time = new Float64Array(3);
22
    this.counter_start_value = new Uint16Array(3);
23

24
    this.counter_next_low = new Uint8Array(4);
25
    this.counter_enabled = new Uint8Array(4);
26
    this.counter_mode = new Uint8Array(4);
27
    this.counter_read_mode = new Uint8Array(4);
28

29
    // 2 = latch low, 1 = latch high, 0 = no latch
30
    this.counter_latch = new Uint8Array(4);
31
    this.counter_latch_value = new Uint16Array(3);
32

33
    this.counter_reload = new Uint16Array(3);
34

35
    // TODO:
36
    // - counter2 can be controlled by an input
37

38
    cpu.io.register_read(0x61, this, function()
39
    {
40
        var now = v86.microtick();
41

42
        var ref_toggle = (now * (1000 * 1000 / 15000)) & 1;
43
        var counter2_out = this.did_rollover(2, now);
44

45
        return ref_toggle << 4 | counter2_out << 5;
46
    });
47
    cpu.io.register_write(0x61, this, function(data)
48
    {
49
        if(data & 1)
50
        {
51
            this.bus.send("pcspeaker-enable");
52
        }
53
        else
54
        {
55
            this.bus.send("pcspeaker-disable");
56
        }
57
    });
58

59
    cpu.io.register_read(0x40, this, function() { return this.counter_read(0); });
60
    cpu.io.register_read(0x41, this, function() { return this.counter_read(1); });
61
    cpu.io.register_read(0x42, this, function() { return this.counter_read(2); });
62

63
    cpu.io.register_write(0x40, this, function(data) { this.counter_write(0, data); });
64
    cpu.io.register_write(0x41, this, function(data) { this.counter_write(1, data); });
65
    cpu.io.register_write(0x42, this, function(data) {
66
        this.counter_write(2, data);
67
        this.bus.send("pcspeaker-update", [this.counter_mode[2], this.counter_reload[2]]);
68
    });
69

70
    cpu.io.register_write(0x43, this, this.port43_write);
71
}
72

73
PIT.prototype.get_state = function()
74
{
75
    var state = [];
76

77
    state[0] = this.counter_next_low;
78
    state[1] = this.counter_enabled;
79
    state[2] = this.counter_mode;
80
    state[3] = this.counter_read_mode;
81
    state[4] = this.counter_latch;
82
    state[5] = this.counter_latch_value;
83
    state[6] = this.counter_reload;
84
    state[7] = this.counter_start_time;
85
    state[8] = this.counter_start_value;
86

87
    return state;
88
};
89

90
PIT.prototype.set_state = function(state)
91
{
92
    this.counter_next_low = state[0];
93
    this.counter_enabled = state[1];
94
    this.counter_mode = state[2];
95
    this.counter_read_mode = state[3];
96
    this.counter_latch = state[4];
97
    this.counter_latch_value = state[5];
98
    this.counter_reload = state[6];
99
    this.counter_start_time = state[7];
100
    this.counter_start_value = state[8];
101
};
102

103
PIT.prototype.timer = function(now, no_irq)
104
{
105
    var time_to_next_interrupt = 100;
106

107
    // counter 0 produces interrupts
108
    if(!no_irq)
109
    {
110
        if(this.counter_enabled[0] && this.did_rollover(0, now))
111
        {
112
            this.counter_start_value[0] = this.get_counter_value(0, now);
113
            this.counter_start_time[0] = now;
114

115
            dbg_log("pit interrupt. new value: " + this.counter_start_value[0], LOG_PIT);
116

117
            // This isn't strictly correct, but it's necessary since browsers
118
            // may sleep longer than necessary to trigger the else branch below
119
            // and clear the irq
120
            this.cpu.device_lower_irq(0);
121

122
            this.cpu.device_raise_irq(0);
123
            var mode = this.counter_mode[0];
124

125
            if(mode === 0)
126
            {
127
                this.counter_enabled[0] = 0;
128
            }
129
        }
130
        else
131
        {
132
            this.cpu.device_lower_irq(0);
133
        }
134

135
        if(this.counter_enabled[0])
136
        {
137
            const diff = now - this.counter_start_time[0];
138
            const diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
139
            const ticks_missing = this.counter_start_value[0] - diff_in_ticks; // XXX: to simplify
140
            time_to_next_interrupt = ticks_missing / OSCILLATOR_FREQ;
141
        }
142
    }
143

144
    return time_to_next_interrupt;
145
};
146

147
PIT.prototype.get_counter_value = function(i, now)
148
{
149
    if(!this.counter_enabled[i])
150
    {
151
        return 0;
152
    }
153

154
    var diff = now - this.counter_start_time[i];
155
    var diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
156

157
    var value = this.counter_start_value[i] - diff_in_ticks;
158

159
    dbg_log("diff=" + diff + " dticks=" + diff_in_ticks + " value=" + value + " reload=" + this.counter_reload[i], LOG_PIT);
160

161
    var reload = this.counter_reload[i];
162

163
    if(value >= reload)
164
    {
165
        dbg_log("Warning: Counter" + i + " value " + value  + " is larger than reload " + reload, LOG_PIT);
166
        value %= reload;
167
    }
168
    else if(value < 0)
169
    {
170
        value = value % reload + reload;
171
    }
172

173
    return value;
174
};
175

176
PIT.prototype.did_rollover = function(i, now)
177
{
178
    var diff = now - this.counter_start_time[i];
179

180
    if(diff < 0)
181
    {
182
        // should only happen after restore_state
183
        dbg_log("Warning: PIT timer difference is negative, resetting (timer " + i + ")");
184
        return true;
185
    }
186
    var diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
187
    //dbg_log(i + ": diff=" + diff + " start_time=" + this.counter_start_time[i] + " diff_in_ticks=" + diff_in_ticks + " (" + diff * OSCILLATOR_FREQ + ") start_value=" + this.counter_start_value[i] + " did_rollover=" + (this.counter_start_value[i] < diff_in_ticks), LOG_PIT);
188

189
    return this.counter_start_value[i] < diff_in_ticks;
190
};
191

192
PIT.prototype.counter_read = function(i)
193
{
194
    var latch = this.counter_latch[i];
195

196
    if(latch)
197
    {
198
        this.counter_latch[i]--;
199

200
        if(latch === 2)
201
        {
202
            return this.counter_latch_value[i] & 0xFF;
203
        }
204
        else
205
        {
206
            return this.counter_latch_value[i] >> 8;
207
        }
208
    }
209
    else
210
    {
211
        var next_low = this.counter_next_low[i];
212

213
        if(this.counter_mode[i] === 3)
214
        {
215
            this.counter_next_low[i] ^= 1;
216
        }
217

218
        var value = this.get_counter_value(i, v86.microtick());
219

220
        if(next_low)
221
        {
222
            return value & 0xFF;
223
        }
224
        else
225
        {
226
            return value >> 8;
227
        }
228
    }
229
};
230

231
PIT.prototype.counter_write = function(i, value)
232
{
233
    if(this.counter_next_low[i])
234
    {
235
        this.counter_reload[i] = this.counter_reload[i] & ~0xFF | value;
236
    }
237
    else
238
    {
239
        this.counter_reload[i] = this.counter_reload[i] & 0xFF | value << 8;
240
    }
241

242
    if(this.counter_read_mode[i] !== 3 || !this.counter_next_low[i])
243
    {
244
        if(!this.counter_reload[i])
245
        {
246
            this.counter_reload[i] = 0xFFFF;
247
        }
248

249
        // depends on the mode, should actually
250
        // happen on the first tick
251
        this.counter_start_value[i] = this.counter_reload[i];
252

253
        this.counter_enabled[i] = true;
254

255
        this.counter_start_time[i] = v86.microtick();
256

257
        dbg_log("counter" + i + " reload=" + h(this.counter_reload[i]) +
258
                " tick=" + (this.counter_reload[i] || 0x10000) / OSCILLATOR_FREQ + "ms", LOG_PIT);
259
    }
260

261
    if(this.counter_read_mode[i] === 3)
262
    {
263
        this.counter_next_low[i] ^= 1;
264
    }
265
};
266

267
PIT.prototype.port43_write = function(reg_byte)
268
{
269
    var mode = reg_byte >> 1 & 7,
270
        binary_mode = reg_byte & 1,
271
        i = reg_byte >> 6 & 3,
272
        read_mode = reg_byte >> 4 & 3;
273

274
    if(i === 1)
275
    {
276
        dbg_log("Unimplemented timer1", LOG_PIT);
277
    }
278

279
    if(i === 3)
280
    {
281
        dbg_log("Unimplemented read back", LOG_PIT);
282
        return;
283
    }
284

285
    if(read_mode === 0)
286
    {
287
        // latch
288
        this.counter_latch[i] = 2;
289
        var value = this.get_counter_value(i, v86.microtick());
290
        dbg_log("latch: " + value, LOG_PIT);
291
        this.counter_latch_value[i] = value ? value - 1 : 0;
292

293
        return;
294
    }
295

296
    if(mode >= 6)
297
    {
298
        // 6 and 7 are aliased to 2 and 3
299
        mode &= ~4;
300
    }
301

302
    dbg_log("Control: mode=" + mode + " ctr=" + i +
303
            " read_mode=" + read_mode + " bcd=" + binary_mode, LOG_PIT);
304

305
    if(read_mode === 1)
306
    {
307
        // msb
308
        this.counter_next_low[i] = 0;
309
    }
310
    else if(read_mode === 2)
311
    {
312
        // lsb
313
        this.counter_next_low[i] = 1;
314
    }
315
    else
316
    {
317
        // first lsb then msb
318
        this.counter_next_low[i] = 1;
319
    }
320

321
    if(i === 0)
322
    {
323
        this.cpu.device_lower_irq(0);
324
    }
325

326
    if(mode === 0)
327
    {
328
    }
329
    else if(mode === 3 || mode === 2)
330
    {
331
        // what is the difference
332
    }
333
    else
334
    {
335
        dbg_log("Unimplemented counter mode: " + h(mode), LOG_PIT);
336
    }
337

338
    this.counter_mode[i] = mode;
339
    this.counter_read_mode[i] = read_mode;
340

341
    if(i === 2)
342
    {
343
        this.bus.send("pcspeaker-update", [this.counter_mode[2], this.counter_reload[2]]);
344
    }
345
};
346

347
PIT.prototype.dump = function()
348
{
349
    const reload = this.counter_reload[0];
350
    const time = (reload || 0x10000) / OSCILLATOR_FREQ;
351
    dbg_log("counter0 ticks every " + time + "ms (reload=" + reload + ")");
352
};
353

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

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

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

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