了解 Z80 模拟器中的操作码
Understanding opcodes in Z80 emulator
我目前正在使用 Z80 处理器模拟器,作为初学者,我找到了一个示例,其中为内存分配了值以显示 "Hello World!"。
1. 0x21, 0x0C, 0X00,// ld hl, 0008
2. 0x06, 0x0F, // ld b,0f
3. 0x7e, // ld a,(hl)
4. 0x23, // inc hl
5. 0xD3, 0x00, // out (00), a
6. 0x10, 0xFA, // djnz
7. 0x76, // halt
8. 0x48, 0x65, 0x6C, 0x6C, 0x6F, // Hello
9. 0x2D, 0x77, 0x6F, 0x72, 0x6C, // Worl
10. 0x64, 0x21, 0x20, 0x21//d! !
我对内存中的值有一些疑问:
第一行,因为命令0x21
定义了LD HL,XX
指令,所以在汇编里就是LD HL,0CH
,那[=14是什么=] 在那里做什么?我认为 LD HL,xx
是一个 16 位指令,所以用八个零补充 0C
但我认为这是不正确的。
在第 5 行中,OUT (00),A
到底在做什么?我通过一些研究发现 OUT
将数据从累加器传输到输出端口,但我不太清楚 0x00
专门做什么。
我非常感谢任何提示或对我的问题的帮助,提前感谢您花时间阅读它们。
回答您的问题:
0x21
是 three-byte LD HL, nn
指令的 op-code。 nn
部分是一个 16 位立即数。在 Z80 中,所有 16 位立即数都按 little-endian 字节顺序编码,这意味着包含最低有效位 (LSB) 的字节在前,然后是包含最高有效位 (MSB) 的字节价值。在您的示例中,字节为 0x0c, 0x00
。因此,生成的 16 位值是 0x000c
,指令是 ld hl, 0x000c
,即使注释提到了不同的值。
out (n), a
是一条Z80指令,将寄存器a
的值输出到一个端口。尽管 Z80 端口有 16 位地址(就像内存地址),指定端口的 n
操作数是一个 8 位立即数。该立即数转到完整端口地址的低字节,而地址的高字节是寄存器 a
的值。这是我的模拟器中的样子:
void on_out_n_a(fast_u8 n) {
fast_u8 a = self().on_get_a();
self().on_output_cycle(make16(a, n), a);
self().on_set_wz(make16(a, inc8(n)));
}
在硬件层面上发生的事情是,处理器将其信号线(如 ~IORQ、~RD、~WR 和地址总线引脚)设置为特定状态,以便连接到处理器的外围设备可以识别输出周期和 "see" 写入哪个值和哪个端口。您可以在 Z80 用户手册的 "Input or Output Cycles".
部分找到有关输出循环期间发生的情况的更多详细信息
为了使您的模拟器能够实际向外围设备输出值,您需要这些设备(无论是真实设备还是模拟设备)确认输出并提供端口地址和输出值,因此它们可以弄清楚他们是否应该做出反应以及他们应该如何具体地做出反应。例如,在 ZX Spectrum 中,输出端口 0xfe(十进制 254)的三个最低位决定了屏幕边框的颜色。因此,Spectrum 仿真器会捕获对该端口的写入,从值中提取颜色代码并更新屏幕以显示新颜色的边框。
我目前正在使用 Z80 处理器模拟器,作为初学者,我找到了一个示例,其中为内存分配了值以显示 "Hello World!"。
1. 0x21, 0x0C, 0X00,// ld hl, 0008
2. 0x06, 0x0F, // ld b,0f
3. 0x7e, // ld a,(hl)
4. 0x23, // inc hl
5. 0xD3, 0x00, // out (00), a
6. 0x10, 0xFA, // djnz
7. 0x76, // halt
8. 0x48, 0x65, 0x6C, 0x6C, 0x6F, // Hello
9. 0x2D, 0x77, 0x6F, 0x72, 0x6C, // Worl
10. 0x64, 0x21, 0x20, 0x21//d! !
我对内存中的值有一些疑问:
第一行,因为命令
0x21
定义了LD HL,XX
指令,所以在汇编里就是LD HL,0CH
,那[=14是什么=] 在那里做什么?我认为LD HL,xx
是一个 16 位指令,所以用八个零补充0C
但我认为这是不正确的。在第 5 行中,
OUT (00),A
到底在做什么?我通过一些研究发现OUT
将数据从累加器传输到输出端口,但我不太清楚0x00
专门做什么。
我非常感谢任何提示或对我的问题的帮助,提前感谢您花时间阅读它们。
回答您的问题:
0x21
是 three-byteLD HL, nn
指令的 op-code。nn
部分是一个 16 位立即数。在 Z80 中,所有 16 位立即数都按 little-endian 字节顺序编码,这意味着包含最低有效位 (LSB) 的字节在前,然后是包含最高有效位 (MSB) 的字节价值。在您的示例中,字节为0x0c, 0x00
。因此,生成的 16 位值是0x000c
,指令是ld hl, 0x000c
,即使注释提到了不同的值。out (n), a
是一条Z80指令,将寄存器a
的值输出到一个端口。尽管 Z80 端口有 16 位地址(就像内存地址),指定端口的n
操作数是一个 8 位立即数。该立即数转到完整端口地址的低字节,而地址的高字节是寄存器a
的值。这是我的模拟器中的样子:
void on_out_n_a(fast_u8 n) {
fast_u8 a = self().on_get_a();
self().on_output_cycle(make16(a, n), a);
self().on_set_wz(make16(a, inc8(n)));
}
在硬件层面上发生的事情是,处理器将其信号线(如 ~IORQ、~RD、~WR 和地址总线引脚)设置为特定状态,以便连接到处理器的外围设备可以识别输出周期和 "see" 写入哪个值和哪个端口。您可以在 Z80 用户手册的 "Input or Output Cycles".
部分找到有关输出循环期间发生的情况的更多详细信息为了使您的模拟器能够实际向外围设备输出值,您需要这些设备(无论是真实设备还是模拟设备)确认输出并提供端口地址和输出值,因此它们可以弄清楚他们是否应该做出反应以及他们应该如何具体地做出反应。例如,在 ZX Spectrum 中,输出端口 0xfe(十进制 254)的三个最低位决定了屏幕边框的颜色。因此,Spectrum 仿真器会捕获对该端口的写入,从值中提取颜色代码并更新屏幕以显示新颜色的边框。