Extending BASIC with C

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
Post Reply
User avatar
stefano
Posts: 487
Joined: Tue Dec 11, 2012 9:24 am
Contact:

Extending BASIC with C

Post by stefano »

I'm proud of this trick, it works on both the Spectrum and the ZX81.
It could be extended to the Lambda and others too.

Code: Select all


------------------
1   REM  @#@#@# <-- C compiled code
10  LET A$="HELLO": LET A2=1234
20  PRINT USR 16514,A$,A2

..or PRINT USR VAL(16514),"HELLO",1233+1   , or whatever you need to do.. it's regular BASIC !
------------------


#include <stdio.h>
#include <zx81.h>

main(unsigned int arg2, char *arg1)
{
	ARG_STR;
	ARG_UINT;
	ARG_END;
	
	printf("Arg1: ");
	zx_asciimode(0);
	printf("%s", arg1);
	zx_asciimode(1);
	printf(", arg2: %u \n" ,arg2);

	STMT_RET;
}
3-zx-basic-c.png
Those macros could be used as a wrap around of several functions in the same program which can be prepared as a 'library' for your BASIC program. The assembler can create a map (zcc +zx81 -m..) and show the entry locations.
The declared parameters are checked by the BASIC syntax checker, e.g. passing a string when a number is expected will stop the program execution.
Add as many arguments you likeof the following types: INT, UINT, STR (zero terminated), or PTR (plain char/byte sequence)
dr beep
Posts: 1606
Joined: Thu Jun 16, 2011 8:35 am
Location: Boxmeer

Re: Extending BASIC with C

Post by dr beep »

Yes, that is how I added disccommands into my ZX-PC interface

https://www.8bit-wiki.de/fileadmin/8bit ... /PC-ZX.pdf
User avatar
stefano
Posts: 487
Joined: Tue Dec 11, 2012 9:24 am
Contact:

Re: Extending BASIC with C

Post by stefano »

..and here's a quick and dirty example on how to use it on a zx81.

Code: Select all

/*
    Example on how to use the ZX specific macros
    to extend the Sinclair BASIC.
    
     zcc +zx -m -create-app -pragma-define:CLIB_CONIO_NATIVE_COLOUR=1 -zorg=50000 basiclib.c
    
*/

#include <stdio.h>
#include <graphics.h>
//#include <zx81.h>

#ifdef __SPECTRUM__
void zx_ink(int c)
{
	ARG_UINT;
	ARG_END;
	
	zx_setink(c);

	STMT_RET;
}
#endif

void zx_plot(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	plot(x,y);

	STMT_RET;
}

void zx_unplot(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	unplot(x,y);

	STMT_RET;
}

void zx_xorplot(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	xorplot(x,y);

	STMT_RET;
}

void zx_draw(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	drawr(x,y);

	STMT_RET;
}

void zx_undraw(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	undrawr(x,y);

	STMT_RET;
}

void zx_xordraw(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	xordrawr(x,y);

	STMT_RET;
}

void zx_drawto(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	drawto(x,y);

	STMT_RET;
}

void zx_undrawto(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	undrawto(x,y);

	STMT_RET;
}

void zx_xordrawto(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	xordrawto(x,y);

	STMT_RET;
}

void zx_circle(int x, int y, int r)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	circle(x,y,r,1);

	STMT_RET;
}

void zx_uncircle(int x, int y, int r)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	uncircle(x,y,r,1);

	STMT_RET;
}

void zx_xorcircle(int x, int y, int r)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	xorcircle(x,y,r,1);

	STMT_RET;
}

void zx_fill(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	fill(x,y);

	STMT_RET;
}

#ifdef __SPECTRUM__
void zx_print(char *t)
{
	ARG_STR;
	ARG_END;
	
	puts_cons(t);

	STMT_RET;
}

int cnt;
void zx_miniprint(char *t)
{
	ARG_STR;
	ARG_END;
	
	cnt=0;
	while (t[cnt]!=0)
		putc4x6(t[cnt++]);

	STMT_RET;
}

#else

int cnt;
void zx_miniprint(char *t)
{
	ARG_PTR;
	ARG_END;
	
	cnt=0;
	while (t[cnt]!=128)  //text end:  GRAPHICS+SPACE (inverted blank)
		putc4x6(zx_ascii(t[cnt++]));

	STMT_RET;
}

