'****************************************************************************** ' 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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}