# Macros to display signed decimal numbers in two's complement hexadecimal
# notation.  Because the code underlying most of Kermit's arithmetic and
# numeric comparison functions uses machine arithmetic, it is necessary to 
# employ string operations and lexical comparisons to handle edge cases.
# Works in both C-Kermit (8.0 and later) and K95 (2.0 and later).
#
# F. da Cruz, Columbia University, 19 November 2007

# Macro BINTOHEX converts a binary string to hex.
#   \%1 = binary number (string)
#   \%2 = word size in bits
# 
# \fradix() is constrained by machine integer word length
# so we do it in pieces in case the number is too big.
#
define BINTOHEX {
    undef \%6                                # Result accumulator
    for \%9 1 \%2 4 {                        # Do four bits at at a time
        .\%8 := \fsubstr(\%1,\%9,4)          # Get chunk of 4
        if not def \%8 break                 # Make sure we have one
        .\%7 := \fradix(\%8,2,16)            # Convert to Hex digit
        .\%6 := \%6\%7                       # Accumulate
    }
    return \%6
}
# Macro DECTOHEX converts a signed decimal number to 2's complement hex.
#   \%1 = decimal number string (default 0)
#   \%2 = word size in bits (must be a power of two, 4 or greater, default 32)
#
# Because of how \fradix() works, this function operates correctly only
# for numbers whose absolute value fits in the machine's integer word.
#
define DECTOHEX {
    local m1
    if not def \%1 .\%1 = 0                  # Supply default if no arg given
    if not numeric \%1 return NOT_A_NUMBER:\%1  # Check that arg is a number
    if not def \%2 .\%2 := 32                   # Use 32 bits if no second arg
    (setq m1 (truncate (- (^ 2 (- \%2 1)) 1)))  # Largest positive number
    if eq "\fsubstr(\%1,1,1)" "+" .\%1 := \fsubstr(\%1,2) # strip any + sign
    if not eq "\fsubstr(\%1,1,1)" "-" {         # Argument is signed?
        .\%1 := \flpad(\%1,\flen(\v(svalue)),0) # No - check magnitude
        if lgt \%1 \v(svalue) return OVERFLOW 
        return \flpad(\fradix(\%1,10,16),(\%2 / 4),0) # Convert to hex and pad
    }
    .\%1 := \fsubstr(\%1,2)                  # Negative number - remove sign
    .\%1 := \flpad(\%1,\flen(\m(m1)),0)      # Must use lexical comparison
    (++ m1)                                  # Avoid fencepost error
    if llt \m(m1) \%1 return UNDERFLOW       # Check magnitude
    .\%9 := \flpad(\fradix(\%1,10,2),\%2,0)  # Convert to binary and pad
    .\%8 ::= \frindex(1,\%9) - 1             # Find first 1 on the right
    if == \%8 -1 {                           # Watch out for negative 0
         return \frepeat(0,\%2 / 4)
    }
    .\%7 := \fsubstr(\%9,1,\%8)              # Split string here
    .\%6 := \fsubstitute(\%7,01,10)          # Complement bits in left part
    .\%5 := \%6\fsubstr(\%9,\%8+1)           # Put back with right part
    .\%4 := \fexec(bintohex \%5 \%2)         # Convert to hex
    return \%4
}
# Test the functions...

set take echo on
echo \fexec(dectohex  7)          # No word size specified
echo \fexec(dectohex)

echo \fexec(dectohex  7 4)        # 4-bit word
echo \fexec(dectohex  8 4)
echo \fexec(dectohex -8 4)
echo \fexec(dectohex -9 4)
echo \fexec(dectohex 99 4)

echo \fexec(dectohex  0 8)        # 8-bit word
echo \fexec(dectohex -0 8)
echo \fexec(dectohex  1 8)
echo \fexec(dectohex +1 8)
echo \fexec(dectohex  2 8)
echo \fexec(dectohex  3 8)
echo \fexec(dectohex  4 8)
echo \fexec(dectohex  5 8)
echo \fexec(dectohex  6 8)
echo \fexec(dectohex  7 8)
echo \fexec(dectohex -1 8)
echo \fexec(dectohex -2 8)
echo \fexec(dectohex -3 8)
echo \fexec(dectohex -4 8)
echo \fexec(dectohex -5 8)
echo \fexec(dectohex -6 8)
echo \fexec(dectohex -7 8)
echo \fexec(dectohex -8 8)
echo \fexec(dectohex 64 8)
echo \fexec(dectohex 65 8)
echo \fexec(dectohex -128 8)

echo \fexec(dectohex 0 16)       # 16-bit word
echo \fexec(dectohex 64 16)
echo \fexec(dectohex 65 16)
echo \fexec(dectohex -128 16)
echo \fexec(dectohex -32768 16)
echo \fexec(dectohex 99999 16)
echo \fexec(dectohex -99999 16)

echo \fexec(dectohex 0 32)       # 32-bit word
echo \fexec(dectohex 1 32)
echo \fexec(dectohex 16383 32)
echo \fexec(dectohex 2147483647 32)
echo \fexec(dectohex -1 32)
echo \fexec(dectohex -2 32)
echo \fexec(dectohex -2147483647 32)
echo \fexec(dectohex -2147483648 32)

echo \fexec(dectohex 0 64)       # 64-bit word
echo \fexec(dectohex 2147483647 64)
echo \fexec(dectohex -1 64)
echo \fexec(dectohex -2 64)
echo \fexec(dectohex -2147483647 64)
echo \fexec(dectohex -2147483648 64)

echo \fexec(dectohex 0 128)      # 128-bit word
echo \fexec(dectohex 1 128)
echo \fexec(dectohex -1 128)
echo \fexec(dectohex -2 128)

set take echo off
if c-kermit exit
