Assemble汇编语言设计技术

 

最近,我帮助朋友编写一个使用Assemble汇编语言的程序,接触了一些Assemble行业的开发工作者以及行业管理人员,在与他们合作和交流过程中发现,我国Assemble汇编语言设计技术根本性的变化,没有一个飞跃性的发展,Assemble汇编设计技术仍然处于落后的、不发达的国家。当然,我可能言过其实,Assemble汇编设计的工作者他们使用Assemble汇编语言设计了很多的产品,在各个领域运用Assemble汇编技术创造出辉煌的成绩,我们必须认可的,并向他们表示衷心问候。但是,当我完成这一篇文章,让我们从事Assemble汇编语言设计的朋友们阅读后,我们可以讨论我所表达的意思是否正确。我已经20年没有再深入地讨论过Assemble汇编语言设计问题了,非常有信心的相信我国的Assemble汇编设计技术这一方面发展应当进入世界同等水平发展,而且有层出不穷的更新的设计技术出现,可是当我再回头接触时,令人只有伤感。

电子邮箱:yinshenk@21cn.com

一、            概述

通常,我们知道计算机是由电子元器件组合而成的,主要是逻辑电路组成,因此计算机中真正可以处理的仅仅只是由“0”、“1” 组成的状态码或者二进制编码,实际被称为指令代码。这种指令代码对于设计人员来说不易编写或修改,非常容易出现错误,由于必须依赖于机器,使用极其不方便。于是人们在生产实践过程中发明“指令的助记符”这个概念,于是采用几个字母(往往就是其含义的英文单词或缩写)代替某条指令,如加法用ADD表示,这种符号系统的扩大就是汇编语言。使用汇编语言编程,大大提高了编写效率和质量,并且同样实现了可直接对硬件内部进行控制。当然,使用汇编语言编程,需要我们设计人员对硬件的了解,才能够编写出高质量的程序,这是汇编语言的最大缺点。如果我们对硬件的认识不具体,根本无法编写出高效率的程序,甚至不法编写汇编程序。这就是汇编语言与机器的体系结构存在的依赖关系。

汇编语言设计程序,是所有程序语言的基本资源。我们不管你使用除汇编语言或指令代码以外的程序语言编程,仅仅是编程表达、编程规则和变成环境的变化,但是这些变化都是基于汇编语言或指令代码基础上形成的新型开发语言,主要是为了解决商业化服务对象、编程效率和编程目标明确化。如果我们在不同的计算机上编程,我们需要不同的资源环境,其资源环境最重要的问题是提供一个适应计算机运行的底层资源,或者目标资源,这些资源就包含有目标指令代码文件(当然也有其它部分的资源组成,我们这里不必进行详细解释)。如果我们使用高级语言编程,高级语言编译成为最终可以执行的程序,就是一个指令代码程序。不管计算机语言的发展,汇编语言或指令代码是所有计算机语言的基本,掌握汇编语言有助于设计程序。

汇编语言设计程序,处理效率高、占用资源少。如果使用过各种不同的语言编程的人员都非常清楚的感受到,汇编语言编写程序生成最终执行文件,是所有语言不可达到的最小空间,并且处理目的明确,浪费代码极少,处理速度惊人。由于汇编具备所有程序语言所追求实现的目标,然而所有程序语言又不可能达到的目标,所以汇编语言仍然是最常用的设计语言之一,是不可以取代的。比如在设计最底层的资源函数,DSP硬件封装设计等,都会使用汇编语言完成。C语言或其它语言已经有替代汇编语言设计程序,其根本是为了解决一个开发设计效率和程序交流性问题,但是,如果我们深入分析的情况下作断定,汇编语言的重要地位仍然是不可磨灭的。

我们从未来发展的角度来看,汇编语言永久是生产、开发、设计的一个重要语言。首先,DSP硬件封装设计、单片机功能化设计的角度出发,这类软件设计领域将会永无止境的发展下去,并且人们将会提出生产、设计达到极限效率产品,必须依赖于汇编语言实现目标;第二,我们从计算机语言发展角度出发,不管出现什么样语言,其语言已经达到更加方便,更加设计合理,设计效率更加提高等优点,它必须提供一个仿真或DEBUG测试平台,那么就必须包含有代码的意思存在,也就是依然存在汇编语言的影子;第三,从资源代码封装的角度出发,随着提供资源更加丰富,开发能力不断增强,资源的可靠性和资源的效率性随之也是永久的话题,与汇编结果对比大小、效率是设计者们永久追求的目标,最终结果依然会采用翻译成汇编代码进行优化,以达到最高境界。

上面描述,主要是告诉从事汇编语言的或非从事汇编语言的设计人员,我们必须正确看待汇编语言,正确看待汇编语言设计技术,不能够忽略汇编语言的重要性和汇编语言设计技术的重要性,希望大家共同努力推动发展汇编语言设计技术。

下面简单介绍本文想描述的内容。我由于与使用汇编语言的朋友的接触并一起共同开发产品,才知道我国汇编语言设计技术的落后,而且这么落后,才促使我产生必须写一些东西与大家讨论的念头。当然,我可能有些偏激,接触的任务不能够代表我国真实的汇编发展水平,这也是我需要写一些东西以其道抛砖引玉的作用,看看我国汇编设计技术实际状况。因此,首先还是按照套路写一些基本内容,对那些想看又不懂得人来说起到启发作用,对那些已经掌握汇编语言的朋友们来说,就过过目,全当废话。然后,带入我想描述真正的内容部分,主要思路是按照软件设计生命周期规范讨论汇编语言设计的起步工作,流程分析,程序结构设计、程序设计。重点在程序结构设计和程序设计两个方面,我准备比较具体的描述。在阅读整个文章后,希望你们能够明确的看到设计过程中可以实现性、设计方法可以借鉴性、设计方法具有理智性。同时,也能够看出设计如何保证可读性、唯一性、可延续性、可维护性、可更改性,除这些以外,也希望看到汇编语言产品生产所需要追求的目标,从事汇编语言设计人员应当如何提高自己的设计水平。

在这里需要声明,我编写这篇文章是想讨论现在设计技术问题,所以提出的一些设计方法希望大家教条使用,而是根据自己设计环境,借鉴提出的思想,相应建立更加符合实际状况的设计技术规范,建立更加符合实际的优秀编程框架,并在长期实践过程中完善规范和设计框架,以保证长期、有效、高效率的设计工作。

二、            汇编指令

汇编指令是计算机语言中最基本的和最原始的语言,描述计算机可运行的指令,是指令编码的“助记符”。不同的计算机有不同的指令集和指令代码集。目前最常见的汇编指令集有:Intel公司8080系列指令集,8051系列的51指令集,Ateml公司的AVR指令集,motorola公司的68000指令集,rockwall公司的6502指令集,Zilog公司的Z80命令集等等。不管是哪一种计算机,不管是哪一种指令,我们可以认为是计算机可以识别并能够相应作出动作的指令。

指令代码描述是由一定的规范格式,划分格式主要分成两种形式,一种是以带参数指令和不带参数指令格式描述,一般通用格式表达:

命令码 <第一参数>,<第二参数>,<第三参数>

具体命令需要带参数的数目,内容都必须正确无误的加上,否则会出现编译错误。另一种描述可能现在已经有很少人用了,字节数目描述,单字节指令、双字节指令、多字节指令等说法,这种描述让我们一眼就可以看出是根据指令代码形式表达的。字节数目表达方式来说,可以帮助我们使用指令是注意使用空间问题,同时也可以互助地帮助我们知道使用的执行指令周期。当然,现在汇编语言设计可能不必那么像以前刻意考虑空间和指令周期的问题,因为,现在计算机速度和可提供空间的成本问题已经可以忽略不计了。

计算机指令码一般是用一个字节来表示,范围在0-255的数值代码,那么指令集一般也在128个范围内。指令可以分为传送指令、跳转指令、算术/逻辑指令、外部/端口访问指令和状态指令,以及栈操作指令。一个指令执行状况可以分为影响状态位指令和不影响状态位指令;也可以分为修改结果指令和产生状态结果指令;也可以分为条件控制和无条件控制指令;一个指令执行数据操作可以分为立即数、直接地址和间接地址操作,等。总之我们必须掌握这些规律,才能够很好的使用指令编写程序。

