我有一些处理1-256字节的函数,运行在嵌入式C平台上,在这个平台上传递一个字节比传递一个int更快更紧凑(一个指令与三个指令),那么编码的首选方式是什么:
最有效的代码是简单地接受一个无符号字符,并将参数值0视为请求执行256个字节,依靠调用者避免任何意外尝试读取0个字节。 但是这似乎有点危险。 其他人如何处理嵌入式系统上的此类问题? 如何处理? 编辑 平台是PIC18Fxx(128K代码空间;3.5K RAM),连接到SPI闪存芯片; 当期望较少的字节时读取256字节可能会溢出PIC中的读取缓冲区。 写入256个字节而不是0会破坏闪存芯片中的数据。 如果不检查忙状态,则PIC的SPI端口每12个指令时间只能发送一个字节; 如果检查,则速度会变慢。 典型的写事务需要除要接收的数据之外还需要发送4个字节; 读取需要额外的一个字节进行“SPI回转”(访问SPI端口的最快方法是在发送下一个字节之前读取最后一个字节)。
编译器是HiTech PICC-18std。
我一般喜欢 HiTech 的 PICC-16 编译器;HiTech 似乎已经将他们的精力从 PICC-18std 产品转向了 PICC-18pro 系列,其编译时间更慢,似乎需要使用 3 字节的 'const' 指针而不是两字节指针,并且有自己关于内存分配的想法。也许我应该更多地看看 PICC-18pro,但当我尝试在 PICC-18pro 的评估版本上编译我的项目时,它没有工作,我也没有弄清楚为什么——也许与变量布局不符合我的汇编程序有关——我只是继续使用 PICC-18std。
顺便说一句,我刚刚发现 PICC-18 特别喜欢 do {} while(--bytevar); 而特别不喜欢 do {} while(--intvar); 我想知道编译器在生成后者时正在思考什么?
编译器加载变量的指针,甚至不使用 LFSR 指令(需要两个字),而是使用 MOVLW/MOVWF 的组合(需要四个字)。然后它使用这个指针来执行递减和比较。虽然我承认 do{}while(--wordvar); 不能产生像 do{}while(wordvar--); 那样好的代码,但代码比后者实际生成的要好。单独执行递减和 while 测试(例如 while (--lpw,lpw))会产生合理的代码,但它看起来有些丑陋。后置递减运算符可以为倒计时循环产生最佳代码:
- 接受一个int,如果为零,则提前退出,否则将计数值的LSB复制到无符号字符中,并在do {} while (--count)循环中使用它(参数值为256将转换为0,但将运行256次)。
- 接受一个无符号字符,如果为零,则提前退出,并为256字节拥有特殊版本的函数(这些情况将事先知道)。
- 接受一个无符号字符,如果为零,则运行256次。
- 有一个类似上述函数的函数,但通过包装函数调用它,这些函数的行为为(0-255)和(仅256)。
- 有一个类似上述函数的函数,但通过包装器宏调用它,这些宏的行为为(0-255)和(仅256)。
最有效的代码是简单地接受一个无符号字符,并将参数值0视为请求执行256个字节,依靠调用者避免任何意外尝试读取0个字节。 但是这似乎有点危险。 其他人如何处理嵌入式系统上的此类问题? 如何处理? 编辑 平台是PIC18Fxx(128K代码空间;3.5K RAM),连接到SPI闪存芯片; 当期望较少的字节时读取256字节可能会溢出PIC中的读取缓冲区。 写入256个字节而不是0会破坏闪存芯片中的数据。 如果不检查忙状态,则PIC的SPI端口每12个指令时间只能发送一个字节; 如果检查,则速度会变慢。 典型的写事务需要除要接收的数据之外还需要发送4个字节; 读取需要额外的一个字节进行“SPI回转”(访问SPI端口的最快方法是在发送下一个字节之前读取最后一个字节)。
编译器是HiTech PICC-18std。
我一般喜欢 HiTech 的 PICC-16 编译器;HiTech 似乎已经将他们的精力从 PICC-18std 产品转向了 PICC-18pro 系列,其编译时间更慢,似乎需要使用 3 字节的 'const' 指针而不是两字节指针,并且有自己关于内存分配的想法。也许我应该更多地看看 PICC-18pro,但当我尝试在 PICC-18pro 的评估版本上编译我的项目时,它没有工作,我也没有弄清楚为什么——也许与变量布局不符合我的汇编程序有关——我只是继续使用 PICC-18std。
顺便说一句,我刚刚发现 PICC-18 特别喜欢 do {} while(--bytevar); 而特别不喜欢 do {} while(--intvar); 我想知道编译器在生成后者时正在思考什么?
编译器加载变量的指针,甚至不使用 LFSR 指令(需要两个字),而是使用 MOVLW/MOVWF 的组合(需要四个字)。然后它使用这个指针来执行递减和比较。虽然我承认 do{}while(--wordvar); 不能产生像 do{}while(wordvar--); 那样好的代码,但代码比后者实际生成的要好。单独执行递减和 while 测试(例如 while (--lpw,lpw))会产生合理的代码,但它看起来有些丑陋。后置递减运算符可以为倒计时循环产生最佳代码:
decf _lpw btfss _STATUS,0 ; 如果进位标志位为0(即不是零),跳过下一条指令 decf _lpw+1 bc loop ; 只有当lpw为零时,进位标志位才会清除
但它生成的代码比--lpw还要糟糕。最好的代码应该是一个向上计数的循环:
infsnz _lpw incfsz _lpw+1 bra loop 但编译器没有生成这样的代码。 编辑2 我可能会采用另一种方法:为字节数分配一个全局的16位变量,并编写函数,使得在退出前计数器总是归零。然后,如果只需要8位值,只需要加载8位即可。我会使用宏来进行调整,以获得最佳效率。在PIC上,对于已知为零的变量使用| =永远不会比使用=慢,并且有时会更快。例如,intvar | = 15或intvar | = 0x300将是两个指令(每种情况只需处理一个字节的结果,可以忽略其他字节);intvar | = 4(或任何2的幂)是一条指令。显然,在某些其他处理器上,intvar = 0x300将比intvar | = 0x300更快;如果我使用宏,可以根据需要进行调整。
do {...} while(--byteVar);
循环(或者等效的汇编代码)如果传入零值将会循环256次。在20世纪80年代的计算机上,可能有很多程序都是写成当循环计数为零时请求256次循环,而不是跳过整个循环。认为0代表比最大可通过值“更大”的想法确实有点奇怪,但这与一些操作系统延迟例程并不矛盾,它们接受无符号超时值,但使用0作为“无限期等待事件”的特殊情况。 - supercat