zx_line

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

zx_line

Post by stefano »

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.
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by siggi »

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
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

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..
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

..reversely on the Spectrum and the TS2068 the a GO TO in the basic line it would do exactly what you are asking for :(
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by siggi »

stefano 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..
Hi Stefano
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 .....
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

I'm afraid I have not been able to keep the basic very stable. Those would be just a sequence (no goto, nor gosub).. I'll try to to host part of the interpreter and bend it properly
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

Siggi, I've been wondering what command are you mostly interested in.
e.g. I suppose that some disk drive command would have an usr line followed by a fake basic one, right?
are serious basic extensions around ? new commands etc?
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by siggi »

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
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by 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
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

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]);
}
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

in the last example there's at least one error, let me correct myself:

{
char *errno;
errno=zx_getint("errno");
printf ("Error code: %u",errno[0]);
}
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by siggi »

stefano wrote:I see. Does that "PRINT USR" call work also when issued directly, without being inserted in a program line ?
Yes, it works also in command mode.

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?????
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.
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 ;-), .......
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 ;-) )
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')
Could also
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
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

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
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

About ERR_SP, it is worth to experiment, zx_line should be able to trap it and give you back the BASIC error code.
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by siggi »

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
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

the suggested functions should be feasible, I'll try.
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

ok, I consider this a low priority request: the clear command can be issued by activating a zx_line with the CLEAR command on it, getint and setint are unsigned already and the signed values can be read through the float functions and a recast, e.g.
(int) zx_getfloat("a")
stefano
Well known member
Posts: 1628
Joined: Mon Jul 16, 2007 7:39 pm

Post by stefano »

..but I'm changing the type to 'unsigned int' in the declarations :)
siggi
Well known member
Posts: 344
Joined: Thu Jul 26, 2007 9:06 am

Post by siggi »

Yes: nice to have, but low priority ;-)
Post Reply