找回密码
 立即注册
查看: 757|回复: 2

80351(3):软件C语言编译器与人类程序员使用8051汇编语言编程的方法对比(下)

[复制链接]
  • 打卡等级:偶尔看看II
  • 打卡总天数:24
  • 最近打卡:2025-06-13 00:05:49

115

主题

1223

回帖

1万

积分

荣誉版主

积分
13002
发表于 2024-11-9 14:03:55 | 显示全部楼层 |阅读模式
本帖最后由 杨为民 于 2024-12-3 17:27 编辑

(1)计算机语言有三种类型:
1)人类说给计算机听的;
2)计算机说给计算机听的;
3)计算机说给人类听的;
第1类人类说给计算机听的语言就是我们通常所说的计算机语言。计算机语言分为两种,与指令集密切相关的语言被称为“低级计算机语言”,比如汇编语言,与人类语言密切相关的语言被称为“高级计算机语言”,比如C语言。人类程序员用某种计算机听得懂的语言告诉计算机自己想要它做什么事情,然后依靠软件编译器将人类的语言翻译为计算机二进制机器码,计算机通过执行这些机器码指令来实现人类的意图。
第2类计算机说给计算机听的通常称为计算机通讯协议,比如TCP/IP和HTTP协议。在单片机领域,最常见的是IIC、SPI和CAN总线等硬件通讯接口协议和MODIBUS等工业通讯协议。
第3类计算机说给人类听的主要是指各种人机交互设备程序,比如数码管显示和TFT屏幕菜单和图形图像显示程序。
(2)没有规矩,不成方圆,任何一个想学单片机语言编程的人都要牢记一个道理:语言是用来在两个物体之间交流的,而不是用来自言自语的,因此大家都必须遵从统一的标准,否则互相谁听得懂谁呢?。就像同一种语言还存在各种方言一样,对于同一种计算机语言,不同编译器还有不同的要求,因此程序员使用任何计算机语言都必须遵从自己所使用的编译器的语法规则,否则该编译器是无法正确理解你的意思,要么编译出错,要么运行出错
(3)计算机语言语法规则有“前缀”、“中缀”和“后缀”三种形式。比如幼儿刚开始学语言, “妈妈”和“饭饭”就是常见的两个名词,“要”是第一个动词,把动词与名词组合起来的规则称语法
神秘的是幼儿刚开始学说话的时候,无师自通地发明的语法规则常常是动词在后面的形式,比如“饭饭要”和“妈妈 抱”,这就是动词在后的“后缀”语法,省略了主体是“宝宝”。而后来大人强制加上主体将互相交流的语言变为动词在中间的形式,比如“我 要 吃饭”和“妈妈 抱 我”,这就是动词在中间的“中缀”语法。再后来幼儿仍然要省略主体,仍然要不甘心地偏把动词放到了前面,比如“打怪兽”和“躲 猫猫”,这就是动词在前面的“前缀”语法。
(4)大多数计算机语言的语法规则是动词在中间的形式(计算机术语是中缀波兰/正波兰”),比如C语言加法写为“ A + B”的形式。汇编语言通常是将动词写在前面(计算机术语是“前缀波兰”),比如8051汇编语言的加法写为“ADD  A, # 12”。将动词写在后面(计算机术语是“后缀波兰/逆波兰”)的计算机语言很少,FORTH语言是其中一种,比如在FORTH计算机语言中,加法“1+2”写为“1 2 +”
一、8位指令集与32位指令集的软件编译器比较
(5)在我们人类看来,像“C1 = A1 / B1 + 123.45678;”这样的算术表达式的含义简单、清晰和明了,但是仅是对程序员而言,要把这个表达式翻译为计算机听得懂的指令确实很繁琐的。下图是Keil的C51编译器对这个表达式编译出来的汇编语言程序的前半部分:
Fig_01_表达式A.jpg
这是表达式汇编语言程序的前半部分,第81到第92这12行汇编语言程序只完成一个操作:将4字节的浮点数变量B1的值加载到“EBX”(R0R1R2R3)32位寄存器中,第95到第106这12行汇编语言程序也只完成一个操作:将4字节的浮点数变量A1的值加载到“EAX”(R4R5R6R7)32位寄存器中,然后第109行汇编语言程序访问“?C?FPDIV”库函数进行除法,并将除法的商依然放回“EAX”中。

(6)下图是Keil的C51编译器对这个表达式编译出来的汇编语言程序的后半部分:
Fig_02_表达式B.jpg
其中第112到115这4行程序将浮点数123.45678放到EBX中,第118行完成浮点加法,第121和122两行程序将整个表达式的计算结果存放到变量C1中。
(7)从上面的程序可以看出使用8位的8051指令来实现32位的算术运算,虽然已经通过调用库函数简化了不少,但是仍然很长,比如为了读1个4字节变量需要写12行汇编语言程序。从这个例子可以看出,如果程序员要用8051汇编语言编写32位的数据处理程序,将是一件很苦恼的事情

