Bootstrap

8051寄存器、指令集、伪指令和关键字详细介绍

8051寄存器、指令集、伪指令和关键字详细介绍

作者将狼才鲸
创建日期2022-04-09
修改日期2024-0723

一、概述

  • C51是8位CPU。顾名思义,它的某个总线是8位,或者一些总线是8位的。实际上它的数据总线是8位的,每条CPU指令只能处理一个8位的数据,而它的外部地址总线是16位的,可以执行最大64KB的程序,也可以简单的理解为编译出来的可执行程序不能超过64KB(可以类比理解为在电脑上你只能下载安装64K以内的软件),这是C51的限制。

为什么51单片机的地址总线是16位的,但是它却是8位机?

二、寄存器和地址

  • 地址分为ROM地址和RAM地址,最大16位64K。

  • RAM:

地址大小描述备注
0x00~0x2F32工作寄存器地址4组R0~R7寄存器
0x20~0x2F16位寻址为了让寄存器可直接位寻址,用SBIT设置
0x30~0x7F48内部RAMIRAM,可设置为堆栈
0x80~0xFF128内部RAM和特殊寄存器共用IRAM、SFR
0x0100~0xFFFF65280B外部RAMRAM
  • ROM:
地址大小描述备注
0x0000~0x0FFF4KB内部ROMIROM
0x1000~0xFFFF60KB外部ROMROM
  • 寄存器介绍:
    21个特殊功能寄存器(52系列是26个)不连续地分布在128个字节的SFR存储空间中,地址空间为80H-FFH,在这片SFR空间中,包含有128个位地址空间,地址也是80H-FFH,但只有83个有效位地址,可对11个特殊功能寄存器的某些位作位寻址操作(这里介绍一个技巧:其地址能被8整除的都可以位寻址)。
    SFR被称为特殊功能寄存器,芯片厂商自定义外设的寄存器地址也都在这组地址里面。
C51单片机的寄存器
符号地址功能介绍
R0~R70x00~0x2F工作寄存器地址
位寻址0x20~0x2F为了让寄存器可直接位寻址
堆栈或内部RAM0x30~0x7F
P080HP0口锁存器
SP81H堆栈指针
DPL82H数据地址指针(低8位)
DPH83H数据地址指针(高8位)
PCON87H电源控制寄存器
TCON88HT0、T1定时器/计数器控制寄存器
TMOD89HT0、T1定时器/计数器方式控制寄存器
TL08AH定时器/计数器0(低8位)
TL18BH定时器/计数器0(高8位)
TH08CH定时器/计数器1(低8位)
TH18DH定时器/计数器1(高8位)
P190HP1口锁存器
SCON98H串行口控制寄存器
SBUF99H串行口锁存器
P2A0HP2口锁存器
IEA8H中断允许控制寄存器
P3B0HP3口锁存器
IPB8H中断优先级控制寄存器
PSWD0H程序状态字
ACCE0H累加器
BF0HB寄存器

51单片机寄存器功能一览表
C51/C52 特殊功能寄存器表

三、指令集

