Chinaunix首页 | 论坛 | 博客
  • 博客访问: 490135
  • 博文数量: 121
  • 博客积分: 4001
  • 博客等级: 上校
  • 技术积分: 1390
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-13 21:48
文章分类

全部博文(121)

文章存档

2011年(4)

2010年(11)

2009年(106)

我的朋友

分类:

2009-03-15 20:56:17

51汇编程序基本框架(不要再用 equ 来定义变量了)

看了一些人写的51汇编程序,发现有不少人在用 equ 来分配变量,
觉得有必要向大家介绍一种比较好的变量定义方法。
先说说用 equ 定义变量的缺点:
1:人为为每一个变量指定地址,还必须要防止地址冲突,工作量太大。
2:编译器只是把用 equ 定义的标识符当作常数而已,在很多情况下当
   用户把该标识符用错时,编译器不会给出警告或报错。
   例如,假设想在 IDATA 区定义一个变量,如果用 equ 方式作如下定义:
    MyByte equ 87h   ;(其实编译器只是将 MyByte 视为一个常量而已)
如果想将该变量(位于 IDATA 区)赋值到累加器 a,正确的访问方式应该是
    mov  r0,#MyByte
    mov  a,@r0
如果程序员将上面的语句错写为
    mov  a,MyByte
(原因可能是粗心,或者忘记了 MyByte 是大于 7Fh 的)
编译器不会为此报错,结果执行的操作是将地址为 87h 的特殊功能寄存器
的内容赋值到 a,而不是将 IDATA 区的 87h 赋值到 a,与用户的本意大相径庭。

这只是一个例子,实际上还会有其它类似的问题。
   如果使用了下面描述的方式,则编译器会帮你指出此类错误。

所以,不要再用 equ 来定义变量了,这是一种很不好的方法,应该淘汰了。
建议 equ 只用于定义常数,不要做其它用途。
下面是一种比较好的变量定义方法,没有上面所说的缺点。

;定义变量的方法:
;1:定义字节变量用 ds 关键字,语法为:
;    变量名  ds  分配的字节数
;2:定义位变量用 dbit 关键字,语法为:
;    位变量名  dbit 分配的位数
;3:定义特殊功能寄存器(SFR)用 data 关键字,语法为:
;    寄存器名 data 寄存器地址
;4:定义可位寻址的特殊功能寄存器中的位用 bit 关键字,语法为:
;    位名 bit 位地址
;5:常量定义用 equ 关键字,语法为:
;    常量名 equ 常数
;具体见如下示例:

;51汇编程序基本框架
;在万利 MedWin3 下编译通过,在 Keil C 8.08 下编译通过。
;作者:icmap
;日期:2008-09-13

$NOMOD51   ;如果不使用默认的 51 特殊功能寄存器定义,可以用本语句取消。
;$include    (W79E825.inc)   ;用此语句可以包含头文件

;---------- 特殊功能寄存器定义 (仅作示范,不全)
    p0      data    080h
    sp      data    081h
    dpl     data    082h
    dph     data    083h

    pcon    data    087h
    tcon    data    088h
    tmod    data    089h
    tl0     data    08ah
    tl1     data    08bh
    th0     data    08ch
    th1     data    08dh

    p1      data    090h
    scon    data    098h
    sbuf    data    099h

    p2      data    0a0h
    ie      data    0a8h

    psw     data    0d0h

;---------- 可位寻址的特殊功能寄存器中的位定义(仅作示范,不全)
    bitIT0      bit tcon.0  ;外部中断0触发方式
    bitIE0      bit tcon.1  ;外部中断0标志
    bitIT1      bit tcon.2  ;外部中断1触发方式
    bitIE1      bit tcon.3  ;外部中断1标志
    bitTR0      bit tcon.4  ;定时器0启动控制
    bitTF0      bit tcon.5  ;定时器0溢出标志
    bitTR1      bit tcon.6  ;定时器1启动控制
    bitTF1      bit tcon.7  ;定时器1溢出标志

    bitRI       bit scon.0  ;接收中断标志
    bitTI       bit scon.1  ;发送中断标志
    bitRB8      bit scon.2
    bitTB8      bit scon.3
    bitREN      bit scon.4  ;接收使能
    bitSM2      bit scon.5  ;多机通信控制
    bitSM1      bit scon.6  ;串口模式位1
    bitSM0_FE   bit scon.7  ;串口模式位0或FE

;---------- 常量定义
nStackSize      equ 36    ;指定堆栈大小

