Plot function in z88dk

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
Shaun_B
Posts: 410
Joined: Wed Apr 22, 2009 10:22 am

Re: Plot function in Z88DK

Post by Shaun_B » Thu Feb 01, 2018 8:46 am

RobertK wrote:
Wed Jan 31, 2018 3:53 pm
Thanks Andy, the speed is impressive!
I may seem entirely hopeless, but can anyone please assist me in wrapping this ASM code into a C function? I know that you need to put the ASM code between #asm and #endasm, but everything else regarding ASM in z88dk is currently trial and error for me.
I Robert,

As far as I know, the #asm/#endasm is deprecated - use it like this example:

https://github.com/sbebbers/fruit-machi ... ain.c#L417

Regards,

Shaun.

User avatar
RobertK
Posts: 38
Joined: Tue Dec 19, 2017 4:29 pm
Location: Vienna

Re: Plot function in z88dk

Post by RobertK » Thu Feb 01, 2018 10:56 am

Here is my sample program using a function I found at the z88dk forums, but for some reason my plotted dots don't appear:

Code: Select all

// found at https://www.z88dk.org/forum/viewtopic.php?id=6271
// Contributors: swensont, siggi, Timmy, alvin

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

// alvin: "It's faster to pop them or use HL to walk the stack.  The best solution is 
// Timmy's which uses CALLEE linkage so that the subroutine can pop the parameters 
// off the stack and not put them back:
static void __CALLEE__ zx_plot( short x, short y)
{
   __asm   
   POP HL   ; HL = return address
   POP DE   ; E  = y,  and  remove y from stack (necessary for CALLEE functions)
   POP BC   ; C  = x,  and  remove x from stack (necessary for CALLEE functions)
   PUSH HL  ; push return address back to stack
   LD B, E  ; BC = yx

   RET   ;; note the compiler automatically adds RET on closing '}' so this is not required
   __endasm;
}

main ( ) {

   short x,y;
   printf("plotting...\n\n");

   /* plot points */	
   zx_plot(10,10);
   zx_plot(20,20);
   
   /* draw a border around the screen */
   for (x = 0; x <= 63; ++x)
   {
	zx_plot(x, 43);
	zx_plot(x, 0);
   }
   for (y = 1; y <= 42; ++y)
   {
	zx_plot(0, y);
	zx_plot(63, y);
   }
   
   printf("done.\n");
   
}
zxplot.c
(1.16 KiB) Downloaded 28 times

I compile it with a batch file containing these commands (the z88root directory needs to be modified):

Code: Select all

setlocal
set z88root=C:\Misc\z88dk\

set path=%PATH%;%z88root%bin\
set zcccfg=%z88root%lib\config\
set z80_ozfiles=%z88root%lib\

zcc +zx81 -startup=2 -o zxplot -create-app zxplot.c
endlocal

pause
Can anyone please check why the plotted dots don't appear?

And/or can anyone try to put Andy's ASM code into a C function?

User avatar
siggi
Posts: 822
Joined: Thu May 08, 2008 9:30 am
Location: Dauernheim, Germany
Contact:

Re: Plot function in z88dk

Post by siggi » Thu Feb 01, 2018 12:16 pm

