杨为民 发表于 2024-11-28 01:01:49

F351(8):倚天剑 Forth 8051程序结构

倚天剑 Forth 8051单片机 交叉编译器软件(以下简称倚天剑XCC51)的设计目标是对PC机上的STC单片机的Forth 8051程序进行编译,形成二进制的内存机器码映像,然后再生成HEX格式文件供STC-ISP软件烧录到目标单片机中运行,控制单片机实现各种功能。本文给出了实现LED灯交替闪烁功能的Keil的C51、A51与Forth 8051汇编语言三种源程序,详细地介绍Forth 8051汇编语言程序的基本结构。
一、Keil的C51和A51程序结构(1)打狗棒是STC近期推出的基于STC8H8K64U单片机的开发板,其中每个端口引脚都连接一个下拉的LED发光二极管,十分适合初学者学习,本文就以控制该开发板P2端口的LED灯闪烁来作为范例程序,下面是这三种程序都实现的统一的效果视频:
(2)使用Keil的C51编译器的C语言程序如下:
该程序分为4个部分:定义程序要使用的SFR,定义程序中要使用的软件延时函数,设置要使用的P2端口和后台任务的主循环,每隔500毫秒,P2每个管脚的电平变化一次。(3)使用Keil的C51编译器的A51汇编语言程序如下:

与C语言程序比较,多了一个系统引导部分,见上图的第92行到第105行程序。这是因为Keil的C51编译器在进行连接时它觉得有必要时会自动为用户添加一段引导程序,然后再跳转到用户的“main()”函数开始处执行用户程序。
二、Forth8051汇编语言程序的设置(4)倚天剑XCC51与Keil等编译器一个最大的不同是它在运行时本身是一个交互式的系统,有完整的对命令行的执行处理功能和对文本程序的编译生成功能。因此在Forth程序中你可以加上主机Forth程序来执行各种编译生成功能。Keil等各种编译器的“宏语言”有与之类似的程序功能,但与这些“宏语言”不同的是倚天剑XCC51主机Forth是一个“完备的计算机语言”,而“宏语言”不是,“宏语言”只能完成一些特定的功能。(5)本文Forth范例程序的文件名为“F051_01.J”,它是一个纯文本文件,可以用各种文字编辑器来查看和编写。倚天剑XCC51编译过程是一遍扫描,按照顺序逐行解释和编译Forth程序的,下面也依次分段介绍整个的程序,有助于读者了解编译器的编译过程和原理。Forth8051汇编语言程序首先要进行编译器设置部分,见下图:
(6)类似于其他计算机语言的命名空间,Forth系统按字典的方式来管理Forth字的名称标识符,同样的名称在不同的字典中对应不同的Forth字。倚天剑XCC51内设三个字典:根字典“ROOT”、汇编字典“ASM”和编辑字典“EDIT”。根字典提供了完整WDForth语言支持,汇编字典供用户自己建立交叉编译器用,倚天剑XCC51已经内装了“Forth 8051汇编语言编译器”,编辑字典提供用户建立自己的本地文本编辑或者远程交互系统使用。第17行程序Forth字“ROOT_ONLY”将搜索字典复位到只搜索根字典,Forth字“ASM”把汇编字典也添加上,此后倚天剑XCC51编译器将优先搜索汇编字典,找不到时再搜索根字典。倚天剑XCC51允许用户建立自己的字典,数量只受内存的限制。用户通过建立自己的字典,把自己定义的计算机语言词汇放在其中,就可以构成一个自己的“Forth系统”:使用自己的语言,编写自己程序,实现自己任务了,这就是笔者把它取名“我的Forth”的原因。(7)由于采用一遍扫描,倚天剑XCC51对任何一个Forth字(字符串)包括数字序列(比如“1234”)的编译流程是:首先按指定的次序在指定的字典中去查找有没有这个字,查到了,就编译或者执行这个字,查不到就试图把它转换为数值,如果转换成功,就把这个数值放到Forth的运算堆栈的栈顶,如果转换不成功,就中断编译过程,执行编译出错管理程序。对每个命令行或者程序行重复这个过程就是Forth系统编译执行的基本流程。第18行程序指定倚天剑XCC51以后缺省地按照十六进制来转换字符串,直到进制被改变。进制可以用命令随时改变,改变进制的Forth字有三个“HEX”、“DECIMAL”和“OCTAL”。(8)通常在程序中区分大小写的称“强语言”,比如C语言,不区分的称“弱语言”,比如BASIC语言。倚天剑XCC51则提供了两个命令“TEXT_COMP_ON”和“TEXT_COMP_OFF”给用户随时切换,满足不同用户和不同用途的需要。第19行程序指定编译器以后忽略程序中英文字母的大小写。(9)单片机特殊功能寄存器(SFR)设置部分,见下图:
在倚天剑XCC51中定义8051汇编语言寄存器的Forth字有两个:“DOPR”定义目的操作数,“SOPR”定义源操作数。比如在上图中第22行的定义中“95”(十六进制)是寄存器的地址,“DIRECT”指明是一个直接存取的地址(不是R0~R7的寄存器),而“P2M1,”是用户编程时代表该SFR的名称。本程序加上逗号,可以使其在程序里看起来像8051汇编语言的左操作数。
三、Forth8051汇编语言程序的预定义函数 (10)用户程序中自定义过程(函数)的部分,见下图:
(11)正如前面介绍的Forth编译流程,除了数值之外程序中所有的出现的Forth字都有预先定义好。因此在Forth程序中被访问的子程序定义要放在前面,访问它的语句要放在后面。在本范例中软件延时函数就需要放在前面。第35行到第61行是定义该函数(Forth字)的程序。(12)对于8051单片机,CPU复位时从地址0000H开始执行指令,从0003H开始是中断矢量区域。本范例中没有使用中断,所以编译后机器指令代码从0003H开始存放。第29行就是将编译器当前的PC设置为0003H。(13)在8051汇编语言中用LCALL指令跳转的地址是一个16位整数,第35行程序就是在Forth中定义一个常数“Delay_MS”,它的值为编译器当前的PC值,供以后访问语句引用。(14)为了读者与上节给出的程序对此,本范例参照Keil的C51规范,如果只有一个16位整数参数,则调用程序把这个参数放在8位组成的寄存器对R6R7中,然后用“LCALL”指令访问它,其中参数的高8位在R6中。(15)8051汇编语言程序中会有大量的短距离(小于128字节)条件转移指令,为了避免要定义大量的地址标号名称,倚天剑XCC51中引入了可以重复使用的、可以向前或者向后引用的10个局部地址标号:0$、1$...9$。不过在重复对局部地址标号赋值前要将局部这些标号复位。程序第37行中的“LBL_CLR”字执行这个功能。(16)倚天剑XCC51在编译的过程中要对汇编程序正确性进行检查,8051指令有很多是不对称指令,指令的两个操作数是不能交换的。比如将一个立即数存到一个DATA地址中的指令 “12,# 34MOV”,它是将数值34存入到地址为12的DATA存储器中,而不是反过来。因此倚天剑XCC51编译器在一段新程序开始的时候应该将编译器的操作数状态复位。第37行程序中的“OP-RESET”字就是实现这个功能。(17)由于本范例使用的STC8H8K64U单片机运行速度很快,要产生毫秒级的延时,必须使用两重16位变量的空循环来实现,除了用延时函数参数R6R7寄存器对做外循环变量外,本范例用R2R3寄存器对来做内循环变量,第41行程序就是每次内循环开始前对R2R3赋初值。(18)Forth程序以Forth字为基本单元,字之间用空格分开,为了程序易读加入的多余空格和制表符会被忽略,但是一个字不能跨行分为两半,这会被编译器认为是两个字。(19)在延时程序中,第45行到第47行和第53行到第55行程序是对16位变量寄存器对做“减一”操作的汇编指令序列,第50行和第58行是判断16位寄存器对不为0时进行跳转的条件转移汇编指令,它们实现了16位变量的两重循环。(20)由于倚天剑XCC51随时都可能使用十六进制,8051汇编语言中的“A”和“ACC”等CPU寄存器标识符有可能产生不确定性,因此在倚天剑XCC51汇编语言中用“ACCR”和“BCCR”作为CPU中“A”和“B”寄存器的名称,用单个的Forth字“MUL-AB”和“DIV-AB”来代表乘法和除法指令,避免产生歧义。
四、Forth8051汇编语言程序的用户主函数(21)用户程序的主函数部分,见下图:
(22)与在操作系统下的用户程序不同,单片机用户程序都有一个明确地入口点(这里是“Forth51_Main”),系统引导完成后从这里进入。第67行程序定义了这个地址标号供后面的引导程序访问。(23)单片机用户程序都分为两个部分,首先是对单片机系统的设置程序(Setup)部分,见上图中第70行程序到第73行程序,然后是一个不应该退出的无限循环(Loop)部分,见上图中第75行到96行程序。由于主循环部分的长度依赖程序的功能和程序员的编程风格,没有固定的长度,编译后可能产生超过128字节地址偏移,所以这个主循环没有采用局部地址标号,直接在第77行定义了一个循环开始地址常数,在主循环结束语句第96行用远跳转指令访问,形成无限循环程序结构。(24)在第85行和第93行对延时函数进行访问前,对寄存器对R6R7的赋值相当于C语言中对函数参数的赋值,其等价于C语言程序“Delay_MS(500);”(25)根据笔者多年的经验,在使用8051“MOV”指令的编程中,很容易在右操作数(源操作数)是地址还是立即数的问题上翻车,因此在倚天剑XCC51汇编语言中专门将右操作数是立即数情况的指令分离出来,用“MOV#”作为操作符,这样既避免了在程序中加或者不加“#”问题上产生的BUG,也符合Forth语言堆栈里的数究竟是数值还是地址由后面使用它的字决定的特点。对比Keil汇编语言中的“MOV”指令格式,倚天剑XCC51的主要差别是把操作符位置移到了操作数后面,但是左右操作数的习惯还是保持了不变。
五、Forth8051汇编语言程序的引导程序(26)在倚天剑XCC51程序中,单片机的引导程序也需要用户自己编写,这样除了必须得建立系统运行环境和状态外,用户还可以加上从FLASH存储器中加载和初始化系统数据的隐形部分。本范例的引导程序部分见下图:
1)第101行定义了目标机系统引导代码的开始地址。2)由于8051单片机存取DATA空间数据的速度比存取XDATA空间数据速度高很多,所以一般在倚天剑XCC51程序中将SP置为80H,将20H到7FH之间的全部DATA空间保留给用户做变量区使用。3)由于在倚天剑XCC51的WDForth中“SP”已经有特殊的含义了,因此倚天剑XCC51特别用“CPUSP”和“CPUPSW”来指明这是8051目标机CPU中的寄存器。4)当全部用户写完后,第114行程序将当前的目标机PC值定义为一个常数“Forth_Code_Top”,从开始地址0000H算,这就是整个要烧录到单片机的代码区的长度。5)最后第118行将目标机PC设置为单片机的复位地址0000H,然后写入三个字节的远跳转指令。这样单片机加电后,CPU就转移到系统引导区开始运行了。 (27)在倚天剑XCC51程序中,烧录代码的长度由用户自己明显地指定。由于很多STC单片机都有IAP的功能,用户可以指定比实际代码区长度大的烧录长度,与STC-ISP配合,保留出一定的CODE空间供系统运行时的临时区或者掉电保留数据区使用。输出烧录文件的程序部分见下图:
1)第124行程序是将已经生成的机器码输出为HEX格式的烧录文件。其中Forth字“YTJ_HexOut”是烧录命令,其前面的数字是从地址0000开始输出的字节数,其后面的HEX文件的文件名。2)第127行“BYE”是退出整个倚天剑XCC51程序的命令,这时整个编译器退出。如果注释掉这个命令,系统将退出编译状态,继续留在倚天剑XCC51的Forth运行环境中,等待再次出现“JL>”的命令行提示符后,用户继续操作,已经生成的新Forth字和已经编译好的代码仍然留在内存中。
六、结束语对比前面的三种语言程序,倚天剑XCC51的8051汇编程序几乎和Keil的A51汇编程序一样,只是两者在指令格式上不同,Forth语言的操作符在操作符的后面。然而汇编语言是每条每条机器指令的写,没有C语言程序那么简洁、直观。
如何用倚天剑XCC51实现一个类似于C语言程序的编译器,笔者将在后面的文章中专门介绍。




页: [1]
查看完整版本: F351(8):倚天剑 Forth 8051程序结构