#endif


main()
{
	clg();
	STMT_RET;
}

After compiling I got the following entries, and program:
test.P
(4.05 KiB) Downloaded 12 times

Code: Select all

clg            = 16514   Init new interrupt handler, clear screen and reset graphics pointers. (0,0) = top-left.
_zx_fast       = 16591
_zx_slow       = 16597
_zx_plot       = 16796   x,y
_zx_unplot     = 16842   x,y
_zx_xorplot    = 16888   x,y
_zx_draw       = 16934   +/- x,y (relative coordinate)
_zx_undraw     = 16998   +/- x,y (relative coordinate)
_zx_xordraw    = 17062   +/- x,y (relative coordinate)
_zx_drawto     = 17126   x,y
_zx_undrawto   = 17172   x,y
_zx_xordrawto  = 17218   x,y
_zx_circle     = 17264   x,y,radius
_zx_uncircle   = 17320   x,y,radius
_zx_xorcircle  = 17376   x,y,radius
_zx_fill       = 17432   x,y

_zx_miniprint  = 17480   "text[]" ..MUST have GRAPHICS+SPACE (inverted SPACE) as string terminator
_x_4x6         = 19730   miniprint coordinates (use POKE)
_y_4x6         = 19732

Enjoy ;)


A similar toolkit can be created to support the Lambda 8300 or the high resolution on the TS2068.
Many functions can be added (poligons, surfaces, turtle graphics..), but probably a programmer would prefer to include only the functions he really needs.

gfx-test.png
User avatar
stefano
Posts: 487
Joined: Tue Dec 11, 2012 9:24 am
Contact:

Re: Extending BASIC with C

Post by stefano »

Medium resolution version, this one works on those ZX81 with a redefinable character set board (or mod).
It works by altering the font into a pseudo-graphics set providing a 2x3 grid dots per character (thus, 64x72 pseudo-pixels).
Text can't be mixed, but for a minimum of textual output miniprint could help.
zx_cls() restores the text, clg() gets back in graphics mode.

The way to define a different default font position in a C program is to use a #pragma directive, i.e. to load the battenberg font at position $8400 as for the QuickSilva expansion :

#pragma output hrgpage=132

Such predefined value can by the way be changed also by the user, after loading the program, by POKEing at address 16518.

Code: Select all

clg            = 16514  Init new interrupt handler, enter in UDG mode and clear screen and reset graphics pointers. (0,0) = top-left.
_zx_text       = 17553  get back to text mode (exit from UDG mode)

_zx_fast       = 16591  
_zx_slow       = 16597  

_zx_plot       = 16796  x,y
_zx_unplot     = 16842  x,y
_zx_xorplot    = 16888  x,y
_zx_draw       = 16934  +/- x,y (relative coordinate)
_zx_undraw     = 16998  +/- x,y (relative coordinate)
_zx_xordraw    = 17062  +/- x,y (relative coordinate)
_zx_drawto     = 17126  x,y
_zx_undrawto   = 17172  x,y
_zx_xordrawto  = 17218  x,y
_zx_circle     = 17264  x,y,radius
_zx_uncircle   = 17320  x,y,radius
_zx_xorcircle  = 17376  x,y,radius
_zx_fill       = 17432  x,y

_zx_miniprint  = 17480  "text[]" ..MUST have GRAPHICS+SPACE (inverted SPACE) as string terminator 

_x_4x6         = $4DD0  miniprint coordinates (use POKE)
_y_4x6         = $4DD2
zx81-udgmode.png

udglib.p
(4.24 KiB) Downloaded 13 times
User avatar
stefano
Posts: 487
Joined: Tue Dec 11, 2012 9:24 am
Contact:

Re: Extending BASIC with C

Post by stefano »

Pushing the tool to its limits, gathering a good portion of the functions we have on z88dk.
Here's the extended function set:

Code: Select all


; USR 16514 must be always invoked at the beginning of your program, followed by CLS.

clg               = 16514   Init new interrupt handler, clear screen and reset graphics pointers. (0,0) = top-left.

_zx_fast          = 16591
_zx_slow          = 16597

