我正在开发一个需要低级别访问字节的小项目。下面的代码应该告诉许多人我的长期目标是什么,但我发现了一个有趣的短期问题,这导致了一些头痛。这是我的对象的“构造函数”,它以历史悠久的方式调用:
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"
}
在阅读类型化数组的基本文档后,我的想法是,我可以创建一个原始字节的 ArrayBuffer,然后使用类型化数组机制随意访问它们作为 8 位或 16 位值,同时保持对我自己的字节序(这样我就可以使用相同的方法编写代码来模拟具有与 Z80 不同字节序的 CPU)。
但是,我有了一个发现。
如果我创建一个 CPU_Z80 对象,使用
myCPU = new CPU_Z80(65536,65536) // example instantiation
然后在控制台中发出以下代码:
myCPU.reg_AF = 0x3AD2;
console.log(myCPU.reg_AF);
返回我输入的值。但如果我发出以下命令:
myCPU.reg_AF = 0x3AD2;
console.log(myCPU.reg_A);
返回的值为零,不是预期的 0x3A。
当然,这完全违背了使用上述定义使寄存器访问变得方便快捷的目的。各种 TypedArray 构造函数不是提供一种在我需要时访问 ArrayBuffer 中的基础数据的方法,而是只是生成一个对象,用 ArrayBuffer 中的数据加载该对象一次,并始终从该点返回加载的值。
但是,更糟糕的是,为了进一步调查问题,我尝试检查底层的 ArrayBuffer。以下:
myCPU.reg_AF = 0x3AD2;
console.log(myCPU.registerSet[0]);
令我惊讶的是,产生了价值
undefined
。
所以,上面的代码,我以为将值 0x3AD 存储在
registerSet
ArrayBuffer 中,实际上并没有做任何事情。快速检查发现,这种行为在 Chrome 和 Firefox 上是相同的。
然而,如果我不是引用 ArrayBuffer,而是将 TypedArray 定义为固定数量的相关单元,则通过数组索引访问其内容将按预期工作。
ArrayBuffers 发生了什么?如果它们没有提供我期望的功能,那么它们还有什么意义呢?因为如果使用 ArrayBuffers,类型化数组的行为会完全改变,并且是以一种反直觉(并且令人恼火)的方式这样做。更糟糕的是,没有任何文档(Mozilla 在这方面通常要好得多)提醒我注意这些问题,或者提供解决方案。
有人可以解释一下吗:
您正在替换示例中的字段
reg_AF
。 Uint16Array
已替换为数字。
要在 Uint16Array 中设置值,您需要使用
fill()
或 set()
等方法或通过索引设置值
myCPU.reg_AF[0] = 0x3AD2;
您还可以定义 getter/setter 来通过直接赋值保持更清晰的语法。