How to play AY music with z88dk?

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I think I calculate about 4560 bytes required from the add_sprites_for_all_levels() function in defines.c. So there are probably still problems with the compile that runs with stack size 256 bytes.

There are a few more things you can do. The clothes sprites, if they are always at the bottom priority and displayed on blank tiles, could be changed to 1-byte LOAD sprites. This will reduce the size of the associated graphics (and make them faster to draw).

A much bigger impact is if you always draw sprites at multiples of 4 pixels (or other multiples) then you can reclaim some of the memory used by sp1 for rotation tables. sp1's rotation tables at 0xf200-0xffff are table lookups for 1,2,3,4,5,6,7 pixel rotations and if you only use 4 in that list, you can take back the memory for the others. The block memory allocator can be given this memory quite easily.

Lastly you could consider printing background tiles for background elements like the clothes instead of using sprites.
Timmy
Well known member
Posts: 177
Joined: Sat Mar 10, 2012 4:18 pm

Post by Timmy »

Just in case, if you do want to move the ay-related stuff into a 128k bank, as alvin said, you'd have lots of stuff to do too. You'd have to make sure that your interrupt table, which is likely to be at d000 - d100 for now, to move to a place between 32768 - 49151 (decimal), to ensure non-snowing on the real 128k+ machines.

What I usually do when I make a game with z88dk, is to reuse the created sprites to save memory. You can reused created sprites by changing its pointer, as long as they have the same dimensions. For example, if all your sprites are of 5x3 size, then you can swap the one with the other without allocating more memory for them. Then you create, during initialisation, all the sprites you need and then reuse them. When not in use, you just hide them.

For example, if your main screen (with the clotheslines) have a max of 8 sprites, then create those at the start of your game, and reuse these sprites in later levels as spiders or brooms. This way it saves you a lot of allocating memory. The dogs' eyes, the moving clothing and other small stuff that don't need to be sprites, you can use tiles for those.
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

It was really useful your info! I have been applying and I'm sure I won't get into a deadlock anymore.
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Jordi, I have updated the vt_play() function in https://github.com/stefanbylund/vt_sound so it's now just a call to VT_PLAY to make it more suitable for calling from a custom interrupt service routine. Thanks Alvin for pointing this out :)
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

Stefan123 wrote:Jordi, I have updated the vt_play() function in https://github.com/stefanbylund/vt_sound so it's now just a call to VT_PLAY to make it more suitable for calling from a custom interrupt service routine. Thanks Alvin for pointing this out :)
Thank you very much.

I'm wondering (remember, I'm a noob in zx spectrum and z88dk) how easy could be to move my .AY music and the player to a different 128k bank ( https://github.com/jsmolina/speccy-misifu ) .

Correct me and add what should be the next step:

1) Move ay.lst and int.c (ISR) to a zproject-07.lst, and compile their own:

Code: Select all

zcc +zx -vn -m4 -clib=sdcc_iy --codesegBANK07 -L$(VT_SOUND_LIB_SDCC_IY) -lvt_sound --constsegBANK_07 @zproject-07.lst -pragma-include:zpragma.inc -startup=31
2) Edit of course misify_ay.asm
-SECTION rodata_user
+SECTION BANK_07
3) Make the linker to join both files, how? custom loader? And the ISR? Should I have to change its address?
Compiling there https://github.com/jsmolina/speccy-misi ... akefile#L8



Do the all-ram mode might avoid pagination?
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

btw, if anyone wants to contribute or do some commit to my code is more than welcome!
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Placing the PT3 sound module in a bank that is switched in to the top 16K would not be so difficult. Let's say that sound module A is placed in bank N then you only need to switch in bank N when calling vt_init() once and in your ISR when calling vt_play(). Just remember to put your code, ISR and IM2 interrupt vector table below address 0xC000.

Placing the VT player in a bank (in addition to the sound module) is also possible but a little bit more complicated. In this case when calling vt_play() in your ISR, you need to have the VT player and the sound module placed in the same bank when switching it in to the top 16K. I'm doing something similar on the Spectrum Next but I'm not sure about the details of doing it on the Spectrum 128. The memory banking model is a lot more flexible on the Spectrum Next :) Maybe the all-ram mode can make it easier. I'm sure Alvin can give you better guidance for this use case.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

The all-ram mode is only available on the +3 which is probably the least common spectrum model so it's better not to use that.

Your program's 64k space consists of the bottom 16k rom, then 16k banks 5/2/0. This is the memory configuration spectrum basic runs out of and your program loads into. The 128k spectrums have eight 16k banks called bank 0/1/2/3/4/5/6/7, any of which can be placed in the top 16k replacing bank 0 which is normally there. Unfortunately there are two contention patterns in the 128k models - the original models contend on odd banks 1,3,5,7 and the +2/+3 contend on banks 4,5,6,7. Why does that matter? If the "I" register (the z80's interrupt table pointer) points into the same contended bank as it is executing code from, due to a hw bug on machines other than the +3, the machine can crash. So we must avoid that situation.