指令代码指令长度指令周期助记符操作对象描述举例
0011NOP延时1个指令周期的时间NOP ; 延时
0122AJMPaddr11短跳转,绝对转移,地址范围0xXX±0x000~07FF,转移范围为当前指令地址高5位相同的2K范围AJMP ADDR1
0232LJMPaddr16全域跳转,可以跳转到64K范围所有绝对地址,后面常用自定义的标号跳转LJMP ADDR2
0311RRA累加器循环右移,实际上也是除以2
0411INCA累加器自增1,所有INC指令都不会产生借位可用作循环处理
0521INCdirect将给出地址当中的数据自增1(自增完后放回原处)同上
0611INC@R0将R0中的数据当作地址,将这个地址中的数据自增1同上
0711INC@R1同上同上
0811INCR0将R0中的数据自增1同上
0911INCR1同上同上
0A11INCR2同上同上
0B11INCR3同上同上
0C11INCR4同上同上
0D11INCR5同上同上
0E11INCR6同上同上
0F11INCR7同上同上
1032JBCbit, offset如果某个可位寻址的位地址值为1,则清零后跳转到offset这个地址继续执行,否则顺序执行下一条指令JBC BIT C
1122ACALLaddr11短函数调用,函数中最后一条要是RET。绝对跳转,地址范围0xXX±0x000~07FF,跳转范围为当前指令地址高5位相同的2K范围,有参数的话子函数中还需要压栈和弹栈。调用完成后接着顺序执行本指令的下一条ACALL FUNC1
1232LCALLaddr16全域函数调用,函数中最后一条要是RET。地址范围0x0000~FFFF,有参数的话子函数中还需要压栈和弹栈。调用完成后接着顺序执行本指令的下一条LCALL FUNC2
1311RRCA带进位累加器循环右移,也就是除以2
1411DECA累加器自减1,所有DEC指令都不会产生借位可用作循环处理
1521DECdirect将给出地址当中的数据自减1(自增完后放回原处)同上
1611DEC@R0将R0中的数据当作地址,将这个地址中的数据自减1同上
1711DEC@R1同上同上
1811DECR0将R0中的数据自减1同上
1911DECR1同上同上
1A11DECR2同上同上
1B11DECR3同上同上
1C11DECR4同上同上
1D11DECR5同上同上
1E11DECR6同上同上
1F11DECR7同上同上
2032JBbit, offset如果某个可位寻址的位地址值为1,则跳转到offset这个地址继续执行,否则顺序执行下一条指令,该bit不清零实现if else switch
2122AJMPaddr11重复1
2212RET从子函数中返回
2311RLA累加器循环左移,也相当于乘以2
2421ADDA, #immed累加器和一个立即数(常数)相加,结果放回到AADD A, #080H
2521ADDA, direct累加器和内存地址里的值相加,结果放回到A
2611ADDA, @R0累加器和R0里面存的内存地址里面指向的值相加,结果放回到A
2711ADDA, @R1同上
2811ADDA, R0累加器和R0里面的值相加,结果放回到AADD A, R0
2911ADDA, R1同上
2A11ADDA, R2同上
2B11ADDA, R3同上
2C11ADDA, R4同上
2D11ADDA, R5同上
2E11ADDA, R6同上
2F11ADDA, R7同上
3032JNBbit, offset如果直接寻址位为0则转移实现if else switch
3122ACALLaddr11重复2
3212RETI中断程序返回
3311RLCA带进位累加器循环左移,也就是乘以2
3421ADDCA, #immed带进位求和
3521ADDCA, direct
3611ADDCA, @R0
3711ADDCA, @R1
3811ADDCA, R0
3911ADDCA, R1
3A11ADDCA, R2
3B11ADDCA, R3
3C11ADDCA, R4
3D11ADDCA, R5
3E11ADDCA, R6
3F11ADDCA, R7
4022JCoffset如果CY进位为1 则跳转,进位不清零
4122AJMPaddr11重复3
4221ORLdirect, A后面的与前面的相或,结果放到前面,*direct &= A
4332ORLdirect, #immed
4421ORLA, #immed
4521ORLA, direct
4611ORLA, @R0
4711ORLA, @R1
4811ORLA, R0
4911ORLA, R1
4A11ORLA, R2
4B11ORLA, R3
4C11ORLA, R4
4D11ORLA, R5
4E11ORLA, R6
4F11ORLA, R7
5022JNCoffset如果CY进位为0 则转移
5122ACALLaddr11重复4
5221ANLdirect, A累加器“与”到直接地址
5332ANLdirect, #immed
5421ANLA, #immed
5521ANLA, direct
5611ANLA, @R0
5711ANLA, @R1
5811ANLA, R0
5911ANLA, R1
5A11ANLA, R2
5B11ANLA, R3
5C11ANLA, R4
5D11ANLA, R5
5E11ANLA, R6
5F11ANLA, R7
6022JZoffset累加器为0 则转移
6122AJMPaddr11重复5
6221XRLdirect, A累加器“异或”到直接地址
6332XRLdirect, #immed
6421XRLA, #immed
6521XRLA, direct
6611XRLA, @R0
6711XRLA, @R1
6811XRLA, R0
6911XRLA, R1
6A11XRLA, R2
6B11XRLA, R3
6C11XRLA, R4
6D11XRLA, R5
6E11XRLA, R6
6F11XRLA, R7
7022JNZoffset累加器为1 则转移
7122ACALLaddr11重复6
7222ORLC, bit直接寻址位“或”到进位位
7312JMP@A+DPTR相对DPTR 的无条件间接转移
7421MOVA, #immed
7532MOVdirect, #immed
7621MOV@R0, #immed
7721MOV@R1, #immed
7821MOVR0, #immed
7921MOVR1, #immed
7A21MOVR2, #immed
7B21MOVR3, #immed
7C21MOVR4, #immed
7D21MOVR5, #immed
7E21MOVR6, #immed
7F21MOVR7, #immed
8022SJMPoffset无条件相对转移
8122AJMPaddr11重复7
8222ANLC, bit直接寻址位“与”到进位位
8312MOVCA, @A+PC代码字节传送到累加器
8414DIVAB累加器除以B 寄存器
8532MOVdirect, direct
8622MOVdirect, @R0
8722MOVdirect, @R1
8822MOVdirect, R0
8922MOVdirect, R1
8A22MOVdirect, R2
8B22MOVdirect, R3
8C22MOVdirect, R4
8D22MOVdirect, R5
8E22MOVdirect, R6
8F22MOVdirect, R7
9032MOVDPTR, #immed16 位常数加载到数据指针
9122ACALLaddr11重复8
9222MOVbit, C进位位位传送到直接寻址
9312MOVCA, @A+DPTR代码字节传送到累加器
9421SUBBA, #immed累加器减去立即数(带借位)
9521SUBBA, direct
9611SUBBA, @R0
9711SUBBA, @R1
9811SUBBA, R0
9911SUBBA, R1
9A11SUBBA, R2
9B11SUBBA, R3
9C11SUBBA, R4
9D11SUBBA, R5
9E11SUBBA, R6
9F11SUBBA, R7
A022ORLC, /bit直接寻址位的反码“或”到进位位
A122AJMPaddr11重复9
A221MOVC, bit直接寻址位传送到进位位
A312INCDPTR数据指针加1
A414MULAB累加器和B 寄存器相乘
A5reserved
A622MOV@R0, direct
A722MOV@R1, direct
A822MOVR0, direct
A922MOVR1, direct
AA22MOVR2, direct
AB22MOVR3, direct
AC22MOVR4, direct
AD22MOVR5, direct
AE22MOVR6, direct
AF22MOVR7, direct
B022ANLC, /bit直接寻址位的反码“与”到进位位
B122ACALLaddr11重复10
B221CPLbit取反直接寻址位
B311CPLC取反进位位
B432CJNEA, #immed, offset比较直接地址和累加器,不相等转移
B532CJNEA, direct, offset
B632CJNE@R0, #immed, offset
B732CJNE@R1, #immed, offset
B832CJNER0, #immed, offset
B932CJNER1, #immed, offset
BA32CJNER2, #immed, offset
BB32CJNER3, #immed, offset
BC32CJNER4, #immed, offset
BD32CJNER5, #immed, offset
BE32CJNER6, #immed, offset
BF32CJNER7, #immed, offset
C022PUSHdirect直接地址压入堆栈
C122AJMPaddr11重复11
C221CLRbit清直接寻址位
C311CLRC清进位位,和CLR CY是一样的效果
C411SWAPA累加器高、低4 位交换
C521XCHA, direct直接地址和累加器交换
C611XCHA, @R0
C711XCHA, @R1
C811XCHA, R0
C911XCHA, R1
CA11XCHA, R2
CB11XCHA, R3
CC11XCHA, R4
CD11XCHA, R5
CE11XCHA, R6
CF11XCHA, R7
D022POPdirect直接地址弹出堆栈
D122ACALLaddr11重复11
D221SETBbit置位直接寻址位
D311SETBC置位进位位
D411DAA累加器十进制调整
D532DJNZdirect, offset直接地址减1,不为0 则转移
D611XCHDA, @R0间接RAM 和累加器交换低4 位字节
D711XCHDA, @R1
D822DJNZR0, offset寄存器减1,不为0 则转移
D922DJNZR1, offset
DA22DJNZR2, offset
DB22DJNZR3, offset
DC22DJNZR4, offset
DD22DJNZR5, offset
DE22DJNZR6, offset
DF22DJNZR7, offset
E012MOVXA, @DPTR外部RAM(16 地址)传送到累加器
E122AJMPaddr11重复12
E212MOVXA, @R0外部RAM(8 地址)传送到累加器
E312MOVXA, @R1
E411CLRA累加器清零
E521MOVA, direct
E611MOVA, @R0
E711MOVA, @R1
E811MOVA, R0
E911MOVA, R1
EA11MOVA, R2
EB11MOVA, R3
EC11MOVA, R4
ED11MOVA, R5
EE11MOVA, R6
EF11MOVA, R7
F012MOVX@DPTR, A累加器传送到外部RAM(16 地址)
F122ACALLaddr11重复13
F212MOVX@R0, A累加器传送到外部RAM(8 地址)
F312MOVX@R1, A
F411CPLA累加器求反
F521MOVdirect, A累加器传送到直接地址
F611MOV@R0, A直接地址传送到间接RAM
F711MOV@R1, A
F811MOVR0, A
F911MOVR1, A
FA11MOVR2, A
FB11MOVR3, A
FC11MOVR4, A
FD11MOVR5, A
FE11MOVR6, A
FF11MOVR7, A
伪指令ORG汇编起始伪指令
伪指令END结束伪指令
伪指令DB字节数据定义伪指令
伪指令DW字数据定义伪指令
伪指令DS空间定义伪指令
伪指令EQU赋值伪指令
伪指令MACRO ENDM宏定义函数NOP2 MACRO \ NOP \ NOP \ ENDM 该举例为无参数宏定义函数也可以带参数
伪指令BIT位地址符号定义伪指令
伪指令DATA片内RAM直接字节地址定义伪指令
伪指令XDATA片外RAM直接字节地址定义伪指令
伪指令HIGH16位数的高字节#HIGH(65536)
伪指令LOW16位数的低字节#LOW(65536)

