diff --git a/samples/Makefile b/samples/Makefile index 81372f802..4499586ce 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -78,6 +78,7 @@ EXELIST = ascii \ hello \ mandelbrot \ mousedemo \ + multdemo \ nachtm \ ovrldemo \ plasma \ @@ -121,6 +122,9 @@ else mousedemo: mousedemo.o endif +multdemo: multidemo.o + @$(LD) -t $(SYS) -m $(basename $@).map -C $(SYS)-overlay.cfg -o $@ $^ $(CLIB) + nachtm: nachtm.o ovrldemo: overlaydemo.o diff --git a/samples/multidemo.c b/samples/multidemo.c new file mode 100644 index 000000000..e780e07a2 --- /dev/null +++ b/samples/multidemo.c @@ -0,0 +1,253 @@ +/* + * Minimalistic overlay demo program. + * + * 2012-17-07, Oliver Schmidt (ol.sc@web.de) + * + */ + + + +#include +#include +#include +#include +#include +#include +#include + + +/* The symbols _OVERLAY?_LOAD__ and _OVERLAY?_SIZE__ were generated by the + * linker. They contain the overlay area address and size specific to a + * certain program. + */ +extern void _OVERLAY1_LOAD__, _OVERLAY1_SIZE__; +extern void _OVERLAY2_LOAD__, _OVERLAY2_SIZE__; +extern void _OVERLAY3_LOAD__, _OVERLAY3_SIZE__; +extern void _OVERLAY4_LOAD__, _OVERLAY4_SIZE__; + +struct { + char *name; + int page; + void *addr; + unsigned size; +} overlay[] = + {{"multdemo.1", -1, &_OVERLAY1_LOAD__, (unsigned)&_OVERLAY1_SIZE__}, + {"multdemo.2", -1, &_OVERLAY2_LOAD__, (unsigned)&_OVERLAY2_SIZE__}, + {"multdemo.3", -1, &_OVERLAY3_LOAD__, (unsigned)&_OVERLAY3_SIZE__}, + {"multdemo.4", -1, &_OVERLAY4_LOAD__, (unsigned)&_OVERLAY4_SIZE__}}; + +/* Copy overlays into extended memory up to overlay 3. Overlay 4 is known to + * to be loaded only once for onetime initialization purposes so there's no + * use in allocating extended memory for it. + */ +#define MAX_EM_OVERLAY 3 + + +/* Functions resident in an overlay can call back functions resident in the + * main program at any time without any precautions. The function log() is + * an example for such a function resident in the main program. + */ +void log (char *msg) +{ + /* Functions resident in an overlay can access all program variables and + * constants at any time without any precautions because those are never + * placed in overlays. The string constant below is an example for such + * a constant resident in the main program. + */ + printf ("Log: %s\n", msg); +} + + +/* In a real-world overlay program one would probably not use a #pragma but + * rather place the all the code of certain source files into the overlay by + * compiling them with --code-name OVERLAY1. + */ +#pragma code-name (push, "OVERLAY1"); + +void foo (void) +{ + log ("Calling main from overlay 1"); +} + +#pragma code-name (pop); + + +#pragma code-name (push, "OVERLAY2"); + +void bar (void) +{ + log ("Calling main from overlay 2"); +} + +#pragma code-name (pop); + + +#pragma code-name (push, "OVERLAY3"); + +void foobar (void) +{ + log ("Calling main from overlay 3"); +} + +#pragma code-name(pop); + + +#pragma code-name (push, "OVERLAY4"); + +unsigned char loademdriver (void) +{ + DIR* dir; + struct dirent* ent; + + printf ("Dbg: Searching for emdrivers\n"); + dir = opendir ("."); + if (!dir) { + log ("Opening directory failed"); + return 0; + } + + while (ent = readdir (dir)) { + char *ext; + + if (!_DE_ISREG (ent->d_type)) { + continue; + } + + ext = strrchr (ent->d_name, '.'); + if (!ext || strcasecmp (ext, ".emd")) { + printf ("Dbg: Skipping file %s\n", ent->d_name); + continue; + } + + printf ("Dbg: Trying emdriver %s\n", ent->d_name); + if (em_load_driver (ent->d_name) == EM_ERR_OK) { + printf ("Dbg: Loaded emdriver %s\n", ent->d_name); + break; + } + + printf ("Dbg: Emdriver %s failed\n", ent->d_name); + } + + closedir (dir); + return ent != NULL; +} + +void copyoverlays (void) +{ + unsigned page = 0; + unsigned char num; + + for (num = 0; num < MAX_EM_OVERLAY; ++num) { + int file; + int size; + + if ((overlay[num].size + EM_PAGE_SIZE - 1) / EM_PAGE_SIZE > + em_pagecount () - page) { + printf ("Dbg: Not enough memory for overlay %u\n", num + 1); + continue; + } + + printf ("Dbg: Reading overlay file %s\n", overlay[num].name); + file = open (overlay[num].name, O_RDONLY); + if (file == -1) { + log ("Opening overlay file failed"); + continue; + } + + overlay[num].page = page; + size = overlay[num].size; + while (size) { + void *buf; + + /* In general one could as well use em_copyto() to copy a fully + * loaded overlay into extended memory in one step. However the + * "streaming" of an overlay from disk to extended memory shown + * here has two advantages: + * - It can be done from another overlay (like done here). + * - It avoids unnecessary double buffering with emdrivers that + * provide a hardware memory window. + */ + buf = em_use (page++); + size -= read (file, buf, EM_PAGE_SIZE); + em_commit (); + } + + printf ("Dbg: Stored overlay %u in pages %u-%u\n", + num + 1, overlay[num].page, page - 1); + + close (file); + } +} + +#pragma code-name(pop); + + +unsigned char loadoverlay (unsigned char num) +{ + if (overlay[num - 1].page < 0) { + int file; + + printf ("Dbg: Loading overlay %u from file\n", num); + file = open (overlay[num - 1].name, O_RDONLY); + if (file == -1) { + log ("Opening overlay file failed"); + return 0; + } + read (file, overlay[num - 1].addr, + overlay[num - 1].size); + close (file); + return 1; + } else { + struct em_copy copyinfo; + + printf ("Dbg: Loading overlay %u from memory\n", num); + copyinfo.offs = 0; + copyinfo.page = overlay[num - 1].page; + copyinfo.buf = overlay[num - 1].addr; + copyinfo.count = overlay[num - 1].size; + em_copyfrom (©info); + return 1; + } +} + +void main (void) +{ + if (loadoverlay (4)) { + log ("Loading extended memory driver"); + if (loademdriver ()) { + log ("Copying overlays into ext. memory"); + copyoverlays (); + } else { + log ("No extended memory driver found"); + } + } + + log ("Press any key..."); + cgetc (); + + if (loadoverlay (1)) { + log ("Calling overlay 1 from main"); + + /* The linker makes sure that the call to foo() ends up at the right mem + * addr. However it's up to user to make sure that the - right - overlay + * is actually loaded before making the the call. + */ + foo (); + } + + /* Replacing one overlay with another one can only happen from the main + * program. This implies that an overlay can never load another overlay. + */ + if (loadoverlay (2)) { + log ("Calling overlay 2 from main"); + bar (); + } + + if (loadoverlay (3)) { + log ("Calling overlay 3 from main"); + foobar (); + } + + log ("Press any key..."); + cgetc (); +}