如何使用较小的代码空间减少十六进制 ASCII 字符转换的代码空间?
在嵌入式应用程序中,我的空间非常有限(注1(。 我需要将字节从串行 I/O 转换,将 ASCII 值"0"转换为"9",将"A"转换为"F"转换为通常的十六进制值 0 到 15。 此外,需要检测所有其他 240 种组合,包括"a"到"f"(作为错误(。
库函数(如scanf(), atoi(), strtol()
(太大而无法使用。
速度不是问题。 代码大小是限制因素。
我目前的方法将 256 字节代码重新映射到 256 个代码,使得"0"到"9"和"A"到"Z"的值为 0 - 35。 任何关于如何减少或不同方法的想法都值得赞赏。
unsigned char ch = GetData(); // Fetch 1 byte of incoming data;
if (!(--ch & 64)) { // decrement, then if in the '0' to '9' area ...
ch = (ch + 7) & (~64); // move 0-9 next to A-Z codes
}
ch -= 54; // -= 'A' - 10 - 1
if (ch > 15) {
; // handle error
}
注 1:引导加载程序的 PIC 保护存储器中存在 256 条指令用于代码和常量数据(1 字节数据成本 1 条指令(。 此代码需要 ~10 条指令。 当前的ap需要重写并且只有1条备用指令,即使减少1条指令也是有价值的。 我正在一点一点地经历它。 还研究了整体重建。
注:附注16。 我更喜欢用"C"编码,但必须尽一切努力。 程序集代码如下。 不需要快速回答。
if (!(--ch & 64)) {
002D:DECF 44,F 002E:BTFSC 44.6 002F:GOTO 034
ch = (ch + 7) & (~64);
0030:MOVLW 07 0031:ADDWF 44,W 0032:ANDLW BF 0033:MOVWF 44
}// endif
ch -= 54;
0034:MOVLW 36 0035:SUBWF 44,F
[编辑最佳解决方案]
按照@GJ的建议优化现有解决方案。在 C 语言中,执行ch += 7; ch &= (~64);
而不是ch = (ch + 7) & (~64);
保存了 1 条指令。通过无需在if()
内重新加载ch
,可以进行组装以节省另一个。
PIC16系列是RISC MCPU,因此您可以尝试优化ASM代码。这是你的c编译器asm代码...
decf ch, f
btfsc ch, 6
goto Skip
movlw 07
addwf ch, w
andlw 0xBF
movwf ch
Skip
movlw 0x36
subwf ch, f
这是我对上层代码的优化...
decf ch, w //WREG = (--ch)
btfsc WREG, 6 //if (!(WREG & 64)) {
goto Skip
addlw 7 //WREG += 7
andlw 0xBF //WREG &= (~64)
Skip
addlw 0x100 - 0x36 //WREG -= 54;
movwf ch //ch = WREG
//
addlw 0x100 - 0x10 //if (WREG > 15) {
btfsc STATUS, 0 //Check carry
goto HandleError
。所以只有 7 个操作码(少 2 个(没有范围错误检查和 10 个操作码有范围错误检查!
编辑:也试试这个PIC16 c编译器优化功能,不确定是否有效...
WREG = (--ch);
if (!(WREG & 64)) { // decrement, then if in the '0' to '9' area ...
WREG = (WREG + 7) & (~64); // move 0-9 next to A-Z codes
}
ch = WREG - 54; // -= 'A' - 10 - 1
if (WREG > 15) {
; // handle error
}
编辑II:添加的版本与非XLP技术制造的旧PIC16 MCPU兼容,但代码大小长一个操作码。
decf ch, f ;//ch = (--ch)
movf ch, w ;//WREG = ch
btfsc ch, 6 ;//if (!(ch & 64)) {
goto Skip
addlw 7 ;//WREG += 7
andlw 0xBF ;//WREG &= (~64)
Skip
addlw 0x100 - 0x36 ;//WREG -= 54;
movwf ch ;//ch = WREG
//
addlw 0x100 - 0x10 ;//if (WREG > 15) {
btfsc STATUS, 0 ;//Check carry
goto HandleError
编辑三:解释
"D Kruegers"解决方案也非常好,但需要一些修改......
此代码..
if (((ch += 0xC6) & 0x80) || !((ch += 0xF9) & 0x80)) {
ch += 0x0A;
}
。我们可以翻译成...
if (((ch -= ('0' + 10)) < 0) || ((ch -= ('A' - '0' - 10)) >= 0)) {
ch += 10;
}
之后,我们可以在 asembler 中进行优化...
call GetData
//if GetData return result in WREG then you do not need to store in ch and read it again!
// movwf ch
// movf ch, w
addlw 0x100 - '0' - 10 //if (((WREG -= ('0' + 10)) < 0) || ((WREG -= ('A' - '0' - 10)) >= 0)) {
btfss STATUS, 0
goto DoAddx
addlw 0x100 - ('A' - '0' - 10)
btfsc STATUS, 0
DoAddx
addlw 10 //WREG += 10; }
movwf ch //ch = WREG;
addlw 0x100 - 0x10 //if (WREG > 15) {
btfsc STATUS, 0 //Check carry
goto HandleError
通过良好的编译器优化,这可能会占用更少的代码空间:
unsigned char ch = GetData(); // Fetch 1 byte of incoming data;
if (((ch += 0xC6) & 0x80) || !((ch += 0xF9) & 0x80)) {
ch += 0x0A;
}
if (ch > 15) {
; // handle error
}
也许使用 ctype.h 的isxdigit()
:
if( isxdigit( ch ) )
{
ch -= (ch >= 'A') ? ('A' - 10) : '0' ;
}
else
{
// handle error
}
这是否更小将在很大程度上取决于isxdigit
的实现,也许还有编译器和处理器架构,但值得一试,而且更具可读性。 isxdigit()
通常是一个宏,因此没有函数调用开销。
另一种方法是无条件执行转换,然后检查结果的范围:
ch -= (ch >= 'A') ? ('A' - 10) :
(ch >= '0') ? '0' : 0 ;
if( ch > 0xf )
{
// handle error
}
我怀疑后者会更小,但在某些情况下,修改错误时ch
可能没有帮助,例如报告原始值时出错。