spinix-hive/lerner/clib.spin

1016 lines
33 KiB
Plaintext

'******************************************************************************
' C Function Library in Spin
' Author: Dave Hein
' Copyright (c) 2010
' See end of file for terms of use.
'******************************************************************************
'******************************************************************************
' Revison History
' v1.0 - 4/2/2010 First official release
'******************************************************************************
{{
This object provides some standard C library functions written in Spin.
There are three main types of functions provided -- string manipulation,
formatted I/O and memory allocation. C functions that use a variable
number of arguments are supported in two ways. The first way uses
unique function names for each number of arguments that is used, such
as printf3 for a printf with three arguments. The second way uses an
argument list, such as vprintf. The argument list consists of an array
of longs that holds the values of the arguments.
The start function must be called before using any of the serial I/O or
memory allocation functions. It will setup a a serial port that transmits on
pin 30 and receives on pin 31 at 57600 baud. It will also establish a stack
space of 200 longs for the top object. The malloc heap begins immediately
after the stack space, and extends to the end of the RAM.
Serial I/O and stack space parameters can be specified by calling start1
instead of start. The first four parameters of start1 are the same as those
used in a call to the FullDuplexSerial start function. The fifth parameter,
stacksize, defines the size in longs of the stack space.
Additional serial ports can be created by calling the openserial routine.
In additional to rxpin, txpin, mode and baudrate the receive and transmit
buffer sizes are also specified. A file stream pointer is returned, which
is used when calling fputc, fgetc and all of the other "f" routines.
The functions puts, putchar and getchar use the str1, tx1 and rx1 methods
of a modified FullDuplexSerial object. This object, cserial, allows for multiple
serial ports through the use of a structer pointer called a handle. See
cserial.spin for more information.
clib uses a file stream pointer to access I/O devices, such as the serial port.
The I/O routines that start with the letter "f" require a file stream pointer,
which is normally named stream. To save space, this object does not support
file I/O. clibsd.spin should be used if file I/O is needed.
}}
OBJ
mem : "cmalloc"
ser : "cserial"
fstr : "cfloatstr"
CON
NEW_LINE = 10
SEEK_SET = 0
SEEK_CUR = 1
SEEK_END = 2
stream_serial = 1
stream_fileio = 2
stream_size = 8
stream_type = 0
stream_handle = 1
DAT
stdout long 0
stdin long 0
stdio long 0
echoflag long 1
linefeed long 0
'******************************************************************************
' Initialization Routines
'******************************************************************************
PUB start
{{
This routine sets a top object stack size of 200 longs followed by the malloc
heap space. It initializes the standard I/O serial port to receive on P31 and
transmit on P30 at 57,600 baud. The serial port mode flag is set to use a
lock for multi-cog operation. It returns the stdio pointer if successful, or
NULL if not.
}}
return start1(31, 30, %110000, 115200, 200)
PUB start1(rxpin, txpin, mode, baudrate, stacksize)
{{
This routine calls the malloc initialization routine. The number of longs in
the top object stack is set by the value of "stacksize". It also calls the
openserial function to initialize the standard I/O serial port. It returns the
stdio pointer if successful, or NULL if not.
}}
mem.mallocinit(stacksize)
stdio := openserial(rxpin, txpin, mode, baudrate, 64, 64)
stdin := malloc(stream_size)
stdout := malloc(stream_size)
memcpy(stdin, stdio, stream_size)
memcpy(stdout, stdio, stream_size)
return stdio
PUB openserial(rxpin, txpin, mode, baudrate, rxsize, txsize) | stream, handle
{{
This routine allocates memory for a file info and serial port data structure.
It calls the start1 function in the cserial object to create a serial port in
the next available cog. It returns a pointer to the file info structure if
successful, or a NULL value if not successful.
}}
stream := malloc(stream_size + ser#header_size + rxsize + txsize)
ifnot stream
return 0
handle := stream + stream_size
long[stream][stream_type] := stream_serial
long[stream][stream_handle] := handle
ifnot ser.start1(handle, rxpin, txpin, mode, baudrate, rxsize, txsize)
free(stream)
return 0
return stream
'******************************************************************************
' String Routines
'******************************************************************************
PUB strcmp(str1, str2)
{{
This routine compares two strings and returns a value of zero if they are
equal. If "str1" is greater than "str2" it will return a positive value, and
if "str1" is less than "str2" it will return a positive value.
}}
if (strcomp(str1, str2))
return 0
repeat while (byte[str1] and byte[str1] == byte[str2])
str1++
str2++
return byte[str1] - byte[str2]
PUB strncmp(str1, str2, n)
{{
This routine compares the first "n" characters of two strings. It returns a
value of zero if they are equal. If "str1" is greater than "str2" it will
return a positive value, and if "str1" is less than "str2" it will return a
negative value.
}}
if (n =< 0)
return 0
repeat while (--n and byte[str1] and byte[str1] == byte[str2])
str1++
str2++
return byte[str1] - byte[str2]
PUB memcpy(dest, src, n) | bitflag
{{
This routine copies "n" bytes from the memory location pointed to by "src" to
the memory location pointed to by "dest". The copy is performed as either longs,
words or bytes depending on the least significant bits of the parameters. A
bytemove is performed if "n" is less than 180 to avoid the extra computational
overhead for small mem copies. The value of "dest" is returned.
}}
if n < 180
bytemove(dest, src, n)
return dest
bitflag := (dest | src | n) & 3
ifnot bitflag
longmove(dest, src, n >> 2)
elseifnot bitflag & 1
wordmove(dest, src, n >> 1)
else
bytemove(dest, src, n)
return dest
PUB memset(dest, val, n) | bitflag
{{
This routine sets the "n" bytes pointed to by "dest" to the value in the
least significant byte of "val". It returns the value of "dest". memset
uses either bytefill, wordfill or longfill depending on the least significant
bits of the parameters. A bytefill is used when "n" is less than 220 to
avoid the extra computational overhead for small buffers.
}}
if n < 220
bytefill(dest, val, n)
return dest
val &= 255
bitflag := (dest | n) & 3
ifnot bitflag
val |= (val << 24) | (val << 16) | (val << 8)
longfill(dest, val, n >> 2)
elseifnot bitflag & 1
val |= (val << 8)
wordfill(dest, val, n >> 1)
else
bytefill(dest, val, n)
return dest
PUB strcat(dst, src) | dlen, slen
{{
This routine concatenates the string pointed to by "src" to the string at
"dst". It returns the value of "dst".
}}
dlen := strsize(dst)
slen := strsize(src) + 1
bytemove(dst + dlen, src, slen)
return dst
PUB strcpy(dst, src) | slen
{{
This routine copies the string pointed to by "src" to the location pointed
to by "dst". It returns the value of "dst".
}}
slen := strsize(src) + 1
bytemove(dst, src, slen)
return dst
PUB strncpy(dst, src, num) | slen
{{
This routine copies the first num bytes from the src string to the dst string.
If src contains less than num bytes, then only the bytes contained in the src
string are copied, and the remaining bytes are set to zero. Note that if num
is less than or equal to the string length of src the dst string will not be
terminated with a NULL. This routine returns the value of the dst pointer.
}}
if (num < 1)
return dst
slen := strsize(src)
if (slen > num)
slen := num
bytemove(dst, src, slen)
bytefill(dst + slen, 0, num - slen)
return dst
PUB isdigit(char)
{{
This routine return true if the value of "char" represents an ASCII decimal
digit between 0 and 9. Otherwise, it returns false.
}}
return char => "0" and char =< "9"
PUB itoa(number, str, base) | mask, shift, nbits, str0
{{
This routine converts the 32-bit value in "number" to an ASCII string at the
location pointed to by "str". The numeric base is determined by the value
of "base", and must be either 2, 4, 8, 10 or 16. Leading zeros are suppressed,
and the number is treated as unsigned except when the base is 10. The length
of the resulting string is returned.
}}
str0 := str
case base
10 : return itoa10(number, str)
2 : nbits := 1
4 : nbits := 2
8 : nbits := 3
16 : nbits := 4
other:
byte[str] := 0
return 0
mask := base - 1
if (nbits == 3)
shift := 30
else
shift := 32 - nbits
repeat while shift > 0 and ((number >> shift) & mask) == 0
shift -= nbits
repeat while (shift => 0)
byte[str++] := HexDigit[(number >> shift) & mask]
shift -= nbits
byte[str++] := 0
return str - str0 - 1
'******************************************************************************
' Output Routines
'******************************************************************************
PUB set_linefeed(val)
linefeed := val
PUB putnewline(handle)
ser.tx1(handle, 13)
if linefeed
ser.tx1(handle, 10)
PUB puts(str)
{{
This routine sends the contents of the string pointed to by "str" to the
standard output.
}}
fputs(str, stdout)
putnewline(stdout)
PUB putchar(char)
{{
This routine sends the character in "char" to the standard output.
}}
fputc(char, stdout)
PUB fputc(char, stream) | type, handle
{{
This routine sends the character in "char" to the output device defined by
"stream".
}}
type := long[stream][stream_type]
handle := long[stream][stream_handle]
if (type == stream_serial)
if char == NEW_LINE
putnewline(handle)
else
ser.tx1(handle, char)
PUB fputs(str, stream) | type, handle, len, char
{{
This routine sens the string pointed to by "str" to the output device defined
by "stream".
}}
type := long[stream][stream_type]
handle := long[stream][stream_handle]
if (type == stream_serial)
'ser.str1(handle, str)
repeat while (char := byte[str++])
if char == NEW_LINE
putnewline(handle)
else
ser.tx1(handle, char)
PUB fwrite(buffer, elem_size, num, stream) | type, handle, char
{{
This routine writes a block of data to the output device defined by "stream".
It returns the number of bytes written, or a -1 if there is an error.
}}
type := long[stream][stream_type]
handle := long[stream][stream_handle]
if (type == stream_serial)
num *= elem_size
repeat num
char := byte[buffer++]
if char == NEW_LINE
putnewline(handle)
else
ser.tx1(handle, char)
return num
PUB printf0(fmtstr)
{{
This is a version of "printf" with a format string and no additional parameters.
}}
vfprintf(stdout, fmtstr, @fmtstr)
PUB printf1(fmtstr, arg1)
{{
This is a version of "printf" with a format string and one additional parameter.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB printf2(fmtstr, arg1, arg2)
{{
This is a version of "printf" with a format string and two additional parameters.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB printf3(fmtstr, arg1, arg2, arg3)
{{
This is a version of "printf" with a format string and three additional parameters.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB printf4(fmtstr, arg1, arg2, arg3, arg4)
{{
This is a version of "printf" with a format string and four additional parameters.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB printf5(fmtstr, arg1, arg2, arg3, arg4, arg5)
{{
This is a version of "printf" with a format string and five additional parameters.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB printf6(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6)
{{
This is a version of "printf" with a format string and six additional parameters.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB printf7(fmtstr, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
{{
This is a version of "printf" with a format string and six additional parameters.
}}
vfprintf(stdout, fmtstr, @arg1)
PUB vprintf(fmtstr, arglist)
{{
This routine uses the string pointed to by "format" to send a formatted string
to the standard output. The parameter "arglist" points to a long array of
values that are merged with the output string dependent on the contents of the
format string.
}}
vfprintf(stdout, fmtstr, arglist)
PUB sprintf0(str, fmtstr)
{{
This is a version of "sprintf" with a format string and no additional parameters.
}}
vsprintf(str, fmtstr, @fmtstr)
PUB sprintf1(str, fmtstr, arg1)
{{
This is a version of "sprintf" with a format string and one additional parameter.
}}
vsprintf(str, fmtstr, @arg1)
PUB sprintf2(str, fmtstr, arg1, arg2)
{{
This is a version of "sprintf" with a format string and two additional parameters.
}}
vsprintf(str, fmtstr, @arg1)
PUB sprintf3(str, fmtstr, arg1, arg2, arg3)
{{
This is a version of "sprintf" with a format string and three additional parameters.
}}
vsprintf(str, fmtstr, @arg1)
PUB sprintf4(str, fmtstr, arg1, arg2, arg3, arg4)
{{
This is a version of "sprintf" with a format string and four additional parameters.
}}
vsprintf(str, fmtstr, @arg1)
PUB sprintf5(str, fmtstr, arg1, arg2, arg3, arg4, arg5)
{{
This is a version of "sprintf" with a format string and five additional parameters.
}}
vsprintf(str, fmtstr, @arg1)
PUB sprintf6(str, fmtstr, arg1, arg2, arg3, arg4, arg5, arg6)
{{
This is a version of "sprintf" with a format string and six additional parameters.
}}
vsprintf(str, fmtstr, @arg1)
PUB fprintf0(stream, fmtstr)
vfprintf(stream, fmtstr, @fmtstr)
PUB fprintf1(stream, fmtstr, arg1)
vfprintf(stream, fmtstr, @arg1)
PUB fprintf2(stream, fmtstr, arg1, arg2)
vfprintf(stream, fmtstr, @arg1)
PUB fprintf3(stream, fmtstr, arg1, arg2, arg3)
vfprintf(stream, fmtstr, @arg1)
PUB fprintf4(stream, fmtstr, arg1, arg2, arg3, arg4)
vfprintf(stream, fmtstr, @arg1)
PUB fprintf5(stream, fmtstr, arg1, arg2, arg3, arg4, arg5)
vfprintf(stream, fmtstr, @arg1)
PUB fprintf6(stream, fmtstr, arg1, arg2, arg3, arg4, arg5, arg6)
vfprintf(stream, fmtstr, @arg1)
PUB vfprintf(stream, fmtstr, arglist)| str[25]
{{
This routine prints a formatted string to the output pointed to by "stream".
It calls vsprintf to generate the formatted string in the stack string array
"str", and then sends it to the output stream by calling fputs. The format is
pointed to by "format" and the parameters are contained on a long array pointed
to by "arglist". Note, care must be taken to ensure that the formatted output
string will fit within the 100-byte space provided by "str". Also, the stack
must be large enough to accomodate "str".
}}
vsprintf(@str, fmtstr, arglist)
fputs(@str, stream)
PUB vsprintf(str, fmtstr, arglist) | arg, width, digits, fmtstr0
{{
This routines generates a formatted output string based on the string pointed
to by "format". The parameter "arglist" is a pointer to a long array of values
that are merged into the output string. The characters in the format string
are copied to the output string, exept for special character sequences that
start is % or \. The % character is used to merge values from "arglist". The
characters following the % are as follows: %[0][width][.digits][l][type].
If a "0" immediately follows the % it indicates that leading zeros should be
displayed. The optional "width" paramter specifieds the minimum width of the
field. The optional ".digits" parameter specifies the number of fractional
digits for floating point, or it may also be used to specify leading zeros and
the minimum width for integer values. The "l" parameter indicates long values,
and it is ignored in this implementation. The "type" parameter is a single
character that indicates the type of output that should be generated. It can
be one of the following characters:
d - signed decimal number
i - same as d
u - unsigned decimal number
x - hexidecimal number
o - octal number
b - binary number
c - character
s - string
e - floating-point number using scientific notation
f - floating-point number in standard notation
% - prints the % character
The \ character is used to print the following special characters:
b - backspace
n - newline or linefeed
r - carriage return
t - tab, or ASCII 9
\ - prints the \ character
xxx - this inserts the value of a 3-digital octal number between 000 and 377
Note, care must be taken the the generated output string does not exceed the size
of the string. A string size of 100 bytes is normally sufficient.
}}
arg := long[arglist]
arglist += 4
repeat while (byte[fmtstr])
if (byte[fmtstr] == "%")
fmtstr0 := fmtstr++
if (byte[fmtstr] == "0")
width := -1
digits := getvalue(@fmtstr)
else
width := getvalue(@fmtstr)
if (byte[fmtstr] == ".")
fmtstr++
digits := getvalue(@fmtstr)
else
digits := -1
if (byte[fmtstr] == "l")
fmtstr++
case byte[fmtstr]
"d", "i": str := putdec(str, arg, width, digits)
"u" : str := putudec(str, arg, width, digits)
"o" : str := putoctal(str, arg, width, digits)
"b" : str := putbinary(str, arg, width, digits)
"x" : str := puthex(str, arg, width, digits)
"f" : str := fstr.putfloatf(str, arg, width, digits)
"e" : str := fstr.putfloate(str, arg, width, digits)
"c" : byte[str++] := arg
"%" : byte[str++] := "%"
"s" :
strcpy(str, arg)
str += strsize(arg)
other :
byte[str++] := "%"
fmtstr := fmtstr0
fmtstr++
arg := long[arglist]
arglist += 4
elseif (byte[fmtstr] == 92)
case byte[++fmtstr]
0 : quit
"b" : byte[str++] := 8
"t" : byte[str++] := 9
"r" : byte[str++] := 13
"n" : byte[str++] := 10
"0".."3": byte[str++] := getoctalbyte(@fmtstr)
other : byte[str++] := byte[fmtstr]
fmtstr++
else
byte[str++] := byte[fmtstr++]
byte[str++] := 0
PRI getoctalbyte(pstr) | str
{{
This private routine is used to read a 3-digit octal number contained
in the format string.
}}
str := long[pstr]
repeat 3
if (byte[str] & $f8) == "0"
result := (result << 3) + (byte[str++] & 7)
else
quit
long[pstr] := str - 1
PRI itoa10(number, str) | str0, divisor, temp
{{
This private routine is used to convert a signed integer contained in
"number" to a decimal character string. It is called by itoa when the
numeric base parameter has a value of 10.
}}
str0 := str
if (number < 0)
byte[str++] := "-"
if (number == $80000000)
byte[str++] := "2"
number += 2_000_000_000
number := -number
elseif (number == 0)
byte[str++] := "0"
byte[str] := 0
return 1
divisor := 1_000_000_000
repeat while (divisor > number)
divisor /= 10
repeat while (divisor > 0)
temp := number / divisor
byte[str++] := temp + "0"
number -= temp * divisor
divisor /= 10
byte[str++] := 0
return str - str0 - 1
PRI getvalue(pstr) | str
{{
Thie private routine is used to extract the width and digits
fields from a format string. It is called by vsprintf.
}}
str := long[pstr]
ifnot isdigit(byte[str])
return -1
result := 0
repeat while isdigit(byte[str])
result := (result * 10) + byte[str++] - "0"
long[pstr] := str
DAT
HexDigit byte "0123456789abcdef"
PRI printpadded(str, numstr, count, width, digits)
{{
This private routine is used to generate a formatted string
containg at least "width" characters. The value of count
must be identical to the length of the string in "str".
Leading spaces will be generated if width is larger than the
maximum of count and digits. Leading zeros will be generated
if digits is greater than count.
}}
if digits < count
digits := count
repeat while (width-- > digits)
byte[str++] := " "
if byte[numstr] == "-"
byte[str++] := byte[numstr++]
digits--
repeat while (digits-- > count)
byte[str++] := "0"
strcpy(str, numstr)
return str + strsize(numstr)
PRI putbinary(str, number, width, digits) | count, numstr[9]
{{
This private routine converts a number to a string of binary digits.
printpadded is called to insert leading blanks and zeros.
}}
count := itoa(number, @numstr, 2)
return printpadded(str, @numstr, count, width, digits)
PRI putoctal(str, number, width, digits) | count, numstr[3]
{{
This private routine converts a number to a string of octal digits.
printpadded is called to insert leading blanks and zeros.
}}
count := itoa(number, @numstr, 8)
return printpadded(str, @numstr, count, width, digits)
PRI puthex(str, number, width, digits) | count, numstr[3]
{{
This private routine converts a number to a string of hexadecimal digits.
printpadded is called to insert leading blanks and zeros.
}}
count := itoa(number, @numstr, 16)
return printpadded(str, @numstr, count, width, digits)
PRI putdec(str, number, width, digits)| count, numstr[3]
{{
This private routine converts a signed number to a string of decimal
digits. printpadded is called to insert leading blanks and zeros.
}}
count := itoa10(number, @numstr)
return printpadded(str, @numstr, count, width, digits)
PRI putudec(str, number, width, digits) | count, numstr[3], adjust
{{
This private routine converts an unsigned number to a string of decimal
digits. printpadded is called to insert leading blanks and zeros.
}}
adjust := 0
repeat while (number < 0)
number -= 1_000_000_000
adjust++
count := itoa10(number, @numstr)
byte[@numstr] += adjust
return printpadded(str, @numstr, count, width, digits)
'******************************************************************************
' Input Routines
'******************************************************************************
PUB getchar
{{
This routine returns a single character from the standard input. It will not
return until a character has been received.
}}
return fgetc(stdin)
PUB gets(str) | last
{{
This routine returns a string from the standard input. It will not return until
is has received a carriage return. The carriage return is not included in the
returned string. Received characters are echoed back out ot the the serial port.
Backspaces will cause the previous character to be removed from the buffer, and
a character sequence of backspace, space and backspace will be transmitted to the
serial port to erase the previous character. Backspace characters are not inserted
into the string.
}}
result := fgets(str, 100000000, stdin)
if result > 0
last := strsize(str) - 1
if last => 0
str += last
if byte[str] == NEW_LINE
byte[str] := 0
PRI gets1(str, size, handle) | char, str0, editflag, echoflag1
editflag := (handle == long[stdio][stream_handle])
echoflag1 := editflag & echoflag
str0 := str
repeat while (size > 1)
char := ser.rx1(handle)
if (char == 8 AND editflag)
if (str > str0)
if echoflag1
ser.tx1(handle, 8)
ser.tx1(handle, " ")
ser.tx1(handle, 8)
str--
size++
next
if char == 4
if (str == str0)
str0 := 0
quit
if char == NEW_LINE OR char == 13
if echoflag1
putnewline(handle)
byte[str++] := NEW_LINE
quit
else
if echoflag1
ser.tx1(handle, char)
byte[str++] := char
size--
byte[str] := 0
return str0
PUB fgetc(stream) | char, type, handle
{{
This routine returns a single character from the input device defined by "stream".
It will not return until a character has been received.
}}
type := long[stream][stream_type]
handle := long[stream][stream_handle]
if (type == stream_serial)
result := ser.rx1(handle)
PUB fgets(str, size, stream) | type, handle
{{
This routine returns a string from the input device defined by "stream". It will
return until it has either received a carriage return or "size" number of characters.
}}
type := long[stream][stream_type]
handle := long[stream][stream_handle]
size--
if (type == stream_serial)
result := gets1(str, size, handle)
PUB fread(buffer, elem_size, num, stream) | type, handle
{{
This routine reads a block of data from the input device defined by "stream".
It returns the number of bytes read, or a -1 if the end of file is
encountered.
}}
type := long[stream][stream_type]
handle := long[stream][stream_handle]
if (type == stream_serial)
num *= elem_size
repeat num
byte[buffer++] := ser.rx1(handle)
return num
PUB scanf1(fmtstr, parg1)
{{
This routine is a version of "scanf" with a format string and one parameter.
}}
vscanf(fmtstr, @parg1)
PUB scanf2(fmtstr, parg1, parg2)
{{
This routine is a version of "scanf" with a format string and two parameters.
}}
vscanf(fmtstr, @parg1)
PUB scanf3(fmtstr, parg1, parg2, parg3)
{{
This routine is a version of "scanf" with a format string and three parameters.
}}
vscanf(fmtstr, @parg1)
PUB scanf4(fmtstr, parg1, parg2, parg3, parg4)
{{
This routine is a version of "scanf" with a format string and four parameters.
}}
vscanf(fmtstr, @parg1)
PUB vscanf(fmtstr, arglist) | str[25]
{{
This routine reads a string from the standard input using gets, and it converts
the data based on the format in the "format" string. The converted values are
stored at the locations determined by the list of long pointers in "arglist".
See vsscanf for the description of the format string.
}}
gets(@str)
vsscanf(@str, fmtstr, arglist)
PUB sscanf1(str, fmtstr, parg1)
{{
This is a version of sscanf with a format string and one parameter.
}}
vsscanf(str, fmtstr, @parg1)
PUB sscanf2(str, fmtstr, parg1, parg2)
{{
This is a version of sscanf with a format string and two parameters.
}}
vsscanf(str, fmtstr, @parg1)
PUB sscanf3(str, fmtstr, parg1, parg2, parg3)
{{
This is a version of sscanf with a format string and three parameters.
}}
vsscanf(str, fmtstr, @parg1)
PUB sscanf4(str, fmtstr, parg1, parg2, parg3, parg4)
{{
This is a version of sscanf with a format string and four parameters.
}}
vsscanf(str, fmtstr, @parg1)
PUB vsscanf(str, fmtstr, arglist) | parg
{{
This routine converts the contents of the string pointed to by "str" into
numerical values that are stored at the locations derermined by the list of
long pointers in "arglist". The format string contains conversion flags,
which are prefixed with the % character. The list of conversion flags is
as follows:
b - Convert a binary number
o - Convert an octal number
d - Convert a signed decimal number
x - Convert a hexadecimal number
f - Convert a floating point number
e - Same as f
Any other characters will be ignored, and will cause a character to be skipped
on the input string. Leading spaces are also ignored when converting a
number.
}}
parg := long[arglist]
arglist += 4
repeat while byte[fmtstr]
if byte[fmtstr++] == "%"
case byte[fmtstr]
"b" : long[parg] := getbin(@str)
"o" : long[parg] := getoct(@str)
"d" : long[parg] := getdec(@str)
"x" : long[parg] := gethex(@str)
"f", "e" : long[parg] := fstr.strtofloat(@str)
other: str++
fmtstr++
parg := long[arglist]
arglist += 4
else
str++
PRI getbin(pstr) | str
{{
This private routine is used by vsscanf to convert a string of binary digits to
a numerical value.
}}
str := long[pstr]
repeat while byte[str] == " "
str++
repeat while (byte[str] & $fe) == "0"
result := (result << 1) + (byte[str++] & 1)
long[pstr] := str
PRI getoct(pstr) | str
{{
This private routine is used by vsscanf to convert a string of octal digits to
a numerical value.
}}
str := long[pstr]
repeat while byte[str] == " "
str++
repeat while (byte[str] & $f8) == "0"
result := (result << 3) + (byte[str++] & 7)
long[pstr] := str
PRI gethex(pstr) | str, char
{{
This private routine is used by vsscanf to convert a string of hexadecimal digits to
a numerical value.
}}
str := long[pstr]
repeat while byte[str] == " "
str++
repeat
char := byte[str++]
case char
"0".."9": result := (result << 4) + char - "0"
"a".."f": result := (result << 4) + char - "a" + 10
"A".."F": result := (result << 4) + char - "A" + 10
other : quit
long[pstr] := str - 1
PRI getdec(pstr) | str, signflag
{{
This private routine is used by vsscanf to convert a string of decimal digits to
a numerical value.
}}
signflag := 0
str := long[pstr]
repeat while byte[str] == " "
str++
if isdigit(byte[str])
result:= byte[str] - "0"
elseif byte[str] == "-"
signflag := 1
elseif byte[str] <> "+"
long[pstr] := str
return
str++
repeat while isdigit(byte[str])
result := (result * 10) + byte[str++] - "0"
if signflag
result := -result
long[pstr] := str
'******************************************************************************
' Malloc Routines
'******************************************************************************
PUB malloc(size)
{{
This routine provides an interface to the malloc routine in cmalloc.spin
}}
return mem.malloc(size)
PUB free(ptr)
{{
This routine provides an interface to the free routine in cmalloc.spin
}}
return mem.free(ptr)
PUB calloc(size)
{{
This routine provides an interface to the calloc routine in cmalloc.spin
}}
return mem.calloc(size)
'******************************************************************************
' Standard I/O redirection routines
'******************************************************************************
PUB setstdin(stream)
memcpy(stdin, stream, stream_size)
PUB setstdout(stream)
echoflag := 0
memcpy(stdout, stream, stream_size)
PUB resetstdin
memcpy(stdin, stdio, stream_size)
PUB resetstdout
echoflag := 1
memcpy(stdout, stdio, stream_size)
PUB getstdin
return stdin
PUB getstdout
return stdout
PUB CheckStackSpace
printf1(string("The last %d longs have not been used on the stack\n"), mem.CheckStackSpace)
PUB CheckMallocSpace | ptr, numalloc, numfree
numalloc := 0
numfree := 0
ptr := mem.GetMallocList
printf0(string("MallocList\n"))
repeat while(ptr)
printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1])
numalloc += word[ptr][1]
ptr := word[ptr]
ptr := mem.GetFreeList
printf0(string("FreeList\n"))
repeat while(ptr)
printf2(string("addr = %04x size = %d\n"), ptr, word[ptr][1])
numfree += word[ptr][1]
ptr := word[ptr]
printf2(string("%d bytes allocated, %d bytes free\n"), numalloc, numfree)
{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}