| Keyword | Name | Description | Minimum | Maximum |
|---|---|---|---|---|
| char | Character | 8-bit unsigned | 0 | 255 |
| unsigned char | ||||
| signed char | Signed Character | 8-bit signed | -128 | 127 |
| int | Signed Integer | 16-bit signed | -32768 | 32767 |
| signed int | ||||
| signed | ||||
| unsigned int | Unsigned Integer | 16-bit unsigned | 0 | 65535 |
| unsigned | ||||
| void | Void | No Value | 0 | 0 |
| int * | Pointer to Integer | 16-bit unsigned | 0 | 65535 |
| signed int * | ||||
| signed * | ||||
| unsigned int * | ||||
| unsigned * | ||||
| char * | Pointer to Character | 16-bit unsigned | 0 | 65535 |
| unsigned char * | ||||
| signed char * | ||||
| void * | Pointer to Void | 16-bit unsigned | 0 | 65535 |
| int ** | Pointer to a Pointer to Integer | 16-bit unsigned | 0 | 65535 |
| signed int ** | ||||
| signed ** | ||||
| unsigned int ** | ||||
| unsigned ** | ||||
| char ** | Pointer to a Pointer to Character | 16-bit unsigned | 0 | 65535 |
| unsigned char ** | ||||
| signed char ** | ||||
| void ** | Pointer to a Pointer to Void | 16-bit unsigned | 0 | 65535 |
| int(*)() | Pointer to a Function | 16-bit unsigned | 0 | 65535 |
Notes:
- Single dimension arrays of the above types are supported, such as int[] char*[], etc.
- Function pointers are limited to one single type,
int(\*)(), and they have no argument types. - Structures and unions composed of the above types are supported.
- Pointers to structures and pointers to unions are supported.
- Typedef is supported.
ElfC follows the C "usual arithmetic conversions" The C Programming Language, 2nd Edition describes these rules in Section 2.7, Type Conversions, on page 42, and in Appendix A, Section A6.5 Arithmetic Conversions, on page 198. Since long, float and double types are not supported, these can be simplified as the two rules below.
- Signed and unsigned character types are promoted to int.
- If either of the operands are unsigned int, then (signed) int operator is promoted to unsigned.
Notes:
- Since character types are 8-bit types that fit completely into a subset of the 16-bit int type, they will keep their signed or unsigned value when promoted.
- A negative signed character value promotes to the same negative int value, and an unsigned charater value promotes to the same positive int value.
- Since int types do not fit completely into the range of unsigned int types, a negative int value will promote to a large positive value.
- A positive signed int value promotes to the same positive unsigned int value.
Examples: These examples are in the promote.c example program.
#include <stdio.h>
unsigned char uc = 77;
signed char sc = -7;
int main() {
char c;
c = uc + sc;
printf("char c = '%c' (%d)\n", c,c);
}This program will print:
char c = 'F' (70)
The value uc promotes to int 77 and sc promotes to int -7, and 77 + (-7) = int 70, which is then assigned back to unsigned char c as 'F' or 70.
#include <stdio.h>
unsigned int ui1 = 7;
signed int i = -77;
int main() {
unsigned int ui2, ui3;
ui2 = ui1 + i;
printf("unsigned int ui2 = %u (%#x%)\n", ui2, ui2);
}This program will print:
unsigned int ui2 = 65466 (0xffba)
In this example the int value -77 promotes to the unsigned int value of 65459, and 65459 plus the unigned int value 7 equals the unsigned int value of 65566.
ElfC supports declarations made in global scope and local scope.
If the declaration appears outside of any function then it is said to have global scope, which terminates at the end of the file.
If the declaration appears within a function definition or within the list of parameter declarations in a function definition, the identifier has local scope.
ElfC does not support declarations made in block scope. Declarations cannot be declared within a block defined by a pair of braces, unless that block is the definition of a function.
If an identifier is declared by declaration with local scope, then it is only available within the function where it was declared.
If an identifier is declared by declaration with global scope, then it is available throughout the file where it was declared.
ElfC supports the C standard type declaration syntax as specified in ANSI C C89/C90 specification. The declaration type may be prefixed by a storage class specifier, a type qualifier or both. If both are used, the storage class specifier must come before the type qualifier.
| Storage Class Specifiers |
|---|
| extern |
| static |
| auto |
| register |
| typedef |
Notes:
The auto and register storage class specifiers may only be used in local scope. ElfC will emit an error if they are used in global scope.
The register storage class specifier is treated the same as auto, except if the const type qualifier is used, then ElfC will emit an error because that combination is forbidden by the ANSI C C89/C90 specification.
The auto storage class specifier is the default for local scope declarations and static is the default for global scope declarations.
The typedef keyword is used to define user types.
| Type Qualifiers |
|---|
| const |
| volatile |
| const volatile |
| volatile const |
The type qualifier volatile is accepted, and will suppress optimization.
The type qualifier const is supported for variables and pointers to constant variables.
Either volatile const or const volatile are accepted as valid syntax, and are treated exactly the same.
If a storage class specifier is used in a declaration, the type qualifier must come after it.
Per the ANSI C C89/C90 specification, register variables cannot be made constant. ElfC accepts the register keyword in a local declaration, but ignores it unless the variable is also declared const, in that case ElfC will emit an error.
Functions may be declared to return a const value. Per the ANSI C C89/C90 specification, const is ignored since a function may never be an lvalue.
Arguments to a function may be declared as const and ElfC will treat these arguments as initialized inside the function. An attempt to assign a value to a const argument inside the function is considered an error.
A pointer to an unqualified variable can be assigned to a pointer to a const or volatile variable, and vice versa.
Type qualifiers are ignored when considering if two types are compatible.
Implementation Defined Behavior
Note: This behavior is not specified by the ANSI C89/C90 specification, but may contradict behavior specified in C99 and later versions.
ElfC does not require immediate initialization in the declaration of a const variable, since ElfC does not fully support all the possible declaration type initializations.
ElfC allows variables to be declared as const and initialized later by an assignment in the code. However, a second attempt at assignment will be treated as an error.
The const keyword is supported for static (and global) arrays, and const arrays must be initialized when declared.
User defined types may include const in their typedef definition, and const may be
applied to an existing user defined type.
The const keyword is ignored for structures and unions. ElfC will emit a warning message when const is ignored in these cases.
The const keyword is supported for members inside structures and unions.
ElfC does not prevent the assignment of a pointer to a const or volatile variable to an unqualified pointer.
Functions may be declared to return a volatile value and arguments may be qualified as volatile but ElfC will ignore the volatile keyword, per the ANSI C C89/C90 specification.
The volatile keyword is ignored for structures and unions. ElfC will emit a warning message when volatile is ignored in these cases.
The volatile keyword is supported for the members inside structures and unions.
User defined types may include volatile in their typedef definition, and volatile may be applied to an existing user defined type.
Differences from ANSI C89/C90
Note: The following behavior differs from the ANSI C89/C90 specification.
Neither const pointers to (varying) variables, nor const pointers to const variables are supported by ElfC, i.e. the syntaxes int * const p; and const int * const p; are not supported, and will emit an error.
The const keyword is not supported for local (auto) arrays, because ElfC does not support initializing local arrays.
ElfC supports local labels and goto.
Local labels have functional scope and their own namespace.
The goto keyword can be used to jump directly to a local label within the same function.
There are few restrictions on local label locations and goto. For example, they may jump into the body of an iteration statement, like for or while statement, bypassing any initializations or tests done at the beginning.
ElfC will emit a warning when a local label is defined within the body of an iteration statement, but the ANSI C89/C90 specification does not prohibit this. (Jumping into an iteration statement may bypass initializations, and is considered bad practice.)
Local labels and goto should be used with caution, if used at all.
The following statement and preprocessor directive were added to SubC by ElfC to support inline Asm/02 code.
-
The
asmstatement passes a string literal directly into the generated assembly file. -
The
#pragmapreprocessor directive can be used to directly insert a line of assembly code into the generated assembly file.
-
The new
-LElfC option will compile and assemble a C source file into a prg file defining an Elf/OS (Mini/DOS) library procedure. -
The source file should contain a public function with same name as the file name of the C file. If no public function in the file matches the file name, an error will be generated.
-
The procedure name will be the file name with the C prefix and serve as the public entry point for the procedure function.
-
If needed, the compiler will emit an immediate jump to the public entry point function with the same name as the procedure.
-
The entry point function's public name will be suppressed to prevent duplication of the procedure name when assembling.
-
The prg produced by the assembler can then be incorporated into a library for Elf/OS or Mini/DOS.
-
The file can contain public functions and public labels. These will be available in the procedure, along with the procedure function name.
-
An Elf/OS (Mini/DOS) library is created by concatenating multiple procedure prg files.
Example:
The C file cstime.c containing the function:
static struct tm _tm;
char *cstime(void) {
systime(&_tm);
return asctime(&_tm);
}When compiled with:
..\elfc -L cstime.c
Will produce the file cstime.prg that can be concatenated into an Elf/OS (Mini/DOS) library, such as time.lib. The library can then be linked to a C program to provide the cstime function.
type systime.prg asctime.prg cstime.prg > time.lib
- The flags
-, +, space, 0 and #are supported. - The width specification is supported.
- The suppression operator
*is supported. - The
%d, %i, %u, %o, %x, %X, %c, %s, %p, %n and %%conversions are supported. - The decimal precision is not supported.
- The length modifiers h, l (el) and L are not supported.
- The %f, %e, %E, %g and %G conversions are not supported.
- The width specification is supported.
- The suppression operator
*is supported. - The
%d, %i, %u, %o, %x, %c, %s, %p, %n and %%conversions are supported. - The charset operators
%[...]and%[^...]are supported. - The %f, %e, and %g conversions are not supported.
When the -M option is specified, only the following conversions are supported. Using the
Elfio and Elfstd libraries, reduces the binary code size by about 2K bytes.
- Only the
%d, %u, %x, %c, %s, %p, %n and %%conversions are supported. - The suppression operator
*is supported. - The
%i, %o, and %Xconversions are not supported. - The flags
-, +, space, 0 and #are not supported. - The width specification is not supported.
- The decimal precision is not supported.
- The length modifiers h, l (el) and L are not supported.
- The %f, %e, %E, %g and %G conversions are not supported.
- The width specification is supported.
- The suppression operator
*is supported. - The
%d, %u, %x, %c, %s, %p, %n and %%conversions are supported. - The
%i and %oconversions are not supported. - The charset operators
%[...]and%[^...]are not supported. - The %f, %e, and %g conversions are not supported.
Note: The __ELFIO__ macro is defined when the -M option is used to compile code.
The following functions were omitted from the ElfC stdlib C library.
- double atof(char* s);
- long atol(char* s);
- double strtod(char* s, char** endp);
- long strtol(char* s, char** endp, int base
- unsigned long strtoul(char* s, char** endp, int base);
- long labs(long n);
- ldiv_t ldiv(long num, long denom);
- int system(char *s);
- char* getenv(char *name);
Notes:
- All the long and double utility functions were omitted because these types are not supported in the current version.
- The math32 library provides equivalent functions as the long utility functions.
- The atoi32 function in math32 library provides equivalent function as atol.
- The strtoi32 function in math32 library provides equivalent function as strtol.
- The div32 function in math32 library provides equivalent function as ldiv.
- The abs32 function in math32 library provides equivalent function as labs.
- The system and genenv() functions have no equivalent functions in Elf/OS or Mini/DOS.
The following functions were omitted from the ElfC stdio C library.
- FILE* reopen(char* filename, char *mode, FILE* stream);
- int setvbuf(FILE* stream, char *buf, int mode, int size);
- int setbuf(FILE* stream, char *buf);
Note: Elf/OS and Mini/DOS use their own buffering, and do not allow streams to be reassigned.
- Because long types are not supported,
lseektakes two int arguments for the offset, and returns an int value, 0 for success or -1 for error. - The
lseek32function uses the int32 struct typeoff_tand can be used for file sizes greater than 32KB, up to 2GB. itoxanditouare available to convert int values to hexadecimal and unsigned integer ASCII strings.
ftellandfseekuse an int type value for position, since long type is not supported.fflushis implemented as a NOP (No Operation) function.tmpnamcreates a filename with form similar totemp.00.fclosewill delete a temporary file created bytmpfileafter closing it.- Terminating a program by
abortor without callingfcloseto close a temporary file, may leave behind files created bytmpfile. fseek32,fgetposandfsetposf functions use the int32 struct typepos_tand can be used for files sizes greater than 32KB, up to 2GB.
strcasecmpwill compare two strings, ignoring case in both strings.strcasestrwill locate a string within another string, ignoring case in both strings.strdupwill allocate memory for a duplicate string, copy the characters, including the terminating null, into memory, and then return a pointer to the duplicate string.strlcpywill concatenate up to a specified number (length - 1) of characters and then append a null. Unlikestrncat,strlcatalways appends a null.strlcpywill copy up to a specified number (length - 1) of characters and then append a null. Unlikestrncpy,strlcpyalways appends a null.strlwrwill lowercase a string in place.strncasecmpwill compare up to n characters of two strings, ignoring case in both strings.strndupwill allocate memory for a string of n characters, the copy up to n characters of the original string into memory, always terminating with a null, and then return a pointer to the duplicate string.strnlenwill return the length of a string up to n, or n if the string is longer than n characters.strnstrlocate a string within the first n characters of another string.strrevreverse the contents of a string in place.strseplocate in a string pointed to by *str the first occurrence of any character in the separator string and replace it with a '\0'. The location of the next character after the separator is returned in *str, or NULL when the end of string is reached.struprwill uppercase a string in place.strimwill remove any whitespace from the beginning or end of string in place.
- ElfC supports the variable argument macros as specified by the ANSI C C89/C90 specification.
- The variable argument macros use an argument pointer of type
va_list, eg.va_list ap; - The
va_startshould be called beforeva_argis used to initialize the argument pointer. - The
va_argmacro can then be used to get the next argument. - The
va_endshould be called after all variable arguments have been processed. - The argument pointer is invalid after
va_endis called.
#include <stdio.h>
#include <stdarg.h>
/*
* Function to print a number of variable arguments, specified by count
*/
int example(int count, ...) {
int i, x;
/* define argument pointer of type va_list */
va_list ap;
/* Set argument pointer to next argument after last */
va_start(ap, count);
/* print all the integer arguments */
for (i=0; i < count; i++){
/* get the next argument as integer */
x = va_arg(ap, int);
printf("arg %d = %d\n"i, x);
}
/* call va_end before function exits */
va_end(ap);
}- ElfC supports the
assertmacro as specified by the ANSI C C89/C90 specification. - If the macro
NDEBUGis defined theassertmacro is ignored. - The
__FILE__,__LINE__and__FUNCTION__macros can be used as the file, line and function arguments, so the correct values are printed if the assertion is false.
The standard C time structure tm is defined by the ElfC time C library.
struct tm {
int tm_sec; /* seconds after the minute (0 to 60) */
int tm_min; /* minutes after the hour (0 to 60) */
int tm_hour; /* hours after midnight (0 to 23) */
int tm_mday; /* day of the month (1 to 31) */
int tm_mon; /* months since January (0 to 11) */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday (0 to 6) */
int tm_yday; /* days since January 1 (0 to 365) */
int tm_isdst; /* Daylight Savings Time (0 => no, 1 => yes, -1 => unknown) */
};Notes:
- Many of the functions in C time library use static objects that may be over-written by other calls.
The following functions were omitted from the ElfC time C library because Elf/OS v5 and Mini/DOS do not provide API for a clock or system time variable. Equivalent functions that provide the same information through a time structure are provided instead.
- clock_t clock(void)
- time_t void(void)
- double difftime(time_t time2, time_t time1)
- time_t mktime(struct tm *tp)
ElfC does not support the locale.h header, so the following function should be used to set time zone information and daylight savings time information before calling other time functions.
- void timezone(char *tzname, int tzoff_min, int tzdst)
Notes:
- tzname is a string abbreviation for the time zone name, such as "EST" for US Eastern Standard Time.
- tzoff_min is the offset from Universal Co-ordinated Time in minutes. Offset values WEST of UTC should be negative, while offsets to the EAST are positive.
- tzdst indicates if daylight savings time is in effect. It should be 1 if daylight savings time is in effect, and 0 if not.
The following functions provide equivalent time functions by through a pointer to a time structure rather than through a time_t system time variable.
- int systime(struct tm *tp) (equivalent to the localtime() function)
- int utctime(struct tm *tp) (equivalent to the gmttime() function)
- char *cstime(struct tm *tp) (equivalent to the ctime() function)
Notes:
- The systime() function will populate the time structure pointed to by the tp argument with values from the OS kernel API.
- The systime() function will use the time zone to set the tm_isdst field, or will set tm_isdst field to -1 (Unknown) if timezone() function has not been called previously.
- The _dow() and _doy() internal time functions are used to set the tm_wday and tm_yday fields.
- The utctime() function uses information set by the timezone() to calculate Universal Co-ordinated Time (UTC).
- The utctime() function may not be accurate if timezone() has not be called previously.
- The systime() and utctime() return 1 if a Real Time Clock (RTC) was used to provide the current time and 0 if data values in the kernel were used instead.
- The cstime() function will return a pointer to a buffer with a string representing the current time.
- The cstime() function is equivalent to calling systime() and passing the pointer with the result to asctime().
The following internal time functions calculate the day of the week and day of the year from the fields of the time structure, and then sets the corresponding value in the time structure.
- void _dow(struct tm *tp)
- void _doy(struct tm *tp)
The following functions are supported as documented.
- char *asctime(struct tm *tp)
- int strftime(char *s, int smax, char *fmt, struct tm *tp);
Notes:
- asctime() provides a pointer to a buffer with a simple string representation of the time.
- strftim() formats the date and time information pointed to by tp into a buffer s using a format string fmt that is similar to a printf format string.
- strftime() will write up to smax characters are written into the buffer s and will return the actual number of characters written, excluding '\0'.
- All of the ANSI (C89) strftime conversion formats are supported.
| %a | abbreviated weekday name |
| %A | full weekday name |
| %b | abbreviated month name |
| %B | full month name |
| %c | local date and time representation |
| %d | day of month, as two digits with zero (01 to 31) |
| %F | ISO date representation (%Y-%m-%d) |
| %H | hour (24-hour clock) as two digits with zero (00 to 23) |
| %I | hour (12-hour clock) as two digits with zero (01 to 12) |
| %j | day of year (001-366) |
| %m | month (01 to 12) |
| %M | minute (00 to 59) |
| %P | AM or PM |
| %p | am or pm |
| %S | second (00 to 60) |
| %U | week number of the year, Sunday as first day of week (00 to 53) |
| %w | weekday (0 to 6, Sunday is 0) |
| %W | week number of the year, Monday as first day of week (00 to 53) |
| %x | local date representation |
| %X | local time representation |
| %y | year without century (00 to 99) |
| %Y | year with century |
| %Z | time zone offset in hours and minutes, as set by timezone function |
| %% | percent sign (%) |
The math2 library functions use the following structure and type.
struct int32 {
unsigned int low; /* Lower 16 bits */
unsigned int high; /* Upper 16 bits */
};
/* 32-bit number represented as two 16-bit values */
typedef struct int32 int32_t;Note: off_t in <stdlib.h> and pos_t in <stdio.h> are also defined by struct int32 typedefs.
The following functions are supported in the ElfC math32 library.
- abs32(a) - 32-bit absolute value of a
- add32(a, b) - 32-bit addition: returns a + b
- sub32(a, b) - 32-bit subtraction: returns a - b
- mul32(a, b) - 32-bit subtraction: returns a - b
- cmp32(a, b) - Compare two 32-bit numbers, returns -1 if a < b, 0 if a == b or 1 if a > b
- shl32(a) - Shift 32-bit number left by 1 bit
- shr32(a) - Shift 32-bit number right by 1 bit
- div32(a, b, *rem) - 32-bit division: returns quotient, remainder in *rem
- to_int32(int n) - Convert 16-bit number to 32-bit number with sign extension
- neg32(a) - Negate a 32-bit number
- atoi32(char *str) - Convert a string into 32-bit integer
- char *itoa32(a, char *str) - Convert 32-bit integer to string, returns pointer to beginning of string
- strtoi32(const char *nptr, char **endptr, int base) - Convert string to 32-bit integer
Note: all variables and return values are type int32_t, unless typed differently
- If
_ELFCLIB_is defined, C code is compiled for an Elf/OS library procedure. - If
_STGROM_is defined, assembly code to support STG ROM breakpoints is created. - If
_MAXMON_is defined, assembly code to support MAXMON ROM breakpoints is created. BRKPTinserts assembly code in the code file to invoke the break point handler, when_STGROM_or_MAXMON_is defined.__LINE__inserts the current line number in the code file.__FILE__insert the current file name in the code file.__FUNCTION__insert the current function name in the code file.- If
NDEBUGis defined, theassertmacro is ignored. - The <assert.h> header file defines the
assertmacro. - The <stdarg.h> header file defines the
va_listtype and theva_start,va_argandva_endmacros. - The <stdio.h> header file defines the
getcharandputcharmacros. - The <stdlib.h> header file defines the
abs,MINandMAXmacros. - The
__ELFIO__macro is defined when the-Moption is used to compile code.
Note: The __LINE__, __FILE__, __FUNCTION__ and __ELFIO__ macros begin and end with two underscores.
- The
setjmplibrary is not supported. - The
signallibrary is not supported. - The
mathlibrary is not supported, because there are no real types (float or double) in this release.
- The
float.hheader file is not supported. - The
locale.hheader file is not supported. - The
stdlib.hheader file implements definitions forunistd.h,stddef.handfcntl.h. - The
unistd.h,stddef.handfcntl.hheader files are empty except for a single#include <stdlib.h>statement.
| Option | Description |
|---|---|
| -c | compile and assemble only, do not link |
| -d opt | activate debug option OPT, ? = list |
| -o file | write linker output to FILE |
| -t | test only, generate no code |
| -v | verbose output |
| -D m=v | define macro M with optional value V |
| -L | compile and assemble a library object file |
| -N | do not link stdlib and stdio by default |
| -M | use smaller elfstd and elfio libraries |
| -P | print expanded macro text |
| -S | compile to assembly language |
| -V | print version and exit |
| Debug Options | |
| -d gsym | dump global symbols |
| -d lsym | dump local symbols |
| -d stat | print usage statistics |
| -d tree | dump abstract symbol tree (AST) |
Details about the ElfC internal implementation can be found on the ELFC Internal Information page.