Build state of emulator explicitly. Fixes #40

This commit is contained in:
copy 2015-05-18 22:18:59 +02:00
parent 4244e295eb
commit f82373d6b2
19 changed files with 777 additions and 256 deletions

View file

@ -65,7 +65,7 @@ function Virtio9p(filesystem, bus) {
//this.configspace = [0x0, 0x4, 0x68, 0x6F, 0x73, 0x74]; // length of string and "host" string
//this.configspace = [0x0, 0x9, 0x2F, 0x64, 0x65, 0x76, 0x2F, 0x72, 0x6F, 0x6F, 0x74 ]; // length of string and "/dev/root" string
this.configspace = [0x6, 0x0, 0x68, 0x6F, 0x73, 0x74, 0x39, 0x70]; // length of string and "host9p" string
this.configspace = new Uint8Array([0x6, 0x0, 0x68, 0x6F, 0x73, 0x74, 0x39, 0x70]); // length of string and "host9p" string
this.VERSION = "9P2000.L";
this.BLOCKSIZE = 8192; // Let's define one page.
this.msize = 8192; // maximum message size
@ -73,13 +73,38 @@ function Virtio9p(filesystem, bus) {
this.replybuffersize = 0;
this.fids = [];
this._state_skip = [
this.fs,
this.bus,
];
}
Virtio9p.prototype.get_state = function()
{
var state = [];
state[0] = this.deviceid;
state[1] = this.hostfeature;
state[2] = this.configspace;
state[3] = this.VERSION;
state[4] = this.BLOCKSIZE;
state[5] = this.msize;
state[6] = this.replybuffer;
state[7] = this.replybuffersize;
state[8] = this.fids.map(function(f) { return [f.inodeid, f.type, f.uid] });
return state;
};
Virtio9p.prototype.set_state = function(state)
{
this.deviceid = state[0];
this.hostfeature = state[1];
this.configspace = state[2];
this.VERSION = state[3];
this.BLOCKSIZE = state[4];
this.msize = state[5];
this.replybuffer = state[6];
this.replybuffersize = state[7];
this.fids = state[8].map(function(f) { return { inodeid: f[0], type: f[1], uid: f[2] } });
};
Virtio9p.prototype.Createfid = function(inode, type, uid) {
return {inodeid: inode, type: type, uid: uid};
}

View file

@ -46,6 +46,7 @@ var STATUS_UNLINKED = 0x4;
/** @constructor */
function FS(baseurl) {
/** @type {Array.<Inode>} */
this.inodes = [];
this.events = [];
@ -77,8 +78,6 @@ function FS(baseurl) {
// root entry
this.CreateDirectory("", -1);
this._state_skip = [];
}
@ -90,7 +89,7 @@ FS.prototype.AddEvent = function(id, OnEvent) {
OnEvent();
return;
}
this.events.push({id: id, OnEvent: OnEvent});
this.events.push({id: id, OnEvent: OnEvent});
}
FS.prototype.HandleEvent = function(id) {
@ -276,7 +275,7 @@ FS.prototype.PushInode = function(inode) {
function Inode(qidnumber)
{
this.updatedir = false; // did the directory listing changed?
this.parentid= -1;
this.parentid = -1;
this.firstid = -1; // first file id in directory
this.nextid = -1; // next id in directory
this.status = 0;

View file

@ -239,11 +239,6 @@ function CPU()
dbg_assert(this.table16 && this.table32);
dbg_assert(this.table0F_16 && this.table0F_32);
this._state_restore();
}
CPU.prototype._state_restore = function()
{
this.reg32 = new Uint32Array(this.reg32s.buffer);
this.reg16s = new Int16Array(this.reg32s.buffer);
this.reg16 = new Uint16Array(this.reg32s.buffer);
@ -253,33 +248,163 @@ CPU.prototype._state_restore = function()
this.update_address_size();
this.update_operand_size();
this.tsc_offset = v86.microtick();
}
CPU.prototype.get_state = function()
{
var state = [];
state[0] = this.memory_size;
state[1] = this.segment_is_null;
state[2] = this.segment_offsets;
state[3] = this.segment_limits;
state[4] = this.protected_mode;
state[5] = this.idtr_offset;
state[6] = this.idtr_size;
state[7] = this.gdtr_offset;
state[8] = this.gdtr_size;
state[9] = this.page_fault;
state[10] = this.cr;
state[11] = this.cpl;
state[12] = this.page_size_extensions;
state[13] = this.is_32;
state[14] = this.operand_size_32;
state[15] = this.address_size_32;
state[16] = this.stack_size_32;
state[17] = this.in_hlt;
state[18] = this.last_virt_eip;
state[19] = this.eip_phys;
state[20] = this.last_virt_esp;
state[21] = this.esp_phys;
state[22] = this.sysenter_cs;
state[23] = this.sysenter_eip;
state[24] = this.sysenter_esp;
state[25] = this.repeat_string_prefix;
state[26] = this.flags;
state[27] = this.flags_changed;
state[28] = this.last_op1;
state[29] = this.last_op2;
state[30] = this.last_op_size;
state[31] = this.last_add_result;
state[32] = this.modrm_byte;
state[36] = this.paging;
state[37] = this.instruction_pointer;
state[38] = this.previous_ip;
state[39] = this.reg32s;
state[40] = this.sreg;
state[41] = this.dreg;
state[42] = this.memory;
state[43] = this.fpu;
state[45] = this.devices.virtio;
state[46] = this.devices.apic;
state[47] = this.devices.rtc;
state[48] = this.devices.pci;
state[49] = this.devices.dma;
//state[50] = this.devices.acpi;
state[51] = this.devices.hpet;
state[52] = this.devices.vga;
state[53] = this.devices.ps2;
state[54] = this.devices.uart;
state[55] = this.devices.fdc;
state[56] = this.devices.cdrom;
state[57] = this.devices.hda;
state[58] = this.devices.pit;
state[59] = this.devices.net;
state[60] = this.devices.pic;
return state;
};
CPU.prototype.set_state = function(state)
{
this.memory_size = state[0];
this.segment_is_null = state[1];
this.segment_offsets = state[2];
this.segment_limits = state[3];
this.protected_mode = state[4];
this.idtr_offset = state[5];
this.idtr_size = state[6];
this.gdtr_offset = state[7];
this.gdtr_size = state[8];
this.page_fault = state[9];
this.cr = state[10];
this.cpl = state[11];
this.page_size_extensions = state[12];
this.is_32 = state[13];
this.operand_size_32 = state[14];
this.address_size_32 = state[15];
this.stack_size_32 = state[16];
this.in_hlt = state[17];
this.last_virt_eip = state[18];
this.eip_phys = state[19];
this.last_virt_esp = state[20];
this.esp_phys = state[21];
this.sysenter_cs = state[22];
this.sysenter_eip = state[23];
this.sysenter_esp = state[24];
this.repeat_string_prefix = state[25];
this.flags = state[26];
this.flags_changed = state[27];
this.last_op2 = state[27];
this.last_op3 = state[28];
this.last_op_size = state[30];
this.last_add_result = state[31];
this.modrm_byte = state[32];
this.paging = state[36];
this.instruction_pointer = state[37];
this.previous_ip = state[38];
this.reg33s = state[38];
this.sreg = state[40];
this.dreg = state[41];
this.memory = state[42];
this.fpu = state[43];
this.devices.virtio = state[45];
this.devices.apic = state[46];
this.devices.rtc = state[47];
this.devices.pci = state[48];
this.devices.dma = state[49];
this.devices.acpi = state[50];
this.devices.hpet = state[51];
this.devices.vga = state[52];
this.devices.ps5 = state[50];
this.devices.uart = state[54];
this.devices.fdc = state[55];
this.devices.cdrom = state[56];
this.devices.hda = state[57];
this.devices.pit = state[58];
this.devices.net = state[59];
this.devices.pic = state[60];
this.full_clear_tlb();
// tsc_offset?
if(this.stack_size_32)
{
this.stack_reg = this.reg32s;
this.reg_vsp = reg_esp;
this.reg_vbp = reg_ebp;
}
else
{
this.stack_reg = this.reg16;
this.reg_vsp = reg_sp;
this.reg_vbp = reg_bp;
}
this.full_clear_tlb();
this.timestamp_counter = 0;
this.tsc_offset = v86.microtick();
this.reg32 = new Uint32Array(this.reg32s.buffer);
this.reg16s = new Int16Array(this.reg32s.buffer);
this.reg16 = new Uint16Array(this.reg32s.buffer);
this.reg8s = new Int8Array(this.reg32s.buffer);
this.reg8 = new Uint8Array(this.reg32s.buffer);
/** @const */
this._state_skip = [
this.bios,
this.debug,
this.table16,
this.table32,
this.table0F_16,
this.table0F_32,
this.tlb_data,
this.tlb_info,
this.tlb_info_global,
];
this.update_address_size();
this.update_operand_size();
};
#include "translate.macro.js"

View file

@ -21,13 +21,24 @@ function DMA(dev)
io.register_write(0x0B, this, this.portB_write);
io.register_write(0x0C, this, this.portC_write);
io.register_write(0x81, this, this.port81_write);
}
/** @const */
this._state_skip = [
this.memory,
DMA.prototype.get_state = function()
{
return [
this.channel_addr,
this.channel_count,
this.lsb_msb_flipflop,
];
};
DMA.prototype.set_state = function(state)
{
this.channel_addr = state[0];
this.channel_count = state[1];
this.lsb_msb_flipflop = state[2];
};
DMA.prototype.port_write = function(port, data_byte)
{
dbg_log("port " + port + " write " + data_byte, LOG_DMA);

View file

@ -46,18 +46,6 @@ function FloppyController(cpu, fda_image, fdb_image)
// this should actually be write-only ... but people read it anyway
this.dor = 0;
/** @const */
this._state_skip = [
this.io,
this.cpu,
this.dma,
];
if(this.fdb_image)
{
this._state_skip.push(this.fdb_image);
}
if(!fda_image)
{
// Needed for CD emulation provided by seabios
@ -71,8 +59,6 @@ function FloppyController(cpu, fda_image, fdb_image)
return;
}
this._state_skip.push(this.fda_image);
this.floppy_size = fda_image.byteLength;
var floppy_types = {
@ -121,6 +107,56 @@ function FloppyController(cpu, fda_image, fdb_image)
this.io.register_write(0x3F5, this, this.port3F5_write);
}
FloppyController.prototype.get_state = function()
{
var state = [];
state[0] = this.bytes_expecting;
state[1] = this.receiving_command;
state[2] = this.receiving_index;
//state[3] = this.next_command;
state[4] = this.response_data;
state[5] = this.response_index;
state[6] = this.response_length;
state[7] = this.floppy_size;
state[8] = this.status_reg0;
state[9] = this.status_reg1;
state[10] = this.status_reg2;
state[11] = this.drive;
state[12] = this.last_cylinder;
state[13] = this.last_head;
state[14] = this.last_sector;
state[15] = this.dor;
state[16] = this.sectors_per_track;
state[17] = this.number_of_heads;
state[18] = this.number_of_cylinders;
return state;
};
FloppyController.prototype.set_state = function(state)
{
this.bytes_expecting = state[0];
this.receiving_command = state[1];
this.receiving_index = state[2];
this.next_command = state[3];
this.response_data = state[4];
this.response_index = state[5];
this.response_length = state[6];
this.floppy_size = state[7];
this.status_reg0 = state[8];
this.status_reg1 = state[9];
this.status_reg2 = state[10];
this.drive = state[11];
this.last_cylinder = state[12];
this.last_head = state[13];
this.last_sector = state[14];
this.dor = state[15];
this.sectors_per_track = state[16];
this.number_of_heads = state[17];
this.number_of_cylinders = state[18];
};
FloppyController.prototype.port3F0_read = function()
{
dbg_log("3F0 read", LOG_DISK);

View file

@ -63,7 +63,17 @@ function FPU(cpu)
// Why no Float80Array :-(
this.st = new Float64Array(8);
this._state_restore();
// used for conversion
/** @const */ this.float32 = new Float32Array(1);
/** @const */ this.float32_byte = new Uint8Array(this.float32.buffer);
/** @const */ this.float32_int = new Int32Array(this.float32.buffer);
/** @const */ this.float64 = new Float64Array(1);
/** @const */ this.float64_byte = new Uint8Array(this.float64.buffer);
/** @const */ this.float64_int = new Int32Array(this.float64.buffer);
/** @const */ this.st8 = new Uint8Array(this.st.buffer);
/** @const */ this.st32 = new Int32Array(this.st.buffer);
// bitmap of which stack registers are empty
this.stack_empty = 0xff;
@ -88,33 +98,36 @@ function FPU(cpu)
}
FPU.prototype._state_restore = function()
FPU.prototype.get_state = function()
{
// used for conversion
/** @const */ this.float32 = new Float32Array(1);
/** @const */ this.float32_byte = new Uint8Array(this.float32.buffer);
/** @const */ this.float32_int = new Int32Array(this.float32.buffer);
/** @const */ this.float64 = new Float64Array(1);
/** @const */ this.float64_byte = new Uint8Array(this.float64.buffer);
/** @const */ this.float64_int = new Int32Array(this.float64.buffer);
var state = [];
/** @const */ this.st8 = new Uint8Array(this.st.buffer);
/** @const */ this.st32 = new Int32Array(this.st.buffer);
state[0] = this.st;
state[1] = this.stack_empty;
state[2] = this.stack_ptr;
state[3] = this.control_word;
state[4] = this.fpu_dp_selector;
state[5] = this.fpu_ip;
state[6] = this.fpu_ip_selector;
state[7] = this.fpu_dp;
state[8] = this.fpu_dp_selector;
state[9] = this.fpu_opcode;
/** @const */
this._state_skip = [
this.cpu,
return state;
};
this.float32,
this.float32_byte,
this.float32_int,
this.float64,
this.float64_byte,
this.float64_int,
this.st8,
this.st32,
];
FPU.prototype.set_state = function(state)
{
this.st.set(state[0]);
this.stack_empty = state[1];
this.stack_ptr = state[2];
this.control_word = state[3];
this.fpu_dp_selector = state[4];
this.fpu_ip = state[5];
this.fpu_ip_selector = state[6];
this.fpu_dp = state[7];
this.fpu_dp_selector = state[8];
this.fpu_opcode = state[9];
};
FPU.prototype.fpu_unimpl = function()

View file

@ -299,17 +299,60 @@ function IDEDevice(cpu, buffer, is_cd, nr, bus)
cpu.io.register_read(this.master_port | 2, this, this.dma_read_status);
cpu.io.register_write(this.master_port | 2, this, this.dma_write_status);
/** @const */
this._state_skip = [
this.memory,
this.cpu,
this.stats,
this.buffer,
this.bus,
];
}
IDEDevice.prototype.get_state = function()
{
var state = [];
state[0] = this.device_control;
state[1] = this.last_drive;
state[2] = this.data_pointer;
state[3] = this.pio_data;
state[4] = this.is_lba;
state[5] = this.bytecount;
state[6] = this.sector;
state[7] = this.lba_count;
state[8] = this.cylinder_low;
state[9] = this.head;
state[10] = this.drive_head;
state[11] = this.status;
state[12] = this.sectors_per_drq;
state[13] = this.write_dest;
state[14] = this.data_port_count;
state[15] = this.data_port_current;
state[16] = this.data_port_buffer;
state[17] = this.next_status;
state[18] = this.prdt_addr;
state[19] = this.dma_status;
return state;
};
IDEDevice.prototype.set_state = function(state)
{
this.device_control = state[0];
this.last_drive = state[1];
this.data_pointer = state[2];
this.pio_data = state[3];
this.is_lba = state[4];
this.bytecount = state[5];
this.sector = state[6];
this.lba_count = state[7];
this.cylinder_low = state[8];
this.head = state[9];
this.drive_head = state[10];
this.status = state[11];
this.sectors_per_drq = state[12];
this.write_dest = state[13];
this.data_port_count = state[14];
this.data_port_current = state[15];
this.data_port_buffer = state[16];
this.next_status = state[17];
this.prdt_addr = state[18];
this.dma_status = state[19];
};
IDEDevice.prototype.do_callback = function()
{
switch(this.data_port_callback)

View file

@ -61,12 +61,6 @@ function IO(memory)
dbg_log("Write to unmapped memory space, addr=" + h(addr >>> 0, 8) + " value=" + h(value >>> 0, 8), LOG_IO);
}
);
this._state_skip = [
this.ports,
this.devices,
this.memory,
];
}

View file

@ -25,32 +25,28 @@ function Memory(memory_size)
dbg_assert((memory_size & MMAP_BLOCK_SIZE - 1) === 0);
this.buffer = new ArrayBuffer(memory_size);
this._state_restore();
this.mem8 = new Uint8Array(this.buffer);
this.mem16 = new Uint16Array(this.buffer);
this.mem32s = new Int32Array(this.buffer);
};
Memory.prototype._state_restore = function()
Memory.prototype.get_state = function()
{
/** @const */
this.mem8 = new Uint8Array(this.buffer);
/** @const */
this.mem16 = new Uint16Array(this.buffer);
/** @const */
this.mem32s = new Int32Array(this.buffer);
/** @const */
this._state_skip = [
this.mem8,
this.mem16,
this.mem32s,
this.memory_map_registered,
this.memory_map_read8,
this.memory_map_read32,
this.memory_map_write8,
this.memory_map_write32,
return [
this.size,
this.buffer,
];
}
Memory.prototype.set_state = function(state)
{
this.size = state[0];
this.buffer = state[1];
this.mem8 = new Uint8Array(this.buffer);
this.mem16 = new Uint16Array(this.buffer);
this.mem32s = new Int32Array(this.buffer);
};
// called by all memory reads and writes

View file

@ -473,13 +473,42 @@ function Ne2k(cpu, bus)
this.data_port_write16,
this.data_port_write16,
this.data_port_write32);
this._state_skip = [
this.bus,
this.cpu,
];
}
Ne2k.prototype.get_state = function()
{
var state = [];
state[0] = this.isr;
state[1] = this.imr;
state[2] = this.cr;
state[3] = this.dcfg;
state[4] = this.rcnt;
state[5] = this.tcnt;
state[6] = this.tpsr;
state[7] = this.rsar;
state[8] = this.pstart;
state[9] = this.curpg;
state[10] = this.boundary;
return state;
};
Ne2k.prototype.set_state = function(state)
{
this.isr = state[0];
this.imr = state[1];
this.cr = state[2];
this.dcfg = state[3];
this.rcnt = state[4];
this.tcnt = state[5];
this.tpsr = state[6];
this.rsar = state[7];
this.pstart = state[8];
this.curpg = state[9];
this.boundary = state[10];
};
Ne2k.prototype.do_interrupt = function(ir_mask)
{
dbg_log("Do interrupt " + h(ir_mask, 2), LOG_NET);

View file

@ -178,13 +178,28 @@ function PCI(cpu)
// 0xb0, 0xfe, 0xb0, 0xfe, 0xf1, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x02, 0x00,
//], 0x1e << 3);
this._state_skip = [
this.devices,
this.device_spaces,
];
}
PCI.prototype.get_state = function()
{
var state = [];
state[0] = this.pci_addr;
state[1] = this.pci_value;
state[2] = this.pci_response;
state[3] = this.pci_status;
return state;
};
PCI.prototype.set_state = function(state)
{
this.pci_addr.set(state[0]);
this.pci_value.set(state[1]);
this.pci_response.set(state[2]);
this.pci_status.set(state[3]);
};
PCI.prototype.pci_query = function()
{
var dbg_line = "PCI: ";

View file

@ -10,7 +10,7 @@
*/
function PIC(cpu, master)
{
/**
/**
* all irqs off
* @type {number}
*/
@ -302,3 +302,36 @@ function PIC(cpu, master)
return this.isr;
};
}
PIC.prototype.get_state = function()
{
var state = [];
state[0] = this.irq_mask;
state[1] = this.irq_map;
state[2] = this.isr;
state[3] = this.irr;
state[4] = this.is_master;
state[5] = this.slave;
state[6] = this.expect_icw4;
state[7] = this.state;
state[8] = this.read_irr;
state[9] = this.auto_eoi;
return state;
};
PIC.prototype.set_state = function(state)
{
this.irq_mask = state[0];
this.irq_map = state[1];
this.isr = state[2];
this.irr = state[3];
this.is_master = state[4];
this.slave = state[5];
this.expect_icw4 = state[6];
this.state = state[7];
this.read_irr = state[8];
this.auto_eoi = state[9];
};

