304 lines
7.1 KiB
JavaScript
304 lines
7.1 KiB
JavaScript
"use strict";
|
|
|
|
|
|
/**
|
|
* @constructor
|
|
*
|
|
* Programmable Interval Timer
|
|
*/
|
|
function PIT(dev)
|
|
{
|
|
var
|
|
io = dev.io,
|
|
pic = dev.pic,
|
|
|
|
next_tick = Date.now(),
|
|
|
|
me = this,
|
|
|
|
/**
|
|
* @const
|
|
* In kHz
|
|
*/
|
|
OSCILLATOR_FREQ = 1193.1816666, // 1.193182 MHz
|
|
|
|
|
|
counter_next_low = new Uint8Array(3),
|
|
counter_enabled = new Uint8Array(3),
|
|
counter_mode = new Uint8Array(3),
|
|
counter_read_mode = new Uint8Array(3),
|
|
|
|
// 2 = latch low, 1 = latch high, 0 = no latch
|
|
counter_latch = new Uint8Array(3),
|
|
counter_latch_value = new Uint16Array(3),
|
|
|
|
counter_reload = new Uint16Array(3),
|
|
counter_current = new Uint16Array(3),
|
|
|
|
// only counter2 output can be read
|
|
counter2_out = 0;
|
|
|
|
|
|
// TODO:
|
|
// - counter2 can be controlled by an input
|
|
|
|
|
|
this.get_timer2 = function()
|
|
{
|
|
//dbg_log("timer2 read", LOG_PIT);
|
|
return counter2_out;
|
|
};
|
|
|
|
var parity = 0;
|
|
|
|
io.register_read(0x61, function()
|
|
{
|
|
// > xxx1 xxxx 0=RAM parity error enable
|
|
// > PS/2: Read: This bit tiggles for each refresh request.
|
|
//
|
|
// tiggles??
|
|
|
|
parity ^= 0x10;
|
|
return parity | counter2_out << 5;
|
|
});
|
|
|
|
|
|
this.timer = function(time)
|
|
{
|
|
var current,
|
|
mode,
|
|
steps = (time - next_tick) * OSCILLATOR_FREQ >>> 0;
|
|
|
|
if(!steps)
|
|
{
|
|
return;
|
|
}
|
|
dbg_assert(steps >= 0);
|
|
|
|
next_tick += steps / OSCILLATOR_FREQ;
|
|
|
|
// counter 0 produces interrupts
|
|
if(counter_enabled[0])
|
|
{
|
|
current = counter_current[0] -= steps;
|
|
|
|
if(current <= 0)
|
|
{
|
|
pic.push_irq(0);
|
|
mode = counter_mode[0];
|
|
|
|
if(mode === 0)
|
|
{
|
|
counter_enabled[0] = 0;
|
|
counter_current[0] = 0;
|
|
}
|
|
else if(mode === 3 || mode === 2)
|
|
{
|
|
counter_current[0] = counter_reload[0] + current % counter_reload[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
// counter 2 has an output bit
|
|
if(counter_enabled[2])
|
|
{
|
|
current = counter_current[2] -= steps;
|
|
|
|
if(current <= 0)
|
|
{
|
|
mode = counter_mode[2];
|
|
|
|
if(mode === 0)
|
|
{
|
|
counter2_out = 1;
|
|
counter_enabled[2] = 0;
|
|
counter_current[2] = 0;
|
|
}
|
|
else if(mode === 2)
|
|
{
|
|
counter2_out = 1;
|
|
counter_current[2] = counter_reload[2] + current % counter_reload[2];
|
|
}
|
|
else if(mode === 3)
|
|
{
|
|
counter2_out ^= 1;
|
|
counter_current[2] = counter_reload[2] + current % counter_reload[2];
|
|
}
|
|
}
|
|
// cannot really happen, because the counter gets changed by big numbers
|
|
//else if(current === 1)
|
|
//{
|
|
// if(counter_mode[2] === 2)
|
|
// {
|
|
// counter2_out = 0;
|
|
// }
|
|
//}
|
|
}
|
|
}
|
|
|
|
io.register_read(0x40, function() { return counter_read(0); });
|
|
io.register_read(0x41, function() { return counter_read(1); });
|
|
io.register_read(0x42, function() { return counter_read(2); });
|
|
|
|
function counter_read(i)
|
|
{
|
|
var latch = counter_latch[i];
|
|
|
|
if(latch)
|
|
{
|
|
counter_latch[i]--;
|
|
|
|
if(latch === 2)
|
|
{
|
|
return counter_latch_value[i] & 0xFF;
|
|
}
|
|
else
|
|
{
|
|
return counter_latch_value[i] >> 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var next_low = counter_next_low[i];
|
|
|
|
if(counter_mode[i] === 3)
|
|
{
|
|
counter_next_low[i] ^= 1;
|
|
}
|
|
|
|
if(next_low)
|
|
{
|
|
return counter_current[i] & 0xFF;
|
|
}
|
|
else
|
|
{
|
|
return counter_current[i] >> 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
io.register_write(0x40, function(value) { counter_write(0, value); });
|
|
io.register_write(0x41, function(value) { counter_write(1, value); });
|
|
io.register_write(0x42, function(value) { counter_write(2, value); });
|
|
|
|
function counter_write(i, value)
|
|
{
|
|
if(counter_next_low[i])
|
|
{
|
|
counter_reload[i] = counter_reload[i] & ~0xFF | value;
|
|
}
|
|
else
|
|
{
|
|
counter_reload[i] = counter_reload[i] & 0xFF | value << 8;
|
|
}
|
|
|
|
if(counter_read_mode[i] !== 3 || !counter_next_low[i])
|
|
{
|
|
if(!counter_reload[i])
|
|
{
|
|
counter_reload[i] = 0xFFFF;
|
|
}
|
|
|
|
// depends on the mode, should actually
|
|
// happen on the first tick
|
|
counter_current[i] = counter_reload[i];
|
|
|
|
counter_enabled[i] = true;
|
|
|
|
dbg_log("counter" + i + " reload=" + h(counter_reload[i]) +
|
|
" tick=" + (counter_reload[i] || 0x10000) / OSCILLATOR_FREQ + "ms", LOG_PIT);
|
|
}
|
|
|
|
if(counter_read_mode[i] === 3)
|
|
{
|
|
counter_next_low[i] ^= 1;
|
|
}
|
|
}
|
|
|
|
io.register_write(0x43, port43_write);
|
|
|
|
function port43_write(reg_byte)
|
|
{
|
|
var mode = reg_byte >> 1 & 7,
|
|
binary_mode = reg_byte & 1,
|
|
i = reg_byte >> 6 & 3,
|
|
read_mode = reg_byte >> 4 & 3,
|
|
next_low;
|
|
|
|
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
|
|
counter_latch[i] = 2;
|
|
counter_latch_value[i] = counter_current[i];
|
|
|
|
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)
|
|
{
|
|
// lsb
|
|
counter_next_low[i] = 1;
|
|
}
|
|
else if(read_mode === 2)
|
|
{
|
|
// msb
|
|
counter_next_low[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
// first lsb then msb
|
|
counter_next_low[i] = 1;
|
|
}
|
|
|
|
|
|
if(mode === 0)
|
|
{
|
|
}
|
|
else if(mode === 3 || mode === 2)
|
|
{
|
|
// what is the difference
|
|
}
|
|
else
|
|
{
|
|
dbg_log("Unimplemented counter mode: " + h(mode), LOG_PIT);
|
|
}
|
|
|
|
counter_mode[i] = mode;
|
|
counter_read_mode[i] = read_mode;
|
|
|
|
if(i === 2)
|
|
{
|
|
if(mode === 0)
|
|
{
|
|
counter2_out = 0;
|
|
}
|
|
else
|
|
{
|
|
// correct for mode 2 and 3
|
|
counter2_out = 1;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|