(8)金水151是32位的C语言编译器,其金水明80151指令集的指令是直接面向32位数据操作的。对于同样的算术表达式,其软件编译出来的和程序员写的80151汇编语言程序如下图:
Fig_03_80151.jpg
其中的指令和顺序与我们小时候在纸上列竖式进行计算是一样的。
“LDX     EAX, A1”将XDATA空间的变量A1放到32位寄存器EAX中,
“LDX     EBX, B1”将XDATA空间的变量B1放到32位寄存器EBX中,
“DIVF    EAX, EBX”将EAX与EBX做浮点数除法,结果留在EAX中
“MVR     EBX , # 123.45678”将实数“123.45678”放到32位寄存器EBX中,
“ADDF    EAX, EBX”将EAX与EBX做浮点数加法,结果留在EAX中
“STX     C1, EAX”将最终的计算结果从EAX保存到XDATA空间的变量C1中去。
以上6行程序的是计算表达式“C1 = A1 / B1 + 123.45678”最短的汇编语言程序了,其每一步都有明确的操作意义,并且符合我们从小习惯计算方法
(9)结论:除了上面4字节的浮点数操作指令,金水明80151指令集还有一系列对等的32/16/8位整数操作指令,因此金水明80151汇编语言特别适合人类程序员用来编写高效的数据处理程序
二、人类程序员与C51软件编译器的比较
(10)我们从小便知先乘除后加减,算术运算是有运算优先级的。碰到当计算从低优先级遇到高优先级时,就需要把低优先级运算的操作数先保存起来,先处理后面高优先级的运算。有运算优先级的算术表达式汇编语言编程方法

(11)下图是一个计算有优先级表达式“C1 = A1/B1 - A2/B2”的例子:
Fig_04_优先级计算.jpg
对于STC单片机的TFPU,由于所有的浮点数除法只能在EAX-EBX寄存器对中进行,因此第164到166行计算出减数“A2/B2”后,需要找个地方保存起来,空出EAX-EBX寄存器对。
金水明80151指令集有两个扩展的32位寄存器“ECX”和“EDX”,正好可以第168行程序将减数(A2/B2)保存到ECX中。等待第170到172行程序计算出被减数后,第174行再取回减数到EBX,由第176行程序完成计算。

(12)Keil的C51编译器对应的8051指令集只有一对32位寄存器“R4R5R6R7”和“R0R1R2R3”,保存中间计算结果就要另想方法。下图是Keil的C51编译器编译出来的汇编语言程序:
Fig_05_C51_优先级计算A.jpg
从图中可以看到由于没有额外的寄存器,Keil的C51编译器通过第219行到第222行程序把减数(A2/B2)推入SP指定的系统堆栈中去保存了。

在后半部分程序中:
Fig_06_C51_优先级计算B.jpg
其中第256到259行程序又将减数从SP指定的系统堆栈中弹出到EBX寄存器中,由第262行程序完成最终计算。
(13)从上面的程序可以看到大段大段的汇编语言程序用来存取4字节的XDATA数据,不知道是否会有程序员愿意用这种8051汇编语言来写大规模数据处理的程序否?
(14)目前8位的基于8051指令集的STC单片机已经开始增加32位的DPU32硬件运算加速单元,大大提高了算术运算的速度。但是由于8051指令集没有32/16位的XDATA空间存取指令,程序员想要用汇编语言来编写数据处理程序是一件困苦的工作。
(15)由于金水明80151指令集有完整的32/16/8位运算和内存读写指令,因此特别适合用来在具有DPU32硬件加速单元的基于8051的STC单片机用汇编语言编写数据处理程序。
三、针对STC单片机未来发展的“堆栈机”C语言编译方法
(16)单片机的CPU(指令集)按其寄存器的数量多少主要分为两类,软件编译器也分为对应的两类。
一类是寄存器数量多,比如RISC-V单片机有32个32位的寄存器,ARM单片机有37个寄存器,单个模式下也有15个。对应这种多寄存器指令集的C语言编译器,它们尽量将每个函数的参数和局部变量分配到寄存器中,以此来提高C语言程序的实际运行速度。上一节金水明80151汇编语言程序员就将计算的中间结果保存到ECX寄存器中了。
另一类是寄存器数量少,比如飞思卡尔单片机。对应这一类单片机的C语言编译器由于没有多余的寄存器可以保存局部变量和计算中间结果,通常采用“堆栈机”模型来实现将C语言编译为汇编语言。
(17)STC单片机采用的8051/80251指令集属于多寄存器的类型,Keil的C51/C251编译器就对应多寄存器这一类。由于8051/80251指令集属于CISC指令集,很难通过提高CPU主频来实现运算速度质的飞跃,同时8051/80251指令集没有浮点数指令和DSP(数字信号处理)指令,笔者认为STC单片机将通过专门的DSP单元来提高单片机的数据处理能力。这是由于DSP属于RISC架构,可以通过单独提高DSP单元的工作主频的方式来提高单片机的数据处理的速度。
(18)STC最新推出的AI8051U单片机正式拉开了“双核单片机”的序幕。早期STC8H系列搭载的MDU16单元属于像80287/80387这样的片外“协处理器”范畴,发送完计算指令后,需要用软件查询其操作是否完成来协调它与单片机CPU内核的运行。
现在的AI8051U单片机和STC32G/F单片机上的MDU32和TFPU运算单元在对其发布执行命令后,主CPU的时钟自动停止,等待这些单元的操作完成后再恢复继续执行,实现了内核与这些数据处理单元两者之间的同步运行,这是一种基本的“双核架构”。当未来这些整数和浮点数的加速单元提供的操作指令越来越多的时候(AI8052U单片机面世的时候),它就成了一个独立“GPU”了,与8051/80251内核一起构成一个标准的“双核单片机”。
(19)由于STC单片机的DSP单元有一个明显特点是其运算只在一对32位寄存器中间进行,而且是不对称的(比如被除数、被乘数永远是EAX),因此未来的STC单片机的主流属于极少寄存器CPU架构。为了配合STC的发展,金水151编译器对于C语言运算表达式的编译采用了“堆栈机”模型。在“堆栈机”编译模型中,所有的运算都集中在EAX-EBX一个寄存器对完成。

