logo
 
首页-> 产品信息-> 8位单片机
 
入门指南
开发&工具
应用方案
客户支持
 
学习e条龙
一、单片机简介 二、指令系统 三、IO端口 四、中断系统
五、定时/计数器 六、串行接口 七、模拟外设 八、其它硬件
九、开发工具 十、应用实例 十一、附录  

SPMC65系列单片机指令系统

内容索引
2.1 符号约定 2.4 常用伪指令
2.2 寻址方式  2.5 常用程序实例
2.3 指令系统  

2.1 符号约定:

SPMC65 系列单片机指令按其功能可划分为:

•  数据传送指令,包括立即数到寄存器、寄存器到寄存器、寄存器到存储器、存储器到寄存器的数据传送操作;

•  算术运算,包括加、减、比较运算;

•  逻辑运算,包括与、或、异或、移位等操作;

•  位操作,包括位测试、位置位、位清除、位取反等操作;

•  转移指令,包括条件转移、无条件转移、中断返回、子程序调用等操作;

•  控制指令 ,如开中断、关中断、清除进位、清除负标志等操作;

为了将指令的用法讲解清楚,下表将指令系统叙述过程中要用到的符号列出。

表 2. 1 指令系统叙述过程中要用到的符号

A

累加器 (A)

X

X 寄存器

Y

Y 寄存器

PC

程序指针 (PC)

SP

堆栈指针 (SP)

P

状态寄存器 (P)

N

负标志位( N ):该位指示一个数据或者运算结果的 bit7 的状态。用户可以通过该位进行条件跳转。

V

溢出标志位 (V) :该位仅用于有符号数的算术运算。如果两个同符号数相加或两个不同符号数相减,结果大于 127 或者小于 -128 ,该位置 1 。

D

十进制模式标志位 (D) :在 SPMC65 系列芯片中有两种算术运算模式:二进制模式和十进制模式,该位标明了当前的运算模式。用户可以通过相应的指令来选择一种运算模式。

I

中断屏蔽位 (I) :该位用于使能 / 禁止除“非屏蔽中断源( NMI )”以外的所有中断源。将该位置 1 , CPU 将忽略中断请求,置 0 , CPU 将接受中断请求。

Z

零标志位( Z ):数据和算术运算结果标志位。当数据或算术运算结果为 0 时,该位被置 1 ;为其它值时,该位被置 0 。

C

进位标志( C ):当加法操作中产生进位或减法操作中没有产生借位时,该位被置 1 。此外,移位或循环指令 也会改变该位的值。

#dd

8 位( BIT )的立即数

Label

程序标号

程序注释前缀

Adr 08

8 位存储器地址

Adr16

16 位存储器地址

aa

8 位存储器地址

aaaa

16 位存储器地址

Cycles

指令周期数, 1 个 Cycle 的时间为 1 个 CPU 时钟周期,即 1/F SYS

SPMC65 系列单片机的汇编指令格式规定

汇编指令包括四部分,格式如下:

[ 标号 : ] 操作码 [ 操作数 ] [ ; 注释 ]

[ ] 中的内容为可选项

标号:标识了一条指令的位置,可以使用标号作为访问该指令的地址。标号应以字母或下划线‘ _ '开头(局部标号还可以用问号‘?'开头),以冒号‘ : '结束;标号的命名可以取数字、字母和下划线的组合,长度为 2~32 个字符,区分大小写。不要用指令记助符、寄存器名称或系统保留字的字符串作为标号。而宏名可以字母、下划线或百分号 (%) 开头。标号的后缀可以是字母、下划线、问号或井号‘ # '。

操作码: 指令区域,用于写指令。

操作数:操作数可以是程序中的数据或地址。当操作码为单字节时,无操作数。当进行立即数寻址时,操作数为一个字节数据,使用一个符号来标识其所在位置。当操作数作为程序地址时,其实就是一个标号。

注释:注释可以提高程序的可读性。在注释语句的前面应该加一个分号‘ ; '。

例如:

LDA #5 ; 将 8 位立即数 5 载入累加器 A

STA Counter ; 将累加器 A 中的数载入变量 Counter 中

数制

  汇编器默认数制为十进制,如果要表示成其它数制,需要给数字加上后缀或前缀。下表列出了各种数制的表达方式。应用时,将其与数值组合即可。

二进制

以“ % ”为前缀或以“ B ”、“ b ”为后缀

10101011B

八进制

以“ O ”、“ o ”或“ Q ”、“ q ”为后缀

253Q

十进制

以“ D ”或“ d” 为后缀或无后缀

171

十六进制

以“ H ”、“ h” 为后缀或以“ $ ”、“ 0X ”、“ 0x ” 为前缀

$AB

字符串定义

