123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- "use strict";
- /**
- * @const
- * In kHz
- */
- var OSCILLATOR_FREQ = 1193.1816666; // 1.193182 MHz
- /**
- * @constructor
- *
- * Programmable Interval Timer
- */
- function PIT(cpu)
- {
- /** @const @type {CPU} */
- this.cpu = cpu;
- this.counter_start_time = new Float64Array(3);
- this.counter_start_value = new Uint16Array(3);
- this.counter_next_low = new Uint8Array(4);
- this.counter_enabled = new Uint8Array(4);
- this.counter_mode = new Uint8Array(4);
- this.counter_read_mode = new Uint8Array(4);
- // 2 = latch low, 1 = latch high, 0 = no latch
- this.counter_latch = new Uint8Array(4);
- this.counter_latch_value = new Uint16Array(3);
- this.counter_reload = new Uint16Array(3);
- // TODO:
- // - counter2 can be controlled by an input
- cpu.io.register_read(0x61, this, function()
- {
- var now = v86.microtick();
- var ref_toggle = (now * (1000 * 1000 / 15000)) & 1;
- var counter2_out = this.did_rollover(2, now);
- return ref_toggle << 4 | counter2_out << 5;
- });
- cpu.io.register_read(0x40, this, function() { return this.counter_read(0); });
- cpu.io.register_read(0x41, this, function() { return this.counter_read(1); });
- cpu.io.register_read(0x42, this, function() { return this.counter_read(2); });
- cpu.io.register_write(0x40, this, function(data) { this.counter_write(0, data); });
- cpu.io.register_write(0x41, this, function(data) { this.counter_write(1, data); });
- cpu.io.register_write(0x42, this, function(data) { this.counter_write(2, data); });
- cpu.io.register_write(0x43, this, this.port43_write);
- }
- PIT.prototype.get_state = function()
- {
- var state = [];
- state[0] = this.counter_next_low;
- state[1] = this.counter_enabled;
- state[2] = this.counter_mode;
- state[3] = this.counter_read_mode;
- state[4] = this.counter_latch;
- state[5] = this.counter_latch_value;
- state[6] = this.counter_reload;
- state[7] = this.counter_start_time;
- state[8] = this.counter_start_value;
- return state;
- };
- PIT.prototype.set_state = function(state)
- {
- this.counter_next_low = state[0];
- this.counter_enabled = state[1];
- this.counter_mode = state[2];
- this.counter_read_mode = state[3];
- this.counter_latch = state[4];
- this.counter_latch_value = state[5];
- this.counter_reload = state[6];
- this.counter_start_time = state[7];
- this.counter_start_value = state[8];
- };
- PIT.prototype.timer = function(now, no_irq)
- {
- var time_to_next_interrupt = 100;
- // counter 0 produces interrupts
- if(!no_irq)
- {
- if(this.counter_enabled[0] && this.did_rollover(0, now))
- {
- time_to_next_interrupt = 0;
- this.counter_start_value[0] = this.get_counter_value(0, now);
- this.counter_start_time[0] = now;
- dbg_log("pit interrupt. new value: " + this.counter_start_value[0], LOG_PIT);
- this.cpu.device_raise_irq(0);
- var mode = this.counter_mode[0];
- if(mode === 0)
- {
- this.counter_enabled[0] = 0;
- }
- }
- else
- {
- this.cpu.device_lower_irq(0);
- }
- }
- time_to_next_interrupt = 0;
- return time_to_next_interrupt;
- };
- PIT.prototype.get_counter_value = function(i, now)
- {
- if(!this.counter_enabled[i])
- {
- return 0;
- }
- var diff = now - this.counter_start_time[i];
- var diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
- var value = this.counter_start_value[i] - diff_in_ticks;
- dbg_log("diff=" + diff + " dticks=" + diff_in_ticks + " value=" + value + " reload=" + this.counter_reload[i], LOG_PIT);
- if(value < 0)
- {
- var reload = this.counter_reload[i];
- value = value % reload + reload;
- }
- return value;
- };
- PIT.prototype.did_rollover = function(i, now)
- {
- var diff = now - this.counter_start_time[i];
- if(diff < 0)
- {
- // should only happen after restore_state
- dbg_log("Warning: PIT timer difference is negative, resetting");
- return true;
- }
- var diff_in_ticks = Math.floor(diff * OSCILLATOR_FREQ);
- //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);
- return this.counter_start_value[i] < diff_in_ticks;
- };
- PIT.prototype.counter_read = function(i)
- {
- var latch = this.counter_latch[i];
- if(latch)
- {
- this.counter_latch[i]--;
- if(latch === 2)
- {
- return this.counter_latch_value[i] & 0xFF;
- }
- else
- {
- return this.counter_latch_value[i] >> 8;
- }
- }
- else
- {
- var next_low = this.counter_next_low[i];
- if(this.counter_mode[i] === 3)
- {
- this.counter_next_low[i] ^= 1;
- }
- var value = this.get_counter_value(i, v86.microtick());
- if(next_low)
- {
- return value & 0xFF;
- }
- else
- {
- return value >> 8;
- }
- }
- };
- PIT.prototype.counter_write = function(i, value)
- {
- if(this.counter_next_low[i])
- {
- this.counter_reload[i] = this.counter_reload[i] & ~0xFF | value;
- }
- else
- {
- this.counter_reload[i] = this.counter_reload[i] & 0xFF | value << 8;
- }
- if(this.counter_read_mode[i] !== 3 || !this.counter_next_low[i])
- {
- if(!this.counter_reload[i])
- {
- this.counter_reload[i] = 0xFFFF;
- }
- // depends on the mode, should actually
- // happen on the first tick
- this.counter_start_value[i] = this.counter_reload[i];
- this.counter_enabled[i] = true;
- this.counter_start_time[i] = v86.microtick();
- dbg_log("counter" + i + " reload=" + h(this.counter_reload[i]) +
- " tick=" + (this.counter_reload[i] || 0x10000) / OSCILLATOR_FREQ + "ms", LOG_PIT);
- }
- if(this.counter_read_mode[i] === 3)
- {
- this.counter_next_low[i] ^= 1;
- }
- };
- PIT.prototype.port43_write = function(reg_byte)
- {
- var mode = reg_byte >> 1 & 7,
- binary_mode = reg_byte & 1,
- i = reg_byte >> 6 & 3,
- read_mode = reg_byte >> 4 & 3;
- if(i === 1)
- {
- dbg_log("Unimplemented timer1", LOG_PIT);
- }
- if(i === 3)
- {
- dbg_log("Unimplemented read back", LOG_PIT);
- return;
- }
- if(read_mode === 0)
- {
- // latch
- this.counter_latch[i] = 2;
- var value = this.get_counter_value(i, v86.microtick());
- dbg_log("latch: " + value, LOG_PIT);
- this.counter_latch_value[i] = value ? value - 1 : 0;
- return;
- }
- if(mode >= 6)
- {
- // 6 and 7 are aliased to 2 and 3
- mode &= ~4;
- }
- dbg_log("Control: mode=" + mode + " ctr=" + i +
- " read_mode=" + read_mode + " bcd=" + binary_mode, LOG_PIT);
- if(read_mode === 1)
- {
- // msb
- this.counter_next_low[i] = 0;
- }
- else if(read_mode === 2)
- {
- // lsb
- this.counter_next_low[i] = 1;
- }
- else
- {
- // first lsb then msb
- this.counter_next_low[i] = 1;
- }
- if(i === 0)
- {
- this.cpu.device_lower_irq(0);
- }
- if(mode === 0)
- {
- }
- else if(mode === 3 || mode === 2)
- {
- // what is the difference
- }
- else
- {
- dbg_log("Unimplemented counter mode: " + h(mode), LOG_PIT);
- }
- this.counter_mode[i] = mode;
- this.counter_read_mode[i] = read_mode;
- };
|