487 lines
12 KiB
JavaScript
487 lines
12 KiB
JavaScript
"use strict";
|
|
|
|
/**
|
|
* @constructor
|
|
*/
|
|
function Memory(buffer, memory_size)
|
|
{
|
|
var int8array = new Uint8Array(buffer),
|
|
int16array = new Uint16Array(buffer),
|
|
int8sarray = new Int8Array(buffer),
|
|
int32sarray = new Int32Array(buffer);
|
|
|
|
this.mem8 = int8array;
|
|
this.mem8s = int8sarray;
|
|
this.mem32s = int32sarray;
|
|
|
|
this.buffer = buffer;
|
|
|
|
// debug function called by all memory reads and writes
|
|
|
|
function debug_write(addr, size, value)
|
|
{
|
|
if(!DEBUG)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//dbg_assert(typeof value === "number" && !isNaN(value));
|
|
debug_read(addr, size, true);
|
|
}
|
|
|
|
/** @param {boolean=} is_write */
|
|
function debug_read(addr, size, is_write)
|
|
{
|
|
if(!DEBUG)
|
|
{
|
|
return;
|
|
}
|
|
|
|
dbg_assert(typeof addr === "number");
|
|
dbg_assert(!isNaN(addr));
|
|
|
|
if((addr >= memory_size || addr < 0) && !memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
dbg_log("Read from unmapped memory space, addr=" + h(addr, 8) /*+ " at " + h(instruction_pointer, 8)*/, LOG_IO);
|
|
}
|
|
|
|
//dbg_assert(memory_map_registered[addr >>> MMAP_BLOCK_SIZE]);
|
|
};
|
|
|
|
this.dump_all = function(start, end)
|
|
{
|
|
start = start || 0;
|
|
end = end || 0x100000;
|
|
|
|
|
|
// textarea method: (slow)
|
|
//var result_string = "";
|
|
|
|
//for(var i = start; i < start + end; i++)
|
|
//{
|
|
// result_string += String.fromCharCode(int8array[i]);
|
|
//}
|
|
|
|
//dump_text(btoa(result_string));
|
|
|
|
// save as method:
|
|
dump_file(buffer.slice(start, end), "memory.bin");
|
|
|
|
};
|
|
|
|
this.dump = function(addr, length)
|
|
{
|
|
length = length || 4 * 0x10;
|
|
var line, byt;
|
|
|
|
for(var i = 0; i < length >> 4; i++)
|
|
{
|
|
line = h(addr + (i << 4), 5) + " ";
|
|
|
|
for(var j = 0; j < 0x10; j++)
|
|
{
|
|
byt = this.read8(addr + (i << 4) + j);
|
|
line += h(byt, 2) + " ";
|
|
}
|
|
|
|
line += " ";
|
|
|
|
for(j = 0; j < 0x10; j++)
|
|
{
|
|
byt = this.read8(addr + (i << 4) + j);
|
|
line += (byt < 33 || byt > 126) ? "." : String.fromCharCode(byt);
|
|
}
|
|
|
|
dbg_log(line);
|
|
}
|
|
};
|
|
|
|
this.print_memory_map = function()
|
|
{
|
|
var width = 0x80,
|
|
height = 0x10,
|
|
block_size = memory_size / width / height | 0,
|
|
row;
|
|
|
|
for(var i = 0; i < height; i++)
|
|
{
|
|
row = "0x" + h(i * width * block_size, 8) + " | ";
|
|
|
|
for(var j = 0; j < width; j++)
|
|
{
|
|
var used = this.mem32s[(i * width + j) * block_size] > 0;
|
|
|
|
row += used ? "X" : " ";
|
|
}
|
|
|
|
dbg_log(row);
|
|
}
|
|
};
|
|
|
|
|
|
var
|
|
/**
|
|
* Arbritary value, the minimum number of bytes that can be mapped
|
|
* by one device. This might be spec'd somewhere ...
|
|
*
|
|
* Written as a power of 2.
|
|
*
|
|
* @const
|
|
*/
|
|
MMAP_BLOCK_SIZE = 14,
|
|
|
|
/** @const */
|
|
MMAP_BYTEWISE = 1,
|
|
MMAP_DWORDWISE = 4,
|
|
|
|
|
|
// this only supports a 32 bit address space
|
|
memory_map_registered = new Int8Array(1 << 32 - MMAP_BLOCK_SIZE),
|
|
|
|
memory_map_read = [],
|
|
memory_map_write = [];
|
|
|
|
for(var i = 0; i < (1 << 32 - MMAP_BLOCK_SIZE); i++)
|
|
{
|
|
// avoid sparse arrays
|
|
memory_map_read[i] = memory_map_write[i] = undefined;
|
|
}
|
|
|
|
/**
|
|
* @param addr {number}
|
|
* @param size {number}
|
|
* @param is_dword {boolean} true if the memory is addressed in dwords, otherwise byte
|
|
*
|
|
*/
|
|
this.mmap_register = function(addr, size, is_dword, read_func, write_func)
|
|
{
|
|
dbg_log("mmap_register32 " + h(addr, 8) + ": " + h(size, 8), LOG_IO);
|
|
dbg_assert((addr & (1 << MMAP_BLOCK_SIZE) - 1) === 0);
|
|
dbg_assert(size >= (1 << MMAP_BLOCK_SIZE) && (size & (1 << MMAP_BLOCK_SIZE) - 1) === 0);
|
|
|
|
var aligned_addr = addr >>> MMAP_BLOCK_SIZE,
|
|
unit_size = is_dword ? MMAP_DWORDWISE : MMAP_BYTEWISE;
|
|
|
|
for(; size > 0; aligned_addr++)
|
|
{
|
|
memory_map_registered[aligned_addr] = unit_size;
|
|
|
|
memory_map_read[aligned_addr] = function(read_addr)
|
|
{
|
|
return read_func(read_addr - addr | 0);
|
|
};
|
|
memory_map_write[aligned_addr] = function(write_addr, value)
|
|
{
|
|
write_func(write_addr - addr | 0, value);
|
|
}
|
|
|
|
size -= 1 << MMAP_BLOCK_SIZE;
|
|
}
|
|
};
|
|
|
|
function mmap_read8(addr)
|
|
{
|
|
var aligned_addr = addr >>> MMAP_BLOCK_SIZE,
|
|
registered = memory_map_read[aligned_addr];
|
|
|
|
//dbg_log("mmap_read8 " + h(addr, 8), LOG_IO);
|
|
|
|
if(memory_map_registered[aligned_addr] === MMAP_BYTEWISE)
|
|
{
|
|
return registered(addr);
|
|
}
|
|
else
|
|
{
|
|
return mmap_read32(addr & ~3) >> 8 * (addr & 3) & 0xFF;
|
|
}
|
|
};
|
|
|
|
function mmap_write8(addr, value)
|
|
{
|
|
var aligned_addr = addr >>> MMAP_BLOCK_SIZE,
|
|
registered = memory_map_write[addr >>> MMAP_BLOCK_SIZE];
|
|
|
|
//dbg_log("mmap_write8 " + h(addr, 8) + ": " + h(value, 2), LOG_IO);
|
|
|
|
if(memory_map_registered[aligned_addr] === MMAP_BYTEWISE)
|
|
{
|
|
registered(addr, value);
|
|
}
|
|
else
|
|
{
|
|
// impossible without reading. Maybe this should do nothing
|
|
dbg_assert(false);
|
|
}
|
|
};
|
|
|
|
function mmap_read32(addr)
|
|
{
|
|
var registered = memory_map_read[addr >>> MMAP_BLOCK_SIZE];
|
|
|
|
//dbg_log("mmap_read32 " + h(addr, 8), LOG_IO);
|
|
//dbg_assert((addr & 3) === 0);
|
|
dbg_assert(registered);
|
|
|
|
if((addr & 3) === 0 &&
|
|
memory_map_registered[addr >>> MMAP_BLOCK_SIZE] === MMAP_DWORDWISE)
|
|
{
|
|
return registered(addr);
|
|
}
|
|
else
|
|
{
|
|
return mmap_read8(addr) | mmap_read8(addr + 1) << 8 |
|
|
mmap_read8(addr + 2) << 16 | mmap_read8(addr + 3) << 24;
|
|
}
|
|
};
|
|
|
|
function mmap_write32(addr, value)
|
|
{
|
|
var registered = memory_map_write[addr >>> MMAP_BLOCK_SIZE];
|
|
|
|
//dbg_log("mmap_write32 " + h(addr, 8) + ": " + h(value, 8), LOG_IO);
|
|
//dbg_assert((addr & 3) === 0);
|
|
dbg_assert(registered);
|
|
|
|
if((addr & 3) === 0 &&
|
|
memory_map_registered[addr >>> MMAP_BLOCK_SIZE] === MMAP_DWORDWISE)
|
|
{
|
|
registered(addr, value);
|
|
}
|
|
else
|
|
{
|
|
mmap_write8(addr, value & 0xFF);
|
|
mmap_write8(addr + 1, value >> 8 & 0xFF);
|
|
mmap_write8(addr + 2, value >> 16 & 0xFF);
|
|
mmap_write8(addr + 3, value >> 24 & 0xFF);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
*/
|
|
this.read8s = function(addr)
|
|
{
|
|
debug_read(addr, 1);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
return mmap_read8(addr) << 24 >> 24;
|
|
}
|
|
else
|
|
{
|
|
return int8sarray[addr];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
*/
|
|
this.read8 = function(addr)
|
|
{
|
|
debug_read(addr, 1);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
return mmap_read8(addr);
|
|
}
|
|
else
|
|
{
|
|
return int8array[addr];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
*/
|
|
this.read16 = function(addr)
|
|
{
|
|
debug_read(addr, 2);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
return mmap_read8(addr) | mmap_read8(addr + 1) << 8;
|
|
}
|
|
else
|
|
{
|
|
return int8array[addr] | int8array[addr + 1] << 8;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
*/
|
|
this.read_aligned16 = function(addr)
|
|
{
|
|
debug_read(addr, 2);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
return mmap_read8(addr) | mmap_read8(addr + 1) << 8;
|
|
}
|
|
else
|
|
{
|
|
return int16array[addr >> 1];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
*/
|
|
this.read32s = function(addr)
|
|
{
|
|
debug_read(addr, 4);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
return mmap_read32(addr) | 0;
|
|
}
|
|
else
|
|
{
|
|
return int8array[addr] | int8array[addr + 1] << 8 |
|
|
int8array[addr + 2] << 16 | int8array[addr + 3] << 24;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
*/
|
|
this.read_aligned32 = function(addr)
|
|
{
|
|
debug_read(addr, 4);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
return mmap_read32(addr) | 0;
|
|
}
|
|
else
|
|
{
|
|
return int32sarray[addr >> 2];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
* @param value {number}
|
|
*/
|
|
this.write8 = function(addr, value)
|
|
{
|
|
debug_write(addr, 1, value);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
mmap_write8(addr, value);
|
|
}
|
|
else
|
|
{
|
|
int8array[addr] = value;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
* @param value {number}
|
|
*/
|
|
this.write16 = function(addr, value)
|
|
{
|
|
debug_write(addr, 2, value);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
mmap_write8(addr, value & 0xff);
|
|
mmap_write8(addr + 1, value >> 8 & 0xff);
|
|
}
|
|
else
|
|
{
|
|
int8array[addr] = value;
|
|
int8array[addr + 1] = value >> 8;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
* @param value {number}
|
|
*/
|
|
this.write_aligned16 = function(addr, value)
|
|
{
|
|
debug_write(addr, 2, value);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
mmap_write8(addr, value & 0xff);
|
|
mmap_write8(addr + 1, value >> 8 & 0xff);
|
|
}
|
|
else
|
|
{
|
|
int16array[addr >> 1] = value;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param addr {number}
|
|
* @param value {number}
|
|
*/
|
|
this.write32 = function(addr, value)
|
|
{
|
|
debug_write(addr, 4, value);
|
|
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
mmap_write32(addr, value);
|
|
}
|
|
else
|
|
{
|
|
int8array[addr] = value;
|
|
int8array[addr + 1] = value >> 8;
|
|
int8array[addr + 2] = value >> 16;
|
|
int8array[addr + 3] = value >> 24;
|
|
}
|
|
};
|
|
|
|
this.write_aligned32 = function(addr, value)
|
|
{
|
|
if(memory_map_registered[addr >>> MMAP_BLOCK_SIZE])
|
|
{
|
|
mmap_write32(addr, value);
|
|
}
|
|
else
|
|
{
|
|
int32sarray[addr >> 2] = value;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param offset {number}
|
|
* @param blob {Array.<number>}
|
|
*/
|
|
this.write_blob = function(blob, offset)
|
|
{
|
|
dbg_assert(blob && blob.length);
|
|
int8array.set(blob, offset);
|
|
};
|
|
|
|
/**
|
|
* zero byte terminated string
|
|
*/
|
|
this.read_string = function(addr)
|
|
{
|
|
var str = "",
|
|
data_byte;
|
|
|
|
while(data_byte = this.read8(addr))
|
|
{
|
|
str += String.fromCharCode(data_byte);
|
|
addr++;
|
|
}
|
|
|
|
return str;
|
|
};
|
|
|
|
this.write_string = function(str, addr)
|
|
{
|
|
for(var i = 0; i < str.length; i++)
|
|
{
|
|
this.write8(addr + i, str.charCodeAt(i));
|
|
}
|
|
};
|
|
}
|