'fmt.bas by Michael Fellinger (mwf@rmi.net) ' 'For the BasicX family. Please report bugs to the basicx mailing list. That way users can ' hear about them sooner than if you tell me. I don't get to play with this every day. I ' will fix them or explain why they aren't a bug as soon as I can. ' 'This is a beta release that only supports decimal I/O of byte, integer, long, unsigned integer ' and unsigned long types. Support of floating point with fixed decimal, engineering, and ' scientific formats is planned followed by support of hex and binary formats for fixed types. ' Priority of implimentation is subject to my schedule and your requests. ' 'This module supplies subroutines that format numbers into ascii and place the character ' string into a specified queue and subroutines that take characters from a specified ' queue and build numbers. These routines work well with the serial ports but can work with ' anything if you write a routine to handle the "other end" of the queues. ' 'For the output routines you specify the queue to use (eg the serial port output queue defined ' when you opened the serial port) followed by the number to output and finally the number of ' character spaces you want the number to use. The latter allows stable formats that don't move ' around as the number changes. For signed numbers don't forget that the sign must appear in ' the allotted space. ' 'For the input routines you specify the queue to get the string from and the queue to echo the ' characters. The routines return the number. Any white space is skipped before the number. ' An optional sign and any number of digits are accepted. Any other character terminates the ' number and is left in the queue. If the number terminates in white space the next number will ' be read. If a printing character terminates the number YOU must remove it before calling the ' input routines again or you will simply stall with no new characters accepted. ' 'Please read the function headers for more information. Private chrs as byte 'The FmtUL routine formats unsigned long variables to decimal character strings. The size needed ' is normally 10 for no leading space, longer if you want leading space. Shorter sizes may ' overflow but this is handled like Excel with the field filled with # to indicate the number ' doesn't fit. Sub FmtUL(byref que() as byte, byval val as unsignedlong, byval size as byte) Call fmt(que, val, size, false) End Sub 'The ScnUL routine reads an unsigned long number from the queue of ASCII text. Function ScnUL(byref que() as byte, byref echo() as byte) as unsignedlong set ScnUL=new unsignedlong call ws(que, echo) ScnUL=scan(que, echo) End Function 'The FmtL routine formats signed long variables to decimal character strings. The normal size is ' 11 to include space for the minus sign. Sub FmtL(byref que() as byte, byval val as long, byval size as byte) Dim value as new unsignedlong Dim sign as boolean if val < 0 then value=culng(-val) sign=true else value=culng(val) sign=false end if call fmt(que, value, size, sign) end sub 'The ScnL function reads a signed long from the specified queue. Function ScnL(byref que() as byte, byref echo() as byte) as long dim sign as boolean call ws(que, echo) sign=getsgn(que, echo) ScnL=Clng(scan(que, echo)) if sign then ScnL=-ScnL end if End Function 'The FmtUI routine formats unsigned integers. The normal size is 5. Sub FmtUI(byref que() as byte, byval val as unsignedinteger, byval size as byte) Call fmt(que, Culng(val), size, false) end sub 'The ScnUI function reads unsigned integers. Function ScnUI(byref que() as byte, byref echo() as byte) as unsignedinteger set ScnUI=new unsignedinteger call ws(que, echo) ScnUI=CUInt(scan(que, echo)) End Function 'The FmtI routine formats signed integers. The normal size is 6. Sub FmtI(byref que() as byte, byval val as integer, byval size as byte) Dim value as new unsignedlong Dim sign as boolean if val < 0 then value=culng(-val) sign=true else value=culng(val) sign=false end if call fmt(que, value, size, sign) end sub 'The ScnI function reads a signed integer from the specified queue. Function ScnI(byref que() as byte, byref echo() as byte) as integer dim sign as boolean call ws(que, echo) sign=getsgn(que, echo) ScnI=Cint(scan(que, echo)) if sign then ScnI=-ScnI end if End Function 'The FmtB routine formats unsigned bytes. The normal size is 3. Sub FmtB(byref que() as byte, byval val as byte, byval size as byte) Call fmt(que, Culng(val), size, false) end sub 'The ScnB function reads unsigned bytes. Function ScnB(byref que() as byte, byref echo() as byte) as byte call ws(que, echo) ScnB=Cbyte(scan(que, echo)) End Function 'The FmtF routine formats single precision floating point numbers. Size is the total field ' width. If possible, the format is fixed point. Otherwise, the format is scientific. The ' normal size is 14 bytes. Sub FmtF(byref que() as byte, byval val as single, byval size as byte) dim exp_part as single dim number as single dim count as byte dim point as byte dim sign as boolean point=Asc(".") exp_part=fix(Log10(abs(val))+1.0) If abs(exp_part)>Csng(size-2) then ' scientific call putqueuestr(que,"scientific") call fmtl(que, clng(exp_part), 5) else ' fixed If val < 0.0 Then sign = true number=-val else sign = false number=val end if count=Cbyte(exp_part)+1 call FmtL(que, Clng(fix(val)), count) call putqueue(que,point,1) count=size-count-1 number=number-fix(number) number=number*exp10(csng(count)) call FmtUL(que, CUlng(Clng(fix(number))), count) end if end sub 'The ScnS function reads single precision floating point. The format of the number must be ' [sgn]#[.#][E[sgn]#] where the bracketed parts are optional. This liberal rule should cover ' any likely input. Function ScnS(byref que() as byte, byref echo() as byte) as single dim sign as boolean dim exsgn as boolean dim int_part as single dim frac_part as single dim exp_part as single dim tmp as byte int_part=0.0 frac_part=0.0 exp_part=0.0 call ws(que, echo) sign=getsgn(que, echo) int_part=Csng(scan(que,echo)) call peekqueue(que, tmp, 1) if tmp=46 then 'decimal point call getqueue(que, tmp, 1) call putqueue(echo, tmp, 1) frac_part=Csng(scan(que,echo)) frac_part=exp10(-Csng(chrs))*frac_part end if call peekqueue(que, tmp, 1) if (tmp=69) or (tmp=101) then call getqueue(que, tmp, 1) call putqueue(echo, tmp, 1) exp_part=Csng(ScnI(que,echo)) end if ScnS=int_part+frac_part if sign then ScnS=-ScnS end if ScnS=ScnS*exp10(exp_part) end function 'This is the end of the normal user routines. Following this are the routines that do the ' real work. In special circumstances they may be of use so I haven't made them private. 'The fmt() subroutine does all the dirty work of formatting. The primary user routines call this ' with the sign and magnitude of the number separated. Sub fmt(byref que() as byte, byval val as unsignedlong, byval size as byte, byval sign as boolean) dim value as new unsignedlong dim flag as boolean 'indicates first significant digit found dim count as byte 'potential digits processed dim digit as byte 'digit extracted from val dim place as long 'a 1 in the digit place being processed dim i as integer value=val flag=False count=10 if sign then count=count+1 end if place=1000000000 Do While place >= 1 digit=48 '0 Do While value >= Culng(place) value=value-Culng(place) digit=digit+1 Loop If digit = 48 then if ((count=1)and not sign) or ((count=2) and sign) then if not flag then for i=1 to Cint(size-count+1) call putqueuestr(que, " ") next end if flag=true end if if flag then call putqueue(que, digit, 1) end if else if count > size then for i=1 to cint(size) call putqueuestr(que, "#") next exit sub else if count < size then if not flag then for i=1 to cint(size-count) call putqueuestr(que, " ") next end if end if if not flag then if sign then call putqueuestr(que, "-") end if end if flag=True call putqueue(que, digit, 1) end if end if place=place\10 count=count-1 loop End Sub 'The scan() subroutine builds unsigned numbers from characters. It is not normally called ' by the user. Function scan(byref que() as byte, byref echo() as byte) as unsignedlong set scan=new unsignedlong dim digit as byte dim temp as new unsignedlong dim num as new unsignedlong num=0 chrs=0 Do call peekqueue(que, digit, 1) if(digit>47) and (digit<58) then call getqueue(que, digit, 1) call putqueue(echo, digit, 1) num=num+num temp=num num=num+num num=num+num+temp+Culng(digit)-48 chrs=chrs+1 else scan=num exit function end if loop end function 'The ws() subroutine removes any whitespace in front of the number. This ' includes control characters and spaces. It does not remove any printing ' characters. sub ws(byref que() as byte, byref echo() as byte) dim digit as byte do call peekqueue(que, digit, 1) if digit > 32 then exit sub else call getqueue(que, digit, 1) end if loop end sub 'The getsgn() function gets the sign of the number. Plus is positive. Minus is negative. ' No sign is assumed positive. The plus or minus character is removed from the queue. Any ' other character remains in the queue. function getsgn(byref que() as byte, byref echo() as byte) as boolean dim digit as byte getsgn=False 'assume positive call peekqueue(que, digit, 1) if digit = 45 then '- call getqueue(que, digit, 1) call putqueue(echo, digit, 1) getsgn=True end if if digit=43 then '+ call getqueue(que, digit, 1) call putqueue(echo, digit, 1) end if end function