6

如何使用小代码空间减少十六进制 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 条指令也是有价值的。我一点一点地经历它。也看过整体重建。

注:PIC16。我更喜欢用“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 指令。无需chif().

4

3 回答 3

5

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 
}

编辑二:添加的版本与不是采用 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; 
  }

之后我们可以在汇编器中优化...

        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
于 2013-06-13T20:28:22.533 回答
4

通过良好的编译器优化,这可能会占用更少的代码空间:

unsigned char ch = GetData(); // Fetch 1 byte of incoming data;

if (((ch += 0xC6) & 0x80) || !((ch += 0xF9) & 0x80)) {
  ch += 0x0A;
}

if (ch > 15) { 
  ; // handle error
}
于 2013-06-15T13:51:30.953 回答
2

也许使用 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在某些情况下修改 on error 可能没有帮助,例如错误报告原始值。

于 2013-06-12T19:59:04.727 回答