指令集有简单指令集和复杂指令集之说,也有一般指令和扩展增强指令之说。不管怎样,我们都必须根据应用环境编写有用的应用程序,不要凭自己的主观思维处理,可能会导致事成无用的局面。下面给出一个AT90PWM CPU指令集,以让你们感受一下指令的定义和描述,并且让你们注意一些描述的细节。

ATmega8 / ATmega48/88/168 / ATmega8515 / ATmega8535 / AT90PWM2/3 Instruction Set

The Assembler accepts mnemonic instructions from the instruction set. A summary of the instruction set mnemonics and their parameters is given here. For a detailed description of the Instruction set, refer to the AVR Data Book.

Arithmetic and Logic Instructions

Mnemonic

Operands

Description

Operation

Flags

Cycles

ADD 

Rd,Rr 

Add without Carry 

Rd = Rd + Rr 

Z,C,N,V,H,S 

1

ADC

Rd,Rr

Add with Carry

Rd = Rd + Rr + C

Z,C,N,V,H,S

1

ADIW

Rd, K

Add Immediate To Word

Rd+1:Rd,K

Z,C,N,V,S

2

SUB

Rd,Rr

Subtract without Carry

Rd = Rd - Rr

Z,C,N,V,H,S

1

SUBI

Rd,K8

Subtract Immediate

Rd = Rd - K8

Z,C,N,V,H,S

1

SBC

Rd,Rr

Subtract with Carry

Rd = Rd - Rr - C

Z,C,N,V,H,S

1

SBCI

Rd,K8

Subtract with Carry Immedtiate

Rd = Rd - K8 - C

Z,C,N,V,H,S

1

AND

Rd,Rr

Logical AND

Rd = Rd · Rr

Z,N,V,S 

1

ANDI

Rd,K8

Logical AND with Immediate

Rd = Rd · K8

Z,N,V,S

1

OR

Rd,Rr

Logical OR

Rd = Rd V Rr

Z,N,V,S

1

ORI

Rd,K8

Logical OR with Immediate

Rd = Rd V K8

Z,N,V,S

1

EOR

Rd,Rr

Logical Exclusive OR

Rd = Rd EOR Rr

Z,N,V,S

1

COM

Rd

One's Complement

Rd = $FF - Rd

Z,C,N,V,S

1

NEG

Rd

Two's Complement

Rd = $00 - Rd

Z,C,N,V,H,S

1

SBR

Rd,K8

Set Bit(s) in Register

Rd = Rd V K8

Z,C,N,V,S

1

CBR

Rd,K8

Clear Bit(s) in Register

Rd = Rd · ($FF - K8)

Z,C,N,V,S

1

INC

Rd

Increment Register

Rd = Rd + 1

Z,N,V,S

1

DEC

Rd

Decrement Register

Rd = Rd -1

Z,N,V,S

1

TST

Rd

Test for Zero or Negative

Rd = Rd · Rd

Z,C,N,V,S

1

CLR

Rd

Clear Register

Rd = 0

Z,C,N,V,S

1

SER

Rd

Set Register

Rd = $FF

None

1

SBIW

Rdl,K6

Subtract Immediate from Word

Rdh:Rdl = Rdh:Rdl - K 6

Z,C,N,V,S

2

MUL

Rd,Rr

Multiply Unsigned

R1:R0 = Rd * Rr

Z,C

2

MULS

Rd,Rr

Multiply Signed

R1:R0 = Rd * Rr

Z,C

2

MULSU

Rd,Rr

Multiply Signed with Unsigned

R1:R0 = Rd * Rr

Z,C

2

FMUL

Rd,Rr

Fractional Multiply Unsigned

R1:R0 = (Rd * Rr) << 1

Z,C

2

FMULS

Rd,Rr

Fractional Multiply Signed

R1:R0 = (Rd *Rr) << 1

Z,C

2

FMULSU

Rd,Rr

Fractional Multiply Signed with Unsigned

R1:R0 = (Rd * Rr) << 1

Z,C

2

 

Branch Instructions

Mnemonic

Operands

Description

Operation

Flags

Cycles

RJMP

k

Relative Jump

PC = PC + k +1

None

2

IJMP

None

Indirect Jump to (Z)

PC = Z

None

2

JMP
(Note 1)

k

Jump 

PC = k

None

3

RCALL

k

Relative Call Subroutine

STACK = PC+1, PC = PC + k + 1

None

3/4*

ICALL

None

Indirect Call to (Z)

STACK = PC+1, PC = Z 

None

3/4*

CALL
(Note 1)

k

Call Subroutine

STACK = PC+2, PC = k

None

4/5*

RET

None

Subroutine Return

PC = STACK

None

4/5*

RETI

None

Interrupt Return

PC = STACK

I

4/5*

CPSE

Rd,Rr

Compare, Skip if equal 

if (Rd ==Rr) PC = PC 2 or 3

None

1/2/3

CP

Rd,Rr

Compare

Rd -Rr

Z,C,N,V,H,S

1

CPC

Rd,Rr

Compare with Carry

Rd - Rr - C

Z,C,N,V,H,S

1

CPI

Rd,K8

Compare with Immediate

Rd - K

Z,C,N,V,H,S

1

SBRC

Rr,b

Skip if bit in register cleared

if(Rr(b)==0) PC = PC + 2 or 3

None

1/2/3

SBRS

Rr,b

Skip if bit in register set

if(Rr(b)==1) PC = PC + 2 or 3

None

1/2/3

SBIC

P,b

Skip if bit in I/O register cleared

if(I/O(P,b)==0) PC = PC + 2 or 3

None

1/2/3

SBIS

P,b

Skip if bit in I/O register set

if(I/O(P,b)==1) PC = PC + 2 or 3

None

1/2/3

BRBC

s,k

Branch if Status flag cleared

if(SREG(s)==0) PC = PC + k + 1

None

1/2

BRBS

s,k

Branch if Status flag set

if(SREG(s)==1) PC = PC + k + 1

None

1/2

BREQ

k

Branch if equal

if(Z==1) PC = PC + k + 1

None

1/2

BRNE

k

Branch if not equal

if(Z==0) PC = PC + k + 1

None

1/2

BRCS

k

Branch if carry set

if(C==1) PC = PC + k + 1

None

1/2

BRCC

k

Branch if carry cleared

if(C==0) PC = PC + k + 1

None

1/2

BRSH

k

Branch if same or higher

if(C==0) PC = PC + k + 1

None

1/2

BRLO

k

Branch if lower

if(C==1) PC = PC + k + 1

None

1/2

BRMI

k

Branch if minus

if(N==1) PC = PC + k + 1

None

1/2

BRPL

k

Branch if plus

if(N==0) PC = PC + k + 1

None

1/2

BRGE

k

Branch if greater than or equal (signed)

if(S==0) PC = PC + k + 1

None

1/2

BRLT

k

Branch if less than (signed)

if(S==1) PC = PC + k + 1

None

1/2

BRHS

k

Branch if half carry flag set

if(H==1) PC = PC + k + 1

None

1/2

BRHC

k

Branch if half carry flag cleared

if(H==0) PC = PC + k + 1

None

1/2

BRTS

k

Branch if T flag set

if(T==1) PC = PC + k + 1

None

1/2

BRTC

k

Branch if T flag cleared

if(T==0) PC = PC + k + 1

None

1/2

BRVS

k

Branch if overflow flag set

if(V==1) PC = PC + k + 1

None

1/2

BRVC

k

Branch if overflow flag cleared

if(V==0) PC = PC + k + 1

None

1/2

BRIE

k

Branch if interrupt enabled

if(I==1) PC = PC + k + 1

None

1/2

BRID

k

Branch if interrupt disabled

if(I==0) PC = PC + k + 1

None

1/2

 

Data Transfer Instructions

Mnemonic

Operands

Description

Operation

Flags

Cycles

MOV

Rd,Rr

Copy register

Rd = Rr

None

1

MOVW

Rd,Rr

Copy register pair

Rd+1:Rd = Rr+1:Rr, r,d even

None

1

LDI

Rd,K8

Load Immediate

Rd = K

None

1

LDS

Rd,k

Load Direct

Rd = (k)

None

2*

LD

Rd,X

Load Indirect

Rd = (X)

None

2*

LD

Rd,X+

Load Indirect and Post-Increment

Rd = (X), X=X+1

None

2*

LD

Rd,-X

Load Indirect and Pre-Decrement