字符串必须放在双引号 (“”) 或单引号 (‘ ') 内表示,除非语法上另有所指。

2.2 寻址方式

  在计算机中,寻找操作数的方法定义为指令的寻址方式,根据这个原则, SPMC65 系列单片机的寻址方式分为以下 11 种:

•  立即数寻址( Immediate addressing mode )

•  绝对寻址( Absolute addressing mode )

•  零页寻址( Zero page addressing mode )

•  绝对变址寻址( Absolute indexed addressing mode )

•  零页变址寻址( Zero page indexed addressing mode )

•  间接跳转寻址( Indirect addressing mode )

•  变址间接寻址( Indexed indirect addressing mode )

•  间接变址寻址( Indirect indexed addressing mode )

•  隐含寻址( Implied addressing mode )

•  累加器寻址( Accumulator addressing mode )

•  相对寻址( Relative addressing mode )

•  立即数寻址 ( Immediate Addressing Mode )

  这种寻址方式的操作数以 8 位立即数的形式出现,紧跟在操作码的后面,立即数用“ # ”号表示,以区别直接地址。

汇编格式: 操作码 #dd

例如: AND #$08

  立即数寻址过程如 图 2.1 所示,首先 xx 是由累加器 A 提供,这是由 AND 的操作数决定的, 08 就是立即数,然后进行逻辑与操作。由于 AND 操作数是两个字节,所以执行该操作数后 PC 向后移动两个字。就是 mmmm+2 ,以下同。

图 2. 1 立即数寻址方式示意图

•  绝对寻址( Absolute addressing mode )

绝对寻址用两个字节 (Adr 16) 来 指明目标操作数地址。

汇编格式: 操作码 Adr16

例子: AND $E030

绝对寻址过程如 图 2.2 所示,操作码(指令) AND 的一个操作数从 $E030 指向的存储单元里直接取得。

图 2. 2 绝对寻址过程示意图

•  零页寻址( Zero page addressing mode )

零页寻址采用零页(地址范围 $0000~$00FF )的低字节来指明操作数地址(高位地址默认为零)。

汇编格式: 操作码 Adr08

例子: AND $30

零页寻址过程如 图 2.3 所示,操作码(指令) AND 的一个操作数从零页存储单元 $30 中直接取得。

图 2. 3 零页寻址过程示意图

•  绝对变址寻址( Absolute indexed addressing mode )

绝对变址寻址使用一个双字节基址和一个变址指针(寄存器 X 或 Y ) 来 指明目标操作数地址。

汇编格式: 操作码 Adr 16, X

或: 操作码 Adr 16, Y

例子: LDA $E500,X

绝对变址寻址的过程如 图 2.4 所示,由绝对地址(基址)与变址指针( X 寄存器)的地址相加得到的地址里的数作为操作数。

图 2. 4 绝对变址寻址过程示意图

•  零页变址寻址( Zero page indexed addressing mode )

零页变址寻址使用零页地址作为基址,基址与变址指针(寄存器 X 或 Y )相加 来 指明目标操作数地址。

汇编格式: 操作码 Adr 08, X

或: 操作码 Adr 08, Y

例子: LDA $75,X

零页变址寻址的过程如 图 2.5 所示,由零页地址(基址)与变址指针( X 寄存器)的地址相加得到的地址里的数作为操作数。

图 2. 5 零页变址寻址过程示意图

•  间接跳转寻址( Indirect addressing mode )

间接跳转寻址只能用于 JMP 指令中,它用 16 位地址作为程序跳转地址的指针。

汇编格式: 操作码 ( Adr 16)

例子: JMP ( $F600)

如下图所示,间接跳转寻址将 $F600 与 $F601 两单元中的数据组成的 16 位地址 $ppqq 作为 PC 的跳转地址( $pp 、 $qq 为存储器中的数值)。

图 2. 6 间接跳转寻址过程示意图

•  变址间接寻址( Indexed indirect addressing mode )

变址间接寻址采用“ 8 位地址+变址指针 X ”来 指明目标操作数地址。 在此寻址模式下,只能采用 X 寄存器作为变址指针。

汇编格式: 操作码 (Adr 08, X)

例子: AND ($30, X)

变址间接寻址如 图 2.7 所示,首先 $30 与变址指针( X 寄存器)中的值 $63 相加,其结果 $93 作为目标操作数地址的指针,它所指向的数值 $qq 与下一个存储单元中的数值 $pp 共同组成了 16 位的目标操作数地址。地址 $ppqq 指向的内容 $yy 就是目标操作数。

图 2. 7 变址间接寻址过程示意图

•  间接变址寻址( Indirect indexed addressing mode )

间接变址寻址采用“ 8 位地址”来 指明目标操作数的基址,再加上 变址指针( Y 寄存器)作为目标操作数的地址 。 在此寻址模式下,只能采用 Y 寄存器作为变址指针。

汇编格式: 操作码 (aa), Y

例子: LDA ($20), Y

间接变址寻址如 图 2.8 所示,由地址 $20 里的数据 $32 作为基址地址的低 8 位,下一个地址 $21 里的数据 $01 作为基址地址的高 8 位,二者组成的 16 位基址 $0132 加上指针( Y 寄存器)中的数据 $15 形成目标操作数地址 $0147 ,目标操作数地址里的数据作为操作数。

图 2.8 间接变址寻址

•  隐含寻址( Implied addressing mode )

隐含寻址是特殊指令对应的特殊寻址方式,它的源操作数和目标操作数的地址都由指令默认。

汇编格式: 操作码

例子: TAX ; 将累加器内容传送到 X 寄存器中

CLC ; 清除进位标志

•  累加器寻址( Accumulator addressing mode )

累加器寻址采用累加器 A 作为指令操作数。

汇编格式: 操作码 A

例子: ROL A ; 累加器 A 内容循环左移

ASL A ; 累加器 A 的内容算数左移

•  相对寻址( Relative addressing mode )

相对寻址使用一个 8 位地址来定义目标操作数的 地址,该寻址方式仅用在条件跳转指令中,最大的向前跨度为 127 字节,最大向后跨度为 128 字节。

汇编格式: 操作码 Adr 08

例子: BCC M127

BCC P128

相对地址寻址过程如 图 2.9 所示,其中 M127 与 P128 都是标号

图 2. 9 相对寻址过程示意图

2.3  指令系统

• 指令概述

  指令依照其用途可分为六类:数据传送类指令、算术运算类指令、逻辑运算类指令、位操作指令、转移类指令、控制类指令。它们的具体分类及用途详见 表 2.2 。

表 2. 2 SPMC65 指令分类列表

类别

用途

指令

数据传送指令

立即数到寄存器、寄存器到寄存器、寄存器到存储器、存储器到寄存器的数据传送操作

LDA 、 LDX 、 LDY 、 STA 、 STX 、 STY 、 TAX 、 TXA 、 TAY 、 TYA 、 TXS 、 TSX 、 PHA 、 PHP 、 PLA 、 PLP

算术运算指令

加、减、比较运算

ADC 、 SBC 、 DEC 、 DEX 、 DEY 、 INC 、 INX 、 INY 、 CMP 、 CPX 、 CPY

逻辑运算指令

与、或、异或、移位等操作

AND 、 ORA 、 EOR 、 ASL 、 LSR 、 ROL 、 ROR 、 BIT

控制转移指令

条件转移、无条件转移、中断返回、子程序调用等操作

BCS 、 BCC 、 BEQ 、 BNE 、 BMI 、 BPL 、 BVS 、 BVC 、 JMP 、 JSR 、 RTS 、 RTI

位操作指令

位置位、位清除、位取反、位测试等操作

SET 、 CLR 、 INV 、 TST

处理器控制指令

开中断、关中断、清除进位、清除负标志等操作

CLC 、 CLD 、 CLI 、 CLV 、 SEC 、 SED 、 SEI 、 NOP

•  数据传送指令

数据传送是把源操作数传送到目标地址的过程,是计算机最基本、最重要的一种操作。在实际应用中,数据传送指令使用的比例是最高的。

1. LDA

LDA 的作用是将数据送入累加器 A 中,具体格式参见下表:

表 2.3 指令 LDA

汇编指令格式

操作码

字节数

指令周期( Cycles)

LDA #dd

A9H

2

2

LDA aa

A5H

2

3

LDA aa, X

B5H

2

4

LDA aaaa

ADH

3

4

LDA aaaa, X

BDH

3

4 *

LDA aaaa, Y

B9H

3

4 *

LDA (aa, X)

A1H

2

6

LDA (aa), Y

B1H

2

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期,一页的大小为 256 个字节。

该指令对 P 标志位的影响见下表:

表 2 . 4 LDA 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0时置1

[ 例 ] :

LDA #$F0 ; 把立即数 $F0 传送至累加器 A

LDA $F0 ; 把位于 $F0 地址的数据复制到累加器 A

2. LDX

LDX 的作用是将数据送入 X 寄存器中。具体格式参见下表:

表 2 . 5 指令 LDX

汇编指令格式

操作码

字节数

指令周期( Cycles)

LDX #dd

A2H

2

2

LDX aa

A6H

2

3

LDX aa, Y

B6H

2

4

LDX aaaa

AEH

3

4

LDX aaaa, Y

BEH

3

4 *

* 如果数据寻址时超出了一页的范围 , 需要再加一个指令周期

该指令对 P 标志位的影响见下表:

表 2 . 6 LDX 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1
[ 例 ] :

LDX #$F0 ; 把 立即数 $F0 传送至 X 寄存器

LDX $F0 ; 把位于 $F0 地址的数据复制到 X 寄存器

3. LDY

LDY 的作用是将数据送入 Y 寄存器中。具体格式参见下表:

表 2 . 7 指令 LDY

汇编指令格式

操作码

字节数

指令周期( Cycles)

LDY #dd

A0H

2

2

LDY aa

A4H

2

3

LDY aa, X

B4H

2

4

LDY aaaa

ACH

3

4

LDY aaaa, X

BCH

3

4 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期

该指令对 P 标志位的影响见下表:

表 2 . 8 LDY 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDY #$F0 ; 把 立即数 $F0 传送至 Y 寄存器

LDY $F0 ; 把位于 $F0 地址的数据复制到 Y 寄存器

LDX #$12 ; 把立即数 $12 传送至 X 寄存器

LDY $E005,X ; 把地址为 $E017($E005+$12) 的数据复制到 Y 寄存器

4. STA

STA 的作用是将累加器 A 的值送入存储器。具体格式参见下表:

表 2 . 9 指令 STA

汇编指令格式

操作码

字节数

指令周期( Cycles)

STA aa

85H

2

3

STA aa, X

95H

2

4

STA aaaa

8DH

3

4

STA aaaa, X

9DH

3

4

STA aaaa, Y

99H

3

4

STA (aa, X)

81H

2

6

STA (aa), Y

91H

2

6

该指令对标志寄存器 P 不产生影响。

[ 例 ] :

LDA #$12 ; 把立即数 $12 送至累加器

STA $78 ; 把累加器中的数据 $12 复制到地址为 $78 的存储单元

STX

STX 的作用是将寄存器 X 的值送入存储器中。具体格式参见下表:

表 2 . 10 指令 STX

汇编指令格式

操作码

字节数

指令周期( Cycles)

STX aa

86H

2

3

STX aa, Y

96H

2

4

STX aaaa

8EH

3

4

该指令对标志寄存器 P 不产生影响。

[ 例 ] :

LDX #$12 ; 把立即数 12 送至寄存器 X

STX $78 ; 把寄存器 X 中的数据 $12 复制到地址为 $78 的存储单元

6. STY

STY 的作用是将寄存器 Y 的值送入存储器中。具体格式参见下表:

表 2 . 11 指令 STY

汇编指令格式

操作码

字节数

指令周期( Cycles)

STY aa

84H

2

3

STY aa, X

94H

2

4

STY aaaa

8CH

3

4

该指令对标志寄存器 P 不产生影响。

[ 例 ] :

LDY #$12 ; 把立即数 $12 送至寄存器 Y

STY $78 ; 把寄存器 Y 中的数据 $12 复制到地址为 $78 的存储单元

LDX #$10 ; 把立即数 $10 送至寄存器 X

STY $63,X ; 把寄存器 Y 中的数据 $12 复制到地址为 $73 ( $63+$10 )的存储单元

7. TAX

TAX 的作用是将累加器 A 的值送入寄存器 X 中。具体格式参见下表:

表 2 . 12 指令 TAX

汇编指令格式

操作码

字节数

指令周期( Cycles)

TAX

AAH

1

2

该指令对 P 标志位的影响见下表:

表 2 . 13 TAX 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:运算结果为零置1

[ 例 ] :

LDA #$17 ; 把立即数 $17 送至累加器 A

TAX ; 把累加器 A 中的数值 $17 复制到寄存器 X 中

8. TXA

TXA的作用是将 X 寄存器的值送入累加器 A 中 。参见下表:

表 2 . 14 指令 TXA

汇编指令格式

操作码

字节数

指令周期( Cycles)

TXA

8AH

1

2

该指令对 P 标志位的影响见下表:

表 2 . 15 TXA 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:运算结果为零置1

[ 例 ] :

LDX #$17 ; 把立即数 $17 送至寄存器 X

TXA ; 把寄存器 X 中的数值 $17 复制到累加器 A 中

9. TAY

TAY 的作用是将累加器 A 的值送入寄存器 Y 中。具体格式参见下表:

表 2 . 16 指令 TAY

汇编指令格式

操作码

字节数

指令周期( Cycles)

TAY

A8H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 17 TAY 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:运算结果为零置1

[ 例 ] :

LDA #$17 ; 把立即数 $17 送至累加器 A

TAY ; 把累加器 A 中的数值 $17 复制到寄存器 X 中

10. TYA

TYA 的作用是将 Y 寄存器的值传送到累加器 A 中。具体格式参见下表:

表 2 . 18 指令 TYA

汇编指令格式

操作码

字节数

指令周期( Cycles)

TYA

98H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 19 TYA 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:运算结果为零置1

[ 例 ] :

LDY #$17 ; 把立即数 $17 送至寄存器 Y

TYA ; 把寄存器 Y 中的数值 $17 复制到累加器 A 中

11. TXS

TXS的作用是 将 X 寄存器的值传送到堆栈指针 SP 的低 8 位中 。具体格式参见下表:

表 2 . 20 指令 TXS

汇编指令格式

操作码

字节数

指令周期( Cycles)

TXS

9AH

1

2

TXS 指令对标志寄存器 P 不产生影响。

[ 例 ] :

LDX #$FC ; 把立即数 $FC 送至寄存器 X

TXS ; 把 X 中的值赋给堆栈指针 SP 的低 8 位, SP 的值将变为 $FC

12. TSX

TSX 的作用是将堆栈指针 SP 中的值送入 X 寄存器中。具体格式参见下表:

表 2 . 21 TSX 指令对 P 标志位的影响

汇编指令格式

操作码

字节数

指令周期( Cycles)

TSX

BAH

1

2

该指令对 P 标志位的影响见下表:

表 2 . 22 TAY 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:运算结果为零置1

13. PHA

PHA的作用是 将累加器 A 的值压入堆栈 。该指令产生的操作是:首先将累加器 A的值传送到堆栈指针SP所指向的存储单元,然后使SP的值减小1。具体格式参见下表:

表 2 . 23 指令 PHA

汇编指令格式

操作码

字节数

指令周期( Cycles)

PHA

48H

1

3

该指令对标志寄存器 P 不产生影响。

[ 例 ] :

LDX #$FF ; X ? $FF 立即数 0xFF( 即 $FF) 载入寄存器 X

TXS ; SP ? $FF, 立即数 0xFF( 即 $FF) 载入堆栈寄存器 SP, 堆栈指针指向 $01FF

LDA #$35 ; A ? $35

PHA ; [$1FF] ? $35, SP ? SP - 1, 此时 $35 被传送到 $01FF 单元, SP 的值变为 $1FE

14. PHP

PHP的作用是 将状态标志寄存器的值压入堆栈, 主要作用是保护现场。该指令产生的操作是:首先将标志寄存器 P的值传送到堆栈指针SP所指向的存储单元,然后使SP的值减小1。具体格式参见下表:

表 2 . 24 指令 PHP

汇编指令格式

操作码

字节数

指令周期( Cycles)

PHP

08H

1

3

该指令对标志寄存器 P 不产生影响。

15. PLA

PLA的作用是 将堆栈内容弹出给 A 。该指令产生的操作是:首先使堆栈指针 SP 的值增加 1 ,然后把 SP 指向的存储单元中的数值传送给累加器 A 。 具体格式参见下表:

表 2 . 25 指令 PLA

汇编指令格式

操作码

字节数

指令周期( Cycles)

PLA

68H

1

4

该指令对 P 标志位的影响见下表:

表 2 . 26 PLA 指令对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:运算结果为零置1

[ 例 ] :

LDX #$FF ; X ? $FF 立即数 0xFF( 即 $FF) 载入寄存器 X

TXS ; SP ? $FF 立即数 0xFF( 即 $FF) 载入堆栈寄存器 SP, 堆栈指针指向 $01FF

LDA #$35 ; A ? $35

PHA ; [SP] ? $35, SP ? SP – 1, 此时 $35 被传送到 $01FF 单元, SP 的值变为 $FE

LDA #$78 ; A ? $78

PLA ; SP ? SP + 1, A ? [SP], 此时 SP 变为 $FF , $01FF 单元中的数值 $35 被传

; 送至累加器 A

16. PLP

PLP的作用是 将堆栈中的内容弹出给状态标志寄存器 P 。该指令首先将堆栈指针 SP 的值增加 1 ,然后将 SP 指向的存储单元中的数值赋给标志寄存器 P 。 具体格式参见下表:

表 2 . 27 指令 PLP

汇编指令格式

操作码

字节数

指令周期( Cycles)

PLP

28H

1

4

该指令对 P 标志位的影响见下表:

表 2 . 28 PLP 指令对 P 标志位的影响

N

V

D

I

Z

C

!

!

!

!

!

!

PLP指令影响所有P标志。

•  算术运算指令

SPMC65 的算术运算指令主要包括带进位加法、减法,寄存器或存储器的自增、自减,以及比较运算指令。

1. ADC

ADC 是累加器 A 的带进位加法指令,该指令将累加器 A 中的数值与指定的操作数相加,再加上进位标志 C 的值,得到的结果赋给累加器 A 。具体格式参见下表:

表 2 . 29 指令 ADC

汇编指令格式

操作码

字节数

指令周期(Cycles)

ADC #dd

69H

2

2

ADC aa

65H

2

3

ADC aa, X

75H

2

4

ADC aaaa

6DH

3

4

ADC aaaa, X

7DH

3

4*

ADC aaaa, Y

79H

3

4*

ADC (aa, X)

61H

2

6

ADC (aa), Y

71H

2

6*

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期

该指令对 P 标志位的影响见下表:

表 2 . 30 指令 ADC 对 P 标志位的影响

N

V

D

I

Z

C

!

!

*

-

!

!

N:将bit7的值赋给N

V:仅用于有符号数算术运算,两个同符号数相加或异符号数相减,结果大于127或小于-128时,该位置1

Z:结果为0时置1

C:最高位产生进位时,该位被置1

D:*为1表示当前处于十进制模式

[ 例 1] :

LDA #$45 ; A ? $45

CLC ; 将进位标志 C 设置为 0 ,参见 2.3.7 节关于 CLC 指令的介绍

ADC #$32 ; A ? A + $32 + C ,此时累加器 A 的值变为 $77 , N=0, V=0, Z=0, C=0

[ 例 2] :

LDA #$25 ; A ? $25

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

ADC #$18 ; A ? A + $18 + C ,此时累加器 A 的值变为 $3E , N=0, V=0, Z=0, 由于未发

; 生进位, C 的值变为 0

[ 例 3] :

LDA #$45 ; A ? $45

STA $68 ; [$68] ? A ,该句将 $45 传送到地址为 $68 的存储单元

CLC ; 将进位标志 C 设置为 0 ,参见 2.3.7 节关于 CLC 指令的介绍

LDA #$72 ; A ? $72

ADC $68 ; A ? A + [$68] + C ,即 ($72 + $45 + 0) ,此时累加器 A 的值变为 $B7 , Z=0 ,

; C=0 ,由于相当于两个同符号数相加,结果大于 127 ,所以 V=1 ,由于结果

; 最高位为 1 ,所以 N=1

[ 例 4] :

SED ; 将十进制模式标志 D 设置为 1 ,切换到十进制运算模式,参见 2.3.7 节关于 SED

; 指令的介绍

LDA #$25 ; A ? $25

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

ADC #$18 ; 十进制运算, A ? (BCD)A + (BCD)$18 + C ,累加器 A 的值变为 $44 , C=0,

; N=0, Z=0, V=0 ,可与例 2 对比

2. SBC

SBC 是累加器 A 的带借位减法指令,该指令将累加器 A 中的数值与指定的操作数相减,若进位标志 C 的值是 0 则再减去 1 ,得到的结果赋给累加器 A ,即 A ? A - data - 。具体格式参见下表:

表 2 . 31 指令 SBC

汇编指令格式

操作码

字节数

指令周期( Cycles)

SBC #dd

E9H

2

2

SBC aa

E5H

2

3

SBC aa, X

F5H

2

4

SBC aaaa

EDH

3

4

SBC aaaa, X

FDH

3

4 *

SBC aaaa, Y

F9H

3

4 *

SBC (aa, X)

E1H

2

6

SBC (aa), Y

F1H

2

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期

该指令对 P 标志位的影响见下表:

表 2 . 32 指令 SBC 对 P 标志位的影响

N

V

D

I

Z

C

!

!

*

-

!

!

N:将bit7的值赋给N

V:仅用于有符号数算术运算,两个同符号数相加或异符号数相减,结果大于127或小于-128时,该位置1

Z:结果为0时置1

C: 计算过程未发生借位则C置1,发生借位则C置0

D: *为1表示当前处于十进制模式

[ 例 1] :

LDA #$45 ; A ? $45

CLC ; 将进位标志 C 设置为 0 ,参见 2.3.7 节关于 CLC 指令的介绍

SBC #$32 ; A ? A - $32 - ,此时累加器 A 的值变为 $12 , N=0, V=0, Z=0, 由于未发生

; 借位,所以 C=1

[ 例 2] :

LDA #$39 ; A ? $39

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

SBC #$47 ; A ? A - $47 - ,此时累加器 A 的值变为 $F2 (借位,相当于 $139-$47 ), V=0,

; Z=0, 由于发生了借位,所以 C=0 ,由于结果的 bit7 为 1 ,所以 N=1

[ 例 3] :

LDA #$62 ; A ? $62

STA $68 ; [$68] ? A ,该句将 $62 传送到地址为 $68 的存储单元

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

LDA #$94 ; A ? $94

SBC $68 ; A ? A – [$68] - ,即 ($94 - $62 - 0) ,此时累加器 A 的值变为 $32 , Z=0, N=0,

; 由于未发生借位,所以 C=1 ,由于相当于两个不同符号数相减( $94 作为有符

; 号数使用时相当于 -108 10 , $68 相当于 98 10 )结果小于 -128 10 ,所以 V=1

[ 例 4] :

SED ; 将十进制模式标志 D 设置为 1 ,切换到十进制运算模式,参见 2.3.7 节关于 SED

; 指令的介绍

LDA #$39 ; A ? $39

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

SBC #$47 ; 十进制运算, A ? (BCD)A - (BCD)$47 - ,累加器 A 的值变为 $92 (发生

; 借位,相当于 139 – 47 - 0 ), C=0, Z=0, V=0, N=1 (结果的最高位为 1 ),可