The code snippet above is only an example, how parameters (plot coordinates) could be passed efficiently from C to ASM. This snippet does NOTHING, because the plot-code is not included (Andy's could be added after " LD B, E ; BC = yx" and "RET"/end of function).

HTH Siggi
My ZX81 web-server: online since 2007
http://zx81-siggi.endoftheinternet.org/index.html

User avatar
Andy Rea
Posts: 1417
Joined: Fri May 09, 2008 2:48 pm
Location: notts UK

Re: Plot function in z88dk

Post by Andy Rea » Thu Feb 01, 2018 9:16 pm

here you go straight out of the notepad..... zx_plot and zx_unplot and another question or problem for someone more familiar with z88DK for zx81

i tried to expand the number of parameters passed to the function to allow a plotmode param however it just kept crashing the zx81, as you'll see much of the code for plot and unplot is the same so it is a little stupid to have 2 functions ...

any way... its pretty fast... NO BOUNDS CHECK and only works in standard text display mode with a FULL d-file.

chuck the following code in your z88dk assembler and smoke it !

Code: Select all

// found at https://www.z88dk.org/forum/viewtopic.php?id=6271
// Contributors: swensont, siggi, Timmy, alvin

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

// alvin: "It's faster to pop them or use HL to walk the stack.  The best solution is 
// Timmy's which uses CALLEE linkage so that the subroutine can pop the parameters 
// off the stack and not put them back:
static void __CALLEE__ zx_plot( short x, short y)
{
   __asm   
   POP HL   ; HL = return address
   POP BC   ; C  = y,  and  remove y from stack (necessary for CALLEE functions)
   EX (SP),HL
   LD  B,L  ; BC = XY
     
  

CALC_DFILE_BYTE:
	;WORK OUT SCREEN BYTE FROM CO-ORDS
	LD	A,C			;GET CURRENT Y POSITION 0 TO 47
	SRL	A			;A = 0 TO 23
					
	LD	E,A			;E = 1 * A
	
	LD	D,0			;DE = 1 *  A
	ADD	A,A			;A = 2* ,	MAX = 46
	ADD	A,A			;A = 4 *  , MAX = 92
	ADD	A,A			;A = 8 * , MAX =184
	ADD	A,A			;A = 16 Y , MAX = 112 AND CARRY SET
	LD 	H,0			;NO EFFECT ON CARRY
	RL	H			;PICK UP ANY CARRY	
	LD	L,A
	ADD	HL,HL		;HL = 32 * Y
	ADD	HL,DE		;HL = 33 * Y
	
	
	
	LD	A,B			;GET X POSITION 
	SRL	A			;DIVIDE BY 2
	LD	E,A
	LD	D,0			
	ADD	HL,DE		;HL NOW = SCREEN OFFSET
	LD	DE,($400C)
	ADD	HL,DE		;HL NOW = CURRENT DFILE + OFFSET
	
	INC	HL			;COMPENSATE FOR 1ST HALT IN DFILE

	;screen address now in HL

	
        LD      A,$01        
        SRA     C              
        JR      NC,EVEN_Y

        LD      A,$04


EVEN_Y: SRA     B          
        JR      NC,EVEN_X  

        RLCA			; 1 OR 4 BECOMES 2 OR 8                

EVEN_X:
	LD	C,A		;SAVE A FOR NOW

	LD 	A,(HL)		;GET BYTE FROM SCREEN
	RLCA
	CP	16
	JR	NC,A_ZERO
	RRCA
	JR	NC,GOOD_CHAR
	XOR	$8F		;ELSE INVERTED CHAR
	JR	GOOD_CHAR
A_ZERO:
	XOR	A
GOOD_CHAR:
	
	OR	C
	CP	8
	JR	C,LAST_BIT
	XOR	$8F

LAST_BIT:
	LD	(HL),A



   RET   ;; note the compiler automatically adds RET on closing '}' so this is not required
   __endasm;
}

static void __CALLEE__ zx_unplot( short x, short y)
{
   __asm   
   POP HL   ; HL = return address
   POP BC   ; C  = y,  and  remove y from stack (necessary for CALLEE functions)
   EX (SP),HL
   LD  B,L  ; BC = XY
     
  

UN_CALC_DFILE_BYTE:
	;WORK OUT SCREEN BYTE FROM CO-ORDS
	LD	A,C			;GET CURRENT Y POSITION 0 TO 47
	SRL	A			;A = 0 TO 23
					
	LD	E,A			;E = 1 * A
	
	LD	D,0			;DE = 1 *  A
	ADD	A,A			;A = 2* ,	MAX = 46
	ADD	A,A			;A = 4 *  , MAX = 92
	ADD	A,A			;A = 8 * , MAX =184
	ADD	A,A			;A = 16 Y , MAX = 112 AND CARRY SET
	LD 	H,0			;NO EFFECT ON CARRY
	RL	H			;PICK UP ANY CARRY	
	LD	L,A
	ADD	HL,HL		;HL = 32 * Y
	ADD	HL,DE		;HL = 33 * Y
	
	
	
	LD	A,B			;GET X POSITION 
	SRL	A			;DIVIDE BY 2
	LD	E,A
	LD	D,0			
	ADD	HL,DE		;HL NOW = SCREEN OFFSET
	LD	DE,($400C)
	ADD	HL,DE		;HL NOW = CURRENT DFILE + OFFSET
	
	INC	HL			;COMPENSATE FOR 1ST HALT IN DFILE

	;screen address now in HL

	
        LD      A,$01        
        SRA     C              
        JR      NC,UN_EVEN_Y

        LD      A,$04


UN_EVEN_Y: SRA     B          
        JR      NC,UN_EVEN_X  

        RLCA			; 1 OR 4 BECOMES 2 OR 8                

UN_EVEN_X:
	LD	C,A		;SAVE A FOR NOW

	LD 	A,(HL)		;GET BYTE FROM SCREEN
	RLCA
	CP	16
	JR	NC,UN_A_ZERO
	RRCA
	JR	NC,UN_GOOD_CHAR
	XOR	$8F		;ELSE INVERTED CHAR
	JR	UN_GOOD_CHAR
UN_A_ZERO:
	XOR	A
UN_GOOD_CHAR:
	LD B,A
	LD A,C
	CPL
	AND	B
	CP	8
	JR	C,UN_LAST_BIT
	XOR	$8F

UN_LAST_BIT:
	LD	(HL),A



   RET   ;; note the compiler automatically adds RET on closing '}' so this is not required
   __endasm;
}

main ( )
{

  short x,y;
  printf("plotting...\n\n");

   /* plot points */	
   zx_plot(5,10);
   zx_plot(15,20);
   
   /* draw a border around the screen */
	
   for (x = 0; x <= 63; ++x)
   {
	zx_plot(x, 47);
	zx_plot(x, 0);
   }
   for (y = 1; y <= 47; ++y)
   {
	zx_plot(0, y);
	zx_plot(63, y);
   }

 for (x = 0; x <= 63; ++x)
   {
	zx_unplot(x, 47);
	zx_unplot(x, 0);
   }
   for (y = 1; y <= 47; ++y)
   {
	zx_unplot(0, y);
	zx_unplot(63, y);
   }
   printf("done.\n");

 for (y=1; y <= 47; ++y)
  {
    for (x=0; x<=63; x += y)
      {
         zx_plot (x,y);
         zx_plot ((63-x), (47-y));
      }
   }

   
}

6 x ZX81, 1 x TS1500 , 1 x +3e, 1 x timex 2040 printer, 1 x timex 2020 cassette deck, siclair printer and some spectrum

User avatar
RobertK
Posts: 38
Joined: Tue Dec 19, 2017 4:29 pm
Location: Vienna

Re: Plot function in z88dk

Post by RobertK » Thu Feb 01, 2018 11:31 pm

Thanks Andy, that works just GREAT!

I hope that I will soon be able to post a little program that uses these functions.

And I hope some z88dk expert can try to merge the two functions, I assume that two functions need more memory than just one...

User avatar
Andy Rea
Posts: 1417
Joined: Fri May 09, 2008 2:48 pm
Location: notts UK

Re: Plot function in z88dk

Post by Andy Rea » Sat Feb 03, 2018 10:19 pm

once again straight from the notepad, further investigation into my previous attempt at combining plot and unplot into one function ended up with some illegal bytes been put into D-file, its all fixed up now and its 3 function to boot... PLOT , UNPLOT and XOR-PLOT

zx_plot( short x, short y, short pmode)
x is 0 to 63
y is 0 to 47
pmode is 0 = unplot, 1 = plot, 2 =xor plot

again NO BOUNDS check and only works with FULL d-file

regards Andy

Code: Select all

// found at https://www.z88dk.org/forum/viewtopic.php?id=6271
// Contributors: swensont, siggi, Timmy, alvin

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

// alvin: "It's faster to pop them or use HL to walk the stack.  The best solution is 
// Timmy's which uses CALLEE linkage so that the subroutine can pop the parameters 
// off the stack and not put them back:
static void __CALLEE__ zx_plot( short x, short y, short pmode)
{
   __asm   
   POP HL   ; HL = return address
   POP DE   ; E  = plot mode
   POP BC   ; C  = y,  and  remove y from stack (necessary for CALLEE functions)
   EX (SP),HL
   LD  B,L  ; BC = XY
   PUSH DE  ; SAVE PMODE FOR LATER
     
  

CALC_DFILE_BYTE:
	;WORK OUT SCREEN BYTE FROM CO-ORDS
	LD	A,C			;GET CURRENT Y POSITION 0 TO 47
	SRL	A			;A = 0 TO 23
					
	LD	E,A			;E = 1 * A
	
	LD	D,0			;DE = 1 *  A
	ADD	A,A			;A = 2* ,	MAX = 46
	ADD	A,A			;A = 4 *  , MAX = 92
	ADD	A,A			;A = 8 * , MAX =184
	ADD	A,A			;A = 16 Y , MAX = 112 AND CARRY SET
	LD 	H,0			;NO EFFECT ON CARRY
	RL	H			;PICK UP ANY CARRY	
	LD	L,A
	ADD	HL,HL		;HL = 32 * Y
	ADD	HL,DE		;HL = 33 * Y
	
	
	
	LD	A,B			;GET X POSITION 
	SRL	A			;DIVIDE BY 2
	LD	E,A
	LD	D,0			
	ADD	HL,DE		;HL NOW = SCREEN OFFSET
	LD	DE,($400C)
	ADD	HL,DE		;HL NOW = CURRENT DFILE + OFFSET
	
	INC	HL			;COMPENSATE FOR 1ST HALT IN DFILE

	;screen address now in HL

	
        LD      A,$01        
        SRA     C              
        JR      NC,EVEN_Y

        LD      A,$04


EVEN_Y: SRA     B          
        JR      NC,EVEN_X  

        RLCA			; 1 OR 4 BECOMES 2 OR 8                

EVEN_X:
	LD	B,A		;SAVE the new pixwl block in b

	LD 	A,(HL)		;GET BYTE FROM SCREEN
	RLCA
	CP	16
	JR	NC,A_ZERO
	RRCA
	JR	NC,GOOD_CHAR
	XOR	$8F		;ELSE INVERTED CHAR
	JR	GOOD_CHAR
A_ZERO:
	XOR	A
GOOD_CHAR:

        ; so at this point we have the screen char ( or zero if not a valif block graphic ) in A
        ; and the quarter block graphic in B, for plotting we OR B with A result in A
        ; for unplotting we complment ( invert )  B and then AND with A
	; for XOR plot we XOR B with A
	
	ld	C,a	;put a in safe place

	pop	de
	xor	a
	or	e
	jr	z,do_unplot
	dec	e
	jr	z,do_plot
        
	;else xor-plot

	ld	a,c	
	xOR	B
	CP	8
	JR	C,plot_done
	XOR	$8F
	jr	plot_done

do_unplot:
	LD A,B
	CPL
	AND	C
	CP	8
	JR	C,plot_done
	XOR	$8F
	jr	plot_done

do_plot:
	ld	a,c	
	OR	b
	CP	8
	JR	C,plot_done
	XOR	$8F



plot_done:
	LD	(HL),A



	



   RET   ;; note the compiler automatically adds RET on closing '}' so this is not required
   __endasm;
}



main ( )
{

  short x,y;
  printf("plotting...\n\n");

   /* plot points */	
   zx_plot(5,10,1);
   zx_plot(15,20,1);
   
   /* draw a border around the screen */
	
   for (x = 0; x <= 63; ++x)
   {
	zx_plot(x, 47,1);
	zx_plot(x, 0,1);
   }
   for (y = 1; y <= 47; ++y)
   {
	zx_plot(0, y,1);
	zx_plot(63, y,1);
   }

 for (x = 0; x <= 63; ++x)
   {
	zx_plot(x, 47,0);
	zx_plot(x, 0,0);
   }
   for (y = 1; y <= 47; ++y)
   {
	zx_plot(0, y,0);
	zx_plot(63, y,0);
   }
   printf("done.\n");

 for (y=1; y <= 47; ++y)
  {
    for (x=0; x<=63; x += y)
      {
         zx_plot (x,y,2);
         zx_plot (63-x, 47-y,2);
      }
   }

   
}


6 x ZX81, 1 x TS1500 , 1 x +3e, 1 x timex 2040 printer, 1 x timex 2020 cassette deck, siclair printer and some spectrum

User avatar
Andy Rea
Posts: 1417
Joined: Fri May 09, 2008 2:48 pm
Location: notts UK

Re: Plot function in z88dk

Post by Andy Rea » Sun Feb 04, 2018 6:35 pm

Just a little rejig of the assembler to make it a few bytes shorter, functionally the same as last post.

Code: Select all

// found at https://www.z88dk.org/forum/viewtopic.php?id=6271
// Contributors: swensont, siggi, Timmy, alvin

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

// alvin: "It's faster to pop them or use HL to walk the stack.  The best solution is 
// Timmy's which uses CALLEE linkage so that the subroutine can pop the parameters 
// off the stack and not put them back:
static void __CALLEE__ zx_plot( short x, short y, short pmode)
{
  __asm   
   POP HL   ; HL = return address
   POP DE   ; E  = plot mode
   POP BC   ; C  = y,  and  remove y from stack (necessary for CALLEE functions)
   EX (SP),HL
   LD  B,L  ; BC = XY
   PUSH DE  ; SAVE PMODE FOR LATER
     
  

CALC_DFILE_BYTE:
	;WORK OUT SCREEN BYTE FROM CO-ORDS
	LD	A,C			;GET CURRENT Y POSITION 0 TO 47
	SRL	A			;A = 0 TO 23
					
	LD	E,A			;E = 1 * A
	
	LD	D,0			;DE = 1 *  A
	ADD	A,A			;A = 2* ,	MAX = 46
	ADD	A,A			;A = 4 *  , MAX = 92
	ADD	A,A			;A = 8 * , MAX =184
	ADD	A,A			;A = 16 Y , MAX = 112 AND CARRY SET
	LD 	H,0			;NO EFFECT ON CARRY
	RL	H			;PICK UP ANY CARRY	
	LD	L,A
	ADD	HL,HL		;HL = 32 * Y
	ADD	HL,DE		;HL = 33 * Y
	
	
	
	LD	A,B			;GET X POSITION 
	SRL	A			;DIVIDE BY 2
	LD	E,A
	LD	D,0			
	ADD	HL,DE		;HL NOW = SCREEN OFFSET
	LD	DE,($400C)
	ADD	HL,DE		;HL NOW = CURRENT DFILE + OFFSET
	
	INC	HL			;COMPENSATE FOR 1ST HALT IN DFILE

	;screen address now in HL

	
        LD      A,$01        
        SRA     C              
        JR      NC,EVEN_Y

        LD      A,$04


EVEN_Y: SRA     B          
        JR      NC,EVEN_X  

        RLCA			; 1 OR 4 BECOMES 2 OR 8                

EVEN_X:
	LD	B,A		;SAVE the new pixwl block in b

	LD 	A,(HL)		;GET BYTE FROM SCREEN
	RLCA
	CP	16
	JR	NC,A_ZERO
	RRCA
	JR	NC,GOOD_CHAR
	XOR	$8F		;ELSE INVERTED CHAR
	JR	GOOD_CHAR
A_ZERO:
	XOR	A
GOOD_CHAR:

        ; so at this point we have the screen char ( or zero if not a valif block graphic ) in A
        ; and the quarter block graphic in B, for plotting we OR B with A result in A
        ; for unplotting we complment ( invert )  B and then AND with A
	; for XOR plot we XOR B with A
	
	ld	C,a	;save for unplot.... 

	pop	de
	dec	e
	jr	z,do_plot
	dec	e
	jr	z,xor_plot
        
	;else unplot

do_unplot:
	LD A,B
	CPL
	AND	C
	jr	plot_done

xor_plot:
	xOR	B
	jr	plot_done


do_plot:
	OR	b

plot_done:
	CP	8
	JR	C,put_char
	XOR	$8F



put_char:
	LD	(HL),A



	



   RET   ;; note the compiler automatically adds RET on closing '}' so this is not required
   __endasm;
}



main ( )
{

  short x,y;
  printf("plotting...\n\n");

   /* plot points */	
   zx_plot(5,10,1);
   zx_plot(15,20,1);
   
   /* draw a border around the screen */
	
   for (x = 0; x <= 63; ++x)
   {
	zx_plot(x, 47,1);
	zx_plot(x, 0,1);
   }
   for (y = 1; y <= 47; ++y)
   {
	zx_plot(0, y,1);
	zx_plot(63, y,1);
   }

 for (x = 0; x <= 63; ++x)
   {
	zx_plot(x, 47,0);
	zx_plot(x, 0,0);
   }
   for (y = 1; y <= 47; ++y)
   {
	zx_plot(0, y,0);
	zx_plot(63, y,0);
   }
   printf("done.\n");

 for (y=1; y <= 47; ++y)
  {
    for (x=0; x<=63; x += y)
      {
         zx_plot (x,y,2);
         zx_plot (63-x, 47-y,2);
      }
   }

   
}

6 x ZX81, 1 x TS1500 , 1 x +3e, 1 x timex 2040 printer, 1 x timex 2020 cassette deck, siclair printer and some spectrum

User avatar
Andy Rea
Posts: 1417
Joined: Fri May 09, 2008 2:48 pm
Location: notts UK

Re: Plot function in z88dk

Post by Andy Rea » Mon Feb 05, 2018 10:41 pm

just thinking about this... probably already functions out there but a Bresenham line and circle function would go nice with this.

Andy
6 x ZX81, 1 x TS1500 , 1 x +3e, 1 x timex 2040 printer, 1 x timex 2020 cassette deck, siclair printer and some spectrum

User avatar
RobertK
Posts: 38
Joined: Tue Dec 19, 2017 4:29 pm
Location: Vienna

Re: Plot function in z88dk

Post by RobertK » Tue Feb 06, 2018 8:37 am

Thanks once more, the parameterised function works great as well.
Andy Rea wrote:
Mon Feb 05, 2018 10:41 pm
just thinking about this... probably already functions out there but a Bresenham line and circle function would go nice with this.
And maybe more important: a function that checks whether there is a plotted pixel at (x,y), we could call that checkplot(x,y) for example. Is this technically possible?

I would need this for collision detection for a little game that I would like to port to the ZX81. I could of course use a 64x48 bit array to remember where the obstacles are, but such a function would help to save memory.
Last edited by RobertK on Tue Feb 06, 2018 2:06 pm, edited 1 time in total.

User avatar
Andy Rea
Posts: 1417
Joined: Fri May 09, 2008 2:48 pm
Location: notts UK

Re: Plot function in z88dk

Post by Andy Rea » Tue Feb 06, 2018 1:30 pm

RobertK wrote:
Tue Feb 06, 2018 8:37 am
Thanks once more, the parameterised function works great as well.
Andy Rea wrote:
Mon Feb 05, 2018 10:41 pm
just thinking about this... probably already functions out there but a Bresenham line and circle function would go nice with this.
And maybe more important: a function that checks whether there is a plotted pixel at (x,y), we could call the checkplot(x,y) for example. Is this technically possible?

I would need this for collision detection for a little game that I would like to port to the ZX81. I could of course use a 64x48 bit array to remember where the obstacles are, but such a function would help to save memory.
yes a checkpoint function should be easy to implement. when i get a moment i'll see what i can rustle up.
6 x ZX81, 1 x TS1500 , 1 x +3e, 1 x timex 2040 printer, 1 x timex 2020 cassette deck, siclair printer and some spectrum

Post Reply