还有一些编译器自定义的符号,如Keil的伪指令:
$NOMOD51
$NOPRINT
NAME ; 给当前模块命令,同时也是一段代码的入口
SEGMENT ;类似于typedef
PUBLIC
?C_START ; main函数入口
IF
ELSE
ENDIF
标号:
CODE
IDATA
EXTRN
RSEG ; 段选择指令
CSEG
AT
$INCLUDE(USER.ASM)

C51 各个存储区说明
keil C51中各个地址的区别
51单片机片内RAM的128B(00H~FFH) 分为哪几部分各部分地址范围及功能?
51单片机特殊功能寄存器中的字节寻址和位寻址
C51 特殊功能寄存器SFR的名称和地址
C51最全111条汇编指令合集,以及使用时的注意事项,超详细
51单片机指令表
正确区分LJMP、AJMP、SJMP、JMP单片机跳转指令
51单片机的汇编指令中AJMP 和SJMP都是两个字节,都是两个机器周期,它们有什么区别呢?
谁能帮我解释一下 INC A ; INC direct INC Rn INC @Ri INC DPTR
MCS51单片机的伪指令有哪些?
keil_C51伪指令

四、扩展

  • Keil官网上显示支持98家公司的9500款芯片(截止到2022-09-29),其中一半基于ARM核,一半基于8051核,少量基于其它核。

  • 点击Legacy Device List查看所有器件

  • 参考网址
    厂商列表 MDK5 Device List:https://www.keil.com/dd2/,里面有几十家公司
    器件列表 Legacy Device List:https://www.keil.com/dd/

  • 除了Keil,其它的8051模拟器还有:
    emu8051:
    https://github.com/jarikomppa/emu8051
    https://www.cnblogs.com/jikexianfeng/p/6357529.html
    EdSim51:
    http://edsim51.com/
    https://zhuanlan.zhihu.com/p/371060362


  • Keil伪指令
  1. Keil A51汇编代码中支持很多常用的伪指令,需要掌握,写汇编时经常会用到,这些伪指令可以在A51相关的英文文档里看到所有的描述;国内网站上没找到有人完整的翻译所有的伪指令,但是能在Keil官网上找到英文原版的,我没有仔细去翻找,但是应该在C51用户手册里面的一系列文档中的某些章节里面;Keil安装好的文件里的帮助文档的a51文档中能搜到所有的伪指令含义。

  2. Keil汇编伪指令介绍详见同级目录下的文档[《Keil A51汇编伪指令》](./02_doc/Keil A51汇编伪指令.md)


  1. 从官网下载Keil C51程序,这是一个IDE,集成了编辑器、编译器、链接器、模拟器。
  2. 下载地址 https://www.keil.com/demo/eval/c51.htm 需要注册并填写个人信息,评估版只支持编译2k容量的代码。
  3. 安装过程中,安装路径不要有空格,不要有中文目录。
  4. 安装完成后打开“Keil uVision5”软件,
    点击“Project”–>“new uVision Project”–>选中你愿意放工程的目录–>
    在选择设备弹窗中,我选择Cadence公司的R8051XC2(8DPTR),选择哪款8051芯片都无所谓,前期只操作基础的8051寄存器,不同公司的基础寄存器是一致的,我这里以这款芯片为例–>
    点击下一步后,弹出“Copy ‘STARTUP.A51’ to Project Folder and Add File to Project?”,点是,这样编译器会自动给你填充一份Boot汇编代码模板,这个Boot代码能让你跳转到main函数。
  5. 点击左上角两个箭头的按钮,编译程序,下方“Build Output”区域提示
    “.\Objects\cadence_first_project” - 0 Error(s), 2 Warning(s).
    错误为零则代表编译通过,生成的可执行文件是没有后缀的Objects\cadence_first_project
  6. 点击右上角红色的‘d’图标,能直接使用软件自带的模拟器仿真运行8051程序,
    如果提示“EVALUATION MODE…2K”,表示未注册的评估版只支持2K代码,我前期的代码没有超过2K,直接点确定下一步–>
    使用Keil默认提供的boot汇编程序,测试时断点会停在“?C_STARTUP”这一行–>
    点击行号前面可以创建和取消断点–>
    点击左上角几个带箭头或者叉叉的图标,可以单步执行、跳转到函数内部执行、持续运行、立即停止运行,快捷键是F10、F11、F5–>
    进入调试模式后,Keil里有各种窗口看串口输出的信息、RAM里的数据值、当前各个变量和寄存器的值、所有寄存器的值,修改当前变量的值,让程序中的变量按自己手动输入的值生效并运行,如果程序跑飞了可以查看异常寄存器和地址寄存器存储的上一个语句的地址。
  • 如果熟悉了这款芯片后,你不使用Keil自带的Boot程序,而是自己写整个寄存器宏定义,则在Keil工程配置里面,魔法棒图标–>Device–>勾选使用LX51和使用AX51,使用自己定义的SFR特殊功能寄存器。
  • 如果你需要将程序下到板子里面去,甚至还需要将hex可执行文件转成容量更小的bin文件,则在Output页面中勾选"Create HEX File"。

  • 在Keil使用过程中的一些技巧:
  1. 不使用Keil自带的GB2312中文编码,而是使用UTF-8中文编码,这样在用Git进行版本管理时能正常显示中文,在Linux和Windows之间来回切换工程后也不容易产生乱码,导致中文信息丢失无法恢复,步骤如下:
    打开工程–>点击左上角“Edit”–>点击弹出菜单最下方Configuration–>
    在弹出页面最左侧Editor页面中点击"Encoding"–>
    从ANSI改为UTF-8,点击“OK”,这样可以输入中文。

  2. Keil将Tab设置为固定4个空格(为了和Linux内核还有Git Tab以8个字节显示进行兼容,空格能让显示格式固定)
    Configuration–>Editor–>C/C++ Files–>Tab size: 4