Having said all that, the simplest way to put the ay music into another bank is to keep everything almost the same. Your im2 table is still at 0xd000, "I" still points at 0xd0. Notice that this is in the top 16k where you will be paging in a possibly contended bank. To avoid executing code while "I" is pointing up there, your interrupt routine can change "I" before calling the music code and restore it on return before re-enabling interrupts. Because you'll be paging the top 16k out for the music code, you can't have your stack in the top 16k. In the default mem config of sp1, that's where it normally is. So you can either move your stack elsewhere permanently for the entire program or you can get your interrupt routine to move it temporarily to the top of the 16k bank your music code is in and then restore before returning.

I think doing the above things has the least impact on how your program works now.

About the output from z88dk, you can build an sna that will automatically contain everything. Or you can stay with tap and you will get a separate binary out of z88dk that contains the data from other banks than 5/2/0 (remember 5/2/0 is part of the 64k space the main part of your program compiles into). You will need to write a short basic loader that will load your screen$, the main code and the extra bank code before starting the program. This is fairly easy to do in an emulator or using a text to tap utility like bas2tap.

I'll have a look at your project in a bit.
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

Stefan123 wrote:Jordi, I have updated the vt_play() function in https://github.com/stefanbylund/vt_sound so it's now just a call to VT_PLAY to make it more suitable for calling from a custom interrupt service routine. Thanks Alvin for pointing this out :)
Hi! I tried to use the new code and it started playing the music, but it crashed immediately.

It's just calling vt_play(); from here, right? https://github.com/jsmolina/speccy-misi ... /int.c#L23
Stefan123
Member
Posts: 85
Joined: Fri Oct 21, 2016 7:57 am

Post by Stefan123 »

Try to replace IM2_DEFINE_ISR_8080(isr) with IM2_DEFINE_ISR(isr). IM2_DEFINE_ISR_8080(isr) only preserves a subset of the registers (AF, BC, DE, HL) while IM2_DEFINE_ISR(isr) preserves all registers (AF, BC, DE, HL, AF', BC', DE', HL', IX, IY), which is needed when calling the VT player.

Code: Select all

IM2_DEFINE_ISR(isr)
{
   // update the clock
   ++tick;

   if (row1_moving != NONE)
   {
      --row1_moving;
   }

   vt_play();
}
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

Stefan123 wrote:Try to replace IM2_DEFINE_ISR_8080(isr) with IM2_DEFINE_ISR(isr). IM2_DEFINE_ISR_8080(isr) only preserves a subset of the registers (AF, BC, DE, HL) while IM2_DEFINE_ISR(isr) preserves all registers (AF, BC, DE, HL, AF', BC', DE', HL', IX, IY), which is needed when calling the VT player.

Code: Select all

IM2_DEFINE_ISR(isr)
{
   // update the clock
   ++tick;

   if (row1_moving != NONE)
   {
      --row1_moving;
   }

   vt_play();
}
Oh, thanks. Really appreciate your help.
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

alvin wrote:The all-ram mode is only available on the +3 which is probably the least common spectrum model so it's better not to use that.

Your program's 64k space consists of the bottom 16k rom, then 16k banks 5/2/0. This is the memory configuration spectrum basic runs out of and your program loads into. The 128k spectrums have eight 16k banks called bank 0/1/2/3/4/5/6/7, any of which can be placed in the top 16k replacing bank 0 which is normally there. Unfortunately there are two contention patterns in the 128k models - the original models contend on odd banks 1,3,5,7 and the +2/+3 contend on banks 4,5,6,7. Why does that matter? If the "I" register (the z80's interrupt table pointer) points into the same contended bank as it is executing code from, due to a hw bug on machines other than the +3, the machine can crash. So we must avoid that situation.

Having said all that, the simplest way to put the ay music into another bank is to keep everything almost the same. Your im2 table is still at 0xd000, "I" still points at 0xd0. Notice that this is in the top 16k where you will be paging in a possibly contended bank. To avoid executing code while "I" is pointing up there, your interrupt routine can change "I" before calling the music code and restore it on return before re-enabling interrupts. Because you'll be paging the top 16k out for the music code, you can't have your stack in the top 16k. In the default mem config of sp1, that's where it normally is. So you can either move your stack elsewhere permanently for the entire program or you can get your interrupt routine to move it temporarily to the top of the 16k bank your music code is in and then restore before returning.

I think doing the above things has the least impact on how your program works now.

About the output from z88dk, you can build an sna that will automatically contain everything. Or you can stay with tap and you will get a separate binary out of z88dk that contains the data from other banks than 5/2/0 (remember 5/2/0 is part of the 64k space the main part of your program compiles into). You will need to write a short basic loader that will load your screen$, the main code and the extra bank code before starting the program. This is fairly easy to do in an emulator or using a text to tap utility like bas2tap.

I'll have a look at your project in a bit.
I think I understood the concept about the banks, and memory contention (I read this spanish post that was useful https://magazinezx.speccy.org/17/128k-mode.html )

But, I tried all the ways to change the stack place, but I probably miss more knowledge, as always I get a hang.

Let's be *explicit* on what I should set to use AY music on another bank

1) Stack should be moved to leave space for 'bank window', where?