; graphics commands
_zx_setpos        = 17905   x,y
_zx_plot          = 16796   x,y
_zx_unplot        = 16842   x,y
_zx_xorplot       = 16888   x,y
_zx_draw          = 16934   +/- x,y (relative coordinate)
_zx_undraw        = 16998   +/- x,y (relative coordinate)
_zx_xordraw       = 17062   +/- x,y (relative coordinate)
_zx_drawto        = 17126   x,y
_zx_undrawto      = 17172   x,y
_zx_xordrawto     = 17218   x,y
_zx_circle        = 17264   x,y,radius
_zx_uncircle      = 17320   x,y,radius
_zx_xorcircle     = 17376   x,y,radius
_zx_fill          = 17432   x,y  flood fill starting at the given position
_zx_clga          = 17673  clear screen area: x,y,x2,y2
_zx_fillb         = 17741  fill screen area x,y,x2,y2

_zx_miniprint     = 17480  "text[]" ..MUST have GRAPHICS+SPACE (inverted SPACE) as string terminator
x_4x6             = 21535  x miniprint coordinate (use POKE)
y_4x6             = 21537  y miniprint coordinate (use POKE)

; text/video effects
_zx_invtxt        = 17553  inverts the picture in text mode
_zx_mirrortxt     = 17568  mirrors the text horizontally (font is not flipped)
_zx_filltxt       = 17583  arg: the numeric code of the character used to fill the screen, e.g.  CODE "A", or 8
_zx_rollchr       = 17615  tweaks the video generator to roll the text display, '0'= normal position
_zx_scrolluptxt   = 17643  scroll up
_zx_scrolldowntxt = 17658  scroll down

;  turtle graphics
_zx_pen_up        = 17809  the next "zx_fwd" will trace a line
_zx_pen_down      = 17824  the next "zx_fwd" will move without drawing
_zx_move          = 17839  x,y
_zx_set_direction = 17951  clockwise progression, e.g EAST=0, SOUTH=90, WEST=180, NORTH=270
_zx_fwd           = 17979  make your turtle walk, n steps
_zx_turn_left     = 18007  arg: degrees
_zx_turn_right    = 18035  arg: degrees

;  picture blitting
_zx_putsprite     = 18063  x,y,sprite data: e.g. CHR$ 8 + CHR$ 8 + "AAAAAAAA"
_zx_andsprite     = 18120
_zx_xorsprite     = 18178
_zx_getsprite     = 18235  x,y,sprite data.  The sprite data must be pre-initialized with expected size and a default content.

..if you wish to rebuild/personalize the tool, here's the source.

Code: Select all

/*
    Example on how to use the ZX specific macros
    to extend the Sinclair BASIC.
    
     zcc +zx -m -create-app -pragma-define:CLIB_CONIO_NATIVE_COLOUR=1 -zorg=50000 basiclib.c

     zcc +zx81 -m -create-app basiclib.c
    
*/

#include <stdio.h>
#include <graphics.h>
#include <games.h>
#include <lib3d.h>
//#include <zx81.h>

#ifdef __SPECTRUM__
void zx_ink(int c)
{
	ARG_UINT;
	ARG_END;
	
	zx_setink(c);

	STMT_RET;
}
#endif

void zx_plot(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	plot(x,y);

	STMT_RET;
}

void zx_unplot(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	unplot(x,y);

	STMT_RET;
}

void zx_xorplot(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	xorplot(x,y);

	STMT_RET;
}

void zx_draw(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	drawr(x,y);

	STMT_RET;
}

void zx_undraw(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	undrawr(x,y);

	STMT_RET;
}

void zx_xordraw(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	xordrawr(x,y);

	STMT_RET;
}

void zx_drawto(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	drawto(x,y);

	STMT_RET;
}

void zx_undrawto(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	undrawto(x,y);

	STMT_RET;
}

void zx_xordrawto(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	xordrawto(x,y);

	STMT_RET;
}

void zx_circle(int x, int y, int r)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	circle(x,y,r,1);

	STMT_RET;
}

void zx_uncircle(int x, int y, int r)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	uncircle(x,y,r,1);

	STMT_RET;
}

void zx_xorcircle(int x, int y, int r)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	xorcircle(x,y,r,1);

	STMT_RET;
}

void zx_fill(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	fill(x,y);

	STMT_RET;
}

#ifdef __SPECTRUM__
void zx_print(char *t)
{
	ARG_STR;
	ARG_END;
	
	puts_cons(t);

	STMT_RET;
}

