]> git.sur5r.net Git - cc65/blob - libsrc/cbm/exec.c
Added a little more info about how CBM program-chaining works.
[cc65] / libsrc / cbm / exec.c
1 /*
2 ** Program-chaining function for Commodore platforms.
3 **
4 ** 2013-08-24, Greg King
5 **
6 ** This function exploits the program-chaining feature in CBM BASIC's ROM.
7 ** It puts the desired program's name and unit number into a LOAD statement.
8 ** Then, it points BASIC to that statement, so that the ROM will run that
9 ** statement after this program quits.  The ROM will load the next program,
10 ** and will execute it (because the LOAD will be seen in a running program).
11 */
12
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <device.h>
19 #if   defined(__CBM610__)
20 #  include <cbm610.h>
21 #elif defined(__CBM510__)
22 #  include <cbm510.h>
23 #endif
24
25
26 #pragma data-name(push, "LOWCODE")
27 static struct line {
28     const char end_of_line;
29     const struct line *const next;
30     const unsigned line_num;
31     const char load_token, quotes[2], add_token, quote;
32     char name[21];
33     const char comma;
34     char unit[3];
35 } basic = {
36     '\0', &basic + 1,           /* high byte must be non-zero */
37     0, 0x93,
38
39     /* This string operation copies the name to high BASIC RAM.
40     ** So, it won't be overwritten when the next program is loaded.
41     */
42     "\"\"", 0xaa, '\"',
43     "\"                    ",   /* format: "123:1234567890123456\"" */
44     ',', "01"
45 };
46 #pragma data-name(pop)
47
48 /* These values are platform-specific. */
49 extern const struct line *txtptr;
50 #pragma zpsym("txtptr")
51 extern char basbuf[];           /* BASIC's input buffer */
52 extern void basbuf_len[];
53 #pragma zpsym("basbuf_len")
54
55
56 int __fastcall__ exec (const char* progname, const char* cmdline)
57 {
58     static int fd;
59     static unsigned char dv, n = 0;
60
61     /* Exclude devices that can't load files. */
62     dv = getcurrentdevice ();
63     if (dv < 8 && dv != 1 || dv > 30) {
64         return _mappederrno (9);        /* illegal device number */
65     }
66     utoa (dv, basic.unit, 10);
67
68     /* Don't try to run a program that can't be found. */
69     fd = open (progname, O_RDONLY);
70     if (fd < 0) {
71         return fd;
72     }
73     close (fd);
74
75     do {
76         if ((basic.name[n] = progname[n]) == '\0') {
77             break;
78         }
79     } while (++n < 20);         /* truncate long names */
80     basic.name[n] = '\"';
81
82     /* Build the next program's argument list. */
83     basbuf[0] = 0x8f;           /* REM token */
84     basbuf[1] = '\0';
85     if (cmdline != NULL) {
86         strncat (basbuf, cmdline, (size_t)basbuf_len - 2);
87     }
88
89 #if defined(__CBM510__) || defined(__CBM610__)
90     pokewsys ((unsigned)&txtptr, (unsigned)&basic);
91 #else
92     txtptr = &basic;
93 #endif
94
95     /* (The return code, in ST, will be destroyed by LOAD.
96     ** So, don't bother to set it here.)
97     */
98     exit (__AX__);
99 }