Return to Guide Menu   Return to Main Menu

= = = 2. WRITING PSEUDO C PROGRAMS = = =

DUO Light Pseudo C is based on the C programming language. Pseudo C consists of ASCII statements which are compiled into DUO Light Bytecode. I recommend using Pseudo C to write programs because it is easier than assembly or manually producing bytecode.

To compile a Pseudo C program, you must use the Pseudo C compiler. Download the Pseudo C compiler from this page.

The compiler is a Python script. To run a Python script, you must install Python on your computer. Download Python from this page and install Python on your computer. Note that OS X should already include Python.

Now create a plain text file (with the type .txt). On Windows you can use WordPad to create a plain text file. WordPad is located in the "Accessories" folder. Make sure to save as a plain text document. On OS X you can use TextEdit. Just press command + shift + T to convert the document type from rich text to plain text.

Copy and paste the following code into the text document:

writeDisplay(0, 0, "Hello!");
endOfProgram:
goto endOfProgram;

Finally, execute the compiler script in the command prompt using the text file as an argument. In other words, type this into your command prompt:

(path to compiler) (path to text file)

If everything goes well, the compiler will print a success message and create a .dlbc file in the same directory as the text file. This .dlbc file is a DUO Light bytecode program. You can confirm that the program works by using an actual DUO Light or the DUO Light emulator.

Now you are ready to write and compile your own Pseudo C programs!

= PSEUDO C SYNTAX AND KEYWORDS =

Pseudo C code consists of a series of statements. It is possible to nest statements within each other. There are several kinds of statements:

Variables are allocations of memory. Variables are used to store data such as numbers and text. Pseudo C has 5 base variable types:

To declare a variable, place the base type, then a variable name, and finally a semicolon. Example:

byte myCharacter;
long myNumber;

All integers are unsigned by default. Unsigned integers cannot be negative. To declare a signed integer variable, place signed before the variable base type. Example:

signed byte myLittleNumber;
signed long myLargeNumber;

A pointer is a kind of variable which refers to the location of another value. To declare a pointer variable, place one or more asterisks before the variable name. Example:

byte *myPointer;
long **myLocation;

In the example above, myPointer refers to a byte location. On the other hand, myLocation refers to another pointer which in turn refers to a long location. The number of asterisks determines the "depth" of the pointer variable.

An array is an allocated sequence of values with fixed length. To declare an array, place an integer length in brackets after the variable name. Example:

byte myText[20];
long myList[15];

In the example above, myText stores 20 byte values, while myList stores 15 long values.

There are many different operators in Pseudo C:

You can use several types of operands with operators:

There are a handful of escape sequences which may be used in character or string literals:

An operation statement contains an operation followed by a semicolon. Example:

byte myNumber;
myNumber = 100;
myNumber++;
byte myResult;
myResult = myNumber + 5;

In a similar fashion to classic arithmetic, operations may be grouped by using parentheses. Example:

byte myNumber;
myNumber = (2 + 3) * 4;

On a technical level, arrays are actually pointers to allocated values. As a result, you can use the bracket operator with an array to access an array element. Example:

byte *myArray;
myArray = {2, 4, 9, 80};
byte myNumber;
myNumber = myArray[2];

A variable initialization statement simultaneously declares a variable and assigns a value to the variable. Place an equal sign between a declaration statement and a value or operation. Example:

byte myNumber = (2 + 3) * 4;
float myQuantity = 90.4;

A code block is a way to group statements together. Code blocks are enclosed by curly braces. Variables declared within a code block cannot be accessed from statements outside of the code block. However, statements inside a code block can access variables from parent code blocks or the top level "scope". Example:

byte myNumber = 5;
{
byte myQuantity = myNumber;
}

Functions are invocable code blocks which may be passed arguments and may return values. To declare a function, place a return value declaration, then comma separated argument declarations enclosed by parentheses, and finally a code block. Example:

short myNumber = 10;

void subtractAndMultiplyMyNumber(short num1, short num2)
{
myNumber -= num1;
myNumber *= num2;
}

subtractAndMultiplyMyNumber(2, 3);

It is worth noting that arguments are passed by reference instead of by value. This means that if you pass a variable into a function as an argument, and if you modify the argument, the original variable will also be modified. BE MINDFUL OF THIS.

To abort execution of a function, use the return keyword. In addition, you can place a value after return which will be passed to the caller. Example:

short subtractAndMultiply(short input, short num1, short num2)
{
short result = input;
result -= num1;
result *= num2;
return result;
}

short myNumber = subtractAndMultiply(10, 2, 3);

