News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

Problems with loading new game level

Started by bart71, 13:43, 25 July 25

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

bart71

Sorry for sort of duplicating thread, now I see this board is probably better place to discuss my problem.
https://www.cpcwiki.eu/forum/software-related/issue-with-re-enabling-amsdos-when-arkos-player-used/

In short - I have problems with using amsdos firmware routines after using Arkos Tracker player (AKG) in my code (cpctelera + assembly).
If I disable playback in interrupt handler, I can load next game level without problems but with music playing loading does not work. Any hints/suggestions how to fix it? Maybe I need somehow to force firmware/amsdos reload?

my loader:

.area _LOADER (ABS)
;;_loadlevel::
.org 0xA640
_loadlevel::
  ;;call #0xbca7 ;; reset sound manager
 
  ld c,#0xff          ;; disable all roms
  ld hl, #_dummy         ;; execution address for program
  call #0xbd16  ;;mc_start_program    ;; start it
  _dummy:: nop
  call #0xbccb  ;;kl_rom_walk     ;; enable all roms
   
 ;;   ld c,#0x07
    ld hl,#_loader
    call #0xbd13 ;;mc_boot_program
    _loader:: ;; this is a loader which loads file to specific address and returns start addr in (HL)
    ;;call #0xbccb
    ld b, #10 ;;filename length
    ld hl,#filename ;;pointer to filename string
    ;;ld l,#0x24
    ;;ld h,#0x89
    ld de, #0x0 ;; 2k buffer - anything in this case
    call #0xbc77 ;;cas_in_open
    ;
    LD L, #0x00
    LD H, #0x01
    ;; read file
    call #0xbc83 ;;cas_in_direct
    ;; firmware function to close a file opened for reading
    call #0xbc7a ;;cas_in_close
    LD L, #0x62
    LD H, #0xa1
    ;;scf
    ret
    filename: .asciz "LEVEL3.BIN"

invoking load:
void LoadGame(void) {
//cpct_setVideoMode(0);
//cpct_removeInterruptHandler();
//PLAYER_ARKOS_STOP();
textX=32;textY=11;
Print(" LOADING \2");
cpct_removeInterruptHandler();
PLAYER_ARKOS_STOP();
delay(4);
//loadlevel();

interrupt handler (commenting out PlaySound() makes the program working correctly but no music plays)
void sInterruptHandler(void)
{
   static u8 sInterrupt = 0;
   // Play sound at 1/50th
   if (++sInterrupt == 6)
   {
      if (graj) PlaySound();
      // cpct_scanKeyboard_if();
      sInterrupt = 0;
   }
   cpct_scanKeyboard();
   if (intro)
   {
      if (cpct_isAnyKeyPressed()) {graj=1; Gra();}
   }
}

any help / hints appreciated

Jean-Marie

I'm not a CPCtelera specialist, but I'm not sure it's a good idea to call cpct_removeInterruptHandler() before loading the level. This function will turn off interrupts entirely by writing EI:RET at 38h.
And firmware functions would need the firmware interrupt to be present at 38h (jp 0B939h on a 464).
The way I see it, I would follow those steps :

1) call cpct_disableFirmware. It returns a Word containing a pointer to the current firmware ROM code. Save it!
2) Set a new interrupt handler to run your Interrupt Service Routine, the one calling the Music player (don't forget to use DI before and EI eventually).
3) Start the Music player. And the game.

Then, when the time has come to load a new level :
4) Stop the Music player (PLAYER_ARKOS_STOP())
5) call cpct_reenableFirmware with the previously saved Word as input parameter.
6) Open, Read, Close your level file.
7) Repeat steps 1 & 2

bart71

Thanks a lot !!
Will try it out this evening.
Strange thing it is all working fine in current setup if I only comment out PlaySound(); in interrupt handler.
I tried yesterday something like that: 

void main(void) {
  firmwareptr=cpct_disableFirmware();

...
...
...
    PLAYER_ARKOS_STOP();
    cpct_reenableFirmware(firmwareptr);
    loadlevel();

but did not help much

but what did a bit of change was 

cpct_enableLowerROM();
this gave me "press PLAY then any key" (with corrupted font)
no AMSDOS still but at least soemthing 


Summarizing:

Hope your hints will work, thanks once one :-)

Jean-Marie

Also, Arkos player seems to modify the shadow registers, especially AF' which is used by AMSDOS. 
So you'd need to save & restore it when calling PlaySound.