4.1、8051资源描述

  • 为了直观,这里直接列出了8051的所有寄存器,而8051所有的汇编指令表格在这个寄存器表格后面以文章的链接给出来。
  • 8051单片机数据存储器可划分为两大区域:00H~7FH为片内低128字节RAM区;80H~FFH为特殊功能寄存器区(SFR)。
  • 我举例用的8051 IP核使用的是Cadence的r8051xc,相关的通用寄存器需要查看r8051xc相关的文档。
  • 8051寄存器中地址以0和8结尾的都是可以位寻址的,如0x80 P0、0x88 TCON,而且8051中对每一个可位寻址的位都有一个对应的名字,直接操作这个名字就能操作这个位,具体的含义请在程序源码中看注释,或者需要时直接在网上搜索。
  • 地址为00H~7FH的低128字节片内RAM区又可划分为三个区域:通用寄存器区地址(00H~1FH)、可位寻址区(20H~2FH)、用户RAM区(30H~7FH,堆栈也可以设置在这里)。
  1. 8051通用寄存器介绍,共128个:
地址0x000x010x020x030x040x050x060x07
通用寄存器0组R00组R10组R20组R30组R40组R50组R60组R7
地址0x080x090x0A0x0B0x0C0x0D0x0E0x0F
1组R01组R11组R21组R31组R41组R51组R61组R7
地址0x100x110x120x130x140x150x160x17
2组R02组R12组R22组R32组R42组R52组R62组R7
地址0x180x190x1A0x1B0x1C0x1D0x1E0x1F
3组R03组R13组R23组R33组R43组R53组R63组R7
地址0x200x210x220x230x240x250x260x27
位地址00H~06H07H~0FH10H~16H17H~1FH20H~26H27H~2FH30H~36H37H~3FH
地址0x280x290x2A0x2B0x2C0x2D0x2E0x2F
40H~46H47H~4FH50H~56H57H~5FH60H~66H67H~6FH70H~76H77H~7FH
地址0x300x310x320x330x340x350x360x37
剩下都是用户RAM一般开辟成堆栈……
地址0x380x390x3A0x3B0x3C0x3D0x3E0x3F
地址…………………………………………
地址0x780x790x7A0x7B0x7C0x7D0x7E0x7F
  1. R8051XC2特殊功能寄存器区(SFR)介绍,最大128个,8051通用的寄存器会加粗,未加粗的是R8051XC2特有的:
