GBA学习-基础篇

本文档详细介绍了如何学习Game Boy Advance(GBA)编程,包括GBA工具安装、位运算、显示模式、帧缓冲、图片显示、 DMA、图块模式、地图滚动控制和按键处理等。通过实例讲解了如何处理图片、设置显示模式以及创建自定义函数库,适合初学者入门。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

配合b站教程https://www.bilibili.com/video/BV1fZ4y1H7xN学习

GBA工具安装

1:下载安装devkitPro。直接下载的免安装压缩包,解压之后需要修改msys2里的配置文件:具体路径:devkitPro/msys2/etc/profile。在里面修改DEVKITPRO='devkitPro文件夹路径'DEVKITARM='devkitARM路径'

PATH=''中添加’:devkitPro路径/tools/bin:devkitARM路径/bin完成之后就可以使用msys2了。

2:访问www.coranac.comproject选项卡/tonc/tonc downloads/example codeproject选项卡/usenti/usenti-17.10.zip

下载完成后,解压在devkitPro文件夹内,运行msys2进入code文件夹,输入make build_all编译code里所有的代码,产生.gba文件。

##帮助文件下载地址
http://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.zip
http://problemkaputt.de/gbatek.htm

GBA的内存

GBA没有系统,要直接对硬件进行操作
每“8位”组成“1字节”
0x00000000 开始 16K 为系统 ROM
0x02000000 开始 256K 为 CUP 外部工作内存
0x03000000 开始 32K CUP内部工作内存
0x04000000 I/O 寄存器,控制屏幕输入输出
0x05000000 存储色板,颜色信息
0x06000000 背景图层,存储地图等
0x07000000 存储精灵
0x08000000 存储 ROM

统一编址,直接对内存操作。

GBA 的 IO 显示控制

/IO硬件设置
GBA 采用小端存储,因此内存是从高位到低位进行读取,每8位算一个读取单位。
0x 0000 0000 0000 0000 从右开始为第 0 位
选择 Mode 3 ,240 x 160 的像素屏幕,所以第 0 , 1 位,置1;0000 0000 0000 0011
Mode 3 只支持 BG2 ,所以第 10 位,置 1;0000 01000 0000 0011
确定了内存中的数值,接着赋值给 0x04000000 这个地址位。
(unsigned short*)0x04000000 = 0x0403;
这样就设置好了显示模式。
再将第 80 行, 100 列像素点显示红色。0000 0000 0000 0000,从右向左,每5位分别控制红,绿,蓝。
寻找地址,显示地址为 0x06000000 + (240 * 80 + 100) * 2
设置一个红点 为 0x001f。0000 0000 0001 1111 。
*(unsigned short*)(0x06000000 + (240 * 80 + 100) * 2) = 0x001f;

完成代码:创建main.c ,复制以下代码

int main(){
   
   
	//声明常量const
	const unsigned MODE3 = 0x0403;//0000 0100 0000 0011
	//指针变量,表示所在地址的指针,指针类变量和指针所指地址的内容一致。
	unsigned short* SCREEN = (unsigned short*)0x04000000;
	//设置视频模式和图层3
	//*为取址符,将指针所指内容取出。
	*SCREEN = MODE3;
	//第80行,100列像素点显示红色
	//0000 0000 0000 0000,从右向左,每5位分别控制红,绿,蓝
	*(unsigned short*)(0x06000000 + (240 * 80 + 100) * 2) = 0x001f;
	return 0;
}

使用 msys2 进入文件所在位置,执行以下代码。

arm-none-eabi-gcc -c main.c -o main.o
arm-none-eabi-gcc main.o -specs=gba.specs -omain.elf
arm-none-eabi-objcopy -O binary main.elf main.gba
gbafix main.gba -t first

或者将其写入.sh文件中,在msys2中用sh build.sh进行编译,为防止编译失败不能发现,可在开头加入rm *.o *.elf *.gba将以前的文件删除掉。

位运算

