News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

Using Arkos Tracker 2 together with CPCTelera

Started by Norman, 13:14, 10 October 20

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Norman

Hello,

currently I'm trying to use AT2 to create music for my game written in C with CPCTelera, which only supports AT1 songs, so manual work is required to use AT2 songs with CPCTelera.
What I found is this topic: cpctelera arkos 2 conversion problem. However, I can't get the examples provided there to work.

Can anyone tell me which export settings to use in AT2 and how to integrate the exported song and AT2 player routines into my C project? That'd be really helpful.

Cheers

Targhan

Even though I develop AT2, I don't know anything about CPC Telera. I'm sure @Arnaud or @teopl or someone else will be able to help you :).
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

Arnaud

#2
Hi,
i assume you have succesfully converted you song/fx with this tutorial an have a single assembly file :
https://www.julien-nevo.com/arkostracker/index.php/using-a-song-in-production-using-rasm/

After you have to make some assembly functions and song visible by C compiler, simply add these line :

GAMEMUSIC_START:
_GAMEMUSIC_START::   'LINE ADDED
...
SOUNDEFFECTS_SOUNDEFFECTS:
_SOUNDEFFECTS_SOUNDEFFECTS::  'LINE ADDED
...
_PLY_AKM_INITSOUNDEFFECTS::
PLY_AKM_INITSOUNDEFFECTS:   'LINE ADDED
...
DO SAME FOR OTHERS LINES


In the Arkos2_api.zip you will find :

  • ArkosPlayer2.h : API of AT2
  • sound.h and sound.c : my own implementation to use sound and fx

It should be easy adapt this code to your own music/fx.





Norman

Thanks for your reply, Arnaud, at least I managed to get the program to compile with all player routines integrated.
I followed these tutorials:And adapted the source files you provided to my song, but whenever I call PlaySound(), it crashes:

#include <cpctelera.h>
#include <sound.h>

void main() {
   static u8 pattern = 0;
   cpct_disableFirmware();
   cpct_setVideoMode(1);
   InitSound();

   while (1) {
      cpct_drawSolidBox((u8*)(0xC000), pattern++, 8, 8);
      cpct_waitVSYNC();
      PlaySound();
   }
}

I also tried calling it at interrupt instead, but that doesn't work either. I've attached a "working" CPCTelera project if you'd kindly want to have a look at it.
Cheers

Arnaud

Ok i forgot something important, read arguments from C calling function because they use __z88dk_callee:

Simply add these lines after these two functions in the asm code.

_PLY_AKM_PlaySoundEffect::
        ;Gets the address to the sound effect.
pop  af          ;; AF = Return address
pop  hl          ;; H Rubbish / L = Sound effect
pop  bc          ;; B = Volume / C = Channel
push af          ;; Save back return address in the stack to fullfill __z88dk_callee convention
ld   a, l        ;; A = Subsong index

_PLY_AKM_Init::
pop  af          ;; AF = Return address
pop  hl          ;; HL = Music address
pop  bc          ;; Rubbish  / C  = Subsong index (>=0)
push af          ;; Save back return address in the stack to fullfill __z88dk_callee convention
ld   a, c        ;; A = Subsong index



Norman

#5
Thanks, that was missing, works like a charm now  :)

As a summary for anbody who needs this:

       
  • Create a CPCTelera project, Arnauds zip above is a good starting point so you're able to call the "Arkos Player" functions from C
  • Create one big assembly file (At2Files.asm) containing player code, SFX code and data using this guide: Using a song in production, using any assembler
  • Add the code in the post above to the assembly file
  • Profit  ;)