int cnt;
void zx_miniprint(char *t)
{
	ARG_STR;
	ARG_END;
	
	cnt=0;
	while (t[cnt]!=0)
		putc4x6(t[cnt++]);

	STMT_RET;
}

#else

int cnt;
void zx_miniprint(char *t)
{
	ARG_PTR;
	ARG_END;
	
	cnt=0;
	while (t[cnt]!=128)  //text end:  GRAPHICS+SPACE (inverted blank)
		putc4x6(zx_ascii(t[cnt++]));

	STMT_RET;
}

#ifdef UDGMODE

zx_text()
{
	zx_cls();

	STMT_RET;
}

#else

// Invert screen in text mode
zx_invtxt()
{
	invtxt();

	STMT_RET;
}


// Mirror screen in text mode
zx_mirrortxt()
{
	mirrortxt();

	STMT_RET;
}


// Fill text screen in text mode with specified character code
// and position text cursor at (0;0)
zx_filltxt(int a)
{
	ARG_UINT;
	ARG_END;
	
	filltxt(a);

	STMT_RET;
}


// Special effect: uses the new interrupts to roll all displayed text characters vertically
// argument: 0..7, where '0' is the 'correct' adjustment
zx_rollchr(int a)
{
	ARG_UINT;
	ARG_END;

	rollchr(a);

	STMT_RET;
}


zx_scrolluptxt()
{
	scrolluptxt();

	STMT_RET;
}


zx_scrolldowntxt()
{
	scrolldowntxt();

	STMT_RET;
}


void zx_clga(int x1, int y1, int x2, int y2)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	clga(x1,y1,x2,y2);

	STMT_RET;
}


void zx_fillb(int x1, int y1, int x2, int y2)
{
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	fillb(x1,y1,x2,y2);

	STMT_RET;
}


zx_pen_up()
{
	pen_up();

	STMT_RET;
}


zx_pen_down()
{
	pen_down();

	STMT_RET;
}


void zx_move(int x, int y)
{
	ARG_INT;
	ARG_INT;
	ARG_END;
	
	move(x,y);

	STMT_RET;
}


void zx_setpos(int x, int y)
{
	ARG_UINT;
	ARG_UINT;
	ARG_END;
	
	setpos(x,y);

	STMT_RET;
}


zx_set_direction(int a)
{
	ARG_UINT;
	ARG_END;

	set_direction(a);

	STMT_RET;
}


zx_fwd(int a)
{
	ARG_UINT;
	ARG_END;

	fwd(a);

	STMT_RET;
}


zx_turn_left(int a)
{
	ARG_UINT;
	ARG_END;

	turn_left(a);

	STMT_RET;
}


zx_turn_right(int a)
{
	ARG_UINT;
	ARG_END;

	turn_right(a);

	STMT_RET;
}


void zx_putsprite(int x, int y, char *spr)
{
	ARG_UINT;
	ARG_UINT;
	ARG_PTR;
	ARG_END;
	
	putsprite(spr_or, x, y, spr);

	STMT_RET;
}


void zx_andsprite(int x, int y, char *spr)
{
	ARG_UINT;
	ARG_UINT;
	ARG_PTR;
	ARG_END;
	
	putsprite(spr_and, x, y, spr);

	STMT_RET;
}


void zx_xorsprite(int x, int y, char *spr)
{
	ARG_UINT;
	ARG_UINT;
	ARG_PTR;
	ARG_END;
	
	putsprite(spr_xor, x, y, spr);

	STMT_RET;
}


void zx_getsprite(int x, int y, char *spr)
{
	ARG_UINT;
	ARG_UINT;
	ARG_PTR;
	ARG_END;
	
	getsprite(x, y, spr);

	STMT_RET;
}


#endif

#endif



main()
{
	clg();

	STMT_RET;
}


Obviously all this stuff was available in the cross-compiler kit, but the possibility to do magic stuff in BASIC is intriguing ;)
Now it is up to your creativity !

..with a single page of code..
gfx_list.png
gfx_list.png (13.63 KiB) Viewed 154 times
..you can get this:
gfx_run.png
gfx_run.png (10.26 KiB) Viewed 154 times
Attachments
gfx_demo_pgm.P
(6.81 KiB) Downloaded 6 times
gfx_lorez_full.p
(5.82 KiB) Downloaded 5 times
Post Reply