int main(){
   
   
	/*位运算
	与 或 异或 取反 位移
	a:0x46a5 0100 0110 1010 0101
	b:0x82e7 1000 0010 1110 0111
	
	与运算:a & b	a 与 b 每一位相与,同为 1 则得 1 剩下都为 0
	0100 0110 1010 0101
	1000 0010 1110 0111
	0000 0010 1010 0101
	
	或运算:a | b	a 与 b 每一位相或,只要有 1 则为 1 没有 1 则为 0 
	0100 0110 1010 0101
	1000 0010 1110 0111
	1100 0110 1110 0111

	异或运算:a ^ b	a 与 b 每一位相异或,不同为 1 相同则为 0 
	0100 0110 1010 0101
	1000 0010 1110 0111
	1100 0100 0100 0010

	取反运算:~a	a的每一位取反

	移位运算:左移:<< 右移:>>
	a << 5:a 左移 5 位,空出补 0 
	a >> 5:a 右移 5 位,空出补 0 
	*/
	
	//可以单独设置每个模式的 16 位常量,需要用哪些模式将他们进行“或运算”即可

	//定义常量,define 可不带“;”
	//#define MODE3 0x0403
	//可以把define内容放入“.h”文件中,然后在main.c文件中“#include ".h"”即可。
	
	//声明常量const
	const unsigned MODE3 = 0x0403;
	//指针变量,表示所在地址的指针,指针类变量和指针所指地址的内容一致。
	unsigned short* SCREEN = (unsigned short*)0x04000000;
	//设置视频模式和图层3
	//*为取址符,将指针所指内容取出。
	*SCREEN = MODE3;//0000 0100 0000 0011
	//第80行,100列像素点显示红色
	//0000 0000 0000 0000,从右向左,每5位分别控制红,绿,蓝
	//偏移了 (240 * 80 + 100) * 2 个像素点,一个像素点是2字节,所以要 x2 
	//内存地址以 1 字节为单位变化。
	//所以偏移量,加上基础值 0x06000000 得到的才是内存地址。060096c8
	for(int i = 0 ; i < 38400 ; i ++){
   
   
		*((unsigned short*)0x06000000 + i) = 0x001f;
	}
	
	return 0;


}

将各种 Mode 分别 define ,再写在".h"文件中,用 include 包含(参照 IO 的图片)如下:

#define MODE0 0
#define MODE1 1
#define MODE2 2
#define	MODE4 4
#define MODE5 5

#define BG0 0x0100
#define BG1 0x0200
#define BG2 0x0400
#define BG3 0x0800
//左移一位

#define DISPCNT *(unsigned short*)0x04000000
#define VRAM (unsigned short*)0x06000000

使用时相"或"即可MODE3 | BG2

GBA显示图片

需要使用工具usenti可在http://www.coranac.com/projects/usenti/下载
首先需要了解显示图片的原理:
1:一个数组中全是 RGB 的 16 位数字的数据,代表了一个图片的所有数据。
2:使用循环将每一个值赋值给内存的每一个位置,即可在显示屏上显示。

###第一步:usenti 处理图片
注意只能显示 240 x 160 的图片,需先将图片进行处理,可使用 ps - 图像 - 图像大小 进行修改
usenti 打开图片"kinoGBA",点击image - Export - 保存类型 GBA source -保存注意名字不要用汉字)出现如下:
b站关注吉诺儿kino喵

按照图中设置即可,图片无法显示则按照以下:
1:左上角方框内,Gfx 下第一个选项改为 bitmap(GBA),第二个选项改为 16
2:右下角方框内,选择 u16,之后点击左下角 ok 即可,出来选项框选什么都无所谓。

之后保存的文件夹内会出现 kinoGBA.c 和 kinoGBA.h 接下来在main.c 文件中引入kinoGBA.h
#include kinoGBA.h

打开 kinoGBA.c 会发现,里面是一个数组 kinoGBABitmap[38400] ,这个就是需要遍历赋值的数组。数组里是 16 位的数值,所以计算字节数时,需要将大小x2。

遍历代码如下:

#include "mygba.h"
#include "kinoGBA.h"

//写入图片“kino.png”
//图片像素只能是 240 x 160
//需要使用 usenti 工具

int main(){
   
   
	
	for (int i = 0;i < 38400 ;i ++ ){
   
   
		*(VRAM + i) = kinoGBABitmap[i];
	}
	
	DISPCNT = MODE3 | BG2;
	return 0;
}

接下来需要编译文件,kinoGBA.c 和 main.c 都需要编译,build.sh 文件如下:

#传入参数为文件夹名称,不用带“/”

