gre*_*rep 5 c gcc for-loop 68000 volatile
我有一个指向像这样的控制端口的地址(对于上下文我正在开发 Sega/Megadrive 游戏):
volatile u32 * vdp_ctrl = (u32 *) 0x00C00004;
Run Code Online (Sandbox Code Playgroud)
以及我想设置的一组初始值:
const u8 initial_vdp_vals[24] = {
0x20,
0x74,
0x30,
..etc
};
Run Code Online (Sandbox Code Playgroud)
有一个循环:
typedef struct {
u16 upper;
u8 reg;
u8 val;
} bitset;
typedef union {
bitset b;
u32 as_u32;
} u_bitset;
static inline void init_vdp() {
u_bitset cmd = {{0x00008000}};
for(int i = 0; i < 24; i++) {
cmd.b.val = initial_vdp_vals[i];
*vdp_ctrl = cmd.as_u32;
cmd.b.reg += 1;
}
}
Run Code Online (Sandbox Code Playgroud)
问题是 gcc(至少使用 O2)优化了它并且只将最后一个值写入*vdp_ctrl指针。我已经成功通过设置属性之一来解决这个bitset结构来volatile,但是这似乎并不像一个直观的解决方案,并装配它产生的结果是非常漫长的。
所以我的问题有两个:
vdp_ctrl指针需要随着时间的推移接受多次写入的“正确”方法是什么。我将经常使用这种模式(将常量/静态数据写入循环中的控制/数据地址),并将随机字段标记为 volatile 似乎不直观。 move.l #initial_vdp_vals, a0
move.l #24, d0
move.l #0x00008000, d1
.copy:
move.b (a0)+, d1
move.w d1, 0x00C00004
add.w #0x0100, d1
dbra d0, .copy
Run Code Online (Sandbox Code Playgroud)
这是非常好的和简洁的。所以我的另一个问题可能是(作为一个完整的 C 新手):我的 C 解决方案是否有更好的方法让我更接近上面的程序集?老实说,我什至不确定我的代码是否正确,因为我只是想先解决这个循环优化问题,因为我知道这将是一个持续不断的问题。
产生我的问题的可运行示例:
volatile unsigned long * vdp_ctrl = (unsigned long *) 0x00C00004;
const unsigned char initial_vdp_vals[24] = {
0x20,
0x74,
0x30,
0x40,
0x05,
0x70,
0x00,
0x00,
0x00,
0x00,
0x00,
0x08,
0x81,
0x34,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00
};
typedef struct {
unsigned int upper;
unsigned char reg;
unsigned char val;
} bitset;
typedef union {
bitset b;
unsigned long as_u32;
} u_bitset;
static inline void init_vdp() {
u_bitset cmd = {{0x00008000}};
for(int i = 0; i < 24; i++) {
cmd.b.val = initial_vdp_vals[i];
*vdp_ctrl = cmd.as_u32;
}
}
void init() {
init_vdp();
for(;;) {
}
}
Run Code Online (Sandbox Code Playgroud)
与m68k-linux-gnu-gcc -ffreestanding -O2 -S -c test.c -o test.s. 生成以下内容:
#NO_APP
.file "test.c"
.text
.align 2
.globl init
.type init, @function
init:
link.w %fp,#0
move.l vdp_ctrl,%a0
moveq #24,%d0
move.l #32768,%d1
.L2:
move.l %d1,(%a0)
subq.l #1,%d0
jne .L2
.L3:
jra .L3
.size init, .-init
.globl initial_vdp_vals
.section .rodata
.type initial_vdp_vals, @object
.size initial_vdp_vals, 24
initial_vdp_vals:
.byte 32
.byte 116
.byte 48
.byte 64
.byte 5
.byte 112
.byte 0
.byte 0
.byte 0
.byte 0
.byte 0
.byte 8
.byte -127
.byte 52
.byte 0
.byte 0
.byte 1
.byte 0
.byte 0
.byte 0
.byte 0
.byte 0
.byte 0
.byte 0
.globl vdp_ctrl
.data
.align 2
.type vdp_ctrl, @object
.size vdp_ctrl, 4
vdp_ctrl:
.long 12582916
.ident "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)
Run Code Online (Sandbox Code Playgroud)
注意:看起来我的数组的大小决定了它是否得到优化。当我只制作两个元素时,它没有优化。
在这种代码中,您应该使用精确大小的整数。我强烈建议打包结构和工会。
#include <stdint.h>
#define vdp_ctrl ((volatile uint32_t *) 0x00C00004)
const unsigned char initial_vdp_vals[24] = {
0x20,
0x74,
0x30,
0x40,
0x05,
0x70,
0x00,
0x00,
0x00,
0x00,
0x00,
0x08,
0x81,
0x34,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00
};
typedef struct {
uint16_t upper;
uint8_t reg;
uint8_t val;
} __attribute__((packed)) bitset;
typedef union {
bitset b;
uint32_t as_u32;
} __attribute__((packed)) u_bitset ;
static inline void init_vdp() {
u_bitset cmd = {.b.upper = 0x00008000};
for(int i = 0; i < 24; i++)
{
cmd.b.val = initial_vdp_vals[i];
*vdp_ctrl = cmd.as_u32;
}
}
void init() {
init_vdp();
for(;;) {
}
}
Run Code Online (Sandbox Code Playgroud)
它会生成您需要的代码。
IMO 最好用宏而不是真实的物体。对于这个简单的代码来说,这可能不会产生任何影响,但如果代码变得更复杂,就会产生任何影响。
| 归档时间: |
|
| 查看次数: |
139 次 |
| 最近记录: |