X=X-1, Rd = (X)

None

2*

LD

Rd,Y

Load Indirect

Rd = (Y)

None

2*

LD

Rd,Y+

Load Indirect and Post-Increment

Rd = (Y), Y=Y+1

None

2*

LD

Rd,-Y

Load Indirect and Pre-Decrement

Y=Y-1, Rd = (Y)

None

2*

LDD

Rd,Y+q

Load Indirect with displacement

Rd = (Y+q)

None

2*

LD

Rd,Z

Load Indirect 

Rd = (Z)

None

2*

LD

Rd,Z+

Load Indirect and Post-Increment

Rd = (Z), Z=Z+1

None

2*

LD

Rd,-Z

Load Indirect and Pre-Decrement

Z=Z-1, Rd = (Z)

None

2*

LDD

Rd,Z+q

Load Indirect with displacement

Rd = (Z+q)

None

2*

STS

k,Rr

Store Direct

(k) = Rr

None

2*

ST

X,Rr

Store Indirect

(X) = Rr

None

2*

ST

X+,Rr

Store Indirect and Post-Increment

(X) = Rr, X=X+1

None

2*

ST

-X,Rr

Store Indirect and Pre-Decrement

X=X-1, (X)=Rr

None

2*

ST

Y,Rr

Store Indirect

(Y) = Rr

None

2*

ST

Y+,Rr

Store Indirect and Post-Increment

(Y) = Rr, Y=Y+1

None

2

ST

-Y,Rr

Store Indirect and Pre-Decrement

Y=Y-1, (Y) = Rr

None

2

STD

Y+q,Rr

Store Indirect with displacement

(Y+q) = Rr

None

2

ST

Z,Rr

Store Indirect

(Z) = Rr

None

2

ST

Z+,Rr

Store Indirect and Post-Increment

(Z) = Rr, Z=Z+1

None

2

ST

-Z,Rr

Store Indirect and Pre-Decrement

Z=Z-1, (Z) = Rr

None

2

STD

Z+q,Rr

Store Indirect with displacement

(Z+q) = Rr

None

2

LPM

None

Load Program Memory

R0 = (Z)

None

3

LPM

Rd,Z

Load Program Memory

Rd = (Z)

None

3

LPM

Rd,Z+

Load Program Memory and Post-Increment

Rd = (Z), Z=Z+1

None

3

SPM

None

Store Program Memory

(Z) = R1:R0

None

-

IN

Rd,P

In Port

Rd = P

None

1

OUT

P,Rr

Out Port

P = Rr

None

1

PUSH

Rr

Push register on Stack

STACK = Rr

None

2

POP

Rd

Pop register from Stack

Rd = STACK

None

2

 

Bit and Bit-test Instructions

Mnemonic

Operands

Description

Operation

Flags

Cycles

LSL

Rd

Logical shift left

Rd(n+1)=Rd(n), Rd(0)=0, C=Rd(7)

Z,C,N,V,H,S

1

LSR

Rd

Logical shift right

Rd(n)=Rd(n+1), Rd(7)=0, C=Rd(0)

Z,C,N,V,S

1

ROL

Rd

Rotate left through carry

Rd(0)=C, Rd(n+1)=Rd(n), C=Rd(7)

Z,C,N,V,H,S

1

ROR

Rd

Rotate right through carry

Rd(7)=C, Rd(n)=Rd(n+1), C=Rd(0)

Z,C,N,V,S

1

ASR

Rd

Arithmetic shift right

Rd(n)=Rd(n+1), n=0,...,6

Z,C,N,V,S

1

SWAP

Rd

Swap nibbles

Rd(3..0) = Rd(7..4), Rd(7..4) = Rd(3..0)

None

1

BSET 

s

Set flag

SREG(s) = 1

SREG(s)

1

BCLR

s

Clear flag

SREG(s) = 0

SREG(s)

1

SBI

P,b

Set bit in I/O register

I/O(P,b) = 1

None

2

CBI

P,b

Clear bit in I/O register

I/O(P,b) = 0

None

2

BST

Rr,b

Bit store from register to T

T = Rr(b)

T

1

BLD

Rd,b

Bit load from register to T

Rd(b) = T

None

1

SEC

None

Set carry flag

C =1

C

1

CLC

None

Clear carry flag

C = 0

C

1

SEN

None

Set negative flag

N = 1

N

1

CLN

None

Clear negative flag

N = 0

N

1

SEZ

None

Set zero flag

Z = 1

Z

1

CLZ

None

Clear zero flag

Z = 0

Z

1

SEI

None

Set interrupt flag

I = 1

I

1

CLI

None

Clear interrupt flag

I = 0

I

1

SES

None

Set signed flag

S = 1

S

1

CLS

None

Clear signed flag

S = 0

S

1

SEV

None

Set overflow flag

V = 1

V

1

CLV

None

Clear overflow flag

V = 0

V

1

SET

None

Set T-flag

T = 1

T

1

CLT

None

Clear T-flag

T = 0

T

1

SEH

None

Set half carry flag

H = 1

H

1

CLH

None

Clear half carry flag

H = 0

H

1

NOP

None

No operation

None

None

1

SLEEP

None

Sleep

See instruction manual

None

1

WDR

None

Watchdog Reset

See instruction manual

None

1

NOTE 1: These instructions are only available in ATmega168.

The Assembler is not case sensitive.

The operands have the following forms:

Rd: Destination (and source) register in the register file
Rr: Source register in the register file
b: Constant (0-7), can be a constant expression
s: Constant (0-7), can be a constant expression
P: Constant (0-31/63), can be a constant expression
K6; Constant (0-63), can be a constant expression
K8: Constant (0-255), can be a constant expression
k: Constant, value range depending on instruction. Can be a constant expression
q: Constant (0-63), can be a constant expression
Rdl:  R24, R26, R28, R30. For ADIW and SBIW instructions
X,Y,Z: Indirect address registers (X=R27:R26, Y=R29:R28, Z=R31:R30)

其中出现Z,C,N,V,H,S标志,是指CPU执行结果标志寄存器,包括结果状态、高位状态、负数状态、溢出状态、半字节操作标志和信号状态。

三、            汇编宏指令

指令集是我们选择一个型号的CPU,对应提供一套指令集,而汇编宏指令是由编译工具提供的,可以更好地帮助我们编写程序、描述程序和限制程序等描述操作。我们可以这样认为,我们编写程序,是一个完成具体事务的过程,但是我们编写的程序是一个产品,需要进行包装,需要提供合格证、说明书等东西,才是一个可以出厂的产品,如果没有包装,没有合格证和说明书等,仍然还不是可以出厂的产品,或者不是一个合格的产品。使用汇编宏指令,是对汇编语言产品的包装。现代汇编程序产品设计中,如果不是用宏指令,几乎是一个不可想象的事,甚至如果不是用宏命令就完成部了程序的设计。

使用宏指令是解决程序设计过程结构问题、数据分配问题是一个最好的办法。我们可以看出一个编写优秀的汇编程序,其宏指令运用合理,非常普遍,描述注释部分,描述程序起始位置、描述数据类型、描述数据分配等,都是用到宏命令。最重要的是两个方面:使用宏命令更好地解决描述层次色彩和更好地解决程序的可维护性、可更改性、可交流性问题。

宏指令在编译后的程序是不会出现宏指令的影子的,所以也有把宏命令成为伪操作命令,这是又一定的道理的。下面给出AT90PWM CPU的AVR工具提供宏命令:

Assembler directives

The Assembler supports a number of directives. The directives are not translated directly into opcodes. Instead, they are used to adjust the location of the program in memory, define macros, initialize memory and so on. An overview of the directives is given in the following table.

Directive

Description

BYTE

Reserve byte to a variable

CSEG

Code Segment

CSEGSIZE

Program memory size

DB

Define constant byte(s)

DEF

Define a symbolic name on a register

DEVICE

Define which device to assemble for

DSEG

Data Segment

DW

Define Constant word(s)

ENDM, ENDMACRO

EndMacro

EQU

Set a symbol equal to an expression

ESEG

EEPROM Segment

EXIT

Exit from file

INCLUDE

Read source from another file

LIST

Turn listfile generation on

LISTMAC

Turn Macro expansion in list file on

MACRO

Begin Macro

NOLIST

Turn listfile generation off

ORG

Set program origin

SET

