]> git.sur5r.net Git - cc65/blob - samples/multidemo.c
Added somewhat more complex demo. It looks for emdrivers in the current directory...
[cc65] / samples / multidemo.c
1 /*
2  * Minimalistic overlay demo program.
3  *
4  * 2012-17-07, Oliver Schmidt (ol.sc@web.de)
5  *
6  */
7
8
9
10 #include <string.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <dirent.h>
15 #include <em.h>
16 #include <conio.h>
17
18
19 /* The symbols _OVERLAY?_LOAD__ and _OVERLAY?_SIZE__ were generated by the
20  * linker. They contain the overlay area address and size specific to a
21  * certain program.
22  */
23 extern void _OVERLAY1_LOAD__, _OVERLAY1_SIZE__;
24 extern void _OVERLAY2_LOAD__, _OVERLAY2_SIZE__;
25 extern void _OVERLAY3_LOAD__, _OVERLAY3_SIZE__;
26 extern void _OVERLAY4_LOAD__, _OVERLAY4_SIZE__;
27
28 struct {
29     char     *name;
30     int      page;
31     void     *addr;
32     unsigned size;
33 } overlay[] =
34     {{"multdemo.1", -1, &_OVERLAY1_LOAD__, (unsigned)&_OVERLAY1_SIZE__},
35      {"multdemo.2", -1, &_OVERLAY2_LOAD__, (unsigned)&_OVERLAY2_SIZE__},
36      {"multdemo.3", -1, &_OVERLAY3_LOAD__, (unsigned)&_OVERLAY3_SIZE__},
37      {"multdemo.4", -1, &_OVERLAY4_LOAD__, (unsigned)&_OVERLAY4_SIZE__}};
38
39 /* Copy overlays into extended memory up to overlay 3. Overlay 4 is known to
40  * to be loaded only once for onetime initialization purposes so there's no
41  * use in allocating extended memory for it.
42  */
43 #define MAX_EM_OVERLAY 3
44
45
46 /* Functions resident in an overlay can call back functions resident in the
47  * main program at any time without any precautions. The function log() is
48  * an example for such a function resident in the main program.
49  */
50 void log (char *msg)
51 {
52     /* Functions resident in an overlay can access all program variables and
53      * constants at any time without any precautions because those are never
54      * placed in overlays. The string constant below is an example for such 
55      * a constant resident in the main program.
56      */
57     printf ("Log: %s\n", msg);
58 }
59
60
61 /* In a real-world overlay program one would probably not use a #pragma but
62  * rather place the all the code of certain source files into the overlay by
63  * compiling them with --code-name OVERLAY1.
64  */
65 #pragma code-name (push, "OVERLAY1");
66
67 void foo (void)
68 {
69     log ("Calling main from overlay 1");
70 }
71
72 #pragma code-name (pop);
73
74
75 #pragma code-name (push, "OVERLAY2");
76
77 void bar (void)
78 {
79     log ("Calling main from overlay 2");
80 }
81
82 #pragma code-name (pop);
83
84
85 #pragma code-name (push, "OVERLAY3");
86
87 void foobar (void)
88 {
89     log ("Calling main from overlay 3");
90 }
91
92 #pragma code-name(pop);
93
94
95 #pragma code-name (push, "OVERLAY4");
96
97 unsigned char loademdriver (void)
98 {
99     DIR* dir;
100     struct dirent* ent;
101
102     printf ("Dbg: Searching for emdrivers\n");
103     dir = opendir (".");
104     if (!dir) {
105         log ("Opening directory failed");
106         return 0;
107     }
108
109     while (ent = readdir (dir)) {
110         char *ext;
111
112         if (!_DE_ISREG (ent->d_type)) {
113             continue;
114         }
115
116         ext = strrchr (ent->d_name, '.');
117         if (!ext || strcasecmp (ext, ".emd")) {
118             printf ("Dbg: Skipping file %s\n", ent->d_name);
119             continue;
120         }
121
122         printf ("Dbg: Trying emdriver %s\n", ent->d_name);
123         if (em_load_driver (ent->d_name) == EM_ERR_OK) {
124             printf ("Dbg: Loaded emdriver %s\n", ent->d_name);
125             break;
126         }
127  
128         printf ("Dbg: Emdriver %s failed\n", ent->d_name);
129     }
130
131     closedir (dir);
132     return ent != NULL;
133 }
134
135 void copyoverlays (void)
136 {
137     unsigned page = 0;
138     unsigned char num;
139
140     for (num = 0; num < MAX_EM_OVERLAY; ++num) {
141         int file;
142         int size;
143
144         if ((overlay[num].size + EM_PAGE_SIZE - 1) / EM_PAGE_SIZE >
145              em_pagecount () - page) {
146             printf ("Dbg: Not enough memory for overlay %u\n", num + 1);
147             continue;
148         }
149
150         printf ("Dbg: Reading overlay file %s\n", overlay[num].name);
151         file = open (overlay[num].name, O_RDONLY);
152         if (file == -1) {
153             log ("Opening overlay file failed");
154             continue;
155         }
156
157         overlay[num].page = page;
158         size = overlay[num].size;
159         while (size) {
160             void *buf;
161
162             /* In general one could as well use em_copyto() to copy a fully
163              * loaded overlay into extended memory in one step. However the
164              * "streaming" of an overlay from disk to extended memory shown
165              * here has two advantages:
166              * - It can be done from another overlay (like done here).
167              * - It avoids unnecessary double buffering with emdrivers that
168              *   provide a hardware memory window.
169              */
170             buf = em_use (page++);
171             size -= read (file, buf, EM_PAGE_SIZE);
172             em_commit ();
173         }
174
175         printf ("Dbg: Stored overlay %u in pages %u-%u\n",
176                 num + 1, overlay[num].page, page - 1);
177
178         close (file);
179     }
180 }
181
182 #pragma code-name(pop);
183
184
185 unsigned char loadoverlay (unsigned char num)
186 {
187     if (overlay[num - 1].page < 0) {
188         int file;
189
190         printf ("Dbg: Loading overlay %u from file\n", num);
191         file = open (overlay[num - 1].name, O_RDONLY);
192         if (file == -1) {
193             log ("Opening overlay file failed");
194             return 0;
195         }
196         read (file, overlay[num - 1].addr,
197                     overlay[num - 1].size);
198         close (file);
199         return 1;
200     } else {
201         struct em_copy copyinfo;
202
203         printf ("Dbg: Loading overlay %u from memory\n", num);
204         copyinfo.offs  = 0;
205         copyinfo.page  = overlay[num - 1].page;
206         copyinfo.buf   = overlay[num - 1].addr;
207         copyinfo.count = overlay[num - 1].size;
208         em_copyfrom (&copyinfo);
209         return 1;
210     }
211 }
212
213 void main (void)
214 {
215     if (loadoverlay (4)) {
216         log ("Loading extended memory driver");
217         if (loademdriver ()) {
218             log ("Copying overlays into ext. memory");
219             copyoverlays ();
220         } else {
221             log ("No extended memory driver found");
222         }
223     }
224
225     log ("Press any key...");
226     cgetc ();
227
228     if (loadoverlay (1)) {
229         log ("Calling overlay 1 from main");
230
231         /* The linker makes sure that the call to foo() ends up at the right mem
232          * addr. However it's up to user to make sure that the - right - overlay
233          * is actually loaded before making the the call.
234          */
235         foo ();
236     }
237
238     /* Replacing one overlay with another one can only happen from the main
239      * program. This implies that an overlay can never load another overlay.
240      */
241     if (loadoverlay (2)) {
242         log ("Calling overlay 2 from main");
243         bar ();
244     }
245
246     if (loadoverlay (3)) {
247         log ("Calling overlay 3 from main");
248         foobar ();
249     }
250
251     log ("Press any key...");
252     cgetc ();
253 }