# Macros to convert signed decimal numbers to two's complement hexadecimal # notation. This version does not use machine arithmetic; it does everything # with strings, so is able to handle integers of any size. As written it # accommodates word sizes of 4, 8, 16, 32, 64, and 128 bits. Other word # sizes can be added by changing the definitions at the top. # # Because machine arithmetic is not used, this version is considerably slower # than the first one but it works for numbers that don't fit in 32 bits in # Kermit 95 and in 32-bit versions of C-Kermit. # # F. da Cruz, Columbia University, 7 January 2008 .legal = :4:8:16:32:64:128: # Legal word sizes # Largest positive integer for the supported word sizes .maxint<4> = 7 .maxint<8> = 127 .maxint<16> = 32767 .maxint<32> = 2147483647 .maxint<64> = 9223372036854775807 .maxint<128> = 340282366920938463463374607431768211455 # Ditto plus one (because we can't necessarily do arithmetic on big numbers) .maxplusone<4> = 8 .maxplusone<8> = 128 .maxplusone<16> = 32768 .maxplusone<32> = 2147483648 .maxplusone<64> = 9223372036854775808 .maxplusone<128> = 340282366920938463463374607431768211456 # Powers of 16 (need to add mor to get up to 128 bits) local &p dcl \&p[] = 16 - 256 - 4096 - 65536 - 1048576 - 16777216 - 268435456 - 4294967296 - 68719476736 - 1099511627776 - 17592186044416 - 281474976710656 - 4503599627370496 - 72057594037927936 - 1152921504606846976 - 18446744073709551616 .\&p[0] = 1 # 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 .\%1 := \flpad(\%1,\%2,0) # Pad to size if necessary 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 } def DIV2 { # Divide decimal string by two local \%i undef \%6 \%7 result for \%i 1 \flen(\%1) 1 { # One digit at a time. .\%9 := \%7\:(\%1[\%i:1]) # Get a digit. .\%8 ::= \%9/2 # Divide it by 2 .\%7 ::= \%9%2 # Get remainder .\%6 := \%6\%8 # Accumulate result } .result := \%6 # Make result .remainder := \%7 # and remainder visible } def DTOB { # Convert decimal string to binary while true { # without using machine div2 \%1 # arithmetic. .\%6 := \m(remainder)\%6 .\%1 := \m(result) if not \fverify(0,\%1) break } return \%6 } # Macro DECTOHEX converts a signed decimal number to 2's complement hex, # using the macros defined above as workers. # \%1 = decimal number string (default 0) # \%2 = word size in bits # (must be a power of two, 4 or greater, default 32, max 128) # Returns result in \v(result) and/or as value of \fexec(dectohex arg arg). # define DECTOHEX { local digits 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 if not \findex(:\%2:,\m(legal)) end 1 "UNSUPPORTED WORD SIZE - \%2" .digits := \flen(\m(maxint<\%2>)) # Number of digits in it if eq "\fsubstr(\%1,1,1)" "+" .\%1 := \fsubstr(\%1,2) # strip any + sign if not eq "\fsubstr(\%1,1,1)" "-" { # If argument is not signed... if lgt \flpad(\%1,\m(digits),0) \m(maxint<\%2>) return OVERFLOW dtob \%1 # Convert from decimal to binary bintohex \v(return) \%2 # And from binary to hex return \flpad(\v(return),(\%2/4),0) # Return padded result } .\%1 := \fsubstr(\%1,2) # Negative number - remove sign .\%1 := \flpad(\%1,\flen(\m(maxint<\%2>)),0) # Must use lexical comparison if llt \m(maxplusone<\%2>) \%1 return UNDERFLOW # Check magnitude dtob \%1 # Convert to binary .\%9 := \flpad(\v(return),\%2,0) # and pad .\%8 ::= \frindex(1,\%9) - 1 # Find first 1 on the right if == \%8 -1 return \frepeat(0,\%2 / 4) # Watch out for negative 0 .\%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 } # Returns the 2's complement of the given binary string define TWOSCOMPLEMENT { .\%2 = \flen(\%1) .\%9 := \flpad(\%1,\%2,0) .\%8 ::= \frindex(1,\%9) - 1 # Find first 1 on the right if == \%8 -1 return \frepeat(0,\%2 / 4) # Watch out for negative 0 .\%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 return \%5 } # Converts hex string to binary define HEXTOBIN { undef \%7 for \%9 1 \flen(\%1) 1 { .\%7 := \%7\flpad(\fradix(\:(\%1[\%9:1]),16,2),4,0) } return \%7 } # Not used - Could be used for optimization if necessary def IS32BIT { .\%1 := \flpad(\ftrim(\%1,0),10,0) if lgt \%1 2147483647 return 0 return 1 } # Add two unsigned decimal strings regardless of magnitude # def DECIMALADD { local \%s \%c .\%9 := \fmax(\flen(\%1),\flen(\%2)) .\%1 := \flpad(\%1,\%9,0) .\%2 := \flpad(\%2,\%9,0) .\%c = 0 undef \%s for \%9 \flen(\%1) 1 -1 { .\%6 := \:(\%1[\%9:1]) .\%7 := \:(\%2[\%9:1]) increment \%6 \%7+\%c .\%5 ::= \%6 % 10 .\%c ::= \%6 / 10 .\%s := \%5\%s } if \%c .\%s := \%c\%s return \%s } # Multitply two decimal strings regardless of magnitude by repetitive # addition. Practical only when one of the factors is small and the other # one is (or the result would be) bigger than the hardware integer size. # def DECIMALTIMES { local \%s \%c if > \%1 \%2 { .\%9 := \%1, .\%1 := \%2, .\%2 := \%9 } .\%s = 0 for \%9 1 \%1 1 { .\%s := \fexec(decimaladd \%2 \%s) } return \%s } # Convert a two's complement hexadecimal string into a signed # decimal string. Currently works only up 64 bit word sizes, but that's # just a matter of adding more definitions at the top plus a few minor # adjustments. Does not use machine arithmetic except where it is known # to be safe, so works for 64-bit quantities on 32-bit architectures. # define HEXTODEC { local digits \%b \%d \%p .digits := \flen(\%1) if not \m(digits) end 1 "\%0: NO ARGUMENT GIVEN" .\%1 := \fupper(\%1) if \fverify(0123456789ABCDEF,\%1) end 1 "\%0: '\%1' NOT A HEX STRING" if not \findex(\:(\%1[1:1]),89ABCDEF) { # Positive number .\%d = 0 .\%p = 0 for \%9 \flen(\%1) 1 -1 { # Loop through digits right to left .\%6 := \:(\%1[\%9:1]) # Hex digit .\%6 := \fradix(\%6,16,10) # Decimal value .\%6 := \fexec(decimaltimes \&p[\%p] \%6) # Times power of 16 .\%d := \fexec(decimaladd \%6 \%d) # Add to result increment \%p # Next power of 16 } return \%d } # Negative number (bit 0 set) if = 1 \findex(\%1,800000000000000000000000000) { # Special case .\%b ::= \flen(\%1) * 4 # Look this one up in our table .\%b := \m(maxplusone<\%b>) # to avoid infinite recursion if not def \%b .\%b = ERROR return -\%b } .\%b := \fexec(hextobin \%1) # Normal case - convert to binary .\%7 := \flen(\%b) # Get length of binary .\%b := \fexec(twoscomplement \%b) # Get two's complement .\%b := \fexec(bintohex \%b \%7) # Convert to hex .\%d := \fexec(hextodec \%b) # Call ourselves to convert to decimal return -\%d } # Test HEXTODEC... echo **************** echo TESTING HEXTODEC... echo **************** def try { .t1 := \v(ftime) # Current time .result := \fexec(hextodec \%1) .t2 := \v(ftime) # New time (setq t3 (- t2 t1)) # Difference echo HEX \%1 = DEC \m(result) [\ffpround(\m(t3),2) sec] # Print } try 0 try 7 try 8 try F try 1234 try 80 try 83 try xxx try ffff try 8000 try 8001 try 5555 try 12345 try fffffffffffffffe # TEST DECTOHEX... echo **************** echo TESTING DECTOHEX... echo **************** def try { # Note \v(time) lacks the fractional part in Windows for some reason. .t1 := \v(ftime) # Current time .result := \fexec(dectohex \%1 \%2) # Do the conversion .t2 := \v(ftime) # New time (setq t3 (- t2 t1)) # Difference echo \%1[\%2] = \m(result) [\ffpround(\m(t3),2) sec] # Print } try 7 # No word size specified try 7 4 # 4-bit word try 8 4 try -8 4 try -9 4 try 99 4 try 0 8 # 8-bit word try -0 8 try 1 8 try +1 8 try 2 8 try 3 8 try 4 8 try 5 8 try 6 8 try 7 8 try -1 8 try -2 8 try -3 8 try -4 8 try -5 8 try -6 8 try -7 8 try -8 8 try 64 8 try 65 8 try -128 8 try 0 16 # 16-bit word try 64 16 try 65 16 try -128 16 try -32768 16 try 99999 16 try -99999 16 try 0 32 # 32-bit word try 1 32 try 16383 32 try 2147483647 32 try -1 32 try -2 32 try -2147483647 32 try -2147483648 32 try 0 64 # 64-bit word try 2147483647 64 try -1 64 try -2 64 try 1234567890 64 try -2147483647 64 try -2147483648 64 try 8224373093854475231 64 try 0 128 # 128-bit word try 1 128 try -1 128 try -2 128 try 317282366902934863643347307341786875499 128 if c-kermit exit