能否创建两个uint8_t指针,分别指向由uint16_t指针指向的值的前半部分和后半部分?

4

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

  • 两个8位寄存器可以合并并作为一个16位寄存器对待
  • 更改配对中其中一个8位寄存器的值应更改组合寄存器的值

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

如果我将寄存器AF实现为uint16_t*,那么我可以将寄存器A和F的内容分别存储为指向寄存器AF的第一字节和第二字节的uint8_t*吗?如果不行,请提供其他建议 :)

编辑:仅澄清,这与Z80架构非常相似


2
尝试使用联合体...但要注意大小端。 - Dmitri
它也类似于x86。 - phuclv
3个回答

4
使用联合体。
union b
{
    uint8_t a[2];
    uint16_t b;
};

成员a和b共享这些字节。当在成员a中写入一个值,然后使用成员b读取该值时,将重新解释此类型。这可能是一个陷阱表示,会导致未定义的行为,但是uint8_t和uint16_t类型没有它们。
另一个问题是字节序,写入成员a的第一个元素将始终更改成员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 可能会导致未定义的行为。


这是否意味着我需要使用:AF.A,AF.F,AF.AF每当我需要单独访问寄存器时?修改其中一个部分是否会修改整个寄存器? - user6290240
@JoshThieme 是的,但要小心。我建议你使用第二个选项。请查看更新。 - 2501
感谢您提供深入的答案。然而,由于某些CPU指令的工作方式,我认为a和b需要是整数指针而不是整数。这个方法能够适应这种情况吗? - user6290240
@JoshThieme 在这种情况下,指向原始指针是有问题的。大多数类型不会别名。我建议您提出一个具体的例子来问一个新问题。 - 2501

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


2
Volatile和strict aliasing无关。如果类型不兼容,则行为未定义。 - 2501

-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则是大端字节序)。

当然,正如大多数人建议的那样,你应该使用一个union,这将使您的代码由于指针地址计算而变得不太容易出错。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接