地址0x800x810x820x830x840x850x860x87
描述P0 IO口锁存器SP 堆栈指针DPL 数据地址低8位DPH 数据地址高8位WDTRELPCON
地址0x880x890x8A0x8B0x8C0x8D0x8E0x8F
TCON Timer控制TMOD Timer方式TL0 Timer0低8位TL1 Timer1低8位TH0 Timer0高8位TH1 Timer1高8位CKCON
地址0x900x910x920x930x940x950x960x97
P1 IO口锁存器DPSDPCPAGESELD_PAGESEL
地址0x980x990x9A0x9B0x9C0x9D0x9E0x9F
S0CON串口控制S0BUF串口锁存IEN2S1CONS1BUFS1RELL
地址0xA00xA10xA20xA30xA40xA50xA60xA7
P2 IO口锁存器DMAS0DMAS1DMAS2DMAT0DMAT1DMAT2
地址0xA80xA90xAA0xAB0xAC0xAD0xAE0xAF
IE0 中断允许IP0S0RELL
地址0xB00xB10xB20xB30xB40xB50xB60xB7
P3 IO口锁存器DMAC0DMAC1DMAC2DMASELDMAM0DMAM1
地址0xB80xB90xBA0xBB0xBC0xBD0xBE0xBF
IP 中断优先级 IEN1IP1S0RELHS1RELHIRCON2
地址0xC00xC10xC20xC30xC40xC50xC60xC7
IRCONCCENCCL1CCH1CCL2CCH2CCL3CCH3
地址0xC80xC90xCA0xCB0xCC0xCD0xCE0xCF
T2CONCRCLCRCHTL2TH2RTCSEL
地址0xD00xD10xD20xD30xD40xD50xD60xD7
PSW 程序状态字IEN4I2C2DATI2C2ADRI2C2CONI2C2STASMB2_SELSMB2_DST
地址0xD80xD90xDA0xDB0xDC0xDD0xDE0xDF
ADCCONP5 IO口I2CDATI2CADRI2CCONI2CSTASMB_SELSMB_DST
地址0xE00xE10xE20xE30xE40xE50xE60xE7
ACC 累加器SPSTASPCONSPDATSPSSNP6 IO口
地址0xE80xE90xEA0xEB0xEC0xED0xEE0xEF
P4 IO口MD0MD1MD2MD3MD4MD5ARCON
地址0xF00xF10xF20xF30xF40xF50xF60xF7
B 寄存器
地址0xF80xF90xFA0xFB0xFC0xFD0xFE0xFF

