How to plan a game?

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
amateus
Member
Posts: 42
Joined: Fri Nov 15, 2019 9:13 am

How to plan a game?

Post by amateus »

Hi guys,

As you've probably saw, I've been doing some tests with SP1, trying to understand the library, trying to get a grasp of the intention behind the methods and tackling some interesting features that I've saw here and there mentioned, but didn't have any example.
On top of this I'm also learning C, which was not something I knew, and after being spoiled with higher level languages that take care of most of the memory stuff for you, it's been an interesting exercise.

That said, I never did a full game for spectrum, and initial planning and memory management worries me. So I've search the forums for tips or even examples on this, without much success.

So I would like to pick the brain of the more experienced out there, if you're up to it. I don't really understand how people breakdown their game in memory, and handle this plan with z88dk and SP1.

From the top of my list:

- banks switch (SP1 and stacks needs to be moved from default positions I think)
- level loading?
- game code vs stack vs sprites
- audio (music and fx)
- using memory bellow 32768 have level data?

Say I want to make a game, for 128k that uses the multiple banks for level + audio. Or a 48k game that loads each level. How do I plane the code management? I know roughly the spectrum memory map but to know precisely how much my code occupies, or how much my sprites occupies or move things around in memory to be allowed to switch banks, for example, this is something I'm still missing.

Thank you,
António
Timmy
Well known member
Posts: 179
Joined: Sat Mar 10, 2012 4:18 pm

Re: How to plan a game?

Post by Timmy »

Well, making games on the Spectrum (with or without sp1) is always a (jigsaw) puzzle of how to put everything into 48K (or more). It's not unusual to have games that use almost every byte of memory. Most of my games were.

One of the biggest constraints is the Spectrum's memory model. It's really complicated.
It will take me at least 3 days to explain that. In a few days I''ll have more time here (I hope) so I'll have a bit more time to talk about it then.

In the meantime, to quickly answer a bit of your questions (explanations after the memory model discussions):

- sp1 is so delicate you shouldn't move it, but you can move stack and/or interrupts.
- level loading? you store it in memory in advance and then uncompress (or just copy) it when you need it.
- "game code vs stack vs sprites" not sure of the question, but you need all of this fit into 48k.
- audio? this depends on which kind of music you get/create. for example, i got music from a musician, and i put them somewhere. (more explanations after memory model discussion, again).
- memory below 32768? z88dk can compile below 32768 if you set a parameter for it. i think it was called "-zorg=30000", for example.

I'd also suggest you to use an emulator that shows exactly what data is stored in which memory. I personally use ZXSpin for that, but it's not available on Linux.

More information later (but probaly not today).
amateus
Member
Posts: 42
Joined: Fri Nov 15, 2019 9:13 am

Re: How to plan a game?

Post by amateus »

Hi Timmy,

Thank you for your input. Let me try to clarify it: I know the spectrum memory layout, my difficulty is to understand how to plan a game from scratch, saying, game cycle takes this space, sprites takes this space, etc

- level loading: in a 48k, many games load each level from tape. My question here is how do we handle this? I mean, I have a func in the code that loads the game sprites. How do I plan this if I have to load each sprites every time a player ends the level? How do I structure the program in order to replace parts of it by a tape loading.

- game code vs stack vs sprites vs audio: how do I define where sprites or tiles will be saved (in order to replace them by a load operation if needed), where game code ends, etc, in sum, what are the best practices for defining all of this?

- contend memory- I know we can define z88dk to start bellow, but for example, that memory is slower, so how do I position game cycle above and level data bellow?

I'm more interested in the theory and not in the how do I do this, to be fair. That will come later I guess.

So, a practical example:

- I want to make a game for 48k. I know I will not be able to load the full game, so multi-loading is needed. I know I will have to load sprites and tiles, a simple music, game logic. When a player ends a level it will load the next one, replacing gfx and/or code (if the level has specific, different behavior).
How do I structure my program, in terms of memory, to do this. How do I know or how do I define that the next level needs to be loaded to that particular memory address that will replace gfx and code?

- I want to make a game for 128k. Besides the problems in the previous example, I also need to understand how do bank switching helps me in this and how to structure my program in order to use it. Additional levels gfx and/or music may be loaded from banks, etc

