将位域转换为 uint8_t

问题描述

我下面的位域代表 6502 cpu 的 7 个状态标志。我正在尝试模拟指令 PHP,它将状态标志的副本推送到堆栈上。

struct Flags {
    uint8_t C: 1;
    uint8_t Z: 1;
    uint8_t I: 1;
    uint8_t D: 1;
    uint8_t V: 1;
    uint8_t N: 1;
    uint8_t B: 2;
};

我需要一种方法将每个字段打包成一个单字节数据类型,例如 uint8_t,以便我可以将它推送到堆栈中。但是,下面的代码给出了这个错误operand of type 'struct Flags' where arithmetic or pointer type is required。我该如何解决这个问题?

int main() {
    struct Flags f = {0,1,0};
    uint8_t f_as_byte = (uint8_t) f;
}

解决方法

位域的问题在于它是实现定义的位排列顺序。对于 6502 仿真器来说,这可能是相当不可接受的。 PHP 命令必须以精确所需的格式推送状态字,例如

uint8_t as_state = f.N << 7 | f.V << 6 | f.B << 4 | f.D << 3 | f.I << 2 | f.Z << 1 | f.C;

您的布局错误,B 成员的位置错误。总而言之,考虑到像上面这样的复杂代码,也许将标志视为单个 uint8_t 并为其提供访问宏会更容易,例如

#define FLAG_N 0x80U
...
#define FLAG_C 0x1U

#define SET_FLAG(flag_var,flag) ((flag_var) |= (flag))
#define CLR_FLAG(flag_var,flag) ((flag_var) &= ~(flag))
#define GET_FLAG(flag_var,flag) ((_Bool)((flag_var) & (flag)))

uint8_t flags = 0;
SET_FLAG(flags,FLAG_C);

if (GET_FLAG(flags,FLAG_N)) { ... }

这样可以将 PHP 指令编码为按原样推送 flags...

Except that the B flag that is pushed is not a flag from the status register...

,

您可以使用 union

typedef unsigned char uint8_t;

struct Flags {
    uint8_t C:1;
    uint8_t Z:1;
    uint8_t I:1;
    uint8_t D:1;
    uint8_t V:1;
    uint8_t N:1;
    uint8_t B:2;
};

union uFlags {
    uint8_t B;
    struct Flags F;
};

int
main()
{
    struct Flags f = { 0,1,0 };
    union uFlags u;

    u.F = f;
    uint8_t f_as_byte = u.B;
}

使用指定的初始化程序,您可以直接初始化 union 而无需单独的 f

typedef unsigned char uint8_t;

struct Flags {
    uint8_t C:1;
    uint8_t Z:1;
    uint8_t I:1;
    uint8_t D:1;
    uint8_t V:1;
    uint8_t N:1;
    uint8_t B:2;
};

union uFlags {
    uint8_t B;
    struct Flags F;
};

int
main()
{
    union uFlags u = {
        .F = { .V = 1 }
    };

    uint8_t f_as_byte = u.B;
}
,

不是将结构转换为 (uint8_t),而是取其地址并将其转换为 (uint8_t *)

#include <stdio.h>

struct Flags {
    uint8_t C: 1;
    uint8_t Z: 1;
    uint8_t I: 1;
    uint8_t D: 1;
    uint8_t V: 1;
    uint8_t N: 1;
    uint8_t B: 2;
};

int main() {
    struct Flags f = {0,0};
    uint8_t f_as_byte = *(uint8_t *)&f;
    printf("flags: %.2X\n",f_as_byte);
    return 0;
}

但是请注意,用于表示位域成员的实际位值是实现定义的。 C 标准甚至不保证 Flags 结构适合单个字节。这使得位域成为表示硬件寄存器或指令操作码的危险选择,因为这会使仿真器不可移植。小端和大端系统通常对位域成员使用不同的布局。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...