
//
//  Fast Long Nibble Rotate Right
//
//  Author:     Graham Cole
//
//  Input:      l       -   32-bit word R4/R5/R6/R7.
//
//  Output:     l       -   32-bit word R4/R5/R6/R7
//
//  Function:   l is rolled four bits to the left.
//
//  Notes:      Very useful for ASCII-Hexadecimal conversions etc.
//

#pragma ASM

$REGUSE _fast_long_nibble_rotate_right( A, R0, R4, R5, R6, R7 )

#pragma ENDASM

unsigned long int fast_long_nibble_rotate_right( unsigned long int l )
{
    #pragma ASM
                                    ;
        MOV     A,PSW               ;Get the base address of 
        ANL     A,#0x18             ; the current register bank.
        ORL     A,#0x07             ;Point to register R7.
        MOV     R0,A                ;R0 is a pointer to R7.
                                    ;
                                    ;rotate value 4 bits left.
                                    ;
                                    ;Acc R4R5R6R7
                                    ;xx  ABCDEFGH
        XCH     A,R4                ;AB  xxCDEFGH
        SWAP    A                   ;ba  xxCDEFGH
        XCH     A,R5                ;CD  xxbaEFGH
        SWAP    A                   ;dc  xxbaEFGH
        XCH     A,R6                ;EF  xxbadcGH
        SWAP    A                   ;fe  xxbadcGH
        XCH     A,R7                ;GH  xxbadcfe
        SWAP    A                   ;hg  xxbadcfe
        XCHD    A,@R0               ;he  xxbadcfg - R0 points to R7
        DEC     R0                  ;
        XCHD    A,@R0               ;hc  xxbadefg - R0 points to R6
        DEC     R0                  ;
        XCHD    A,@R0               ;ha  xxbcdefg - R0 points to R5
        XCH     A,R4                ;xx  habcdefg
        
    #pragma ENDASM

    return( l );
}

//
//  Compact Binary To Uppercase ASCII Hexadcimal.
//
//  Author: Graham Cole
//
//  This function converts a value (in LS nibble of R7) in the range 0...15 to
//  the equivalent ASCII hexadacimal character in the range:
//
//      '0'-'9' or 'A'-'F'
//
//  The returned hexadecimal character is returned in the accumulator and, for
//  Keil compatibility, in the register R7.
//
//  An alternative entry point is provided for calls with the value in the LS
//  nibble of the accumulator.
//
//  The first line of C code will suppress the UNUSED variable warning and the 
//  Keil C51 compiler will optimise this code out. The return() statement is 
//  compiled to the RET instruction.
//
//  The trick of using a combination of ADD and DA instructions to perform this 
//  conversion is an old one that I belive predates the 8051 and, as far as I 
//  know, the original author is lost to history. The technique is compact and
//  only slightly slower than the table look-up method.
//

#pragma ASM

    $REGUSE _compact_binary_to_uppercase_ascii_hexadcimal( A, PSW, R7 )

#pragma ENDASM

char compact_binary_to_uppercase_ascii_hexadcimal( char value )
{
    value = value;                          // Supress UNUSED warning.

    #pragma ASM

        MOV     A,R7                        ;
                                            
compact_binary_to_uppercase_ascii_hexadcimal:
                                            
        ANL     A,#0x0F                     ;
        ADD     A,#0x90                     ;
        DA      A                           ;
        ADDC    A,#0x40                     ;
        DA      A                           ;
        MOV     R7,A                        ;

    #pragma ENDASM

    return( value );
}

