How to write a simple C+asm horizontal scrolling game (Tutorial, z88dk)

How to write a simple C/asm horizontal scrolling game

Note: This tutorial has multiple parts... I am still writing but this first part, setting up a good C/asm development environment can take some time to get it to work properly, so I've decided to post this first, and hopefully I can finish the tutorial in a later post in this thread.


I) Setting Up development environment


I1) For starters, you'd need a C compiler. We will use z88dk for the task.

There are many versions of this compiler, as they make a new version every night.
In case people are reading this in 2030, the current version, as I am writing this, is http://nightly.z88dk.org/z88dk-win32-20200901-20ac09b-16910.zip (1st september 2020).

I can't really promise versions after this one will work. I'm probably not using this version myself either (checked, I'm using a version of earlier this year), but at least it should work, in theory.

Download a version and unzip/install it under C:\z88dk so that C:\z88dk\bin contains sccz80.exe
(I don't have linux installed on this moment so you need to figure out that one yourself.)

Create a directory C:\z88dk\tutorial2 just for this project. (Yes, I know it's lazy and you can do them anywhere you want.)
From now on I will just call this the project directory, or by its name. Almost everything from now on will be done inside this project directory. Assume everything you do is in there unless I say otherwise.



I2) A Text editor, if possible have simple one with syntax highlighting.

I am currently using Notepad++, which is a lightweight text editor.

While you are using one, make a batch file called "build.bat" with the following in it:
ECHO Setting up parameters

SET PATH=%PATH%;C:\z88dk\bin\
SET ZCCCFG=C:\z88dk\lib\config\
SET Z80_OZFILES=C:\z88dk\lib\

zcc +zx -vn tut2.c -o tut2.bin -lndos -create-app -zorg=32768

PAUSE 

This is just a simple batch file to start compiling your code. It saves time and you don't need to care about it any more.



I3) Building a file test (also, an emulator, obviously)

Now use your editor and create this file call "tut2.c" (in your project directory).
#include <spectrum.h>
#include <stdio.h>

void main()
{
  printf ("Hello World!\n");
}

Also, make a copy of this as "hello.c", as we are going to overwrite tut2.c for the real project later.

Now start your build.bat script, and z88dk should create a file called "tut2.tap"

Start this tap file with your favourite emulator and it should show up a message with "Hello World!" in it.

It not, then repeat this process, or ask someone here for help.

However, if this works, then you have a working development environment. Congrats!!!

If it works, you should rename the tut2.c and tut2.tap to something, so that we can make out new game in the file tut2.c.




II) Writing our game.

From now on we will be concentrating on writing the game in tut2.c.

A) Drawing a scrolling screen

We start again with drawing a screen that scrolls. It's mostly just simple setting up stuff for later.
void __FASTCALL__ scroll_left()
{
	#asm
	db 6,191,33,1,64,17,0,64,175,197,1,31,0,237,176,18,35,19,193,16,244,201
	#endasm
}

int main()
{
	while (1)
	{
		scroll_left();
		
		#asm
		halt
		#endasm
	}
	
	return 0;
}

Well, this is only the scrolling part of the game. It's also a lot of new C code, so let's just explain this a bit.

There are basically 2 parts in this short code.

The first part is a function (function is just a different word for "subroutine", like GO SUB and RETURN) called scroll_left(). As you can see, it consists of the machine code program that was also in our basic code. One of the great feature of this C compiler is that we can include machine code and assembly code in an easy way. (Every C compiler has their own way to do this, so this is NOT portable.)

