CokeOS v3 is somewhat generic, in that it knows nothing about the Coke machine or any physical devices. Instead, it allows code and data to be uploaded to the board, provides user code with timer and interrupt access, and allows user code to communicate in a standardized manner with the host and with each other.
The kernel spends most of its time waiting for incoming commands over the serial port. While waiting, it checks for timer functions that need to be executed. The resource manager handles memory. Everything that is uploaded to the board is called a resource and is assigned a resource identifier (rezID) that can be used as a handle to refer to the uploaded data. Resources can be used for all kinds of things: executable code, fonts, graphics bitmaps, graphics scrips, command scripts, doodad patterns, text strings, audio clips, and whatever else a particular module might want to use that can be uploaded to the board.
There are four ways of getting the functions in your code called: IPC, commands, timers, and hooks.
|IPC_Coke_Hello||the module has just been relocated and initialized|
|IPC_Coke_Goodbye||the code resource is being freed|
|IPC_Coke_Execute||the Coke_ExecCodeResource command has been called|
If you want your module to receive more IPC messages than just that, you need to register your module ID using Coke_RegisterIPC. Then, other modules can call the IPC entry point using the ID, and the entry point will also be called when an IPC message is broadcast to all modules.
The entry point is called with the IPC message code in a, and a pointer to a parameter table in x. The pointer in x may be used to pass data in either direction (or both) or it may be ignored. The IPC function should return with the carry clear if it handled the message, or carry set if it didn't. The IPC function should preserve the registers.
The only differences between a command handler and a normal function are that the command handler calls Coke_GetChar for each parameter that the command requires, and a handler has standardized return values. Upon return, register a must be one of the following:
|COKE_NAK||the command is not implemented|
|COKE_ACK||the command was executed successfully|
|COKE_FAIL||the command failed. Register b must contain the error code.|
|COKE_QUERY||the command was executed successfully and is returning data to the host. Register b contains the length of the query data to return, and register x holds a pointer to the data in memory.|
The macro _endhook is used to implement a linked list of hook functions, so any number of functions can be hooked to a single interrupt, and hooks can be installed and removed in any order. When writing a hook, keep in mind that it is being called from within an interrupt, and take appropriate precautions to not call non-reentrant functions or otherwise disturb the main code.
Coke_GetChar: returns the next parameter of a command in a. The parameter may come from the serial port or from a script.
Coke_SendMessage: takes a message code in a. Adds the message code to the outgoing message queue, and sends it out to the host the next time through the idle loop. It is safe to call SendMessage from both main code and interrupt routines.
Coke_InstallCommand: takes a command number in a, function pointer in x. Installs the command so that invoking that command will call the given function.
Coke_InstallCommandTable: takes a pointer to a command table in x.
Each entry of the table is of the form:
The table ends with a zero for commandnumber.
Coke_RemoveCommand: takes a command number in a. Removes the command so that subsequent invokations of the command return COKE_NAK.
Coke_RemoveCommandTable: takes a pointer to a command table in x. Removes all the commands in a table. The table has the same format as with Coke_InstallCommandTable above.
Coke_SetTimer: takes a timer ID in a, param in b, function pointer in x, timeout value in y. Installs a timer function, or updates a timer function if the given timer ID is already installed. The function will be called approximately every (8 * timeoutvalue) milliseconds, during idle time. If the timeout value is zero, the function is called every time through the idle loop. Param is passed to the timer function in a when it is called, and can be used for whatever the function wants. Timer functions should leave all registers unmodified.
Coke_RemoveTimer: takes a timer ID in a. Removes the given timer function from the timer list, if it is installed.
Coke_InstallHook: takes a hook code in a, pointer to the end
of a hook routine in x. Installs a hook function into an interrupt hook.
Any number of functions can be installed into an interrupt hook. The end of
the hook function (which x points to) should consist of the _endhook
macro. Hook functions do not need to leave the registers unmodified. Hook code
is one of the following:
|HOOK_USER||8 ms real-time interrupt|
|HOOK_AUDIO||interrupt whose rate is controlled by the system variable audiodelay and can be toggled with the system call sys_audio.|
|HOOK_GFX||interrupt whose rate is controlled by the system variable gfxdelay.|
|HOOK_IRQ||external /IRQ line|
|HOOK_SWI||software interrupt, invoked with the SWI command.|
Coke_RemoveHook: takes a hook code in a, pointer to the end of a hook routine x. Removes the given hook function from the given hook. Hook functions can be removed in any order.
Coke_RegisterIPC: takes a module ID in a and a pointer to the IPC entry point in x. Registers the entry point with the kernel so it can receive broadcast messages and other modules can call it with Coke_CallIPC.
Coke_UnregisterIPC: takes a module ID in a. Unregisters the module's IPC entry point.
Coke_CallIPC: takes an IPC message code in a, flags or a
module ID in b, and an optional pointer to a parameter table in
x. Calls an IPC function or broadcasts a message. Register b
can either contain a module ID or the logical OR of the following flags:
|IPCFLAG_Broadcast||all registered IPC entry points are called with the given message. Carry is always clear on return.|
|IPCFLAG_StopAfterOne||if IPCFLAG_Broadcast is set, all registered IPC entry points are called until one handles the message (returns with carry clear), at which point the broadcast is stopped. Carry is returned clear if someone handled the message, set if no one did.|
Coke_InvokeCommand: takes a command number in a, and parameters on the stack. Allows a routine to call a command handler by pushing parameters on the stack. The parameters required by the command must be pushed in REVERSE ORDER. (This also means that words must be pushed explicitly as hibyte, then lobyte.) InvokeCommand cleans up the stack itself, so the caller doesn't need to pull off what it pushed. However, REGISTERS ARE NOT PRESERVED! The command's response is returned in a -- all other registers are indeterminate.
Coke_InvokeCommandMem: takes a command number in a, pointer to a parameter list in x. Allows a routine to call a command handler by providing a pointer to a parameter list in memory. Note that words must be stored in memory with lobyte first. Registers are preserved, except for a, which returns the command's response.
Resource_CreateResource: takes resource size (in bytes) in d. Allocates a chunk of memory for whatever a module wants. Attributes are initially set to zero, but can be changed with Resource_SetAttributes. If there isn't enough room to allocate the memory, carry is returned set. Otherwise, carry is returned clear and the new resource ID is returned in b.
Resource_GetResource: takes a resource ID in b. If the resource does not exist, carry is returned set. Otherwise, carry is returned clear and a pointer to the start of the resource data is returned in x.
Resource_GetLength: takes a resource ID in b. If the resource does not exist, carry is returned set. Otherwise, carry is returned clear and the size in bytes of the given resource is returned in d.
Resource_GetAttributes: takes a resource ID in b. If the resource does not exist, carry is returned set. Otherwise, carry is returned clear and the attributes byte of the resource is returned in a.
Resource_SetAttributes: takes a resource ID in b and an attributes byte in a. If the resource does not exist, carry is returned set. Otherwise, carry is returned clear and the attributes byte of the resource is set to the given value.
Resource_FreeResources: takes a resource ID in b. Frees all resources after and including the given ID.
cokedasm coke.asm -r -ocoke.rel -D__module
To assemble normally, use this:
cokedasm coke.asm -f3 -ocoke.out -c
You can then upload coke.out as a resource and use the Program module to write it into the external EEPROM.
Here are the include files: