I’m working on a little project that requires access to bytes at a low level. The code below should inform many here what I’m aiming to achieve in the long term, but I’ve alighted upon an interesting short term problem that’s resulted in some head scratching. Here’s the “constructor” for my object, which is invoked in the time honoured fashion:
function CPU_Z80(memSize,IOSize)
{
//This is the constructor for the ORIGINAL Z80 CPU, as used in CP/M computers and old 8-bit Z80 home computers.
//Use this to emulate the behaviour of that CPU.
this.registerSet = new ArrayBuffer(26);
this.reg_AF = new Uint16Array(this.registerSet, 0, 1);
this.reg_BC = new Uint16Array(this.registerSet, 2, 1);
this.reg_DE = new Uint16Array(this.registerSet, 4, 1);
this.reg_HL = new Uint16Array(this.registerSet, 6, 1);
this.reg_AF_Alt = new Uint16Array(this.registerSet, 8, 1);
this.reg_BC_Alt = new Uint16Array(this.registerSet, 10, 1);
this.reg_DE_Alt = new Uint16Array(this.registerSet, 12, 1);
this.reg_HL_Alt = new Uint16Array(this.registerSet, 14, 1);
this.reg_A = new Uint8Array(this.registerSet, 0, 1);
this.reg_B = new Uint8Array(this.registerSet, 2, 1);
this.reg_C = new Uint8Array(this.registerSet, 3, 1);
this.reg_D = new Uint8Array(this.registerSet, 4, 1);
this.reg_E = new Uint8Array(this.registerSet, 5, 1);
this.reg_H = new Uint8Array(this.registerSet, 6, 1);
this.reg_L = new Uint8Array(this.registerSet, 7, 1);
this.reg_F = new Uint8Array(this.registerSet, 1, 1);
this.reg_A_Alt = new Uint8Array(this.registerSet, 8, 1);
this.reg_B_Alt = new Uint8Array(this.registerSet, 10, 1);
this.reg_C_Alt = new Uint8Array(this.registerSet, 11, 1);
this.reg_D_Alt = new Uint8Array(this.registerSet, 12, 1);
this.reg_E_Alt = new Uint8Array(this.registerSet, 13, 1);
this.reg_H_Alt = new Uint8Array(this.registerSet, 14, 1);
this.reg_L_Alt = new Uint8Array(this.registerSet, 15, 1);
this.reg_F_Alt = new Uint8Array(this.registerSet, 9, 1);
this.reg_I = new Uint8Array(this.registerSet, 16, 1)
this.reg_R = new Uint8Array(this.registerSet, 17, 1);
this.reg_IX = new Uint16Array(this.registerSet, 18, 1);
this.reg_IY = new Uint16Array(this.registerSet, 20, 1);
this.reg_SP = new Uint16Array(this.registerSet, 22, 1);
this.reg_PC = new Uint16Array(this.registerSet, 24, 1);
this.memSpace = new Uint8Array(memSize);
this.IOSpace = new Uint8Array(IOSize);
this.clock = new Uint32Array(2); //Counter being used as a surrogate for the CPU clock.
this.assembler = null;
//End "constructor"
}
My thought, upon reading the basic docs for typed arrays, was that I could create an ArrayBuffer of raw bytes, then use the typed array mechanism to access them at will as either 8-bit or 16-bit values, while maintaining control over enndianness myself (so that I can use the same approach for writing code to simulate a CPU with different endianness to a Z80).
But, I’ve made a discovery.
If I create a CPU_Z80 object, using
myCPU = new CPU_Z80(65536,65536) // example instantiation
and then issue the following code in the console:
myCPU.reg_AF = 0x3AD2;
console.log(myCPU.reg_AF);
the value I entered is returned. But if I issue the following:
myCPU.reg_AF = 0x3AD2;
console.log(myCPU.reg_A);
the value returned is zero, not the expected 0x3A.
This, of course, defeats entirely the object of using the definitions above to make register access nice and convenient. Instead of providing a means of accessing the underlying data in the ArrayBuffer as and when I want it, the various TypedArray constructors simply generate an object, load that object with data from the ArrayBuffer once, and always return that loaded value from that point on.
But, worse still, in order to investigate matters further, I tried examining the underlying ArrayBuffer. The following:
myCPU.reg_AF = 0x3AD2;
console.log(myCPU.registerSet[0]);
yielded, to my astonishment, the value undefined
.
So, the code above, which I thought was storing the value 0x3AD” in the registerSet
ArrayBuffer, was actually doing nothing of the sort. A quick check reveals that this behaviour is identical on Chrome and Firefox.
Yet, if instead of referencing an ArrayBuffer, I define a TypedArray as a fixed number of relevant units, accessing the contents thereof via array indices works as expected.
What is going on with ArrayBuffers? And if they don’t provide the functionality I was expecting, what’s the point of them? Because the behaviour of typed arrays completely changes if one uses ArrayBuffers, and does so in a counter-intuitive (and infuriating) manner. Worse still, none of the documentation (Mozilla is usually far better in this respect) alerted me to any of this, or offers solutions.
Can someone explain:
- what use are ArrayBuffers if they don’t work as expected; and
- how to reference raw byte data with proper refreshing of the views?