The __FASTCALL__ modifier (z88dk specific) means that we don't need the C compiler to interfere with this part, because normally it will set up the stack and input and output parameters for us. Here it's not needed (we don't have any input or output), so we can just use a fastcall for it. If you write normal functions you should not use fastcall, and it's probably not very portable either. But here we're just using it to add out scrolling function in here, in assembly.

A more portable way could be using assembly code in separate assembly files. I'm too lazy for now.

By the way the brackets "()" in "scroll_left()" indicates that we have no input parameters, we will soon see other functions that have parameters. "void" means there are output values either.

The second function is the "main()" part, and this main function is called when the game is loaded. It has no input parameters because of the empty brackets, but the "int" before the main indicates we have an output parameter. We will "return 0" for this function for now. It means the function returns a 0 when it exits, it's not a line number.

The main function has a "while (1)" loop, which means the game will loop forever this part (and will never reach "return 0", for now). What it does is, it goes to the subroutine called "scroll_left()", run that code and then return. The other part of the main function is an HALT instruction, which does nothing much other than slowing down the programme.

-- END PART 1 --
«1

Comments


  • A2) Printing Characters
    #include <spectrum.h>
    
    void __FASTCALL__ scroll_left()
    {
    	#asm
    	db 6,191,33,1,64,17,0,64,175,197,1,31,0,237,176,18,35,19,193,16,244,201
    	#endasm
    }
    
    void __CALLEE__ print_uchar(uchar *address, uchar c)
    {
    	#asm
    		pop hl
    		pop bc
    		pop de
    		push hl
    		push de
    		ld hl, bc
    		add hl, hl
    		add hl, hl
    		add hl, hl
    		ld de,(23606)
    		add hl, de
    		pop de
    		ld b,8
    	loop2:
    		ld a,(hl)
    		ld (de),a
    		inc hl
    		inc d
    		djnz loop2
    		ret
    	#endasm
    }
    
    int main()
    {
    	while (1)
    	{
    		scroll_left();
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		#asm
    		halt
    		#endasm
    	}
    	
    	return 0;
    }
    
    

    For starters, the line "#include <spectrum.h>". This line indicates that we are using some pre-defined functions from this spectrum library. Z88dk has many libraries for different machines, and this indicates we are using the spectrum library functions. You can find the documentation for the library in this link: https://www.z88dk.org/wiki/doku.php?id=library:zxspectrum (but I wouldn't worry about it for now.)
    Adding this line also means that code from that library can be now included in the resulting tap file. z88dk is designed in a way that only relevant code will be added, to keep the footprint as small as possible.

    The spectrum.h library does have a function (like those DEF FNs) that helps to find the start address of a location, and it's called zx_cyx2saddr(y,x). This function returns the screen address, given its y and x coordinates. We send that address into the print function.

    Well, I couldn't find a good simple print function, so I decided to write my own print_uchar() here.

    The __CALLEE__ parameter (also very z88dk specific) in here means that you can receive and use parameters in this function. In z88dk, parameters are pushed onto the stack and then a call is made. The code must first pop the return address and then the parameters and work with them. In this case we take the character code, find the address of that character in spectrum ROM, and then we poke the 8 bytes of the font into the screen.

    In the main() part, the only difference is the new print_uchar function, this looks pretty similar to the basic version now.

    Before I forget, the use of "#asm halt #endasm" is obsolete. In SDCC there is a function called "intrinsic_halt()" nowadays that does a halt but does not confuse SDCC's internal optimiser. We are still using the old notation because of backward compatibility. (Well, also I couldn't get it to work somehow so I defaulted to this.)



    B) Adding a player

    First, we will draw a player on screen:
    int main()
    {
    	char y;
    	
    	y = 10;
    	
    	while (1)
    	{
    		scroll_left();
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		print_uchar(zx_cyx2saddr(y,3), '>');
    		
    		#asm
    		halt
    		#endasm
    	}
    	
    	return 0;
    }
    

    3 New Lines are added in main(). (I've skipped the other 2 functions in the source for now, you can find them in the previous part.)

    In "char y;", a variable is declared. Other than that, "y=10" is sets the start y position to 10, and we re-use print_uchar to draw our player ">".

    Well, the player will be printed at coordinates (y,3)... Remember the spectrum basic has its Y coordinate first? Well, the z88dk version uses this convention too.

    Also if you add this code and then run the game, you'll find that the ">" also scrolls to the left, leaving a nice looking trail.

    Of course it's not really a player if you can't move it, so we are going to add keyboard controls now.



    C) Adding Keyboard control
    #include <input.h>
    
    
    
    
    int main()
    {
    	char y;
    	
    	y = 10;
    	
    	while (1)
    	{
    		scroll_left();
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		y = y + (in_Inkey()=='a') - (in_Inkey()=='q');
    		y = y + (y<0)-(y>20);
    		
    		print_uchar(zx_cyx2saddr(y,3), '>');
    		
    		game_over = (zx_screenstr(y,4)=='*');
    		
    		#asm
    		halt
    		#endasm
    	}
    	
    	return 0;
    }
    

    The keyboard controls here needs the library "input.h", put this line directly under the "spectrum.h" line. The documentation for this library is located at https://www.z88dk.org/wiki/doku.php?id=library:input or https://github.com/z88dk/z88dk/wiki/Classic---Input-library

    Yes, that's it, 2 new lines added in this version. What it does is that, if the player press "a" then "(in_Inkey()=="a")" becomes "1". So the player moves down.
    And surprisingly, pressing "q" will makes the other part becomes 1, and therefore y will decrease with 1, and the player moves up.

    "in_Inkey()" is a function from the input.h library we talked about earlier. I was planning to add joystick support in here but I couldn't get it to work right now. Maybe later I can get it working, but for now in_Inkey() is fine.

    If you play this now, you might notice you can move the player outside of screen, causing an error. therefore, we add the following line to stop that:
    	y = y + (y<0)-(y>20);
    

    This line basically just stops the coordinate going negative or larger than 20. Remember this line in the BASIC version? We literally copied it from there. :)

    The whole program already looks like a game now, again!



    D) Adding a thing to end the game


    Now all we need is to make the game end if the player hits a star.

    Surprisingly, the spectrum.h library has a function we can use! It does not work in exact the same way, but for detecting stars, it works!

    We'll add the following code here:
    	game_over = (zx_screenstr(y,4)=='*');
    

    Easy, isn't it? We just check the space just to the right of the player if there is anything there, and if so, then we just end the game.

    We'll also declare this game_over variable, and use it to end the game instead of while (1):

    (note, the functions we described earlier are still needed, as well as the #include-s.)
    int main()
    {
    	char y;
    	char game_over;
    	
    	y = 10;
    	game_over = 0;
    	
    	while (!game_over)
    	{
    		scroll_left();
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		y = y + (in_Inkey()=='a') - (in_Inkey()=='q');
    		y = y + (y<0)-(y>20);
    		
    		print_uchar(zx_cyx2saddr(y,3), '>');
    		
    		game_over = (zx_screenstr(y,4)=='*');
    		
    		#asm
    		halt
    		#endasm
    	}
    	
    	return 0;
    }
    

    We're almost there! Unfortunately the scores part is a lot of work, because now you'll have to print a string.

    And that means we need 2 more functions, one to print a string, and one to convert an integer to a string.

    Here is the complete code, up to this part:
    #include <spectrum.h>
    #include <input.h>
    
    void __FASTCALL__ scroll_left()
    {
    	#asm
    	db 6,191,33,1,64,17,0,64,175,197,1,31,0,237,176,18,35,19,193,16,244,201
    	#endasm
    }
    
    void __CALLEE__ print_uchar(uchar *address, uchar c)
    {
    	#asm
    		pop hl
    		pop bc
    		pop de
    		push hl
    		push de
    		ld hl, bc
    		add hl, hl
    		add hl, hl
    		add hl, hl
    		ld de,(23606)
    		add hl, de
    		pop de
    		ld b,8
    	loop2:
    		ld a,(hl)
    		ld (de),a
    		inc hl
    		inc d
    		djnz loop2
    		ret
    	#endasm
    }
    
    int main()
    {
    	char y;
    	char game_over;
    	
    	y = 10;
    	game_over = 0;
    	
    	while (!game_over)
    	{
    		scroll_left();
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		y = y + (in_Inkey()=='a') - (in_Inkey()=='q');
    		y = y + (y<0)-(y>20);
    		
    		print_uchar(zx_cyx2saddr(y,3), '>');
    		
    		game_over = (zx_screenstr(y,4)=='*');
    		
    		#asm
    		halt
    		#endasm
    	}
    	
    	return 0;
    }
    

    -- END OF PART 2 --
  • edited September 5
    Thank you for your fast tutoring
    if i was not aware off the basic lines i realy would have to dig out
    print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    a lot.
    I notice yuo prefered to write your own print_uchar routine and i think to understand zx_cyx2addr is a z88 function that checks the ZX_Coordinates_of_Y_and_X and recalcs them 2_adress which is then used in DE_register to move the correct chr$ from HL to DE.
    Is there no simple "print at" variant somewhere? I think those things are what make me slow in C. Basic is so ... basic and direct.
    thanks!!!
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited September 6
    Crisis wrote: »
    if i was not aware off the basic lines i realy would have to dig out
    print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    a lot.

    If this is easier, you could read
    print_uchar( zx_cyx2saddr(rand()%21,30), '*');
    
    as
    print( at( rand()%21 , 30 ), '*');
    
    There is a difference, the print_uchar() function only prints one character and not a whole string. I will discuss that in the next part.

    ===
    I notice yuo prefered to write your own print_uchar routine and i think to understand zx_cyx2addr is a z88 function that checks the ZX_Coordinates_of_Y_and_X and recalcs them 2_adress which is then used in DE_register to move the correct chr$ from HL to DE.
    Something like that. zx_cyx2saddr(y,x) returns an address on the screen memory, and i use it directly to feed the print_uchar() routine.
    Just like rand()%21 returns a number and I used it immediately in that zx_cyx2saddr() function.

    ===
    Is there no simple "print at" variant somewhere? I think those things are what make me slow in C. Basic is so ... basic and direct.
    No not really. After a few hours searching how printing should be done easier, I got stuck... I think the idea is to use either ANSI or ZX codes in conio.h but I can't get it working... I couldn't find any printing routine that prints the normal Sinclair font, and without it, screen$ would not work. So I wrote my own.

    Too much work to figure out, and writing assembly is just quicker. :P (may not be for you, though)

    Good thing is, now that those function have been written, I can reuse the function in a different game later.
    Post edited by Timmy on
  • edited September 6
    E1) Adding a score


    The last thing we add for now is a score counter, and printing it, also we return score at the end:
    int main()
    {
    	char y;
    	char game_over;
    	int score;
    	char score_str[15] = "score : 00000 ";
    	
    	y = 10;
    	game_over = 0;
    	score = 0;
    	
    	while (!game_over)
    	{
    		scroll_left();
    		
    		print_string(zx_cyx2saddr(21,0), score_str);
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		y = y + (in_Inkey()=='a') - (in_Inkey()=='q');
    		y = y + (y<0)-(y>20);
    		
    		print_uchar(zx_cyx2saddr(y,3), '>');
    		
    		game_over = (zx_screenstr(y,4)=='*');
    		
    		score = score + 10;
    
    		#asm
    		halt
    		#endasm
    	}
    	
    	return score;
    }
    

    We set the start score as 0 at the beginning of the game.
    Then we print the score right after scrolling the screen.
    And we increase the score right after the game over check.

    Also, we return score at the end of the game.

    The variable score_str is used to store the score and display it. Note that this string is 15 characters long, even if you can only see 14 visible characters, because every string in C ends with a \0 character.



    E2) Printing a string

    This part of the game is printing the score_str using print_string().
    It turns out, it's a simple regular C function, nothing special like assembly or machine code here:
    void print_string(uchar *address, uchar *string_address)
    {
    	uchar *s;
    	uchar *a;
    	
    	a = address;
    	s = string_address;
    	
    	while (s[0])
    	{
    		print_uchar(a, s[0]);
    		s++;
    		a++;
    	}
    }
    

    Due to the weird way of the Spectrum screen is being set up, you'd think this would never work. But as long as the string fits onto 1 line, this function will work. (You mean, this function is only fast because it cuts corners? -- Ed)



    E3) Updating score_str

    The final part is to update the variable score_str each time the score is increased.

    We do this by adding a line just above the line the score is being printed:
    i16toa(score, &(score_str[8]) );
    

    This function is going to change some bytes inside the score_str, given the score. What it does is, it updates the "00000" inside the string into that score number.

    We are "borrowing" some code here, and it's a callee assembly function:
    void __CALLEE__ i16toa(uint number, char* dest)
    {
    	#asm
    
    		pop bc
    		pop de				; de = dest
    		pop hl				; hl = number
    		push bc
    
    		ld bc,-10000
    		call num1
    		ld bc,-1000
    		call num1
    		ld bc,-100
    		call num1
    		ld c,-10
    		call num1
    		ld c,b
    
    .num1	ld	a,'0'-1
    .num2	inc	a
    
    		add hl,bc
    		jr c, num2
    		sbc hl,bc
    
    		ld (de),a
    		inc de
    		ret
    	#endasm
    }
    

    I think I got this code a WoS post somewhere, but I'm also sure it's from Jonathan, I just can't remember where I exactly got it from.

    I could explain a lot of it but to be honest I just use it instead. If you have problems with it, you can always write your own... :P

    Anyway, that's all the code for today.



    TLDR) The complete game in C+asm:
    #include <spectrum.h>
    #include <input.h>
    
    void __FASTCALL__ scroll_left()
    {
    	#asm
    	db 6,191,33,1,64,17,0,64,175,197,1,31,0,237,176,18,35,19,193,16,244,201
    	#endasm
    }
    
    void __CALLEE__ print_uchar(uchar *address, uchar c)
    {
    	#asm
    		pop hl
    		pop bc
    		pop de
    		push hl
    		push de
    		ld hl, bc
    		add hl, hl
    		add hl, hl
    		add hl, hl
    		ld de,(23606)
    		add hl, de
    		pop de
    		ld b,8
    	loop2:
    		ld a,(hl)
    		ld (de),a
    		inc hl
    		inc d
    		djnz loop2
    		ret
    	#endasm
    }
    
    void print_string(uchar *address, uchar *string_address)
    {
    	uchar *s;
    	uchar *a;
    	
    	a = address;
    	s = string_address;
    	
    	while (s[0])
    	{
    		print_uchar(a, s[0]);
    		s++;
    		a++;
    	}
    }
    
    void __CALLEE__ i16toa(uint number, char* dest)
    {
    	#asm
    
    		pop bc
    		pop de				; de = dest
    		pop hl				; hl = number
    		push bc
    
    		ld bc,-10000
    		call num1
    		ld bc,-1000
    		call num1
    		ld bc,-100
    		call num1
    		ld c,-10
    		call num1
    		ld c,b
    
    .num1	ld	a,'0'-1
    .num2	inc	a
    
    		add hl,bc
    		jr c, num2
    		sbc hl,bc
    
    		ld (de),a
    		inc de
    		ret
    	#endasm
    
    }
    
    int main()
    {
    	char y;
    	char game_over;
    	int score;
    	char score_str[15] = "score : 00000 ";
    	
    	y = 10;
    	game_over = 0;
    	score = 0;
    	
    	while (!game_over)
    	{
    		scroll_left();
    		
    		i16toa(score, &(score_str[8]) );
    		print_string(zx_cyx2saddr(21,0), score_str);
    		
    		print_uchar(zx_cyx2saddr(rand()%21,30), '*');
    			
    		y = y + (in_Inkey()=='a') - (in_Inkey()=='q');
    		y = y + (y<0)-(y>20);
    		
    		print_uchar(zx_cyx2saddr(y,3), '>');
    		
    		game_over = (zx_screenstr(y,4)=='*');
    		
    		score = score + 10;
    
    		#asm
    		halt
    		#endasm
    	}
    	
    	return score;
    }
    


    --- END Part 3 ---
    Post edited by Timmy on
  • Thanks again, or still thanks,
    you give another riddle
    you end with 'return score' and its the main()
    what will happen with this score value?

    btw, the decimal printing routine?
    i know it and its from a interrupt CLOCK example !!
    i think i used it to in my MC aproach off OUTplay
    cheers!
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • Crisis wrote: »
    you end with 'return score' and its the main()
    what will happen with this score value?

    That is returned by the USR statement. As in: LET score = USR 32768
  • JianYang wrote: »
    Crisis wrote: »
    you end with 'return score' and its the main()
    what will happen with this score value?

    That is returned by the USR statement. As in: LET score = USR 32768

    Thank you, that realy explains.
    I was wondering about the POP statements with which the asm routines start and i figure that BC is usualy the starting point of a USR call. And reversed BC passes its value to BASIC and with a "LET score=usr adres" that will be the reached score.
    its clear now, thanks!
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited September 10
    Hello
    this is my very naive aproach off the AT command:
    /* attempt to write ZX_AT(y,x) command for z88dk and others */
    /* AA explains: http://www.worldofspectrum.org/forums/discussion/comment/129919#Comment_129919
    /*   BB = block (0-2) indicates top third, middle third or bottom third of screen
    /*   SSS = scan line (0-7) indicates the vertical pixel row within a character
    /*   LLL = vertical character line (0-7) within a block
    /*   CCCCC = horizontal character coordinate (0-31)
    /* 8-bit Character Coordinates
    /*   X : 000C CCCC (0-31)
    /*   Y : 000B BLLL (0-23)
    
    
    void ZX_AT (int y , int x)
    
             {
               int y , x , screenaddress = 16384 ;
    
               screenaddress = screenaddress + (256 * y) + x ;
    
               return screenaddress ;
    
              }
    

    does it make sence ??
    btw i have some 'perl' problems , hence my z88dk install does not work YET ...

    edit:
    its NOT working like this, this basic routine shows what happens.:
    10 LET s=16384: LET y=10 : LET x=23
    20 let sad=s+(256*y)+x
    25 print AT y,x;"!"
    30 POKE sad,255
    
    well this is a tutorial so i wont put all my bs here.
    cheers
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • Crisis wrote: »
    its NOT working like this
    If you want to figure it out for yourself, ignore this post. Otherwise here is a working example of what I think is what you wanted to do:
    #define byte unsigned char
    
    byte *at (int y, int x) {
      byte *base = (byte *) 16384;
      int block = (y >> 3) & 3;
      return base + 2048 * block + 32 * (y & 7) + x;
    }
    
    int main () {
      int y;
      byte *a;
    
      for (y = 0; y < 24; ++y) {
        a = at (y, 31 - y);
        *a = 255;
      }
      return 0;
    }
    
  • edited September 11
    JianYang wrote: »
    Crisis wrote: »
    its NOT working like this
    If you want to figure it out for yourself, ignore this post.

    i think i cant. ignore it
    my attempt is nothing more then the PRINT AT y,x;
    even without a character actualy. but you have to check that so a chr$ is involded
    btw my z88dk is still lacking 4 perl parts, i still have to find them in debian, but apart fromthat i thoufgt that it should work in basic so i made basic lines. the c routine should calculate the same place so the PRINT AT y,x should be at the same spot as the upper screen adres of that screen position.
    well, the POKE is 2 lines higher then the PRINT statement, in basic.
    You 'do much more already with 'printing' the whole screen with all poke 255 so probably thats 192 lines on screen.
    i had the naive aproach that 16384 is %0100000000000000
      plus256 x %000BBLLL=                %000BBLLL00000000
             plus  %000CCCCCC             %00000000000ccccc
                                        = %010BBLLL000CCCCC
    
    but not in reallity
    i try it in ASM first with OR or some

    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • I'm not sure if I follow you.
    20 let sad=s+(256*y)+x
    
    If you multiply y with 256 you're addressing pixel lines, if you multiply y with 32 you're addressing character lines. In each case it only works for y between 0 and 7. For everything beyond that, you'll have to do a little more work.

    So if you just want to test the simplest case, you should not set y to 10.

    I might still completely fail to understand you. In that case I apologize for wasting your time.
  • I'm not sure if I follow you.
    20 let sad=s+(256*y)+x
    
    If you multiply y with 256 you're addressing pixel lines, if you multiply y with 32 you're addressing character lines. In each case it only works for y between 0 and 7. For everything beyond that, you'll have to do a little more work.

    So if you just want to test the simplest case, you should not set y to 10.

    I might still completely fail to understand you. In that case I apologize for wasting your time.
  • I am not completely sure what you are trying to do here. Are you all trying to reinvent zx_cyx2saddr(), but using C instead of the original assembly function? If so, then you have my blessings for it.

    In the meantime, I should go and make another game.

    Still, this is just a small game to let you add small things onto it. You could try having stuff to collect, a high score, a one button runner, variable speed, colours, and many more.
  • edited September 16
    @JianYang dear speccy lover, you do NOt waste my time. I indeed try to calc the correct pixel line being the topline of the character. Then from that pixel line the full 8 bytes off any chr$ position can be copied by a serial POKE . As @Timmy mentions i try to make a very simple and very "C' variation of the zx_cyx2saddr routine.
    I am still learning c in its basic cq original form.
    EG i should make a POINTER from the screen adres since the adres is the pointer to the value of that adres.
    the two line off might be by the blocks since thats a breakpoint in the calculation.

    btw, i even dont get z88dk installed since my PERL is not complete. i CANT install App::Prove , CPU::Z80::Assembler and two other commands
    I did find CPU::Z80::Assembler as a package to be installed,
    https://metacpan.org/pod/CPU::Z80::Assembler
    yet this package itself needs more OTHER perl files which are NOT installed on my computer and i seem NOt to be able to find them in debian. So my z88dk is crippled and can NOT test.
    if that means its not working at all? i do not know. i know i have to install 2 different packege serially and probably even more.
    a simple perl_repository whould be very helpfull. Now i am preparing the preparation for the prepare of installing z88dk. realy like that in a quadriple step.
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • I indeed try to calc the correct pixel line being the topline of the character.

    Then you definitely want to multiply y with 32. As in:
       10 FOR y=0 TO 7
       20 PRINT AT y,0;"*";
       30 POKE 16384+y*32,255
       40 NEXT y
    
    btw, i even dont get z88dk installed since my PERL is not complete. i CANT install App::Prove , CPU::Z80::Assembler and two other commands
    What exactly are the errors you're getting? I'm also using z88dk on debian. I had a lot of trouble getting it to work right, but I don't remember any problems with perl modules. But then my versions of z88dk and debian are quite old, so that might not translate to your installation.
  • I have never tried installing z88dk on a linux computer, so I can't help you out much, for now.

    I might need to learn how to do it soon, but maybe you should post some screenshots or screen outputs to show what is going wrong. Even if we might not be able to help you immediately, it might help me to understand how to install it in linux myself.
  • edited September 17
    JianYang wrote: »
    I indeed try to calc the correct pixel line being the topline of the character.

    Then you definitely want to multiply y with 32. As in:
       10 FOR y=0 TO 7
       20 PRINT AT y,0;"*";
       30 POKE 16384+y*32,255
       40 NEXT y
    

    thank you
    as usual i kept turning around the wrong idear, inc h is for pixels and the BB is 2048 per 8 lines
    this is (your) new routine in basic
       10 FOR y=0 TO 21 : LET x=y
       20 PRINT AT y,x;"*";
       30 POKE 16384  +y*32  +INT ( y/8 ) *2048 +x  , 255
       40 NEXT y
    
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited September 17
    @Timmy Hi, i use debian.10-3 upgraded to 10.5, work name is 'buster'
    In debian the path has to set in .bashrc just like in other batch files or ~/.bash_profile
    i just added it like explained above
    export PATH=${PATH}:${HOME}/z88dk/bin
    export ZCCCFG=${HOME}/z88dk/lib/config
    
    to the end off .bashrc wich is in the common /home/username directory

    the list off DEV is given on https://github.com/z88dk/z88dk/wiki/installation
    for newbies in installing: remember its the DEV that you have to install since thats the source. just the program is not enough in this case.

    The instruction mention that for the TESTing some perl packages are needed. i think they are old by now but perhaps i just have the wrong debian since there are severall different installs like 4, 8, 9, 10, 11 and 12 and then there is "free" and "nonefree" and those all specialised per type of computer-architecture. its a burdian !!!! (oops)

    so i skip that for now and have a look what happens with
    cd z88dk
    chmod 777 build.sh
    ./build.sh
    

    and it starts good i think:
    chris@cb:~/z88dk$ chmod 777 build.sh
    chris@cb:~/z88dk$ ./build.sh
    make -C testsuite
    make[1]: Entering directory '/home/chris/z88dk/testsuite'
    make[1]: Nothing to be done for 'all'.
    make[1]: Leaving directory '/home/chris/z88dk/testsuite'
    make: Entering directory '/home/chris/z88dk/libsrc'
    Makefile:27: *** mixed implicit and normal rules: deprecated syntax
    Makefile:27: *** mixed implicit and normal rules: deprecated syntax
    make: Leaving directory '/home/chris/z88dk/libsrc'
    make: Entering directory '/home/chris/z88dk/libsrc'
    
    --- Building Jupiter Ace Library ---
    
    
    lets wait a 20 minutes since its more then 50 different whole emulators in fact
    i made the test.c file as given on the install page:
    #include <stdio.h>
    int main()
    {
       printf("Hello World !\n");
           return 0;
    }
    
    then invoke the compiling:
    chris@cb:~/z88dk$ dir test.c
    test.c
    
    chris@cb:~/z88dk$ zcc +zx -vn test.c -o test -lndos
    z80asm: invalid option -- 'D'
    z80asm: invalid option -- '_'
    z80asm: invalid option -- '_'
    z80asm: invalid option -- 'S'
    z80asm: invalid option -- 'C'
    z80asm: invalid option -- 'C'
    z80asm: invalid option -- 'Z'
    z80asm: invalid option -- '8'
    z80asm: invalid option -- '0'
    z80asm: invalid option -- 's'
    z80asm: invalid option -- 'm'
    z80asm: invalid option -- 'z'
    z80asm: invalid option -- '8'
    z80asm: invalid option -- '0'
    /tmp/tmpXX7kcY57.asm:1: error: command or comment expected (was MODULE test_c )
    /tmp/tmpXX7kcY57.asm:2: error: command or comment expected (was LINE 0, "test.c" )
    /tmp/tmpXX7kcY57.asm:12: error: command or comment expected (was C_LINE	0,"test.c" )
    /tmp/tmpXX7kcY57.asm:14: error: command or comment expected (was MODULE	test_c )
    /home/chris/z88dk/lib/config/../..//lib/z80_crt0.hdr:15: error: command or comment expected (was EXTERN	base_graphics	; Address of graphics screen )
    
    
    
    
    ( BIG WASTE )
    
    
    
    
    /tmp/tmpXX7kcY57.asm:497: error: command or comment expected (was GLOBAL	vfscanf )
    /tmp/tmpXX7kcY57.asm:498: error: command or comment expected (was GLOBAL	vsscanf )
    /tmp/tmpXX7kcY57.asm:499: error: command or comment expected (was GLOBAL	getarg )
    /tmp/tmpXX7kcY57.asm:500: error: command or comment expected (was GLOBAL	fchkstd )
    /tmp/tmpXX7kcY57.asm:501: error: command or comment expected (was GLOBAL	fgetc_cons )
    /tmp/tmpXX7kcY57.asm:502: error: command or comment expected (was GLOBAL	fgetc_cons_inkey )
    /tmp/tmpXX7kcY57.asm:503: error: command or comment expected (was GLOBAL	fputc_cons )
    /tmp/tmpXX7kcY57.asm:504: error: command or comment expected (was GLOBAL	fgets_cons )
    /tmp/tmpXX7kcY57.asm:505: error: command or comment expected (was GLOBAL	puts_cons )
    /tmp/tmpXX7kcY57.asm:506: error: command or comment expected (was GLOBAL	fabandon )
    /tmp/tmpXX7kcY57.asm:507: error: command or comment expected (was GLOBAL	fdtell )
    /tmp/tmpXX7kcY57.asm:508: error: command or comment expected (was GLOBAL	fdgetpos )
    /tmp/tmpXX7kcY57.asm:509: error: command or comment expected (was GLOBAL	rename )
    /tmp/tmpXX7kcY57.asm:510: error: command or comment expected (was GLOBAL	remove )
    /tmp/tmpXX7kcY57.asm:511: error: command or comment expected (was GLOBAL	getk )
    /tmp/tmpXX7kcY57.asm:512: error: command or comment expected (was GLOBAL	getk_inkey )
    /tmp/tmpXX7kcY57.asm:513: error: command or comment expected (was GLOBAL	printk )
    /tmp/tmpXX7kcY57.asm:514: error: command or comment expected (was GLOBAL	perror )
    /tmp/tmpXX7kcY57.asm:515: error: command or comment expected (was GLOBAL	_main )
    /tmp/tmpXX7kcY57.asm:521: error: unable to resolve reference: printf 
    /tmp/tmpXX7kcY57.asm:521: error: unable to resolve reference: i_1+0 
    *** 799 errors found ***
    
    

    the list is TO LONG
    799 error !!!
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
    Thanked by 1Timmy
  • I think I remember there is an incompatible program called z80asm in Debian. Try
    type z80asm
    
    and look if it's a z80asm in your z88dk installation. You might have to remove the conflicting assembler.
  • JianYang wrote: »
    I think I remember there is an incompatible program called z80asm in Debian. Try
    type z80asm
    
    and look if it's a z80asm in your z88dk installation. You might have to remove the conflicting assembler.

    I did read that remark.
    was i so eager to install an z80asm ? or is it native to debian?
    I will check my install'
    thank you

    btw

    this is my last rewrite of my attempt, untested as you know
    it point to a single upper line adres
    /* attempt to write ZX_AT(y,x) command for z88dk and others */
    
    /* AA explains: http://www.worldofspectrum.org/forums/discussion/comment/129919#Comment_129919
    /*   BB = block (0-2) indicates top third, middle third or bottom third of screen
    /*   SSS = scan line (0-7) indicates the vertical pixel row within a character
    /*   LLL = vertical character line (0-7) within a block
    /*   CCCCC = horizontal character coordinate (0-31)
    /* 8-bit Character Coordinates
    /*   X : 000C CCCC (0-31)
    /*   Y : 000B BLLL (0-23)
    
    /* this routine returns the topline screen address from the supposed chr$ coordinates as 16 bit value */
    /* so its name is not correct at this moment */
    
    void ZX_AT(int y , int x)
    
             {
               int y , yb , x , *screenaddress = 16384 ;                     /* pointer to screenadress? */
               yb = y / 8 ;                                                  /* integer is not floating > its INT (y/8) */
               *screenaddress = screenaddress + y * 32 + yb * 2048  + x ;
               return screenaddress ;                                        /* address top pixel line of the chr$  actualy */
              }
    
    

    i hope this one is decent enough to be 'c'
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited September 18
    You were correct
    https://www.tablix.org/~avian/blog/articles/z80dasm/
    I deinstalled it and the test now only mentions:
    chris@cb:~/z88dk$ zcc +zx -vn test.c -o test -lndos
    test: Is a directory
    chris@cb:~/z88dk$ 
    
    which i have to check ofcourse
    thanks for reminding me

    btw:
    https://stackoverflow.com/questions/592620/how-can-i-check-if-a-program-exists-from-a-bash-script
    could be helpfull for auwtomation ??
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • i hope this one is decent enough to be 'c'
    Just from looking at it, I'd propose two changes (ignore if you want to figure it out for yourself):
    void ZX_AT(int y , int x)
    
             {
               int y , yb , x ;
               char *screenaddress = 16384 ;                     /* if you make it an integer pointer, your offsets get automatically doubled */
               yb = y / 8 ;                                                  /* integer is not floating > its INT (y/8) */
               screenaddress = screenaddress + y * 32 + yb * 2048  + x ; /* if you write into *screenaddress you write into the memory pointed to by screenaddress, read the beginning of screen RAM */
               return screenaddress ;                                        /* address top pixel line of the chr$  actualy */
              }
    
    The rest looks fine to me.
    chris@cb:~/z88dk$ zcc +zx -vn test.c -o test -lndos 
    
    Try to add
    -create-app
    
    that should produce test.tap.

    Congratulations! I think you've made it.
  • edited September 18
    I like 2048, it's a nice game. I think I even made a Spectrum version of it.

    I wonder why you are not using 1792 instead of 2048, though.

    Did you know that 1792 is the year of the first French Republic?
    Post edited by Timmy on
  • Strangely, I'm trying to install z88dk on a linux today, and I got stuck somewhere during sdcc that it asks me to be online checking on a subversion server.

    Does it really have to be online just for sdcc? I am not even going to use that part.
  • Timmy wrote: »
    Does it really have to be online just for sdcc? I am not even going to use that part.

    Looks like it. There's this in the Makefile:
    bin/zsdcc$(EXESUFFIX):
            svn checkout -r 11535 https://svn.code.sf.net/p/sdcc/code/trunk/sdcc -q $(SDCC_PATH)
    

    Seems like the build process downloads a certain version of sdcc and then patches it.
  • edited September 19
    Timmy wrote: »
    Strangely, I'm trying to install z88dk on a linux today, and I got stuck somewhere during sdcc that it asks me to be online checking on a subversion server.

    Does it really have to be online just for sdcc? I am not even going to use that part.

    Hi
    have some problems with sdcc is where i got stranded some years ago. I dont recall a online request or some alike. And probably you do not need to install indeed.
    although zcc ??
    ~/z88dk/doc
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • JianYang wrote: »
    Looks like it. There's this in the Makefile:
    bin/zsdcc$(EXESUFFIX):
            svn checkout -r 11535 https://svn.code.sf.net/p/sdcc/code/trunk/sdcc -q $(SDCC_PATH)
    

    Seems like the build process downloads a certain version of sdcc and then patches it.

    Well, the number changes every time so I guess it's just a latest working patched version.

    I think my problem is that I can't download it in advance, but only during building of z88dk. And removing that line doesn't work. And I don't even use sdcc either.
    Crisis wrote: »
    Hi
    have some problems with sdcc is where i got stranded some years ago. I dont recall a online request or some alike. And probably you do not need to install indeed.

    I'm asking if there is something easier in the official forums. Hopefully they can make this experience better.
  • JianYang wrote: »
    chris@cb:~/z88dk$ zcc +zx -vn test.c -o test -lndos 
    
    Try to add
    -create-app
    
    that should produce test.tap.

    Congratulations! I think you've made it.

    zcc +zx -vn test.c -o test -lndos -create-app
    thank you, the print on screen is even in 64c, a little surprice
    and in the main z88dk directory is the directory 'test'. thats why the earlier remark was given. ive tested the test routine in test now.
    so it seems to work now.
    And i was not yet heading for sdcc as Timmy already did. I think i first read a bit more and leave this since this "yeah finaly" moment
    so i can try my own c-scribbles and test it all over.
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • Hi About setting the path in debian.
    The file bashrc mentions the use off bash_aliases and that bashrc will check its existence.
    That menas that that proposed bash_profile is the same as bash_aliases. i changed it and it works. further i did change the following order of the path as suggested on the install page. that worked to.
    so about chaging bashrc, its only debian using a different name after all.
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
  • edited September 19
    JianYang wrote: »
    I think I remember there is an incompatible program called z80asm in Debian. Try
    type z80asm
    
    and look if it's a z80asm in your z88dk installation. You might have to remove the conflicting assembler.
    Hi

    my over-the-adge attempt of a bash file 'check_z80asm.sh'
    #!/bin/sh
    #
    if command -v z80asm &> /dev/null
    then
        echo "z80asm found"
        command -v z80asm
        exit
    fi
    

    now i wonder if this is usable in the installing eg by calling it before z80asm is assembled, and then it should be able to check thats NOT the needed cq to installed variation (not version) of z80asm.
    just an attempt and not complete for its purpose.
    cheers

    ps
    is 'type' better ?
    Post edited by Crisis on
    my old website http://home.hccnet.nl/c.born/ has changed to http://www.cborn.nl/zxfiles/ so just click it and select a file
Sign In or Register to comment.