一、新的16位的8051指令设计 (1)8051指令集是8位的指令集,对于16/32位的数据操作就需要多条指令来完成。用8位8051汇编语言来写的程序,无论是用Keil的A51或者是用Fort的F051写的汇编语言程序比起C语言程序都有两个明显的差距:一个程序太长,另一个是重复性很高。如果汇编语言程序能够直接采用16/32位指令,则可以大大地缩短这个差距。 (2)本文范例仍然用LED交替闪烁的例子,其C语言的程序见下图: (3)上面C51程序涉及16位数据操作有4种:第26行的16位变量初值操作、第27和29行的16位减1操作、第28和30行的16位条件转移操作以及第46和50行的16位函数参数初值操作。本文将介绍如何用Forth打造这4种16位指令,以实现缩短Forth语言程序,达到与C语言程序一一对应的效果。 (4)Forth程序的设计与实现方法是“自上而下”的设计,“自下而上”的实现。为了打造新的“16位指令”,首先设计出最终的与上面C51程序对应的Forth语言程序,。
设计程序如下: 在上面的Forth 80151语言程序与C51程序一一对应,对应C51程序写在每行程序的注释中。 (5)参数设计:F80151语言采用Keil的C51规范,单个16位的函数参数放在R6R7寄存器对中,即Delay_MS函数的参数MS放在寄存器对R6R7中。 (6)变量设计:F80151语言采用Keil的C51规范,在进行优化后,将单个16位的函数局部变量放在R2R3寄存器对中,即Delay_MS函数的局部变量n放在寄存器对R2R3中。 (7)命名设计:F80151语言采用Keil的C51规范和8086的助记符规范,将16位目标操作数R6R7命名为“AX”寄存器,将16位源操作数R2R3命名为“BX”寄存器。 (8)指令设计:根据上面程序,共需要9条新的80151的16位指令: 1)“F151_AX_LD#”,初值指令,将16位立即数加载到AX寄存器中。 2)“F151_BX_LD#” ,初值指令,将16位立即数加载到BX寄存器中。 3)“F151_AX_DEC” ,减1指令,将16位AX寄存器中的值减1。 4)“F151_BX_DEC” ,减1指令,将16位BX寄存器中的值减1。 5)“F151_AX_JNZ” ,条件转移指令,如果16位AX寄存器中的值不等于0则转移。 6)“F151_BX_JNZ” ,条件转移指令,如果16位BX寄存器中的值不等于0则转移。 7)“F151_RETURN”,函数返回指令 8)“F151_SFR_LD#” ,初值指令,将8位立即数加载到SFR寄存器中。 9)“F151_GOTO”,无条件转移指令。 (9)总结:用Forth 打造16位的8051指令,先完成程序设计、参数设计、变量设计、命名设计和指令设计,然后再去用Forth语言去实现这些指令。
二、Forth程序的项目管理方法(10)通常一个大一点的编程项目都是由多个程序文件组成,这就需要一个“项目管理”方法来控制这些程序按一定的次序进行编译、连接和输出,Forth语言编程也不例外: Forth程序项目管理方法是不用专门的IDE,由用户在自己程序中各种“编译命令”来控制编译器按程序语句次序对文件进行模块加载、源程序编译、模块保存和HEX烧录文件生成。 (11)Forth提倡用一个专门的文本文件来使用这些编译命令,控制编译的进行,这个文件称为“项目文件”。 (12)本文范例“F051_16指令”子目录下有下列文件: 1)“01_P151.J”,Forth“项目”文件,文本文件。 2)“02_META.J”,Forth“元接口”程序文件,文本文件。 3)“03_BOOT.J”,Forth“引导”程序文件,文本文件。 4)“11_MAIN.J”,Forth“主函数”程序文件,文本文件。 5)“LOAD.COM”,DOS“COM”程序加载器。 6)“YTJ_F051.COM”,“倚天剑XCC51交叉编译器”DOS程序。 7)“YTJ_XCC51_V21.BAT”,启动“倚天剑XCC51交叉编译器”的批处理文件。 8)“Zap_HEX.bat”,防止头脑不清醒时出错,编译前先删除旧HEX格式烧录文件的批处理文件。 (13)在DOS、WinXP已经Win7-32位等操作系统下,双击“YTJ_XCC51_V21.BAT”批处理文件就可以进入DOS环境,执行YTJ_F051.COM程序,进入“倚天剑XCC51交叉编译器”命令行界面,此时键入“TF_LAOD 01_P151.J<CR>”,就可以编译本范例项目,生成“H80151.hex”烧录文件,把它烧录到STC8H单片机中执行就可以看到LED灯闪烁了。 下面是这一过程的视频:
视频中可以看到: 1)双击“YTJ_XCC51_V21.BAT”批处理文件进入进入倚天剑XCC51交叉编译器”命令行界面的过程: 2)可以看到键入“TF_LAOD 01_P151.J<CR>”,开始编译本范例项目,生成“H80151.hex”烧录文件的过程: 3)可以看到把它烧录到STC8H单片机中后打狗棒开发板上LED灯闪烁的过程: 4)可以看到重新启动“倚天剑XCC51交叉编译器”进入命令行界面,键入了错误的命令“LIST”后,系统提示出错的结果和键入“BYE”命令后退出系统的过程:
(14)重要结论:倚天剑XCC51交叉编译器具有执行手工命令,读写DOS文件以及完善的命令出错复位功能,如果再加上对文件的删除功能,就构成了一个“寄生在DOSINT21”上的微型的“操作系统”了。
三、构造80151新16位指令的方法
(15)作为Forth“自下而上”的实现部分,本文范例的新16位指令在Forth程序文件“02_META.J”中构建: (16)第29行到第36行的程序转移指令的构造最简单,这两条指令本来在8051指令集中有,在F151中只需要加增加新定义就可以了。 重要说明:倚天剑XCC51交叉编译器 的设计目标是作为一个Forth操作系统,因此采用标准Forth语法,采用英文冒号“:”和分号“;”来定义一个Forth复合词。前面介绍的金湖X51 Forth的设计目标是作为专门的Forth编译器,因此把英文冒号“:”和分号“;”留给了目标机专用,其本身用英文冒号“CW_:”和分号“;_CW”来定义一个Forth复合词。 (17)第43行到第45行构造了一个将16位立即数赋值给R6R7寄存器对的新16位指令。在Keil的A51汇编语言中,这个操作需要两条指令来完成: MOV R6, # HIGH(立即数) MOV R7, # LOW(立即数) 第44行Forth程序中的“DUP”字的意思就是把立即数复制两份,分别给R6和R7赋值用。 其中“R6,”的意思是将R6设置为目的操作数,其中的“100 / FF AND”这三个字换成十进制的意思是“把立即数除以256,然后再与255执行二进制AND操作”,这就是妥妥的“HIGH(立即数)”函数的操作了,然后的“MOV#”字就是执行8051的MOV指令。 同理该行程序后面的“FF AND”就是“LOW(立即数)”函数,然后“MOV#”指令将16位立即数的低8位赋值给R7。 (18)第54行到第58行构造了16位寄存器减1的新16位指令。实现的方法就是写一段将R6R7寄存器对做减去16位立即数1的8051指令程序。只是8051指令集中的寄存器名“A”、“B”、“C”在Forth语言中也是十六进制数符号,为避免混乱,在Forth汇编语言中将它变为了“ACC”、“BCC”和“CY”了。 (19)第69行到第72行构造了根据16位寄存器AX的值进行转移的新16位指令。由于R6R7寄存器对16位数据是否为0的充分必要条件是R6和R7的二进制OR操作后为0,所以实现的方法就是将R6和R7在累加器ACC中ORL,然后使用8051指令集的“JNZ”指令进行条件转移。 (20)第81行到第83行构造了对SFR进行立即数赋值的新指令。这段程序是展示Forth编译器如何根据8051二进制指令来构造新指令的方法。 在8051指令集中,向DATA空间的某个地址dir8(8位地址)写入一个立即数data8(8位)的二进制机器码由3个字节组成:75,dir8, data8,其中十六进制数“75”指令操作码,对应的汇编语言语句为“MOV dir8, # data8”或者写为“MOV d8, # i8” 。 在倚天剑XCC51交叉编译器的Forth语言中,字“CODE_C,”的意思是把堆栈顶上一个数的最低8位以二进制的形式放到目标机的CODE空间中,存放的地址由编译器的地址指针“PC”决定,放完后“PC”的值加1,执行下一个存放地址。 第82行程序“75 CODE_C, SWAP CODE_C, CODE_C,”的意思是首先将指令的操作码“75”写入CODE空间,然后交换两个操作数“SFR V”的位置,先将SFR地址接着写入CODE空间,最后将要赋给SFR的值V写入CODE空间,形成一个完整的3字节机器码指令供CPU去执行。 (21)结论:使用上面这个方法,只要我们知道某个指令集的每个指令的二进制机器码,那么就可以用Forth系统构造出一个完整的“该指令集的编译器”。比如可以构造出80251指令集、ARM指令集或者RISC-V指令集的编译器。“倚天剑XCC51交叉编译器”就是针对8051单片机的编译器。 当然对于STC单片机现在和未来的新指令集,如法炮制,也可以用Forth系统打造出其专用的编译器,比如“Forth@AI8051U编译器”和“Forth@AI8052U编译器”。
四、Forth项目文件的结构(22)Forth项目文件是一个文本文件,其内容本身也是一个“Forth源程序”,当用户输入命令“TF_LOAD项目文件名<CR>”之后,倚天剑XCC51交叉编译器就逐行从左到右依次执行每一个Forth字,对整个Forth项目进行编译、连接和输出。
下图是本文范例的项目文件程序: (23)第17行首先加载“02_META.J”源程序文件。Forth字“TF_include”的意思将后面文件名指定的文件以文本文件的在这个位置“包含”进来。Forth字“TF_include”与C语言“#inlucde”的功能是一样的。 (24)当第17行程序加载“META”程序,构造好了新的16位指令以后,第20行程序加载“11_MAIN.J”Forth主程序,用新的16位指令集编译与C语言一一对应的主程序。 (25)作为一个在STC单片机的可从0000H地址启动的独立运行的程序,第23行程序继续为项目添加引导程序,为主程序的运行建立起工作环境。 (26)第26行程序将从地址0000H到标号“Forth_Code_Top”地址范围的机器码按HEX格式输出为“H80151.hex”烧录文件。 其中标号“Forth_Code_Top”在“03_BOOT.J”程序中定义,位于本次编译的所有机器码的顶部。 (27)在完成本次由“TF_LOAD”命令执行的所有编译任务后,第29行程序“BYE”命令强制倚天剑XCC51交叉编译器系统退出,PC机回到DOS状态。用户还想要做什么操作,必须重新启动Forth系统。如前面的视频展示的那样。 (28)由于Forth在进行编译的过程中,由可能改变了系统的某些状态,留下一些新定义的Forth字,因此需要第,如果不退出系统,如果用户后面进行了错误操作可能导致不可预料的结果。
(29)当然如果用户有足够的把握,也可以不要第29行程序,不退出Forth系统,用户可以继续用手工命令继续进行编译工作,手工生成HEX格式的烧录文件。
|