Set a symbol to an expression

 

These directives were introduced in AVRASM v1.74:

 

New Directives

Description

ELSE,ELIF

Conditional assembly

ENDIF

Conditional assembly

ERROR

Outputs an error message

IF,IFDEF,IFNDEF

Conditional assembly

MESSAGE

Outputs a message string

 

The following directives are only available with AVRASM2:

 

AVRASM2  Directives

Description

DD

Define Doubleword

DQ

Define Quadword

UNDEF

Undefine register symbol

WARNING

Outputs a warning message

OVERLAP/NOOVERLAP

Set up overlapping section (requires AVRASM2.1 or better)

Note that all directives must be preceded by a period.

看完宏指令后,如果你能够产生这样一个想法说明你已经明白了宏指令的作用了,宏指令不仅仅是程序包装的问题,还是解决汇编设计人员与汇编工具之间的交流问题,编译控制问题,把汇编人员编写程序的处理意见和处理方法告诉给编译工具,编译工具按照汇编设计人员的意图进行编译,生成目标代码。

四、            简单汇编语言程序设计

我一直担心我用比较少量的文字编写情况下无法把汇编语言设计入门的引子写好,也不知道你们看懂了吗。总之,CPU指令是计算机语言,是计算机执行的命令,宏指令是提供程序设计的一种工具,为了更好地将汇编程序描述清楚。不管怎样我还要继续写下去,也可能通过一个具体的实例描述,胜过前面文字的描述。下面是一个具体的程序代码:

.def   temp   =r19

.def   temp2 =r20

delay:    ldi temp,$FF

d1:       dec temp

       brne d1

       ret

 

long_delay:   ser temp2

ld1:      rcall delay

       dec temp2

       brne ld1

       ret

这段程序是我们写每一个汇编程序设计人员都会编写的代码,主要是通过CPU处理指令占用时间进行延时。这种延时程序与睡眠延时有很大的区别,睡眠延时虽然也是延时,但是不会占用CPU处理事务的时间,是通过Timer/counter控制器处理完成的,完成后后通知CPU,CPU接着继续往下处理,而我们看到的这段程序,是依赖于占用促CPU时间达到实际意义上的延时,实际运行的结果并不重要。

通过这段程序我们可以看到第一行、第二行通过宏指令定义一个替代R18和R19寄存器描述;第三行命令定义指令执行次数为0xFF(256)次;第四行进行递减操作;第五行命令执行递减结果状态操作,如果temp不为零继续返回到第四行操作,直到temp结果为零,操作结束RET。下面程序类似,仅仅是用rcall delay命令调用前面的延时程序,目的是达到更上时间的延时,而使用的代码量最少。

五、            汇编语言设计技术

我们从这一章节开始逐步进入我真正想写的一些内容了,希望大家认真阅读,并发表感慨。

汇编语言设计程序,首先必须掌握CPU性能、CPU指令集和宏指令系统,这是一个最基本的条件,如果这一点我们无法做到,根本无法编写出来一个具体应用程序。当然,如果你已经了解并掌握许多种汇编语言的设计,你已经掌握汇编语言编写程序的基本规律和方法,你可以不必花费过多的时间去认识指令和宏指令,就可以编写程序了,这种情况我们必须分别去看。总之,只有在熟悉汇编语言指令和汇编宏指令的情况才能够编写程序。

但是,对于软件产品的设计,仅仅掌握汇编指令和汇编宏指令是不够的,我们还需要掌握工厂设计程序的基本工艺技术,掌握国家提供的程序设计规范、标准、掌握国际上的一些汇编语言设计技术,整体结合起来,才是汇编语言设计技术。而仅仅会用汇编编写一两个程序,那时做作业、做实验,根本谈不上掌握汇编设计技术。

比如,软件产品第一解决实现功能问题,如果功能是达到了,性能不稳定的现象存在,你能够说产品设计过关了、合格了吗?又比如,对于任何一个软件产品的设计,在实现功能设计的基础上,必须要讨论的可维护性和可更改性问题,以保障产品的市场生命力,我们实际编写程序过程中思考过没有,提出或应用了实质性的解决方法没有。等等问题,我通过朋友的交谈和网上阅读,发现我国现状应该根本没有向前发展,甚至根本没有一个规范的指导和促进。那么,我还是具体的一一个来描述。

5.1、程序设计指标

程序设计指标包括功能性指标、非功能性指标、特殊要求及安全要求和未来性保证四个方面。功能性指标包括功能、扩展性、维护维修性、逻辑性等具体指标;非功能性指标包括设计书类、设计过程控制的要求;特殊性及安全设计要求主要是商业行为控制设计指标;未来性设计主要程序结构以及程序实现的方法保证的一些设计指标。

5.2、程序设计生命周期

国家、国际上早已有描述的标准,我没有必要作解释了。

5.3、存储资源分配设计

单片机设计其最重要的一项工作就是存储资源分配、规划设计,而且在硬件设计和软件设计工作正式开展前就必须完成的工作,所以我把这项内容专门提出来。主要是通过经验判断,能够非常准确地确定数据信息量和程序占用空间,具体给出实际存储空间大小。

5.4、设计项目目录文件管理分配

我们知道,只要启动一个设计任务,我们总会在自己的计算机上定义一个目录,准备将设计工作放到这个目录下完成。这一点应当是所有设计人员共识的。但是,仅仅具备这一点是不够的,我们需要从几个方面考虑定义目录及文件。第一,工程管理角度定义需求或资料管理目录,程序设计目录、程序设计过程控制目录,以及培训计划目录;第二,应用涉及的DEF、CPU、I/O设备、Timer、Memory、以及相关的功能硬件设备等建立INC文件的结构;第三,主题程序或流程描述程序和功能程序描述文件划分。我们基本上需要按照提出的三个方面建立具体应用的管理环境。保证设计、交流、档案管理的问题。

5.5、设计流程的分析

流程分析主要是根据需求或应用对象,建立一个符合实际需求的方法,也是我们程序设计的主体描述。应用的不同实际流程不同,我们必须从实际出发建立合理的流程模式,不能套模式、简单TOP TO Bottom式设计。

一般,思考的方法有按时间处理的时序描述模式,按事件处理的事件服务模式,按操作规则描述的逻辑模式,两种或两种以上的模式混合使用的混合模式,我们根据应用准确建立具体的应用模型。

5.6、设计容错技术

如果我们设计产品仅仅完成功能设计这是不够的,我们必须在设计功能的同时设计产品的容错问题,必须清楚地认识到产品的质量控制点和质量保证点。我们尽量不要在设计完成后再来对出现的问题检查、纠正,这样往往会出现越修越错现象,生产周期无法保证。

文件结构和程序结构设计是设计容错技术的一个最基本的内容。我们如果选用优秀的方法建立文件结构和程序结构,保证了程序逻辑正确描述,同事保证了更改方便性。具体描述容错技术有方法保证、环境保证、程序处理保证三个方面。看门狗处理、陷阱技术、错误处理服务等,高级的容器处理服务都是我们讨论的程序容错处理技术。

5.7、三层结构设计

这一部分设计严格来说是容错技术的一个部分,但是我把它单独提出来说明,是我发现汇编设计的朋友们在实际设计过程中根本不注意这个方面的问题了,如果出现堆栈溢出就调整一下堆栈指针,而不是在设计过程中完全可以预见性的处理好这类问题。

如果我们按照三层结构设计程序,硬件支持层尽量不使用CALL调用(可能会出现使用延时过程),直接放映硬件设备功能。第二层功能模块描述,解决必要的功能描述和复用性功能描述,将会使用到CALL,以及数据堆栈保存和堆栈的存储器寄存器调换等。第一层应用流程描述,直观、尽量鲜明的描述整个应用的全貌。这样一来,我们可以分析出来,一层、二层调用的最大使用堆栈空间量和中断调用与正常运行交叉占用量,得到实际堆栈使用空间大小的判断量。

5.8、捷克松模块分析法

这一部分实际上也是容错技术的一个部分,我同样单独提出来说明是因为我们从事汇编语言设计的人员根本没有掌握这个设计方法。捷克松设计方法主要是从程序设计模块划分确定功能保障性、模块的可复用性、数据交换的保障性等问题。

