1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
/*
** Program-chaining function for Commodore platforms.
**
** 2015-09-27, Greg King
**
** This function exploits the program-chaining feature in CBM BASIC's ROM.
**
** CC65's CBM programs have a BASIC program stub. We start those programs by
** RUNning that stub; it SYSes to the Machine Language code. Normally, after
** the ML code exits, the BASIC ROM continues running the stub. But, it has
** no more statements; so, the program stops.
**
** This function puts the desired program's name and device number into a LOAD
** statement. Then, it points BASIC to that statement, so that the ROM will run
** that statement after this program quits. The ROM will load the next program,
** and will execute it (because the LOAD will be seen in a running program).
*/
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <device.h>
#if defined(__CBM610__)
# include <cbm610.h>
#elif defined(__CBM510__)
# include <cbm510.h>
#endif
/* The struct below is a line of BASIC code. It sits in the LOWCODE segment
** to make sure that it won't be hidden by a ROM when BASIC is re-enabled.
** The line is:
** 0 CLR:LOAD""+"" ,01
** After this function has written into the line, it might look like this:
** 0 CLR:LOAD""+"program name" ,08
**
** When BASIC's LOAD command asks the Kernal to load a file, it gives the
** Kernal a pointer to a file-name string. CC65's CBM programs use that
** pointer to give a copy of the program's name to main()'s argv[0] parameter.
** But, when BASIC uses a string literal that is in a program, it points
** directly to that literal -- in the models that don't use banked RAM
** (Pet/CBM, VIC-20, and 64). The literal is overwritten by the next program
** that is loaded. So, argv[0] would point to machine code. String operations
** create a new result string -- even when that operation changes nothing. The
** result is put in the string space at the top of BASIC's memory. So, the ""+
** in this BASIC line guarantees that argv[0] will get a name from a safe place.
*/
#pragma data-name(push, "LOWCODE")
static struct line {
const char end_of_line; /* fake previous line */
const struct line* const next;
const unsigned line_num;
const char CLR_token, colon, LOAD_token, quotes[2], add_token, quote;
char name[21];
const char comma;
char unit[3];
} basic = {
'\0', &basic + 1, /* high byte of link must be non-zero */
0, 0x9C, ':', 0x93, "\"\"", 0xAA, '\"',
"\" ", /* format: "123:1234567890123456\"" */
',', "01"
};
#pragma data-name(pop)
/* These values are platform-specific. */
extern const void* vartab; /* points to BASIC program variables */
#pragma zpsym("vartab")
extern const void* memsize; /* points to top of BASIC RAM */
#pragma zpsym("memsize")
extern const struct line* txtptr; /* points to BASIC code */
#pragma zpsym("txtptr")
extern char basbuf[]; /* BASIC's input buffer */
extern void basbuf_len[];
#pragma zpsym("basbuf_len")
int __fastcall__ exec (const char* progname, const char* cmdline)
{
static int fd;
static unsigned char dv, n;
/* Exclude devices that can't load files. */
/* (Use hand optimization, to make smaller code.) */
dv = getcurrentdevice ();
if (dv < 8 && __AX__ != 1 || __AX__ > 30) {
return _mappederrno (9); /* illegal device number */
}
utoa (dv, basic.unit, 10);
/* Tape files can be openned only once; skip this test for the Datasette. */
if (dv != 1) {
/* Don't try to run a program that can't be found. */
fd = open (progname, O_RDONLY);
if (fd < 0) {
return -1;
}
close (fd);
}
n = 0;
do {
if ((basic.name[n] = progname[n]) == '\0') {
break;
}
} while (++n < 20); /* truncate long names */
basic.name[n] = '\"';
/* This next part isn't needed by machines that put
** BASIC source and variables in different RAM banks.
*/
#if !defined(__CBM510__) && !defined(__CBM610__) && !defined(__C128__)
/* cc65 program loads might extend beyond the end of the RAM that is allowed
** for BASIC. Then, the LOAD statement would complain that it is "out of
** memory". Some pointers that say where to put BASIC program variables
** must be changed, so that we do not get that error. One pointer is
** changed here; a BASIC CLR statement changes the others.
*/
vartab = (char*)memsize - 256;
#endif
/* Build the next program's argument list. */
basbuf[0] = 0x8F; /* REM token */
basbuf[1] = '\0';
if (cmdline != NULL) {
strncat (basbuf, cmdline, (size_t)basbuf_len - 2);
}
/* Tell the ROM where to find that BASIC program. */
#if defined(__CBM510__) || defined(__CBM610__)
pokewsys ((unsigned)&txtptr, (unsigned)&basic);
#else
txtptr = &basic;
#endif
/* (The return code, in ST [status], will be destroyed by LOAD.
** So, don't bother to set it here.)
*/
exit (__AX__);
}
|