Programming for Cibyl
From SpelWiki
This page describes guidelines for how to port and write efficient programs for Cibyl.
Contents |
Profiling
You will know this from teaching: Before you start chasing performance problems and bugs, run the profiler to see where the cycles are spent. Recent versions of Cibyl generate output which is possible to parse by the J2ME emulator profiler (enable it in ktoolbar).
Java functionality
Calling Java functionality
Java methods are called trough a C API, which is defined on the Cibyl API page. The first parameter is normally the this pointer for non-static methods (and non-constructors).
Exception handling
Java exceptions in Cibyl are handled similarly to what native Java does. Cibyl defines a NOPH_try / NOPH_catch pair, which does almost the same thing as the Java try / catch. The main difference is that the exception is not handled in a block following the catch statement, but through a function pointer. A typical example of this use is the following:
static void exception_handler_fatal(NOPH_Exception_t exception, void *arg)
{
NOPH_Throwable_printStackTrace(exception);
NOPH_delete(exception);
exit(1);
}
...
{
NOPH_try(exception_handler_fatal, NULL) {
record_store = NOPH_RecordStore_openRecordStore(buf, FALSE);
variable = 0; /* Will not be executed if openRecordStore threw an exception */
} NOPH_catch();
...
}
which will catch the exception, print it out and then return. You pass the function pointer and the argument in the NOPH_try statement. There is a builtin exception handler, NOPH_setter_exception_handler which will set the argument you pass to it (the address of an int) to 1 if the exception occurs. You can throw exceptions by using the NOPH_throw call, which takes an exception object as argument. I.e., something like
{
...
NOPH_throw( NOPH_Exception_new_string("Informative string describing this exception") );
}
This entry on Simon's blog describes the implementation.
Event handling
J2ME ui events are handled through C callback functions. For example, the Canvas::keyPressed() event can be handled by
static void keyPressed(int keyCode)
{
printf("Key pressed: %d\n", keyCode);
}
int main (int argc, char *argv[])
{
NOPH_Canvas_registerKeyPressedCallback(keyPressed);
...
while(1) {
/* Something has to be called for the events to be delivered */
NOPH_Thread_sleep(100);
}
This entry on Simon's blog describes the implementation.
Code
Language
Cibyl is constructed to support high-level languages, and it is recommended to avoid writing directly in assembly. While it is possible to write code in MIPS assembly, it is not recommended since some features depend on how GCC compiles code for higher-level languages.
Signedness
Use signed values. Java does not have an unsigned integer datatype, and unsigned values can sometimes be more inefficent because of that.
Data types
The 32-bit signed int is generally the most efficient type in Cibyl, although what matters is really the generated code. Integer types are more efficient than floating point.
For floating point support, the float type will give better performance than the double type. The long double type is not supported.
Function handling
- Avoid function pointers: these have to go through a jump table whereas normal calls are done directly just calls of normal static Java methods
- Method calls have a certain overhead, so if inlining can be used it is normally beneficial
Memory handling
Alignment and size
4-byte accesses are the most efficient. You should try to choose your types accordingly and avoid 1- and 2-byte types. In many cases, the main performance culprit will be e.g., drawing, and then the types used have only a minor influence.
Try to keep data aligned on natural borders, i.e., 4-byte words should be aligned on a 4-byte border and so on. This avoids the expensive lwl and lwr instructions. The compiler will most of the time take care of this automatically.
Dynamic allocation
A new malloc implementation has improved performance a lot, so malloc/free can no longer be considered performance problems.
Library support
ANSI C
The ANSI C library supports file reading with fopen, fread and friends, but performance has not been a priority when implementing these. If you want better performance, it's a good idea to use exported Java classes instead. Link with -lc to get this.
libjava
libjava contains implementations of the InputStream and OutputStream Java classes for the C FILE operations. Link with -ljava
libmidp
libmidp contains J2ME-specific implementations of the the Connector class for the C FILE operations. Link with -lmidp
libjsr075
Using the JSR075 package allows you to read and write files on the phone using the Java FileConnection class wrapped around the C FILE operations.
The Java FileConnection API requires a preprocessing step when generating the Java wrappers. To do this, invoke cibyl-generate-java-wrappers with the -D JSR075 option. This can be done automatically by adding
CIBYL_GENERATE_JAVA_WRAPPERS_OPTS="-D JSR075"
to your project Makefile (in the project base directory) and linking with -ljsr075
Other libraries
There are also some other libraries shipped with Cibyl, libcibar and libs9.
libcibar is a library to implement file operations in an in-memory-loaded file. This allows some file operations to complete much faster than otherwise, since the file operations are done by copying to/from memory. The cibyl-generate-cibar tool is used to generate a .cibar file from a directory. The name comes from Cibyl-AR, since the functionality of the tool is somewhat similar to the AR tool.
libs9 is an implementation of a T9-like database. This is used to use T9 input with user-specified dictionaries. It is (will be) used by the Sarien port to Cibyl for text entry while using the in-game dictionary. The name comes from "Simon9" or "Sarien9".
Configuration
The file java/CibylConfig.java contains some configuration variables for Cibyl. This is further described on the Cibyl directory structure page.
You can change the configuration options by editing the CibylConfig.java file (which is normally copied to src/ from the Cibyl base directory). Copy this file to your application directory and then copy it to src/ when compiling. If you use the Makefile system, you can add a rule in your Makefile:
src/CibylConfig.java: CibylConfig.java
cp $< $@
Useful tools
- objdump: A disassembler can be useful to check the code properties
- Dissy: My graphical frontend to objdump which allows easier navigation through disassembled code
