对于PIC16芯片中有符号扩展的优化

2019-04-15 12:11发布

问题来源

举个小栗子

市面上有很多种芯片,其中对应的指令集也很丰富,可是不同的指令集对于各种基本功能的支持力度是不同的,以PIC16芯片和X86芯片相比较,举出一个很简单的例子,就是乘法,比如实现c = a * b
在X86中,实现如下: movl b, %eax mul b 这里不敢说乘法指令的全部操作只需要一条指令,但是乘法的本身还是只需要一条指令的,即mul b,但是在PIC16中会变成什么样子呢,代码如下: movf a, w movf mul.atmp, f movf b, w movf mul.btmp, f call mul.i8 movf mul.result, w movf c, 1 这里可能写的不规范,但是总体的意思是,将变量ab 分别放到乘法库函数定义的符号mul.atmpmul.btmp的符号里面,然后调用乘法库函数mul.i8,最后从库函数预先设定的符号里面取值,放到符号c中,如果觉得不好理解,可以先看成下面的高级函数形式的声明: char mul.i8(char mul.atmp, char mul.btmp); 然后用户调用这个函数: mul.result = mul.i8(a, b); c = mul.result;

小栗子的结论

从上面的小栗子可以看出,不同的芯片对于每种基本基本操作的支持力度真的是不一样的,所以本文题目中的操作—有符号扩展,在两个芯片中的支持力度也不同,下面开始分析

分析过程

所需要的指令集

指令名称 指令作用 rlf mem, w 将符号mem的值循环左移一位,最高位进入进位位,最终的值写入寄存器,不写入frlf mem, f 将符号mem的值循环左移一位,最高位进入进位位,原本符号位进入mem的最低位,写回到fclrf memmem处的值清零 decf mem, fmem的值减一,并进行回写 comf mem, fmem的值取反,并进行回写 bcf mem, bmem的第b为清零

本芯片中如何用两条指令完成一次算数右移

先告诉你们一个不幸的消息,PIC16芯片中关于移位只有循环右移和循环左移,没有算数右移算数左移和逻辑右移。而且PIC16里面也没有相应的标志位寄存器,对于标志位,PIC16的策略是保存在一个内存符号里面,好在PIC16有一条指令bcf,这条指令可以将某个内存符号中的特定位置0,对应的也有bsf指令,可以将特定的内存位置的特定位置1,那么如何使用两条指令进行模拟呢。下面直接说结论吧,。
算数右移: rlf mem, w rrf mem, f 算数左移/逻辑左移: bcf flags, 3 // 这里不一定是3,这里假设状态位的第三位是进位位,即这里是把进位位清零 rlf mem, f 逻辑右移: bcf flags, 3 // 这里不一定是3,这里假设状态位的第三位是进位位,即这里是把进位位清零 rrf mem, f

两种没有经过优化的方式

使用库函数的方式

就像第一部分所说的,如果使用库函数,那么是8位扩展为16位,需要7条指令,因为可以指定移位次数是7,代码如下: movf a, w movf srashift.atmp, f movl 7 movf srashift.btmp, f call srashift.i8 movf srashift.result, w movf c, f 同样可以看成高级语言函数声明: srashift.result = srashift(char srashift.atmp, char srashift.btmp); 用户可以调用这个函数: srashift.result = srashift(a, 7); c = srashift.result; 而PIC16中的最高位宽是32位,最低位宽是8位,所以这里存在三种有符号扩展,分别为i8->i16i8 -> i32i16 -> i32,这三种有符号扩展分别所需要的指令条数为7条,至少11条,和13条。这是统计数据,相信我,没骗你们。

重复生成移位指令

这个小题目看起来神秘莫测的,但是其实非常2,所谓的重复生成移位指令就是一条一条的移位指令的写,这里以8位扩展为16位为例,可以看出位宽只差为8位,可是本平台每移动一次就需要两条指令,所以如果是8位扩展为16位需要16条指令。同理8位扩展32位需要48条指令,16位扩展32位需要32条指令。

本博客提出的方法

直接说出方法吧,这里以8位扩展16为例,设低位保存在mem + 0,值为00000000,高位保存在mem + 1中,由于申请这个符号的时候值是未知的,所以设置为xxxxxxxx,完成有符号移位只需要五条指令: clrf mem + 1, f rlf mem + 0, w rlf mem + 1, f decf mem + 1, f comf mem + 1, f 让我们来看看这五条指令的执行效果: 指令名称 执行效果 高位初始值 xxxxxxxx clrf mem + 1, f 00000000 rlf mem + 0, w 00000000 rlf mem + 1, f 00000001 decf mem + 1, f 00000000 comf mem + 1, f 11111111 可以看出,最后的高位是11111111,与mem + 0的最高位相等。容易证明,对于正数来说也是如此,最后的结果是00000000

结语

从这篇博客里面也可以得知,不管一条指令有多么简单,都不要把这条指令的实现当成理所当然的,就比如乘法,在PIC16里面是没有对应的指令来实现的,再退一步,加法,也是通过各种位运算实现的呀。对于不同的芯片的相同指令,有不同的代码生成方法,这正是编译的乐趣所在,嘻嘻~