; 以与例 2 对比。

3. DEC

DEC 的作用是对存储器中的数值进行减 1操作 , 具体格式参见下表:

表 2 . 33 指令 DEC

汇编指令格式

操作码

字节数

指令周期( Cycles)

DEC aa

C6H

2

5

DEC aa, X

D6H

2

6

DEC aaaa

CEH

3

6

DEC aaaa, X

DEH

3

6

该指令对 P 标志位的影响见下表:

表 2 . 34 指令 DEC 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-


N:将bit7的值赋给N

Z:结果为0置1

[ 例 1] :

LDA #$45 ; A ? $45

STA $68 ; [$68] ? A ,此时 $68 存储单元内的数值是 $45

DEC $68 ; [$68] ? [$68] – 1 ,此时 $68 存储单元内的数值变为 $44

[ 例 2] :

SED ; 将十进制模式标志 D 设置为 1 ,切换到十进制运算模式,参见 2.3.7 节关于 SED

; 指令的介绍

LDA #$30 ; A ? $30

LDX #$12 ; X ? $12

STA $63,X ; [$63 + X] ? A ,此时 $30 被传送到 $75 存储单元中

DEC $63,X ; [$63 + X] ? [$63 + X] – 1 ,此时 $75 存储单元内的数值变为 $2F ,证明十进

; 制运算模式对 DEC 指令无效。

4. DEX

DEX 的作用是使 X 寄存器内的数值减 1 , 具体格式参见下表:

表 2 . 35 指令 DEX

汇编指令格式

操作码

字节数

指令周期( Cycles)

DEX

CAH

1

2

该指令对 P 标志位的影响见下表:

表 2 . 36 指令 DEX 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDX #$36 ; X ? $36

DEX ; X ? X – 1 ,此时寄存器 X 内的数值变为 $35

5. DEY

DEY 的作用是使 Y 寄存器内的数值减 1 , 具体格式参见下表:

表 2 . 37 指令 DEY

汇编指令格式

操作码

字节数

指令周期( Cycles)

DEY

88H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 38 指令 DEY 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDY #$00 ; Y ? $00 , N=0, Z=1

DEY ; Y ? Y – 1 ,此时寄存器 Y 内的数值变为 $FF , N=1,Z=0

6. INC

INC 的作用是对存储器中的数值进行加 1操作 , 具体格式参见下表:

表 2 . 39 指令 INC

汇编指令格式

操作码

字节数

指令周期( Cycles)

INC aa

E6H

2

5

INC aa, X

F6H

2

6

INC aaaa

EEH

3

6

INC aaaa, X

FEH

3

6

该指令对 P 标志位的影响见下表:

表 2 . 40 指令 INC 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 1] :

LDA #$45 ; A ? $45

STA $68 ; [$68] ? A ,此时 $68 存储单元内的数值是 $45

INC $68 ; [$68] ? [$68] + 1 ,此时 $68 存储单元内的数值变为 $46

[ 例 2] :

LDA #$FF ; A ? $FF

LDX #$12 ; X ? $12

STA $63,X ; [$63 + X] ? A ,此时 $FF 被传送到 $75 存储单元中

INC $63,X ; [$63 + X] ? [$63 + X] + 1 ,此时 $75 存储单元内的数值变为 $00

7. INX

INX 的作用是使 X 寄存器内的数值加 1 ,具体格式参见下表:

表 2 . 41 指令 INX

汇编指令格式

操作码

字节数

指令周期( Cycles)

INX

E8H

1

2



该指令对 P 标志位的影响见下表:

表 2 . 42 指令 INX 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDX #$36 ; X ? $36

INX ; X ? X + 1 ,此时寄存器 X 内的数值变为 $37

8. INY

INY 的作用是使 Y 寄存器内的数值加 1 ,具体格式参见下表:

表 2 . 43 指令 INY

汇编指令格式

操作码

字节数

指令周期( Cycles)

INY

C8H

1

2

该指令对 P 标志位的影响见下表:

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDY #$36 ; Y ? $36

INY ; Y ? Y + 1 ,此时寄存器 Y 内的数值变为 $37

9. CMP

CMP 的作用是 将累加器 A 的值与指定操作数进行比较,方法是:累加器 A 的值与指定的操作数相减,若结果为负,则 C 标志置 0 ,否则 C 置 1 ;若结果为 0 ,则 Z 标志置 1 。 CMP 指令只改变标志寄存器,而不会改变累加器 A 的值。该指令通常与条件转移类指令配合使用(参见 2.3.5 节)。 具体格式如下:

表 2 . 44 指令 CMP

汇编指令格式

操作码

字节数

指令周期( Cycles)

CMP #dd

C9H

2

2

CMP aa

C5H

2

3

CMP aa, X

D5H

2

4

CMP aaaa

CDH

3

4

CMP aaaa, X

DDH

3

4 *

CMP aaaa, Y

D9H

3

4 *

CMP (aa, X)

C1H

2

6

CMP (aa), Y

D1H

2

6 *

 

该指令对 P 标志位的影响见下表:

表 2 . 42 指令 INX 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDX #$36 ; X ? $36

INX ; X ? X + 1 ,此时寄存器 X 内的数值变为 $37

8. INY

INY 的作用是使 Y 寄存器内的数值加 1 ,具体格式参见下表:

表 2 . 43 指令 INY

汇编指令格式

操作码

字节数

指令周期( Cycles)

INY

C8H

1

2

该指令对 P 标志位的影响见下表:

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDY #$36 ; Y ? $36

INY ; Y ? Y + 1 ,此时寄存器 Y 内的数值变为 $37

9. CMP

CMP 的作用是 将累加器 A 的值与指定操作数进行比较,方法是:累加器 A 的值与指定的操作数相减,若结果为负,则 C 标志置 0 ,否则 C 置 1 ;若结果为 0 ,则 Z 标志置 1 。 CMP 指令只改变标志寄存器,而不会改变累加器 A 的值。该指令通常与条件转移类指令配合使用(参见 2.3.5 节)。 具体格式如下:

表 2 . 44 指令 CMP

汇编指令格式

操作码

字节数

指令周期( Cycles)

CMP #dd

C9H

2

2

CMP aa

C5H

2

3

CMP aa, X

D5H

2

4

CMP aaaa

CDH

3

4

CMP aaaa, X

DDH

3

4 *

CMP aaaa, Y

D9H

3

4 *

CMP (aa, X)

C1H

2

6

CMP (aa), Y

D1H

2

6 *

表 2 . 51 指令 AND 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0时置1

[ 例 ] :

LDA #%10110100 ; A ? %10110100

