From 541ce3c478107198372635ee95b5778bf75db851 Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Sun, 15 Sep 2013 20:17:04 -0700 Subject: [PATCH] Added comments and clarification on the PIC and PIT emulation code. --- CMOS.js | 34 +++++--- PCEmulator.js | 14 ++-- PIC.js | 218 +++++++++++++++++++++++++++++++++----------------- PIT.js | 54 ++++++------- 4 files changed, 200 insertions(+), 120 deletions(-) diff --git a/CMOS.js b/CMOS.js index c2714a1..f5cd69a 100644 --- a/CMOS.js +++ b/CMOS.js @@ -4,22 +4,34 @@ Original is Copyright (c) 2011-2012 Fabrice Bellard Redistribution or commercial use is prohibited without the author's permission. -Clock Emulator + CMOS Ram Memory, actually just the RTC Clock Emulator + + Useful references: + ------------------ + http://www.bioscentral.com/misc/cmosmap.htm + http://wiki.osdev.org/CMOS */ -function formatter(a) { return ((a / 10) << 4) | (a % 10);} + +/* + In this implementation, bytes are stored in the RTC in BCD format + binary -> bcd: bcd = ((bin / 10) << 4) | (bin % 10) + bcd -> binary: bin = ((bcd / 16) * 10) + (bcd & 0xf) +*/ +function bin_to_bcd(a) { return ((a / 10) << 4) | (a % 10);} + function CMOS(PC) { var time_array, d; time_array = new Uint8Array(128); this.cmos_data = time_array; this.cmos_index = 0; d = new Date(); - time_array[0] = formatter(d.getUTCSeconds()); - time_array[2] = formatter(d.getUTCMinutes()); - time_array[4] = formatter(d.getUTCHours()); - time_array[6] = formatter(d.getUTCDay()); - time_array[7] = formatter(d.getUTCDate()); - time_array[8] = formatter(d.getUTCMonth() + 1); - time_array[9] = formatter(d.getUTCFullYear() % 100); + time_array[0] = bin_to_bcd(d.getUTCSeconds()); + time_array[2] = bin_to_bcd(d.getUTCMinutes()); + time_array[4] = bin_to_bcd(d.getUTCHours()); + time_array[6] = bin_to_bcd(d.getUTCDay()); + time_array[7] = bin_to_bcd(d.getUTCDate()); + time_array[8] = bin_to_bcd(d.getUTCMonth() + 1); + time_array[9] = bin_to_bcd(d.getUTCFullYear() % 100); time_array[10] = 0x26; time_array[11] = 0x02; time_array[12] = 0x00; @@ -30,7 +42,7 @@ function CMOS(PC) { } CMOS.prototype.ioport_write = function(mem8_loc, data) { if (mem8_loc == 0x70) { - // the high order bit is used to indicate NMI masking + // the high order bit is used to indicate NMI masking // low order bits are used to address CMOS // the index written here is used on an ioread 0x71 this.cmos_index = data & 0x7f; @@ -41,7 +53,7 @@ CMOS.prototype.ioport_read = function(mem8_loc) { if (mem8_loc == 0x70) { return 0xff; } else { - // else here => 0x71, i.e., CMOS read + // else here => 0x71, i.e., CMOS read data = this.cmos_data[this.cmos_index]; if (this.cmos_index == 10) // flip the UIP (update in progress) bit on a read diff --git a/PCEmulator.js b/PCEmulator.js index 3525d59..117ae5b 100644 --- a/PCEmulator.js +++ b/PCEmulator.js @@ -7,25 +7,25 @@ Redistribution or commercial use is prohibited without the author's permission. Main PC Emulator Routine */ +// used as callback wrappers for emulated PIT and PIC chips function set_hard_irq_wrapper(irq) { this.hard_irq = irq;} - function return_cycle_count() { return this.cycle_count; } -function PCEmulator(uh) { +function PCEmulator(params) { var cpu; cpu = new CPU_X86(); this.cpu = cpu; - cpu.phys_mem_resize(uh.mem_size); + cpu.phys_mem_resize(params.mem_size); this.init_ioports(); this.register_ioport_write(0x80, 1, 1, this.ioport80_write); this.pic = new PIC_Controller(this, 0x20, 0xa0, set_hard_irq_wrapper.bind(cpu)); this.pit = new PIT(this, this.pic.set_irq.bind(this.pic, 0), return_cycle_count.bind(cpu)); this.cmos = new CMOS(this); - this.serial = new Serial(this, 0x3f8, this.pic.set_irq.bind(this.pic, 4), uh.serial_write); + this.serial = new Serial(this, 0x3f8, this.pic.set_irq.bind(this.pic, 4), params.serial_write); this.kbd = new KBD(this, this.reset.bind(this)); this.reset_request = 0; - if (uh.clipboard_get && uh.clipboard_set) { - this.jsclipboard = new clipboard_device(this, 0x3c0, uh.clipboard_get, uh.clipboard_set, uh.get_boot_time); + if (params.clipboard_get && params.clipboard_set) { + this.jsclipboard = new clipboard_device(this, 0x3c0, params.clipboard_get, params.clipboard_set, params.get_boot_time); } cpu.ld8_port = this.ld8_port.bind(this); cpu.ld16_port = this.ld16_port.bind(this); @@ -36,7 +36,7 @@ function PCEmulator(uh) { cpu.get_hard_intno = this.pic.get_hard_intno.bind(this.pic); } -PCEmulator.prototype.load_binary = function(Gg, ha) { return this.cpu.load_binary(Gg, ha); }; +PCEmulator.prototype.load_binary = function(url, mem8_loc) { return this.cpu.load_binary(url, mem8_loc); }; PCEmulator.prototype.start = function() { setTimeout(this.timer_func.bind(this), 10); }; diff --git a/PIC.js b/PIC.js index 5280400..3122afa 100644 --- a/PIC.js +++ b/PIC.js @@ -1,11 +1,71 @@ /* -JSLinux-deobfuscated - An annotated version of the original JSLinux. + 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. + Original is Copyright (c) 2011-2012 Fabrice Bellard + Redistribution or commercial use is prohibited without the author's permission. -8259 PIC (Programmable Interrupt Controller) Emulation Code + 8259A PIC (Programmable Interrupt Controller) Emulation Code + + The 8259 combines multiple interrupt input sources into a single + interrupt output to the host microprocessor, extending the interrupt + levels available in a system beyond the one or two levels found on the + processor chip. + + There are three registers, an Interrupt Mask Register (IMR), an + Interrupt Request Register (IRR), and an In-Service Register + (ISR): + IRR - a mask of the current interrupts that are pending acknowledgement + ISR - a mask of the interrupts that are pending an EOI + IMR - a mask of interrupts that should not be sent an acknowledgement + + End Of Interrupt (EOI) operations support specific EOI, non-specific + EOI, and auto-EOI. A specific EOI specifies the IRQ level it is + acknowledging in the ISR. A non-specific EOI resets the IRQ level in + the ISR. Auto-EOI resets the IRQ level in the ISR immediately after + the interrupt is acknowledged. + + After the IBM XT, it was decided that 8 IRQs was not enough. + The backwards-compatible solution was simply to chain two 8259As together, + the master and slave PIC. + + Useful References + ----------------- + https://en.wikipedia.org/wiki/Programmable_Interrupt_Controller + https://en.wikipedia.org/wiki/Intel_8259 + http://www.thesatya.com/8259.html */ + +/* + Common PC arrangements of IRQ lines: + ------------------------------------ + + PC/AT and later systems had two 8259 controllers, master and + slave. IRQ0 through IRQ7 are the master 8259's interrupt lines, while + IRQ8 through IRQ15 are the slave 8259's interrupt lines. The labels on + the pins on an 8259 are IR0 through IR7. IRQ0 through IRQ15 are the + names of the ISA bus's lines to which the 8259s are attached. + + Master 8259 + IRQ0 – Intel 8253 or Intel 8254 Programmable Interval Timer, aka the system timer + IRQ1 – Intel 8042 keyboard controller + IRQ2 – not assigned in PC/XT; cascaded to slave 8259 INT line in PC/AT + IRQ3 – 8250 UART serial ports 2 and 4 + IRQ4 – 8250 UART serial ports 1 and 3 + IRQ5 – hard disk controller in PC/XT; Intel 8255 parallel ports 2 and 3 in PC/AT + IRQ6 – Intel 82072A floppy disk controller + IRQ7 – Intel 8255 parallel port 1 / spurious interrupt + + Slave 8259 (PC/AT and later only) + IRQ8 – real-time clock (RTC) + IRQ9 – no common assignment, but 8-bit cards' IRQ2 line is routed to this interrupt. + IRQ10 – no common assignment + IRQ11 – no common assignment + IRQ12 – Intel 8042 PS/2 mouse controller + IRQ13 – math coprocessor + IRQ14 – hard disk controller 1 + IRQ15 – hard disk controller 2 +*/ + function PIC(PC, port_num) { PC.register_ioport_write(port_num, 2, 1, this.ioport_write.bind(this)); PC.register_ioport_read(port_num, 2, 1, this.ioport_read.bind(this)); @@ -13,9 +73,9 @@ function PIC(PC, port_num) { } PIC.prototype.reset = function() { this.last_irr = 0; - this.irr = 0; - this.imr = 0; - this.isr = 0; + this.irr = 0; //Interrupt Request Register + this.imr = 0; //Interrupt Mask Register + this.isr = 0; //In-Service Register this.priority_add = 0; this.irq_base = 0; this.read_reg_select = 0; @@ -24,54 +84,66 @@ PIC.prototype.reset = function() { this.auto_eoi = 0; this.rotate_on_autoeoi = 0; this.init4 = 0; - this.elcr = 0; + this.elcr = 0; // Edge/Level Control Register this.elcr_mask = 0; }; -PIC.prototype.set_irq1 = function(Rg, Qf) { - var wc; - wc = 1 << Rg; +PIC.prototype.set_irq1 = function(irq, Qf) { + var ir_register; + ir_register = 1 << irq; if (Qf) { - if ((this.last_irr & wc) == 0) - this.irr |= wc; - this.last_irr |= wc; + if ((this.last_irr & ir_register) == 0) + this.irr |= ir_register; + this.last_irr |= ir_register; } else { - this.last_irr &= ~wc; + this.last_irr &= ~ir_register; } }; -PIC.prototype.get_priority = function(wc) { - var Sg; - if (wc == 0) +/* + The priority assignments for IRQ0-7 seem to be maintained in a + cyclic order modulo 8 by the 8259A. On bootup, it default to: + + Priority: 0 1 2 3 4 5 6 7 + IRQ: 7 6 5 4 3 2 1 0 + + but can be rotated automatically or programmatically to a state e.g.: + + Priority: 5 6 7 0 1 2 3 4 + IRQ: 7 6 5 4 3 2 1 0 +*/ +PIC.prototype.get_priority = function(ir_register) { + var priority; + if (ir_register == 0) return -1; - Sg = 7; - while ((wc & (1 << ((Sg + this.priority_add) & 7))) == 0) - Sg--; - return Sg; + priority = 7; + while ((ir_register & (1 << ((priority + this.priority_add) & 7))) == 0) + priority--; + return priority; }; PIC.prototype.get_irq = function() { - var wc, Tg, Sg; - wc = this.irr & ~this.imr; - Sg = this.get_priority(wc); - if (Sg < 0) + var ir_register, in_service_priority, priority; + ir_register = this.irr & ~this.imr; + priority = this.get_priority(ir_register); + if (priority < 0) return -1; - Tg = this.get_priority(this.isr); - if (Sg > Tg) { - return Sg; + in_service_priority = this.get_priority(this.isr); + if (priority > in_service_priority) { + return priority; } else { return -1; } }; -PIC.prototype.intack = function(Rg) { +PIC.prototype.intack = function(irq) { if (this.auto_eoi) { if (this.rotate_on_auto_eoi) - this.priority_add = (Rg + 1) & 7; + this.priority_add = (irq + 1) & 7; } else { - this.isr |= (1 << Rg); + this.isr |= (1 << irq); } - if (!(this.elcr & (1 << Rg))) - this.irr &= ~(1 << Rg); + if (!(this.elcr & (1 << irq))) + this.irr &= ~(1 << irq); }; PIC.prototype.ioport_write = function(mem8_loc, x) { - var Sg; + var priority; mem8_loc &= 1; if (mem8_loc == 0) { if (x & 0x10) { @@ -105,9 +177,9 @@ PIC.prototype.ioport_write = function(mem8_loc, x) { break; case 0x20: case 0xa0: - Sg = this.get_priority(this.isr); - if (Sg >= 0) { - this.isr &= ~(1 << ((Sg + this.priority_add) & 7)); + priority = this.get_priority(this.isr); + if (priority >= 0) { + this.isr &= ~(1 << ((priority + this.priority_add) & 7)); } if (x == 0xa0) this.priority_add = (this.priority_add + 1) & 7; @@ -120,8 +192,8 @@ PIC.prototype.ioport_write = function(mem8_loc, x) { case 0x65: case 0x66: case 0x67: - Sg = x & 7; - this.isr &= ~(1 << Sg); + priority = x & 7; + this.isr &= ~(1 << priority); break; case 0xc0: case 0xc1: @@ -141,9 +213,9 @@ PIC.prototype.ioport_write = function(mem8_loc, x) { case 0xe5: case 0xe6: case 0xe7: - Sg = x & 7; - this.isr &= ~(1 << Sg); - this.priority_add = (Sg + 1) & 7; + priority = x & 7; + this.isr &= ~(1 << priority); + this.priority_add = (priority + 1) & 7; break; } } @@ -172,69 +244,69 @@ PIC.prototype.ioport_write = function(mem8_loc, x) { } }; PIC.prototype.ioport_read = function(Ug) { - var mem8_loc, Pg; + var mem8_loc, return_register; mem8_loc = Ug & 1; if (mem8_loc == 0) { if (this.read_reg_select) - Pg = this.isr; + return_register = this.isr; else - Pg = this.irr; + return_register = this.irr; } else { - Pg = this.imr; + return_register = this.imr; } - return Pg; + return return_register; }; -function PIC_Controller(PC, Wg, Ug, Xg) { +function PIC_Controller(PC, master_PIC_port, slave_PIC_port, cpu_set_irq_callback) { this.pics = new Array(); - this.pics[0] = new PIC(PC, Wg); - this.pics[1] = new PIC(PC, Ug); + this.pics[0] = new PIC(PC, master_PIC_port); + this.pics[1] = new PIC(PC, slave_PIC_port); this.pics[0].elcr_mask = 0xf8; this.pics[1].elcr_mask = 0xde; this.irq_requested = 0; - this.cpu_set_irq = Xg; + this.cpu_set_irq = cpu_set_irq_callback; this.pics[0].update_irq = this.update_irq.bind(this); this.pics[1].update_irq = this.update_irq.bind(this); } PIC_Controller.prototype.update_irq = function() { - var Yg, Rg; - Yg = this.pics[1].get_irq(); - if (Yg >= 0) { + var slave_irq, irq; + slave_irq = this.pics[1].get_irq(); + if (slave_irq >= 0) { this.pics[0].set_irq1(2, 1); this.pics[0].set_irq1(2, 0); } - Rg = this.pics[0].get_irq(); - if (Rg >= 0) { + irq = this.pics[0].get_irq(); + if (irq >= 0) { this.cpu_set_irq(1); } else { this.cpu_set_irq(0); } }; -PIC_Controller.prototype.set_irq = function(Rg, Qf) { - this.pics[Rg >> 3].set_irq1(Rg & 7, Qf); +PIC_Controller.prototype.set_irq = function(irq, Qf) { + this.pics[irq >> 3].set_irq1(irq & 7, Qf); this.update_irq(); }; PIC_Controller.prototype.get_hard_intno = function() { - var Rg, Yg, intno; - Rg = this.pics[0].get_irq(); - if (Rg >= 0) { - this.pics[0].intack(Rg); - if (Rg == 2) { - Yg = this.pics[1].get_irq(); - if (Yg >= 0) { - this.pics[1].intack(Yg); + var irq, slave_irq, intno; + irq = this.pics[0].get_irq(); + if (irq >= 0) { + this.pics[0].intack(irq); + if (irq == 2) { //IRQ 2 cascaded to slave 8259 INT line in PC/AT + slave_irq = this.pics[1].get_irq(); + if (slave_irq >= 0) { + this.pics[1].intack(slave_irq); } else { - Yg = 7; + slave_irq = 7; } - intno = this.pics[1].irq_base + Yg; - Rg = Yg + 8; + intno = this.pics[1].irq_base + slave_irq; + irq = slave_irq + 8; } else { - intno = this.pics[0].irq_base + Rg; + intno = this.pics[0].irq_base + irq; } } else { - Rg = 7; - intno = this.pics[0].irq_base + Rg; + irq = 7; + intno = this.pics[0].irq_base + irq; } this.update_irq(); return intno; diff --git a/PIT.js b/PIT.js index 2d936c7..8205bbd 100644 --- a/PIT.js +++ b/PIT.js @@ -1,23 +1,27 @@ /* -JSLinux-deobfuscated - An annotated version of the original JSLinux. + 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. + Original is Copyright (c) 2011-2012 Fabrice Bellard + Redistribution or commercial use is prohibited without the author's permission. -8254 Programmble Interrupt Timer Emulator + 8254 Programmble Interrupt Timer Emulator + + Useful References + ----------------- + https://en.wikipedia.org/wiki/Intel_8253 */ -function PIT(PC, ah, bh) { +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(bh); + 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 = ah; + this.set_irq = set_irq_callback; // Ports: // 0x40: Channel 0 data port // 0x61: Control @@ -27,9 +31,7 @@ function PIT(PC, ah, bh) { PC.register_ioport_write(0x61, 1, 1, this.speaker_ioport_write.bind(this)); } - - -function IRQCH(bh) { +function IRQCH(cycle_count_callback) { this.count = 0; this.latched_count = 0; this.rw_state = 0; @@ -37,7 +39,7 @@ function IRQCH(bh) { this.bcd = 0; this.gate = 0; this.count_load_time = 0; - this.get_ticks = bh; + this.get_ticks = cycle_count_callback; this.pit_time_unit = 1193182 / 2000000; } IRQCH.prototype.get_time = function() { @@ -64,29 +66,23 @@ IRQCH.prototype.pit_get_out = function() { d = this.get_time() - this.count_load_time; switch (this.mode) { default: - // Interrupt on terminal count - case 0: + case 0: // Interrupt on terminal count eh = (d >= this.count) >> 0; break; - // One shot - case 1: + case 1: // One shot eh = (d < this.count) >> 0; break; - // Frequency divider - case 2: + case 2: // Frequency divider if ((d % this.count) == 0 && d != 0) eh = 1; else eh = 0; break; - // Square wave - case 3: + case 3: // Square wave eh = ((d % this.count) < (this.count >> 1)) >> 0; break; - // SW strobe - case 4: - // HW strobe - case 5: + case 4: // SW strobe + case 5: // HW strobe eh = (d == this.count) >> 0; break; } @@ -97,21 +93,21 @@ IRQCH.prototype.get_next_transition_time = function() { d = this.get_time() - this.count_load_time; switch (this.mode) { default: - case 0: - case 1: + case 0: // Interrupt on terminal count + case 1: // One shot if (d < this.count) fh = this.count; else return -1; break; - case 2: + 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: + case 3: // Square wave base = (d / this.count) * this.count; gh = ((this.count + 1) >> 1); if ((d - base) < gh) @@ -119,8 +115,8 @@ IRQCH.prototype.get_next_transition_time = function() { else fh = base + this.count; break; - case 4: - case 5: + case 4: // SW strobe + case 5: // HW strobe if (d < this.count) fh = this.count; else if (d == this.count)