在国际上汇编语言设计模块分析方法普遍采用模块划分和数据流划分两种方法,最为流行的是模块分析法,捷克松模块分析法就是其中最为代表的一种方法,他是根据三层结构原理,从应用层到应用功能模块层,到硬件支持层之间,我们根据应用的功能与设备的组织进行唯一功能描述和复用性功能描述,最后,得到最少功能模块数,最佳模块定义,实现整个应用,并可以验证应用设计和理性、正确性和可以实施性等问题。

5.9、程序编程规范设计

这个部分也是容错处理技术一个部分。说到这里,我们已经不难看出,程序设计技术讨论来讨论去的问题都是怎样保证设计的正确性问题,反过来对流程建立的问题讨论比较少,这说明,对于软件生产来说,解决了功能(简单认识的功能)问题是非常容易的,生产的保证与商业利益的保证必需建立完整的有效手段,不然根本无法保证长期运作。

(程序编程规范省略了)。

5.10、程序检测规范的设计

(省略了)。

5.11、程序未来性设计

功能性设计的扩展性设计、抽象化设计、模块化设计、维护维修性设计;非功能性文档描述等等。

六、            产品设计起步

我们设计产品,起步阶段首先的工作就是收集资源,这是一个普遍的认识。只有通过收集资源才能够明确的认识到实现的可能性。如果无法收集到资源,也就无法实现了,这一点认识具有普片性和正确性。但是,我们讨论这个问题必须从定量化、指标化角度考虑,也就是从长期生产实践中建立完整的风险意识控制办法,不能简单认识这个问题。McCall方法就是教我们怎样去分析问题。

6.1、提供项目可实施性依据

我们常说的可行性报告,从环境条件、设计技术、资源保证,以及开发资金保证等要素分析。

6.2、整理项目设计资源

设计技术能力和设计可提供的资源,需求分析等建立可以保证在一定周期时间内完成的必要条件。如果不能够在一定周期时间内完成,对于企业来说,投入这个项目是一个受损失的或失败的。

6.3、保证的资金解决办法

具备设计方面的可能,我们还必须考虑资金保证的问题,这点我们应该都有体会了。

七、            程序流程分析

我在汇编语言设计技术这一章节里,已经描述了程序流程分析,主要是解决如何使用计算机处理能力实现具体化应用功能,按时间、事件、逻辑建立一个有效的处理方法。

在这里强调两个方面的问题,第一是我们必须使用有效的工具进行描述,第二是我们必须进行准确定量化描述。使用有效工具进行描述,如时间周期控制,需要描述流程的过程以外,需要我们建立准确的、完整的时间变化与控制和控制反馈量变化描述的时序图,是用图表描述参数值等。使用事件控制,需要我们描述流程的过程以外,需要我们准确定义事件名称、作用、目的,以及状态变化,利用状态图、图表工具,定量的描述出来。是用逻辑控制的同样也是需要使用逻辑图准确描述逻辑变化。只有获得了准确、完整的数据信息,才使我们设计工作有了保证,才使我们的设计工作正确实现。

我们有一些汇编设计的朋友,他们是没有丰富的工作经验还是没有进行过系统化的训练,在描述流程分析时,没有明确的时间、事件和逻辑控制划分,在描述上把所有的工具都是用上了,让他人阅读后无法准确定位控制方法,甚至造成设计错误。我们在描述中和使用描述工具描述中,我们必须准确定位控制方法,不要出判断题。

总之使用单片机设计,尤其是使用汇编技术设计,流程一般不会过于复杂的。如果使用单片机汇编技术设计一个复杂的处理过程,那是一个非常不明智的选择。通常设计复杂处理过程会使用DSP开发技术或使用嵌入开发技术。所以,我们分析流程,基本不会遇到过于复杂的,只要我们思维上基于用时间、事件和逻辑这三个思想去分析,答案应当是非常明确的,这一点是肯定的。我们只需要注意数据量的完整性和正确性问题,同时选择有效的工具正确描述。

八、            程序结构分析

汇编语言设计产品最大缺点就是不便于交流,因此我们必须通过设计结构和设计方法上解决这个问题,尽量实现可交流性问题。这也是我编写文章所描述的主要思想。那么,为了实现这个目的,我们必须从设计结构和设计方法上保证。8.1、程序结构

前面,我已经提到了使用捷克松模块分析法,进行三层结构设计,并且也描述道一、二、三层的划分方法,只要我们按照这种方法设计,我们阅读程序就能够非常清楚地看到流程控制方法和流程实现过程,非常清楚看出主体函数(流程描述)和功能服务模块。

比如,我下面给出一个主题流程描述实例:

.DEVICE AT90PWM2 ; Use the AT90PWM2

.CSEG

.INCLUDE "at90pwm2def.inc"

.INCLUDE "pjt200w.inc"

.INCLUDE "pjt200w_lamp.inc"

 

.INCLUDE "cpu.inc"

.INCLUDE "register.inc"

.INCLUDE "interrupt.inc"

.INCLUDE "watchdog.inc"

.INCLUDE "timer.inc"

.INCLUDE "io.inc"

.INCLUDE "adc.inc"

.INCLUDE "psc.inc"

 

.ORG 0x0                        ; Program start address.

    CreateInterruptServer          ; Create interrupt vocter server.

 

STARTUP:                        ; RESET interrupt into program startup.

    InitializeStack                 ; Initialize Stack.

    InitializeRegister              ; Initialize system register

    InitializeIOPort            ; Initialize I/O port.

 

    SetBit LED_PORT-0x20, LED0      ; LED0 turn on.

    SetBit LED_PORT-0x20, LED1      ; LED1 turn on.

    SetBit LED_PORT-0x20, LED2      ; LED2 turn on.

 

    InitializeADC               ; Initialize ADC.

    ADC_ChannelSelect ADC3          ; Select ADC3.

 

    Delay_80usec                ;delay 1s (80) to wait power stable

 

    ClearBit LED_PORT-0x20, LED0   ; LED0 turn off.

    ClearBit LED_PORT-0x20, LED1   ; LED1 turn off.

    ClearBit LED_PORT-0x20, LED2   ; LED2 turn off.

 

    WatchdogTimer_on                ; Turn On watchdog timer.

 

    EnableInterrupt                 ; Enable Interrupt.

 

    InitializePSC0 159              ; Initialize PCS0, data 159 = 100KHz.

mainLoop:

    Call mainController

    rjmp mainLoop

; main process end, or reser interrupt end.

 

PSC2_CAPT:                      ; PSC2 Capture event Handler

    RETI                        ;

 

PSC2_EC:                        ; PSC2 End Cycle Handler

    RETI                        ;

 

PSC1_CAPT:                      ; PSC1 Capture event Handler

    RETI                        ;

 

PSC1_EC:                        ; PSC1 End Cycle Handler

    RETI                        ;

 

PSC0_CAPT:                      ; PSC0 Capture event Handler

    RETI                        ;

 

PSC0_EC:                        ; PSC0 End Cycle Handler

    RETI                        ;

 

ANA_COMP_0:                        ; Analog Comparator 0 Handler

    RETI                        ;

 

ANA_COMP_1:                        ; Analog Comparator 1 Handler

    RETI                        ;

 

ANA_COMP_2:                        ; Analog Comparator 2 Handler

    RETI                        ;

 

EXT_INT0:                       ; IRQ0 Handler

    RETI                        ;

 

TIM1_CAPT:                      ; Timer1 Capture Handler

    RETI                        ;

 

TIM1_COMPA:                        ; Timer1 Compare A Handler

    RETI                        ;

 

TIM1_COMPB:                        ; Timer1 Compare B Handler

    RETI                        ;

 

TIM1_OVF:                       ; Timer1 Overflow Handler

    RETI                        ;

 

TIM0_COMPA:                        ; Timer0 Compare A Handler

    RETI                        ;

 

TIM0_OVF:                       ; Timer0 Overflow Handler

    RETI                        ;

 

ADC_PROC:                       ; ADC Conversion Complete Handler

    RETI                        ;

 

EXT_INT1:                       ; IRQ1 Handler

    RETI                        ;

 

SPI_STC:                        ; SPI Transfer Complete Handler

    RETI                        ;

 

USART_RXC:                      ; USART, RX Complete Handler

    RETI                        ;

 

USART_UDRE:                        ; USART, UDR Empty Handler

    RETI                        ;

 

USART_TXC:                      ; USART, TX Complete Handler

    RETI                        ;

 

