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