| 
 | 
 
 本帖最后由 jiangzhengwenjz 于 2016-9-13 23:24 编辑  
 
【改版进阶教程】使用DevkitARM中的GNU Toolchain改版  
 
这个教程可说是进阶教程中的入门级,很多原理性的东西不会涉及,而非主体部分的操作也尽量作简单处理。 
 
先给出几个问题的解答: 
 
1. 为什么要用devkitarm改版? 
答:因为这个软件是免费的,也能满足普通改版者的需求。 
 
2. 所有改版者都需要devkitarm吗? 
答:并不,使用该开发包需要改版者预先掌握GBA ASM和C基础。 
 
3. 我们需要用它做什么? 
答:能够编译你C和ASM源文件。 
 
4. 为什么要自寻麻烦,不用IDE? 
答:因为好像没有好用的IDE。 
 
下面开始正文了 
 
一、软件的安装: 
 
所有教程里需要的软件在本帖末尾附带的链接中均可直接下载。 
 
先下载devkitProUpdater-1.6.0.exe,并进行安装。完毕后将其安装目录下的devkitarm\bin目录添加到PATH变量(不如就加到系统变量吧) 
 
然后下载make-3.81.exe,并进行安装。完毕后同样将bin目录加到PATH变量。(也可用MinGW中的make.exe,我就是用这个) 
 
此外如果安装devkitpro时出现报错的话,请注意 安装目录\msys\bin是否已经添加到PATH变量,若没有则手动添加,否则程序无法正常运行。 
 
最后,下载armips.exe并将其路径添加到PATH变量。 
 
至此,我们的开发环境已然搭建完成了。 
 
二、测试: 
 
在实际上手编写程序之前,先测试一下开发环境是否正确搭建,是非常有必要的。这时需要随便找一个可以用这些程序来进行编译的工程,比如野生双战工程https://github.com/jiangzhengwenjz/Double_wild_battle 
 
一般而言,在Readme文件中都会有详细的编译方法,对于这个工程也不例外。由于我们只是测试编译,所以没必要修改文件了,直接放入名为bpre0.gba的美版火红1.0 ROM,双击compile.bat就能进行编译。如果看到你的ROM被修改了,或是打开ROM测试一番,有几率遭遇野生双战了,那就说明编译成功了。(当然,要实际使用这个工程请仔细阅读readme,不然某些情况下100%死机) 
 
如果没成功,就检验第一步有什么遗漏。 
 
三、各程序简介: 
 
