zx_line
zx_line
I just succeeded in porting the zx_line function to the spectrum and ts2068. It was originally a simplified zx_goto, because on the zx81 the main interpreter loop couldn't be easily trapped.
The idea is very simple, the program line is spotted with a standard rom call, then it is fed to the BASIC interpreter as an immediate command. . I this way every program line is a sort of 'subroutine', no need to 'return' with a STOP command or other tricks.
The zx81 and lambda permit only one single instruction per line m ts2068 and spectrum are more flexible. An interesting use for this sub is interfacing with the rom extensions, like the zxbasic fcntl library.
The idea is very simple, the program line is spotted with a standard rom call, then it is fed to the BASIC interpreter as an immediate command. . I this way every program line is a sort of 'subroutine', no need to 'return' with a STOP command or other tricks.
The zx81 and lambda permit only one single instruction per line m ts2068 and spectrum are more flexible. An interesting use for this sub is interfacing with the rom extensions, like the zxbasic fcntl library.
Is it then also possible to execute the immediate command "gosub 1000" to run a complete BASIC subroutine by a C program? Thus it would be easy to modify the BASIC part without recompiling the C part (e. g to adapt a generic C program to different mass storage systems, called by BASIC to load/store data).
Siggi
Siggi
Hi Stefanostefano wrote:Sadly a GOSUB call will crash the ZX81, and a GOTO will only adjust the pointers so that the lines will be executed after the end of the C program. I could provide a function able to execute all the program lines in a given range, if you think it can be useful..
that would help a lot! Thus the C program does not need to know in detail, how big the BASIC interface subroutine is (how much BASIC program lines it contains). Then it could execute e. g. lines 100-199 to LOAD data (using BASIC), execute 200-299 to SAVE data, execute 300-399 to read a directory from anywhere ......
But a GOSUB inside this subroutines will probably crash also?
But a GOTO inside the own subroutine should be possible (e. g. for error handling/retry on BASIC level). And USR-calls .....
Here is an example, how LOAD or SAVE is done using MEFISDOS or a floppy disk drive or an USB-stick (both used by the German ZX-Team) at BASIC level:
START:
POKE 0,2 or POKE 8,1 to do a bank-switching to enable EPROM/RAM@8K containing MEFISDOS or floppy disk or USB drivers
PRINT USR 8195, "L "+ F$ + " " + str$(address) to LOAD a file (filename stored in F$) to address given by variable "address"
IF PEEK ERRNO THEN GOTO ERRHAND
<continue normally>
ERRHAND: do some error handling (maybe ask for correct filename and store in F$)
GOTO START to retry
In case that GOTO is not possible, error handling must be done in the C program (if it does not depend on the mass storage system, handled outside by BASIC)
Siggi
START:
POKE 0,2 or POKE 8,1 to do a bank-switching to enable EPROM/RAM@8K containing MEFISDOS or floppy disk or USB drivers
PRINT USR 8195, "L "+ F$ + " " + str$(address) to LOAD a file (filename stored in F$) to address given by variable "address"
IF PEEK ERRNO THEN GOTO ERRHAND
<continue normally>
ERRHAND: do some error handling (maybe ask for correct filename and store in F$)
GOTO START to retry
In case that GOTO is not possible, error handling must be done in the C program (if it does not depend on the mass storage system, handled outside by BASIC)
Siggi
And here an example, how a directory could be read: The USB driver supports the command "D,<n>" and writes the filename of the <n>th file somewhere to screen. The Basic program could PEEK the charaters at the known screen position to read the filename into a BASIC variable:
LET N$=""
LET I=0
LOOP:
LET C$=PEEK (SCREENADDR + I)
IF C$=" " THEN GOTO END
LET N$=N$+C$
LET I=I+1
GOTO LOOP
This could be done also by C, but only if the C program knows in detail, where the USB driver writes the filename on screen (I am doing that in my MIDIPLAYER to get the name of a midi-file to be opened and played) ...
Siggi
Siggi
LET N$=""
LET I=0
LOOP:
LET C$=PEEK (SCREENADDR + I)
IF C$=" " THEN GOTO END
LET N$=N$+C$
LET I=I+1
GOTO LOOP
This could be done also by C, but only if the C program knows in detail, where the USB driver writes the filename on screen (I am doing that in my MIDIPLAYER to get the name of a midi-file to be opened and played) ...
Siggi
Siggi
I see. Does that "PRINT USR" call work also when issued directly, without being inserted in a program line ?
I modified the zx_line() implementation restricting it to the exact line number, it is now easier to execute a stack of program lines with a simple "for" loop (with the already discussed limits).
it *should* be possible something like:
{
zx_setint ("addr", position);
zx_setstr ('f', "filename.p";
for (x=1000; x>1200; x++) zx_line(x);
}
...
999 STOP
1000 POKE 0,2 <- better to do this in C, but we may want to keep the BASIC driver generic
1010 PRINT USR 8195, "L "+ F$ + " " + str$(ADDR) <- as above, a single string can be passed
Otherwise a low level driver specific for the MEFISDOS should be simple to do.. high level stuff (e.g. random file access, etc) depend on the DOS itself and how it is accidentally close to fcntl.
The error handling should be possible in two ways:
1020 IF PEEK ERRNO THEN RETURN
..in this case, you can use the return code of zx_line() which will inform you on the current BASIC error status (e.g. RETURN without GOSUB, STOP statement, or just a simple '0:OK')
otherwise you can:
1020 LET ERR=PEEK(ERRNO)
..and use zx_getint("err") to pick the value from the BASIC variable
last but not least this should also work:
{
char *errno;
errno=zx_getint(errno);
printf ("Error code: %u",errno[0]);
}
I modified the zx_line() implementation restricting it to the exact line number, it is now easier to execute a stack of program lines with a simple "for" loop (with the already discussed limits).
it *should* be possible something like:
{
zx_setint ("addr", position);
zx_setstr ('f', "filename.p";
for (x=1000; x>1200; x++) zx_line(x);
}
...
999 STOP
1000 POKE 0,2 <- better to do this in C, but we may want to keep the BASIC driver generic
1010 PRINT USR 8195, "L "+ F$ + " " + str$(ADDR) <- as above, a single string can be passed
Otherwise a low level driver specific for the MEFISDOS should be simple to do.. high level stuff (e.g. random file access, etc) depend on the DOS itself and how it is accidentally close to fcntl.
The error handling should be possible in two ways:
1020 IF PEEK ERRNO THEN RETURN
..in this case, you can use the return code of zx_line() which will inform you on the current BASIC error status (e.g. RETURN without GOSUB, STOP statement, or just a simple '0:OK')
otherwise you can:
1020 LET ERR=PEEK(ERRNO)
..and use zx_getint("err") to pick the value from the BASIC variable
last but not least this should also work:
{
char *errno;
errno=zx_getint(errno);
printf ("Error code: %u",errno[0]);
}
Yes, it works also in command mode.stefano wrote:I see. Does that "PRINT USR" call work also when issued directly, without being inserted in a program line ?
But caution: the PRINT command is aborted, because the USR call returns to BASIC by using RST 08 + DB 0xFF ( error exit to BASIC with error code 0), thus the stack is cleaned up (ERR_SP is used for that puspose by ROM) and due to the error code 0 the BASIC program continues running (thus the error needs to be passed via ERRNO). Maybe this will not work in a C environment and will also "cleanup " the stack used by the C program?????
There are a lot of mass storage systems for ZX81 around: MEFISDOS, several USB drivers for VDRIVE2, C64-Floppy, HD, "microdisk" of ZXNU, USB-Driver of ZxMore and ZxBlast, ZxPand, tape ;-), .......Otherwise a low level driver specific for the MEFISDOS should be simple to do.. high level stuff (e.g. random file access, etc) depend on the DOS itself and how it is accidentally close to fcntl.
I want to keep the C program usable for all of them and do the speciifc mass storage access using their exisiting BASIC interface (and don't want to write drivers for all of them to embedd them into Z88DK file handling ;-) )
Could also1020 IF PEEK ERRNO THEN RETURN
..in this case, you can use the return code of zx_line() which will inform you on the current BASIC error status (e.g. RETURN without GOSUB, STOP statement, or just a simple '0:OK')
POKE 16384, (PEEK ERRNO) - 1
be used to pass an error code to C? This can be used to force an error at BASIC level ...
Siggi
It could, but as you can see in my last example you can do everything from within the C program.
If you want to keep your BASIC portion portable, you can simply:
1020 LET ERROR=(PEEK ERRNO)-1
then you'll pick it from thr BASIC variable: { error=zx_getint("error"); }
Another BASIC driver could have other way to pass the error code by using the same "standard" variable.. using multiple lines you could in example:
1020 LET ERROR=1
1030 IF USR(8888)=65535 THEN LET ERROR=0
If you want to keep your BASIC portion portable, you can simply:
1020 LET ERROR=(PEEK ERRNO)-1
then you'll pick it from thr BASIC variable: { error=zx_getint("error"); }
Another BASIC driver could have other way to pass the error code by using the same "standard" variable.. using multiple lines you could in example:
1020 LET ERROR=1
1030 IF USR(8888)=65535 THEN LET ERROR=0
I have now implemented a BASIC interface to a mass storage system into my SENDMAIL program:
http://forum.tlienhard.com/phpBB3/viewt ... 159#p37159
I found, that the current ZX81 zx_line interface works well, when I run all zx_line in FAST mode. I am calling external asm and C programs, which "catch" the DISPLAY routine, thus FAST avoids a lot of trouble
Some of them use RST 08/DEFB $FF to return to BASIC, which also works.
Some additional function would be nice to have:
zx_clear()
zx_getunsignedint()
zx_setunsignedint()
And a zx_stop() function would be nice to have, to avoid to call a BASIC line "999 STOP" to reset the BASIC system, because such a BASIC line could be easily deleted accidentallyby the user, resulting in instabilities ...
Nevertheless:
Thanks for zx_line() :-)
Siggi
http://forum.tlienhard.com/phpBB3/viewt ... 159#p37159
I found, that the current ZX81 zx_line interface works well, when I run all zx_line in FAST mode. I am calling external asm and C programs, which "catch" the DISPLAY routine, thus FAST avoids a lot of trouble
Some of them use RST 08/DEFB $FF to return to BASIC, which also works.
Some additional function would be nice to have:
zx_clear()
zx_getunsignedint()
zx_setunsignedint()
And a zx_stop() function would be nice to have, to avoid to call a BASIC line "999 STOP" to reset the BASIC system, because such a BASIC line could be easily deleted accidentallyby the user, resulting in instabilities ...
Nevertheless:
Thanks for zx_line() :-)
Siggi