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目录下的所有文件copycygwin目录的bin下面覆盖

include patch目录下文件copycygwin目录的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里的.

 

金点 tangl_99

2002/12/24