Hope I was able to explain better my doubts. Again, I'm more interested in the theory behind this than in the actual how-to (for now).

Thank you.
Timmy
Well known member
Posts: 179
Joined: Sat Mar 10, 2012 4:18 pm

Re: How to plan a game?

Post by Timmy »

Yes, there is a lot to talk about even if we are only talking about theory.

Well, actually it's not that much but my English is not very good and I write really slow, so even for a simple subject like contended memory, it would take me an hour to write this post.

===============

So the Spectrum has contended memory. This means that code in some places is slower than others.

On the Spectrum 48K, only the memory block 16384-32767 is contended.

Put sp1 and music in uncontended memory. Interrupts can stay where it is, on a 48K game.

But contended memory is not very slow. You can still do many things in it. I put many code in 27000-32767 too. And level data, and fonts, and sprite data. Not really a good idea but my games are fast enough. I do sound effects in contended memory too, because it only calls ROM beep routine and ROM is uncontended.

Music on the Spectrum is a strange thing. Most of the time it is one separate machine code blob. You put it in a free space in memory, and then call it when you need it. I usually load the music code separately, too. And I put it just between the stack and the interrupt routine.

If every level of your game is completely different, then make separate C programs. :P

===============

Next time I will talk about the 128K and the extra challenges.
amateus
Member
Posts: 42
Joined: Fri Nov 15, 2019 9:13 am

Re: How to plan a game?

Post by amateus »

Thank for the insight Timmy.

I wonder how do you position sprites in a specific memory address? Until now, sprites, level data, code etc were all in a big chunk and z88dk will take care of were will it put them.

How do you force specific code, being asm sprites file or a specific array with level data to be loaded in memory address X?

By compiling them separately?

For example, defining an array as extern and then actually declaring and assigning that array in a different project so it can be loaded?

Thanks.
Timmy
Well known member
Posts: 179
Joined: Sat Mar 10, 2012 4:18 pm

Re: How to plan a game?

Post by Timmy »

amateus wrote: Sat Jan 23, 2021 10:09 am I wonder how do you position sprites in a specific memory address? Until now, sprites, level data, code etc were all in a big chunk and z88dk will take care of were will it put them.
Yes, z88dk takes care where everything will be.

But does it really matter where everything is precisely? If it's close to where I want the data to be, then I don't care.

What helps a bit is that z88dk compiles sequentially. If your data is at the top of your code, then it will be stored at the earlier locations. It doesn't help a lot though.
How do you force specific code, being asm sprites file or a specific array with level data to be loaded in memory address X?
Have you looked at your loading routines yet? Your loading routine needs two things, the length of the data, and where it should start loading.
You can specify where you load your data, so just tell your loading routine to start loading there instead.
For example, defining an array as extern and then actually declaring and assigning that array in a different project so it can be loaded?
You can use the address of the extern array to start loading the data from there.

For my music code, that is compiled separately, I load it in my basic loader with 'LOAD""CODE 40000' (but there is no problem to load in inside your z88dk program).
Then I just call the music code from inside z88dk with a 'call 40000' inline assembly code, for example.
Timmy
Well known member
Posts: 179
Joined: Sat Mar 10, 2012 4:18 pm

Re: How to plan a game?

Post by Timmy »

Oh, I think I forgot to write about 128k programming. So here's a bit of information (without going in all the details, you can always ask later.)

The 128k is a little bit different.

Most importantly, bank switching. On the 128k, memory addresses 49152-65535 can change depending on which bank you selected.
Bank selection can be done with OUT (32765), a, and if you are doing this in BASIC, you should also do POKE 23388, a.
Memory banks 1,3,5,7 are contended.

Most of sp1 can be left at above 49152. However, the interrupt table and the interrupt routine should be moved to somewhere between 32768-49151. (and the stack should be moved to somewhere safe too.)

I'd put level data in the banks that are not used, and when you switch level, you only need to switch bank, then copy(!) the level data into your main memory. Then switch bank so that sp1 is back in place, and pretend it's just another room/level.