其实在添加环境变量时就很明显了,devkitpro安装目录下的devkitarm\bin里就是它所附带的GNU Toolchain,里面有非常多的工具,不过常用的并不多,这里只介绍一部分。- arm-none-eabi-as - 依据asm源文件输出中间目标文件
 
 - arm-none-eabi-gcc - 依据c源文件输出中间目标文件
 
 - arm-none-eabi-ld - 对目标文件进行连接
 
 - arm-none-eabi-nm - 列举目标文件中的符号
 
 - arm-none-eabi-objcopy - 从目标文件中获取纯数据
 
  复制代码 而armips的功能则较为复杂,它本身是asm的编译器(只不过部分用法和arm-none-eabi-as不同罢了,具体请阅读https://github.com/Kingcom/armips/blob/master/Readme.txt),不过可以直接打开/关闭某个文件,执行写入。此外,它还可以导入目标文件,并使用其中的全局变量。 
 
因此,如何编写程序其实已经很明显了,先通过as或是gcc生成目标文件(一般扩展名.o),然后对生成的目标文件进行连接,最后用armips进行字节变更,并导入用ld连接后的目标文件即可。 
 
但是,由于并没有一个IDE生成makefile,如果一个个文件去编译,连接,在文件较多的情况下,每次都要输入如此众多的命令,显然是不现实的。这也是为何我们要用make的原因。make是一个很强大的程序,默认的文件名为makefile,无扩展名。(或是用-f参数选择文件输入)我们能在其中预先定义好编译的规则,这样,只需要在命令窗口中输入make即可完成整体的编译。windows下在某目录下打开命令窗口的方法是:shift+右键即可看到该选项。 
 
当然,makefile有其专门的语法,不过我们的重点并不在此,只需掌握一些基本的方法即可。 
 
四、实战: 
空中楼阁般的谈话很难让人抓住问题的关键,所以干脆来看个例子吧。依然是刚才的野生双战。具体如何书写代码不是本教程的重点,不过看过这个例子应该也就了解得差不多了。 
 
了解makefile语法可以看这篇:http://blog.csdn.net/ruglcc/article/details/7814546/ 
 
(不过我个人觉得不需要看就是了,因为我们只需用到基本中的基本,复杂的部分要用到也可以依葫芦画瓢。。。) 
 
如果要用我这个模板的话,你也需建立一个src文件夹,用来存放源文件(当然,也可以有子文件夹) 
 
.gitignore, ReadMe.txt, compile.bat, compile.sh与工程本身无关,大可无视。我们直接来看makefile好了。(只需在cmd中输入make即可编译)- #后的是注释
 
 - #在这里我们定义变量,这样写规则时会简单
 
 - #这个工程没有C文件,所以不用gcc
 
 - #所有的源码都依赖于跳出,无需获得.bin以及各代码具体的地址,故无需objcopy及nm
 
 - AS=arm-none-eabi-as
 
 - LD=arm-none-eabi-ld
 
 - ARS=armips
 
 - #这种变量都相当于C中的宏
 
 - #gcc的参数可以看我其他工程
 
 - ASFLAGS=-mthumb
 
 - LDFLAGS=-z muldefs
 
  
- BLDPATH:=build
 
 - #这段不用懂,你可以认为就是设定了src中.asm文件的编译规则吧。
 
 - rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
 
  
- ASSRC := $(call rwildcard,src/,*.asm)
 
 - ASOBJS := $(ASSRC:%.asm=$(BLDPATH)/%.o)
 
  
- $(BLDPATH)/%.o: %.asm
 
 -         $(shell mkdir -p $(dir $@))
 
 -         $(AS) $(ASFLAGS) -c $< -o $@
 
 - #all实际上是一个伪目标,不过不用.PHONY也无所谓的
 
 - #--relocatable是为了从armips中导入链接后的.o文件,也就是暂时不指定链接的地址
 
 - #除非用objcopy直接获取bin,这就需要在linker.lsc中指定地址
 
 - all: $(ASOBJS)
 
 -         $(LD) $(LDFLAGS) -T linker.lsc --relocatable -o  "build\linked.o" $(ASOBJS)
 
 -         $(ARS)  insert.s
 
  复制代码 linker.lsc: 
(这个文件在链接时使用,来确定数据的排布,如果用objcopy的话,将0x08000000改为写入的地址,才能生成对应的bin)- OUTPUT_ARCH(arm)
 
 - MEMORY {
 
  
-         rom     : ORIGIN = 0x08000000, LENGTH = 32M
 
 -         ewram   : ORIGIN = 0x02000000, LENGTH = 4M - 4k
 
 - }
 
  
- SECTIONS {
 
 -         .text : {
 
  
-                 FILL (0xABCD)
 
  
-                 __text_start = . ;
 
 -                 *(.init)
 
 -                 *(.text)
 
 -                 *(.ctors)
 
 -                 *(.dtors)
 
 -                 *(.rodata)
 
 -                 *(.fini)
 
 -                 *(.data)
 
 -                 *(COMMON)
 
 -                 __text_end  = . ;
 
  
-                 __bss_start__ = . ;
 
 -                 *(.bss)
 
 -                 __bss_end__ = . ;
 
 -         _end = __bss_end__ ;
 
 -         __end__ = __bss_end__ ;
 
 -         } >rom = 0xff
 
 - }
 
  复制代码 再来看看armips所需的文件吧,也在makefile中要求编译了:- //一切C源码中的变量,和asm中的全局变量都可以直接在这里引用
 
 - //.open和.close打开 关闭文件
 
 - .gba
 
 - .thumb
 
 - .open "bpre0.gba", 0x8000000
 
 - ;remove the stupid limiter
 
 - .org 0x807F760
 
 - .byte 0,0
 
 - ;misc fixes
 
 - .org 0x802D812
 
 - .byte 0,0
 
 - .org 0x802D858
 
 - .byte 0,0
 
 - .org 0x802D8A6
 
 - .byte 0,0
 
 - .org 0x802D8F4
 
 - .byte 1,0x1C
 
 - .org 0x802DF36
 
 - .byte 8,0x1C
 
 - .org 0x802DE58
 
 - .byte 0,0
 
 - .org 0x802DE84
 
 - .byte 0,0
 
 - .org 0x802DEA4
 
 - .byte 0,0
 
 - .org 0x802DEC2
 
 - .byte 4,0x1C
 
 - .org 0x802D874
 
 - .word 0x2023D6C
 
 - .org 0x802D94C
 
 - .word 0x2023D6C
 
 - .org 0x802DF64
 
 - .word 0x2023D6C
 
 - .org 0x802DF04
 
 - .word 0x2023D6C
 
 - ;flee fix
 
 - .org 0x8016824
 
 - ldr r0, =double_wild_flee_fix+1
 
 - bx r0
 
 - .pool
 
 - ;audio fix
 
 - .org 0x8021D46
 
 - .byte 0,0x47
 
 - .org 0x8021D94
 
 - .word double_wild_audio_fix+1
 
 - ;pokemon catch
 
 - .org 0x802D44C
 
 - .byte 0x30,0x47
 
 - .org 0x802D480
 
 - .word double_wild_catch_fix+1
 
 - ;dex fix
 
 - .org 0x802D95C
 
 - ldr r0, =double_wild_dex_fix+1
 
 - bx r0
 
 - .pool
 
 - ;dex fix 2
 
 - .org 0x802D9DE
 
 - .byte 0,0x47
 
 - .org 0x802DA00
 
 - .word double_wild_dex_fix_2+1
 
 - ;ball throw fix
 
 - .org 0x80EF5F8
 
 - .byte 0x10,0x47
 
 - .org 0x80EF680
 
 - .word double_wild_ball_throw_fix+1
 
 - ;pokeball fix
 
 - .org 0x80A1E1C
 
 - ldr r1, =double_wild_pokeball_fix+1
 
 - bx r1
 
 - .pool
 
 - ;main func for normal RAND wildbattle
 
 - .org 0x8082AF4
 
 - ldr r3, =double_wild_main_fix+1
 
 - bx r3
 
 - .pool
 
 - ;my fault: the pure function deactivation
 
 - .org 0x8082A0C
 
 - .word 0
 
 - ;fade fix
 
 - .org 0x8011D72
 
 - mov r0, #0xA
 
 - lsl r0, r0, #0x10
 
 - .org 0x8011DFE
 
 - mov r0, #0xA
 
 - lsl r0, r0, #0x10
 
 - ;switch battle script fix
 
 - .org 0x81D86AD
 
 - .byte 0x28
 
 - .word double_wild_switch_bs_fix
 
 - ;skip fix
 
 - .org 0x801417E
 
 - mov pc, r1
 
 - lsl r0, r0, #0
 
 - lsl r0, r0, #0
 
 - lsl r0, r0, #0
 
 - .org 0x80141B0
 
 - .word skiphack
 
 - .org 0x8019688
 
 - ldr r0, =skipmsghack
 
 - mov r15, r0
 
 - .pool
 
 - //这里导入之前在makefile中链接好的目标文件
 
 - .org 0x8F00000
 
 - .importobj "build/linked.o"
 
 - .close
 
  复制代码 了解了基本的方法以后,就可以看一个更为复杂的例子了。下载HGSSANIMATION.zip,这是一个只完成了一小部分的HG开头动画(就是GAME FREAK那边),可以从中看一些参数,因为这用到了asm和C的混合编程。用C编程基本和网上入门书籍里面的内容差不多,甚至更简单一些。其中的bpre.ld是用于直接定义symbol的值的,很容易看懂。不过使用前还要再声明一下就是了,无论是常量,还是函数指针。连接时使用它就万事大吉了。 
 
五、杂项: 
 
    我觉得看过整个教程后,很多人会疑惑Hackmew的thumb编译器是怎么弄的呢?实际上也很简单,他的基本思路也就是先使用as生成目标文件,再用objcopy生成纯数据文件(.bin)。不过,只是用这么一个batch的话,当然也就不能处理多文件工程(除非你统统.include或是.incbin - -),同时也不能发挥ld的巨大作用了。若是直接用.org也是无法直接写入ROM的。因此,在面对文件较多或是涉及C语言的工程时,还是应当使用上述的方法,而非用Hackmew的batch。当然,.global, .thumb_func等原先用不上的directive现在也变得很重要了,这点也是很明显的。 
 
    就我个人观点,除了地图等必须可视化操作的数据,大部分的数据都可以用这种方法直接写入ROM,不用找空位,留空,计算空间等,同时也是一键写入,随时可增减、修改源码,也能避免很多低级错误,无疑是十分方便的(本质上,这是编译器帮我们处理了大量的工作)。不过对于很多表格等,也需要自己编写对应的程序来输出源文件便于阅读和修改,这点稍显麻烦。不过无疑用这种方法改版绝对是利大于弊的,能够随意地使用SYMBOL,绝对是无比快意的。 
 
六、资料: 
 
程序参数(大部分时候照抄就可以了,除了一些目录什么的): 
https://www.mankier.com/1/gcc 
https://www.mankier.com/1/ld 
https://www.mankier.com/1/as 
https://www.mankier.com/1/nm 
https://www.mankier.com/1/objcopy 
 
教程中资料下载: 
 
 
全文完 |   
 
 
 
 |