AND #%11010010 ; A ? A and %11010010 ,执行此句后累加器的值为 10010000

2. ORA

对累加器 A 中的操作数与指定的操作数进行逻辑“或”运算。即两操作数对应位中的任一个为 1 (或两个都为 1 ),则该位的结果为 1 ,否则为 0 。“或”运算的结果送回累加器 A 。 具体格式参见下表:

表 2 . 52 指令 ORA

汇编指令格式

操作码

字节数

指令周期( Cycles)

ORA #dd

09H

2

2

ORA aa

05H

2

3

ORA aa, X

15H

2

4

ORA aaaa

0DH

3

4

ORA aaaa, X

1DH

3

4 *

ORA aaaa, Y

19H

3

4 *

ORA (aa, X)

01H

2

6

ORA (aa), Y

11H

2

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期

该指令对 P 标志位的影响见下表:

表 2 . 53 指令 ORA 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDA #%10110100 ; A ? %10110100

STA $68 ; [$68] ? A ,此句将 %10110100 存入地址为 $68 的单元

LDA #%11010010 ; A ? %11010010

ORA $68 ; A ? A or [$68] ,即 %11010010 和 %10110100 进行“或”运算,执行此句后

;A 的值将变为 %11110110

3. EOR

这条指令对累加器 A 的值与指定的操作数进行“异或”运算。当两数的对应位不相同时(即一个为 1 ,

另一个为 0 ),该位的异或结果为 1 ;否则该位的异或结果为 0 。异或运算的结果送回到累加器 A 中。 具体格式参见下表:

表 2 . 54 指令 EOR

汇编指令格式

操作码

字节数

指令周期( Cycles)

EOR #dd

49H

2

2

EOR aa

45H

2

3

EOR aa, X

55H

2

4

EOR aaaa

4DH

3

4

EOR aaaa, X

5DH

3

4 *

EOR aaaa, Y

59H

3

4 *

EOR (aa, X)

41H

2

6

EOR (aa), Y

51H

2

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期。

该指令对 P 标志位的影响见下表:

表 2 . 55 指令 EOR 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDA #%10110100 ; A ? %10110100

EOR #%11010010 ; A ? A ^ %11010010 ,执行此句后 A 的值将变为 %01100110

4. ASL

ASL 指令的作用是对累加器或存储器中的数据进行左移,每执行一次 ASL ,数据向左移动一位,低位补零,高位移进标志位 C 中。如 图 2.10 所示:

图 2 . 10 ASL 移位示意图

指令的具体格式参见下表:

表 2 . 56 指令 ASL

汇编指令格式

操作码

字节数

指令周期( Cycles)

ASL A

0AH

1

2

ASL aa

06H

2

5

ASL aa, X

16H

2

6

ASL aaaa

0EH

3

6

ASL aaaa, X

1EH

3

6 *

 

另一个为 0 ),该位的异或结果为 1 ;否则该位的异或结果为 0 。异或运算的结果送回到累加器 A 中。 具体格式参见下表:

表 2 . 54 指令 EOR

汇编指令格式

操作码

字节数

指令周期( Cycles)

EOR #dd

49H

2

2

EOR aa

45H

2

3

EOR aa, X

55H

2

4

EOR aaaa

4DH

3

4

EOR aaaa, X

5DH

3

4 *

EOR aaaa, Y

59H

3

4 *

EOR (aa, X)

41H

2

6

EOR (aa), Y

51H

2

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期。

该指令对 P 标志位的影响见下表:

表 2 . 55 指令 EOR 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

-

N:将bit7的值赋给N

Z:结果为0置1

[ 例 ] :

LDA #%10110100 ; A ? %10110100

EOR #%11010010 ; A ? A ^ %11010010 ,执行此句后 A 的值将变为 %01100110

4. ASL

ASL 指令的作用是对累加器或存储器中的数据进行左移,每执行一次 ASL ,数据向左移动一位,低位补零,高位移进标志位 C 中。如 图 2.10 所示:

图 2 . 10 ASL 移位示意图

指令的具体格式参见下表:

表 2 . 56 指令 ASL

汇编指令格式

操作码

字节数

指令周期( Cycles)

ASL A

0AH

1

2

ASL aa

06H

2

5

ASL aa, X

16H

2

6

ASL aaaa

0EH

3

6

ASL aaaa, X

1EH

3

6 *

STA $68 ; [$68] ? A ,此句将 %10110100 存入地址为 $68 的单元

LSR $68 ; C ? [$68].bit0, [$68] ? [$68] >> 1, 执行此句后,地址为 $68 的存储单元的

; 值将变为 %01011010 , C=0 ( %10110100 的末位移至 C )

6. ROL

这条指令的作用是对指定的操作数进行循环向左移动,标志位 C 的值移至操作数地最低位,而最高位的值移入标志位 C 。每执行一次 ROL ,操作数将循环左移一位。移动过程参考 图 2.12 。

图 2 . 12 ROL 移位过程示意图

指令的具体格式参见下表:

汇编指令格式

操作码

字节数

指令周期( Cycles)

ROL A

2AH

1

2

ROL aa

26H

2

5

ROL aa, X

36H

2

6

ROL aaaa

2EH

3

6

ROL aaaa, X

3EH

3

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期。

该指令对 P 标志位的影响见下表:

N

V

D

I

Z

C

!

-

-

-

!

!

N:将bit7的值赋给N

Z:结果为0时置1

C:将最高位移出的数据bit7赋给C

[ 例 1] :

CLC ; 将进位标志 C 设置为 0 ,参见 2.3.7 节关于 CLC 指令的介绍

LDA #%10110100 ; A ? %10110100

ROL A ; A ? (A << 1) + C ,执行此句后 A 的值将变为 %01101000 , C=1

; ( %10110100 的最高位移至 C )

[ 例 2] :

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

LDA #%10110100 ; A ? %10110100

STA $68 ; [$68] ? A ,此句将 %10110100 存入地址为 $68 的单元

ROL $68 ; [$68] ? ([$68] << 1) + C ,执行此句后 $68 存储单元的值将变为 %01101001 ,

; C=1 ( %10110100 的最高位移至 C )

7. ROR

这条指令的作用是对指定的操作数进行循环向右移动,标志位 C 的值移入操作数的最高位,而操作数最低位的值移入标志位 C 。每执行一次 ROR ,使操作数循环右移一位。移动过程参考 图 2.13 。


图 2 . 13 ROR 移位过程示意图

指令的具体格式参见下表:

汇编指令格式

操作码

字节数

指令周期( Cycles)

ROR A

6AH

1

2

ROR aa

66H

2

5

ROR aa, X

76H

2

6

ROR aaaa

6EH

3

6

ROR aaaa, X

7EH

3

6 *

* 如果数据寻址时超出了一页的范围,需要再加一个指令周期。

该指令对 P 标志位的影响见下表:

表 2 . 60 指令 ROR 对 P 标志位的影响

N

V

D

I

Z

C

!

-

-

-

!

!

N:将bit7的值赋给N

Z:结果为0置1

C:将最低位移出的数据赋给C

[ 例 1] :

CLC ; 将进位标志 C 设置为 0 ,参见 2.3.7 节关于 CLC 指令的介绍

LDA #%00101101 ; A ? %00101101

ROR A ; A ? (A >> 1) + (C << 7) ,执行此句后 A 的值将变为 %00010110 , C=1

; ( %00101101 的最低位移至 C )

[ 例 2] :

SEC ; 将进位标志 C 设置为 1 ,参见 2.3.7 节关于 SEC 指令的介绍

LDA #%00111010 ; A ? %00111010

STA $68 ; [$68] ? A ,此句将 %00111010 存入地址为 $68 的单元

ROR $68 ; [$68] ? ([$68] >> 1) + (C << 7) ,执行此句后 $68 存储单元的值将变

; 为 %10011101 , C=0 ( %00111010 的最低位移至 C )

8. BIT

本指令的作用是对累加器 A 进行测试,方法是将累加器 A 和指定的操作数相“与”,从而改变标志寄存器 P 的值,而不会改变累加器 A 的值。 BIT 指令通常用来判断累加器或变量的某一位(或某几位)是否为 0 ,一般与条件转移类指令(参见 2.3.5 节)配合使用。

指令的具体格式参见下表:

表 2 . 61 指令 BIT

汇编指令格式

操作码

字节数

指令周期( Cycles)

BIT aa

24H

2

3

BIT aaaa

2CH

3

4

该指令对 P 标志位的影响见下表:

表 2 . 62 指令 BIT 对 P 标志位的影响

N

V

D

I

Z

C

!

!

-

-

!

-

N: 将bit7的值赋给“N”

V: 将bit6的值赋给“V”

Z:将存储器aa/aaaa的数值和累加器A相“与”,若结果为0,则置标志“Z”为1,否则置0。

[ 例 1] :

LDA #%00101101 ; A ? %00101101

STA $68 ; [$68] ? A ,此句将 %00101101 存入地址为 $68 的单元

LDA #%00000001 ; A ? %00000001

BIT $68 ; A and [$68] ,此句用来判断 $68 存储单元的最低位是否为 0 ,由于

; %00101101 and %00000001 = %00000001 ,所以 Z=0, N=0, V=0

[ 例 2] :

LDA #%00000011 ; A ? %00000011

STA $68 ; [$68] ? A ,此句将 %00000011 存入地址为 $68 的单元

LDA #%10110100 ; A ? %10110100

BIT $68 ; A and [$68] ,此句用来判断累加器 A 的最低两位是否为 0 ,由于

; %10110100 and %00000011 = %00000000 ,所以 Z=1, N=0, V=0

•  控制转移指令

1. 条件转移指令 BCS 、 BCC 、 BEQ 、 BNE 、 BMI 、 BPL 、 BVS 、 BVC

SPMC65 指令系统中有一系列条件转移指令,它以某些标志位作为依据,若满足指令所规定的条件,则程序转移至指定目标;若不满足条件,则程序顺序执行条件转移指令的下一条指令。这类条件指令转移的目标地址是采用相对寻址方式,即以转移指令为基准的 +127 或 -128 字节的范围之内。指令的具体格式参见下表:

表 2 . 63 条件转移指令

汇编指令格式

转移条件

操作码

字节数

指令周期( Cycles)

BCS aa

C=1

B0H

2

2*

BCC aa

C=0

90H

2

2*

BEQ aa

Z=1

F0H

2

2*

BNE aa

Z=0

D0H

2

2*

BMI aa

N=1

30H

2

2*

BPL aa

N=0

10H

2

2*

BVS aa

V=1

70H

2

2*

BVC aa

V=0

50H

2

2*

注 *. 发生跳转时,需要再加一个指令周期

该指令对标志寄存器 P 不产生影响 。

[ 例 ] :

LDX #$A6 ; X ? $A6

CPX #$17 ; A - $17 ,比较后 C=1 , Z=0 , N=1

BCS Lb1 ; 若 C=1 则跳转到标号 Lb1 处

…… …… ; 此处的指令被跳过

Lb1: BNE Lb2 ; 若 Z=1 则跳转到标号 Lb2 处,但由于 Z=0 ,所以未发生跳转

BMI Lb3 ; 若 N=1 则跳转到标号 Lb3 处

Lb2: …… …… ; 此处的指令被跳过

Lb3: LDA #%00111000 ; A ? %00111000

LSR A ; C ? A.bit0, A ? (A >> 1) ,此时 C=0

BCC Lb1 ; 若 C=0 则跳转到标号 Lb1

2. JMP

JMP 是无条件转移指令,它使程序无条件地跳转到目标地址。指令的具体格式参见下表:

表 2 . 64 指令 JMP

汇编指令格式

操作码

字节数

指令周期( Cycles)