And that's the 128k. If I ever want to do a 3rd day of this thread, it would be about the +2A/+3. They are different from the 128k, obviously. :P

Someone else wrote about it better, link: https://worldofspectrum.org/forums/disc ... ent_837801
jorgegv
Member
Posts: 13
Joined: Wed Nov 18, 2020 5:08 pm

Re: How to plan a game?

Post by jorgegv »

Thanks Timmy for this info, this is very useful for my Adventure engine also!
amateus
Member
Posts: 42
Joined: Fri Nov 15, 2019 9:13 am

Re: How to plan a game?

Post by amateus »

Hi,

Very interesting Timmy thank you.

So basically for 128k we have to have variables or a buffer of assets for level 1, then on level 2, switch to the bank we want, copy the assets from 49152-65535 to that buffer and restore basic memory and then you'll have the new assets.

When you say copy, is this memory copy as in copy memory block to variable my_sprite_gfx[]?

Thanks
Timmy
Well known member
Posts: 179
Joined: Sat Mar 10, 2012 4:18 pm

Re: How to plan a game?

Post by Timmy »

I've decided that I'm too busy right now writing a lot of things, so I'm just posting two links here for the +2A/+3

https://worldofspectrum.org/faq/referen ... erence.htm
* Bank 4,5,6,7 are contended for the other 128k machines

https://worldofspectrum.org/forums/disc ... ent_799397
* Don't use Bank 7.

Note: I post the second link here, but I would suggest not to use assembly to load files, but use a basic loader. A basic loader is easier to test than assembly (but maybe that doesn't matter).
User avatar
dom
Well known member
Posts: 1305
Joined: Sun Jul 15, 2007 10:01 pm

Re: How to plan a game?

Post by dom »

Timmy wrote: Fri Feb 12, 2021 5:44 pmNote: I post the second link here, but I would suggest not to use assembly to load files, but use a basic loader. A basic loader is easier to test than assembly (but maybe that doesn't matter).
If I remember correctly, I don't think a BASIC loader works on a +3 - it basically ignores the bank set in BANKM.

Regardless, classic/appmake create loaders that load into the banks either by tape or by +3, no options needed for .TAP, for +3, add -subtype=plus3 and a .DSK image will pop out: https://github.com/z88dk/z88dk/wiki/Pla ... generation
Timmy
Well known member
Posts: 179
Joined: Sat Mar 10, 2012 4:18 pm

Re: How to plan a game?

Post by Timmy »

amateus wrote: Wed Feb 10, 2021 10:16 pm So basically for 128k we have to have variables or a buffer of assets for level 1, then on level 2, switch to the bank we want, copy the assets from 49152-65535 to that buffer and restore basic memory and then you'll have the new assets.
Correct. The assets define the level.
When you say copy, is this memory copy as in copy memory block to variable my_sprite_gfx[]?
Here's a part from a game that I'm currently finishing (not spectrum and only 32k total):

Code: Select all

// These are level data, 1k each, but I show the first few lines only, because otherwise the code is too long...
// Pretend that they can be stored in one or more banks.

const uchar room1[] = ""
"################################"
"#          ---                 #"
"# ##########-################# #";

const uchar room2[] = ""
"################################"
"##                      #@ 2  @#"
"## !!  #! #--!   #   !  ###  ###";

const uchar room3[] = "" 
"################################"
"#       #!!!@#     # @# @<#@@@!#"
"# #  !# #@@@@# #@< # !@|<<#  !<#";

// etc.

// memory to keep the current room
uchar room [32*32];

const int* rooms[15] = { room1, room2, room3, room4, room5, room6, room7, room8, room9, room10, room11, room12, room13, room14, room15 };
int temprr1; // I used a global variable because I don't know how to access a local variable in inline assembly. :P

void load_level (uchar level)
// Copy the data from somewhere to the variable "room"
// I used the old LDIR here, but it's also possible to use "memcpy()" (not tested).
// If you want to copy data from different banks, you can do bank switching immediately before #asm and immediately after #endasm.
{
	temprr1 = rooms[level-1];
	
	#asm
	ld hl, (_temprr1)
	ld de, _room
	ld bc, 32*32
	ldir
	#endasm
}
Post Reply