diff --git a/lib/9p.js b/lib/9p.js index dab51053..820fd644 100644 --- a/lib/9p.js +++ b/lib/9p.js @@ -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}; } diff --git a/lib/filesystem.js b/lib/filesystem.js index 441d306a..16ee318b 100755 --- a/lib/filesystem.js +++ b/lib/filesystem.js @@ -46,6 +46,7 @@ var STATUS_UNLINKED = 0x4; /** @constructor */ function FS(baseurl) { + /** @type {Array.} */ 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; diff --git a/src/cpu.macro.js b/src/cpu.macro.js index 70d669c0..58411aea 100644 --- a/src/cpu.macro.js +++ b/src/cpu.macro.js @@ -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" diff --git a/src/dma.js b/src/dma.js index 25c39caf..adfc9013 100644 --- a/src/dma.js +++ b/src/dma.js @@ -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); diff --git a/src/floppy.js b/src/floppy.js index d9050ed8..0e4f16e6 100644 --- a/src/floppy.js +++ b/src/floppy.js @@ -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); diff --git a/src/fpu.js b/src/fpu.js index 266acbe9..51aedb2f 100644 --- a/src/fpu.js +++ b/src/fpu.js @@ -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() diff --git a/src/ide.js b/src/ide.js index 252debe7..49b02840 100644 --- a/src/ide.js +++ b/src/ide.js @@ -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) diff --git a/src/io.js b/src/io.js index 76619eef..c7171f93 100644 --- a/src/io.js +++ b/src/io.js @@ -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, - ]; } diff --git a/src/memory.js b/src/memory.js index 21934b48..ca13f265 100644 --- a/src/memory.js +++ b/src/memory.js @@ -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 diff --git a/src/ne2k.js b/src/ne2k.js index 24fbf2c0..54c03400 100644 --- a/src/ne2k.js +++ b/src/ne2k.js @@ -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); diff --git a/src/pci.js b/src/pci.js index bc081d46..745d4a13 100644 --- a/src/pci.js +++ b/src/pci.js @@ -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: "; diff --git a/src/pic.js b/src/pic.js index 18a3fdfc..1d7f156c 100644 --- a/src/pic.js +++ b/src/pic.js @@ -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]; +}; + diff --git a/src/pit.js b/src/pit.js index 3aa39b1b..b1141a5a 100644 --- a/src/pit.js +++ b/src/pit.js @@ -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); diff --git a/src/ps2.js b/src/ps2.js index bee446d7..58a08202 100644 --- a/src/ps2.js +++ b/src/ps2.js @@ -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) diff --git a/src/rtc.js b/src/rtc.js index 36623cbc..482c3e57 100644 --- a/src/rtc.js +++ b/src/rtc.js @@ -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; diff --git a/src/state.js b/src/state.js index 6bb55525..6ea0074e 100644 --- a/src/state.js +++ b/src/state.js @@ -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); }; diff --git a/src/uart.js b/src/uart.js index 75ef3f6c..7c12d0c3 100644 --- a/src/uart.js +++ b/src/uart.js @@ -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); diff --git a/src/vga.js b/src/vga.js index 3f1f61e4..d1fe1648 100644 --- a/src/vga.js +++ b/src/vga.js @@ -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); diff --git a/src/virtio.js b/src/virtio.js index 36f2c93b..9015b01e 100644 --- a/src/virtio.js +++ b/src/virtio.js @@ -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;