4

我正在尝试用 C 编写 Gameboy 模拟器,目前正在决定如何实现以下行为:

  • 两个 8 位寄存器可以组合起来当作一个 16 位寄存器处理
  • 更改配对中 8 位寄存器之一的值应更改组合寄存器的值

例如,寄存器 A 和 F 是 8 位寄存器,可以联合用作 16 位寄存器 AF。但是,当寄存器 A 和 F 的内容发生变化时,这些变化应反映在后续对寄存器 AF 的引用中。

如果我将寄存器 AF 实现为 a uint16_t*,我可以将寄存器 A 和 F 的内容存储为uint8_t*分别指向寄存器 AF 的第一个和第二个字节吗?如果没有,任何其他建议将不胜感激:)

编辑:澄清一下,这是与 Z80 非常相似的架构

4

3 回答 3

4

使用工会。

union b
{
    uint8_t a[2];
    uint16_t b;
};

成员 a 和 b 共享字节。当一个值写入 membera然后使用 member 读取时,b该值将在此类型中重新解释。这可能是一个陷阱表示,它会导致未定义的行为,但类型 uint8_t 和 uint16_t 没有它们。

另一个问题是字节顺序,写入 member 的第一个元素a将始终更改 member 的第一个字节b,但根据字节顺序,该字节可能代表 的最高或最低有效位b,因此结果值会因架构而异。


为了避免陷阱表示和字节顺序,只使用类型uint16_t并使用按位操作写入它。例如,要写入最高有效 8 位:

uint16_t a = 0;
uint8_t b = 200;
a =  ( uint16_t )( ( ( unsigned int )a & 0xFF ) | ( ( unsigned int )b << 8 ) ) ;

同样对于最低有效 8 位:

a =  ( uint16_t )( ( ( unsigned int )a & 0xFF00 ) | ( unsigned int )b );

这些操作应该放在一个函数中。

那里的演员( unsigned int )表是为了避免整数促销。如果INT_MAX等于 2^15-1,并且有符号整数存在陷阱表示,则操作:b << 8在技术上可能会导致未定义的行为。

于 2016-07-21T06:12:07.563 回答
0

你可以这样解决它:

volatile uint16_t afvalue = 0x55AA;   // Needs long lifetime.

volatile uint16_t *afptr = &afvalue;
volatile uint8_t  *aptr  = (uint8_t *)afptr;
volatile uint8_t  *fptr  = aptr + 1;

if (*aptr == 0xAA) {         // Take endianness into account.
  aptr++;                    // Swap the pointers.
  fptr--;
}

afvalue = 0x0000;

现在aptr指向高位并fptr指向低位,无论模拟器运行在小端或大端机器上。

注意:由于这些指针类型不同,编译器可能不知道它们指向相同的内存,从而优化代码,以便*afptr以后读取*aptr. 因此volatile.

于 2016-07-21T06:33:58.590 回答
-1

您当然可以使用指针指向您想要的任何地方。

#include<stdio.h>
#include <stdint.h>

int main() {
    uint16_t a=1048;
    uint8_t *ah=(uint8_t*)&a,*al=((uint8_t*)&a)+1;
    printf("%u,%u,%u\n",a,*ah,*al);
}

如评论中所述,您需要注意字节序(我相信gameboy是小字节序,而x86是大字节序)。

当然,正如大多数人建议的那样,您应该使用 aunion这将使您的代码由于指针地址计算而不易出错

于 2016-07-21T06:19:02.347 回答