将位域转换为 uint8_t
Turning a bitfield into a 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, 0, 0, 0, 1, 0, 0};
uint8_t f_as_byte = (uint8_t) f;
}
你可以用 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, 0, 0, 0, 1, 0, 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;
}
位域的问题在于它是实现定义的位的排列顺序。这对于 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...
与其将结构转换为 (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, 0, 0, 1, 0, 0};
uint8_t f_as_byte = *(uint8_t *)&f;
printf("flags: %.2X\n", f_as_byte);
return 0;
}
但是请注意,用于表示位域成员的实际位值是实现定义的。 C 标准甚至不保证 Flags
结构适合单个字节。这使得位域成为表示硬件寄存器或指令操作码的冒险选择,因为这会使仿真器不可移植。小端和大端系统通常对位域成员使用不同的布局。
我下面的位域代表 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, 0, 0, 0, 1, 0, 0};
uint8_t f_as_byte = (uint8_t) f;
}
你可以用 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, 0, 0, 0, 1, 0, 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;
}
位域的问题在于它是实现定义的位的排列顺序。这对于 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...
与其将结构转换为 (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, 0, 0, 1, 0, 0};
uint8_t f_as_byte = *(uint8_t *)&f;
printf("flags: %.2X\n", f_as_byte);
return 0;
}
但是请注意,用于表示位域成员的实际位值是实现定义的。 C 标准甚至不保证 Flags
结构适合单个字节。这使得位域成为表示硬件寄存器或指令操作码的冒险选择,因为这会使仿真器不可移植。小端和大端系统通常对位域成员使用不同的布局。