[译文]GBAGuy的GBA ASM教材 第四章 分析第一个程序

四章 分析第一个程序

 

今天我们会讨论一下第二章的那段程序,不过之前我们要再多学点指令。

我们要学的第一条指令是STR的另一种形式。我们能以32位方式工作,其实也能以16位,8位方式工作,不过你要确定你使用的地址是不是按照32位,16位对齐了。这里几个例子演示了如果读取和写入16位和8位的值:(我这里只用了寄存器寻址方式,其实都可以用)


ldrh r0, [r1] LDR后面的H意思是halfword(半字,word=32,half=16,byte=8 bits),这条指令读取r1所指地址处的16bits到r0。高16位清零。

 

ldrsh r0, [r1] 读取有符号的半字。和上面这条差不多,只是这个16位数会符号扩展到r0的高16位。如果你读取的数是0xFE3A,那么r0就变成0xFFFFFE3A。如果有人不知道符号扩展的话,就是把最高位填充到高位。如果你读取的数是0x0342, r0就是0x00000342。

 

ldrb r0, [r1] 读取8bits数据,高24位清零。

 

ldrsb r0, [r1] 读取有符号的8bits数,并且符号扩展。(bit 7填充到高位去)

 

指令STR也有类似的变种,不过没有符号扩展的那种。

 

分支指令

 

分支指令可以让我们跳转到另一段代码,也可以是条件跳转。一般来说分支指令是这样的:
b label_name 这条指令将会跳转到标签名之后的代码。希望你知道什么是标签,如果你不知道话,标签是这样:
label: 标签是一个词(规则基本和C一样),后面有一个冒号。

 

条件分支基本一样,只是b后面有一个条件代码。条件有:
EQ Equal,等于
NE Not Equal,不等
CS Carry Set,进位置位
CC Carry Clear,进位为零
HS Unsigned Higher or Same,无符号数比较,大于等于
LO Unsigned Lower,无符号数比较,小于
MI Minus/Negative,负数
PL Plus/Positive or Zero,正数和零
VS Overflow,溢出
VC No Overflow,没溢出
HI Unsigned Higher,无符号数比较,大于
LS Unsigned Lower or Same,无符号数比较,小于等于
GE Signed Greater than or Equal,有符号数比较,大于等于
LT Signed Less than,有符号数比较,小于
GT Signed Greater than,有符号数比较,大于
LE Signed Less than or Equal,有符号数比较,小于等于
AL Always,总是跳转,默认,不需要写明
NV Never,总不跳转,无用


 
下面我们来分析一下第二章的程序。

 

.arm - 告诉汇编器使用ARM指令集
.text - 告诉汇编器将下面内容放置于TEXT区段(代码区,一般是GBA卡的ROM)
.global main - 告诉汇编器允许其他文件访问main,这是必须的,这样连接器才能找到main函数
main: - 指明main函数的标签
mov r0, #0x4000000 - r0赋值为0x4000000(这是REG_DISPCNT的地址, LCD屏幕的控制寄存器)。
mov r1, #0x400 - r1赋值为x400 (不能直接mov 0x403,因为这个数不能由一个8bits数加位移获得)
add r1, r1, #3 - r1加3 (r1 = r1 + 3), r1现在是0x403。(含义是mode 3和BG 2)
str r1, [r0] - 写入到REG_DISPCNT
@blank - asm中的注释是以@开始的,不过我们把文件保存为.S文件,再用GCC编译的话,也能用C/C++风格的注释符号
      mov r0, #0x6000000  @ VRM地址
      mov r1, #0xFF       @ 红色
      mov r2, #0x9600     @ 要填满整个屏幕需要写入的字数(2byte)
loop1:                    @ 一个标签
   strh r1, [r0], #2   @ 把r1中的16bits数赋值到r0所指的地址处
                       @ 然后r0加2,因为16bits是2bytes。
   subs r2, r2, #1     @ r2减1(r2 = r2 - 1). SUB后面的S表明需要根据结果影响CPSR

                       @ (当前处理器状态寄存器)中的标志位
   bne loop1           @ 如果状态寄存器表明结果不为0,则跳转到loop1

infin:                 @ 一个无限循环
   b infin

 

请注意SUB后面的那个S,如果不写这个S,那么状态寄存器就永远不会显示出结果为0,于是这个循环变成了死循环。算术指令(还有些其他指令)只有加上S才会影响到CPSR,这很重要。

 

显示图片

 

我觉得你可能已经打算要显示图片了,那么来做做看吧。如果你没有Bimbo这个软件,那么到GbaDev下一个,然后找一张你喜欢的照片,把它导成二进制文件,命名为“pic.bin”,模式设置为3。下面我会列出完整的程序和注释来解释如何显示:
.arm
.text
.global main
main:
    mov r0, #0x4000000  @ 通常的初始化设置
    mov r1, #0x400      @ 0x403是BG 2 enable,mode 3.
    add r1, r1, #3
    strh r1, [r0]       @ 16位的写操作
    mov r0, #0x6000000  @ VRAM
    ldr r1, =pic        @ 用这种参数是标签的LDR形式,会把该标签的地址写入到r1
    mov r2, #0x960      @ 需要填满整个屏幕要写入的次数,这次是32字节一写,方式和上段代码不同。
loop1:
    ldmia r1!, { r3,r4,r5,r6,r7,r8,r9,r10 } @ 将r1所指的地址处的数据32bits一组读取到列表中的各个寄存器中
                                            @ 每读取一次地址+4,最后读取完这个地址写回到r1,因为r1后面有个!
                                            @ 要注意这个指令不使用方括号,但是r1还是用作指针。
    stmia r0!, { r3,r4,r5,r6,r7,r8,r9,r10 } @ 将列表中的各个寄存器的值依次写入到r0所指的地址中,每次写入32bits
                                            @ 并且地址+4,最后地址写回到r0。
                                           
    @ 这些指令算是一个比较快的内存复制的方法,不过只能用在你有很多寄存器空闲的情况
    @ 这里使用的8个寄存器是r3-r10,r2用作计数器了,r0和r1是地址寄存器,不要把这些写到列表中
   
    subs r2, r2, #1     @ 更新标志位的减法
    bne loop1           @ r2不是0的话会不停循环
   
infin:
    b infin             @ 无限循环
   
.ltorg                  @ 给汇编器一个位置来放置立即数“池”,为了“ldr REG,= (s)”这种指令。
pic:                    @ 一个指向include进来的二进制数据的标签
    .incbin "pic.bin"   @ include一个二进制文件

挑战:到gbadev去下GBATEK,然后看看你能不能够在mode4下面显示这个图片。你需要减少一些颜色,因为mode 4是8位色调色板模式的。
提示:如果你还是使用二进制方式导出,那么先导出调色板,使用一个循环进行复制(使用写回),然后在用一个类似的循环复制图片。调色板大小是0x200 bytes,图片大小时0x12c00 bytes。

 

我会在第五章末尾给出答案。

 

在第五章,我们会较细致地研究一下GBA的图片系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值