PROGRAM castwo; {$nomain} { File:[22,310]CASTWO.PAS Author: Jim Bostwick 17-Oct-83 (From P2 CXTB) Last Edit: 23-JUN-1988 22:09:12 History: 23-JUN-1988 21:55:39 - JMB PA3UTL upgrade. Phil Hannay. 15-Jan-87. Modified overflow handling to return a negative number in POS if there is overflow, and send nothing to terminal. Also ignore leading zeros. } {$NOLIST} {[a+,b+,l-,k+,r+] Pasmat } %INCLUDE 'PAS$EXT:General.typ'; %INCLUDE 'PAS$EXT:Error.ext'; {$LIST} PROCEDURE Castwo(VAR asc: PACKED ARRAY [lo..hi: integer] OF char; VAR Bin: Word; VAR pos: integer; radix: integer); EXTERNAL; {*USER* Pascal-3 procedure which converts an input string of ASCII characters in ASC to an unsigned binary word output in BIN using the given radix input in RADIX. POS specifies starting point in ASC on entry, and points to the terminator character or end of ASC (whichever is encountered first) on exit. POS will be return as a negative value if word overflow occurs (exceeds 65535) and so this routine can be used to verify the validity of an input. Leading space, tab and zeros are ignored. Any non-digit following the first encountered digit is treated as a terminator, and of course, the upper limit of the string in ASC will be treated as the terminator. If the number contained in ASC contains a digit that is not valid for the supplied RADIX value, then that digit will be treated as a "non-digit" and thus a terminating character. You can use this behavior to determine if the conversion terminated earlier than expected, and thus implying that the ascii string in ASC is not a valid number. } {*WIZARD* The input string is converted to a bit pattern in Bin. Up to 16 characters are converted, based upon the specified radix. Only the last n characters before the terminator are converted, with n being determined such that integer overflow (unsigned) cannot occur. If more characters are in the input string, they are ignored. Leading blanks and tabs are ignored, but other leading characters (including '+') will be treated as terminators. Bin will always return a legitimate value, 0...65535. The input radix is limited to 0..10, and 16 (HEX). Warning - DIV operation with word exceeding 77777b (32767.) will result in a zero for an answer. Compares work find with words with 177777b being greater than 77777b. } PROCEDURE Castwo; LABEL 999; { used for premature exit on error } const Debug = false; VAR i, Bpos, Limit, Index, Dig: integer; Rad, Accum, OldAccum, Mult, oldbin, oldmult: Word; Maxch: char; Digits: SET OF char; {legal digits for selected radix} Overflow: boolean; BEGIN {Initialize some stuff.} Bin := 0; Rad := radix; {need it as unsigned } Overflow:= false; { do some preliminary error checks } IF (radix < 2) OR (radix > 16) THEN BEGIN error(3, warning_err, 'CASTWO -- Illegal radix:', radix); GOTO 999 END ELSE IF ((lo = 0) AND (pos < 1)) OR (pos > hi) THEN BEGIN error(4, warning_err, 'CASTWO -- Illegal postition:', pos); GOTO 999 END; if debug then writeln('radix=',radix); IF radix <= 10 THEN BEGIN maxch := chr(60B - 1 + radix); {max ASCII digit for this radix} digits := ['0'..maxch] END ELSE BEGIN maxch := chr(ord('A') - 1 + radix); digits := ['0'..'9', 'A'..maxch] END; if lo = 0 then limit := ord(asc[0]) else limit := hi; { skip leading spaces,tabs, and zeros } Bpos := pos; while (Bpos < limit) and (ord(asc[bpos]) in [40b,11b,60b]) do bpos := bpos + 1; if ((bpos = limit) and not( asc[bpos] in digits)) THEN BEGIN { nothing to convert } if debug then writeln('no bpos found'); pos := limit; GOTO 999 END; {Now search for the terminator character} index := bpos; while (asc[index] in digits) and (index < limit) do index := index + 1; { here, index is either Hi and there is no terminator (eoln), or index points to terminator } pos := index; { set return index } if not(asc[index] in digits) then index := index - 1; IF index < bpos then BEGIN {no valid characters } if debug then writeln('no valid characters'); goto 999 END; if debug then BEGIN write('range to convert ='); for i := bpos to index do write(asc[i]); writeln END; {do the conversion} mult := 1; oldbin:= 0; oldmult:= 0; WHILE (Index >= Bpos) DO BEGIN IF asc[Index] IN ['A'..'F'] THEN DIG := (ord(asc[Index]) - 67B) ELSE DIG := (ord(asc[Index]) - 60B); if debug then writeln('index =',index,' mult =', mult:-6, ' this digit=', dig:1, ' bin before add=', bin:-6); accum:= 0; oldaccum:= 0; while not(overflow) and (dig > 0) do begin accum:=accum+mult; if accum < oldaccum then begin if debug then writeln('overflow in accum'); overflow:=true end else begin oldaccum:= accum; dig:= dig - 1; end; end; if not(overflow) then begin bin:= bin + accum; if debug then writeln('oldbin=',oldbin:-6,' bin after add=',bin:-6); if bin < oldbin then begin { overflow occurred} if debug then writeln('overflow in bin after adding dig'); overflow:= true; end else begin { go to next digit } oldbin:= bin; index := index - 1; if index >= bpos then begin { compute next multiplier - we must add rather that use multiply since we may not detect overflow if we multiply.} i:= radix; accum:= mult; oldaccum:= mult; while not(overflow) and (i>1) do begin accum:=accum+mult; if accum < oldaccum then begin if debug then writeln('overflow in mult'); overflow:=true end else begin oldaccum:= accum; i:= i - 1; end; end; if not(overflow) then mult:= accum; end; end; end; If overflow then BEGIN { overflow - make POS negative } pos:= pos * (-1); if debug then writeln ('overflow occurred, pos=',pos:1); GOTO 999 {force termination rather than overflow} END; END; 999: END;