arnoldemu

It seems mc_start_program will do a lot of the work you need including restoring interrupts for firmware and setting up AF' and BC'.

I have not tested this but I think all you need is:

PLAYER_ARKOS_STOP();
loadlevel();
cpct_disableFirmware();

I *think* cpct_enableFirmware will enable the interrupt and because Arkos player is using BC' and AF' and it's not how the firmware expects this will cause problems.

But, because loadlevel calls mc_start_program this will init everything so you don't need to use cpct_enableFirmware.

Try that but if there are still problems I will be happy to debug it and tell you how to fix it and you can either share a minimal version of the code here as a dsk or you can private message me and tell me where I can download it and I will debug (and not share).

Targhan

Hi @bart71 , sorry I didn't see your PMs and this thread earlier (I'm on vacation). I don't know CPCTelera so I can't really help you with your specific problem, but:
- Before calling the "play song" every frame, if you're under interruption, make sure to save ALL the registers, including AF', BC', DE', HL', and IX/IY.
- The system requires AF' and BC' only, so you should also store these and put them back before loading a file.
- The PLY_AKG_PlaySoundEffect method modifies AF', save it first if needed.

Tell me if it you manage to make this work!

Trg.
Targhan/Arkos

Arkos Tracker 3.2.7 now released! - Follow the news on Twitter!
CPC Scene Radio! 24/7 CPC music only! Website Stream
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.2 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

bart71

Thank you all!
I see that I am in a very good company  :) :) :) 
These are my first attempts with assembler programming so please forgive me ... I am still not aware of many things happening there ..
I will try to preserve registers and see if it helps, sorry for silly question - normally you push values to the stack and get them later - is that what I need to do? Can save those values to unused  memory locations and get them later and load back to registers?
Br
Bartek

Targhan

#7
PUSH/POP are your friends indeed. Make sure you POP in the reverse order you PUSHed. If wanting to save values and restoring them much later, you can either save into memory:
        ld (SaveHL),hl
...
        ld hl,(SaveHL)
...
SaveHL        dw 0
... or by automodifying code:
        ld (SaveHL + 1),hl
...
SaveHL ld hl,0

But this is more dangerous, though faster. Watch out, IX and IY operands are encoded on two bytes (so +2 instead of +1).
Targhan/Arkos

Arkos Tracker 3.2.7 now released! - Follow the news on Twitter!
CPC Scene Radio! 24/7 CPC music only! Website Stream
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.2 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

bart71

Hello again
Something silly:
Tried 2 methods of saving AF' and BC' but SDCC complains on those special registers with apostrophes

    _store::
      push af'
      push bc'
    ret
    _restore::
      pop bc'
      pop af'
    ret
    filename: .asciz "LEVEL3.BIN"
    SaveAF':        .dw 0

src/loader.s:51: Error: <q> missing or improper operators, terminators, or delimiters
src/loader.s:55: Error: <q> missing or improper operators, terminators, or delimiters
src/loader.s:61: Error: <q> missing or improper operators, terminators, or delimiters


However when I look at player code it contains lines like that:

PLY_AKG_CHANNEL3_GLIDEDIRECTION: ld a,#0
    or a
    jr z,PLY_AKG_CHANNEL3_GLIDE_END
    ld (PLY_AKG_CHANNEL3_AFTERARPEGGIOPITCHVARIABLES+1),hl
    ld c,l
    ld b,h
    ex af,af'
    ld a,(PLY_AKG_CHANNEL3_TRACKNOTE+1)
    add a,a
    ld l,a
    ex af,af'

and it compiles OK !!!
What is wrong here?

bart71

#9
OK I see this in Z80 manual:

Allowed Instructions
push af
push bc
push de
push hl
push ix
push iy

OK !!!!!!
have found I need to do ex af,af'


and for BC' I need to do EXX I assume

so pushing AF' would look like:

ex af,af' ; to exchange registers
push af ; push
ex af,af' ; get back proper value of AF


is above correct?

Jean-Marie

This is what I do :

;;Save general registers
push af:push bc:push de:push hl:push ix:push iy
;;Save shadow registers
exx:ex af,af':push af:push bc:push de:push hl
...
Your code here
...
;;Restore shadow registers
pop hl:pop de:pop bc:pop af:exx:ex af,af'
;;Restore general registers
pop iy:pop ix:pop hl:pop de:pop bc:pop af


