2013-11-06 01:12:55 +01:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
function Memory(buffer, memory_size)
|
|
|
|
{
|
2014-09-29 07:10:47 +02:00
|
|
|
this.mem8 = new Uint8Array(buffer);
|
|
|
|
this.mem16 = new Uint16Array(buffer);
|
|
|
|
this.mem32s = new Int32Array(buffer);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
|
|
|
this.buffer = buffer;
|
|
|
|
|
2014-01-08 03:19:02 +01:00
|
|
|
this.size = memory_size;
|
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
// this only supports a 32 bit address space
|
2014-06-15 22:25:17 +02:00
|
|
|
var size = 1 << (32 - MMAP_BLOCK_BITS);
|
|
|
|
var memory_map_registered = new Uint8Array(size);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2013-12-20 22:04:58 +01:00
|
|
|
// managed by IO() in io.js
|
|
|
|
this.memory_map_registered = memory_map_registered;
|
2014-07-14 21:33:40 +02:00
|
|
|
this.memory_map_read8 = [];
|
|
|
|
this.memory_map_write8 = [];
|
|
|
|
this.memory_map_read32 = [];
|
|
|
|
this.memory_map_write32 = [];
|
2013-11-25 12:18:29 +01:00
|
|
|
|
2014-05-08 22:47:56 +02:00
|
|
|
// use by dynamic translator
|
2014-06-17 01:38:39 +02:00
|
|
|
if(OP_TRANSLATION) this.mem_page_infos = new Uint8Array(1 << 20);
|
2014-05-08 22:47:56 +02:00
|
|
|
|
2013-11-25 12:18:29 +01:00
|
|
|
dbg_assert((memory_size & MMAP_BLOCK_SIZE - 1) === 0);
|
2014-09-29 07:10:47 +02:00
|
|
|
|
|
|
|
/** @const */
|
|
|
|
this._state_skip = [
|
|
|
|
"mem8",
|
|
|
|
"mem16",
|
|
|
|
"mem32s",
|
|
|
|
"memory_map_registered",
|
|
|
|
"memory_map_read8",
|
|
|
|
"memory_map_read32",
|
|
|
|
"memory_map_write8",
|
|
|
|
"memory_map_write32",
|
|
|
|
];
|
|
|
|
};
|
|
|
|
|
|
|
|
Memory.prototype._state_restore = function()
|
|
|
|
{
|
|
|
|
this.mem8 = new Uint8Array(this.buffer);
|
|
|
|
this.mem16 = new Uint16Array(this.buffer);
|
|
|
|
this.mem32s = new Int32Array(this.buffer);
|
|
|
|
};
|
2014-02-19 05:50:49 +01:00
|
|
|
|
|
|
|
// called by all memory reads and writes
|
|
|
|
Memory.prototype.debug_write = function(addr, size, value)
|
|
|
|
{
|
|
|
|
if(!DEBUG)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
//dbg_assert(typeof value === "number" && !isNaN(value));
|
|
|
|
this.debug_read(addr, size, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @param {boolean=} is_write */
|
|
|
|
Memory.prototype.debug_read = function(addr, size, is_write)
|
|
|
|
{
|
|
|
|
if(!DEBUG)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_assert(typeof addr === "number");
|
|
|
|
dbg_assert(!isNaN(addr));
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Memory.prototype.mmap_read8 = function(addr)
|
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
return this.memory_map_read8[addr >>> MMAP_BLOCK_BITS](addr);
|
|
|
|
};
|
2014-02-19 05:50:49 +01:00
|
|
|
|
|
|
|
Memory.prototype.mmap_write8 = function(addr, value)
|
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
this.memory_map_write8[addr >>> MMAP_BLOCK_BITS](addr, value);
|
|
|
|
};
|
|
|
|
|
|
|
|
Memory.prototype.mmap_read16 = function(addr)
|
|
|
|
{
|
|
|
|
return this.mmap_read8(addr) | this.mmap_read8(addr + 1) << 8;
|
|
|
|
};
|
|
|
|
|
|
|
|
Memory.prototype.mmap_write16 = function(addr, value)
|
|
|
|
{
|
|
|
|
this.mmap_write8(addr, value & 0xff);
|
|
|
|
this.mmap_write8(addr + 1, value >> 8 & 0xff);
|
|
|
|
};
|
2014-02-19 05:50:49 +01:00
|
|
|
|
|
|
|
Memory.prototype.mmap_read32 = function(addr)
|
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
2014-02-19 05:50:49 +01:00
|
|
|
|
2014-07-14 21:33:40 +02:00
|
|
|
return this.memory_map_read32[aligned_addr](addr);
|
2014-02-19 05:50:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Memory.prototype.mmap_write32 = function(addr, value)
|
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
var aligned_addr = addr >>> MMAP_BLOCK_BITS;
|
2014-02-19 05:50:49 +01:00
|
|
|
|
2014-07-14 21:33:40 +02:00
|
|
|
this.memory_map_write32[aligned_addr](addr, value);
|
2014-02-19 05:50:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.read8 = function(addr)
|
|
|
|
{
|
|
|
|
this.debug_read(addr, 1);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
if(this.memory_map_registered[addr >>> MMAP_BLOCK_BITS])
|
|
|
|
{
|
|
|
|
return this.mmap_read8(addr);
|
|
|
|
}
|
|
|
|
else
|
2013-11-25 12:18:29 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
return this.mem8[addr];
|
2013-12-05 23:12:48 +01:00
|
|
|
}
|
2014-02-19 05:50:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.read16 = function(addr)
|
|
|
|
{
|
|
|
|
this.debug_read(addr, 2);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
if(this.memory_map_registered[addr >>> MMAP_BLOCK_BITS])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
return this.mmap_read16(addr);
|
2013-12-05 23:12:48 +01:00
|
|
|
}
|
2014-02-19 05:50:49 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.mem8[addr] | this.mem8[addr + 1] << 8;
|
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.read_aligned16 = function(addr)
|
|
|
|
{
|
|
|
|
this.debug_read(addr << 1, 2);
|
|
|
|
|
|
|
|
if(this.memory_map_registered[addr >>> MMAP_BLOCK_BITS - 1])
|
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
return this.mmap_read16(addr << 1);
|
2014-02-19 05:50:49 +01:00
|
|
|
}
|
|
|
|
else
|
2013-11-25 12:18:29 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
return this.mem16[addr];
|
2013-12-05 23:12:48 +01:00
|
|
|
}
|
2014-02-19 05:50:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.read32s = function(addr)
|
|
|
|
{
|
|
|
|
this.debug_read(addr, 4);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
if(this.memory_map_registered[addr >>> MMAP_BLOCK_BITS])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
return this.mmap_read32(addr);
|
2013-12-05 23:12:48 +01:00
|
|
|
}
|
2014-02-19 05:50:49 +01:00
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
return this.mem8[addr] | this.mem8[addr + 1] << 8 |
|
|
|
|
this.mem8[addr + 2] << 16 | this.mem8[addr + 3] << 24;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.read_aligned32 = function(addr)
|
|
|
|
{
|
|
|
|
this.debug_read(addr << 2, 4);
|
|
|
|
|
|
|
|
if(this.memory_map_registered[addr >>> MMAP_BLOCK_BITS - 2])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
return this.mmap_read32(addr << 2);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return this.mem32s[addr];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
* @param value {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.write8 = function(addr, value)
|
|
|
|
{
|
|
|
|
this.debug_write(addr, 1, value);
|
|
|
|
|
2014-05-08 22:47:56 +02:00
|
|
|
var page = addr >>> MMAP_BLOCK_BITS;
|
2014-06-17 01:38:39 +02:00
|
|
|
|
|
|
|
if(OP_TRANSLATION) this.mem_page_infos[page] |= MEM_PAGE_WRITTEN;
|
2014-05-08 22:47:56 +02:00
|
|
|
|
|
|
|
if(this.memory_map_registered[page])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mmap_write8(addr, value);
|
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mem8[addr] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
* @param value {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.write16 = function(addr, value)
|
|
|
|
{
|
|
|
|
this.debug_write(addr, 2, value);
|
|
|
|
|
2014-05-08 22:47:56 +02:00
|
|
|
var page = addr >>> MMAP_BLOCK_BITS;
|
2014-06-17 01:38:39 +02:00
|
|
|
|
|
|
|
if(OP_TRANSLATION)
|
|
|
|
{
|
|
|
|
this.mem_page_infos[page] |= MEM_PAGE_WRITTEN;
|
|
|
|
this.mem_page_infos[addr + 1 >>> MMAP_BLOCK_BITS] |= MEM_PAGE_WRITTEN;
|
|
|
|
}
|
2014-05-08 22:47:56 +02:00
|
|
|
|
|
|
|
if(this.memory_map_registered[page])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
this.mmap_write16(addr, value);
|
2014-02-19 05:50:49 +01:00
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mem8[addr] = value;
|
|
|
|
this.mem8[addr + 1] = value >> 8;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
* @param value {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.write_aligned16 = function(addr, value)
|
|
|
|
{
|
|
|
|
this.debug_write(addr << 1, 2, value);
|
|
|
|
|
2014-05-08 22:47:56 +02:00
|
|
|
var page = addr >>> MMAP_BLOCK_BITS - 1;
|
2014-06-17 01:38:39 +02:00
|
|
|
|
|
|
|
if(OP_TRANSLATION) this.mem_page_infos[page] |= MEM_PAGE_WRITTEN;
|
2014-05-08 22:47:56 +02:00
|
|
|
|
|
|
|
if(this.memory_map_registered[page])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-07-14 21:33:40 +02:00
|
|
|
this.mmap_write16(addr << 1, value);
|
2014-02-19 05:50:49 +01:00
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mem16[addr] = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param addr {number}
|
|
|
|
* @param value {number}
|
|
|
|
*/
|
|
|
|
Memory.prototype.write32 = function(addr, value)
|
|
|
|
{
|
|
|
|
this.debug_write(addr, 4, value);
|
|
|
|
|
2014-05-08 22:47:56 +02:00
|
|
|
var page = addr >>> MMAP_BLOCK_BITS;
|
2014-06-17 01:38:39 +02:00
|
|
|
|
|
|
|
if(OP_TRANSLATION)
|
|
|
|
{
|
|
|
|
this.mem_page_infos[page] |= MEM_PAGE_WRITTEN;
|
|
|
|
this.mem_page_infos[addr + 3 >>> MMAP_BLOCK_BITS] |= MEM_PAGE_WRITTEN;
|
|
|
|
}
|
2014-05-08 22:47:56 +02:00
|
|
|
|
|
|
|
if(this.memory_map_registered[page])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mmap_write32(addr, value);
|
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mem8[addr] = value;
|
|
|
|
this.mem8[addr + 1] = value >> 8;
|
|
|
|
this.mem8[addr + 2] = value >> 16;
|
|
|
|
this.mem8[addr + 3] = value >> 24;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Memory.prototype.write_aligned32 = function(addr, value)
|
|
|
|
{
|
|
|
|
this.debug_write(addr << 2, 4, value);
|
|
|
|
|
2014-05-08 22:47:56 +02:00
|
|
|
var page = addr >>> MMAP_BLOCK_BITS - 2;
|
2014-06-17 01:38:39 +02:00
|
|
|
|
|
|
|
if(OP_TRANSLATION) this.mem_page_infos[page] |= MEM_PAGE_WRITTEN;
|
2014-05-08 22:47:56 +02:00
|
|
|
|
|
|
|
if(this.memory_map_registered[page])
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mmap_write32(addr << 2, value);
|
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mem32s[addr] = value;
|
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
/**
|
|
|
|
* @param offset {number}
|
|
|
|
* @param blob {Array.<number>}
|
|
|
|
*/
|
|
|
|
Memory.prototype.write_blob = function(blob, offset)
|
|
|
|
{
|
|
|
|
dbg_assert(blob && blob.length);
|
2014-05-08 22:47:56 +02:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
this.mem8.set(blob, offset);
|
2014-05-08 22:47:56 +02:00
|
|
|
|
|
|
|
var page = offset >>> 12,
|
|
|
|
end = (offset + blob) >>> 12;
|
|
|
|
|
2014-06-17 01:38:39 +02:00
|
|
|
if(OP_TRANSLATION)
|
2014-05-08 22:47:56 +02:00
|
|
|
{
|
2014-06-17 01:38:39 +02:00
|
|
|
for(; page <= end; page++)
|
|
|
|
{
|
|
|
|
this.mem_page_infos[page] |= MEM_PAGE_WRITTEN;
|
|
|
|
}
|
2014-05-08 22:47:56 +02:00
|
|
|
}
|
2014-02-19 05:50:49 +01:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
/**
|
|
|
|
* zero byte terminated string
|
|
|
|
*/
|
|
|
|
Memory.prototype.read_string = function(addr)
|
|
|
|
{
|
|
|
|
var str = "",
|
|
|
|
data_byte;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-02-19 05:50:49 +01:00
|
|
|
while(data_byte = this.read8(addr))
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-02-19 05:50:49 +01:00
|
|
|
str += String.fromCharCode(data_byte);
|
|
|
|
addr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
};
|
|
|
|
|
|
|
|
Memory.prototype.write_string = function(str, addr)
|
|
|
|
{
|
|
|
|
for(var i = 0; i < str.length; i++)
|
|
|
|
{
|
|
|
|
this.write8(addr + i, str.charCodeAt(i));
|
|
|
|
}
|
|
|
|
};
|
2014-09-29 07:10:47 +02:00
|
|
|
|