;---------- 以下为变量定义
;*****如果MCU没有某些数据段,就删除或注释掉相应的段。
;变量定义有不可重定位和可重定位之分,这二种方式可混合使用。
;需要注意的是,某些编译器对可重定位方式支持的不是很好,
;而且可重定位方式对汇编不是很有用,所以建议不用可重定位方式。

;---- 下面为不可重定位的变量分配方式
; bseg 关键词指定 BIT 区
; dseg 关键词指定 DATA 区
; iseg 关键词指定 IDATA 区
; xseg 关键词指定 XDATA 区
; cseg 关键词指定 CDATA 区
;注意:BIT 区的位地址 00h~7Fh 范围与 DATA 区的20h~2Fh范围是同一存储空间,
;  所以在分配 DATA 区字节变量和 BIT 区位变量时要注意不要重叠。

    bseg    at  00h  ;从 BIT 区的位地址 00h 开始分配位变量
bExample:       dbit 1  ;在 BIT 区定义一个位变量,位地址为 00h(即DATA区20h字节的第0位)
bTestA:         dbit 1  ;在 BIT 区定义一个位变量,位地址为 01h(即DATA区20h字节的第1位)
bTestB:         dbit 1  ;在 BIT 区定义一个位变量,位地址为 02h(即DATA区20h字节的第2位)

;在 DATA 区定义变量时要注意不要与 R0~R7 寄存器重叠。

    dseg    at  08h  ;从 DATA 区的 08h 地址(为了避开 R0~R7)开始分配变量
dExample:       ds   2  ;在 DATA 区定义一个 2 字节的变量,地址为 08h
dTest:          ds   1  ;在 DATA 区定义一个 1 字节的变量,地址为 0ah
dLcdBuffer:     ds   21 ;注意不要与 BIT 区重叠,建议在地址 1Fh 结束分配。

    dseg   at   2eh  ;在 DATA 区的可位寻址区域定义可位寻址的字节变量
                     ;注意不要与前面在 BIT 区分配的 BIT 位重叠
dByteA:         ds   1  ;在 DATA 区定义一个 1 字节的变量,地址为 2eh(此变量可位寻址)
dByteB:         ds   1  ;在 DATA 区定义一个 1 字节的变量,地址为 2fh(此变量可位寻址)

    dseg    at  30h  ;从 DATA 区的 30h 地址(已避开 BIT 区)开始分配变量
dByteC:         ds   1  ;在 DATA 区定义一个 1 字节的变量,地址为 30h
dByteD:         ds   3  ;在 DATA 区定义一个 3 字节的变量,地址为 31h
dByteE:         ds   1  ;在 DATA 区定义一个 1 字节的变量,地址为 34h

    iseg    at  80h  ;从 IDATA 区的 80h 地址(为了避开 DATA 区)开始分配变量
iExampleA:      ds   1  ;在 IDATA 区定义一个 1 字节的变量,地址为 80h
iExampleB:      ds   4  ;在 IDATA 区定义一个 4 字节的变量,地址为 81h

    iseg    at 255-nStackSize   ;将堆栈放在 IDATA 区的末尾,首地址为 255-nStackSize
iStack:         ds   nStackSize ;定义多字节变量,作为堆栈用(大小为 nStackSize),
                                ;  见后面代码中的 “mov     sp,#iStack”语句。

    xseg    at  00h     ;从 XDATA 区的 00h 地址开始分配变量
xExample0:      ds   1  ;在 XDATA 区定义一个 1 字节的变量,地址为 00h
xExample1:      ds   1  ;在 XDATA 区定义一个 1 字节的变量,地址为 01h

;==============================================================================;
;Code 段
    cseg    at 0    //程序起始地址
a_Start:
    mov     ie,#00h ;关闭中断
    sjmp    a_Main

    cseg    at  0023h   //串口中断地址,其它中断地址的指定类似此方法。
    ljmp    a_UartInt   //跳转到中断处理子函数

;==============================================================================;
a_Main:
    mov     sp,#iStack ;设置堆栈指针,
                       ;只能在调用任何函
                       ;  数之前设定SP。
    mov     psw,#0  ;将RS0,RS1及其它标志位清0。

;注意:内存清零不要放在子函数中,
;      因为内存清零也会清零堆栈,
;      从而导致 ret 返回地址不正确!!!
    mov     r0,#0ffh     ;内存00h~0ffh的内容清零。
    clr        a
a_Main01:
    mov     @r0,a
    djnz    r0,a_Main01

;用户初始化代码

a_Loop:  ;主循环
;用户代码
    lcall  _Test
    sjmp    a_Loop

;用户代码(子程序)
_Test:
    nop
    nop
    ret

a_UartInt:  //串口中断处理子函数
    nop
    nop
    reti

    END

阅读(12470) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~