五、boot编写

  • 如何写纯汇编程序
; $NOMOD51 ; 使A51不使用8051所有预定义的符号,使用自定义符号
; 不同的芯片厂商可以将SFR寄存器进行全新的定义

	;==== SFR寄存器定义====
	P0		DATA	80H  ; P0 IO口
	SP		DATA	81H  ; 堆栈指针
	DPL		DATA	82H  ; 数据指针低字节
	DPH		DATA	83H  ; 数据指针高字节
	PCON	DATA	87H  ; 电源控制
	TCON	DATA	88H  ; 定时器控制
		TF1	BIT	TCON.7
		TR1	BIT	TCON.6
		TF0	BIT	TCON.5
		TR0	BIT	TCON.4
		IE1	BIT	TCON.3
		IT1	BIT	TCON.2
		IE0	BIT	TCON.1
		IT0	BIT	TCON.0
	TMOD	DATA	89H  ; 定时器方式
	TL0		DATA	8AH  ; 定时器0低字节
	TH0		DATA	8CH  ; 定时器0高字节
	TL1		DATA	8BH  ; 定时器1低字节
	TH1		DATA	8DH  ; 定时器1高字节
	P1		DATA	90H  ; P1 IO口
	SCON0	DATA	98H	 ; UART0
		TI0	BIT	SCON0.1
		RI0	BIT	SCON0.0
	SBUF0	DATA	99H  ; 串口0数据
	SCON1	DATA	9BH	 ; UART1 ; 芯片厂商自行添加的
	SBUF1	DATA	9CH  ; 串口1数据
	P2		DATA	0A0H ; P2 IO口 ; 对于最高位大于等于10(ABCDEF)的数前面必须带0
	IEN0	DATA	0A8H ; 中断使能
		EA	BIT	IEN0.7	
		WDT BIT	IEN0.6	 ; 芯片厂商自行添加
		EX1	BIT	IEN0.2
		EX0	BIT	IEN0.0	
	P3		DATA	0B0H ; P3 IO口
	T2CON	DATA	0C8H ; 定时器2控制
	TL2		DATA	0CCH ; 定时器2低字节
	TH2		DATA	0CDH ; 定时器2高字节
	PSW		DATA	0D0H ; 程序状态寄存器
		CY	BIT	PSW.7
		AC	BIT	PSW.6
		F0	BIT	PSW.5
		RS1	BIT	PSW.4
		RS0	BIT	PSW.3
		OV	BIT	PSW.2
		F1	BIT	PSW.1
		P	BIT	PSW.0
	ACC		DATA	0E0H ; 累加器
	B		DATA	0F0H ; 寄存器B
	EXADR	DATA	0FEH ; SFR扩展接口 ; 支持更多的寄存器
	EXDATA	DATA	0FFH

	; 赋值
	APP_MODE    EQU  0F8H ; 类似于宏定义

	; 中断入口,程序入口(程序从0地址开始执行)
    ORG     0000H
    LJMP    RESET

    ORG		000BH ; 中断入口的地址都是固定的
    LJMP	T0INT

    ORG		001BH
    LJMP	T1INT

    ORG		002BH
    LJMP	T2INT

	ORG		0100H ; 程序起始地址
	
	$INCLUDE(USER.ASM)
	