To mark a location in your code, place a label name followed by a colon. To jump execution to a label, place goto, then a label name, and finally a semicolon. Example:

byte myNumber = 0;
myLoop:
myNumber++;
goto myLoop;

Note that goto jumps into, out of, and between functions will result in unpredictable behavior.

An if statement consists of the keyword if, an operation or value, and a code block. The code block is executed only if the value is non-zero. Example:

byte myNumber = 0;
if (5)
{
myNumber += 2;
}
if (0)
{
myNumber += 3;
}

An else statement may immediately follow an if statement. The code block of an else statement is executed only if the previous code block was not executed. Example:

byte myNumber = 0;
if (5)
{
myNumber += 2;
} else {
myNumber += 3;
}

A while statement is used to execute a code block many times until the given condition returns zero. Example:

byte myNumber = 0;
byte myCount = 6;
while (myCount)
{
myNumber += 3;
myCount--;
}

Use a break statement to abort a while loop. A break statement requires a semicolon. Example:

byte myNumber = 0;
byte myCount = 10;
while (1)
{
if (myCount < 7)
{
break;
}
myNumber += 3;
myCount--;
}

A continue statement is similar to a break statement, except control returns to the start of the while loop. Example:

byte myNumber = 0;
byte myCount = 5;
while (1)
{
myCount--;
if (myCount == 2)
{
continue;
}
myNumber += myCount;
}

Note that array literal values are not copied when used in a statement. If you assign an array literal to a variable, modify the variable, and run the assignment again, the variable will not revert to the original value. BE MINDFUL OF THIS. Example:

byte *myList;
while (1)
{
myList = {8, 7, 6, 5, 4};
byte myNumber = myList[3];
myList[3] = 150;
}

In the example above, myNumber will first be assigned a value of 5. However, during the second iteration, myNumber will be assigned a value of 150 (because the array literal in memory has changed).

You can include a single line comment by placing two forward slashes and then some text. Example:

byte myNumber = 2;
// This will increment myNumber.
myNumber++;

If you want to replace a term in your code with text, you can use a precompiler definition. Place #define, then a term, and finally the text to replace the term. Example:

#define myConstantValue 2
byte myNumber = myConstantValue;

Note that precompiler definitions are not stored in DUO Light memory, so precompiler definitions may be used to save space.

= BUILT-IN PSEUDO C FUNCTIONS =

Pseudo C contains many functions which invoke useful bytecode instructions.

There are two variable types which are unique to built-in functions:

Here is a list of built-in functions with descriptions.

void write(short len, data *src, data *dest)
Copy len number of bytes from src to dest.

void fill(int data, int2 amount, data *dest)
Copy data into dest amount number of times.

signed int2 find(int data, int2 len, data *src)
Try to find data in src with length len number of bytes. Return the index of the first occurrence, or -1 if data was not found.

void convertIntToString(int num, byte *dest)
void convertSignedIntToString(signed int num, byte *dest)
void convertFloatToString(float num, byte *dest)
Convert the number num into a string and store in dest.

int convertStringToInt(byte *text)
signed int convertStringToSignedInt(byte *text)
float convertStringToFloat(byte *text)
Convert the string text into a number.

void writeString(byte *text, byte *dest)
Copy string text to dest.

byte equalString(byte *text1, byte *text2)
Determine whether string text1 and string text2 are equal.

signed int findString(byte *pattern, byte *text)
Try to find string pattern in string text. Return the index of the first occurrence, or -1 if pattern was not found.

int getStringLength(byte *text)
Determine the length of string text.

void getSubstring(byte *text, int startIndex, int endIndex, byte *dest)
Copy from string text to dest starting at startIndex inclusive and ending at endIndex exclusive.

void concatenateString(byte *text, byte *dest)
Copy string text to the end of string dest.

int getSpaceDelimitedStringLength(byte *text)
Determine the number of elements in space delimited string text.

void getSpaceDelimitedStringElement(byte *text, int index, byte *dest)
Copy the element in space delimited string text at offset index to dest.

int random(int max)
Generate a random number from 0 inclusive to max exclusive.

float sine(float num)
Determine the sine of num radians.

void writeTime(int num)
Set the DUO Light internal clock to num milliseconds.

int readTime()
Retrieve the time in milliseconds from the DUO Light internal clock.

void sleep(int num)
Suspend program execution for num milliseconds.

void clearDisplay()
Erase all text on the display.

void writeDisplay(int posX, int posY, byte *text)
Write string text to the display at horizontal position posX and vertical position posY.