JMP aaaa

4CH

3

3

JMP (aaaa)

6CH

3

5

其中“ JMP (aaaa) ”采用间接跳转方式,由地址 aaaa 与地址 aaaa+1 中的数据组成 16 位跳转地址,如下图所示,执行“ JMP ($F600) ”后,程序将跳转到 $F824 地址处。

图 2 . 14 间接跳转示意图

JMP 指令不影响标志寄存器 P 。

[ 例 1] :

JMP Lb1 ; 无条件跳转到指定的标号处

…… …… ; 此处的指令被跳过

Lb1:

…… …… ; 跳转到此处

[ 例 2] :

JMP (Lb1) ; 间接跳转,跳转到 Lb1 中的数据所指向的地址
…… …… ; 此处的指令被跳过

Lb2: ; 跳转到此处

…… ……

Lb1:

.DW Lb2 ; Lb1 处定义了一个 16 位常量,其内容是 Lb2 的地址。参见 .DW 伪指令

3. JSR

本指令是子程序调用指令,执行该指令将使程序跳转到指定的子程序所在的地址。与 JMP 指令不同的是,在跳转之前,硬件自动将子程序的返回地址(下一条指令的地址)压入堆栈,然后跳转到子程序处执行。一般地,子程序执行完毕,应执行 RTS 指令从子程序返回。 JSR 指令的具体格式参见下表:

表 2 . 65 指令 JSR

汇编指令格式

操作码

字节数

指令周期( Cycles)

JSR aaaa

20H

3

6

该指令不影响 P 寄存器标志位。

[ 例 ] :略,参见 RTS 指令。

4. RTS

子程序返回指令 RTS 通常作为一个子程序的最后一条指令,它用以返回到调用这个子程序的断点处。 RTS 首先从堆栈中弹出即将返回到的程序地址,然后跳转到该地址,实现子程序返回。指令的具体格式参见下表:

表 2 . 66 指令 RTS

汇编指令格式

操作码

字节数

指令周期( Cycles)

RTS

60H

1

6

该指令不影响标志寄存器 P 的标志位。

[ 例 ] :

…… ……

JSR Sub1 ; 子程序调用,程序将跳转到 Sub1 标号处

LDA #$35 ; 子程序调用之后,由子程序中的 RTS 指令返回,执行本条语句

Sub1: ; 定义一个名为 Sub1 的子程序

…… …… ; 该子程序的程序体

RTS ; 子程序返回指令,返回到子程序调用语句的下一条语句

5. RTI

当程序运行在中断过程(中断服务程序)时,遇到 RTI 指令,将从中断过程退出,返回到断点处。指令的具体格式参见下表:

表 2 . 67 指令 RTI

汇编指令格式

操作码

字节数

指令周期( Cycles)

RTI

40H

1

6

执行指令 RTI 后,标志寄存器 P 的值从堆栈中弹出,将当前 的 P 值覆盖,所以 N 、 V 、 D 、 Z 、 C 标志位 均恢复 到 中断前的状态。

[ 例 ] :

V_IRQ: ; 此处定义 IRQ 中断服务程序

…… …… ; 中断服务程序内容

RTI ; 中断服务返回指令,执行该指令后将返回到断点处


•  位操作指令

1. SET

本指令的作用是将指定地址的指定位设置为 1 。它的语法是“ SET aa,b ”,用以 将变量 aa 的 bit b 置 1 。其中 b 的取值范围是 0~7 。 SET 指令只能对地址位于 $00~$FF 的数据进行操作,具体格式参见下表:

表 2 . 68 指令 SET

汇编指令格式

操作码

字节数

指令周期( Cycles)

SET aa, 0

8FH

2

5

SET aa, 1

9FH

2

5

SET aa, 2

AFH

2

5

SET aa, 3

BFH

2

5

SET aa, 4

CFH

2

5

SET aa, 5

DFH

2

5

SET aa, 6

EFH

2

5

SET aa, 7

FFH

2

5

该指令不影响标志寄存器 P 的标志位。

[ 例 ] :

LDA #%00101101 ; A ? %00101101

STA $68 ; [$68] ? A ,把 %00101101 送入地址为 $68 的存储单元

SET $68,1 ; [$68].bit1 ? 1 ,此句将 $68 存储单元的值改变为 %00101111

2. CLR

本指令的作用是将指定存储器地址的指定位设置为 0 。它的语法是“ CLR aa,b ”,用以 将变量 aa 的 bit b 置 0 。其中 b 的取值范围是 0~7 。 CLR 指令只能对地址位于 $00~$FF 的数据进行操作,具体格式参见下表:

表 2 . 69 指令 CLR

汇编指令格式

操作码

字节数

指令周期( Cycles)

CLR aa, 0

0FH

2

5

CLR aa, 1

1FH

2

5

CLR aa, 2

2FH

2

5

CLR aa, 3

3FH

2

5

CLR aa, 4

4FH

2

5

CLR aa, 5

5FH

2

5

CLR aa, 6

6FH

2

5

CLR aa, 7

7FH

2

5

该指令不影响标志寄存器 P 的标志位。

[ 例 ] :

LDA #%00101101 ; A ? %00101101

STA $68 ; [$68] ? A ,把 %00101101 送入地址为 $68 的存储单元

CLR $68,5 ; [$68].bit5 ? 0 ,此句将 $68 存储单元的值改变为 %00001101
3. INV

本指令的作用是将指定地址的指定位取反。它的语法是“ INV aa,b ”,如果变量 aa 的 bit b 为 0 ,则将其翻转为 1 ;反之,如果原来是 1 则翻转成 0 。其中 b 的取值范围是 0~7 。 INV 指令只能对地址位于 $00~$FF 的数据进行操作,具体格式参见下表:

表 2 . 70 指令 INV

汇编指令格式

操作码

字节数

指令周期( Cycles)

INV aa, 0

87H

2

5

INV aa, 1

97H

2

5

INV aa, 2

A7H

2

5

INV aa, 3

B7H

2

5

INV aa, 4

C7H

2

5

INV aa, 5

D7H

2

5

INV aa, 6

E7H

2

5

INV aa, 7

F7H

2

5

该指令不影响标志寄存器 P 的标志位。

[ 例 ] :

LDA #%00101101 ; A ? %00101101

STA $68 ; [$68] ? A ,把 %00101101 送入地址为 $68 的存储单元

INV $68,5 ; [$68].bit5 ? ,此句将 $68 存储单元的值改变为 %00001101

INV $68,7 ; [$68].bit7 ? ,此句将 $68 存储单元的值改变为 %10001101

4. TST

TST 是位测试指令,该指令的作用是判断指定地址的指定位是否为 0 。若该位为 0 则将标志 Z 置为 1 ,否则置 0 。 TST 指令只能对地址位于 $00~$FF 的数据进行位测试,通常与条件转移类指令配合使用。指令的具体格式参见下表:

表 2 . 71 指令 TST

汇编指令格式

操作码

字节数

指令周期( Cycles)

TST aa, 0

07H

2

3

TST aa, 1

17H

2

3

TST aa, 2

27H

2

3

TST aa, 3

37H

2

3

TST aa, 4

47H

2

3

TST aa, 5

57H

2

3

TST aa, 6

67H

2

3

TST aa, 7

77H

2

3

该指令对 P 标志位的影响见下表:

表 2 . 72 指令 TST 对 P 标志位的影响

N

V

D

I

Z

C

-

-

-

-

!

-

Z:若变量aa的bit n为0,则标志位Z置1,否则置0。

[ 例 ] :

LDA #%00101101 ; A ? %00101101

STA $68 ; [$68] ? A ,把 %00101101 送入地址为 $68 的存储单元

TST $68,0 ; 由于 [$68] 的 bit0 位为 1 ,所以 Z=0

•  处理器控制指令

1. CLC

本指令的作用是对进位标志位 C 清零。指令的具体格式参见下表:

表 2 . 73 指令 CLC

汇编指令格式

操作码

字节数

指令周期( Cycles)

CLC

18H

1

2

该指令对 P 标志位的影响见下表:

N

V

D

I

Z

C

-

-

-

-

-

!

C:进位标志位清零

2. CLD

本指令的作用是对十进制模式标志位 D 清零,退出十进制模式。指令的具体格式参见下表:

表 2 . 74 指令 CLD

汇编指令格式

操作码

字节数

指令周期( Cycles)

CLD

D8H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 75 指令 CLD 对 P 标志位的影响

N

V

D

I

Z

C

-

-

!

-

-

-

D:十进制模式标位清零

3. CLI

该条指令的作用是对中断屏蔽标志位 I 清零,即打开总的中断开关。但如果要开启某个具体的中断,还需设置相应的中断控制寄存器(参见中断系统一章的相关内容)。指令的具体格式如下:

表 2 . 76 指令 CLI

汇编指令格式

操作码

字节数

指令周期( Cycles)

CLI

58H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 77 指令 CLI 对 P 标志位的影响

N

V

D

I

Z

C

-

-

-

!

-

-

I:中断屏蔽标志位清零

4. CLV

该指令的作用是对溢出标志位 V 清零。指令的具体格式参见下表:

表 2 . 78 指令 CLV

汇编指令格式

操作码

字节数

指令周期( Cycles)

CLV

B8H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 79 指令 CLV 对 P 标志位的影响

N

V

D

I

Z

C

-

!

-

-

-

-

V: 溢出标志位清零

5. SEC

该指令的作用是将进位标志 C 置 1 。指令的具体格式参见下表:

表 2 . 80 指令 SEC

汇编指令格式

操作码

字节数

指令周期( Cycles)

SEC

38H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 81 指令 SEC 对 P 标志位的影响

N

V

D

I

Z

C

-

-

-

-

-

!

C: 进位标志位置1

6. SED

该指令的作用是将十进制模式标志位 D 置 1 ,使 CPU 切换到十进制模式。在该模式下,进行加法或减法运算时,参与运算的操作数以及运算结果将以 BCD 码的形式存在。使用指令 CLD 可以退出十进制模式。指令的具体格式参见下表:

表 2 . 82 指令 SED

汇编指令格式

操作码

字节数

