Now that I can communicate with both the SD card, and the ethernet (Wiznet) interface without interference from each other, I need to return to a key part of the system: The software. I have the following implemented already, with the intention of building it into more of an OS than just a library of random utilities.
I've implemented a simple malloc replacement. The reasons I am not using the built-in libc malloc are 1) I want to write an allocator because I've never done so, and 2) I plan to do some unusual things that regular malloc will not support. Namely, I want to make use of handles. I already have simple malloc and free working, the next thing is to work on is dismiss and summon:
handle dismiss(void*) : Given a pointer to a block, returns a handle to that block. The pointer from this point forward is considered invalid. The pointer input must be at the start of a block originally returned by malloc. (You can't just dismiss part of an array or struct, only the whole thing.)
void* summon(handle) : Given a handle, returns a pointer containing the data that was saved in the handle. The pointer returned may be different than the original one passed in to dismiss.
The idea here is that such a small heap may easily become fragmented. Large data structures, like trees, linked lists, etc, may refer to their members through the use of handles instead of pointers. Traversing a linked list will always 'summon' the next element, and 'dismiss' the previous element. What is the point of this? Summon and dismiss track what objects are currently in use, and provides a way to safely move objects not in use. The heap may be de-fragmented. Objects on the heap may even be moved out of internal AVR ram and into unused parts of the external (video) RAM, OR even swapped to disk. Poor man's virtual memory. The goal is a C program won't care where a summoned struct is coming from.
I've been running (in simavr, not on the actual Cat-644) a simple round-robin cooperative multitasking scheduler. I
read/write sd card blocks (raw)
each block has a checksum, multiple blocks can be chained to create longer files
delete (once a block is used, there's no recycling (yet). Remember filling 1GB from this machine will take forever. I have a while to deal with it.)
directories: To read previously stored data, you need to know the block number to start reading from. This is exceedingly primitive. Plans are to put a directory file on 'block 1'.
I have created 'block device' and 'character device' abstractions. Note, the definitions I'm using here are not quite how we would define them in LInux:
generic device: Only supported call is 'ioctl'.
character device: Can read/write 1 byte at a time (getc, putc), and has a test to tell if a character is ready (kbhit). Implemented block devices: ps2key (input only), serial (both), vgaconsole (output only), file (as on disk). Also has ioctl. For serial ports, IOCTL_SETBAUD, and for files, IOCTL_SEEK.
block device: Can read/write 1 block at a time. All blocks are the same size (SD card is 512 bytes), and addresses are block numbers. THe only supported calls are ioctl, readblock, and writeblock.
I have plans to let files and other char devices read and write more than 1 byte at a time, but I'll get to that as a lower priority.
implemented: drawdot, drawchar, drawsprite, clear screen, flip video page