void readDisplay(int posX, int posY, int2 len, byte *dest)
Read string with length len number of bytes from the display at horizontal position posX and vertical position posY. Store the string in dest.

The next few functions accept or return PS/2 keyboard key codes. A key code in the DUO Light is represented as a short. The more significant byte may either be 0x00 or 0xE0. The less significant byte may have a variety of values. For a list of PS/2 keyboard key codes, please look at this page.

void convertKeyToString(short key, byte shift, byte *dest)
Convert key code key to a string. Boolean value shift determines whether to convert as though the shift key is held. Store the string in dest.

short convertStringToKey(byte *text)
Convert string text to a key code.

byte keyIsPressed(short key)
Return true if key key is held, otherwise false.

short promptKey()
Suspend execution until a key is pressed. Return the key code of that key.

void promptString(int posY, byte *dest)
Request for the user to enter a string at vertical position posY on the display with starting string dest. Store the result string in dest. Note: For a blank starting string, set dest to an empty string.

int promptSelection(int amount, byte *text)
Request for the user to select an option from null delimited string text with amount number of elements. Return the index of the selected option.
Example: byte myResult = promptSelection(3, "One\x00Two\x00Three");

int promptFile()
Request for the user to select a file in the top level directory of the SD card. Returns the index of the selected file, not the file handle!

void setPortMode(byte mode, byte pin)
Set mode of GPIO port pin to mode. The mode may be 0 for input or 1 for output.

byte digitalReadPort(byte pin)
Return whether GPIO port pin has a high value.

void digitalWritePort(byte value, byte pin)
Set output value of GPIO port pin to value.

short analogReadPort(byte pin)
Return a number representing the analog voltage level on GPIO port pin. Note that only ports 0 through 5 may perform analog reads.

The next several commands interface with files on the SD card. A file handle is a signed long which represents the physical address of a file entry. If a file handle is -1, the file cannot be found. A file handle will remain constant for the lifetime of the file.

int getNumberOfFiles()
Return the number of files in the top level directory of the SD card.

signed long openFileByIndex(int index)
Return the handle to the file with index index. File indexes range from 0 to the number of files minus one.

signed long openFileByName(byte *text)
Return the handle to the file in the top level directory with name text.

signed long openFileAfterFile(signed long handle)
Return the handle to the file after the file with handle handle.

signed long createFile(byte *name, int size)
Create a file with the name name and size size number of bytes. Returns the handle to the new file. Note that the DUO Light cannot directly change the size of a file after creation. The file's contents must be copied to a new file with a different length.

void readFile(signed long handle, byte *dest)
Copy the contents of file with handle handle into dest.

void writeFile(byte *src, signed long handle)
Copy from src to the file with handle handle.

void getFileName(signed long handle, byte *dest)
Copy the name of file with handle handle into dest.

int getFileSize(signed long handle)
Return the size of file with handle handle.

void deleteFile(signed long handle)
Delete the file with handle handle.

void runFile(signed long handle)
Execute the file with handle handle.

void quit()
Execute start.dlbc.

byte convertStringToOpcode(byte *text)
Convert mnemonic string text to DLBC opcode.

void convertByteToHex(byte num, byte *dest)
Convert number num to a two digit hexadecimal string. Store the string in dest.

byte convertHexToByte(byte *text)
Convert two digit hexadecimal string text to a number. Return the number.

data *allocate(int len)
Dynamically allocate len number of bytes in memory. Return a pointer to the allocation.

void free(data *ptr)
Remove allocation at ptr created by the allocate function.

void scrollDisplay(byte direction, byte *text)
Scroll the display by one character in direction direction. Fill in the blank space with string text. Possible directions are 0 for left, 1 for right, 2 for up, and 3 for down.

int getDisplayWidth()
Return the width of the display measured in characters. For the DUO Light, the result will be 17.

int getDisplayHeight()
Return the height of the display measured in characters. For the DUO Light, the result will be 14.

void gameOfLife(int width, int height, byte *src, byte *dest)
Determine the next state of cell grid src with width width and height height according to rule B3/S23. Store the result in dest. A dead cell is 0x20, and a live cell is 0x01.

byte mandelbrot(float real, float imag, float offset, int iter)
Return four pixels in the Mandelbrot set located at (real + (0 or offset), imag + (0 or offset)) using iteration count iter. Output will range from 0x0B to 0x1A.

float pow(float num, float exp)
Return num raised to the exp power.

float log(float num, float base)
Return the log of num base base.

If you read all of this, congratulations! You are now a Pseudo C guru.

Return to Guide Menu   Return to Main Menu

Return to the Ostracod Pond