I extended my little build script to do that (it's run from the project folder and assumes the game has the same name as the folder):

#!/bin/sh
export CPCT_PATH=/path-to/cpctelera-git/cpctelera
rasm="/path-to/rasm"
at2disark="/path-to/Arkos Tracker 2/tools/Disark"
name=$(basename $(pwd))

# Prepare Arkos Tracker 2 song.
${rasm} assets/CompileAT2Files.asm assets/UniversalAt2Files -s -sl -sq
"${at2disark}" assets/UniversalAt2Files.bin src/At2Files.asm --symbolFile assets/UniversalAt2Files.sym --sourceProfile sdcc
# Add assembly code to be conformant with __z88dk_callee
sed -i -e 's/_PLY_AKM_INIT::/_PLY_AKM_INIT::\npop af\npop hl\npop bc\npush af\nld a, c/g' -e 's/_PLY_AKM_PlaySoundEffect::/_PLY_AKM_PlaySoundEffect::\npop af\npop hl\npop bc\npush af\nld a,l\n/g' src/At2Files.asm

if [ "$1" = "clean" ]; then
  make clean
fi
if make; then
   # Run disk image with mame or any other emulator.
   mame cpc6128 -flop1 ${name}.dsk -skip_gameinfo -noconfirm_quit -autoboot_delay 1 -autoboot_command "run\"${name}\n"
fi



Cheers
Edit: @Arnaud, one more question. How can I play music in an interrupt if my game loop takes a varying amount of time? If I just put the PlaySound() function in the interrupt, it plays way too fast.

Arnaud

Quote from: Norman on 14:58, 11 October 20Edit: @Arnaud, one more question. How can I play music in an interrupt if my game loop takes a varying amount of time? If I just put the PlaySound() function in the interrupt, it plays way too fast.

Your song runs à 50hz and the interrupt works at 300hz just make a static counter and play it once every 6 ticks.

Here my interruption function :

void sInterruptHandler(void) {
    static u8 sInterrupt = 0;

    if (sInterrupt == 0)    {
        cpct_scanKeyboard_i();
    }
    else if (sInterrupt == 1)    {
        PlaySound();
    }
    else if
...

    if (++sInterrupt == 6)    {
        sInterrupt = 0;
    }
}

Norman

 :picard: I should've known that. Thanks!

Norman

Sorry for bothering you yet again, but I'm encountering another problem. When I play music in the interrupt and don't do much otherwise (e.g. main menu) everything seems fine. However, as soon as I run the game loop and play sound in the interrupt, it crashes the program (undefined/random behavior). I'm not doing anything fancy in my code, so it seems like calling CPU-heavy CPCTelera functions, like decompressing data, rendering a full tilemap etc. causes the crashes.

How could I go about debugging this?

Cheers

Targhan

Two ideas:
- Are you sure you save ALL the registers in your interruption handler (including AF', IX, IY and all the other secondary registers). Don't forget AF.
- Maybe Cpctelera performs some bank switching, making the music player and/or data not available?
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

Norman

Thanks for the suggestions Targhan, I'm going to have a closer look at these.
At worst (as in: time runs out), I'll only play music in the menu  :)
Cheers

SpDizzy

As @Targhan said, those crashes and undefined/random behavior has to be with not saving registers before calling the player, so they are destroyed during this call. It's not CPCTelera related, but assembly call related. Make sure to save your registers (pushing to the stack) before call _PLY_AKM_PLAY and revert back (poping them) for perfect working during game loop.
Quick and dirty implementation (as inline asm on your sound.c file):
void PlaySound()
{
   ActivateMusic();
   //ActivateSFX();
__asm
     ;; Save registers before calling PLY_AKM_PLAY     
      exx
      ex af',af
      push af
      push bc
      push de
      push hl

      call _PLY_AKM_PLAY

    ;; Revert back registers after calling
      pop hl
      pop de
      pop bc
      pop af
      ex af',af
      exx
__endasm;

   //PLY_AKM_PLAY();
}

Good luck with the contest!!

Norman

Beautiful, that did the trick. Thanks @SpDizzy for helping out an assembly noob!
Thanks, I'll probably need that luck because I'm getting anxious to fall behind on "schedule"  :)

Cheers

SpDizzy

Glad to help, but ¡beware! I'm just an assembly noob too! :)There's still time until November,3. Just do your best

Targhan

Watch out, this looks dangerous to me. I strongly suggest that, if the player is called in your handler interruption, you save ALL the registers. The code @SpDizzy kindly provided only saves the auxiliary registers. Please also save the primary registers and IX/IY too! Else, hell awaits you :).