EXT_INT2:                       ; IRQ2 Handler

    RETI                        ;

 

WDT:                            ; Watchdog Timer Handler

;;;;;;;;; jump running process.

    RETI                        ;

 

EE_RDY:                            ; EEPROM Ready Handler

    RETI                        ;

 

TIM0_COMPB:                        ; Timer0 Compare B Handler

    RETI                        ;

 

EXT_INT3:                       ; IRQ3 Handler

    RETI                        ;

 

SPM_RDY:                        ; Store Program Memory Ready Handler

    RETI                        ;

我们编写过汇编语言的人,阅读这个程序,我认为已经达到了交流的效果了。

8.2、 程序管理结构

使用国际化的结构设计仍然不能够完全说明清楚我需要表达的问题,不能够完全看出设计方法的优越性,也不能够看出三层结构设计的效果,那好,我们在这一节里通过程序管理结构进行深入地描述。

我们首先把程序分为四类文件管理,第一类基本定义文件,第二类是设备管理文件,第三类是主体文件,第四类是功能模块文件,如图描述。

应用文件管理

Def.inc

Cpu.inc

Memory.inc

Interrupt.inc

Io.inc

Adc.inc

Keyboard.inc

Lcd.inc

Main.asm

Function.asm

我们可以看出,通过def.inc文件建立CPU控制的一般定义内容,将CPU、端口以及设备端口地址、堆栈地址等进行参量描述。同时,我们也可以再作一个应用的一般定义文件。其他的.inc文件主要是通过MACRO和ENDMACRO宏指令描述应用中需要的设备控制函数,将每一个设备抽象化描述,并提供完整的封装接口,提供给应用服务。对于接口定义放在下面一节中描述。这样,我们建立起来了第三层的硬件支持层的服务。往往我们编写一个应用是首先需要进行设备程序的调试和整理,建立起来设备程序再组织实现功能,再实现整个应用。熟悉设备编程是实现整个程序的关键。如果我们按照一定规范,进行标准化封装,建立资源服务,这对于我们设计来说,解决了环境一致性和设计复用性。这种设计方法提供了我们长期设计资源服务问题,大大提高工作效率。

最后,.asm文件,main的主题流程描述,如上面给出的一个程序实例。Function文件可以是一个或多个组成,主要是提供主体的函数。当然,.inc文件也可以提供主体服务。而function文件,除提供主体的功能服务外,它需要组织.inc文件实现新的需求组合功能,实现数据保护和交换衔接问题等等。.inc文件和function.asm文件是由区别的。如:.inc文件在不调用情况下是不会编译到应用中去的,而function.asm文件不管调用或不调用是会编译到应用中去的。又,.inc是进行设备的抽象描述,function.asm是应用的具体描述。又,.inc文件是可复用性设计的、.function.asm是针对应用设计的。等等。你们会有更多的体会。

程序结构与程序管理结构图。

;;; main.asm

Org 0x00

Startup:

    io_init ;initialize i/o port

 

    call port_GetBit

    …

;;; function.asm

 

port_GetBit:

    io_GetBit POARTA5

    BRCS loop1

    …

;;;; io.inc

 

.MACRO io_init

.ENDMACRO

 

.MACRO io_GetBit

 …

.ENDMACRO

Main.asm

function.asm

io.inc

描述道这里,我应当把三层结构设计的问题交待清楚了,同时也基本上把可读性设计和扩展性、可维修维护性设计交待清楚了,接下来描述程序接口定义。

8.3、程序接口定义

程序接口定义不是指如何命名法,而是需要制定一套程序管理的接口。第一是.inc封装接口。比如:我们定义io.inc程序接口包括有:io_out、io_in、io_SetBit、io_ClearBit、io_GetBit等。又如:lcd.inc程序接口包括有:lcd_init、lcd_open、lcd_close、lcd_pie、lcd_line、lcd_rect、lcd_outText等等。程序接口使用对偶性和聚集性将整个设备功能完全抽象出来,封装成一个设备的管理程序,提供给main.asm或function.asm程序使用。

第二是function.asm封装接口。提供应用需求的接口或功能模块服务,避免不必要的占用存储资源;解决数据丢失和数据交换的衔接问题,避免.inc程序在应用过程中的寄存器冲突等问题。同时解决好复用出现的不匹配问题。

九、            程序设计

当今,信息资源极大丰富的时代,给设计产品带来了提供非常便易,我们可以从网上获得很多的信息,可以依赖于网络实现程序的设计。但是,仅仅满足这一点是不够的,我们需要全面的、全方位的为提供设计产品的服务,解决设计产品效率问题和质量问题。那么,在前面我们已经把设计质量控制方法和设计方法阐述了,下面我们来具体地讨论程序设计中的问题。

9.1、程序设计的步骤

1.定义def.inc文件;

2.编写所有设备程序,并调试通过;

3.编写功能模块;

4.编写流程描述程序;

5.建立测试、检测工具进行检查。

9.2、定义def.inc程序

定义def.inc文件时,尽量完整定义,把所有相关数据定义出来,考虑到未来性设计。比如:

#ifndef _PWM2DEF_INC_

#define _PWM2DEF_INC_

 

 

#pragma partinc 0

 

; ***** SPECIFY DEVICE ***************************************************

.device AT90PWM2

#pragma AVRPART ADMIN PART_NAME AT90PWM2

.equ    SIGNATURE_000   = 0x1e

.equ    SIGNATURE_001   = 0x93

.equ    SIGNATURE_002   = 0x81

 

#pragma AVRPART CORE CORE_VERSION V2E

 

 

; ***** I/O REGISTER DEFINITIONS *****************************************

; NOTE:

; Definitions marked "MEMORY MAPPED"are extended I/O ports

; and cannot be used with IN/OUT instructions

.equ    PICR2H = 0xff ; MEMORY MAPPED

.equ    PICR2L = 0xfe ; MEMORY MAPPED

.equ    PFRC2B = 0xfd ; MEMORY MAPPED

.equ    PFRC2A = 0xfc ; MEMORY MAPPED

.equ    PCTL2   = 0xfb ; MEMORY MAPPED

.equ    PCNF2   = 0xfa ; MEMORY MAPPED

.equ    OCR2RBH = 0xf9 ; MEMORY MAPPED

.equ    OCR2RBL = 0xf8 ; MEMORY MAPPED

.equ    OCR2SBH = 0xf7 ; MEMORY MAPPED

.equ    OCR2SBL = 0xf6 ; MEMORY MAPPED

.equ    OCR2RAH = 0xf5 ; MEMORY MAPPED

.equ    OCR2RAL = 0xf4 ; MEMORY MAPPED

.equ    OCR2SAH = 0xf3 ; MEMORY MAPPED

.equ    OCR2SAL = 0xf2 ; MEMORY MAPPED

.equ    POM2    = 0xf1 ; MEMORY MAPPED

.equ    PSOC2   = 0xf0 ; MEMORY MAPPED

.equ    PICR1H = 0xef ; MEMORY MAPPED

.equ    PICR1L = 0xee ; MEMORY MAPPED

.equ    PFRC1B = 0xed ; MEMORY MAPPED

.equ    PFRC1A = 0xec ; MEMORY MAPPED

.equ    PCTL1   = 0xeb ; MEMORY MAPPED

.equ    PCNF1   = 0xea ; MEMORY MAPPED

.equ    OCR1RBH = 0xe9 ; MEMORY MAPPED

.equ    OCR1RBL = 0xe8 ; MEMORY MAPPED

.equ    OCR1SBH = 0xe7 ; MEMORY MAPPED

.equ    OCR1SBL = 0xe6 ; MEMORY MAPPED

.equ    OCR1RAH = 0xe5 ; MEMORY MAPPED

.equ    OCR1RAL = 0xe4 ; MEMORY MAPPED

.equ    OCR1SAH = 0xe3 ; MEMORY MAPPED

.equ    OCR1SAL = 0xe2 ; MEMORY MAPPED

.equ    PSOC1   = 0xe0 ; MEMORY MAPPED

.equ    PICR0H = 0xdf ; MEMORY MAPPED

.equ    PICR0L = 0xde ; MEMORY MAPPED

.equ    PFRC0B = 0xdd ; MEMORY MAPPED

.equ    PFRC0A = 0xdc ; MEMORY MAPPED

.equ    PCTL0   = 0xdb ; MEMORY MAPPED

