--- /dev/null
+/*
+ * Minimalistic overlay demo program.
+ *
+ * 2012-17-07, Oliver Schmidt (ol.sc@web.de)
+ *
+ */
+
+
+
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <em.h>
+#include <conio.h>
+
+
+/* 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 ();
+}