Code: Select all

REGISTER_SP            = 0xd000
Is this a right place?

Code: Select all

REGISTER_SP            = 0x32768
2) Register IM2 in another place.

Code: Select all

   im2_init((void *)0xd000); // CRT_ORG = 25124
   memset((void *)0xd000, 0xd1, 257);

   z80_bpoke(0xd1d1, 0xc3);
   z80_wpoke(0xd1d2, (unsigned int)isr);

   intrinsic_ei();

Code: Select all

   im2_init((void *)0x32768); 
   memset((void *)0x32768, 0xd1, 257);  

   // and here???
   z80_bpoke(0xd1d1, 0xc3);
   z80_wpoke(0xd1d2, (unsigned int)isr);

   intrinsic_ei();
What do you think guys? Sorry, I feel ignorant.
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

When IM2 is in right place I guess I will be able to
1) set pt3 file in a bank
2) switch bank with port 0x7FFD before calling vt player (and before vt_init call)

right?' Thanks!
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I've just made the changes on your current master branch and will describe them here after I've sent a pull request.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I'm having trouble applying the patch to your last main commit and I'm not sure if it has to do with the windows version of the patch utility. I'll post the patch and the current source in this zip:

https://drive.google.com/file/d/1NaCxq- ... sp=sharing
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Anyway the changes:

1. vt_sound.lib was modified by zobjcopy to change the code and data sections it uses to BANK_6. There is a readme.txt in the ay directory that shows how it was invoked. It was also used to make some symbols PUBLIC that weren't PUBLIC already.

2. misafu_ay.asm's section changed to BANK_6 for the music and ay_music.asm becomes the entry point for all vt_* functions. The functions here take care of banking before calling the vt code.

3. setup_init() and the isr have been rewritten in asm in file int.asm. All functions that bank, including the isr, do so by making the "I" register point at uncontended memory and by moving the stack to the top of memory (0). When the music bank is paged in, code running there will use memory at the end of the bank.

I think that's about it. A few pragmas were changed including moving the ORG address up to 24500 to give more space to the basic loader. It can probably be made lower.

The basic loader is in the text file "loader.bas" and it loads the screen, bank 6 and the main code. It was converted to a tap file "loader.tap" using bas2tap.

I did not modify your makefile; instead there is a windows batch file that builds the tap in "makemisifu.bat" with which you can update the makefile. The zcc compile line turns up the optimization which will slow down your compiles but lead to smaller and faster code. You can add "--opt-code-size" to that as well. There is no create-app in the zcc invoke so only raw binaries will be produced. The following appmake invokes create tap files out of the binaries and then all the tap files are concatenated together to make the final tap.

The dir in the batch file lists the size of the binaries so you can see how much memory is available. The last build I did showed this:

Code: Select all

2018-11-25  12:24 PM             4,556 misifu_BANK_6.bin
2018-11-25  12:24 PM            20,262 misifu_CODE.bin
Bank 6 is 16k and holds all the music related things. The stack will be at the top of that 16k will bank 6 code executes. There is plenty of space there.

The main binary is 20262 bytes which is orged at 24500. So it extends up to 44762 which leaves 0xd000-44762 = 8486 bytes but this space will also contain the stack and the heap.
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

That was GREAT! I took your diff and applied in the code, and it works perfectly :)
https://github.com/jsmolina/speccy-misifu/pull/47

I was also saving more space as we could progress with more levels.

I would like to help you on whatever is related with z88dk, so if in any moment I could help with improving the SP1 documentation or anything you need.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I'm glad it worked out.

SP1 is something I want to come back to to improve, mainly because the zx next makes some of the removed display modes relevant again. At one time it supported the timex hi colour mode and timex hi res mode (actually I think the hi res mode is still there) and at the same time I'd like to improve on some functionality.

There is a big backlog in needed documentation so any help there is great. Derek Fountain started a beginner's guide including an introduction to sp1 that he updates from time to time. You can see it here:

https://github.com/z88dk/z88dk/blob/mas ... edGuide.md
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

alvin wrote:I'm glad it worked out.

SP1 is something I want to come back to to improve, mainly because the zx next makes some of the removed display modes relevant again. At one time it supported the timex hi colour mode and timex hi res mode (actually I think the hi res mode is still there) and at the same time I'd like to improve on some functionality.

There is a big backlog in needed documentation so any help there is great. Derek Fountain started a beginner's guide including an introduction to sp1 that he updates from time to time. You can see it here:

https://github.com/z88dk/z88dk/blob/mas ... edGuide.md
I'm now starting as I promised you my own 'crash course tutorial', that might help understand from a different point of view.
I will start in a different repo: https://github.com/jsmolina/z88dk-tutorial-sp1

When I have something more complete, I might do a Pull request to the z88dk repo.

Regards
jordi
Member
Posts: 57
Joined: Sun Oct 28, 2018 3:35 pm

Post by jordi »

Mostly thanks to you guys the game became a reality!
https://github.com/jsmolina/speccy-misi ... misifu.tap
Post Reply