.export _Mym_MusicStart .importzp sp,tmp2,tmp3,tmp1,ptr1 .include "telestrat.inc" ; To check: AYC ; http://cpcwiki.eu/index.php/AYC _DecodedByte :=$D0 ; Byte being currently decoded from the MYM stream _DecodeBitCounter :=$D2 ; Number of bits we can read in the current byte _DecodedResult :=$D3 ; What is returned by the 'read bits' function _CurrentAYRegister :=$D4 ; Contains the number of the register being decoded _RegisterBufferHigh :=$D5 ; Points to the high byte of the decoded register buffer, increment to move to the next register _BufferFrameOffset :=$D6 ; From 0 to 127, used when filling the decoded register buffer _MusicResetCounter :=$D7 ; 2 bytes Contains the number of rows to play before reseting _CurrentFrame :=$D9 ; From 0 to 255 and then cycles... the index of the frame to play this vbl _PlayerVbl :=$DA _FrameLoadBalancer :=$DB ; We depack a new frame every 9 VBLs, this way the 14 registers are evenly depacked over 128 frames VIA_1 := $30f VIA_2 := $30c _MusicData := $c000 ; mym(char *buf) ; ; Current PSG values during unpacking ; .proc _Mym_MusicStart ; The two first bytes of the MYM music is the number of rows in the music ; We decrement that at each frame, and when we reach zero, time to start again. sta ptr1 stx ptr1+1 ldy #0 lda (ptr1),y sta _MusicResetCounter+0 iny lda (ptr1),y tax inx stx _MusicResetCounter+1 ;ldx _MusicData+0 ;stx _MusicResetCounter+0 ;ldx _MusicData+1 ;inx ;stx _MusicResetCounter+1 ; Initialize the read bit counter ldy #2 ; should be useless because we can do iny which earn 1 byte lda ptr1 clc adc #2 bcc next20 inc ptr1+1 lda ptr1+1 sta __auto_music_ptr+2 next20: sta ptr1 sta __auto_music_ptr+1 ;lda #<(_MusicData+2) ;sta __auto_music_ptr+1 ;lda #>(_MusicData+2) ;sta __auto_music_ptr+2 lda #1 sta _DecodeBitCounter ; Clear all data lda #0 sta _DecodedResult sta _DecodedByte sta _PlayerVbl sta _PlayerRegCurrentValue sta _BufferFrameOffset sta _PlayerCount sta _CurrentAYRegister sta _CurrentFrame ldx #14 loop_init: dex sta _PlayerRegValues,x bne loop_init ; ; Unpack the 128 first register frames ; lda #>_PlayerBuffer sta _RegisterBufferHigh ldx #0 unpack_block_loop: stx _CurrentAYRegister ; Unpack that register jsr _PlayerUnpackRegister2 ; Next register ldx _CurrentAYRegister inx cpx #14 bne unpack_block_loop lda #128 sta _PlayerVbl+0 lda #0 sta _PlayerCount sta _CurrentAYRegister sta _CurrentFrame lda #9 sta _FrameLoadBalancer lda #1 sta _MusicPlaying ; ; Install the IRQ ; php sei lda #<_Mym_PlayFrame sta _InterruptCallBack_3+1 lda #>_Mym_PlayFrame sta _InterruptCallBack_3+2 plp rts _Mym_MusicStop: ; Indicate the main code that the music is finished lda #0 sta _MusicPlaying ; Disable the IRQ so it does not conflict or cause weird things php sei lda #<_DoNothing sta _InterruptCallBack_3+1 lda #>_DoNothing sta _InterruptCallBack_3+2 plp ; Cut the sound so it does not sounds like a dying cat ; y=register number ; x=value to write ldy #7 ; Control register ldx #$FF jsr _PsgPlayRegister ldy #8 ; Volume A ldx #0 jsr _PsgPlayRegister ldy #9 ; Volume B ldx #0 jsr _PsgPlayRegister ldy #10 ; Volume C ldx #0 jsr _PsgPlayRegister rts _Mym_PlayFrame: ; ; Check for end of music ; CountZero: $81,$0d dec _MusicResetCounter+0 bne music_contines dec _MusicResetCounter+1 bne music_contines music_resets: jmp _Mym_MusicStop music_contines: ; ; Play a frame of 14 registers ; lda _CurrentFrame sta _auto_psg_play_read+1 lda #>_PlayerBuffer sta _auto_psg_play_read+2 ldy #0 register_loop: _auto_psg_play_read: ldx _PlayerBuffer ; y=register number ; x=value to write jsr _PsgPlayRegister inc _auto_psg_play_read+2 iny cpy #14 bne register_loop inc _CurrentFrame inc _PlayerCount lda _CurrentAYRegister cmp #14 bcs end_reg dec _FrameLoadBalancer bne end jsr _PlayerUnpackRegister inc _CurrentAYRegister lda #9 sta _FrameLoadBalancer end: rts end_reg: lda _PlayerCount cmp #128 bcc skip2 lda #0 sta _CurrentAYRegister sta _PlayerCount lda #9 sta _FrameLoadBalancer clc lda _PlayerVbl+0 adc #128 sta _PlayerVbl+0 skip2: rts ; y=register number ; x=value to write _PsgPlayRegister: sty VIA_1 txa pha lda VIA_2 ora #$EE ; $EE 238 11101110 sta VIA_2 and #$11 ; $11 17 00010001 ora #$CC ; $CC 204 11001100 sta VIA_2 tax pla sta VIA_1 txa ora #$EC ; $EC 236 11101100 sta VIA_2 and #$11 ; $11 17 00010001 ora #$CC ; $CC 204 11001100 sta VIA_2 rts ; ; Initialise X with the number of bits to read ; Y is not modifier ; _ReadBits: lda #0 sta _DecodedResult ; Will iterate X times (number of bits to read) loop_read_bits: dec _DecodeBitCounter beq get_next_byte shift_bit: asl _DecodedByte rol _DecodedResult dex bne loop_read_bits rts get_next_byte: ; reset mask lda #8 sta _DecodeBitCounter ; fetch a new byte, and increment the adress. __auto_music_ptr: lda _MusicData+2 sta _DecodedByte inc __auto_music_ptr+1 bne shift_bit inc __auto_music_ptr+2 jmp shift_bit _PlayerUnpackRegister: lda #>_PlayerBuffer clc adc _CurrentAYRegister sta _RegisterBufferHigh _PlayerUnpackRegister2: ; ; Init register bit count and current value ; ldx _CurrentAYRegister lda _PlayerRegValues,x sta _PlayerRegCurrentValue ; ; Check if it's packed or not ; and call adequate routine... ; ldx #1 jsr _ReadBits ldx _DecodedResult bne DecompressFragment UnchangedFragment: ; ; No change at all, just repeat '_PlayerRegCurrentValue' 128 times ; lda _RegisterBufferHigh ; highpart of buffer adress + register number sta __auto_copy_unchanged_write+2 ldx #128 ; 128 iterations lda _PlayerRegCurrentValue ; Value to write ldy _PlayerVbl repeat_loop: __auto_copy_unchanged_write: sta _PlayerBuffer,y iny dex bne repeat_loop jmp player_main_return player_main_return: ; Write back register current value ldx _CurrentAYRegister lda _PlayerRegCurrentValue sta _PlayerRegValues,x ; Move to the next register buffer inc _RegisterBufferHigh rts DecompressFragment: lda _PlayerVbl ; Either 0 or 128 at this point else we have a problem... sta _BufferFrameOffset decompressFragmentLoop: player_copy_packed_loop: ; Check packing method ldx #1 jsr _ReadBits ldx _DecodedResult bne PlayerNotCopyLast UnchangedRegister: ; We just copy the current value 128 times lda _RegisterBufferHigh ; highpart of buffer adress + register number sta __auto_player_copy_last+2 ldx _BufferFrameOffset ; Value between 00 and 7f lda _PlayerRegCurrentValue ; Value to copy __auto_player_copy_last: sta _PlayerBuffer,x inc _BufferFrameOffset player_return: ; Check end of loop lda _BufferFrameOffset and #127 bne decompressFragmentLoop jmp player_main_return PlayerNotCopyLast: ; Check packing method ldx #1 jsr _ReadBits ldx _DecodedResult beq DecompressWithOffset ReadNewRegisterValue: ; Read new register value (variable bit count) ldx _CurrentAYRegister lda _PlayerRegBits,x tax jsr _ReadBits ldx _DecodedResult stx _PlayerRegCurrentValue ; Copy to stream lda _RegisterBufferHigh ; highpart of buffer adress + register number sta __auto_player_read_new+2 ldx _BufferFrameOffset ; Value between 00 and 7f lda _PlayerRegCurrentValue ; New value to write __auto_player_read_new: sta _PlayerBuffer,x inc _BufferFrameOffset jmp player_return DecompressWithOffset: ; Read Offset (0 to 127) ldx #7 jsr _ReadBits lda _RegisterBufferHigh ; highpart of buffer adress + register number sta __auto_write+2 ; Write adress sta __auto_read+2 ; Read adress ; Compute wrap around offset... lda _BufferFrameOffset ; between 0 and 255 clc adc _DecodedResult ; + Offset Between 00 and 7f sec sbc #128 ; -128 tay ; Read count (7 bits) ldx #7 jsr _ReadBits inc _DecodedResult ; 1 to 129 ldx _BufferFrameOffset player_copy_offset_loop: __auto_read: lda _PlayerBuffer,y ; Y for reading iny __auto_write: sta _PlayerBuffer,x ; X for writing inx dec _DecodedResult bne player_copy_offset_loop stx _BufferFrameOffset sta _PlayerRegCurrentValue jmp player_return ; ; Size in bits of each PSG register ; _PlayerRegBits: ; Chanel A Frequency .byt 8 .byt 4 ; Chanel B Frequency .byt 8 .byt 4 ; Chanel C Frequency .byt 8 .byt 4 ; Chanel sound generator .byt 5 ; select .byt 8 ; Volume A,B,C .byt 5 .byt 5 .byt 5 ; Wave period .byt 8 .byt 8 ; Wave form .byt 8 _PlayerCount: .res 1,0 ; must be equal to 0 _MusicPlaying: .res 1,0 ; must be equal to 0 _PlayerRegValues: _RegisterChanAFrequency: ; Chanel A Frequency .res 1,8 .res 1,4 _RegisterChanBFrequency: ; Chanel B Frequency .res 1,8 .res 1,4 _RegisterChanCFrequency: ; Chanel C Frequency .res 1,8 .res 1,4 _RegisterChanNoiseFrequency: ; Chanel sound generator .res 1,5 ; select .res 1,8 ; Volume A,B,C _RegisterChanAVolume: .res 1,5 _RegisterChanBVolume: .res 1,5 _RegisterChanCVolume: .res 1,5 ; Wave period .res 1,8 .res 1,8 ; Wave form .res 1,8 _PlayerRegCurrentValue: .res 1,0 _DoNothing: rts _InterruptCallBack_3: ; Used by the music player jsr _DoNothing ; Transformed to "jsr _Mym_PlayFrame" -> 12 cycles ; jsr MiniScrollLoading ; -> 338 cycles pla tay pla tax pla rti _PlayerBuffer: .res 256*14 ; About 3.5 kilobytes somewhere in memory, we put the music file in overlay memory .endproc