.equ    PCNF0   = 0xda ; MEMORY MAPPED

.equ    OCR0RBH = 0xd9 ; MEMORY MAPPED

.equ    OCR0RBL = 0xd8 ; MEMORY MAPPED

.equ    OCR0SBH = 0xd7 ; MEMORY MAPPED

.equ    OCR0SBL = 0xd6 ; MEMORY MAPPED

.equ    OCR0RAH = 0xd5 ; MEMORY MAPPED

.equ    OCR0RAL = 0xd4 ; MEMORY MAPPED

.equ    OCR0SAH = 0xd3 ; MEMORY MAPPED

.equ    OCR0SAL = 0xd2 ; MEMORY MAPPED

.equ    PSOC0   = 0xd0 ; MEMORY MAPPED

.equ    EUDR    = 0xce ; MEMORY MAPPED

.equ    MUBRRL = 0xcc ; MEMORY MAPPED

.equ    MUBRRH = 0xcd ; MEMORY MAPPED

.equ    EUCSRC = 0xca ; MEMORY MAPPED

.equ    EUCSRB = 0xc9 ; MEMORY MAPPED

.equ    EUCSRA = 0xc8 ; MEMORY MAPPED

.equ    UDR = 0xc6 ; MEMORY MAPPED

.equ    UBRRH   = 0xc5 ; MEMORY MAPPED

.equ    UBRRL   = 0xc4 ; MEMORY MAPPED

.equ    UCSRC   = 0xc2 ; MEMORY MAPPED

.equ    UCSRB   = 0xc1 ; MEMORY MAPPED

.equ    UCSRA   = 0xc0 ; MEMORY MAPPED

.equ    AC2CON = 0xaf ; MEMORY MAPPED

.equ    AC1CON = 0xae ; MEMORY MAPPED

.equ    AC0CON = 0xad ; MEMORY MAPPED

.equ    DACH    = 0xac ; MEMORY MAPPED

.equ    DACL    = 0xab ; MEMORY MAPPED

.equ    DACON   = 0xaa ; MEMORY MAPPED

.equ    PIM2    = 0xa5 ; MEMORY MAPPED

.equ    PIFR2   = 0xa4 ; MEMORY MAPPED

.equ    PIM1    = 0xa3 ; MEMORY MAPPED

.equ    PIFR1   = 0xa2 ; MEMORY MAPPED

.equ    PIM0    = 0xa1 ; MEMORY MAPPED

.equ    PIFR0   = 0xa0 ; MEMORY MAPPED

.equ    OCR1BL = 0x8a ; MEMORY MAPPED

.equ    OCR1BH = 0x8b ; MEMORY MAPPED

.equ    OCR1AL = 0x88 ; MEMORY MAPPED

.equ    OCR1AH = 0x89 ; MEMORY MAPPED

.equ    ICR1L   = 0x86 ; MEMORY MAPPED

.equ    ICR1H   = 0x87 ; MEMORY MAPPED

.equ    TCNT1L = 0x84 ; MEMORY MAPPED

.equ    TCNT1H = 0x85 ; MEMORY MAPPED

.equ    TCCR1C = 0x82 ; MEMORY MAPPED

.equ    TCCR1B = 0x81 ; MEMORY MAPPED

.equ    TCCR1A = 0x80 ; MEMORY MAPPED

.equ    DIDR1   = 0x7f ; MEMORY MAPPED

.equ    DIDR0   = 0x7e ; MEMORY MAPPED

.equ    ADMUX   = 0x7c ; MEMORY MAPPED

.equ    ADCSRB = 0x7b ; MEMORY MAPPED

.equ    ADCSRA = 0x7a ; MEMORY MAPPED

.equ    ADCH    = 0x79 ; MEMORY MAPPED

.equ    ADCL    = 0x78 ; MEMORY MAPPED

.equ    AMP1CSR = 0x77 ; MEMORY MAPPED

.equ    AMP0CSR = 0x76 ; MEMORY MAPPED

.equ    TIMSK1 = 0x6f ; MEMORY MAPPED

.equ    TIMSK0 = 0x6e ; MEMORY MAPPED

.equ    EICRA   = 0x69 ; MEMORY MAPPED

.equ    OSCCAL = 0x66 ; MEMORY MAPPED

.equ    PRR = 0x64 ; MEMORY MAPPED

.equ    CLKPR   = 0x61 ; MEMORY MAPPED

.equ    WDTCSR = 0x60 ; MEMORY MAPPED

.equ    SREG    = 0x3f

.equ    SPL = 0x3d

.equ    SPH = 0x3e

.equ    SPMCSR = 0x37

.equ    MCUCR   = 0x35

.equ    MCUSR   = 0x34

.equ    SMCR    = 0x33

.equ    ACSR    = 0x30

.equ    SPDR    = 0x2e

.equ    SPSR    = 0x2d

.equ    SPCR    = 0x2c

.equ    PLLCSR = 0x29

.equ    OCR0B   = 0x28

.equ    OCR0A   = 0x27

.equ    TCNT0   = 0x26

.equ    TCCR0B = 0x25

.equ    TCCR0A = 0x24

.equ    GTCCR   = 0x23

.equ    EEARL   = 0x21

.equ    EEARH   = 0x22

.equ    EEDR    = 0x20

.equ    EECR    = 0x1f

.equ    GPIOR0 = 0x1e

.equ    EIMSK   = 0x1d

.equ    EIFR    = 0x1c

.equ    GPIOR3 = 0x1b

.equ    GPIOR2 = 0x1a

.equ    GPIOR1 = 0x19

.equ    TIFR1   = 0x16

.equ    TIFR0   = 0x15

.equ    PORTE   = 0x0e

.equ    DDRE    = 0x0d

.equ    PINE    = 0x0c

.equ    PORTD   = 0x0b

.equ    DDRD    = 0x0a

.equ    PIND    = 0x09

.equ    PORTC   = 0x08

.equ    DDRC    = 0x07

.equ    PINC    = 0x06

.equ    PORTB   = 0x05

.equ    DDRB    = 0x04

.equ    PINB    = 0x03

 

 。。。

 

9.3、编写设备程序

设备程序编写可以分为利用资源编程和自己设计设备程序两种方式编写出来。当然,利用资源编写比较容易,自己设计设备程序比较费劲,一个目的就是要实现了。

1、定义设备程序接口。真正分析每一个设备的功能,并将所有功能提取出来,定义成为函数。在建立接口过程中同时需要聚集性和对偶性思想定义接口。尽量完整描述。

2、唯一性设计。设备程序设计主要是为应用建立硬件的接口服务,将硬件功能完整的反映出来,因此,编写时,不要思考应用方面的问题,尽量不要考虑功能性组合(当然不是绝对的,在实际情况中可能有组合的功能描述),而是建立唯一性的功能服务。

3、代码简单,效率高。针对功能性设计,不要考虑过于复杂的问题,而忽略了代码质量,编写出低效率的服务,这是不应该的。直接、明了,功能性强,使我们最求的目标。

下面给出一个实际编写的程序。

;*****************************************************************************

;*

;* FILENAME : io.inc

;*

;* NUMBER    : QB200W001

;*

;* TITLE     : QB-1241P 200W Assembler program

;*

;* VERSION   : 1.00.00

;*

;* DATE      : 20006-12-6

;*

;* UPDTAE    : none

;*

;* AUTHOR    : S.Yin

;*

;* COPYRIGHT : Shenzhen Newenjoy Technology Co.,LTD.

;*

;* DESCRIPTION

;*

;* Assembler application program for AT90PWM2.

;*

;*****************************************************************************

;

#ifndef _IO_INC_

#define _IO_INC_

 

.MACRO InitializeIOPort         ;initialize I/O port

    ldi REG_A, 1<<DRV_X | 1<<DRV_Y

    sts DDRB, REG_A

    ldi REG_A, 0

    sts PORTB, REG_A

 

    ldi REG_A, 1<<DRV_BUCK | 1<<LED0 | 1<<LED1 | 1<<LED2

    sts DDRD, REG_A

    ldi REG_A, 0

    sts PORTD, REG_A

.ENDMACRO

 

;*

;* I/O port Read and Write operation.

;*

;* Write byte to Port.

;*

.MACRO OutputPort

    out @0, @1

.ENDMACRO

 

;*

;* Read byte from port.

;*

.MACRO InputPort

    in @0, @1