If this were your subroutine, you would know exactly what registers you would modify (sometimes only AF, and HL for example). But when calling a third party code (such as a music player), don't take any risk and save everything. This will save you hours of debugging.
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

SpDizzy

@Targhan is true, mine was an incomplete solution working under this concrete example, but a risky approach to fit any case. Better save all registers to be safe

Norman

#16
Thanks @Targhan, added that now. So far, I didn't notice anything wrong, but it would've bitten me some time, so yeah  :)
That's another 58 cycles...  ;)

Cheers

Edit: For comprehension of the code, why are the registers first swapped with the shadow registers (2nd set) and then pushed onto the stack? (with exx and ex af,af', then push...)

Targhan

QuoteFor comprehension of the code, why are the registers first swapped with the shadow registers

The original code only stored the auxiliary registers. You cannot PUSH DE', etc. directly, so you have to swap, store DE. If you save all the registers, no need to double-swap by the way. There is no "real" primary and auxiliary registers.
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

SpDizzy

QuoteEdit: For comprehension of the code, why are the registers first swapped with the shadow registers (2nd set) and then pushed onto the stack?

Well... since there are no opcodes for pushing or popping auxiliary/shadow registers, that's the way to go. Swapping them with primary ones, do the job with those, and swap again. But remember to save also primary and index ones, and in doubt, always thrust @Targhan . God bless Arkos Tracker!

Arnaud

#19
@Norman If you don't use alternate registers, CPCTelera cpct_setInterruptHandler will save and restore all default registers.

Have you find your bug ?

Norman

So CPCTelera saves and restores all default (i.e. primary) registers when the interrupt is called? That would explain why it did work even without saving ix and iy manually (as @Targhan suggested)

As mentioned above, the crashes were caused by not saving the auxiliary registers before calling the player in the interrupt. Using the code @SpDizzy provided I could solve it.


Cheers

Targhan

Uh ok, good to know. Couldn't have guessed, as I don't know Cpctelera :).
Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

ronaldo

Quote from: Targhan on 16:05, 17 October 20
Uh ok, good to know. Couldn't have guessed, as I don't know Cpctelera :) .
Just for the record, @Targhan , CPCtelera is not a maker but a framework. It includes compilers, tools and a low-level library, but nothing included is mandatory to be used. You can use everything in there up to your needs. You can just start a blank project and write assembly, machine code or C from scratch. So, effectively, CPCtelera is not an entity that "does" things. CPCtelera does nothing. When developers use the low-level library included, there are a bunch a functions and those have their own inner workings, as expected from every function. Then, it is appropriate to say that "function X from CPCtelera does Y", but not CPCtelera.

So, integrating any Amstrad-related source code into CPCtelera (such as Arkos 2) is the same as integrating it in any Amstrad production using any other tool. CPCtelera includes SDCC and ASZ80 as compiler and assembler. If you are able to convert code to ASZ80, you can then include it as source and assemble it along with your project files with ASZ80. If not, you can always include it in binary form.

So, as in any other developer adventure, CPCtelera makes no difference. It is just a bunch of development tools with some level of coordination between then that you can freely use up to your needs.

And this is why people can integrate your Arkos2 sources and/or binaries in CPCtelera, as they can also do it with any other source/binary.

Hope this helps better understanding it :)

Targhan

Targhan/Arkos

Arkos Tracker 2.0.1 now released! - Follow the news on Twitter!
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.1 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

Powered by SMFPacks Menu Editor Mod