home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / ckscripts / hextodec < prev    next >
Text File  |  2020-01-01  |  10KB  |  337 lines

  1. # Macros to convert signed decimal numbers to two's complement hexadecimal
  2. # notation.  This version does not use machine arithmetic; it does everything
  3. # with strings, so is able to handle integers of any size.  As written it
  4. # accommodates word sizes of 4, 8, 16, 32, 64, and 128 bits.  Other word
  5. # sizes can be added by changing the definitions at the top.
  6. # Because machine arithmetic is not used, this version is considerably slower
  7. # than the first one but it works for numbers that don't fit in 32 bits in
  8. # Kermit 95 and in 32-bit versions of C-Kermit.
  9. #
  10. # F. da Cruz, Columbia University, 7 January 2008
  11.  
  12. .legal = :4:8:16:32:64:128:             # Legal word sizes
  13.  
  14. # Largest positive integer for the supported word sizes
  15.  
  16. .maxint<4> = 7
  17. .maxint<8> = 127
  18. .maxint<16> = 32767
  19. .maxint<32> = 2147483647
  20. .maxint<64> = 9223372036854775807
  21. .maxint<128> = 340282366920938463463374607431768211455
  22.  
  23. # Ditto plus one (because we can't necessarily do arithmetic on big numbers)
  24.  
  25. .maxplusone<4> = 8
  26. .maxplusone<8> = 128
  27. .maxplusone<16> = 32768
  28. .maxplusone<32> = 2147483648
  29. .maxplusone<64> = 9223372036854775808
  30. .maxplusone<128> = 340282366920938463463374607431768211456
  31.  
  32. # Powers of 16 (need to add mor to get up to 128 bits)
  33.  
  34. local &p
  35. dcl \&p[] = 16 -
  36.  256 -
  37.  4096 -
  38.  65536 -
  39.  1048576 -
  40.  16777216 -
  41.  268435456 -
  42.  4294967296 -
  43.  68719476736 -
  44.  1099511627776 -
  45.  17592186044416 -
  46.  281474976710656 -
  47.  4503599627370496 -
  48.  72057594037927936 -
  49.  1152921504606846976 -
  50.  18446744073709551616
  51. .\&p[0] = 1
  52.  
  53. # Macro BINTOHEX converts a binary string to hex.
  54. #   \%1 = binary number (string)
  55. #   \%2 = word size in bits
  56. # \fradix() is constrained by machine integer word length
  57. # so we do it in pieces in case the number is too big.
  58. #
  59. define BINTOHEX {
  60.     undef \%6                                # Result accumulator
  61.     .\%1 := \flpad(\%1,\%2,0)                # Pad to size if necessary
  62.     for \%9 1 \%2 4 {                        # Do four bits at at a time
  63.         .\%8 := \fsubstr(\%1,\%9,4)          # Get chunk of 4
  64.         if not def \%8 break                 # Make sure we have one
  65.         .\%7 := \fradix(\%8,2,16)            # Convert to Hex digit
  66.         .\%6 := \%6\%7                       # Accumulate
  67.     }
  68.     return \%6
  69. }
  70.  
  71. def DIV2 {                                   # Divide decimal string by two
  72.     local \%i
  73.     undef \%6 \%7 result
  74.     for \%i 1 \flen(\%1) 1 {                 # One digit at a time.
  75.         .\%9 := \%7\:(\%1[\%i:1])            # Get a digit.
  76.         .\%8 ::= \%9/2                       # Divide it by 2
  77.         .\%7 ::= \%9%2                       # Get remainder
  78.         .\%6 := \%6\%8                       # Accumulate result
  79.     }
  80.     .result := \%6                           # Make result 
  81.     .remainder := \%7                        # and remainder visible
  82. }
  83.  
  84. def DTOB {                                   # Convert decimal string to binary
  85.     while true {                             # without using machine 
  86.         div2 \%1                             # arithmetic.
  87.         .\%6 := \m(remainder)\%6
  88.         .\%1 := \m(result)                    
  89.         if not \fverify(0,\%1) break
  90.     }        
  91.     return \%6
  92. }
  93. # Macro DECTOHEX converts a signed decimal number to 2's complement hex,
  94. # using the macros defined above as workers.
  95. #   \%1 = decimal number string (default 0)
  96. #   \%2 = word size in bits
  97. #         (must be a power of two, 4 or greater, default 32, max 128)
  98. #   Returns result in \v(result) and/or as value of \fexec(dectohex arg arg).
  99. #
  100. define DECTOHEX {
  101.     local digits
  102.     if not def \%1 .\%1 = 0                  # Supply default if no arg given
  103.     if not numeric \%1 return NOT_A_NUMBER:\%1  # Check that arg is a number
  104.     if not def \%2 .\%2 := 32                   # Use 32 bits if no second arg
  105.     if not \findex(:\%2:,\m(legal)) end 1 "UNSUPPORTED WORD SIZE - \%2"
  106.     .digits := \flen(\m(maxint<\%2>))           # Number of digits in it
  107.  
  108.     if eq "\fsubstr(\%1,1,1)" "+" .\%1 := \fsubstr(\%1,2) # strip any + sign
  109.     if not eq "\fsubstr(\%1,1,1)" "-" {      # If argument is not signed...
  110.         if lgt \flpad(\%1,\m(digits),0) \m(maxint<\%2>) return OVERFLOW
  111.         dtob \%1                             # Convert from decimal to binary
  112.         bintohex \v(return) \%2              # And from binary to hex
  113.         return \flpad(\v(return),(\%2/4),0)  # Return padded result
  114.     }
  115.     .\%1 := \fsubstr(\%1,2)                  # Negative number - remove sign
  116.     .\%1 := \flpad(\%1,\flen(\m(maxint<\%2>)),0) # Must use lexical comparison
  117.     if llt \m(maxplusone<\%2>) \%1 return UNDERFLOW # Check magnitude
  118.     dtob \%1                                 # Convert to binary
  119.     .\%9 := \flpad(\v(return),\%2,0)         # and pad
  120.     .\%8 ::= \frindex(1,\%9) - 1             # Find first 1 on the right
  121.     if == \%8 -1 return \frepeat(0,\%2 / 4)  # Watch out for negative 0
  122.     .\%7 := \fsubstr(\%9,1,\%8)              # Split string here
  123.     .\%6 := \fsubstitute(\%7,01,10)          # Complement bits in left part
  124.     .\%5 := \%6\fsubstr(\%9,\%8+1)           # Put back with right part
  125.     .\%4 := \fexec(bintohex \%5 \%2)         # Convert to hex
  126.     return \%4
  127. }
  128.  
  129. # Returns the 2's complement of the given binary string
  130. define TWOSCOMPLEMENT {
  131.     .\%2 = \flen(\%1)
  132.     .\%9 := \flpad(\%1,\%2,0)
  133.     .\%8 ::= \frindex(1,\%9) - 1             # Find first 1 on the right
  134.     if == \%8 -1 return \frepeat(0,\%2 / 4)  # Watch out for negative 0
  135.     .\%7 := \fsubstr(\%9,1,\%8)              # Split string here
  136.     .\%6 := \fsubstitute(\%7,01,10)          # Complement bits in left part
  137.     .\%5 := \%6\fsubstr(\%9,\%8+1)           # Put back with right part
  138.     return \%5
  139. }
  140.  
  141. # Converts hex string to binary
  142. define HEXTOBIN {
  143.     undef \%7
  144.     for \%9 1 \flen(\%1) 1 {
  145.         .\%7 := \%7\flpad(\fradix(\:(\%1[\%9:1]),16,2),4,0)
  146.     }
  147.     return \%7    
  148. }
  149.  
  150. # Not used - Could be used for optimization if necessary
  151. def IS32BIT { 
  152.     .\%1 := \flpad(\ftrim(\%1,0),10,0)
  153.     if lgt \%1 2147483647 return 0
  154.     return 1
  155. }
  156.  
  157. # Add two unsigned decimal strings regardless of magnitude
  158. #
  159. def DECIMALADD {
  160.     local \%s \%c
  161.     .\%9 := \fmax(\flen(\%1),\flen(\%2))
  162.     .\%1 := \flpad(\%1,\%9,0)
  163.     .\%2 := \flpad(\%2,\%9,0)
  164.     .\%c = 0
  165.     undef \%s
  166.     for \%9 \flen(\%1) 1 -1 {    
  167.         .\%6 := \:(\%1[\%9:1]) 
  168.         .\%7 := \:(\%2[\%9:1]) 
  169.         increment \%6 \%7+\%c
  170.     .\%5 ::= \%6 % 10
  171.     .\%c ::= \%6 / 10
  172.     .\%s := \%5\%s
  173.     }
  174.     if \%c .\%s := \%c\%s
  175.     return \%s
  176. }
  177.  
  178. # Multitply two decimal strings regardless of magnitude by repetitive
  179. # addition.  Practical only when one of the factors is small and the other
  180. # one is (or the result would be) bigger than the hardware integer size.
  181. #
  182. def DECIMALTIMES {
  183.     local \%s \%c
  184.     if > \%1 \%2 { .\%9 := \%1, .\%1 := \%2, .\%2 := \%9 }
  185.     .\%s = 0
  186.     for \%9 1 \%1 1 {
  187.         .\%s := \fexec(decimaladd \%2 \%s)
  188.     }    
  189.     return \%s
  190. }
  191.  
  192. # Convert a two's complement hexadecimal string into a signed
  193. # decimal string.  Currently works only up 64 bit word sizes, but that's
  194. # just a matter of adding more definitions at the top plus a few minor
  195. # adjustments.  Does not use machine arithmetic except where it is known
  196. # to be safe, so works for 64-bit quantities on 32-bit architectures.
  197. #
  198. define HEXTODEC {
  199.     local digits \%b \%d \%p
  200.     .digits := \flen(\%1)
  201.     if not \m(digits) end 1 "\%0: NO ARGUMENT GIVEN"
  202.     .\%1 := \fupper(\%1)
  203.     if \fverify(0123456789ABCDEF,\%1) end 1 "\%0: '\%1' NOT A HEX STRING"
  204.     if not \findex(\:(\%1[1:1]),89ABCDEF) {  # Positive number
  205.         .\%d = 0
  206.         .\%p = 0
  207.         for \%9 \flen(\%1) 1 -1 {  # Loop through digits right to left
  208.             .\%6 := \:(\%1[\%9:1])                    # Hex digit
  209.             .\%6 := \fradix(\%6,16,10)                # Decimal value
  210.             .\%6 := \fexec(decimaltimes \&p[\%p] \%6) # Times power of 16
  211.             .\%d := \fexec(decimaladd \%6 \%d)        # Add to result
  212.             increment \%p                             # Next power of 16
  213.         }
  214.         return \%d
  215.     }
  216.     # Negative number (bit 0 set)
  217.     if = 1 \findex(\%1,800000000000000000000000000) { # Special case
  218.         .\%b ::= \flen(\%1) * 4         # Look this one up in our table
  219.         .\%b := \m(maxplusone<\%b>)     # to avoid infinite recursion
  220.         if not def \%b .\%b = ERROR
  221.         return -\%b
  222.     }
  223.     .\%b := \fexec(hextobin \%1)        # Normal case - convert to binary
  224.     .\%7 := \flen(\%b)                  # Get length of binary
  225.     .\%b := \fexec(twoscomplement \%b)  # Get two's complement
  226.     .\%b := \fexec(bintohex \%b \%7)    # Convert to hex
  227.     .\%d := \fexec(hextodec \%b)        # Call ourselves to convert to decimal
  228.     return -\%d
  229. }
  230.  
  231. # Test HEXTODEC...
  232.  
  233. echo ****************
  234. echo TESTING HEXTODEC...
  235. echo ****************
  236. def try {
  237.     .t1 := \v(ftime)                    # Current time
  238.     .result := \fexec(hextodec \%1)
  239.     .t2 := \v(ftime)                    # New time
  240.     (setq t3 (- t2 t1))                 # Difference
  241.     echo HEX \%1 = DEC \m(result) [\ffpround(\m(t3),2) sec] # Print
  242. }
  243.  
  244. try 0
  245. try 7
  246. try 8
  247. try F
  248. try 1234
  249. try 80
  250. try 83
  251. try xxx
  252. try ffff
  253. try 8000
  254. try 8001
  255. try 5555
  256. try 12345
  257. try fffffffffffffffe
  258.  
  259. # TEST DECTOHEX...
  260.  
  261. echo ****************
  262. echo TESTING DECTOHEX...
  263. echo ****************
  264. def try {
  265.     # Note \v(time) lacks the fractional part in Windows for some reason.
  266.     .t1 := \v(ftime)                    # Current time
  267.     .result := \fexec(dectohex \%1 \%2) # Do the conversion
  268.     .t2 := \v(ftime)                    # New time
  269.     (setq t3 (- t2 t1))                 # Difference
  270.     echo \%1[\%2] = \m(result) [\ffpround(\m(t3),2) sec] # Print
  271. }
  272. try 7          # No word size specified
  273.  
  274. try  7 4       # 4-bit word
  275. try  8 4
  276. try -8 4
  277. try -9 4
  278. try 99 4
  279.  
  280. try  0 8        # 8-bit word
  281. try -0 8
  282. try  1 8
  283. try +1 8
  284. try  2 8
  285. try  3 8
  286. try  4 8
  287. try  5 8
  288. try  6 8
  289. try  7 8
  290. try -1 8
  291. try -2 8
  292. try -3 8
  293. try -4 8
  294. try -5 8
  295. try -6 8
  296. try -7 8
  297. try -8 8
  298. try 64 8
  299. try 65 8
  300. try -128 8
  301.  
  302. try 0 16       # 16-bit word
  303. try 64 16
  304. try 65 16
  305. try -128 16
  306. try -32768 16
  307. try 99999 16
  308. try -99999 16
  309.  
  310. try 0 32       # 32-bit word
  311. try 1 32
  312. try 16383 32
  313. try 2147483647 32
  314. try -1 32
  315. try -2 32
  316. try -2147483647 32
  317. try -2147483648 32
  318.  
  319. try 0 64       # 64-bit word
  320. try 2147483647 64
  321. try -1 64
  322. try -2 64
  323. try  1234567890 64
  324. try -2147483647 64
  325. try -2147483648 64
  326. try  8224373093854475231 64
  327.  
  328. try 0 128      # 128-bit word
  329. try 1 128
  330. try -1 128
  331. try -2 128
  332. try 317282366902934863643347307341786875499 128
  333.  
  334. if c-kermit exit
  335.