RESET:
	; 你的汇编代码,初始化各个模块,执行函数,响应中断,执行程序
	
END
  • 如何写汇编boot程序,并引导到main()函数执行
	;;;;
	; 其它未写出的准备操作:
	; 用DATA申明所有的SFR寄存器名字,P0(80H) ~ B(0F0H)
	; 自定义的宏定义,如DEBUG_LEVEL EQU 01H,用于配置软件的不同功能

	CSEG	AT	  0000H	  ; 板子复位后执行的第一条指令
	LJMP	STARTUP		  ; 执行初始化函数

	CSEG	AT	  0003H	  ; 外部中断0
	LJMP	interupt_0	  ; 依次注册好所有中断处理函数
	;;;; 省略其它中断处理函数

	; SEGMENT申明本模块在CODE代码段,CODE代码段起始地址是0x100,这也是程序默认运行的起始地址,前面的地址是一些固定的中断处理的函数地址
	STARTUP_FUNC  SEGMENT  CODE AT 0100H  ; 等同于ORG	0100H
	RSEG  STARTUP_FUNC ; 定义函数再定义段
	
	PUBLIC STARTUP ; 申明函数,并向别的.asm暴露出函数接口
	
STARTUP: ; 标号,同时也是函数名,和C语言中标号类似,C语言的标号可以goto跳转
	NOP ; 延时一个时钟周期
	CLR EAL ; SBIT(EAL, IE, 7) ; 关闭中断7
	CLR RS0 ; RS0 BIT PSW.3 ; 
	CLR RS1 ; RS0 BIT PSW.3 ; 和上条命令一起选择第一组R0~R7寄存器
	MOV IE, #0H ; 关闭所有中断
	NOP
	MOV SP, #ORIGIN_SP ; ORIGIN_SP EQU 40H ; 初始化堆栈起始地址
	NOP
	LCALL _hardware_init ; 调用你写的函数写驱动模块寄存器初始化你需要的硬件,如引脚、PLL时钟倍频分频、JTAG设置、看门狗复位、IO输出、引脚复用、软件配置判断、内存初始化、串口、SPI、I2C等初始化
	NOP
	LCALL _crt0Startup ; 调用crt0.c里面的C语言函数,其实这时候已经可以直接调用main函数了,但是有些main函数之前的准备工作是用C写的,所以要提前调用一下
		;;;;
		; extern int main(int, char * const []);
		; extern int sysExit(int exit_code);
		; #define sysMain main
		; int crt0Startup(int argc, char * const argv[])
		; {
		;     // 关闭所有中断、DMA缓存刷新、CPU工作模式选择、硬件频率进一步设置、串口的完整初始化(设置波特率)、中断初始化、时钟初始化(更新当前实时时间)、有操作系统的话初始化task、内核、使能中断、调用main函数、main结束后进行资源销毁,便于软复位后系统能再次正常运行
		;     // sysMain(argc, argv); // 跳转到main函数执行
		;     // sysExit();
		; }
	NOP
	LJMP _cpuStop ; 关闭PLL时钟分频倍频,将时钟设置为晶振的原始频率
	NOP
	RET
	NOP
	
	END

