我正在try 使用GBA平铺模式显示一个带有单色像素的平铺,从头开始.

它主要起作用,但不是设置单个像素,而是在16位对齐的位置将相同的 colored颜色 设置为两个字节,我打算在其中写入:

运行mgba emulator时生成的图像:

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.

主要内容:

#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) {};
}

屏幕的有用部分.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.

我原以为我可以在 struct /联合/类型定义中做得很糟糕,但汇编看起来不错:

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

我不擅长手臂装配,但do_it中的strb似乎与矫揉造作相匹配,它应该根据arm doc操作一个字节.

更多信息:

  • 它发生在多个模拟器上:我通常使用mgba,它也发生在vbam上
  • mgba上预期的其他ROM工作
  • 使用arm none eabi gcc工具链v 12.1.0构建
  • 没有任何优化标志
  • 使用-mthumb -mthumb-interwork -mcpu=arm7tdmi -fomit-frame-pointer -ffast-math -fno-strict-aliasing
  • 4bpp模式也会发生这种情况,设置四个像素,而不是预期的两个像素
  • 代码直接从gamepak1内存运行
  • 当使用不同的TILESET/tilemaps位置时会发生这种情况
  • 在位图模式下运行时发生:

这可以工作并在屏幕左上角显示一个绿色像素:

int main() {
  VRAM.bitmap[0][0] = rgb(0b1111, 0b11111, 0);
  DISPCNT = DISPCNT_MODE_3 | DISPCNT_BACKGROUND_2;
  while (1) {};
}

可在此处找到完整的代码库:github

你知道这是什么原因吗?

推荐答案

memmap:

MEMORY
{
   ewram : ORIGIN = 0x02000000, LENGTH = 256K
}
SECTIONS
{
   .text : { *(.text*) } > ewram
}

启动.s

.cpu arm7tdmi
.code 32

.globl _start
_start:
    ldr sp,=0x03008000
    bl notmain
hang:
    b hang

.globl PUT16
PUT16:
    strh r1,[r0]
    bx lr

.globl GET16
GET16:
    ldrh r0,[r0]
    bx lr

.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

不重要.页:1

extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
extern void PUT16 ( unsigned int, unsigned int );
extern unsigned int GET16 ( unsigned int );

#define DISPCNT 0x04000000
#define BG0CNT  0x04000008

#define PMEM 0x05000000
#define TMEM 0x06000000
#define VMEM 0x06008000

void notmain ( void )
{
    unsigned int ra;
    unsigned int rb;

    //display control,
    //mode 0
    //enable BG0
    PUT16(DISPCNT,0x0100);
    //BG0 control
    //256 color palette
    //tiles defined at 0x60000000
    //screen at 0x60008000
    PUT16( BG0CNT,0x1080);
    //setup the first 8 colors
    PUT16(PMEM+0x0,0x0000); //BLACK
    PUT16(PMEM+0x2,0x001F); //RED
    PUT16(PMEM+0x4,0x03E0); //GREEN
    PUT16(PMEM+0x6,0x03FF); //GREEN+RED
    PUT16(PMEM+0x8,0x7C00); //BLUE
    PUT16(PMEM+0xA,0x7C1F); //BLUE+RED
    PUT16(PMEM+0xC,0x7FE0); //BLUE+GREEN
    PUT16(PMEM+0xE,0x7FFF); //BLUE+GREEN+RED (WHITE)
    //lets make a few tiles 64 bytes per tile.
    ra=TMEM;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;

    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000000); ra+=4;
    PUT32(ra,0x00000000); ra+=4; PUT32(ra,0x00000002); ra+=4;

    //make screen black/clear screen
    ra=VMEM;
    for(rb=0;rb<(32*20);rb++)
    {
        PUT16(ra,0x0000);
        ra+=2;
    }
    //put some tiles on the screen
    PUT16(VMEM,1);
}

构建:

arm-none-eabi-as --warn --fatal-warnings  启动.s -o startup.o
arm-none-eabi-gcc -c -mcpu=arm7tdmi -Wall -O2 -ffreestanding  不重要.页:1 -o notmain.o
arm-none-eabi-ld -nostdlib -nostartfiles -T memmap startup.o notmain.o -o notmain.elf
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf -O binary notmain.mb

运行时间:

vba notmain.mb

我运行linux,所以我没有vba-m,但我有vba.这个很好用.您可以使用copyjump将其包装或构建为gba rom,您今天可以购买的盒带不关心gba文件中的标题标题,因为盒带固件是实际的gba rom.mgba似乎需要正确的标题,看起来您知道如何操作.

您的关键问题是,VRAM只能通过16位或32位传输(str或strh)而不是8位传输(strb)正确访问.它会弄乱半字中的另一个像素.

EWRAM  256Kbytes, can be written as bytes, halfwords, and words
IWRAM  32Kbytes, can be written as bytes, halfwords, and words
PRAM   512 halfwords, can be written as halfwords or words (not bytes)
VRAM   98Kbytes, can be written as halfwords or words (not bytes)

C++相关问答推荐

定义_MISIX_C_SAL时,在MacOS上编译失败,并出现奇怪错误

%p与char* 等组合缺少的GCC Wform警告

修改pGM使用指针填充2-D数组但不起作用

为什么我得到更多的256假阳性在PKZIP解密密钥验证?

如何使用Python C API实现多线程程序?

标准的C17标准是用括号将参数包装在函数声明中吗

为什么在函数内部分配内存空间时需要添加符号?

是否所有C编译器在将浮点数转换为整型数时都会隐式删除小数?

使用AVX2的英特尔2022编译器的NaN问题&;/fp:FAST

为什么该函数不将参数值保存到数据 struct 中?

Sizeof(&Q;字符串&Q;)的正确输出是什么?

错误...的多个定义(&Q)首先在这里定义&

在C程序中使用Beaglebone Black UART的问题

C:面筋蛋白';为什么不刷新窗口?

在Ubuntu上使用库部署C程序的最佳实践

如何在C中计算包含递增和递减运算符的逻辑表达式?

如何找出C中分配在堆上的数组的大小?

在C中定义函数指针?

如何转义包含指令中的字符?

OpenGL 中的非渐变 colored颜色 变化