.ENDMACRO

 

;*

;* Pin set high for port.

;*

.MACRO SetBit

    sbi @0, @1

.ENDMACRO

 

;*

;* Pin Set lower for port.

;*

.MACRO ClearBit

    cbi @0, @1

.ENDMACRO

 

;*

;* Get bit from port

;*

.MACRO GetBit

 in r17, @0

 ldi r18, @1

io_loop1:

 ROL r17

 dec r18

    brne io_loop1

.ENDMACRO

#endif /* _IO_INC_ */

9.4、编写功能模块

功能模块主要是针对应用服务建立的,直接描述应用需求服务,因此从流程程序来看功能模块是应用的具体描述,从设备程序来看功能模块是设备程序的组合,从功能模块角度来看是应用的聚集。不管怎样,我们认为功能模块是非常重要的组成部分,保证了流程的正常运行。在这里,我们必须说明一点,在很多应用中都是用了一些科学算法,我们不要把这些科学算法认为是功能模块的一个部分。理想设计应当把科学算法设计成为设备程序,提供一个可以复用的函数,同时也保证了程序的清晰度。

1、定义接口

2、解决数据交换问题

3、解决寄存器冲突问题

4、解决数据流分配问题

下面给出一个程序实例。

;*****************************************************************************

;*

;*    Freq_Switch

;*

;*****************************************************************************

Freq_Switch:

; change buck frequency from 100Khz to 62.5Khz

    lds REG_A, OCR0RAL ;100Khz/50Khz = 2, we need the new duty time is 2 times old one

    cpi REG_A, 255

    brlo freq_duty_half

    ldi REG_B, DUTY_50K_MAX

    rjmp freq_duty_adj

 

freq_duty_half:

    mov REG_B, REG_A

    lsl REG_A           ;multiply 2

 

freq_duty_adj:

    ldi REG_A, 0    ;start from 0

    sts OCR0SAH, REG_A

    ldi REG_A, 0

    sts OCR0SAL, REG_A

 

    ldi REG_A, 0

    sts OCR0RAH, REG_A

    sts OCR0RAL, REG_B

 

    ldi REG_A, 0

    sts OCR0SBH, REG_A

    ldi REG_A, 31   ;duty cycle = 10%

    sts OCR0SBL, REG_A

 

    ldi REG_A, 1

    sts OCR0RBH, REG_A

    ldi REG_A, 0x3f     ;cycle is 16Mhz/320 = 50Khz

    sts OCR0RBL, REG_A

 

;4/ return

freq_switch_end:

    ret

9.5、编写流程程序

往往一个应用可以分解成为几个流程的组合,比如除主流程以外,还有中断服务,还有时间控制服务等,我们定义都认为是流程的一个部分。这是因为这些程序的组合才能够完全表达实际应用的流程。因此我们在描述时,应当尽量体现出来,并且尽量采用可读性的方法编写。

下面给出一个实例。

;*****************************************************************************

;*

;* FILENAME : AT90PWM2-200W-code.asm

;*

;* NUMBER    : QB200W001

;*

;* TITLE     : QB-1241P 200W Assembler program

;*

;* VERSION   : 1.00.00

;*

;* DATE      : 20006-12-6

;*

;* UPDTAE    : none

;*

;* AUTHOR    : S.Yin

;*

;* COPYRIGHT : Shenzhen Newenjoy Technology Co.,LTD.

;*

;* DESCRIPTION

;*

;* Assembler application program for AT90PWM2.

;*

;*****************************************************************************

;.DEVICE AT90PWM2 ; Use the AT90PWM2

 

.CSEG

.INCLUDE "at90pwm2def.inc"

.INCLUDE "pjt200w.inc"

.INCLUDE "pjt200w_lamp.inc"

 

.INCLUDE "cpu.inc"

.INCLUDE "register.inc"

.INCLUDE "interrupt.inc"

.INCLUDE "watchdog.inc"

.INCLUDE "timer.inc"

.INCLUDE "io.inc"

.INCLUDE "adc.inc"

.INCLUDE "psc.inc"

 

.ORG 0x0                            ; Program start address.

    CreateInterruptServer           ; Create interrupt vocter server.

 

STARTUP:                            ; RESET interrupt into program startup.

    InitializeStack                 ; Initialize Stack.

    InitializeRegister              ; Initialize system register

    InitializeIOPort                ; Initialize I/O port.

 

    SetBit LED_PORT-0x20, LED0      ; LED0 turn on.

    SetBit LED_PORT-0x20, LED1      ; LED1 turn on.

    SetBit LED_PORT-0x20, LED2      ; LED2 turn on.

 

    InitializeADC                   ; Initialize ADC.

    ADC_ChannelSelect ADC3          ; Select ADC3.

 

    Delay_80usec                    ;delay 1s (80) to wait power stable

 

    ClearBit LED_PORT-0x20, LED0    ; LED0 turn off.

    ClearBit LED_PORT-0x20, LED1    ; LED1 turn off.

    ClearBit LED_PORT-0x20, LED2    ; LED2 turn off.

 

    WatchdogTimer_on                ; Turn On watchdog timer.

 

    EnableInterrupt                 ; Enable Interrupt.

 

    InitializePSC0 159              ; Initialize PCS0, data 159 = 100KHz.

mainLoop:

    Call mainController

    rjmp mainLoop

; main process end, or reser interrupt end.

 

PSC2_CAPT:                          ; PSC2 Capture event Handler

    RETI                            ;

 

PSC2_EC:                            ; PSC2 End Cycle Handler

    RETI                            ;

 

PSC1_CAPT:                          ; PSC1 Capture event Handler

    RETI                            ;

 

PSC1_EC:                            ; PSC1 End Cycle Handler

    RETI                            ;

 

PSC0_CAPT:                          ; PSC0 Capture event Handler

    RETI                            ;

 

PSC0_EC:                            ; PSC0 End Cycle Handler

    RETI                            ;

 

ANA_COMP_0:                         ; Analog Comparator 0 Handler

    RETI                            ;

 

ANA_COMP_1:                         ; Analog Comparator 1 Handler

    RETI                            ;

 

ANA_COMP_2:                         ; Analog Comparator 2 Handler

    RETI                            ;

 

EXT_INT0:                           ; IRQ0 Handler

    RETI                            ;

 

TIM1_CAPT:                          ; Timer1 Capture Handler

    RETI                            ;

 

TIM1_COMPA:                         ; Timer1 Compare A Handler

    RETI                            ;

 

TIM1_COMPB:                         ; Timer1 Compare B Handler

    RETI                            ;

 

TIM1_OVF:                           ; Timer1 Overflow Handler

    RETI                            ;

 

TIM0_COMPA:                         ; Timer0 Compare A Handler

    RETI                            ;

 

TIM0_OVF:                           ; Timer0 Overflow Handler

    RETI                            ;

 

ADC_PROC:                           ; ADC Conversion Complete Handler

    RETI                            ;

 

EXT_INT1:                           ; IRQ1 Handler

    RETI                            ;

 

SPI_STC:                            ; SPI Transfer Complete Handler

    RETI                            ;

 

USART_RXC:                          ; USART, RX Complete Handler

    RETI                            ;

 

USART_UDRE:                         ; USART, UDR Empty Handler

    RETI                            ;

 

USART_TXC:                          ; USART, TX Complete Handler

    RETI                            ;

 

EXT_INT2:                           ; IRQ2 Handler

    RETI                            ;

 

WDT:                                ; Watchdog Timer Handler

;;;;;;;;; jump running process.

    RETI                            ;

 

EE_RDY:                             ; EEPROM Ready Handler

    RETI                            ;

 

TIM0_COMPB:                         ; Timer0 Compare B Handler

    RETI                            ;

 

EXT_INT3:                           ; IRQ3 Handler

    RETI                            ;

 

SPM_RDY:                            ; Store Program Memory Ready Handler

    RETI                            ;

十、            结束语

汇编程序设计技术,首先解决问题是功能性,围绕这个问题提出了质量保证办法,解决程序的交流与可读性问题,并且设计的扩展性维护性指标,实际效率问题,设计的可复用性等一些关键问题,都是我们设计每个应用必须讨论的。如果,我们总结一套优秀的方法,可以对设计工作带来巨大帮助。

转载于:https://www.cnblogs.com/yinshenk/archive/2009/05/31/1492830.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值