指令周期( Cycles

SED

F8H

1

2

该指令对 P 标志位的影响见下表:

N

V

D

I

Z

C

-

-

!

-

-

-

D:十进制模式标志位置1

[ 例 ] :
SED ; 切换到十进制运算模式

CLC ; C ? 0

LDA #$37 ; A ? $37

ADC #$45 ; A ? (BCD)A + (BCD)$45 + C ,执行此句后 A=$82

7. SEI

SEI 指令的作用是使中断屏蔽标志位 I 置 1 。执行该指令后,除 NMI 之外的所有中断都将被禁止,它相当于所有可屏蔽中断的总开关。指令的具体格式参见下表:

表 2 . 83 指令 SEI

汇编指令格式

操作码

字节数

指令周期( Cycles)

SEI

78H

1

2

该指令对 P 标志位的影响见下表:

表 2 . 84 指令 SEI 对 P 标志位的影响

N

V

D

I

Z

C

-

-

-

!

-

-

I:无条件置1

8. NOP

该指令是空操作,不影响 P 标志位,不产生返回值,只起到延时作用。指令的具体格式参见下表:

表 2 . 85 指令 NOP

汇编指令格式

操作码

字节数

指令周期( Cycles)

NOP

EAH

1

2

  常用伪指令

SPMC65 汇编伪指令与汇编指令不同,它不会被 CPU 执行,而是在汇编器对程序进行汇编期间实现对汇编器的控制。使用伪指令可以完成分配存储区、定义宏、指导汇编器汇编指定的代码等功能,从而增加程序的可读性和可维护性。

•  SPMC65 伪指令的语法格式及分类

SPMC65 伪指令可以出现在程序文件的任意位置,为了与汇编指令区分,建议伪指令的前面加上一个句点“ . ”,例如“ .CODE ”。

SPMC65 伪指令不必区分字母的大小写,也就是说,在 SPMC65 程序中,“ .code ”、“ .CODE ”、“ .cODe ”是等价的,建议伪指令全部大写。但是利用伪指令定义的标号(包括段名、宏名、变量名、结构名等)则要区分其字母的大小写,例如标号“ Temp ”与标号“ temp ”代表两个不同的标号。

为便于对 SPMC65 伪指令的语法进行描述,我们采用了下列符号约定:

lable —— 标号

count —— 数量

value —— 常量数值

args —— 参数

[ ] —— 可缺省项目。如果出现 [[… …]] 的形式,则表示可缺省项的内容本身就带有方括号。

SPMC65 伪指令依照其用途可分为五类:存储类、定义类、条件类、汇编链接类以及调试类。详见 表 2.86 。表中用斜线“ / ”隔开的伪指令是同义伪指令,它们虽然名字不同,但实现的功能是相同的。例如“ ORG/ORGIN ”表示 ORG 与 ORGIN 两条伪指令可以相互替换。

表 2 . 86 伪指令分类列表

类别

用途

伪指令

存储类

定义变量和常量

DB / BYTE / DEFB / STRING , DW / DEFW / WORD ,
DD / LONG , FLOAT , DOUBLE , DS / DEFS , DUP ,
ASCII , BLKB , BLKL , BLKW

定义类

标号定义

段定义

过程和结构的定义

宏定义

VAR / DEFL / SET 、 EQU / EQUAL

CODE , DATA , PAGE0 , SECTION , ORG / ORGIN

PROC , ENDP , STRUCT , ENDST

MACRO , ENDM / MACEND , MACEXIT

条件类

对汇编指令进行条件汇编

IF / IFN / IFNFALSE / IFNZ / IFTRUE ,
IFE / IFFALSE / IFNTRUE / IFZ , IFDIFF / IFNSAME 、
IFNDIFF / IFSAME , IFDEF , IFNDEF , IFMA , IFNMA ,
IFABS / IFNREL , IFNABS / IFREL , IFSSEQ , IFSSNEQ ,
IFPAGE0 , IFNPAGE0 , IFCLEAR , IFNEXT ,
ELSE , ENDIF / ENDC , EXIT

汇编链接类

指导汇编器和连接器

PUBLIC / GLOBAL / XDEF , EXTERN / EXTERNAL / XREF , INCLUDE , LINKLIST , SYMBOLS , COMMENT , ASK , MESSAGE / MESSG , ABSOLUTE , RELATIVE , END

调试类

与调试相关的伪指令

STABF , STABN , STABS

下面将分类介绍比较常用的伪指令。

•  存储类伪指令

1. DB

[ 功能 ] 以字节型数据的形式来存储常量(定位在 ROM 区,参见 .CODE 伪指令)

[ 同义伪指令 ] DEFB 、 BYTE 、 STRING

[ 格式 ]

[label:] .DB [[count]] [value] [,[count]] [value][,…]

[ 说明 ] 本伪指令把一系列 8 位常量值存入连续的数据单元中。多个 value 值用逗号分隔开,也可在每个 value 值的前面指定一个 [count] 值,表示连续存储 count 个相同的 value 。如果存储的常量中含有 ASCII 字符或字符串,则必须用单引号或双引号将其括起来。

例如:

Lb1: .DB 17h ;1 个字节,值为 17h
.DB 3,[2]'d','ef' ;5 个字节,值为 3,'d','d','e','f'

Lb3: .DB “Isn't” ; 包含单引号的字符串

上述定义将使汇编器分配一段 ROM 空间用来存储这些数值:

初值

17h

3

‘d'

‘d'

‘e'

‘f'

‘I'

‘s'

‘n'

单引号

‘t'

ROM 区存储内容

17h

03h

64h

64h

65h

66h

49h

73h

6Eh

27h

74h

如果利用 .DB 定义变量(在 RAM 区存储,参见 .PAGE0 和 .DATA 伪指令),那么 .DB 后面的初始化值将不起作用,汇编器仅仅在 RAM 中分配相应长度的空间,而不会存入初值,因此不建议使用 .DB 定义变量。

2 . DW

[ 功能 ] 以 16 位整型数据的形式来存储常量(定位在 ROM 区,参见 .CODE 伪指令)

[ 同义伪指令 ] DEFW 、 WORD

[ 格式 ]

[label:] .DW [[count]] [value] [,[count]] [value][,…]

[ 说明 ] 本伪指令将申请若干连续的 16 位整型数据单元来存储双字节型常量序列。其使用方法与 .DB 伪指令相同。

例如:

Lb1: .DW 1074h ;1 个双字节,值为 1074h

.DW 3,[2]'d','ef' ;5 个双字节数据

上述定义将使汇编器分配一段 ROM 空间用来存储这些数值:

初值

1074h

3

‘d'

‘d'

‘e'

‘f'

ROM 区存储内容

74h

10h

03h

00h

64h

00h

64h

00h

65h

00h

66h

00h

如果利用 .DW 定义变量(在 RAM 区存储,参见 .PAGE0 和 .DATA 伪指令),那么 .DW 后面的初始化值将不起作用,汇编器仅仅在 RAM 中分配相应长度的空间,而不会存入初值。不建议用 .DW 定义变量。

3. DD

[ 功能 ] 以 32 位长整型数据的形式来存储常量(定位在 ROM 区,参见 .CODE 伪指令)

[ 同义伪指令 ] LONG

[ 格式 ]

[label:] .DD [[count]] [value][,value][,…]

[ 说明 ] 本伪指令申请若干连续的 32 位长整型数据单元来存储四字节型常量。其使用方法与 .DB 伪指令相同。

例如:

Lb1: .DD 123456h ; 占用 1 个 32 位单元

.DD 3,[2]'d','ef' ; 占用 5 个 32 位单元

上述定义将使汇编器分配一段 ROM 空间用来存储这些数值:

初值

123456h

3

‘d'

ROM 区存储内容

56h

34h

12h

00h

03h

00h

00h

00h

65h

00h

00h

00h

初值

‘d'

‘e'

‘f'

ROM 区存储内容

65h

00h

00h

00h

66h

00h

00h

00h

67h

00h

00h

00h

如果利用 .DD 定义变量(在 RAM 区存储,参见 .PAGE0 和 .DATA 伪指令),那么 .DD 后面的初始化值将不起作用,汇编器仅仅在 RAM 中分配相应长度的空间,而不会存入初值。不建议用 .DD 定义变量。

4. FLOAT

[ 功能 ] 以单精度浮点型实数的形式来存储常量(定位在 ROM 区,参见 .CODE 伪指令)

[ 格式 ]

[label:] .FLOAT [[count]] [value][,value][,…]

[ 说明 ] 本伪指令用来把实数常量转换为 IEEE 格式表示的单精度浮点型数据并存储,每个浮点数将占用 4 字节的存储空间。 .FLOAT 的使用方法与 .DB 伪指令相同。同样地,只用当利用 .FLOAT 定义常量时, value 预置的初值才会起作用,而定义的变量(存储于 RAM 区,参见 .PAGE0 和 .DATA 伪指令)不会被相应的 value 值初始化,仅仅会占用同样大小的 RAM 空间,因此不建议用 .FLOAT 定义变量。

例如:

Lb1: .FLOAT 134.576 ; 将 134.576 转换为 IEEE 格式( 43069375h )

.FLOAT [2]78 ; 将两个 78 转换为 IEEE 格式( 429C0000h )

上述对常量的定义将使汇编器分配一段 ROM 空间用来存储这些数值:

初值

134.576

78

78

ROM 区存储内容

75h

93h

06h

43h

00h

00h

9Ch

42h

00h

00h

9Ch

42h

5. DOUBLE

[ 功能 ] 以双精度浮点型实数的形式来存储常量(定位在 ROM 区,参见 .CODE 伪指令)

[ 格式 ]

[label:] .DOUBLE [[count]] [value][,value][,…]

[ 说明 ] 本伪指令用来把实数常量值转换为 IEEE 格式表示的双精度浮点型数据并存储,每个浮点数将占用 8 字节的存储空间。 .DOUBLE 的使用方法与 .DB 伪指令相同。同样地,只用当利用 .DOUBLE 定义常量时, value 预置的初值才会起作用,而定义的变量(在 RAM 区存储,参见 .PAGE0 和 .DATA 伪指令)仅会占用同样大小的 RAM 空间,不会被相应的 value 值初始化。不建议用 .DOUBLE 定义变量。

例如:

Lb1: .DOUBLE 134.576 ; 转换为 IEEE ( 4060D26E978D4FDFh )

若上述定义处于常量区,那么将占用一段 ROM 空间用来存储这些数值:

初值

DFh

4Fh

8Dh

97h

6Eh

D2h

60h

40h

ROM 区存储内容

134.576

6. DUP

[ 功能 ] 与 .DB 、 .DW 、 .DD 、 .FLOAT 、 .DOUBLE 等常量存储类伪指令配合使用,用来存放若干个具有相同数值的常量。

[ 格式 ]

[label:] .DB count .DUP (value)

[label:] .DW count .DUP (value)

[label:] .DD count .DUP (value)

[label:] .FLOAT count .DUP (value)

[label:] .DOUBLE count .DUP (value)

[ 说明 ] 本伪指令将分配 count 个值为 value 的数据单元,如果 value 值为问号( ? ),则 value 值被初始化为 0 。

例如:

Lb1: .DW 11 DUP (20h) // 定义 11 个字的连续空间,并存入 20h

DUP 伪指令可以灵活地出现在存储类伪指令中,例如:

Lb1: .DB 5, [2]1, 3 .DUP (?), 2 .DUP (7,8), 2 .DUP (‘ab')

上述常量定义将在 ROM 中分配空间来存储下列数值:

初值

5

[2]1

3 .DUP (?)

2 .DUP (7,8)

2 .DUP (‘ab')

ROM 区存储内容

05h

01h

01h

00h

00h

00h

07h

08h

07h

08h

61h

62h

61h

62h

7. DS

[ 功能 ] 定义指定长度的变量(定位在 RAM 区,参见 .PAGE0 和 .DATA 伪指令)。

[ 同义伪指令 ] DEFS

[ 格式 ]

[label:] .DS count

[ 说明 ] 本伪指令用来为变量分配一段 RAM 空间, count 代表这段空间占用的字节数。利用 .DS 分配的变量空间不会被初始化,其中的数据是不确定的。建议定义变量使用 DS 伪指令。

例如:

Lb1: .DS 5 ; 定义了变量 Lb1 ,占用 5 个字节的 RAM 空间

如果在 ROM 区使用 .DS 伪指令定义常量(参见 .CODE 伪指令),那么将在 ROM 区占用 count 个字节的存储空间,每个字节的数值都是 FFH 。

•  定义类伪指令

1. VAR

[ 功能 ] 为标号赋值

[ 同义伪指令 ] DEFL 、 SET (由于“ SET ”是 SPMC65 的位操作指令,所以当 .SET 作为伪指令使用时,前面的句点不能省略。)

[ 格式 ]

label: .VAR value

[ 说明 ] 利用 .VAR 伪指令可以使标号 label 的值等于 value 。利用 .VAR 给某一标号赋值后,可以再次利用 .VAR 为该标号(仅适用于 .VAR 赋值过的标号)重新赋值。例如:

Lb1: .VAR 15 ;Lb1=15

Lb2: .VAR Lb1 ;Lb2=15

Lb1: .VAR 16 ;Lb1=16

.VAR 伪指令与 .DB 、 .DS 等存储类伪指令的区别是, .DB 等存储类伪指令要有一段存储空间( ROM 或 RAM )作为载体,存储类伪指令的标号代表了所存储数据的首地址;而 .VAR 仅仅是使标号具有一个数值,它不会占用 ROM 或 RAM 空间。

2. EQU

[ 功能 ] 为标号赋值

[ 同义伪指令 ] EQUAL

[ 格式 ]

label: .EQU value

[ 说明 ] 与 .VAR 类似, .EQU 的作用也是为标号赋值。但不同的是 .EQU 无法为已经定义过的标号重新赋值。

例如:

Lb1: .EQU 15 ;Lb1=15

Lb1: .EQU 16 ; 试图为 Lb1 重新赋值,汇编时将出现错误

3. CODE

[ 功能 ] 将 .CODE 下的程序指令和数据定位到“代码段”。

[ 格式 ]

.CODE

[ 说明 ] 代码段的所有程序指令和数据都将存放在 ROM 中。代码段的起始地址在工程文件夹下的 prog.lik 文件中定义。对于 SPMC65P2408A 单片机,默认的代码段是地址为 $E000~$FFF9 的 ROM 区域。

例如:

.CODE

Var1: .DB 12H

Lb1: LDA #78H

Var1 标号下的数据与 Lb1 标号下的指令都将被定位在 ROM 区。

4. DATA

[ 功能 ] 将 .DATA 下的数据定位到“数据段”。

[ 格式 ]

.DATA

[ 说明 ] .DATA 下定义的所有数据都将存放在 RAM 中,因此数据段可作为变量存储段使用。数据段的起始地址在工程文件夹下的 prog.lik 文件中定义。对于 SPMC65P2408A 单片机,默认的数据段是地址为 $01A0~$01E0 的 RAM 区域。

例如:

.DATA

Var1: .DS 3

上述语句将为变量 Var1 在数据段分配 3 个字节的空间。

5. PAGE0

[ 功能 ] 将 .PAGE0 下的数据定位到“零页段”。

[ 格式 ]

.PAGE0

[ 说明 ] 在 .PAGE0 下定义的数据会被定位到 SPMC65 寻址空间的第 0 页,即 00h~FFh 的区域。事实上,对于 SPMC65 系列单片机, PAGE0 是指地址在 $0060~$00FF 区间内的 RAM 空间。零页 RAM 空间也通常用于变量的存储。

例如:

.PAGE0

Var1: .DS 1

变量 Var1 被分配了 1 个字节的零页 RAM 空间( $0060~$00FF 区间内的某一地址)来存储。

6. ORG

[ 功能 ] 为程序或数据指定一个“段偏移量”。

[ 同义伪指令 ] ORGIN

[ 格式 ]

.ORG value

[ 说明 ] .ORG 伪指令下的程序或数据被定位到由 value 指定的存储地址。 value 是一个段的地址偏移量,可以是标号也可以是数字。在链接时,会将段的起始地址加上偏移量 value 作为这些程序或数据的重定位地址。
例如:

.PAGE0

.ORG 20H

Var1: .DS 1

.CODE

.ORG 600H

Var2: .DB 34H

变量 Var1 将被定位到 $80 ( .PAGE0 的起始地址 $60 加上偏移量 20h ),对于 SPMC65P2408A ,常量 Var2 将被定位到 $E600 ( .CODE 的起始地址 $E000 加上偏移量 600h )。

7. PROC

[ 功能 ] 定义一个过程

[ 格式 ]

label: .PROC

[ 说明 ] .PROC 作为一个过程的起始标志,它应与过程结束标志 .ENDP 成对使用。参见 ENDP 。

8. ENDP

[ 功能 ] 结束一个过程的定义

[ 格式 ]

[label:] .ENDP

[ 说明 ] 结束由 .PROC 定义的过程。 label 与 .PROC 定义的过程名称相同,也可省略。

例如:

Lb1: .PROC

LDA #56h

STA 68h

RTS

.ENDP

9. STRUCT

[ 功能 ] 定义一个结构

[ 格式 ]

label: .STRUCT

[ 说明 ] .STRUCT 用于定义一个结构,它应与结束结构定义的伪指令 .ENDST 成对使用。与 ANSI-C 类似,汇编器可以把不同类型的数据组织在一个结构里,为处理复杂的数据结构提供了便利。使用 .STRUCT 创建的结构仅仅是定义出一个数据存储模板,本身不会占用存储空间。

例如:
MySt: .STRUCT

T1: .DB 3 ; 成员 1 是字节型,默认值为 3

T2: .DW 4 ; 成员 2 是字型,默认值为 4

.ENDST

定义了结构后,可声明使用该结构的具体对象(结构实体),其一般形式如下:

结构实体名 : 结构名 [ 结构成员表 ]

其中 [ 结构成员表 ] 用来存放结构变量中成员的值,也可保留空的方括号( [ ] ),使用结构的默认值。结构成员表中的各成员之间用逗号隔开。对于上例中定义的结构,可以声明若干个结构实体:

.CODE

St1: MySt [5,1F6h] ; 声明 MySt 型结构实体 St1 ,并为两个成员赋初值

St2: MySt [‘k', ] ; 为成员 T1 赋初值,而成员 T2 使用默认初值 4

St3: MySt [ ] ;St3 的两个成员都使用默认初值

对于定位在 RAM 区的结构实体,仅会占用相应长度的存储空间,而不会对其赋初值。

结构实体是不同数据类型的若干数据的集合,在程序中不能做为一个整体来参加数据处理。参加各种运算的应该是结构实体中各个成员项数据。结构成员项引用的一般形式为:

结构实体名 @ 成员名

例如:

LDA St2@T1

上述语句将把结构实体 St2 的成员 T2 中存储的字符 'k' 传送到累加器中。

10. ENDST

[ 功能 ] 结构定义结束标志。

[ 格式 ]

[label:] .ENDST

[ 说明 ] 结束由 .STRUCT 定义的结构。 label 与 .STRUCT 定义的结构名称相同,也可省略。参见 .STRUCT 伪指令。

11. MACRO

[ 功能 ] 定义一个宏

[ 格式 ]

label: .MACRO args

[ 说明 ] 本伪指令用来定义一个宏。“宏”是指在源程序里将一系列的源代码行用一个简单的宏名( label )所取代,当在某一行源程序中调用宏时,该宏的所有代码便会映射到此行,作为一段源程序被执行。宏必须以伪指令 .MACRO 作为起始,在结尾处以 .ENDM 或 .MACEND 结束。宏定义可以包含参数( args ),参数可以是标号、寄存器、立即数等,多个参数之间要用逗号分隔,本身包含逗号的参数要外加大括号“ { } ”。

例如:

Math_Add: .MACRO var1, var2, sum

LDA var1

CLC

ADC var2

STA sum

.ENDM

上述代码定义了一个名为 Math_Add 的宏。定义了宏之后,可以在程序中对宏进行调用:

Math_Add #5,{S_Table, X},S1

上述宏调用相当于产生如下代码:
LDA #5 ; 参数 1 (立即数)

CLC

ADC S_Table, X ; 参数 2 (带有逗号)

STA S1 ; 参数 3 (标号)

在宏里可以定义显式标号(由用户定义),也可以定义隐含标号(由汇编器自动定义)。汇编器不会改变用户定义的显示标号,当程序多次调用该宏时,会在程序体中出现重复的标号,导致汇编出错。例如下面这个名为 M_IsOdd 的宏定义:

M_IsOdd: .MACRO var1

LDA var1

AND #01h

BNE Odd

Even:

LDA #0

JMP Out

Odd:

LDA #1

Out:

.ENDM

若在程序中两次调用该宏:

M_IsOdd #3

M_IsOdd #4

宏被展开后将由于标号 Even 、 Odd 、 Out 被重复定义而出错。解决的方法是使用隐含标号,即在标号后加一个井号( # )作为后缀,汇编器会自动将 # 替换为一个后缀数字符号 _x_xxxx ( x 代表一位数字)。因此, M_IsOdd 宏可修改为:

M_IsOdd: .MACRO var1

LDA var1

AND #01h

BNE Odd#

Even#:

LDA #0

JMP Out#

Odd#:

LDA #1

Out#:

.ENDM

此外,在宏定义中还可以使用字符串连接操作符( | )与数字连接操作符( < 数字 > ),例如:

Mac1 .MACRO arg

value: .VAR value+1

arg|<value>: .EQU value

.ENDM

对该宏的调用语句:

value: .VAR 0

Mac1 Label

Mac1 Label

宏调用语句被展开后将得到这样的结果:

Label1: .EQU 1

Label2: .EQU 2

12. ENDM

[ 功能 ] 宏定义结束

[ 同义伪指令 ] MACEND

[ 格式 ]

.ENDM

[ 说明 ] 结束由 .MACRO 定义的宏。参见 .STRUCT 伪指令。

•  条件类伪指令

1. IF

[ 功能 ] 当条件为真时进行汇编

[ 同义伪指令 ] IFNZ 、 IFNFALSE 、 IFN 、 IFTRUE

[ 格式 ]

.IF value

[ 说明 ] 当 value 的值不为 0 时(条件为真),汇编器将对 .IF 下的语句进行汇编。 value 可以是数值、标号或算术表达式。 .IF 应与条件结束伪指令 .ENDIF 成对使用,多组 .IF 与 .ENDIF 也可嵌套使用。 下面是一个两层 .IF 嵌套的示例:

Lb1: .VAR 3 ;Lb1=3

.IF Lb1 ; 如果 Lb1 不为 0

Lb1: .VAR Lb1 – 1 ;Lb1=Lb1-1 (即 Lb1=2 )

.IF Lb1 ; 如果 Lb1 仍不为 0

Lb1: .VAR Lb1 – 1 ;Lb1=Lb1-1 (即 Lb1=1 )

.ENDIF

.ENDIF

.CODE

LDA #Lb1 ; 相当于 LDA #1

2. IFZ

[ 功能 ] 当条件为假时进行汇编

[ 同义伪指令 ] IFFALSE 、 IFE 、 IFNTRUE

[ 格式 ]

.IFZ value

[ 说明 ] 当 value 的值为 0 时(条件为假),汇编器将对 .IFZ 下的语句进行汇编。 value 可以是数值、标号或算术表达式。与 .IFZ 对应的条件结束伪指令仍然是 .ENDIF 。的与 .IF 类似, .IFZ 也可嵌套使用,而且 .IF 与 .IFZ 可相互嵌套。

例如:

Lb1: .VAR 3 ;Lb1=3

.IF Lb1 ; 如果 Lb1 不为 0

Lb1: .VAR Lb1 – 3 ;Lb1=Lb1-3 (即 Lb1=0 )

.IFZ Lb1 ; 如果 Lb1 为 0

Lb1: .VAR Lb1 + 1 ;Lb1=Lb1+1 (即 Lb1=1 )

.IFZ Lb1 – 1 ; 如果 Lb1-1 不为 0

Lb1: .VAR 2 ;Lb1=2

.ENDIF

.ENDIF

.ENDIF

.CODE

LDA #Lb1 ; 相当于 LDA #2

3. IFDEF

[ 功能 ] 如果标号已经被定义过,则进行汇编

[ 格式 ]

.IFDEF label

[ 说明 ] 如果标号 label 已经被定义过,汇编器将对 .IFDEF 下的语句进行汇编。与 .IFDEF 对应的条件结束伪指令是 .ENDIF 。

例如:

Lb1: .VAR 3 ; 定义标号 Lb1

.IFDEF Lb2 ; 如果定义过标号 Lb2

Lb3: .VAR 4 ; 由于 Lb2 没被定义,此句被汇编器忽略

.ENDIF
.ENDIF

.ENDIF

.CODE

LDA #Lb1 ; 相当于 LDA #1

2. IFZ

[ 功能 ] 当条件为假时进行汇编

[ 同义伪指令 ] IFFALSE 、 IFE 、 IFNTRUE

[ 格式 ]

.IFZ value

[ 说明 ] 当 value 的值为 0 时(条件为假),汇编器将对 .IFZ 下的语句进行汇编。 value 可以是数值、标号或算术表达式。与 .IFZ 对应的条件结束伪指令仍然是 .ENDIF 。的与 .IF 类似, .IFZ 也可嵌套使用,而且 .IF 与 .IFZ 可相互嵌套。

例如:

Lb1: .VAR 3 ;Lb1=3

.IF Lb1 ; 如果 Lb1 不为 0

Lb1: .VAR Lb1 – 3 ;Lb1=Lb1-3 (即 Lb1=0 )

.IFZ Lb1 ; 如果 Lb1 为 0

Lb1: .VAR Lb1 + 1 ;Lb1=Lb1+1 (即 Lb1=1 )

.IFZ Lb1 – 1 ; 如果 Lb1-1 不为 0

Lb1: .VAR 2 ;Lb1=2

.ENDIF

.ENDIF

.ENDIF

.CODE

LDA #Lb1 ; 相当于 LDA #2

3. IFDEF

[ 功能 ] 如果标号已经被定义过,则进行汇编

[ 格式 ]

.IFDEF label

[ 说明 ] 如果标号 label 已经被定义过,汇编器将对 .IFDEF 下的语句进行汇编。与 .IFDEF 对应的条件结束伪指令是 .ENDIF 。

例如:

Lb1: .VAR 3 ; 定义标号 Lb1

.IFDEF Lb2 ; 如果定义过标号 Lb2

Lb3: .VAR 4 ; 由于 Lb2 没被定义,此句被汇编器忽略

.ENDIF

Lb2: .VAR 2 ;Lb2=2

.ENDIF

.CODE

LDA #Lb2 ; 相当于 LDA #1

6. ENDIF

[ 功能 ] 用来结束条件汇编组合的定义

[ 同义伪指令 ] ENDC

[ 格式 ]

.ENDIF

[ 说明 ] 本伪指令用来结束条件汇编组合定义,它必须与 IF 或 IF* (如 .IFDEF 、 .IFZ 等)条件伪指令结合使用。

•  汇编链接类伪指令

1. PUBLIC

[ 功能 ] 将标号声明为公有属性,使其可以被其他文件中的程序引用。

[ 同义伪指令 ] GLOBAL 、 XDEF

[ 格式 ]

.PUBLIC label [,label][,…]

[ 说明 ] .PUBLIC 将每个 label 声明为公有标号,这些标号可以在其他文件中被程序引用。注意,利用 .VAR 伪指令定义的标号不能声明为公有标号。多个标号间要用逗号隔开。参见 .EXTERN 伪指令。

例如:

.PUBLIC Lb1, Lb2 ; 将 Lb1 和 Lb2 声明为公有标号

2. EXTERN

[ 功能 ] 声明引用其他文件中已经定义过的公有标号。

[ 同义伪指令 ] EXTERNAL 、 XREF

[ 格式 ]

.EXTERN label [,label][,…]

[ 说明 ] 对于其他文件中声明的公有标号,可利用 .EXTERN 引用到当前文件中。 label 是在其他文件中定义过,且已经被声明为公有的标号。经 .EXTERN 声明后,这些标号就可以在当前文件中引用了。注意,汇编器不支持两个或两个以上外部标号之间的算术或逻辑运算。多个标号之间要用逗号隔开。例如:

[File1.asm]

.PUBLIC Lb1, Lb2 ; 将标号 Lb1 和 Lb2 声明为公有标号

Lb1: .EQU 3 ; Lb1=3

L b 2: .EQU 4 ;L b2=4

[ File2.asm]

.EXTERN Lb1, Lb2 ; 声明在 File1.asm 中定义的两个全局标号

Lb3: .EQU 5 ;Lb3=5

Lb4: .EQU Lb2 ;Lb4=Lb2=4

Lb5: .EQU Lb3 + Lb4 ;Lb5=5+4=9

Lb6: .EQU Lb1 + Lb4 ; 由于 Lb1 和 Lb4 都涉及到外部标号,相当于有两个外

; 部标号参与了算术运算,因此汇编器将提示本行出错。

3. INCLUDE

[ 功能 ] 将其他文件包含到当前位置。

[ 格式 ]

.INCLUDE filename

.INCLUDE “filename”

[ 说明 ] 该伪指令将指定的文件内容包含到当前文件的当前位置。 filename 可以是相对路径,也可以是绝对路径。当 filename 使用长文件名格式(例如文件路径中含有空格)时, filename 外必须加上双引号。

例如:

.INCLUDE file1.asm ; 包含与当前文件同路径的文件

.INCLUDE ..\src\files\file1.inc ; 使用相对路径

.INCLUDE “c:\My Documents\file1.inc” ; 包含路径为长格式的文件

4. END

[ 功能 ] 定义程序的结束位置

[ 格式 ]

.END

[ 说明 ] 使用 .END 可以强行结束一个程序文件,使汇编器忽略该文件中 .END 下面的内容。对于被包含文件, .END 作用范围仅限于被包含文件本身。例如:

[file1.inc]

Lb1: .VAR 3 ;Lb1=1

.END ; 强行结束 file1.inc

Lb1: .VAR 4 ; 此句将被忽略

[file2.asm]

.INCLUDE file1.inc ; 包含 file1.inc 文件的内容

Lb3: .VAR Lb1 ;Lb3=3

从上面的例子可以看出, file1.inc 中的 .END 伪指令使汇编器忽略了 .END 下面的内容,但是尽管 file2.asm 将 file1.inc 包含进来, file1.inc 中的 .END 不能使 file2.asm 强行结束。

•  常用程序实例

•  二进制无符号数 16 位与 16 位的加法运算

程序:程序流程图见 图 2.15 所示:

图 2 . 15 二进制无符号数 16 位与 16 位加法程序流程图

【函数原型】 F_Bin16Add16

【功能描述】 二进制无符号数 16 位与 16 位的加法运算 , 和为 24 位

【入口参数】 GB_BinAugendAddr-------16bit 被加数的地址

GB_BinAddendAddr-------16bit 加数的地址

GB_BinSumAddr-----------24bit 和的地址

【出口参数】 GB_BinSumAddr-----------24bit 和的地址

.PUBLIC F_Bin16Add16

F_Bin16Add16:

ldx #2 ;2 字节相加

ldy #2

lda #0

sta (GB_BinSumAddr),y ; 清除和的高 8 位

ldy #0

clc

?L_Add_Loop:

lda (GB_BinAugendAddr),y

adc (GB_BinAddendAddr),y

sta (GB_BinSumAddr),y

iny

dex ; 两个字节是否加完?

bne ?L_Add_Loop ; 没有,返回

bcc ?L_Add_End ; 没有进位,直接跳出

lda #1

sta (GB_BinSumAddr),y

?L_Add_End:

rts


•  二进制无符号数 16 位与 16 位的减法运算

程序:程序流程图见 图 2.16 所示:

图 2 . 16 二进制无符号数 16 位与 16 位的减法程序流程图

【函数原型】 F_Bin16Sub16

【功能描述】 二进制无符号数 16 位与 16 位的减法运算 , 差为 16 位

【入口参数】 GB_BinMinuendAddr--------------16bit 被减数的地址

GB_BinSubtrahendAddr----------16bit 减数的地址

GB_BinSubAddr--------------------16bit 差的地址

【出口参数】 GB_BinSubAddr--------------------16bit 差的地址

Z------------- “ Z ” =0 ,结果为正数;

“ Z ” =1 ,结果为负数

.PUBLIC F_Bin16Sub16

F_Bin16Sub16:

ldx #2 ;2 字节相减

ldy #0

sec

?L_Sub_Loop:

lda (GB_BinMinuendAddr),y

sbc (GB_BinSubtrahendAddr),y

sta (GB_BinSubAddr),y

iny

dex ; 两个字节的数是否相减完 ?

bne ?L_Sub_Loop ; 否则跳转

bcc ?L_Sub_End ; 有借位( C=0 ),则 Z=1 ,跳转

lda #$FF ; 无借位则 Z=0

?L_Sub_End:

rts

•  二进制无符号数 8 位与 8 位的乘法运算

函数设计方法

模拟手动乘法,其计算方法如下 图 2.17 ,乘数一共右移八次,每移出一位,被乘数同时也左移一位(第一次除外),并且进行一次判断,若乘数移出位为“ 1 ”,进行一次相加。例如 #95h 乘以 #CDh ,乘积为 #7751h 。其流程图如 图 2.18 所示。

图 2. 17 二进制无符号数 8 位与 8 位的乘法运算式

函数设计方法 :
  模拟手动除法,即:被除数从最高位开始不断的向余数(初始值为 0 )的最低位移入,余数也不断的向左移动,每移一位,余数与除数比较一次,大于或等于除数时,余数减去除数,差存在余数中,同时商加一,然后商向左移动一位,最低位补 0 ,若余数小于除数,则商直接向左移动一位,最低位补 0 。此时,一个循环结束,共有 8 个循环,最后一个循环商不左移一位。其流程如 图 2.20 所示。例如( #F7h )除以( #19h ),商为( #09h ),余数为( #16h ),计算方法如 图 2.19 所示:

图 2. 19 二进制无符号数 8 位与 8 位除法运算式

图 2.20 二进制无符号数 8 位与 8 位除法程序流程图


【函数原型】 F_Bin8Div8
【功能描述】 二进制无符号数 8 位与 8 位的除法运算
【入口参数】 X------------- 被除数Y------------- 除数
【出口参数】 X------------- 商Y------------- 余数Z------------- 
出错标志,“ Z ”值为 1 ,结果出错;“ Z ”值为 0 ,
结果正确注:以下程序中 IB_BinDividend 、 IB_BinDividendH 、 IB_BinDivisor 都是定义的变量。

.PUBLIC F_Bin8Div8

F_Bin8Div8:

cpy #0 ; 除数 =0?

beq ?L_Div_End ; 是则跳转

stx IB_BinDividend ; 被除数载入 x 寄存器

ldx #0

stx IB_BinDividendH ;IB_BinDividendH 将用于保存余数

sty IB_BinDivisor ; 除数载入 y 寄存器

ldy #8 ; 被除数移位次数

?L_Shift_Div:

rol IB_BinDividend

rol IB_BinDividendH ; 被除数移一位到余数中

lda IB_BinDividendH

cmp IB_BinDivisor ; 余数 > 除数 ?

bcc ?L_Div_NextBit ; 否则跳转

?L_Result1:

inx ; 商加 1

sec

lda IB_BinDividendH

sbc IB_BinDivisor ; 余数 – 除数

sta IB_BinDividendH

?L_Div_NextBit:

dey ; 移位上否结束 ?

beq ?L_Div_Exit ; 是则跳转退出

txa

asl a

tax

jmp ?L_Shift_Div ; 再进入循环

?L_Div_Exit:

lda IB_BinDividendH ; 取出余数

tay

lda #$FF ;z=0, 结果是正确的

?L_Div_End:

rts

版权所有:北京凌阳爱普科技有限公司 京ICP备05061966号 未经许可网站内容严禁复制、转载,任何人不得擅自使用
友情链接:凌阳爱普 嵌入式培训 安卓培训 凌阳大学计划 北京嵌入式培训 深圳嵌入式培训 成都嵌入式培训