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

问题来源

举个小栗子

市面上有很多种芯片,其中对应的指令集也很丰富,可是不同的指令集对于各种基本功能的支持力度是不同的,以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的值循环左移一位,最高位进入进位位,最终的值写入寄存器,不写入f
rlf mem, f 将符号mem的值循环左移一位,最高位进入进位位,原本符号位进入mem的最低位,写回到f
clrf mem mem处的值清零
decf mem, f mem的值减一,并进行回写
comf mem, f mem的值取反,并进行回写
bcf mem, b mem的第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里面是没有对应的指令来实现的,再退一步,加法,也是通过各种位运算实现的呀。对于不同的芯片的相同指令,有不同的代码生成方法,这正是编译的乐趣所在,嘻嘻~

猜你喜欢

转载自blog.csdn.net/u014713475/article/details/80475982
pic