(20)下图是金水151编译器实际对前面有优先级表达式的编译结果:
Fig_07_堆栈机.jpg
使用堆栈机模型对C语言表达式的编译方法的最大的特点是对表达式从左到右一次性扫描完成,不需要像一般的多寄存器编译方法那样先要建立语法树,然后再编译。
1)第276行,将第1个元素变量A1进堆栈(栈顶为EAX)。
2)第278行,将第2个元素变量B1进堆栈(栈顶变为EBX,EAX变为次栈顶)。
3)第281行,将次栈顶的EAX除以栈顶的EBX,同时从堆栈中弹出EBX,EAX(这时堆栈已空),然后将除法的结果(A1/B1)放回栈顶EAX。
重要:这一过程与STC单片机的MDU32和TFPU以及未来的DDSP32的操作完全完全契合,可以说是为专门为STC单片机定制的C语言编译方法。
4)第284行,将第3个元素变量A2进堆栈(栈顶变为EBX,EAX变为次栈顶)。
5)此时栈顶EAX-EBX已经被占用了,为了将第4个元素变量B2进堆栈,依次进行了以下操作:
第288行将次栈顶EAX实实在在地推入CPU的SP指定的系统堆栈中,腾出次栈顶。
第289行将栈顶EBX(A2)推入次栈顶EAX,腾出栈顶EBX。
最后第290行将第4个元素变量B2进堆栈(栈顶变为EBX,EAX为次栈顶)。
6)第293行,将次栈顶的EAX(A2)除以栈顶的EBX((B2),同时从堆栈中弹出EBX,EAX(这时堆栈已空),然后将除法的结果(A2/B2)放回栈顶EAX。
7)由于这时被减数是在SP指定的系统堆栈中,为进行二元运算减法,需要进行退栈操作:
第296行程序将EAX内容(A2/B2)移到新栈顶EBX。
第297行系统堆栈中的被减数退出到次栈顶EAX中,做好减法的准备。
8)第298行,将次栈顶的EAX(A1/B1)减去栈顶的EBX((A2/B2),同时从堆栈中弹出EBX,EAX(这时堆栈已空),然后将减法的结果放回栈顶EAX。
9)第301行,将表达式最终的结果存到变量C1中去,完成整个C语言表达式的编译工作。
(21)从上面的操作可以看到人类写的C语言表达式,被操作数总是在操作符的左面(中缀表达),所以从左到右扫描C语言表达式的堆栈机编译方法对于极少寄存器GPU架构是一种最优方法。

(22)对比前面给出的人类程序员使用金水明80151汇编语言编写的程序简明扼要“(11)”,上面的“堆栈机”方法编程既机械又单调还枯燥,如果让程序员来这样写汇编语言程序肯定不行。好在笔者是在金水明151编译器里专门编了段程序来完成这个工作,让计算机自动第完成这个编译工作,无需程序员亲自参与。

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:91
  • 最近打卡:2025-06-13 14:31:48

1

主题

28

回帖

127

积分

注册会员

积分
127
发表于 2024-11-21 22:32:59 | 显示全部楼层
{:5_332:}
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:393
  • 最近打卡:2025-06-16 06:50:40

7

主题

286

回帖

1395

积分

金牌会员

积分
1395
发表于 2024-12-16 11:15:47 | 显示全部楼层
学习了
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-6-16 08:14 , Processed in 0.121326 second(s), 66 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表