I'm trying to display a single tile with a single colored pixel using GBA tile mode, starting from scratch.
It mostly work, but instead of setting a single pixel, it set the same color to the two bytes at the 16bits aligned location where I intend to write:
Resulting image when running through the mgba emulator: Screen
The emulator allows to inspect the tiles, memory and register.
Tilesmap, showing the two pixels set on the tiles.
VRAM, starting at the tiles address, showing that there are indeed two 8bpp pixel set, referencing the color in palette at index 42.
Display Control Register nothing unexpected, running in first tile mode with background 0 enabled.
Background 0 Control register, in 8bpp mode.
Main:
#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) {};
}
Useful parts of 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;
So in the function do_it
, I am setting a single byte value, only once at an hardcoded location.
Yet in memory, it set the value to the given address AND the one that complete 2 bytes alignment:
VRAM.tilesets[3].d_tiles[37][0][0] = 42;
and VRAM.tilesets[3].d_tiles[37][0][1] = 42;
have the same outcome.
I thought I could have done something badly in my structs/unions/types definitions, but assembly looks fine:
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
I'm not fluent in arm assembly, but the strb
in do_it
seems to match the affectation and it should operate on a single byte according to the arm doc.
Some more info:
- It happens on multiple emulator: I usually use mgba, it happens on vbam too
- Other roms work as intended on mgba
- Building with arm-none-eabi-gcc toolchain v 12.1.0
- Without any optimisations flags
- Using
-mthumb -mthumb-interwork -mcpu=arm7tdmi -fomit-frame-pointer -ffast-math -fno-strict-aliasing
- It also happens with 4bpp mode, setting four pixels instead of expected two
- Code is running from gamepak1 memory directly
- It happens when using differents tilesets/tilemaps locations
- It does not happens when running in bitmap mode:
This works and display a SINGLE green pixel on the top left of the screen:
int main() {
VRAM.bitmap[0][0] = rgb(0b1111, 0b11111, 0);
DISPCNT = DISPCNT_MODE_3 | DISPCNT_BACKGROUND_2;
while (1) {};
}
Full codebase can be found there: github
Any idea of what could cause this ?