]> git.sur5r.net Git - cc65/blobdiff - samples/multidemo.c
Added somewhat more complex demo. It looks for emdrivers in the current directory...
[cc65] / samples / multidemo.c
diff --git a/samples/multidemo.c b/samples/multidemo.c
new file mode 100644 (file)
index 0000000..e780e07
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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 (&copyinfo);
+        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 ();
+}