bart71

Now it is more clear, however questions:

1. Why after pushing you are not reverting to original values using exx and ex ?
2. Do I need to preserve/restore those registered with every PlaySound()? My idea is to save it in the beginning of main() and then restore it before calling loadlevel() 

alternative way of storing it to memory it would be something like below, correct?


ex af,af'
ld (storeaf),af

...

ld af,(storeaf)
ex af,af'

storeaf: .dw 0 

Jean-Marie

Quote from: bart71 on 23:22, 26 July 25Why after pushing you are not reverting to original values using exx and ex ?
I forgot to mention this is a snippet of code I use for an Interrupt handler.
The first thing to do, obviously is to save the registers. After that, you're free to use all the registers the way you want, so you can indeed carry out another exx:ex af,af'  to switch to general registers, or keep using the shadow registers. It doesn't really matter since eventually, both set of registers will be restored.
I'm not sure how CPCTelera works with Interrupt handlers, but I assume it inserts some code to save/restore the general registers, otherwise you would have seen a crash already. The way I see it you should save the shadow registers just before calling PlaySound, and restore them when the function returns :
;;Save shadow registers
exx:ex af,af':push af:push bc:push de:push hl
;;Switch back to general registers
exx:ex af,af'
call PlaySound
;;Switch to shadow registers
exx:ex af,af'
;;Restore shadow registers
pop hl:pop de:pop bc:pop af
;;Switch back to general registers
exx:ex af,af'
...

ld (storeaf),af is not a valid instruction : you can't manipulate the flag register directly with a ld instruction. The only way to save/restore the flag register is through push & pop af.

andycadley

Quote from: bart71 on 23:22, 26 July 25Now it is more clear, however questions:

1. Why after pushing you are not reverting to original values using exx and ex ?

The Z80 doesn't really care about which set of registers is which. When you switch with the with EXX the registers which were previously the alternate set are now the main ones, so you don't need to "switch them back"

Quote from: bart71 on 23:22, 26 July 25alternative way of storing it to memory it would be something like below, correct?

ex af, af'
ld (storeaf),af


Alas no, the Z80 doesn't have instructions for storing a 16-bit value to a memory address, so you either do it an 8-bit register at a time or do it indirectly with a pointer in HL, IX or IY. And the problem then becomes not being able to access F directly...

Pushing to the stack is absolutely the best way to preserve registers during an interrupt.

bart71

OK, I hope I will make it working later today.

However no comments on my question #2 ;-)
can't I copy registers while program initializes and restore them just before loading next level?

andycadley

#15
Quote from: bart71 on 09:46, 27 July 25OK, I hope I will make it working later today.

However no comments on my question #2 ;-)
can't I copy registers while program initializes and restore them just before loading next level?
PlaySound will corrupt the values in every register. Which one's you need to preserve depend on when you call it.

In an nterrupt routine you won't know which register values are "important" at the precise moment the interrupt occurs , so your interrupt code needs to restore any it uses to what they were when it was called. Hence, if calling PlaySound under interrupt, you're going to need to preserve them all.

If you're calling it outside of an interrupt handler, for example after a frame flyback check, then you can more easily consider which registers hold important information (which might not be any of them) and thus only preserve what you care about.

Targhan

2) I suggest you should save AF' each time you call PlaySound, though once again, only the system should need to have it restored correctly. If calling PlaySound under interruption (But I don't think that's the case), you MUST save AF', but also all the registers that PlaySound will modify.

PLaySound only modifies AF, BC, DE, HL and AF'.
Targhan/Arkos

Arkos Tracker 3.2.7 now released! - Follow the news on Twitter!
CPC Scene Radio! 24/7 CPC music only! Website Stream
Disark - A cross-platform Z80 disassembler/source converter
FDC Tool 1.2 - Read Amsdos files without the system

Imperial Mahjong
Orion Prime

bart71

Checked in the debugger and some of those registers change only once. will experiment and share results

bart71

Finally I made it to work!!!!!
Thanks everyone.
At the moment it is a temporary dirty fix loading static register values taken from 'silent' working version - it seems ony AF' and BC' need to be restored.

__asm
    ld hl,#0x0044
    push hl
    ex af,af'
    pop af
    ex af,af'
    ld hl,#0x7f8d
    push hl
    exx
    pop bc
    exx
    __endasm;

Powered by SMFPacks Menu Editor Mod