File: exec.c

package info (click to toggle)
cc65 2.19-2
  • links: PTS
  • area: main
  • in suites: forky, sid, trixie
  • size: 20,268 kB
  • sloc: ansic: 117,151; asm: 66,339; pascal: 4,248; makefile: 1,009; perl: 607
file content (141 lines) | stat: -rw-r--r-- 5,171 bytes parent folder | download | duplicates (3)
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__);
}