У меня есть адрес, который указывает на порт управления следующим образом (для контекста я работаю над игрой Sega / Megadrive):
volatile u32 * vdp_ctrl = (u32 *) 0x00C00004;
И массив начальных значений, которые я хочу set:
const u8 initial_vdp_vals[24] = {
0x20,
0x74,
0x30,
..etc
};
С al oop:
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;
}
}
Проблема в том, что g cc (по крайней мере, с O2) оптимизирует это и записывает только последнее значение в указатель *vdp_ctrl
. Мне удалось обойти это, установив одно из свойств в структуре bitset
на volatile
, но это не похоже на интуитивное решение, и сборка, которую он генерирует в результате, очень longy.
Итак, мой вопрос состоит из двух частей:
- Каков «правильный» способ предложить g cc, что мой указатель
vdp_ctrl
должен принимать несколько записей через некоторое время. Я собираюсь часто использовать этот шаблон (запись данных const / stati c для адресов control / data в al oop), а пометка случайных полей как volatile кажется не интуитивно понятным. - «Стандарт» «качество», которое я использую для сгенерированного asm, выглядит следующим образом (не сгенерированный g cc):
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
Что очень мило и лаконично. Таким образом, мой другой вопрос может быть (как полный 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(;;) {
}
}
С 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
gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)
Примечание : Похоже, размер моего массива определяет, будет ли он оптимизирован или нет. Когда я сделал только два элемента, он не оптимизировался.