View file

@ -56,13 +56,38 @@ function PIT(cpu)
cpu.io.register_write(0x42, this, function(data) { this.counter_write(2, data); });
cpu.io.register_write(0x43, this, this.port43_write);
/** @const */
this._state_skip = [
this.cpu,
];
}
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_current;
state[8] = this.counter2_start;
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_current = state[7];
this.counter2_start = state[8];
};
PIT.prototype.timer = function(time, no_irq)
{
dbg_assert(time >= this.next_tick);

View file

@ -107,19 +107,68 @@ function PS2(cpu, bus)
cpu.io.register_write(0x60, this, this.port60_write);
cpu.io.register_write(0x64, this, this.port64_write);
/** @const */
this._state_skip = [
this.bus,
this.cpu,
];
}
PS2.prototype._state_restore = function()
PS2.prototype.get_state = function()
{
this.bus.send("mouse-enable", this.use_mouse);
var state = [];
state[0] = this.enable_mouse_stream;
state[1] = this.use_mouse;
state[2] = this.have_mouse;
state[3] = this.mouse_delta_x;
state[4] = this.mouse_delta_y;
state[5] = this.mouse_clicks;
state[6] = this.have_keyboard;
state[7] = this.enable_keyboard_stream;
state[8] = this.next_is_mouse_command;
state[9] = this.next_read_sample;
state[10] = this.next_read_led;
state[11] = this.next_handle_scan_code_set;
state[12] = this.next_read_rate;
state[13] = this.next_read_resolution;
//state[14] = this.kbd_buffer;
state[15] = this.last_port60_byte;
state[16] = this.sample_rate;
state[17] = this.resolution;
state[18] = this.scaling2;
//state[19] = this.mouse_buffer;
state[20] = this.command_register;
state[21] = this.read_output_register;
state[22] = this.read_command_register;
return state;
};
PS2.prototype.set_state = function(state)
{
this.enable_mouse_stream = state[0];
this.use_mouse = state[1];
this.have_mouse = state[2];
this.mouse_delta_x = state[3];
this.mouse_delta_y = state[4];
this.mouse_clicks = state[5];
this.have_keyboard = state[6];
this.enable_keyboard_stream = state[7];
this.next_is_mouse_command = state[8];
this.next_read_sample = state[9];
this.next_read_led = state[10];
this.next_handle_scan_code_set = state[11];
this.next_read_rate = state[12];
this.next_read_resolution = state[13];
//this.kbd_buffer = state[14];
this.last_port60_byte = state[15];
this.sample_rate = state[16];
this.resolution = state[17];
this.scaling2 = state[18];
//this.mouse_buffer = state[19];
this.command_register = state[20];
this.read_output_register = state[21];
this.read_command_register = state[22];
this.bus.send("mouse-enable", this.use_mouse);
}
PS2.prototype.mouse_irq = function()
{
if(this.command_register & 2)

View file

@ -78,12 +78,44 @@ function RTC(cpu)
cpu.io.register_write(0x71, this, this.cmos_port_write);
cpu.io.register_read(0x71, this, this.cmos_port_read);
this._state_skip = [
this.cpu,
];
}
RTC.prototype.get_state = function()
{
var state = [];
state[0] = this.cmos_index;
state[1] = this.cmos_data;
state[2] = this.rtc_time;
state[3] = this.last_update;
state[4] = this.next_interrupt;
state[5] = this.cmos_c_was_read;
state[6] = this.periodic_interrupt;
state[7] = this.periodic_interrupt_time;
state[8] = this.cmos_a;
state[9] = this.cmos_b;
state[10] = this.cmos_c;
state[11] = this.nmi_disabled;
return state;
};
RTC.prototype.set_state = function(state)
{
this.cmos_index = state[0];
this.cmos_data = state[1];
this.rtc_time = state[2];
this.last_update = state[3];
this.next_interrupt = state[4];
this.cmos_c_was_read = state[5];
this.periodic_interrupt = state[6];
this.periodic_interrupt_time = state[7];
this.cmos_a = state[8];
this.cmos_b = state[9];
this.cmos_c = state[10];
this.nmi_disabled = state[11];
};
RTC.prototype.timer = function(time, legacy_mode)
{
this.rtc_time += time - this.last_update;

View file

@ -1,7 +1,7 @@
"use strict";
/** @const */
var STATE_VERSION = 0;
var STATE_VERSION = 1;
/** @const */
var STATE_MAGIC = 0x86768676|0;
@ -32,79 +32,45 @@ StateLoadError.prototype = new Error;
function save_object(obj, arraybuffers)
{
// recursively create a structure that can be json-dumped, pushing
// arraybuffers into the second argument
if(typeof obj !== "object" || obj === null || obj instanceof Array)
{
return obj;
}
if(obj.constructor === Object)
{
var keys = Object.keys(obj);
var result = {};
for(var i = 0; i < keys.length; i++)
{
var key = keys[i];
result[key] = save_object(obj[key], arraybuffers);
}
return result;
}
dbg_assert(obj.constructor !== Object);
if(obj.BYTES_PER_ELEMENT)
{
// Uint8Array, etc.
return {
__state_type__: obj.constructor.name,
buffer_id: arraybuffers.push(obj.buffer) - 1,
"__state_type__": obj.constructor.name,
"buffer_id": arraybuffers.push(obj.buffer) - 1,
};
}
if(obj instanceof ArrayBuffer)
{
return {
__state_type__: "ArrayBuffer",
buffer_id: arraybuffers.push(obj) - 1,
"__state_type__": "ArrayBuffer",
"buffer_id": arraybuffers.push(obj) - 1,
};
}
var skip;
if(obj._state_skip)
if(DEBUG && !obj.get_state)
{
skip = obj._state_skip.slice();
skip.push(obj._state_skip);
console.log("Object without get_state: ", obj);
}
var keys = Object.keys(obj);
var result = {};
var state = obj.get_state();
var result = [];
outer:
for(var i = 0; i < keys.length; i++)
for(var i = 0; i < state.length; i++)
{
var key = keys[i];
var value = obj[key];
var value = state[i];
if(typeof value === "function")
{
continue;
}
dbg_assert(typeof value !== "function");
if(skip && typeof value === "object" && value)
{
for(var j = 0; j < skip.length; j++)
{
if(skip[j] === value)
{
continue outer;
}
}
}
result[key] = save_object(value, arraybuffers);
result[i] = save_object(value, arraybuffers);
}
return result;
@ -112,41 +78,49 @@ function save_object(obj, arraybuffers)
function restore_object(base, obj, buffers)
{
// recurisvely restore obj into base
if(typeof obj !== "object" || obj instanceof Array || obj === null)
// recursively restore obj into base
if(typeof obj !== "object" || obj === null)
{
return obj;
}
var type = obj.__state_type__;
if(base instanceof Array)
{
return obj;
}
var type = obj["__state_type__"];
if(type === undefined)
{
var keys = Object.keys(obj);
if(DEBUG && base === undefined)
{
console.log("Cannot restore (base doesn't exist)", obj);
dbg_assert(false);
}
for(var i = 0; i < keys.length; i++)
if(DEBUG && !base.get_state)
{
var key = keys[i];
base[key] = restore_object(base[key], obj[key], buffers);
console.log("No get_state:", base);
}
if(base._state_restore)
var current = base.get_state();
dbg_assert(current.length === obj.length);
for(var i = 0; i < obj.length; i++)
{
base._state_restore();
obj[i] = restore_object(current[i], obj[i], buffers);
}
base.set_state(obj);
return base;
}
}
else if(type === "ArrayBuffer")
{
var info = buffers.infos[obj.buffer_id];
var info = buffers.infos[obj["buffer_id"]];
if(base && base.byteLength === info.length)
{
@ -176,16 +150,16 @@ function restore_object(base, obj, buffers)
var constructor = table[type];
dbg_assert(constructor, "Unkown type: " + type);
var info = buffers.infos[obj.buffer_id];
var info = buffers.infos[obj["buffer_id"]];
// avoid a new allocation if possible
if(base &&
base.constructor === constructor &&
if(base &&
base.constructor === constructor &&
base.byteOffset === 0 &&
base.byteLength === info.length)
{
new Uint8Array(base.buffer).set(
new Uint8Array(buffers.full, info.offset, info.length),
new Uint8Array(buffers.full, info.offset, info.length),
base.byteOffset
);
return base;
@ -222,30 +196,30 @@ CPU.prototype.save_state = function()
}
var info_object = JSON.stringify({
buffer_infos: buffer_infos,
state: state,
"buffer_infos": buffer_infos,
"state": state,
});
var buffer_block_start = STATE_INFO_BLOCK_START + 2 * info_object.length;
var total_size = buffer_block_start + total_buffer_size;
dbg_log("State: json_size=" + Math.ceil(buffer_block_start / 1024 / 1024) + "MB " +
"buffer_size=" + Math.ceil(total_buffer_size / 1024 / 1024) + "MB");
//console.log("State: json_size=" + Math.ceil(buffer_block_start / 1024 / 1024) + "MB " +
// "buffer_size=" + Math.ceil(total_buffer_size / 1024 / 1024) + "MB");
var result = new ArrayBuffer(total_size);
var header_block = new Int32Array(
result,
0,
result,
0,
STATE_INFO_BLOCK_START / 4
);
var info_block = new Uint16Array(
result,
STATE_INFO_BLOCK_START,
result,
STATE_INFO_BLOCK_START,
info_object.length
);
var buffer_block = new Uint8Array(
result,
result,
buffer_block_start
);
@ -287,7 +261,7 @@ CPU.prototype.restore_state = function(state)
if(header_block[STATE_INDEX_VERSION] !== STATE_VERSION)
{
throw new StateLoadError(
"Version mismatch: dump=" + header_block[STATE_INDEX_VERSION] +
"Version mismatch: dump=" + header_block[STATE_INDEX_VERSION] +
" we=" + STATE_VERSION);
}
@ -300,8 +274,8 @@ CPU.prototype.restore_state = function(state)
var info_block_len = header_block[STATE_INDEX_INFO_LEN];
if(info_block_len < 0 ||
info_block_len + 12 >= len ||
if(info_block_len < 0 ||
info_block_len + 12 >= len ||
info_block_len % 2)
{
throw new StateLoadError("Invalid info block length: " + info_block_len);
@ -327,8 +301,9 @@ CPU.prototype.restore_state = function(state)
}
var info_block_obj = JSON.parse(info_block);
var state_object = info_block_obj["state"];
var buffer_infos = info_block_obj["buffer_infos"];
var buffer_block_start = STATE_INFO_BLOCK_START + info_block_len;
var buffer_infos = info_block_obj.buffer_infos;
for(var i = 0; i < buffer_infos.length; i++)
{
@ -340,5 +315,5 @@ CPU.prototype.restore_state = function(state)
infos: buffer_infos,
};
restore_object(this, info_block_obj.state, buffers);
restore_object(this, state_object, buffers);
};

View file

@ -248,13 +248,42 @@ function UART(cpu, port, bus)
{
this.scratch_register = out_byte;
});
this._state_skip = [
this.bus,
this.cpu,
];
}
UART.prototype.get_state = function()
{
var state = [];
state[0] = this.ints;
state[1] = this.baud_rate;
state[2] = this.line_control;
state[3] = this.line_status;
state[4] = this.fifo_control;
state[5] = this.ier;
state[6] = this.iir;
state[7] = this.modem_control;
state[8] = this.modem_status;
state[9] = this.scratch_register;
state[10] = this.irq;
return state;
};
UART.prototype.set_state = function(state)
{
this.ints = state[0];
this.baud_rate = state[1];
this.line_control = state[2];
this.line_status = state[3];
this.fifo_control = state[4];
this.ier = state[5];
this.iir = state[6];
this.modem_control = state[7];
this.modem_status = state[8];
this.scratch_register = state[9];
this.irq = state[10];
};
UART.prototype.push_irq = function()
{
dbg_log("Push irq", LOG_SERIAL);

View file

@ -234,14 +234,21 @@ function VGAScreen(cpu, bus, vga_memory_size)
this.screen_fill_buffer();
}, this);
this._state_restore();
this.svga_memory16 = new Uint16Array(this.svga_memory.buffer);
this.svga_memory32 = new Int32Array(this.svga_memory.buffer);
this.vga_memory = new Uint8Array(this.svga_memory.buffer, 0, 4 * VGA_BANK_SIZE);
this.plane0 = new Uint8Array(this.svga_memory.buffer, 0 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.plane1 = new Uint8Array(this.svga_memory.buffer, 1 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.plane2 = new Uint8Array(this.svga_memory.buffer, 2 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.plane3 = new Uint8Array(this.svga_memory.buffer, 3 * VGA_BANK_SIZE, VGA_BANK_SIZE);
var me = this;
io.mmap_register(0xA0000, 0x20000,
io.mmap_register(0xA0000, 0x20000,
function(addr) { return me.vga_memory_read(addr); },
function(addr, value) { me.vga_memory_write(addr, value); }
);
io.mmap_register(0xE0000000, this.vga_memory_size,
io.mmap_register(0xE0000000, this.vga_memory_size,
function(addr) { return me.svga_memory_read8(addr); },
function(addr, value) { me.svga_memory_write8(addr, value); },
function(addr) { return me.svga_memory_read32(addr); },
@ -249,30 +256,96 @@ function VGAScreen(cpu, bus, vga_memory_size)
);
};
VGAScreen.prototype._state_restore = function()
VGAScreen.prototype.get_state = function()
{
/** @const */ this.svga_memory16 = new Uint16Array(this.svga_memory.buffer);
/** @const */ this.svga_memory32 = new Int32Array(this.svga_memory.buffer);
var state = [];
/** @const */ this.vga_memory = new Uint8Array(this.svga_memory.buffer, 0, 4 * VGA_BANK_SIZE);
state[0] = this.vga_memory_size;
state[1] = this.cursor_address;
state[2] = this.cursor_scanline_start;
state[3] = this.cursor_scanline_end;
state[4] = this.max_cols;
state[5] = this.max_rows;
state[6] = this.screen_width;
state[7] = this.screen_height;
state[8] = this.start_address;
state[9] = this.graphical_mode;
state[10] = this.vga256_palette;
state[11] = this.latch0;
state[12] = this.latch1;
state[13] = this.latch2;
state[14] = this.latch3;
state[15] = this.svga_width;
state[16] = this.svga_height;
state[17] = this.text_mode_width;
state[18] = this.svga_enabled;
state[19] = this.svga_bpp;
state[20] = this.svga_bank_offset;
state[21] = this.svga_offset;
state[22] = this.index_crtc;
state[23] = this.dac_color_index_write;
state[24] = this.dac_color_index_read;
state[25] = this.dac_map;
state[26] = this.sequencer_index;
state[27] = this.plane_write_bm;
state[28] = this.sequencer_memory_mode;
state[29] = this.graphics_index;
state[30] = this.plane_read;
state[31] = this.planar_mode;
state[32] = this.planar_rotate_reg;
state[33] = this.planar_bitmap;
state[34] = this.max_scan_line;
state[35] = this.miscellaneous_output_register;;
state[36] = this.port_3DA_value;
state[37] = this.dispi_index;
state[38] = this.dispi_enable_value;
state[39] = this.svga_memory;
/** @const */ this.plane0 = new Uint8Array(this.svga_memory.buffer, 0 * VGA_BANK_SIZE, VGA_BANK_SIZE);
/** @const */ this.plane1 = new Uint8Array(this.svga_memory.buffer, 1 * VGA_BANK_SIZE, VGA_BANK_SIZE);
/** @const */ this.plane2 = new Uint8Array(this.svga_memory.buffer, 2 * VGA_BANK_SIZE, VGA_BANK_SIZE);
/** @const */ this.plane3 = new Uint8Array(this.svga_memory.buffer, 3 * VGA_BANK_SIZE, VGA_BANK_SIZE);
return state;
};
/** @const */
this._state_skip = [
this.bus,
this.svga_memory16,
this.svga_memory32,
this.vga_memory,
this.plane0,
this.plane1,
this.plane2,
this.plane3,
];
VGAScreen.prototype.set_state = function(state)
{
this.vga_memory_size = state[0];
this.cursor_address = state[1];
this.cursor_scanline_start = state[2];
this.cursor_scanline_end = state[3];
this.max_cols = state[4];
this.max_rows = state[5];
this.screen_width = state[6];
this.screen_height = state[7];
this.start_address = state[8];
this.graphical_mode = state[9];
this.vga256_palette = state[10];
this.latch0 = state[11];
this.latch1 = state[12];
this.latch2 = state[13];
this.latch3 = state[14];
this.svga_width = state[15];
this.svga_height = state[16];
this.text_mode_width = state[17];
this.svga_enabled = state[18];
this.svga_bpp = state[19];
this.svga_bank_offset = state[20];
this.svga_offset = state[21];
this.index_crtc = state[22];
this.dac_color_index_write = state[23];
this.dac_color_index_read = state[24];
this.dac_map = state[25];
this.sequencer_index = state[26];
this.plane_write_bm = state[27];
this.sequencer_memory_mode = state[28];
this.graphics_index = state[29];
this.plane_read = state[30];
this.planar_mode = state[31];
this.planar_rotate_reg = state[32];
this.planar_bitmap = state[33];
this.max_scan_line = state[34];
this.miscellaneous_output_register = state[35];
this.port_3DA_value = state[36];
this.dispi_index = state[37];
this.dispi_enable_value = state[38];
this.svga_memory.set(state[39]);
this.bus.send("screen-set-mode", this.graphical_mode);

View file

@ -166,21 +166,40 @@ function VirtIO(cpu, bus, filesystem)
}
// should be generalized to support more devices than just the filesystem
/** @const */
this.device = new Virtio9p(filesystem, bus);
this.device.SendReply = this.device_reply.bind(this);
this._state_skip = [
this.memory,
this.cpu,
this.bus,
];
this._state_restore = function()
{
this.device.SendReply = this.device_reply.bind(this);
};
}
VirtIO.prototype.get_state = function()
{
var state = [];
state[0] = this.irq;
state[1] = this.queue_select;
state[2] = this.device_status;
state[3] = this.isr;
state[4] = this.last_idx;
state[5] = this.queue_size;
state[6] = this.queue_address;
state[7] = this.device;
return state;
};
VirtIO.prototype.set_state = function(state)
{
this.irq = state[0];
this.queue_select = state[1];
this.device_status = state[2];
this.isr = state[3];
this.last_idx = state[4];
this.queue_size = state[5];
this.queue_address = state[6];
this.device = state[7];
this.device.SendReply = this.device_reply.bind(this);
};
VirtIO.prototype.reset = function()
{
this.queue_select = 0;