[Q] running a prg from C (or asm)
Moderator: Moderators
[Q] running a prg from C (or asm)
Hello,
for my upcoming shell, I need to make a routine to run a prg file from my C code.
i have taken a couple lines of code from cbm filebrowser which does:
JSR $C533 ; Relinks BASIC Program from and to any address...
JMP $C7AE ; BASIC Warm Start (RUN)
so I did:
if (cbm_load(cmd, 8, NULL) > 0) asm("\tjsr 50483\n\tjmp 51118");
but it crashes (cpu jam).
any clues?
thanks!
EDIT: maybe I'm overwriting myself?
for my upcoming shell, I need to make a routine to run a prg file from my C code.
i have taken a couple lines of code from cbm filebrowser which does:
JSR $C533 ; Relinks BASIC Program from and to any address...
JMP $C7AE ; BASIC Warm Start (RUN)
so I did:
if (cbm_load(cmd, 8, NULL) > 0) asm("\tjsr 50483\n\tjmp 51118");
but it crashes (cpu jam).
any clues?
thanks!
EDIT: maybe I'm overwriting myself?
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
Thanks.
Probably, I also need to relocate the loading code somewhere it's not touched by the load call itself (like the cassette buffer?), and run it from there.
I fear it's not trivial to do it in C and I should start looking at asm code closer
Probably, I also need to relocate the loading code somewhere it's not touched by the load call itself (like the cassette buffer?), and run it from there.
I fear it's not trivial to do it in C and I should start looking at asm code closer
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
I've gone a bit forward but still have problems.
This is the current code:
char* str = "\"ciao.prg\",8"; // sample loading basic code
POKE(4610, 1); // ?
POKE(4613, 156); // clr
POKE(4614, 58); // :
POKE(4615, 147); // load
memcpy((void*)4616, str, strlen(str));
memset((void*)(4616 + strlen(str)), '\0', 3); // prg end
asm("jsr 50483"); // relink basic code
POKE(45,PEEK(34)); // set free ram
POKE(46,PEEK(35));
asm("jmp 51118"); // run
Running this displays a syntax error, but if you enter "run" again, it works.
I suppose there is something about basic which should be cleaned, maybe something related to error routines?
Any clues?
This is the current code:
char* str = "\"ciao.prg\",8"; // sample loading basic code
POKE(4610, 1); // ?
POKE(4613, 156); // clr
POKE(4614, 58); // :
POKE(4615, 147); // load
memcpy((void*)4616, str, strlen(str));
memset((void*)(4616 + strlen(str)), '\0', 3); // prg end
asm("jsr 50483"); // relink basic code
POKE(45,PEEK(34)); // set free ram
POKE(46,PEEK(35));
asm("jmp 51118"); // run
Running this displays a syntax error, but if you enter "run" again, it works.
I suppose there is something about basic which should be cleaned, maybe something related to error routines?
Any clues?
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
JMP $C7AE ; BASIC Warm Start (RUN)
Isn't axctually the run command, it is the execution loop. The first thing I think is the problem here is that the pointer for the current BASIC text/token/data is not initialized.
"RUN" does a lot of other stuff. It initializes the above pointer, clears memory (and set relevant z-page pointers), restores DATA statement pointers, etc, etc.
Maybe ... /(And below is basically what RUN does)
;First turn of control messages and kernal messages
LDA #$00
JSR $FF90
;Reset all BASIC pointers
JSR $C659
; Continue
JMP $C7AE
Isn't axctually the run command, it is the execution loop. The first thing I think is the problem here is that the pointer for the current BASIC text/token/data is not initialized.
"RUN" does a lot of other stuff. It initializes the above pointer, clears memory (and set relevant z-page pointers), restores DATA statement pointers, etc, etc.
Maybe ... /(And below is basically what RUN does)
;First turn of control messages and kernal messages
LDA #$00
JSR $FF90
;Reset all BASIC pointers
JSR $C659
; Continue
JMP $C7AE
BR
Thomas Lövskog
Thomas Lövskog
Thanks for the information.
This is the new version:
int p = 4613;
POKE(p++, 147); // load
POKE(p++, 34); // "
memcpy((void*)p, cmd, strlen(cmd));
p += strlen(cmd);
POKE(p++, 34); // "
POKE(p++, 44); // ,
POKE(p++, 56); // 8
POKE(p++, 0); POKEW(p, 0); // prg end
POKE(45,PEEK(34)); // set free ram
POKE(46,PEEK(35));
asm("lda #00; jsr 65424"); // turn off control messages and kernal messages (looks like it has no effect)
asm("jsr 50777"); // clr
asm("jsr 50483"); // relink basic code
asm("jmp 51118"); // run
the "clr" put in the right place (asm instead of basic) made the difference.
now it seems to work for some programs but not for others:
- quickman8k: works
- tetwels8k: starts up but then the basic code gets corrupted and fails randomly
- tetwels compiled: totally garbled
This is the new version:
int p = 4613;
POKE(p++, 147); // load
POKE(p++, 34); // "
memcpy((void*)p, cmd, strlen(cmd));
p += strlen(cmd);
POKE(p++, 34); // "
POKE(p++, 44); // ,
POKE(p++, 56); // 8
POKE(p++, 0); POKEW(p, 0); // prg end
POKE(45,PEEK(34)); // set free ram
POKE(46,PEEK(35));
asm("lda #00; jsr 65424"); // turn off control messages and kernal messages (looks like it has no effect)
asm("jsr 50777"); // clr
asm("jsr 50483"); // relink basic code
asm("jmp 51118"); // run
the "clr" put in the right place (asm instead of basic) made the difference.
now it seems to work for some programs but not for others:
- quickman8k: works
- tetwels8k: starts up but then the basic code gets corrupted and fails randomly
- tetwels compiled: totally garbled
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
after loading the program in ram:
asm("jsr $e518 ; Initialize I/O");
asm("jsr 58459"); // initialize vectors
asm("jsr 58276"); // initialize basic ram
asm("jsr 50777"); // clr
asm("jsr 50483"); // relink basic code
asm("jmp 51118"); // run
works but it doesn't set the free ram properly.
but the same happens if you type this program:
10 load "some.prg",8
it works, but the free ram will not be set correctly, and so as soon as the program writes a variable, il will corrupt itself.
it looks like the problem is in the basic code, not c or asm.
I'm sure there are basic gurus here who can help
Maybe the right way is using the keyboard buffer? But will it fit the whole load command?
asm("jsr $e518 ; Initialize I/O");
asm("jsr 58459"); // initialize vectors
asm("jsr 58276"); // initialize basic ram
asm("jsr 50777"); // clr
asm("jsr 50483"); // relink basic code
asm("jmp 51118"); // run
works but it doesn't set the free ram properly.
but the same happens if you type this program:
10 load "some.prg",8
it works, but the free ram will not be set correctly, and so as soon as the program writes a variable, il will corrupt itself.
it looks like the problem is in the basic code, not c or asm.
I'm sure there are basic gurus here who can help
Maybe the right way is using the keyboard buffer? But will it fit the whole load command?
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
ok this seems to be working for all files (except .p00):
POKE(45, size % 256); // set free ram
POKE(46, size / 256);
where "size" is file size + 4607
this is done after writing the program to memory (before "initialize I/O")
POKE(45, size % 256); // set free ram
POKE(46, size / 256);
where "size" is file size + 4607
this is done after writing the program to memory (before "initialize I/O")
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
That is actually not so strange that it doesn't work. After you have loaded the program you restore all the pointers with ...pallas wrote:works but it doesn't set the free ram properly.
but the same happens if you type this program:
10 load "some.prg",8
it works, but the free ram will not be set correctly, and so as soon as the program writes a variable, il will corrupt itself.
asm("jsr 58276"); // initialize basic ram
BASIC will then not see that there is any program loaded. First variable will then write over everything ...
asm("jsr $e518 ; Initialize I/O");
asm("jsr 58459"); // initialize vectors
asm("jsr 58276"); // initialize basic ram
That shouldn't be needed if nothing has seriously been screwed up.
So. Why do you have the same problem with the "10 load "some.prg",8
Well that is a "feature" with the BASIC. It will simply not change the end of BASIC pointer if you use the load command in another program. The end of BASIC is only updated when you type the load command interactively.
The idea here might be that if the newly loaded program is less in size than the first, all variables can be kept between loads.
If you use $FFD5 to load the new program in memory, then it will return i X/Y the highest adress used. Store these in $2D/$2E with
STX $2D ; set start of variables low byte
STY $2E ; set start of variables high byte
The do the clr etc.
Although I have started to muck around the VIC again for my GCart project, it is 30+ years since I dug around the BASIC this much ...
BR
Thomas Lövskog
Thomas Lövskog
Maybe ...
LDA #$... ; What is needed
LDX #$... ; What is needed
LDY #$... ; What is needed
JSR $FFBA ; Set device etc.
LDA #$LENGTH NAME
LDX #$<Name
LDY #$>NAME
JSR $FFBD ; Set name
LDX $2B
LDY $2C ; Load to BASIC Start
JSR $FFD5 ; Load
BCS ERROR
JSR $FFB7 ; Check i/o status
AND #$BF
BNE ERROR
STX $2D
STY $2C ; Store end of BASIC
JSR $C659 ; Clear and set pointers
JSR $C533 ; Rebuild chain
JMP $C7AE ; Run
Just from the top of my head, so ....
LDA #$... ; What is needed
LDX #$... ; What is needed
LDY #$... ; What is needed
JSR $FFBA ; Set device etc.
LDA #$LENGTH NAME
LDX #$<Name
LDY #$>NAME
JSR $FFBD ; Set name
LDX $2B
LDY $2C ; Load to BASIC Start
JSR $FFD5 ; Load
BCS ERROR
JSR $FFB7 ; Check i/o status
AND #$BF
BNE ERROR
STX $2D
STY $2C ; Store end of BASIC
JSR $C659 ; Clear and set pointers
JSR $C533 ; Rebuild chain
JMP $C7AE ; Run
Just from the top of my head, so ....
BR
Thomas Lövskog
Thomas Lövskog
Thanks for the information, now it's a bit clearer.
Unfortunately I can't load the program directly because I will overwrite myself.
So I create a small program in ram which loads the program.
The solution in my previous post seems to work (it sets the "start of variables" pointer by itself).
The initialize* routines seem to be necessary, probably because cc65 does some things which you usually don't do on an asm program (so more cleaning needed).
I've also tried calling the "_exit" routing of cc65, which does the cleanup when you exit() or return from main(), but with no luck.
Unfortunately I can't load the program directly because I will overwrite myself.
So I create a small program in ram which loads the program.
The solution in my previous post seems to work (it sets the "start of variables" pointer by itself).
The initialize* routines seem to be necessary, probably because cc65 does some things which you usually don't do on an asm program (so more cleaning needed).
I've also tried calling the "_exit" routing of cc65, which does the cleanup when you exit() or return from main(), but with no luck.
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
- Mike
- Herr VC
- Posts: 4816
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Pretty much the same code I use in my boot loaders, just I don't bother catching I/O errors:TLovskog wrote:Maybe ... [...]
Code: Select all
LDA #1
LDX $BA ; last device used
LDY #0 ; secondary address = 0, force load to address given in X/Y on KERNAL load
JSR $FFBA ; SETLFS
LDA #NameEnd-Name
LDX #Name MOD 256
LDY #Name DIV 256
JSR $FFBD ; SETNAM
LDA #0 ; load (==0), not verify (!=0)
LDX $2B
LDY $2C
JSR $FFD5 ; call KERNAL load routine
STX $2D ; set start of variables (a.k.a. "end of program") from X/Y
STY $2E
JSR $C533 ; relink BASIC program
JSR $C659 ; reset TXTPTR and clear variables
JMP $C7AE ; enter interpreter loop (a.k.a. "RUN")
.Name
EQUS "FILENAME"
.NameEnd
My solution, which worked on the vic20, didn't work for some reason on the c64, even though I've checked the addresses multiple times.
So I came up with a much simpler solution (based on the idea by Mike) with the keyboard buffer:
#if defined(__C128__)
#define KEYBUF_ADDR 842
#define KEYBUF_NUMB 208
#else
#define KEYBUF_ADDR 631
#define KEYBUF_NUMB 198
#endif
// 8E switches character set to 1 (default uppercase)
printf("\x8Eload\"%s\",8", cmd);
memcpy((void*)KEYBUF_ADDR, "\x91\x91\nrun\n", 7);
POKE(KEYBUF_NUMB, 7);
exit(0);
So I came up with a much simpler solution (based on the idea by Mike) with the keyboard buffer:
#if defined(__C128__)
#define KEYBUF_ADDR 842
#define KEYBUF_NUMB 208
#else
#define KEYBUF_ADDR 631
#define KEYBUF_NUMB 198
#endif
// 8E switches character set to 1 (default uppercase)
printf("\x8Eload\"%s\",8", cmd);
memcpy((void*)KEYBUF_ADDR, "\x91\x91\nrun\n", 7);
POKE(KEYBUF_NUMB, 7);
exit(0);
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/
- Mike
- Herr VC
- Posts: 4816
- Joined: Wed Dec 01, 2004 1:57 pm
- Location: Munich, Germany
- Occupation: electrical engineer
Would be quite interesting to know what you think went wrong. Could you provide this earlier solution here for an overview?pallas wrote:My solution, which worked on the vic20, didn't work for some reason on the c64, even though I've checked the addresses multiple times.
Well, printing out appropriate statements on screen and then executing these commands from stored keypresses in the keyboard buffer surely isn't something I invented first, but I still have some suggestions:So I came up with a much simpler solution (based on the idea by Mike) with the keyboard buffer:
Doubtful, whether \x8E is really necessary, but the code could be made more flexible by making the device number dynamic:Code: Select all
/* 8E switches character set to 1 (default uppercase) */ printf("\x8Eload"%s",8", cmd);
Code: Select all
printf("\x93\x8Eload"%s",%d\n",cmd,PEEK(186));
In this context, '\n' is not portable - some architectures identify it with ASCII 10, others with ASCII 13, sometimes even ASCII 10 + ASCII 13. It's better here to write what you want, \x0D.Code: Select all
memcpy((void*)KEYBUF_ADDR, "\x91\x91\nrun\n", 7); POKE(KEYBUF_NUMB,7);
Together with the cleared screen, this gives:
Code: Select all
memcpy((void *)KEYBUF_ADDR, "\x13\x0Drun\x0D", 6);
POKE(KEYBUF_NUMB,6);
Thanks Mike for the suggestions.
Here is the other routine:
works on vic-20 but crashes (like if you run/stop+restore) on c64.
Here is the other routine:
Code: Select all
#if defined(__VIC20__)
unsigned int p = 4613, size = 4607;
#elif defined(__C64__)
unsigned int p = 2053, size = 2047;
#endif
int fp = open(cmd, O_RDONLY), n;
char buf[BUF_SIZE];
if (fp <= 0) {
printf("Can't run prg\n");
return;
} else {
while ((n = read(fp, buf, sizeof(buf))) > 0) size += n;
close(fp);
}
getcwd(buf, BUF_SIZE);
POKEW(p, 147 + 34 * 256); // load + "
p += 2;
memcpy((void*)p, cmd, strlen(cmd));
p += strlen(cmd);
POKEW(p, 34 + 44 * 256); // " + ,
p += 2;
memcpy((void*)p, buf, strlen(buf));
p += strlen(buf);
POKE(p++, 0); POKEW(p, 0); // prg end
//sprintf((char*)4613, "\x93\"%s\",%s\0\0\0", cmd, buf); bigger!
POKEW(45, size); // set free ram
#if defined(__VIC20__)
asm("jsr $E518"); // Initialize I/O
asm("jsr $E45B"); // initialize vectors
asm("jsr $E3A4"); // initialize basic ram
asm("jsr $C659"); // clr + reset stack
asm("jsr $C533"); // relink basic code
asm("jmp $C7AE"); // run
#elif defined(__C64__)
//POKEW(47, size); optional
//POKEW(49, size); optional
asm("jsr $E518"); // Initialize I/O
asm("jsr $E453"); // initialize vectors
asm("jsr $E3BF"); // initialize basic ram
// currently doesn't work: looks like it crashes before doing the relink
asm("jsr $A659"); // clr + reset stack
asm("jsr $A533"); // relink basic code
asm("jmp $A7AE"); // run
#endif
Pallas - OPByte
http://www.opbyte.it/vic20/
http://www.opbyte.it/vic20/