Date: 2023-05-05
Copyright © 2021-2023 Pierre Sarrazin
<http://sarrazip.com/>
This manual is available under the
Creative Commons Attribution-ShareAlike License.
This document teaches a few parts of the C programming language to users of the Tandy Color Computer's BASIC interpreter. Those C constructs can be compiled by the CMOC compiler, which generates 6809 code mainly for the Color Computer's Disk Extended Basic environment.
To learn C properly and completely, it is recommended to read a book like The C Programming Language, written by the language's creators, Brian Kernighan and Dennis Ritchie. Several other books and online tutorials also exist. CMOC has its own manual, which explains how to compile a C program to a BIN-format executable.
Strings in C do not exist as they do in Basic. They are represented as arrays of char. See the following section for details.
Start your program with #include <cmoc.h> to declare the functions that are provided by CMOC's small C library, e.g., printf(), rand(), etc. Some CoCo specific functions are declared by #include <coco.h>. A .h file is called a header file.
Basic | C | Remarks | PRINT "FOO" |
printf("FOO\n"); |
\n represents a "new line" character. CMOC's printf() maps this to CHR$(13). Omit it if no line change is wanted. A C statement must be terminated by a semi-colon, except when it finishes with curly braces. |
---|---|---|
|
int i = 42; |
The first line declares an integer variable called i and immediately initializes it to 42. An int can take -32768 to 32767 inclusively (16 bits). The 2nd form separates initialization from declaration. Before being assigned 42, i may contain garbage. Use unsigned instead of int to represent the range 0..65535. |
|
|
32-bit signed integer (-2147483648..2147483647). Use unsigned long to represent an unsigned 32-bit integer (0..4294967295). Use the UL suffix for constants: 70000UL. |
|
|
With CMOC, floating-point arithmetic is only usable if the program runs with Extended Basic in memory. The f suffix on 3.1416 implies the single-precision float type, which means a 40-bit float with CMOC. Without the f, the type is double, which CMOC still maps to a 40-bit float. (On many other platforms, float is 32 bits while double is 64 bits.) |
|
0xFF22 |
32-bit hexadecimal constants are also supported: 0xDEADBEEF. |
|
|
%d means signed integer. %f means float or double. Be careful to pass values of types that match the % placeholders. CMOC's printf does not support all of C's printf's % placeholders and options. %u means unsigned integer (0..65535). |
N$="KNUTH"; |
char name[6]; |
C strings are not dynamically allocated like in Basic. The programmer typically declares an array of characters. The length must be enough for the string's characters, plus a terminating zero byte. Here, the string to store has 5 characters, so the array must have (at least) 6 bytes to accommodate the zero byte. printf(name) could also work here, but only if name contains no % characters. With %, it becomes a potential security flaw. It is recommended to always pass printf a format string in quotes. |
PRINT USING "% %";"X" |
|
The minus sign in %-5s means left justify. Omit it for right justify. C's printf supports printf("%7.3f\n", 1.0f / 3.0f); which would be equivalent to ###.###, but CMOC's printf does not support width and precision for %f, as of 2023. |
1+(2-3*4/5) |
|
C has no exponentiation operator. The FuncPlot program on the CMOC Home Page has a powf() function in its source code that can work if Extended Color Basic is present in memory. |
|
if (a > 9) |
Braces are only required to group more than one statement together. They can be used around a single statement, if preferred. |
|
for (int i = 0; i <= 100; i += 2) |
1st argument of for: Initialization statement. A variable declared here is only valid in the for statement. A previously declarated variable can also be used. 2nd argument: Condition to continue. Evaluated before the 1st iteration. 3rd argument: Statement that updates the control variable. The body only needs braces if it contains more than one statement. |
I = 0 |
int i = 0; |
WHILE is not supported by Color Basic. In C, i = i + 2 could also be used, but may generate slightly less efficient code. |
DO |
int x; |
DO/LOOP is not supported by Color Basic. |
A = A + 1 |
++A; |
The right sides can be complex expressions, e.g., C *= A + B * 2; |
A * 256 |
A << 8 |
<< is the left bit shift operator. It can be used to multiply an integer by a power of 2. >> is the right bit shift operator. It can be used to divide an integer by a power of 2. |
X OR 64 |
x | 64 |
|, & and ~ are bitwise operators. Not to be confused with ||, && and ! which are the corresponding logical operators, typically used in an if or while or do statement. |
|
|
Assumes that a is a character array or a pointer to a sequence of characters (char *). |
A$="FOO" |
char c[N]; |
N must be replaced with a number of characters that is sufficient to contain the characters in arrays a and b, plus the terminating null byte. N must be a constant. In C, malloc() can be used to allocate memory dynamically, but CMOC does not come with such a function. Dynamic allocation is often best avoided on a slow machine. |
|
|
strcmp() is case sensitive. stricmp() is not. strncmp() takes a 3rd argument that is the maximum number of characters to compare. They all return 0 when the two strings are considered equal, -1 when the first string comes before the second one in lexical order, +1 when the first one comes after. |
|
if (a > 0 && b < 0 || c == 1 && d != 2) |
&& has precedence over ||. Both of them return true or false: they are logical operators, not bitwise operators. C also has & and |, which are the bitwise versions. ! is the logical negation operator. ~ is the bitwise inversion operator. Basic does not have an exclusive-or operator; ^ is a bitwise operator in C. |
|
|
If a is a character array, then a[0] is a value of type char, which can be treated as an 8-bit integer. A char value is not a string. (There are no strings per se in C, only character arrays.) |
|
"\x1B" |
A string literal is delimited by double quotes and has the type of a character array. A character literal is delimited by single quotes and has type char: it is a byte-sized signed integer value, not a string. (unsigned char is an unsigned byte.) \x introduces a 2-digit hexadecimal code that can be used to represent special characters. |
N$="1000":N=VAL(N$) |
const char *s = "1000"; |
The floating-point example only works if targeting the CoCo's Extended Basic environment. Its floating-point routines must be present. |
X=1000:A$=STR$(X) |
int x = 1000; |
That array passed to itoa() must have enough characters to represent a 16-bit integer, including the terminaing null character. For example, -32768 takes 5 characters, plus the '\0' that ends the string. The 3rd argument to itoa() must be 10 because CMOC's implementation of that function only supports base 10. Unlike STR$(), itoa() does not start the string with a space when the number is non-negative. ftoa() is only available if targeting the CoCo's Extended Basic environment. The array passed to ftoa() must be at least 38 characters long. The f suffix on 3.1416f specifies that it is a single-precision number. Without that suffix, it would be a double-precision number. CMOC however makes both float and double the same size (5 bytes). It is recommended to use the f suffix when specifying float instead of a double. |
F=INSTR(A$, "Y") |
char *found = strstr(a, "Y"); |
If the 2nd string is found in the 1st string, strstr() returns a pointer to inside the 1st string where the 2nd string was found. If the 2nd string is not found, strstr() returns NULL. The index computed as found - a will be 0 if the 2nd string is found at the beginning of a. Note that INSTR returns 1 in such a case; it returns 0 when the 2nd string is not found. |
A$="FOOBAR" |
char a[7]; |
P$ becomes "FOO", S$ becomes "AR" and M$ becomes "OB". The 2nd argument of MID$ is an index such that 1 is the first character ('F' in this example). In C, array indices start at 0, hence the "- 1" in the last strncpy() call. strncpy() copies at most the number of characters specificed by its 3rd argument. This explains why the string gets terminated explicitly with a '\0' on the following line. |
INPUT N |
char *response = readline(); |
readline() is specific to CMOC. In C, use fgets(). response will point to a zero-terminated string. It can be printed with printf("%s", response); atoi() converts a decimal string to an integer value. atoff() converts a decimal string to a floating point value. (This function only works if targeting the CoCo's Extended Basic environment.) |
10 IF A=1 THEN PRINT "FOO":GOTO 90 |
switch (a) |
Do not forget the break statement at the end of a case, otherwise the execution will continue to the next case. The last case in the switch body does not need a break statement. The argument of the switch statement must be an integer expression, not a float, double or string expression. |
10 X=5 |
int main() |
Parameter n is local to function f(). Even if it were named x, it would still be a different variable than the x declared in main(). A local variable can have the same name as a global one. The local variable then masks the global one over the scope where the local variable exists. A variable is local to the curly braces inside which it is declared. One exception is a variable declared in a for() loop, i.e., for (int i = 0; ...) {...}. That variable exists both in the for() body and in the for() parenthesis. |
|
|
See the BGraph library on the CMOC Home Page. |
|
|
See the BControl library on the CMOC Home Page. |
|
|
See the BSound library on the CMOC Home Page. |
|
|
rand() returns an int (signed) between 0 and 32767 inclusively. % is the modulo operator. In this example, rand() % 10 will return a value between 0 and 9 inclusively, then we add 1 to return 1..10 as in the Basic example. rand() is declared by <cmoc.h> |
|
|
This seeds the random number generator used by rand(). This assumes that #include <coco.h> has been specified, to define getTimer(). The latter assumes that Color Basic is running normally, i.e., with interrupts enabled. srand() is declared by <cmoc.h> |
|
|
See the decbfile library on the CMOC Home Page. |
|
for (init; condition; increment) |
Go To Statement Considered Harmful, CACM, by Edsger Dijkstra, March 1968. To leave a C loop from its middle: break. To jump from the middle of a C loop to the loop condition: continue. |
|
|
The program must have specified #include <cmoc.h> before making this call. |
PROCEDURE stuff |
void stuff(int a, int b) |
Procedures are not supported by Color Basic. They are by BASIC09, which runs under OS-9. This C code defines a function named stuff that accepts integer parameters named a and b. The void keyword means that the function has nothing to return. C does not have procedures: "void" functions are the equivalent. Parameters are always passed by value in C, not by reference. Changing a and b in this function would not change the variables of the calling function. Passing a string is done by passing a pointer to the first character of that string. The name of an array represents the address of its first element: void example(void) { char name[10]; strcpy(name, "Joe"); f(name); } Function f is then declared this way: void f(const char *n) { ... } The const keyword means that function f promises not to modify the data pointed to by n. |
PROCEDURE f |
void f(int *pointerToInteger) |
To receive an integer by reference from function f, one must pass the address of an integer-typed location. The address of a variable is obtained with the & operator. To call this function: int n; f(&n); printf("Got %d from f\n", n); A simpler way to return a single value is the return statement: int f(void) { return 1000; } The type of the returned value of a function is specified before its name (int in this example). A function can have more than one return statement. |
|
|
In C, function stuff() must already have been defined, or at least declared. A C function can be declared without being defined by specifying a line like the following, which is called a function prototype: void stuff(int a, int b); |
|
|
There is no boolean type in C89. CMOC's <coco.h> header file defines BOOL as an unsigned byte. It also defines TRUE as 1 and FALSE as 0. |
|
struct Employee |
Record types are not supported by Color Basic. They are by BASIC09. BOOL, TRUE and FALSE can be defined by #include <coco.h> or with typedef and enum. For convenience, typedef can be used to avoid the requirement to specify struct when declaring an instance of a struct: typedef struct S { char a; int b; } S; S foo = { '@', 1000 }; |
REM A comment line. |
/* A comment line. */ |
CMOC and modern C compilers also accept single-line comments that are introduced by //: // A single-line comment.
|