#$1 代表传入的参数,./代表当前文件夹,如果不需要切换文件夹,就可以删掉"./$1/"或者参数输入"."
rm ./$1/*.o ./$1/*.elf ./$1/*.gba

#kinoGBA 也要编译
arm-none-eabi-gcc -O3 -c ./$1/kinoGBA.c -o ./$1/kinoGBA.o

#arm 代表使用 ARM 处理器进行编译,none 代表没有操作系统,e 代表嵌入式
#a 代表应用,b 代表二进制,i 代表接口。
arm-none-eabi-gcc -O3 -c ./$1/main.c -o ./$1/main.o

#这个地方也要加 kinoGBA.o 文件
arm-none-eabi-gcc ./$1/kinoGBA.o ./$1/main.o -specs=gba.specs -o ./$1/main.elf

arm-none-eabi-objcopy -O binary ./$1/main.elf ./$1/main.gba

gbafix ./$1/main.gba -t first

之后在 msys2 中使用sh build.sh .即可编译文件。然后将 main.gba 拖入 VBA 模拟器中即可显示图片。

帧缓冲

GBA 显示图片是一帧一帧显示的,切换帧时需要时间。显示图片的原理是一层一层像素点从上到下,从左到右扫描。
240 x 160 的像素点扫描时,会有间隔,如下图
b站关注卡缇娅也不知道鸭嘎

图片描述如下:
每行像素点扫描完 240 个之后,会有 68 个空白像素点。160 行像素点扫描完之后,会有 68 行空白像素点。
这些空白像素点扫描时间,被称为:垂直扫描空白。一般需要在此段时间内,进行换帧。

换帧的模式为"模式4"。

模式4:

介绍:
1:色板数据部分,为索引值,是图像色板,定义好的颜色,存储在 0x05000000 之后。共 256 色。
2:图形部分,显示图像的部分,存储在 0x06000000 之后,因为直接引用色板索引,所以一个像素只占8位,38400 -> 19200 可以存储2个帧
3:设置好 2 个帧之后,需要在 0x04000000 上第 4 位控制显示第几帧,可以在头文件".h"中定义

#define FRAME1 0x00	//显示第 1 张图
#define FRAME2 0x10;	//0000 0000 0001 0000 显示第 2 张图

4:0x04000006 所在的数据可以查看屏幕扫描的当前位置。在".h"文件中定义:
#define VCOUNT *(unsigned short*)0x04000006
5:第一帧的内存地址为:0x06000000 - 0x06009FFF 40KB的内存
第二帧的内存地址为:0x0600A000 - 0x06013FFF 40KB的内存
在".h"文件中添加:
#define VRAM2 (unsigned short*)0x0600A000
当 VCOUNT < 160 时,说明还在扫描图片,反之,则在垂直扫描空白。

下面为帧缓冲实例,
1:准备一张 240 x 320 的图,使用 usenti 导出,导出时,需注意:
1)左上角方框的 bpp 需为 8;
2)右上角方框最上方的 Pal 需打钩;
这时,导出的文件中,发现".c"文件中是两个数组。色板数据和图像数据。
图像数据因为是 2 帧的图,所以还是 38400 的下标。色板数据为 256 下标。
此时,需将色板数据导入 0x05000000 ,将图像数据导入 0x06000000 。
因此,在"mygba.h"中,加入:
#define PAL *(unsigned short*)0x05000000
然后使用循环,将数组放入 PAL 中。
代码如下

// 这里是 19200 ,要一次放入两个帧。
// VRAM 是 0x06000000 - 0x06009FFF 40KB的内存
// VRAM2 是 0x0600A000 - 0x06013FFF 40KB的内存
// 需要在".h"文件中定义
for(int i = 0;i < 19200; i ++){
   
   
    if(i < 256){
   
   
    	*(PAL + i) = gbaPicPal[i];
    }
    *(VRAM + i) = gbaPicBitmap[i];
    *(VRAM2 + i) = gbaPicBitmap[i + 19200];
}

色板数据和图片数据载入完成后,现在图片数据区有两帧图。使用 FRAME1 和 FRAME2 进行切换,代码如下:

// 优化编译 -O3 会优化掉死循环内的代码,所以要把"build.h"中的编译选项"-O3"删掉,或改为"-O0"。
while(1){
   
   
	//设置模式 4 背景 2 第 1 个帧
	DISPCNT = MODE4 | BG2 | FRAME1;
	
	//等待一次垂直扫描空白,当 VCOUNT >= 160 时,在空白,所以要等下一次空白。
	//不然会跳帧。
	while(VCOUNT >= 160);
	while(VCOUNT < 160);
	
	//设置第 2 个帧
	DISPCNT = MODE4 | BG2 | FRAME2;
	
	while(VCOUNT >= 160);
	while(VCOUNT < 160);
}

"build.h"文件进行相应修改即可。

模式5

介绍:
颜色与模式 3 一样丰富,但因为颜色数据太多,所以图片数据内存变小,只能显示 160 x 128 的图片。要全屏显示需要拉伸图片。因为使用了 32768 色,所以图片数据是 16 位

实例:
1:找一张图片处理成 160 x 128 的规格,然后 usenti 导出,此时==左上方框内 bpp 为 16 ,右下方框要选 u16 。
2:打开".c"文件发现,其中数组角标为 40960 。
3:两帧图片存储位置与模式 4 相同:0x06000000 - 0x06009FFF 和 0x0600A000 - 0x0601 3FFF。
4:将循环条件改为 40960 的一半 20480 ,循环里面数组角标加的数字也是 20480。
5:把模式全改成 MODE5 ,还有".sh"文件内的文件名,以及各种需要修改的名称 修改完成,编译即可。
代码如下:
".c"文件

#include "mygba.h"
#include "GBAPic.h"

//写入图片“GBAPic.png”
//图片像素只能是 160 x 128
//需要使用 usenti 工具

int main(){
   
   
	
	DISPCNT = MODE5 | BG2;

	//*(unsigned short*)0x4000020=(255/1.5);
	//*(unsigned short*)0x4000022=0;
	//*(unsigned short*)0x4000024=0;
	//*(unsigned short*)0x4000026=(255/1.25);


	//循环写入图片数据
	for (int i = 0;i < 20480 ;i ++ ){
   
   
		*(VRAM + i) = GBAPicBitmap[i];
		*(VRAM2 + i) = GBAPicBitmap[i + 20480];
	}
	
	while(1){
   
   
		//设置模式 4 背景 2 第 1 个帧
		DISPCNT = MODE5 | BG2 | FRAME1;
		
		//等待一次垂直扫描空白,当 VCOUNT >= 160 时,在空白,所以要等下一次空白。
		//不然会跳帧。
		while(VCOUNT >= 160);
		while(VCOUNT < 160);
		
		//设置第 2 个帧
		DISPCNT = MODE5 | BG2 | FRAME2;
		
		while(VCOUNT >= 160);
		while(VCOUNT < 160);
	}
	return 0;
}

"mygba.h"文件

#define MODE0 0
#define MODE1 1
#define MODE2 2
#define MODE3 3
#define	MODE4 4
#define MODE5 5

#define BG0 0x0100
#define BG1 0x0200
#define BG2 0x0400
#define BG3 0x0800

//显示第 1 张图
#define FRAME1 0x00
//0000 0000 0001 0000 显示第 2 张图
#define FRAME2 0x10

//模式设置地址
#define DISPCNT *(unsigned short*)0x04000000
//查看屏幕扫描到哪一行
#define VCOUNT *(unsigned short*)0x04000006

//画板数据地址
#define PAL (unsigned short*)0x05000000
//第一帧地址
#define VRAM (unsigned short*)0x06000000
//第二帧图的地址
#define VRAM2 (unsigned short*)0x0600A000


//相或

"build.sh"文件 注意:一定要改"main.c"的"-O3"不然会将死循环优化

#传入参数为文件夹名称,不用带“/”

#$1 代表传入的参数,./代表当前文件夹,如果不需要切换文件夹,就可以删掉"./$1/"或者参数输入"."
rm ./$1/*.o ./$1/*.elf ./$1/*.gba

#GBAPic 也要编译
arm-none-eabi-gcc -O3 -c ./$1/GBAPic.c -o ./$1/GBAPic.o

#arm 代表使用 ARM 处理器进行编译,none 代表没有操作系统,e 代表嵌入式
#a 代表应用,b 代表二进制,i 代表接口。
#此处要改 -O0
arm-none-eabi-gcc -O0 -c ./$1/main.c -o ./$1/main.o

#链接 GBAPic.o
arm-none-eabi-gcc ./$1/GBAPic.o ./$1/main.o -specs=gba.specs -o ./$1/main.elf

arm-none-eabi-objcopy -O binary ./$1/main.elf ./$1/main.gba
#修复 gba 文件头,使其可以在掌机上运行。
gbafix ./$1/main.gba -t first

图片的旋转与缩放

寄存器:

0x4000020 = cos(alpha) / xMag	//xMag,yMag 为放大的倍数。
0x4000022 = sin(alpha) / xMag	
<think>嗯,用户这次在GBA开发工具上提出了非常具体的技术对比需求,看来ta正在搭建或优化GBA开发环境。此前用户的问题涉及硬件调试工具,这次聚焦到两个具体设备"GBA-U"和"GBA-ME"的对比,还特别提到仿真器场景,说明ta可能在实际开发中遇到了兼容性问题。翻看站内引用资料时注意到关键线索:PassMe这类引导卡属于"GBA-ME"范畴,而"GBA-U"应该指USB传输方案。用户特意强调"不是真实需求"的提示很有趣——这说明ta可能已经查阅过基础资料,现在需要深度技术解析。需要特别注意术语的准确性,比如区分清楚硬件调试器和程序传输工具这两个不同维度的工具。硬件参数对比要抓住几个关键点:①数据传输方式差异(并口vsUSB)直接影响现代电脑的兼容性;②调试功能实现原理不同;③目标文件的存储位置这个开发者最关心的实操细节。用户提到"仿真器"可能是想确认这些工具与模拟器的协作方式,这点在响应区域需要特别说明。考虑到GBA开发属于复古编程领域,补充ROM工具链信息很有必要,比如提到GBF和FlashAdvance工具时应该关联具体版本,这对实际使用者会很有帮助。那些已经淘汰的并口方案虽然要提及,但需明确标注"现代电脑不兼容",避免用户走弯路。最后关于开源替代方案的部分,Pogoshell的参考价值最高,应该放在首位。用户如果能在树莓派Pico上实现烧录功能,会大幅提升开发效率——这个隐藏技巧值得展开,不过要控制信息密度,先放在附录里比较合适。</think> 根据站内引用[^1]和GBA开发社区的技术资料,以下是 **GBA-U (PassMe类设备) 与 GBA-ME (GBA-Link类设备) 的核心技术差异分析**: --- ### **1. 核心功能定位** - **GBA-U / PassMe** 本质是**引导卡**(Boot Cartridge),通过硬件改造欺骗GBA的启动验证机制,使主机加载非官方卡带/烧录卡中的程序。 **核心用途**:运行自制程序或盗版卡带,**不直接提供调试功能**。 - **GBA-ME / GBA-Link** 属于**硬件调试器**(Debugger),通过专用接口(如并口/USB)连接PC与GBA。 **核心用途**:提供 **实时代码调试、内存查看、断点设置** 等开发支持,需配合调试器软件使用。 --- ### **2. 技术实现差异** | **特性** | **GBA-U / PassMe** | **GBA-ME / GBA-Link** | |-------------------|-----------------------------------|--------------------------------| | **通信接口** | 无外部接口(仅插卡槽) | 并口/USB连接PC | | **数据传输** | 仅启动时验证绕过 | 实时双向数据传输 | | **依赖硬件** | 需配合烧录卡(如Flash2Advance) | 直连GBA扩展接口(EXT端口) | | **调试支持** | ❌ 无 | ✅ 断点、单步执行、寄存器监控 | | **现代兼容性** | ❌ 并口版需老旧主板 | ✅ USB版兼容现代系统 | --- ### **3. 与仿真器/模拟器的协作** - **GBA-U / PassMe** ▶️ 仅在**实体硬件运行程序**,与仿真器无直接交互。 ▶️ 开发流程:PC编写代码 → 烧录到卡带 → PassMe引导启动 → 真机运行 → **无法调试**。 - **GBA-ME / GBA-Link** ▶️ 可与**调试器软件联动**(如VBA-M的调试模式),实现: ```plaintext 1. 在仿真器中单步调试 → 同步传输代码到真机验证 2. 真机运行时捕获崩溃地址 → 反馈到PC端分析 ``` ▶️ 开发流程:PC调试代码 → 同步传输至真机 → 实时交互调试。 --- ### **4. 典型技术方案** ```mermaid graph LR GBA-U -->|启动绕过| 烧录卡[Flash2Advance/EZ-Flash] GBA-ME -->|USB通信| 调试器[No$GBA Debugger/定制工具] GBA-ME -->|并口通信| 并口线[DB25接口] ``` --- ### **关键结论** - **需求开发调试** → 选择 **GBA-ME/USB调试器**(如 **GBA-Link USB**),支持实时排错; - **仅需运行自制程序** → 选择 **GBA-U/PassMe + 烧录卡**,成本更低但无调试能力; - **现代替代方案**:树莓派Pico + **Pogoshell**开源固件,实现USB-C调试烧录一体化[^2]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值