//addr :代表8位地址 addr16:代表16位地址 data :立即数
//堆栈操作指令
1. 累加器进栈指令 PHA
PHA是隐含寻址方式的单字节指令,操作码是 48
功能是把累加器A的内容按堆栈指针S所指示的位置送入堆栈,然后堆栈指针减1
该指令不影响标志寄存器P的状态
2. 累加器出栈指令 PLA
PLA是隐含寻址方式的单字节指令,操作码是 68
功能是先让堆栈指针S+1,然后取加过1的S所指向的单元的内容,把它送累加器A
该指令影响标志寄存器P中的N,Z两标志位
3. 标志寄存器P进栈指令 PHP
PHP是隐含寻址方式的单字节指令,操作码是 08
功能是把标志寄存器P的内容按堆栈指针S所指示的位置送入堆栈,然后堆栈指针减1
该指令不影响标志寄存器P的状态
4. 标志寄存器P出栈指令 PLP
PLP是隐含寻址方式的单字节指令,操作码是 28
功能是先让堆栈指针S+1,然后取加过1的S所指向的单元的内容,把它送标志寄存器P
5. 堆栈用法举例
堆栈是一个存储区域,用来存放调用子程序或响应中断时的主程序断点,以及其他寄存器或存储器的内容.
当主程序需要调用子程序时,有一组中间结果及标志位的状态需分别保留在寄存器和标志寄存器中
但被调用的子程序执行时,也需要占用这些寄存器并影响标志寄存器,这样除了在执行调用指令时将断点
(调用指令后紧接着的一条指令地址)保存在堆栈中外,还必须将原主程序中保留在寄存器中中间结果和
标志位的状态保留在堆栈中,直到子程序结束,返回主程序时,再将这些中间结果及标志位状态送回寄存器
和标志寄存器中.
6502的堆栈地址是0100-01FF,但由于实际上系统也占用了堆栈,所以堆栈指针并不是指向栈底,我们可以
来测试一下.
进入 NCTOOLS下,A 2000
2000: TSX 堆栈指针低8位送寄存器X
2001: RTS
然后 G 2000,按 R 查看寄存器状态,结果发现 (X) = B4
这里说明堆栈指针的值是 01B4,但这是可变的
下面我们来看看堆栈指针随进栈和出栈的变化情况:
A 2000
2000: TSX ;堆栈初始地址低8位送寄存器 X
2001: STX $3000
2004: PHA ;寄存器A的数据压入堆栈
2005: TSX ;把这时堆栈地址低8位送寄存器 X
2006: STX #3001 ;结果送地址3001
2009: PHA ;寄存器A的数据压入堆栈
200A: TSX ;把这时堆栈地址低8位送寄存器 X
200B: STX $3002 ;结果送地址3002
200E: PLA ;从堆栈中弹出一个数据
200F: TSX ;把这时堆栈地址低8位送寄存器 X
2010: STX $3003 ;结果送地址3003
2013: PLA ;从堆栈中弹出一个数据
2014: TSX ;把这时堆栈地址低8位送寄存器 X
2015: STX $3004 ;结果送地址3004
2018: RTS
然后我们 G 2000
然后 V 3000
看见地址3000-3004的内容分别是 B4 B3 B2 B3 B4
说明堆栈指针地址 是 01B4 01B3 01B2 01B3 01B4
大家可见,当把一个数据进栈后,堆栈指针地址 减 1了,最开始堆栈指针地址是 01B4,后来不就是 01B3了
但把一个数据出栈后,是把最近压入堆栈的数据先出栈,大家可以看到,当又压入一个数据进栈后堆栈指针地址为
01B2,但是当我们弹出一个数据后,堆栈指针为 01B3,说明把最近的一个数据弹出了.
所以6502堆栈是 遵循 "先进后出"的原则,就好象我们把书一本一本的层叠,但我们拿书的时候,拿的却是最上面
的那本.
我举个例子:
A 2000
2000: LDA $00 ;地址 00的内容压入堆栈
2002: PHA
2003: LDA $0A ;地址 0A的内容压入堆栈
2005: PHA
2006: LDA $0D ;地址 0D 的内容压入堆栈
2008: PHA
2009: PLA ;注意:出栈时,先出的是地址0D 的内容,所以是把结果送地址0D,不是地址00
200A: STA $0D
200C: PLA
200D: STA $0A
200F: PLA
2010: STA $00
2012: RTS
当调用子程序时,系统自动将子程序 后一指令的地址 - 1 送堆栈,我们举例说明:
子程序从地址2100开始:
A 2100
2100: LDY #$00
2102: LDA #$01 ;堆栈指针地址高 8 位 送地址46
2104: STA $46
2106: TSX ;堆栈指针地址低 8 位 送寄存器X
2107: INX ;寄存器X内容加1
2108: STX $45 ;送地址 45
210A: LDA ($45), Y ;读取目标地址值送地址3000
210C: STA $3000
210F: INC $45 ;堆栈指针 低 8位加 1
2111: LDA ($45), Y ;读取目标地址值送地址3001
2113: STA $3001
2116: RTS
主程序从地址2000开始:
A 2000
2000: JSR $2100 ;调用子程序 $2100
2003: RTS
这里我们 G 2000,V 3000,发现(3000) = 02,(3001) = 20
执行完JSR $2100后,应该执行 地址 2003的指令,为什么是 2002呢?
因为返回后,程序计数器还会自动加 1.
程序在调用 子程序时,会保存 下一指令地址 -1,到堆栈,保存的顺序是 先存地址 高8位
再存地址 低8位.