ZX Basic integration

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #30138
    gusman
    Participant

    Hi.

    I have managed to get a complete song working on a spectrum 128k using ZX Basic, I compiled a player with the music using RASM, used Disark to extract the assembler source, created functions to init, play and stop and it works perfectly.

    After that I wanted to do things in mid of the song (load some graphic screens and so on) and there I’m having a hard time.

    I have found the CPC integration example and it mostly works, on the sync loop I test for a key press and if it’s pressed I exit the loop, do some work and then I call the loop again. It returns and continues playing but the music is screwed, it still plays the same rythm but the tones are completely off, sometimes are very high, others very low, others only a single tone is played (but with the same rythm)…

    I think I must be missing something but have no idea at all.

    Also, would it be possible to play the music only when an interrupt happens so I can do the graphic work while the music is still playing? I can mix assembler in the basic son an ASM example would be really nice.

    Here are my hybrid functions (music.z80asm is the dissasembled player with the song).

    sub initMusic()
    
    asm
    push ix ;ZX basic only needs IX to be preserved when asm is called
    
    ld hl, HERO_START
    xor a
    call PLY_AKG_INIT
    
    pop ix
    
    end asm
    
    end sub
    
    sub musicUntilPress()
    
    asm
    push ix
    
    Sync:
        ei
        nop
        halt
        di
    
        ;3 - Play one frame of the song.
        call PLY_AKG_PLAY
    
        ld bc, 32766
        in a, (c)
        and 31
        xor 31
        jr nz, keyPressed
    
        ;4 - Loop!
        jr Sync
    
    keyPressed:
    
    pop ix
    end asm
    
    end sub
    
    sub stopMusic()
    
    asm
    
    push ix
    di
    
    call PLY_AKG_STOP
    
    ei
    pop ix
    
    end asm
    
    end sub
    
    asm
    
    Player:
    #include "music.z80asm"
    #30139
    Admin
    Keymaster

    Hi,

    I have no real idea why after you stop the song, and play it again, the song would be “destroyed”. The Stop itself only sends values to the PSG, so does not modify the song data itself.

    Things to test:
    – Do the same of what you do, without calling the STOP. Is the music still screwed?
    – Make sure that whatever you’re doing after the STOP does not change the player or music data (do you display stuff on the screen, could it modify the buffer if you use the ROM buffer mode with the player?)

    As for playing from within a system interruption, it is of course possible, however I do NOT know how to trigger a system interruption with the ZX!! The player itself is system-agnostic so of course it is doable. Once you have set up the interruption handler, you can call the Play, but beware:
    – The player modifies ALL the registers, including IX and IY, AF’ and all the auxiliary registers, so you have to save ALL of them (except for the stack, which the player modifies but saves and restores by itself).

    If you have a working example, maybe we could publish it on the website, some people asked me the same thing on CPC.

    #30140
    gusman
    Participant

    Hi.

    Thanks for the answer.

    Hmmm, this is strange, I only call the most basic routines on my code, writing to the screen memory and nothing else (not using the ROM functions, I write to screen manually), I thought that something had to be preserved between the play calls but from your answer I assume that’s not the case…

    I’m going to set up the interrupt handler and start debugging from there as I will be able to listen when the music gets corrupted.

    I assume that if on my interupt handler only the music must be executed something like this would work, right?

    `Interrput_Handler:

    push af
    push bc
    push de
    push ix
    push iy

    exx
    ex af, af’

    push af
    push bc
    push de
    push ix
    push iy

    call PLY_AKG_PLAY

    pop iy
    pop ix
    pop de
    pop bc
    pop af

    ex af, af’
    exx

    pop iy
    pop ix
    pop de
    pop bc
    pop af

    reti`

    I’m going to try it and if I get it working will post the interrupt setup example.

    Cheers.

    #30141
    gusman
    Participant

    Also, I have noticed something on the forum, whenever I try to edit my posts it gets me to other post!

    It redirects me always to http://www.julien-nevo.com/arkostracker/index.php/forums/topic/editor-hard-crash/

    Cheers.

    #30142
    Admin
    Keymaster

    Yeah, the forum is strangely broken. I don’t have time correcting this, I prefer concentrating on what really matters :).

    About your interruption handling:
    – IX and IY are NOT double registers: only one version exist, IX’ and IY’ don’t.
    – RETI shouldn’t be useful, simply RET.
    – Just before the least RET, do a EI. The Z80 automatically performs a DI when calling an interruption.

    #30143
    gusman
    Participant

    Ok, got it working, using the interrupt the music works without problems and my code runs perfectly without any change so… no idea why the music got corrupted, I suspect that something in the ROM interrupt routine was the culprit.

    Yep, the code I posted had some fails, I was sleepy when I wrote it, last night was too long 🙂

    Here is the code for setting up the player using the interrupt handler.

    PrepareMusic:
    ld hl, HERO_START               ;load your song address
    xor a                           ;subtrack 0
    call PLY_AKG_INIT               ;call initialization routine
    ret
    
    PlayMusic:
    call Interrupt_Setup            ;This function is totally innecessary, just for readability of the code
    ret
    
    StopMusic:
    di                              ;disable interrupts
    call PLY_AKG_STOP               ;stop the player
    IM 1                            ;revert interrupt mode to mode 1
    ei                              ;enable interrupts
    ret
    
    Player:
    ;your player+music code
    #include "music.z80asm"         
    
    JP_ADDRESS EQU #FDFD            ;JP address
    
    ;IM 2 mode must be used on the spectrum to have a custom interrupt handler.
    ;The spectrum architecture makes impossible to know what will be on the bus when an interrupt is executed
    ;so the handler must be in an address where it's high and low bytes are equal.
    ;To be able to store your interrupt handler anywhere three bytes at an address
    ;with that characteristic (FDFD in this case) are reserved and written with "jp Interrupt_Handler"
    
    Interrupt_Handler:
    
    push af                     ;store all registers
    push bc
    push de
    push hl
    push ix
    push iy
    
    exx
    ex af, af'
    
    push af
    push bc
    push de
    push hl
    
    call PLY_AKG_PLAY           ;play music
    
    pop hl                      ;restore all registers
    pop de
    pop bc
    pop af
    
    ex af, af'
    exx
    
    pop iy
    pop ix
    pop hl
    pop de
    pop bc
    pop af
    
    ei                          ;reenable interrupts
    
    ret                         ;return from interrupt handler
    
    .align 256
    Interrupt_Table:            ;interrupt table must be aligned at page boundary
                                ;with 256 + 1 bytes, all with the same value
    .defs 257
    
    Interrupt_Setup:
    di                          ;Disable interrupts
    ld de, Interrupt_Table      ;load interrupt table address
    ld hl, JP_ADDRESS           ;load "JP" address
    ld a, d
    ld i, a                     ;load I with the interrupt table high byte
    ld a, l                     ;load a with lower byte of JP address (indifferent to use H or L, both must be equal)
    Interrupt_Table_Loop:
    ld (de), a                  ;fill the table
    inc e
    jr nz, Interrupt_Table_Loop
    inc d                       ;write the 257th byte of the table
    ld (de), a
    ld (hl), #C3                ;write JP
    inc l
    ld (hl), low(Interrupt_Handler) ;write interrupt handler address
    inc l
    ld (hl), high(Interrupt_Handler)
    im 2                        ;set interrupt mode to 2
    ei                          ;enable interrupts
    ret

    To use it you first call PrepareMusic, when you want your music to start call PlayMusic and if you want to stop it then call StopMusic.

    Hope it helps anyone else who wants to integrate it 😀

    #30147
    Targhan
    Keymaster

    Awesome, I’ll put that in a specific page of the website, with credits where it is due :).

Viewing 7 posts - 1 through 7 (of 7 total)
  • You must be logged in to reply to this topic.