You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GBA tile模式写入VRAM单字节意外写入相邻字节问题排查

GBA Tile模式下单字节写入VRAM出现相邻字节重复赋值问题

问题现象

我正从零开始尝试使用GBA tile模式显示带单个彩色像素的单个tile。

功能基本可运行,但并未按预期设置单个像素,而是在我要写入的16位对齐位置的两个字节中都写入了相同颜色值:

模拟器支持查看tile、内存与寄存器状态,相关状态截图如下:

相关代码

主程序代码

#include "screen.h"

void do_it() {
  // [tile_index][row][column] = palette index
  VRAM.tilesets[3].d_tiles[37][0][0] = 42;
}


int main() {
  // [tilemap][row][column] = tile_index
  VRAM.tilemaps[0][0][0] = 37;
  do_it();
  PALETTE.backgrounds[42] = rgb(0b1111, 0b11111, 0);
  BG0CNT = BGCNT_SIZE_32X32 | BGCNT_COLOR_8BPP | (0 << BGCNT_TILEMAP_INDEX) | (3 << BGCNT_TILESET_INDEX);
  DISPCNT = DISPCNT_MODE_TILE_0 | DISPCNT_BACKGROUND_0;
  while (1) {};
}

screen.h 相关定义片段

typedef unsigned char tile_4bpp[8][4];
typedef unsigned char tile_8bpp[8][8];

typedef union {
  tile_4bpp s_tiles[512];
  tile_8bpp d_tiles[256];
} char_block;

extern union {
  char_block tilesets[4];
  screen_block tilemaps[32];
  color bitmap[HEIGHT][WIDTH];
} VRAM;

在do_it函数中,我仅在硬编码位置执行了一次单字节赋值操作,但内存中目标地址以及与其组成2字节对齐的相邻地址都被写入了该值:执行VRAM.tilesets[3].d_tiles[37][0][0] = 42; 和 VRAM.tilesets[3].d_tiles[37][0][1] = 42; 会得到完全相同的执行结果。

排查记录

我最初怀疑是结构体/联合体/类型定义存在错误,但反汇编结果看起来正常:

Disassembly of section .text:

080000e8 <do_it>:
 80000e8:   4b02        ldr r3, [pc, #8]    ; (80000f4 <do_it+0xc>)
 80000ea:   4a03        ldr r2, [pc, #12]   ; (80000f8 <do_it+0x10>)
 80000ec:   212a        movs    r1, #42 ; 0x2a
 80000ee:   5499        strb    r1, [r3, r2]
 80000f0:   46c0        nop         ; (mov r8, r8)
 80000f2:   4770        bx  lr
 80000f4:   06000000    streq   r0, [r0], -r0
 80000f8:   0000c940    andeq   ip, r0, r0, asr #18

080000fc <main>:
 80000fc:   b510        push    {r4, lr}
 80000fe:   4b09        ldr r3, [pc, #36]   ; (8000124 <main+0x28>)
 8000100:   2225        movs    r2, #37 ; 0x25
 8000102:   801a        strh    r2, [r3, #0]
 8000104:   f7ff fff0   bl  80000e8 <do_it>
 8000108:   4b07        ldr r3, [pc, #28]   ; (8000128 <main+0x2c>)
 800010a:   2254        movs    r2, #84 ; 0x54
 800010c:   4907        ldr r1, [pc, #28]   ; (800012c <main+0x30>)
 800010e:   5299        strh    r1, [r3, r2]
 8000110:   4b07        ldr r3, [pc, #28]   ; (8000130 <main+0x34>)
 8000112:   228c        movs    r2, #140    ; 0x8c
 8000114:   801a        strh    r2, [r3, #0]
 8000116:   4b07        ldr r3, [pc, #28]   ; (8000134 <main+0x38>)
 8000118:   2280        movs    r2, #128    ; 0x80
 800011a:   0052        lsls    r2, r2, #1
 800011c:   801a        strh    r2, [r3, #0]
 800011e:   46c0        nop         ; (mov r8, r8)
 8000120:   e7fd        b.n 800011e <main+0x22>
 8000122:   46c0        nop         ; (mov r8, r8)
 8000124:   06000000    streq   r0, [r0], -r0
 8000128:   05000000    streq   r0, [r0, #-0]
 800012c:   000003ef    andeq   r0, r0, pc, ror #7
 8000130:   04000008    streq   r0, [r0], #-8
 8000134:   04000000    streq   r0, [r0], #-0

我对ARM汇编并不精通,但do_it中的strb指令确实对应上述赋值操作,根据ARM官方文档说明,该指令应当仅操作单个字节。

已确认的信息

  • 该问题在多个模拟器上复现:日常使用mgba,在vbam上也存在同样问题
  • 其他ROM在mgba上运行正常
  • 使用arm-none-eabi-gcc v12.1.0工具链编译
  • 未添加任何优化编译选项
  • 编译参数为-mthumb -mthumb-interwork -mcpu=arm7tdmi -fomit-frame-pointer -ffast-math -fno-strict-aliasing
  • 4bpp模式下也存在同样问题,会设置4个像素而非预期的2个
  • 代码直接从gamepak内存运行
  • 更换tilesets/tilemaps位置后问题仍然存在
  • 在bitmap模式下不会出现该问题,以下代码可正常运行,在屏幕左上角显示单个绿色像素:
int main() {
  VRAM.bitmap[0][0] = rgb(0b1111, 0b11111, 0);
  DISPCNT = DISPCNT_MODE_3 | DISPCNT_BACKGROUND_2;
  while (1) {};
}

完整代码仓库地址:github


内容的提问来源于stack exchange,提问作者Globoplox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 07:09:18