jslinux/PIT.js

226 lines
6.1 KiB
JavaScript

/*
JSLinux-deobfuscated - An annotated version of the original JSLinux.
Original is Copyright (c) 2011-2012 Fabrice Bellard
Redistribution or commercial use is prohibited without the author's permission.
8254 Programmble Interrupt Timer Emulator
Useful References
-----------------
https://en.wikipedia.org/wiki/Intel_8253
*/
function PIT(PC, set_irq_callback, cycle_count_callback) {
var s, i;
this.pit_channels = new Array();
for (i = 0; i < 3; i++) {
s = new IRQCH(cycle_count_callback);
this.pit_channels[i] = s;
s.mode = 3;
s.gate = (i != 2) >> 0;
s.pit_load_count(0);
}
this.speaker_data_on = 0;
this.set_irq = set_irq_callback;
// Ports:
// 0x40: Channel 0 data port
// 0x61: Control
PC.register_ioport_write(0x40, 4, 1, this.ioport_write.bind(this));
PC.register_ioport_read(0x40, 3, 1, this.ioport_read.bind(this));
PC.register_ioport_read(0x61, 1, 1, this.speaker_ioport_read.bind(this));
PC.register_ioport_write(0x61, 1, 1, this.speaker_ioport_write.bind(this));
}
function IRQCH(cycle_count_callback) {
this.count = 0;
this.latched_count = 0;
this.rw_state = 0;
this.mode = 0;
this.bcd = 0;
this.gate = 0;
this.count_load_time = 0;
this.get_ticks = cycle_count_callback;
this.pit_time_unit = 1193182 / 2000000;
}
IRQCH.prototype.get_time = function() {
return Math.floor(this.get_ticks() * this.pit_time_unit);
};
IRQCH.prototype.pit_get_count = function() {
var d, dh;
d = this.get_time() - this.count_load_time;
switch (this.mode) {
case 0:
case 1:
case 4:
case 5:
dh = (this.count - d) & 0xffff;
break;
default:
dh = this.count - (d % this.count);
break;
}
return dh;
};
IRQCH.prototype.pit_get_out = function() {
var d, eh;
d = this.get_time() - this.count_load_time;
switch (this.mode) {
default:
case 0: // Interrupt on terminal count
eh = (d >= this.count) >> 0;
break;
case 1: // One shot
eh = (d < this.count) >> 0;
break;
case 2: // Frequency divider
if ((d % this.count) == 0 && d != 0)
eh = 1;
else
eh = 0;
break;
case 3: // Square wave
eh = ((d % this.count) < (this.count >> 1)) >> 0;
break;
case 4: // SW strobe
case 5: // HW strobe
eh = (d == this.count) >> 0;
break;
}
return eh;
};
IRQCH.prototype.get_next_transition_time = function() {
var d, fh, base, gh;
d = this.get_time() - this.count_load_time;
switch (this.mode) {
default:
case 0: // Interrupt on terminal count
case 1: // One shot
if (d < this.count)
fh = this.count;
else
return -1;
break;
case 2: // Frequency divider
base = (d / this.count) * this.count;
if ((d - base) == 0 && d != 0)
fh = base + this.count;
else
fh = base + this.count + 1;
break;
case 3: // Square wave
base = (d / this.count) * this.count;
gh = ((this.count + 1) >> 1);
if ((d - base) < gh)
fh = base + gh;
else
fh = base + this.count;
break;
case 4: // SW strobe
case 5: // HW strobe
if (d < this.count)
fh = this.count;
else if (d == this.count)
fh = this.count + 1;
else
return -1;
break;
}
fh = this.count_load_time + fh;
return fh;
};
IRQCH.prototype.pit_load_count = function(x) {
if (x == 0)
x = 0x10000;
this.count_load_time = this.get_time();
this.count = x;
};
PIT.prototype.ioport_write = function(mem8_loc, x) {
var hh, ih, s;
mem8_loc &= 3;
if (mem8_loc == 3) {
hh = x >> 6;
if (hh == 3)
return;
s = this.pit_channels[hh];
ih = (x >> 4) & 3;
switch (ih) {
case 0:
s.latched_count = s.pit_get_count();
s.rw_state = 4;
break;
default:
s.mode = (x >> 1) & 7;
s.bcd = x & 1;
s.rw_state = ih - 1 + 0;
break;
}
} else {
s = this.pit_channels[mem8_loc];
switch (s.rw_state) {
case 0:
s.pit_load_count(x);
break;
case 1:
s.pit_load_count(x << 8);
break;
case 2:
case 3:
if (s.rw_state & 1) {
s.pit_load_count((s.latched_count & 0xff) | (x << 8));
} else {
s.latched_count = x;
}
s.rw_state ^= 1;
break;
}
}
};
PIT.prototype.ioport_read = function(mem8_loc) {
var Pg, ma, s;
mem8_loc &= 3;
s = this.pit_channels[mem8_loc];
switch (s.rw_state) {
case 0:
case 1:
case 2:
case 3:
ma = s.pit_get_count();
if (s.rw_state & 1)
Pg = (ma >> 8) & 0xff;
else
Pg = ma & 0xff;
if (s.rw_state & 2)
s.rw_state ^= 1;
break;
default:
case 4:
case 5:
if (s.rw_state & 1)
Pg = s.latched_count >> 8;
else
Pg = s.latched_count & 0xff;
s.rw_state ^= 1;
break;
}
return Pg;
};
PIT.prototype.speaker_ioport_write = function(mem8_loc, x) {
this.speaker_data_on = (x >> 1) & 1;
this.pit_channels[2].gate = x & 1;
};
PIT.prototype.speaker_ioport_read = function(mem8_loc) {
var eh, s, x;
s = this.pit_channels[2];
eh = s.pit_get_out();
x = (this.speaker_data_on << 1) | s.gate | (eh << 5);
return x;
};
PIT.prototype.update_irq = function() {
this.set_irq(1);
this.set_irq(0);
};