在上一节中,我们理解了表达式和零页,接下来让我们一起来看看一些汇编代码与高级语言的对应关系:分支、循环与栈
本节参考了Structured Programming、The stack、Procedures and register saving
分支语句if else
汇编语言一般使用相反的构造方式,即将if
块中的代码紧跟在分支判断之后,若if条件成立则什么都不做,若不成立则直接跳过该块语句。
分支语句一般使用bcc/bcs/beq/bmi/bne/bpl/bvc/bvs做各种标志位的判断。
- 仅有
if
if(!++y) x++;
iny
bne no'overflow
inx
no'overflow:
;; rest of code
- 同时有
if else
if(前一个运算相等) {
//Then
} else {
//Else
}
;; Computation of the conditional expression.
;; We assume for the sake of the example that
;; we want to execute the THEN clause if the
;; zero bit is set, otherwise the ELSE
;; clause. This will happen after a CMP,
;; which is the most common kind of 'if'
;; statement anyway.
BNE else'clause
;; THEN clause code goes here.
JMP end'of'if'stmt
else'clause:
;; ELSE clause code goes here.
end'of'if'stmt:
;; ... rest of code.
循环语句
1. while循环
- while
while(前一次比较不相等) {
//Loop
}
loop'begin:
;; ... computation of condition, setting zero
;; bit if loop is finished...
beq loop'done
;; ... loop body goes here
jmp loop'begin
loop'done:
;; ... rest of program.
- do while
do {
//Loop
} while(前一次比较不相等);
loop'begin:
;; ... loop body goes here
;; ... computation of condition, setting zero
;; bit if loop is finished...
bne loop'begin
;; ... rest of program.
2. for循环
下面的程序实现16
字节的内存拷贝。为了简化代码,x
实际上从16
递减到1
,于是内存地址也进行了相应平移。
int *a = 0xC000;
int *b = 0xD000;
for (int x = 0; x < 16; x++) { a[x] = b[x]; }
ldx #$10 ; 16进制16
loop:
lda #$bfff, x
sta #$cfff, x
dex
bne loop
3. 嵌套循环
由于寄存器只有8
位,使得C64
汇编的嵌套循环非常简单。因为不管是递增还是递减,寄存器总会回到0
,此时就是循环退出的时机。不过,由于可以递增/递减的寄存器只有2个,因此两层以上的嵌套循环需要用到内存。为了不使内存读写拖慢运行速度,可以使用上一节提到的零页内存进行计数。当然,在循环中的代码会破坏x
、y
寄存器时,也可以使用内存进行循环计数。
在与C64
同芯片的NES
中,显示器IO的内存地址位于0x2007
,因此我们可以使用如下代码向其中写入4096
个0x20
。
ldx #$10
ldy #$00
lda #$20
loop:
sta $2007
iny
bne loop
dex
bne loop
栈
栈位于内存的第1
页。可以通过txs
指令修改栈指针,指针指向0xFF
表示栈为空。调用子程序的jsr
指令会将当前指令地址入栈,返回的rts
指令则会将出栈的地址加一后跳转到该处执行。中断时也会使用该栈。
当然,因为一页内存只有256
字节,因此递归调用会很容易溢出。
因此,一个类似C语言的函数应该如下。函数在开始时保护所有寄存器,在结束时恢复。
do'stuff:
pha
txa
pha
tya
pha
;; Rest of do'stuff goes here
pla
tay
pla
tax
pla
rts