mirror of
https://github.com/cc65/cc65.git
synced 2024-08-14 14:29:07 +00:00
270 lines
6.9 KiB
C
270 lines
6.9 KiB
C
/*
|
|
* Extended memory 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 <stdlib.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;
|
|
char *emd = NULL;
|
|
unsigned char max = 0;
|
|
unsigned char num;
|
|
|
|
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: Memorizing file %s\n", ent->d_name);
|
|
emd = realloc (emd, FILENAME_MAX * (max + 1));
|
|
strcpy (emd + FILENAME_MAX * max++, ent->d_name);
|
|
}
|
|
closedir (dir);
|
|
|
|
for (num = 0; num < max; ++num) {
|
|
char *drv;
|
|
|
|
drv = emd + FILENAME_MAX * num;
|
|
printf ("Dbg: Trying emdriver %s\n", drv);
|
|
if (em_load_driver (drv) == EM_ERR_OK) {
|
|
printf ("Dbg: Loaded emdriver %s\n", drv);
|
|
free (emd);
|
|
return 1;
|
|
}
|
|
|
|
printf ("Dbg: Emdriver %s failed\n", drv);
|
|
}
|
|
|
|
free (emd);
|
|
return 0;
|
|
}
|
|
|
|
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 ();
|
|
}
|