I have a program which consists of multiple other programs. I want to be able to run one of the programs, and then that program can run other programs. Here is a stripped-down example of what I'm trying to do:
// The loader executable
void jumpToExecutable() {
// TODO: jump to other executable in memory and run a custom function inside of it (called 'int otherMain()' in this case)
}
void loadExecutable(char* executable) {
cbm_k_setnam(executable);
cbm_k_setlfs("0", "8", "0");
cbm_k_load("1", 0x8001); // not sure if this memory address is correct yet
jumpToExecutable();
}
int main() {
loadExecutable("other.prg");
return EXIT_SUCCESS;
}
// The other executable
int main() {
printf("This program cannot be run directly.");
return EXIT_FAILURE;
}
int otherMain() {
printf("Hello, world!");
return EXIT_SUCCESS;
}
What I know I need to do:
Load the other program into memory
Know the address of otherMain() in the other program
Jump to the address of otherMain() to start executing code
How do I do these things? Are there any other things I need to do to make this work? I don't know much about low-level programming, so my "Jump to the address of otherMain()" thing might be completely misguided anyways.
Thinking about this off the top of my head, consider. Commodore computers, including the X16, don't have an OS per se, they boot to BASIC, meaning the BASIC interpreter is in control, in immediate mode. You can type BASIC keywords and most will execute, as a one line 'program'. As you type in actual line numbered input...without syntax errors, you are creating a program on the fly. When you then type RUN, BASIC will start executing the program and when the program exits, you return to immediate mode. The same is true of 'non' BASIC programs, which are typically loaded with a BASIC stub, to begin execution. You could chain BASIC programs back in the day...I seem to remember that, but I don't know how off the top of my head and I don't think that's what you want anyway. It sounds more like what you want to do is some type of shell program. I have just recently started to think about trying to do something similar...very early days and I don't use CC65, so I'm not familiar with it's finer details! Your 'main' or calling program will have to live somewhere that won't be overwritten and then the called program will need to return control to the caller.
For example; your main program could run from the low memory 'BASIC' program space, then load 'child' programs to hiram at $A000 and jsr to them, then rts back to main.
It certainly can be done and probably there's more than one way, but perhaps I've given you some food for thought and ideas where to dig!
There's no practical way on this architecture to do "preemptive" multi-tasking, except for very limited background routines driven from the timer interrupt. A cooperative framework is possible, but its going to be a tricky setup. To do it right your going to have to "swap" things around. .
First, observe that absolutely every non-BASIC X16 game must contain a tiny BASIC program at $0801 containing a SYS call to the program's actual entry point. This BASIC program is what runs when the user types RUN at the READY prompt, and the SYS call jumps to your program's non-BASIC code.
Second, unless you're writing all of your code manually in assembly (i.e., you know exactly what's being inserted into your PRG), your compiler will likely insert some kind of initialization routine that does some set-up before it actually jumps to your main() function, and there's no guarantee that it cleans up this initialization when the program exits. This initialization may include things like initializing X16/VERA/etc registers, initializing program variables, inserting interrupt handlers, clearing the 6502's stack, etc.
Third, there's no concept of "two programs loaded in memory" on the X16; when you load the second program, it's literally copying the file into memory and nothing else. That BASIC stub I mentioned is what allows the loaded file to act like a runnable program (i.e., type RUN to launch it). That BASIC stub, more often than not, will require the file to be loaded at $0801, which is going to overwrite your first program while it's still running. The second program does not know that there was a first program running, and will not be aware of where the first program stores its variables in memory, so the second program will just totally clobber the first program.
With all of this said, there's still some strategies for safely loading a secondary program into memory, depending on what the goal of the first program is.
Is the main program a simple launcher menu? (i.e., the main program is just a stepping stone and can be fully discarded once the secondary program is launched)
Or, is this a situation where the main program can load several secondary programs to act like plug-ins or modules? (for example, a game that loads small "minigame" programs from disk as they're needed, or an activity center that needs to go back and forth between an activity module and the main program)