How to reduce the code space for a hexadecimal ASCII chars conversion using a _small_ code space?

ぐ巨炮叔叔 提交于 2019-11-28 13:39:46

PIC16 family is RISC MCPU, so you can try to optimize your asm code. This is your c compilers asm code...

    decf    ch, f
    btfsc   ch, 6
    goto    Skip
    movlw   07
    addwf   ch, w
    andlw   0xBF
    movwf   ch
Skip    
    movlw   0x36
    subwf   ch, f

This is my optimization of upper code...

   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

...so only 7 opcodes (2 less) without range error check and 10 opcodes with range error check!

EDIT: Try also this PIC16 c compiler optimized function, not sure if works...

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 
}

EDIT II: added version which is compatible with older PIC16 MCPUs not made in XLP technology, but code size is one opcode longer.

    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

EDIT III: explanation

The 'D Kruegers' solution is also very good, but need some modification...

This code..

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

...we can translate to...

  if (((ch -= ('0' + 10)) < 0) || ((ch -= ('A' - '0' - 10)) >= 0)) {
    ch += 10; 
  }

After that we can optimize in 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

With good compiler optimization, this may take less code space:

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

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

if (ch > 15) { 
  ; // handle error
}

Perhaps using ctype.h's isxdigit():

if( isxdigit( ch ) )
{
    ch -= (ch >= 'A') ? ('A' - 10) : '0' ;
}
else
{
    // handle error
}

Whether that is smaller or not will depend largely on the implementation of isxdigit and perhaps your compiler and processor architecture, but worth a try and far more readable. isxdigit() is normally a macro so there is no function call overhead.

An alternative is to perform the transformation unconditionally then check the range of the result:

ch -= (ch >= 'A') ? ('A' - 10) : 
                    (ch >= '0') ? '0' : 0 ;
if( ch > 0xf )
{
    // handle error
}

I suspect this latter will be smaller, but modification of ch on error may be unhelpful in some cases, such as error reporting the original value.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!