GBA探索日记(二)(外篇 关于编译器)
现在网上有两种编译GBA程序的编译器.
1.gcc
由于GBA采用的是Arm公司的CPU,所以也需要去网上找到个armgcc覆盖你现有的gcc
gcc本身是个不错的c语言编译器.但是比起后面的讲的ArmSDT就有几个致命的缺点.
关于GBA所用的gcc有好几个不错的选择.
(1)devkitadv.你下载后就可以直接用了.它里面的gcc是已经装了armgcc的.
(2)HAM.它也是已经把gcc转换成armgcc的了.
(3)当然,你也可以自己配置armgcc
首先你应该有个常规的gcc.
可以在cygwin这个软件里找到完整的gcc.不过cygwin这个工具很大.有几百MB.
然后再找到armgcc以及一些补丁.具体的补丁我也不是很清楚.
但是你应该可以在Romandoo发布在cgdn论坛的贴子可以找到他提供的完整的补定包和AgbLib3.0
他在贴子中这么写到.
一: 安装CYGWIN工具
运行CYGWIN_SETUP目录下setup.exe进行cygwin的安装
选INSTALL FROM LOCAL DIRECTORY
在SELECT INSTALL ROOT diretory 选择安装目录 next>
再NEXT
在弹出的窗口中,在Perv Curr Exp 中选Curr
点开列表栏中各项,
选 base 的 cygwin ash
bash login sh-utils
选 devel 的 autoconf automake binutils gcc gdb make
选 shell下的 ash bash sh-utils
置NEW栏显示版本号,表示将其安装,而其它的工具选为skip表示跳过不安装
最后选next开始安装!
(注意,我为了节省空间,我删除了许多不重要的东西,其实cygwin下有高达几百m的各式各样的工具,今后你可根据自已需要到网上下载安装去)
二、安装任天堂官方开发包
在 AGB LIBRARY3.0中点 agbsetup1安装
注意,一定要安装到c:\agb目录下,因为这是官方的默认目录,有一些例子与这个目录关连了
所以最好选这个目录 ,不然可能编不过去。否则自已设置会麻烦一点的。
修改c:\autoexec.bat 文件增加如下代码行
SET AGBDIR=C:\AGB
三、安装CYGWIN补丁
(由于刚安装完的cygwin版本并不支持arm cpu,所以需要将某些程序、库覆盖成arm相关的程序库)
将arm-thumb-elf连目录一起copy到安装好的cygwin目录的\lib\gcc-lib下
将bin-patch目录下的所有文件copy到cygwin目录的bin下面覆盖
将include patch目录下文件copy到cygwin目录的include下面
下面应该看看gcc编译GBA的命令:
path=C:\devkitadv\bin;
gcc -o Game.elf Game.cpp
-lm
objcopy -O binary Game.elf Game.bin
这个应该是很简单的.
2.ArmSDT
ArmSDT是Arm公司官方提供的为Arm CPU写程序的编译器.
它在编译速度和编译质量上是上面的gcc无法相比的.
如果你有很大的头文件的,或者很大的程序要编译,那么你就应该使用ArmSDT
不过ArmSDT不是免费软件,但是网上还是有人把它盗了.所以你也可以去cgdn上找.
还有,www.arm.com提供ArmSDT的测试版,其实也可以用的.
不过用ArmSDT编译GBA程序需要个boot.s汇编程序.
下面我就把乐水提供的boot.s写在下面:
AREA my_1st_start, CODE, READONLY
IMPORT
|Image$$RO$$Limit| ;
End of ROM code (=start of ROM data)
IMPORT
|Image$$RW$$Base| ;
Base of RAM to initialize
IMPORT
|Image$$ZI$$Base| ;
Base and limit of area
IMPORT
|Image$$ZI$$Limit| ;
to zero initialize
IMPORT C_Entry
PSR_CPU_MODE_MASK EQU 0x1f ; CPU Mode
PSR_USER_MODE
EQU 0x10 ;
User
PSR_FIQ_MODE
EQU 0x11 ;
FIQ
PSR_IRQ_MODE
EQU 0x12 ;
IRQ
PSR_SVC_MODE
EQU 0x13 ;
Supervisor
PSR_ABORT_MODE EQU 0x17 ; Abort (Prefetch/Data)
PSR_UNDEF_MODE EQU 0x1b ; Undefined Command
PSR_SYS_MODE
EQU 0x1f ;
System
EX_WRAM
EQU 0x02000000
; CPU External Work RAM
EX_WRAM_END
EQU (EX_WRAM +
0x40000)
CPU_WRAM
EQU 0x03000000
; CPU Internal Work RAM
CPU_WRAM_END
EQU (CPU_WRAM +
0x8000)
WRAM
EQU EX_WRAM
; Entire Work RAM
WRAM_END
EQU
CPU_WRAM_END
SOUND_AREA_ADDR_BUF EQU
(CPU_WRAM_END - 0x10); Sound Driver Work Address
INTR_CHECK_BUF EQU (CPU_WRAM_END - 0x8); Interrupt
Check
SOFT_RESET_DIRECT_BUF EQU (CPU_WRAM_END - 0x6); SoftReset() Specify Return to
INTR_VECTOR_BUF
EQU (CPU_WRAM_END - 0x4);
Interrupt Branch Address
Cpu_Mode_USR EQU
0x10
Cpu_Mode_IRQ EQU
0x12
Cpu_Mode_SVC EQU
0x13
Cpu_Stack_USR EQU 0x03007F00 ; GBA USR
stack adress
Cpu_Stack_IRQ EQU 0x03007FA0 ; GBA IRQ
stack adress
Cpu_Stack_SVC EQU 0x03007FE0 ; GBA SVC
stack adress
ENTRY
b
start
DCD
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DCD
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DCD
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
start
mov
r0,
#Cpu_Mode_IRQ:OR:0x80:OR:0x40 ;
Disable interrupts
msr
CPSR_c,
r0 ;
Enable IRQ mode
ldr
r13,
=Cpu_Stack_IRQ ;
Setup IRQ stack pointer
mov
r0,
#Cpu_Mode_SVC:OR:0x80:OR:0x40 ;
Disable interrupts
msr
CPSR_c,
r0 ;
Enable SVC mode
ldr
r13,
=Cpu_Stack_SVC ;
Setup SVC stack pointer
ldr
r0,
=|Image$$RO$$Limit| ; Get
pointer to ROM data
ldr
r1,
=|Image$$RW$$Base| ;
and RAM copy
ldr
r3,
=|Image$$ZI$$Base| ;
Zero init base => top of initialized data
cmp
r0,
r1 ;
Check that they are different
beq
%F1
0 cmp
r1,
r3 ;
Copy init data
ldrcc
r2,
[r0], #4
strcc
r2,
[r1], #4
bcc %B0
1 ldr
r1,
=|Image$$ZI$$Limit| ;
Top of zero init segment
mov
r2,
#0
2 cmp
r3,
r1 ;
Zero init
strcc
r2,
[r3], #4
bcc %B2
mov
r0,
#Cpu_Mode_SVC:OR:0x40 ;
Only IRQ enabled
msr
CPSR_c,
r0
mov
r0,
#Cpu_Mode_USR:OR:0x40 ;
msr
CPSR_c,
r0 ;
Switch to USR mode
ldr
sp,
=Cpu_Stack_USR ;
Setup USR stack pointer
;
b
MyMain__Fv ; Now branch to
start of C code
;final_return_loop
; b
final_return_loop ;
In case of a return...
; END
start_vector
msr cpsr_c,#PSR_IRQ_MODE ; 切换到IRQ管理模式
ldr sp,
sp_irq
; 设置堆栈,为IRQ中断作准备
msr
cpsr_c, #PSR_SYS_MODE ;
切换到系统模式
ldr sp, sp_usr ;
设置堆栈用户模式
ldr r1,
=INTR_VECTOR_BUF ; 设置中断向量缓冲
adr r0,
intr_main ;
设置中断控制函数
str r0,
[r1]
ldr r1,
=C_Entry ; 进入C语言主循环体
mov lr,
pc
bx
r1
b
start_vector
; Reset
;final_return_loop
; b
final_return_loop ;
In case of a return...
ALIGN
sp_usr DCD WRAM_END - 0x100
sp_irq DCD WRAM_END - 0x60
EXTERN IntrTable ;中断向量表
GLOBAL intr_main ;中断控制
ALIGN
CODE32
intr_main
mov r3,
#0x4000000 ;
Check IE/IF
add r3,
r3, #0x200 ;
r3: REG_IE
ldr r2, [r3]
and r1,
r2, r2, lsr #16 ;
r1: IE & IF
ands r0, r1,
#0x2000 ; Game Pak Interrupt
loop bne loop
mov r2,
#0
ands r0, r1,
#0x0001 ; V-blank Interrupt
bne
jump_intr
add r2, r2, #4
ands r0, r1,
#0x0002 ; H-blank Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0004 ; V-counter
Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0008 ; Timer 0
Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0010 ; Timer 1
Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0020 ; Timer 2
Interrupt
bne jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0040 ; Timer 3
Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0080
; Serial Communication Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0100 ;
DMA0 Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0200 ;
DMA1 Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1, #0x0400 ; DMA2
Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x0800 ;
DMA3 Interrupt
bne
jump_intr
add r2,
r2, #4
ands r0, r1,
#0x1000
; Key Interrupt
jump_intr
strh r0, [r3,
#2]
; IF Clear 11c
ldr r1,
=IntrTable ; Jump to
user IRQ process
add r1,
r1, r2
ldr r0,
[r1]
bx
r0
END
需要注意的一点就是在你的GBA程序里,main主函数需要变成C_Entry.
还有,这个boot.s也初始了中断表
那么在你里的程序里也定义全局常量
如下:
const IntrFuncp IntrTable[13] = {
VBlankIntr, // V Blank interrupt
IntrDummy, // H Blank interrupt
IntrDummy, // V Counter interrupt
IntrDummy, // Timer 0 interrupt
IntrDummy, // Timer 1 interrupt
IntrDummy, // Timer 2 interrupt
IntrDummy, // Timer 3 interrupt
IntrDummy, // Serial communication interrupt
IntrDummy, // DMA 0 interrupt
IntrDummy, // DMA 1 interrupt
IntrDummy, // DMA 2 interrupt
IntrDummy, // DMA 3 interrupt
IntrDummy, // Key interrupt
};
最后就是编译命令.
不费话了,直接看下面的命令吧:
set PATH=f:\arm250\bin;%PATH%
zarmasm -Littleend -cpu ARM7TDMI boot.s
zarmcc -c -W -Wall -Otime -fpu none -Littleend -cpu ARM7TDMI
-apcs /narrow/noswst main.c -o
main.o -errors log.txt
zarmlink -bin -first boot.o -map -ro-base 0x08000000 -rw-base
0x3000000 boot.o main.o -o main.gba
del *.o
pause
你可以建立个make.bat
或者写个makefile.
不过有一点要提示你的是:
ArmSDT中的zarmmake.exe的功能很弱.建议你使用MS的nmake.exe
下面就是我为nmake.exe写的makefile:
TARGET =
main.bin
CFILES = main.c
OFILES = $(CFILES:.c=.o)
SFILES =
boot.s
BOOTFILES = boot.o
LIBFILES = *.a
ASFLAGS = -CPU ARM7TDMI -Littleend
CFLAGS =
-c -Wall -cpu ARM7TDMI -fpu none -W -apcs /narrow/noswst -Otime
LDFLAGS = -bin -errors log.txt -map
-ro-base 0x08000000 -rw-base 0x03000000
CC =
zarmcc
LD =
zarmlink
AS =
zarmasm
$(TARGET) :$(BOOTFILES) $(OFILES)
$(LD) $(LDFLAGS) -first $(BOOTFILES)
$(BOOTFILES) $(OFILES) -o $(TARGET)
$(BOOTFILES):$(SFILES)
$(AS)
$(ASFLAGS) $(SFILES) -o $(BOOTFILES)
$(OFILES):$(CFILES)
$(CC)
$(CFLAGS) $(CFILES) -o $@
clean:
@-if
exist *.bin del *.bin
@-if
exist log.txt del log.txt
@-if
exist *.o del *.o
@echo
All generated files have been cleaned!!!
关于makefile中这些语法我就不说了.
你可以去看看关于GNU MAKE指南.
最后需要强调一点的就是一个使用AgbLib里面的代码编译要注意的问题:
如果你使用了下面的代码,那么你就无法使用程序中的全局变量,而只能使用全局常量.
DmaClear(3,
0, EX_WRAM, EX_WRAM_SIZE, 32);
DmaClear(3,
0, CPU_WRAM, CPU_WRAM_SIZE -
0x200,32);
因为程序的全局变量是放在EX_WRAM中的,而全局常量是放在ROM里的.