CSEG ; 绝对地址指示的代码段,可以当成一个函数的入口
RSEG ; 再定位段选择指令,它用来选择一个在前面已经定义过的再定位段作为当前段,例如先申明一个函数段,后面写这个函数段。PS: 程序代码放到代码段,数据对象放到数据段,段分两种,一是种绝对段,一种是再定位段。
SEGMENT ; 申明是哪种段,类似C语言的{}花括号,和END配和使用
AT ; 该段的起始地址
PUBLIC ; 给别的.asm文件暴露出函数接口,类似于C语言 int api_func(void);放在头文件中
$SAVE ; 存储最近的LIST和GEN的设置
$NOLIST ; 不使用最近的LIST配置
$RESTORE ; 恢复最近的LIST和GEN的设置
EXTRN CODE (YOUR_FUNCTION_NAME) ; EXTRN 是与PUBLIC 配套使用的,要调用其它模块的函数,就必须先在模块前声明

汇编语言段和RSEG用法
A51零散笔记
STC8头文件

  • 函数参数:
    可用作函数参数的,及时压栈和弹栈的寄存器有ACC累加器、B寄存器(为乘法和除法指令而设置)、PSW程序状态字(处理进位、非零、正负、溢出等)、DPH/DPL(数据地址指针,读外部RAM数据)、R0~7(工作寄存器);函数调用时,让这些寄存器放弃它们本来的用法,当作函数参数使用。压栈和弹栈时顺序要刚好相反

寄存器B
标志寄存器(PSW)
单片机DPH DPL是什么
求教解释R0~R7.还有,RS0,RS

;