/* * 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 (); }