/*
;**************************************************************************
;*
;*   ROUTINE DE RESTITUTION DES MODULES DSS
;*   --------------------------------------
;*
;*   Si vous voulez utiliser des modules DSS dans vos propres programmes,
;*   voici les routines nécessaires...
;*
;*   Ce fichier contient toutes les fonctions permettant de rejouer un
;*   module DSS. Vous devrez réaliser vous-même les opérations d'allocation
;*   et de libération de la mémoire (CHIP) et des canaux audio et de
;*   chargement du module en mémoire.
;*
;*  >Après avoir chargé le module en mémoire CHIP :
;*
;*   -Placez l'addresse du module en mémoire dans la variable
;*    [ SongPointer ] pointer (voir à la fin du fichier).
;*   -Appelez la fonction [ InitPlay() ] .
;*
;*  >Pour arrêter la restitution du module, appelez la fonction
;*   [ RemovePlayer() ]
;*
;*   Ce programme a été écrit pour l'assembleur de l'Aztec C V3.6.
;*   Il peut très facilement être adapté pour un autre assembleur.
;*   
;*   Voici un exemple d'utilisation de ces fonctions depuis un programme
;*   en C. Notez que les valeurs NOM_MODULE et TAILLE_MODULE doivent
;*   être remplacées par le nom de fichier et la taille du module que
;*   vous voulez jouer.
;*   
;*   ---------------------------------------------------------------------
;*   ---------------------------------------------------------------------
;*
;*      extern LONG InitPlay();
;*      extern VOID RemovePlayer();
;*
;*      extern BYTE *SongPointer;
;*  
;*      main()
;*      {
;*       BYTE *buffer;
;*       struct FileHandle *file;
;*       
;*       if(buffer = (BYTE *)AllocMem(TAILLE_MODULE, MEMF_CHIP|MEMF_CLEAR))
;*        {
;*         if(file = Open(NOM_MODULE, MODE_OLDFILE))
;*          {
;*           Read(file, buffer, TAILLE_MODULE);
;*           Close(file);
;*
;*           SongPointer = buffer;
;*  
;*           if(NOT InitPlay())
;*            {
;*             Delay(1000L);
;*             RemovePlayer();
;*            }
;*          }
;*         FreeMem(buffer, TAILLE_MODULE);
;*        }
;*      }
;*
;*   ---------------------------------------------------------------------
;*   ---------------------------------------------------------------------
;*  
;*  
;**************************************************************************
*/


;**************************************************************************
;*
;*                           DEFINITIONS
;*
;**************************************************************************

;**************************************************************************
;----- Fonctions Amiga ------
;**************************************************************************

_LVOOpenResource        EQU        -498
_LVOAddICRVector        EQU        -6
_LVORemICRVector        EQU        -12


;**************************************************************************
;------- ExecBase ----
;**************************************************************************

SysBase                 EQU     4
PAL                     EQU     50
eb_PowerSupplyFreq      EQU     531


;**************************************************************************
;------- CIAA --------
;**************************************************************************

CIAAPRA         EQU     $bfe001
CIABTALO        EQU     $bfd400
CIABTAHI        EQU     $bfd500
CIABICR         EQU     $bfdd00
CIABCRA         EQU     $bfde00

CIABTBLO        EQU     $bfd600
CIABTBHI        EQU     $bfd700
CIABCRB         EQU     $bfdf00


;**************************************************************************
;------ Chanson ----------
;**************************************************************************

sng_Speed       EQU     8
sng_Tempo       EQU     9
sng_Instr0      EQU     10
sng_Len         EQU     1436
sng_Seq         EQU     1438
sng_Data        EQU     1566

NOTE_MASK       EQU     $7ff
LONIB_MASK      EQU     $f
NIBBLE_SHIFT    EQU     4


;**************************************************************************
;------ Données instrument -----
;**************************************************************************

MAXINSTR        EQU     30
SIZEOF_insdt    EQU     46

instr_Start     EQU     30
instr_Len       EQU     34
instr_RLen      EQU     40


;**************************************************************************
;------- ChannelData -----
;**************************************************************************

SIZEOF_ad        EQU     34
        
ad_SampPer       EQU     0              ; W
ad_Effect        EQU     2              ; B
ad_Info          EQU     3              ; B
ad_Tune          EQU     4              ; B
ad_Volume        EQU     5              ; B
ad_SmpAdr        EQU     6              ; L
ad_SmpLen        EQU     10             ; W
ad_RepAdr        EQU     12             ; L
ad_RepLen        EQU     16             ; W
ad_RealPer       EQU     18             ; W
ad_DMABit        EQU     20             ; W
ad_PitchCtrl     EQU     22             ; B
ad_PortaCtrl     EQU     23             ; B
ad_BlkLoopStart  EQU     24             ; W
ad_BlkLoopCount  EQU     26             ; B
ad_VolumeChange  EQU     27             ; B
ad_SmpOffset     EQU     28             ; W
ad_PortaDir      EQU     30             ; B
ad_PortaSpeed    EQU     31             ; B
ad_PortaDestPer  EQU     32             ; W


;**************************************************************************
;------- AudioChips -------
;**************************************************************************

DMACON          EQU     $dff096
DMASET          EQU     $8000

AUD0LCH         EQU     $dff0a0
AUD1LCH         EQU     $dff0b0
AUD2LCH         EQU     $dff0c0
AUD3LCH         EQU     $dff0d0
AUDxLEN         EQU     $4
AUDxPER         EQU     $6
AUDxVOL         EQU     $8
AUDxDAT         EQU     $a
SIZEOF_AUD      EQU     $10


;**************************************************************************
;------ Bloc ---------
;**************************************************************************

NEXTTRACK       EQU     4
NEXTLINE        EQU     16
BLOCKSIZE       EQU     1024
MAXTRACK        EQU     4


;**************************************************************************
;------- Notes -------
;**************************************************************************

SIZEOF_NoteTable EQU    96
MIN_PER          EQU    2
MAX_PER          EQU    0

DO2             EQU     1712
SI5             EQU     113
STOP            EQU     $7ff


;**************************************************************************
;------- Effets -----
;**************************************************************************

ARPEGGIO        EQU     0
PITCHSLIDEUP    EQU     1
PITCHSLIDEDN    EQU     2
VOLUME          EQU     3
MASTERVOL       EQU     4
TEMPO           EQU     5
JUMPTOPOS       EQU     6
FILTER          EQU     7
PITCHUP         EQU     8
PITCHDN         EQU     9
PITCHCTRL       EQU     $a
SPEED           EQU     $b
VOLUMEUP        EQU     $c
VOLUMEDN        EQU     $d
VOLSLIDEUP      EQU     $e
VOLSLIDEDN      EQU     $f
MASTERVOLUP     EQU     $10
MASTERVOLDN     EQU     $11
MASTSLIDEUP     EQU     $12
MASTSLIDEDN     EQU     $13
LOOPSTART       EQU     $14
JUMPTOLOOP      EQU     $15
NOTEREPLAY      EQU     $16
NOTEDELAY       EQU     $17
NOTECUT         EQU     $18
INSTRSTART      EQU     $19
INSTRTUNE       EQU     $1a
PORTAMENTO      EQU     $1b
PORTAVOLSLIDEUP EQU     $1c
PORTAVOLSLIDEDN EQU     $1d
PORTAMENTOCTRL  EQU     $1e

MAX_VOL         EQU     64
OLDBREAK        EQU     $ff
AVERAGE_SPEED   EQU     125
MIN_SPEED       EQU     $1c


;**************************************************************************
;*
;*                     FIN DES DEFINITIONS
;*
;**************************************************************************


;**************************************************************************
;*
;*                          CODE
;*
;**************************************************************************

                CSEG

;******************************************************
;*                                                    *
;*                 fonctions à appeler                *
;*                                                    *
;******************************************************

;**************************************************************************
;* Installation de l'interruption Timer et initialisation.
;* Retourne 0 (zéro) dans D0 si réussite.
;* Retourne 1 dans D0 si échec.
;**************************************************************************

_InitPlay:
        movem.l d1-d2/a0-a3/a6,-(sp)
        move.l  SysBase,a6
        lea     CiabName,a1
        moveq   #0,d0
        jsr     _LVOOpenResource(a6)
        move.l  d0,CiabBase
        beq.s   InitPlayError
        cmp.b   #PAL,eb_PowerSupplyFreq(a6)
        beq.s   InitSong
        move.w  #4,IsNTSC
InitSong:
        bsr.s   InitSampleAdr
        bsr.s   InitSamples
        bsr.s   InitInterrupt
        tst.l   d0
        beq.s   EndInitPlay
InitPlayError:
        moveq   #1,d0
EndInitPlay:
        movem.l (sp)+,d1-d2/a0-a3/a6
        rts


;**************************************************************************
;* Arrête la restitution du module et enlève l'interruption.
;**************************************************************************

_RemovePlayer:
        movem.l d0-d1/a0-a1/a6,-(sp)
        bsr.s   DisablePlayer
        move.l  CiabBase,a6
        lea     TimerInt,a1
        moveq   #1,d0
        jsr     _LVORemICRVector(a6)
        bclr    #1,CIAAPRA
        movem.l (sp)+,d0-d1/a0-a1/a6
        rts


;******************************************************
;*                                                    *
;*                 fonctions internes                 *
;*                                                    *
;******************************************************

;**************************************************************************
;* Initialisation de l'interruption CIA.
;* Retourne 0 dans D0 si réussite.
;**************************************************************************

InitInterrupt:
        bsr.s   EnablePlayer
        move.l  CiabBase,a6
        lea     TimerInt,a1
        moveq   #1,d0
        jsr     _LVOAddICRVector(a6)
        tst.l   d0
        bne.s   InitInterruptEnd
        move.b  CIABCRB,d0
        and.b   #%10000000,d0
        or.b    #1,d0
        move.b  d0,CIABCRB
        move.b  #$82,CIABICR
        lea     TimerTable,a0
        move.w  IsNTSC,d0
        move.l  (a0,d0),d0
        move.l  d0,TimerValue
        moveq   #0,d1
        move.l  _SongPointer,a0
        move.b  sng_Speed(a0),d1
        bne.s   SetTimer
        moveq   #AVERAGE_SPEED,d1
SetTimer:
        divu    d1,d0
        move.b  d0,CIABTBLO
        lsr.w   #8,d0
        move.b  d0,CIABTBHI
        moveq   #0,d0
InitInterruptEnd:
        rts


;**************************************************************************
;* Initialisation de la table d'adresses des échantillons.
;**************************************************************************

InitSampleAdr:
        move.l  _SongPointer,a1
        lea     sng_Len(a1),a0
        move.w  (a0)+,d0                ; d0 = taille chanson
        subq.w  #1,d0
        moveq   #0,d1
        move.l  d1,d2
SeekLastBlockLoop:
        move.b  (a0)+,d2
        cmp.b   d2,d1
        bhi.s   NotLastBlock
        move.b  d2,d1
NotLastBlock:
        dbra    d0,SeekLastBlockLoop
        addq.w  #1,d1
        moveq   #10,d0
        lsl.l   d0,d1                   ; d1 = taille des blocs
        lea     sng_Data(a1),a0
        add.l   d1,a0                   ; a0 -> premier échantillon
        lea     sng_Instr0(a1),a2       ; a2 -> premier instrument
        lea     SampleAdrTable,a3
        moveq   #MAXINSTR,d0
InitAdrLoop:
        moveq   #0,d1
        move.w  instr_Len(a2),d1        ; longueur ?
        beq.s   InitNextAdr
InitThisAdr:
        move.l  a0,(a3)
        moveq   #0,d2
        cmp.w   #1,instr_RLen(a2)       ; longueur répétition
        beq.s   GetSampleSize
        move.w  instr_RLen(a2),d2
        add.l   d2,d1
GetSampleSize:
        add.l   d1,d1
        bclr    #0,instr_Start+3(a2)    ; aligner instr_Start sur un mot
        add.l   instr_Start(a2),d1      ; Début
        add.l   d1,a0                   ; a0 -> échantillon suivant
InitNextAdr:
        addq.l  #4,a3
        add.l   #SIZEOF_insdt,a2
        dbra    d0,InitAdrLoop
        rts


;**************************************************************************
;* Effacez le premier mot long de chaque échantillon.
;**************************************************************************

InitSamples:
        lea     SampleAdrTable,a0
        moveq   #MAXINSTR,d0
InitSamplesLoop:
        move.l  (a0)+,d1
        beq.s   InitSamplesLoopEnd
        move.l  d1,a1
        moveq   #3,d2
Clear4Bytes:
        clr.b   (a1)+
        dbra    d2,Clear4Bytes
InitSamplesLoopEnd:
        dbra    d0,InitSamplesLoop
        rts


;**************************************************************************
;* RAZ des registres audio Volume audio et arrêt de la DMA audio.
;**************************************************************************

DisablePlayer:
        clr.w   AUD0LCH+AUDxVOL
        clr.w   AUD1LCH+AUDxVOL
        clr.w   AUD2LCH+AUDxVOL
        clr.w   AUD3LCH+AUDxVOL
        move.w  #$0f,DMACON
        rts


;**************************************************************************
;* RAZ des registres audio Volume.
;**************************************************************************

EnablePlayer:
        clr.w   AUD0LCH+AUDxVOL
        clr.w   AUD1LCH+AUDxVOL
        clr.w   AUD2LCH+AUDxVOL
        clr.w   AUD3LCH+AUDxVOL
        rts


;**************************************************************************
;* Routine d'interruption CIA.
;**************************************************************************

TimerIntRoutine:
        movem.l d1-d7/a0-a6,-(sp)
        bsr.s   PlayRoutine
        movem.l (sp)+,d1-d7/a0-a6
        moveq   #0,d0
        rts


;**************************************************************************
;* Routine principale.
;**************************************************************************

PlayRoutine:
        moveq   #0,d0
        move.l  _SongPointer,a6
        move.b  sng_Tempo(a6),d0
        addq.w  #1,PlayTimer
        cmp.w   PlayTimer,d0            ; tempo == playtimer ?
        bne.s   PlayEffects             ; non, jouer effets
        clr.w   PlayTimer               ; oui, RAZ playtimer
        clr.w   ReplayTimer
        move.w  #3,ArpTimer
        bra.s   PlaySound               ; jouer les notes


;**************************************************************************
;* Jouer les effest entre chaque note.
;**************************************************************************

PlayEffects:
        lea     ChannelData0,a0
        lea     AUD0LCH,a1
        moveq   #MAXTRACK-1,d3          ; 4 pistes
PlayEffLoop:
        move.w  (a0),d0
        and.w   #NOTE_MASK,d0
        cmp.w   #STOP,d0                ; note == STOP ?
        beq.s   PlayEffLoopEnd
        bsr.s   MakeEffects
PlayEffLoopEnd:
        add.l   #SIZEOF_ad,a0           ; a0 -> channeldata suivant
        add.w   #SIZEOF_AUD,a1          ; a1 -> audioreg suivant
        dbra    d3,PlayEffLoop
        rts


;**************************************************************************
;* Recherche de la routine d'effets à appeler.
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

MakeEffects:
        move.b  ad_Effect(a0),d0
        tst.b   ad_Info(a0)
        beq.s   MakeEffectsEnd
        cmp.b   #ARPEGGIO,d0
        beq.s   EffArpeggio
        cmp.b   #PITCHSLIDEUP,d0
        beq.s   EffPitchSlideUp
        cmp.b   #PITCHSLIDEDN,d0
        beq.s   EffPitchSlideDn
        cmp.b   #VOLSLIDEUP,d0
        beq.s   EffVolSlideUp
        cmp.b   #VOLSLIDEDN,d0
        beq.s   EffVolSlideDn
        cmp.b   #MASTSLIDEUP,d0
        beq.s   EffMastVolSlideUp
        cmp.b   #MASTSLIDEDN,d0
        beq.s   EffMastVolSlideDn
        cmp.b   #NOTEREPLAY,d0
        beq.s   EffNoteReplay
        cmp.b   #NOTEDELAY,d0
        beq.s   EffNoteDelay
        cmp.b   #NOTECUT,d0
        beq.s   EffNoteCut
        cmp.b   #PORTAVOLSLIDEUP,d0
        beq.s   EffPortaVolSlideUp
        cmp.b   #PORTAVOLSLIDEDN,d0
        beq.s   EffPortaVolSlideDn
MakeEffectsEnd:
        cmp.b   #PORTAMENTO,d0
        beq.s   EffPortamento
        rts


;**************************************************************************
;* Effet = ARPEGGIO (0)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffArpeggio:
        moveq   #0,d2
        move.w  ArpTimer,d2             ; d2 = timer arpeggio
        sub.w   PlayTimer,d2            ; d2 = ArpTimer - PlayTimer
        beq.s   EffArpThree             ; PlayTimer = 0 [3]
        cmp.w   #1,d2
        beq.s   EffArpTwo               ; PlayTimer = 2 [3]
EffArpOne:
        move.b  ad_Info(a0),d2
        lsr.b   #NIBBLE_SHIFT,d2        ; d2 = demi-octet poids fort
        bra.s   EffArpSeek
EffArpTwo:
        move.b  ad_Info(a0),d2
        and.b   #LONIB_MASK,d2          ; d2 = demi-octet poids faible
EffArpSeek:
        add.w   d2,d2                   ; d2 = offset table de notes
        move.w  ad_RealPer(a0),d1       ; d1 = période normale
        bsr.s   FindMaxMinPointer
        mulu    #SIZEOF_NoteTable/4,d0
        lea     NoteTable,a3
        add.l   d0,a3                   ; a3 -> table période réelle
EffArpLoop:
        move.w  (a3,d2.w),d0            ; d0 = nouvelle période
        cmp.w   MIN_PER(a2),d0
        beq.s   EffArpFound
        cmp.w   (a3)+,d1                ; (a3) == périod normale ?
        bne.s   EffArpLoop
        bra.s   EffArpFound
EffArpThree:
        addq.w  #3,ArpTimer
        move.w  ad_RealPer(a0),d0       ; d0 = période normale
EffArpFound:
        move.w  d0,AUDxPER(a1)
        rts


;**************************************************************************
;* Effet = PITCHSLIDEUP (1)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffPitchSlideUp:        
        bsr.s   FindMaxMinPointer
        moveq   #0,d0
        move.b  ad_Info(a0),d0               ; d0 = valeur effet
        sub.w   d0,ad_RealPer(a0)
        move.w  ad_RealPer(a0),d0
        cmp.w   MIN_PER(a2),d0               ; sup. à plus basse période ?
        bge.s   EffPitchOK
        move.w  MIN_PER(a2),ad_RealPer(a0)
        bra.s   EffPitchOK

;**************************************************************************
;* Effet = PITCHSLIDEDN (2)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffPitchSlideDn:
        bsr.s   FindMaxMinPointer
        moveq   #0,d0
        move.b  ad_Info(a0),d0
        add.w   d0,ad_RealPer(a0)
        move.w  ad_RealPer(a0),d0
        cmp.w   MAX_PER(a2),d0               ; inf. à plus haute période ?
        ble.s   EffPitchOK
        move.w  MAX_PER(a2),ad_RealPer(a0)
EffPitchOK:
        move.w  ad_RealPer(a0),d0
        tst.b   ad_PitchCtrl(a0)
        beq.s   SetPitchPeriod
        bsr.s   FindNearestNote
SetPitchPeriod:
        move.w  d0,AUDxPER(a1)               ; nouvelle période dans reg
        rts


;**************************************************************************
;* Recherche du pointeur sur MaxMinNoteTable.
;* ENTREE:
;*      a0 -> ChannelData courant
;*
;* SORTIE:
;*      a2 -> Max-Min pour accord fin
;*      d0 = Accord fin * 4
;**************************************************************************

FindMaxMinPointer:
        moveq   #0,d0
        lea     MaxMinNoteTable,a2
        move.b  ad_Tune(a0),d0
        lsl.w   #2,d0
        add.l   d0,a2                   ; a2 -> min/max pour accord fin
        rts


;**************************************************************************
;* Effet = VOLSLIDEUP ($E)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffVolSlideUp:                          ; augmenter volume
        moveq   #0,d0
        move.b  ad_Info(a0),d0          ; d0 = valeur effet
EffVolSlideUpSet:
        add.b   d0,ad_Volume(a0)        ; ajouter d0 au volume
        cmp.b   #MAX_VOL,ad_Volume(a0)  ; volume <= 64 ?
        ble.s   EffVolSlideOK
        move.b  #MAX_VOL,ad_Volume(a0)  ; volume = 64
        bra.s   EffVolSlideOK

;**************************************************************************
;* Effet = VOLSLIDEDN ($F)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffVolSlideDn:                          ; diminuer volume
        moveq   #0,d0
        move.b  ad_Info(a0),d0          ; d0 = valeur effet
EffVolSlideDnSet:
        sub.b   d0,ad_Volume(a0)        ; enlever d0 du volume
        bge.s   EffVolSlideOK           ; d0 >= 0 ?
        clr.b   ad_Volume(a0)           ; d0 = 0
EffVolSlideOK:
        move.b  ad_Volume(a0),d0        ; d0 = volume
        sub.w   MasterVolume,d0         ; enlever volume général
        bge.s   EffVolSlideSet          ; d0 >= 0 ?
        moveq   #0,d0
EffVolSlideSet:
        move.w  d0,AUDxVOL(a1)
EffVolSlideEnd:
        rts


;**************************************************************************
;* Effet = MASTSLIDEUP ($12)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffMastVolSlideUp:
        moveq   #0,d0
        move.b  ad_Info(a0),d0          ; d0 = valeur effet
EffMastVolSlideUpSet:
        sub.w   d0,MasterVolume         ; enlever d0 du volume général
        tst.w   MasterVolume
        bge.s   EffMastVolSlideOK       ; volume général >= 0 ?
        clr.w   MasterVolume
        bra.s   EffMastVolSlideOK

;**************************************************************************
;* Effet = MASTSLIDEDN ($13)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffMastVolSlideDn:
        moveq   #0,d0
        move.b  ad_Info(a0),d0
EffMastVolSlideDnSet:
        add.w   d0,MasterVolume
        cmp.w   #MAX_VOL,MasterVolume
        ble.s   EffMastVolSlideOK
        move.w  #MAX_VOL,MasterVolume
EffMastVolSlideOK:
        moveq   #0,d0
        movem.l a0-a1,-(sp)
        lea     ChannelData0,a0
        lea     AUD0LCH,a1
        moveq   #MAXTRACK-1,d1          ; 4 pistes
EffMastVolSlideSet:
        bsr.s   EffVolSlideOK
        add.l   #SIZEOF_ad,a0           ; a0 -> channeldata suivant
        add.l   #SIZEOF_AUD,a1          ; a1 -> audioreg suivant
        dbra    d1,EffMastVolSlideSet
        movem.l (sp)+,a0-a1
        rts


;**************************************************************************
;* Effet = NOTEREPLAY ($16)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;*      a6 = SongPointer
;**************************************************************************

EffNoteReplay:
        moveq   #0,d0
        move.b  ad_Info(a0),d0
        cmp.b   sng_Tempo(a6),d0
        bge.s   EffNoteReplayEnd            ; si info >= Tempo, arrêter
        tst.w   ReplayTimer                 ; replaytimer != 0
        bne.s   TimeToReplay
        move.w  d0,ReplayTimer              ; replaytimer = info
TimeToReplay:
        move.w  PlayTimer,d1
        cmp.w   ReplayTimer,d1              ; replaytimer == timer ?
        bne.s   EffNoteReplayEnd
        add.w   d0,ReplayTimer              ; replaytimer += info
ReplayNote:
        move.w  ad_DMABit(a0),d0
        move.w  d0,DMACON                   ; arrêter DMA audio
        move.l  ad_SmpAdr(a0),(a1)          ; adresse échantillon
        move.w  ad_SmpLen(a0),AUDxLEN(a1)   ; longueur échantillon
        bsr.s   WaitForDMA
        or.w    #DMASET,d0
        move.w  d0,DMACON                   ; démarrer DMA audio
        bsr.s   WaitForDMA
        move.l  ad_RepAdr(a0),(a1)          ; adresse répétition
        move.w  ad_RepLen(a0),AUDxLEN(a1)   ; longueur répétition
EffNoteReplayEnd:
        rts


;**************************************************************************
;* Effet = NOTEDELAY ($17)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;*      a6 = SongPointer
;**************************************************************************

EffNoteDelay:
        move.b  ad_Info(a0),d0
        cmp.b   sng_Tempo(a6),d0            ; si info < Tempo, continuer
        blt.s   DoNoteDelay
        clr.w   ad_Effect(a0)               ; sinon, effet = info = 0
        bra.s   EffNoteDelayEnd
DoNoteDelay:
        cmp.b   PlayTimer+1,d0              ; timer == info ?
        bne.s   EffNoteDelayEnd
        move.w  ad_SampPer(a0),d0           ; période + échantillon ?
        beq.s   EffNoteDelayEnd
        and.w   #NOTE_MASK,d0
        beq.s   ReplayNote
        bsr.s   FindRealPeriod
        move.w  d0,AUDxPER(a1)
        move.w  d0,ad_RealPer(a0)
        bne.s   ReplayNote                  ; jouer note
EffNoteDelayEnd:
        rts


;**************************************************************************
;* Effet = NOTECUT ($18)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;*      a6 = SongPointer
;**************************************************************************

EffNoteCut:
        move.b  ad_Info(a0),d0
        cmp.b   sng_Tempo(a6),d0            ; si info < Tempo, continuer
        blt.s   DoNoteCut
        clr.w   ad_Effect(a0)               ; sinon, effet = info = 0
        bra.s   EffNoteCutEnd
DoNoteCut:
        cmp.b   PlayTimer+1,d0              ; timer == info ?
        bne.s   EffNoteCutEnd
        clr.b   ad_Volume(a0)               ; oui, effacer volume
        clr.w   AUDxVOL(a1)
EffNoteCutEnd:
        rts


;**************************************************************************
;* Effet = PORTAMENTO ($1B)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffPortamento:
        move.b  ad_Info(a0),d0                     ; info == 0 ?
        beq.s   DoPortamento
        move.b  d0,ad_PortaSpeed(a0)               ; vitesse = info
        clr.b   ad_Info(a0)
DoPortamento:                                      
        tst.w   ad_PortaDestPer(a0)                ; période dest == 0 ?
        beq.s   EffPortamentoEnd                   ; oui, fin
        moveq   #0,d0
        move.b  ad_PortaSpeed(a0),d0
        tst.b   ad_PortaDir(a0)                    ; dir == 1 ?
        bne.s   PortamentoUp                       ; oui, augmenter ton
        add.w   d0,ad_RealPer(a0)                  ; période += info
        move.w  ad_PortaDestPer(a0),d0
        cmp.w   ad_RealPer(a0),d0                  ; période dest > période ?
        bgt.s   PortamentoOK
        move.w  ad_PortaDestPer(a0),ad_RealPer(a0) ; période = période dest
        clr.w   ad_PortaDestPer(a0)                ; période dest = 0
        bra.s   PortamentoOK
PortamentoUp:
        sub.w   d0,ad_RealPer(a0)                  ; période -= info
        move.w  ad_PortaDestPer(a0),d0
        cmp.w   ad_RealPer(a0),d0                  ; période dest < période ?
        blt.s   PortamentoOK
        move.w  ad_PortaDestPer(a0),ad_RealPer(a0) ; période = période dest
        clr.w   ad_PortaDestPer(a0)                ; période dest = 0
PortamentoOK:
        move.w  ad_RealPer(a0),d0                  ; d0 = période
        tst.b   ad_PortaCtrl(a0)                   ; glissando ?
        beq.s   SetPortaPeriod
        bsr.s   FindNearestNote
SetPortaPeriod:
        move.w  d0,AUDxPER(a1)
EffPortamentoEnd:
        rts


;**************************************************************************
;* Effet = PORTAVOLSLIDEUP ($1C)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffPortaVolSlideUp:
        bsr.s   DoPortamento
        bra.s   EffVolSlideUp


;**************************************************************************
;* Effet = PORTAVOLSLIDEDN ($1D)
;* ENTREE:
;*      a0 -> ChannelData courant
;*      a1 -> AudioRegs courant
;**************************************************************************

EffPortaVolSlideDn:
        bsr.s   DoPortamento
        bra.s   EffVolSlideDn


;**************************************************************************
;* Trouver la plus proche note de la période réelle.
;* ENTREE:
;*      a0 -> ChannelData courant
;*      d0 = Période réelle
;*
;* SORTIE:
;*      d0 = Note la plus proche
;**************************************************************************

FindNearestNote:
        movem.l d1/a0,-(sp)
        moveq   #0,d1
        move.b  ad_Tune(a0),d1
        lea     NoteTable,a0
        mulu    #SIZEOF_NoteTable,d1
        add.l   d1,a0
        moveq   #(SIZEOF_NoteTable/2)-1,d1
FindNearestNoteLoop:
        cmp.w   (a0)+,d0
        bge.s   NearestNoteFound
        dbra    d1,FindNearestNoteLoop
NearestNoteFound:
        move.w  -2(a0),d0
        movem.l (sp)+,d1/a0
        rts


;**************************************************************************
;* Jouer un son sur chaque canal.
;* Appelée quand PlayTimer = Tempo.
;* ENTREE:
;*      a6 = SongPointer
;**************************************************************************

PlaySound:
        lea     sng_Data(a6),a0
        lea     sng_Seq(a6),a1
        move.w  CurrentPos,d0
        move.w  d0,NewPosJump
        moveq   #0,d1
        move.b  0(a1,d0.w),d1           ; d1 = numéro du bloc
        moveq   #10,d0
        lsl.l   d0,d1
        move.w  OffsetBlock,d0
        add.l   d0,d1                   ; d1 = offset dans données bloc
        clr.w   AudioDMA
        lea     AUD0LCH,a3
        lea     ChannelData0,a4
        moveq   #MAXTRACK-1,d7          ; 4 pistes
PlayLoop:
        bsr.s   PlayInstr
        add.w   #SIZEOF_AUD,a3
        add.l   #SIZEOF_ad,a4
        dbra    d7,PlayLoop
        move.w  AudioDMA,d0             ; démarre DMA audio
        or.w    #DMASET,d0
        move.w  d0,DMACON
        bsr.s   WaitForDMA
        lea     ChannelData0,a0         ; placer nouvelles valeurs dans reg. audio
        lea     AUD0LCH,a1
        moveq   #MAXTRACK-1,d0
SetNewValue:
        cmp.b   #NOTEDELAY,ad_Effect(a0)
        beq.s   SetNewValueIterate
        move.l  ad_RepAdr(a0),(a1)
        move.w  ad_RepLen(a0),AUDxLEN(a1)
SetNewValueIterate:
        add.l   #SIZEOF_ad,a0
        add.w   #SIZEOF_AUD,a1
        dbra    d0,SetNewValue
        tst.w   BlockLoopStatus                  ; bouclage ?
        beq.s   NoPosChange
        move.w  NewOffsetBlock,OffsetBlock       ; nouvelle position dans bloc
        clr.w   BlockLoopStatus
NoPosChange:
        add.w   #NEXTLINE,OffsetBlock            ; modifier position dans bloc
        cmp.w   #BLOCKSIZE,OffsetBlock           ; fin du bloc ?
        blt.s   TestJump
ChangePosition:
        clr.w   OffsetBlock                      ; position dans bloc = 0
        move.w  #-1,ChannelData0+ad_BlkLoopStart ; effacer débuts bouclage
        move.w  #-1,ChannelData1+ad_BlkLoopStart
        move.w  #-1,ChannelData2+ad_BlkLoopStart
        move.w  #-1,ChannelData3+ad_BlkLoopStart
        move.w  NewPosJump,CurrentPos
        addq.w  #1,CurrentPos                    ; nouvelle position dans chanson
        move.w  sng_Len(a6),d0
        move.w  CurrentPos,d1
        cmp.w   d0,d1                            ; fin de la chanson ?
        bne.s   TestJump
        clr.w   CurrentPos                       ; position = 0
TestJump:
        tst.w   BreakStatus                      ; saut ou passage à position suivante ?
        beq.s   PlaySoundEnd
        clr.w   BreakStatus
        bra.s   ChangePosition
PlaySoundEnd:
        rts


;******************************************************************
;* Attendre la fin du cycle DMA Audio.
;* 
;* Note :
;*  Il est peut-être préférable de réaliser cela avec le copper !
;*                   \__\|          moveq   #$79,d2
;*                      /|label1:   move.b  $dff007,d3
;*                       |label2:   cmp.b   $dff007,d3
;*                       |          beq.s   label2
;*                       |          dbra    d2,label1
;*
;******************************************************************

WaitForDMA:
        movem.l d0-d1,-(sp)
        move.b  CIABCRA,d1              ; utiliser CIAB du timerA
        move.b  d1,d0
        and.b   #%11000000,d0           ; sauver bits freq and mode
        or.b    #%00001000,d0           ; une seule fois
        move.b  d0,CIABCRA
        move.b  #$2f,CIABTALO           ; attendre 420 µs
        move.b  #1,CIABTAHI
PlayDelay1:
        btst.b  #0,CIABCRA              ; fin du délai ?
        bne.s   PlayDelay1
        move.b  d1,CIABCRA              ; ancienne valeur CIABPRA
        move.b  #%00000001,CIABICR      ; annuler intreq CIAB du timerA
        movem.l (sp)+,d0-d1
        rts


;**************************************************************************
;* Trouver la période réelle en fonction de l'accord fin de l'instrument.
;* ENTREE:
;*      a0 -> ChannelData courant
;*      d0 = Période figurant dans chanson
;*
;* SORTIE:
;*      d0 = Période réelle
;**************************************************************************

FindRealPeriod:
        movem.l d1-d2/a0,-(sp)
        moveq   #0,d1
        move.b  ad_Tune(a0),d1               ; d1 = accord fin instr
        beq.s   FindRealPeriodEnd            ; accord fin == 0 ?
        lea     NoteTable,a0
        moveq   #(SIZEOF_NoteTable/2)-1,d2
FindRealPeriodLoop
        cmp.w   (a0)+,d0                     ; d0 == période dans table ?
        beq.s   StandardPeriodFound
        dbra    d2,FindRealPeriodLoop
StandardPeriodFound:
        mulu    #SIZEOF_NoteTable,d1         ; d1 = offset tables périodes
        move.w  -2(a0,d1),d0                 ; d0 = période réelle
FindRealPeriodEnd:
        movem.l (sp)+,d1-d2/a0
        rts


;**************************************************************************
;* Set all Portamento parameters.
;* ENTREE:
;*      d6 = Period
;*      a4 -> ChannelData courant
;**************************************************************************

FindPortaData:
        movem.l d0/a0,-(sp)
        move.w  d6,d0
        move.l  a4,a0
        bsr.s   FindRealPeriod
        move.w  d0,ad_PortaDestPer(a4)         ; d0 = destperiod
        clr.b   ad_PortaDir(a4)
        move.w  ad_PortaDestPer(a0),d0
        cmp.w   ad_RealPer(a4),d0              ; destperiod > period ?
        beq.s   ClearPortamento                ; if =, no porta
        bgt.s   FindPortaDataEnd               ; if >, dir = 0
        move.b  #1,ad_PortaDir(a4)             ; else, dir = 1
        bra.s   FindPortaDataEnd
ClearPortamento:
        clr.w   ad_PortaDestPer(a4)
FindPortaDataEnd:
        movem.l (sp)+,d0/a0
        rts


;**************************************************************************
;* Jouer une note sur le canal audio courant.
;*      a0 -> Données chanson
;*      a3 -> AudioRegs courant
;*      a4 -> ChannelData courant
;*      d1 = position dans bloc
;**************************************************************************

PlayInstr:
        lea     sng_Instr0(a6),a2
        move.l  (a0,d1.l),ad_SampPer(a4)
        addq.l  #NEXTTRACK,d1
        moveq   #0,d0
        move.b  ad_SampPer(a4),d0
        lsr.b   #3,d0                           ; d0 = numéro instrument
        tst.b   d0                              ; d0 == 0 ?
        beq.s   NoSampleChange
        lea     SampleAdrTable,a5
        subq.b  #1,d0
        move.l  d0,d3
        lsl.w   #2,d0                           ; d0 = offset SampleAdrTable
        mulu    #SIZEOF_insdt,d3
        add.l   #instr_Start,d3
        add.l   d3,a2                           ; a2 -> début InstrData
        move.l  (a5,d0.w),d4
        add.l   (a2)+,d4                        ; Début échantillon
        move.l  d4,ad_SmpAdr(a4)
        beq.s   NoSampleChange
        move.w  (a2)+,ad_SmpLen(a4)             ; Longueur
        move.l  (a2)+,d5                        ; Début répétition
        move.w  (a2)+,d2                        ; Longueur répétition
        bne.s   SetRepeat
        moveq   #1,d2
        moveq   #0,d5
SetRepeat:
        move.w  d2,ad_RepLen(a4)
        add.l   ad_SmpAdr(a4),d5
        move.l  d5,ad_RepAdr(a4)
        move.w  (a2),ad_Tune(a4)                ; Accord + Volume
NoSampleChange:
        move.w  ad_SampPer(a4),d6
        beq.s   TestEffects
        and.w   #NOTE_MASK,d6                   ; note == 0 ?
        beq.s   TestEffects
        cmp.b   #PORTAMENTO,ad_Effect(a4)
        beq.s   ActivatePorta
        cmp.b   #PORTAVOLSLIDEUP,ad_Effect(a4)
        beq.s   ActivatePorta
        cmp.b   #PORTAVOLSLIDEDN,ad_Effect(a4)
        bne.s   CutAudioDMA
ActivatePorta:
        bsr     FindPortaData
        bra     UseInstrVol
CutAudioDMA:
        move.w  ad_DMABit(a4),d0
        move.w  d0,DMACON                       ; couper DMA audio pour la piste
        bsr.s   WaitForDMA
        cmp.w   #STOP,d6                        ; note == 'OFF' ?
        bne.s   TestInstrTune
        clr.w   AUDxVOL(a3)                     ; effacer volume audio
        or.w    d0,AudioDMA
        rts
TestInstrTune:
        cmp.b   #INSTRTUNE,ad_Effect(a4)        ; effet == INSTRTUNE ?
        bne.s   TestNoteDelay
        bsr.s   EffInstrTune
        bra.s   SetPlayRegs
TestNoteDelay:
        cmp.b   #NOTEDELAY,ad_Effect(a4)        ; effet == NOTEDELAY ?
        beq.s   UseInstrVol
TestInstrStart:
        cmp.b   #INSTRSTART,ad_Effect(a4)
        bne.s   SetPlayRegs
        bsr.s   EffSampleOffset
SetPlayRegs:                                    ; écrire dans registres audio
        move.l  ad_SmpAdr(a4),(a3)              ; adresse échant. == NULL ?
        beq.s   TestEffects
        move.w  ad_SmpLen(a4),AUDxLEN(a3)
        movem.l d0/a0,-(sp)
        move.w  d6,d0
        move.l  a4,a0
        bsr.s   FindRealPeriod
        move.w  d0,d6
        movem.l (sp)+,d0/a0
        move.w  d6,AUDxPER(a3)
        move.w  d6,ad_RealPer(a4)
        or.w    d0,AudioDMA
TestEffects:
        move.b  ad_Effect(a4),d0                ; d0 = effet
        beq.s   UseInstrVol
        cmp.b   #MASTERVOL,d0
        beq.s   EffMaster
        cmp.b   #VOLUMEUP,d0
        beq.s   EffVolumeUp
        cmp.b   #VOLUMEDN,d0
        beq.s   EffVolumeDn
        cmp.b   #MASTERVOLUP,d0
        beq.s   EffMastVolUp
        cmp.b   #MASTERVOLDN,d0
        beq.s   EffMastVolDn
        bsr.s   EffVolume
        cmp.b   #TEMPO,d0
        beq.s   EffTempo
        cmp.b   #JUMPTOPOS,d0
        beq.s   EffJumpToPos
        cmp.b   #FILTER,d0
        beq.s   EffFilter
        cmp.b   #PITCHUP,d0
        beq.s   EffPitchUp
        cmp.b   #PITCHDN,d0
        beq.s   EffPitchDn
        cmp.b   #PITCHCTRL,d0
        beq.s   EffPitchCtrl
        cmp.b   #SPEED,d0
        beq.s   EffSpeed
        cmp.b   #LOOPSTART,d0
        beq.s   EffBlockLoopStart
        cmp.b   #JUMPTOLOOP,d0
        beq.s   EffBlockLoopJump
        cmp.b   #PORTAMENTOCTRL,d0
        beq.s   EffPortaCtrl
        rts


;**************************************************************************
;* Réglez le volume pour la piste courante.
;* ENTREE:
;*      a3 -> AudioRegs courant
;*      a4 -> ChannelData courant
;*      d6 = Période
;**************************************************************************

EffVolume:
        cmp.b   #VOLUME,d0
        bne.s   UseInstrVol
        moveq   #0,d2
        move.b  ad_Info(a4),d2          ; d0 = nouveau volume
        move.b  d2,ad_Volume(a4)
        bra.s   SetInstrVol
UseInstrVol:
        tst.w   d6
        beq.s   EffVolumeEnd
        move.b  ad_Volume(a4),d2
SetInstrVol:
        sub.w   MasterVolume,d2         ; nouveau volume - général >= 0
        bge.s   SetVolumeReg
        moveq   #0,d2                   ; nouveau volume = 0
SetVolumeReg:
        move.w  d2,AUDxVOL(a3)          ; écrire volume dans reg audio
EffVolumeEnd:
        rts


;**************************************************************************
;* Effet = MASTERVOL (4)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffMaster:                              ; modifier volume général
        moveq   #MAX_VOL,d2
        move.b  ad_Info(a4),d0
        sub.b   d0,d2
        blt.s   EffMasterEnd
        move.b  d2,MasterVolume+1
EffMasterEnd:
        bra.s   UseInstrVol


;**************************************************************************
;* Effet = TEMPO (5)
;* ENTREE:
;*      a4 -> ChannelData courant
;*      a6 = SongPointer
;**************************************************************************

EffTempo:                               ; modifier tempo
        move.b  ad_Info(a4),d0
        beq.s   EffTempoEnd
        clr.w   PlayTimer
        clr.w   ReplayTimer
        move.w  #3,ArpTimer
        move.b  d0,sng_Tempo(a6)
EffTempoEnd:
        rts


;**************************************************************************
;* Effet = JUMPTOPOS (6)
;* ENTREE:
;*      a4 -> ChannelData courant
;*      a6 = SongPointer
;**************************************************************************

EffJumpToPos:                           ; saut or position suivante
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        cmp.w   sng_Len(a6),d0          ; nouvelle pos > long. chanson ?
        bgt.s   EffJumpToPosEnd         ; oui, arrêter
        tst.w   d0                      ; BREAK
        beq.s   EffBreak
        cmp.w   #OLDBREAK,d0
        beq.s   EffBreak
        subq.b  #2,d0
        move.w  d0,NewPosJump
        bra.s   EffJumpSet
EffBreak:
        move.w  CurrentPos,NewPosJump
EffJumpSet:
        move.w  #1,BreakStatus
EffJumpToPosEnd:
        rts


;**************************************************************************
;* Effet = FILTER (7)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffFilter:                              ; filtre on/off
        move.b  ad_Info(a4),d0
        bne.s   SetFilter
        bset    #1,CIAAPRA
        rts
SetFilter:
        bclr    #1,CIAAPRA
        rts


;**************************************************************************
;* Effet = PITCHUP (8)
;* ENTREE:
;*      a3 -> AudioRegs courant
;*      a4 -> ChannelData courant
;**************************************************************************

EffPitchUp:
        tst.b   ad_Info(a4)
        beq.s   EffPitchUpEnd
        movem.l a0-a2,-(sp)
        move.l  a4,a0
        move.l  a3,a1
        move.w  ad_PitchCtrl(a0),-(sp)  ; sauver contrôles porta+ton
        clr.b   ad_PitchCtrl(a0)        ; effacer contrôle ton
        bsr.s   EffPitchSlideUp
        move.w  (sp)+,ad_PitchCtrl(a0)  ; restaurer contrôles porta+ton
        movem.l (sp)+,a0-a2
EffPitchUpEnd:
        rts


;**************************************************************************
;* Effet = PITCHDN (9)
;* ENTREE:
;*      a3 -> AudioRegs courant
;*      a4 -> ChannelData courant
;**************************************************************************

EffPitchDn:
        tst.b   ad_Info(a4)
        beq.s   EffPitchDnEnd
        movem.l a0-a2,-(sp)
        move.l  a4,a0
        move.l  a3,a1
        move.w  ad_PitchCtrl(a0),-(sp)  ; sauver contrôles porta+ton
        clr.b   ad_PitchCtrl(a0)        ; effacer contrôle ton
        bsr.s   EffPitchSlideDn
        move.w  (sp)+,ad_PitchCtrl(a0)  ; restaurer contrôles porta+ton
        movem.l (sp)+,a0-a2
EffPitchDnEnd:
        rts


;**************************************************************************
;* Effet = PITCHCTRL ($A)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffPitchCtrl:
        move.b  ad_Info(a4),ad_PitchCtrl(a4)
        rts

;**************************************************************************
;* Effet = SPEED ($B)
;* ENTREE:
;*      a4 -> ChannelData courant
;*      a6 = SongPointer
;**************************************************************************

EffSpeed:
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        cmp.w   #MIN_SPEED,d0           ; info >= 28 ?
        blt.s   EffSpeedEnd
        move.l  d1,-(sp)
        move.b  d0,sng_Speed(a6)
        move.l  TimerValue,d1
        divu    d0,d1                   ; d1 = valeurtimer/vitesse
        move.b  d1,CIABTBLO
        lsr.w   #8,d1
        move.b  d1,CIABTBHI
        clr.w   PlayTimer               ; RAZ compteurs
        clr.w   ReplayTimer
        move.w  #3,ArpTimer
        move.l  (sp)+,d1
EffSpeedEnd:
        rts


;**************************************************************************
;* Effet = VOLUMEUP ($C)
;* ENTREE:
;*      a3 -> AudioRegs courant
;*      a4 -> ChannelData courant
;**************************************************************************

EffVolumeUp:
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        beq.s   EffVolumeUpEnd
        move.l  a0,-(sp)
        move.l  a4,a0
        move.l  a3,a1
        bsr.s   EffVolSlideUpSet
        move.l  (sp)+,a0
EffVolumeUpEnd
        bra.s   UseInstrVol


;**************************************************************************
;* Effet = VOLUMEDN ($D)
;* ENTREE:
;*      a3 -> AudioRegs courant
;*      a4 -> ChannelData courant
;**************************************************************************

EffVolumeDn:
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        beq.s   EffVolumeDnEnd
        move.l  a0,-(sp)
        move.l  a4,a0
        move.l  a3,a1
        bsr.s   EffVolSlideDnSet
        move.l  (sp)+,a0
EffVolumeDnEnd:
        bra.s   UseInstrVol


;**************************************************************************
;* Effet = MASTERVOLUP ($10)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffMastVolUp:
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        beq.s   EffMastVolUpEnd
        movem.l d1/a0,-(sp)
        move.l  a4,a0
        bsr.s   EffMastVolSlideUpSet
        movem.l (sp)+,d1/a0
EffMastVolUpEnd:
        bra.s   UseInstrVol


;**************************************************************************
;* Effet = MASTERVOLDN ($11)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffMastVolDn:
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        beq.s   EffMastVolDnEnd
        movem.l d1/a0,-(sp)
        move.l  a4,a0
        bsr.s   EffMastVolSlideDnSet
        movem.l (sp)+,d1/a0
EffMastVolDnEnd:
        bra.s   UseInstrVol


;**************************************************************************
;* Effet = LOOPSTART ($14)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffBlockLoopStart:
        tst.b   ad_Info(a4)                     ; info == 0 => début bouclage
        bne.s   EffBlockLoopStartEnd
        move.w  OffsetBlock,ad_BlkLoopStart(a4) ; stocker position courante
EffBlockLoopStartEnd:
        rts


;**************************************************************************
;* Effet = JUMPTOLOOP ($15)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffBlockLoopJump:
        move.b  ad_Info(a4),d0
        beq.s   EffBlockLoopClear
        tst.w   ad_BlkLoopStart(a4)                 ; début boucle == -1 ?
        bmi.s   EffBlockLoopEnd
        tst.b   ad_BlkLoopCount(a4)                 ; compteur == 0 ?
        bne.s   DecBlockLoopCount
        move.b  d0,ad_BlkLoopCount(a4)              ; stocker info dans compteur
        bra.s   EffBlockLoopSet
DecBlockLoopCount:
        subq.b  #1,ad_BlkLoopCount(a4)              ; décrémenter compteur
        beq.s   EffBlockLoopClear                   ; compteur == 0 ?
EffBlockLoopSet:
        move.w  ad_BlkLoopStart(a4),NewOffsetBlock  ; nouvelle position
        sub.w   #NEXTLINE,NewOffsetBlock
        move.w  #1,BlockLoopStatus                  ; indicateur de bouclage
EffBlockLoopClear:
        move.w  #-1,ad_BlkLoopStart(a4)             ; début boucle = -1
EffBlockLoopEnd:
        rts


;**************************************************************************
;* Effet = INSTRSTART ($19)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffSampleOffset:
        move.l  d0,-(sp)
        moveq   #0,d0
        move.b  ad_Info(a4),d0
        beq.s   UseSampleOffset             ; si info == 0, utiliser ancien offset
        lsl.w   #7,d0
        move.w  d0,ad_SmpOffset(a4)
UseSampleOffset:
        move.w  ad_SmpOffset(a4),d0
        beq.s   EffSampleOffsetEnd
        cmp.w   ad_SmpLen(a4),d0            ; offset >= oneshot ?
        bge.s   OneWordLen                  ; oui => oneshot == 1
        sub.w   d0,ad_SmpLen(a4)            ; oneshot -= offset
        add.w   d0,d0                       ; offset en octets
        add.l   d0,ad_SmpAdr(a4)            ; début += offset
        bra.s   EffSampleOffsetEnd
OneWordLen:
        move.w  #1,ad_SmpLen(a4)
EffSampleOffsetEnd:
        move.l  (sp)+,d0
        rts


;**************************************************************************
;* Effet = INSTRTUNE ($1A)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffInstrTune:
        move.l  d0,-(sp)
        move.b  ad_Info(a4),d0
        and.b   #LONIB_MASK,d0
        move.b  d0,ad_Tune(a4)
        move.l  (sp)+,d0
        rts


;**************************************************************************
;* Effet = PORTAMENTOCTRL ($1E)
;* ENTREE:
;*      a4 -> ChannelData courant
;**************************************************************************

EffPortaCtrl:
        move.b  ad_Info(a4),ad_PortaCtrl(a4)
        rts


;**************************************************************************
;*
;*                     FIN DU CODE
;*
;**************************************************************************


;**************************************************************************
;*
;*                         DONNEES
;*
;**************************************************************************

                DSEG

CiabBase:       dc.l    0
IsNTSC:         dc.w    0

CiabName:       dc.b    'ciab.resource',0
TimerIntName:   dc.b    'Player Module DSS',0
                cnop    0,2

TimerInt:
        dc.l  0                 ; Interrupt.is_Node.ln_Succ
        dc.l  0                 ; Interrupt.is_Node.ln_Pred
        dc.b  2                 ; Interrupt.is_Node.ln_Type = NT_INTERRUPT
        dc.b  -10               ; Interrupt.is_Node.ln_Pri
        dc.l  TimerIntName      ; Interrupt.is_Node.ln_Name
        dc.l  0                 ; Interrupt.is_Data
        dc.l  TimerIntRoutine   ; Interrupt.is_Code


PlayOn:             dc.w    0
PlayTimer:          dc.w    0
CurrentPos:         dc.w    0
OffsetBlock:        dc.w    0
AudioDMA:           dc.w    0
MasterVolume:       dc.w    0
BreakStatus:        dc.w    0
NewPosJump:         dc.w    0
BlockLoopStatus:    dc.w    0
NewOffsetBlock:     dc.w    0
ReplayTimer:        dc.w    0
ArpTimer:           dc.w    3

ChannelData0:
        dc.w    0      ; Instr (5 bits) + Période (11 bits)   0
        dc.b    0      ; Effet                                1
        dc.b    0      ; Info                                 2
        dc.b    0      ; Accord fin                           4
        dc.b    0      ; Volume                               5
        dc.l    0      ; Adr échantillon                      6
        dc.w    0      ; Long. échantillon                    10
        dc.l    0      ; Adr répétition                       12
        dc.w    0      ; Long. répétition                     16
        dc.w    0      ; Période réelle                       18
        dc.w    1      ; bit DMA                              20
        dc.b    0      ; Contrôle Ton (0=off, 1=on)           22
        dc.b    0      ; Contrôle Portamento (0=off, 1=on)    23
        dc.w    -1     ; Début bouclage                       24
        dc.b    0      ; Décompte bouclage                    26
        dc.b    0      ;                                      27
        dc.w    0      ; Offset échantillon                   28
        dc.b    0      ; Direction Portamento                 30
        dc.b    0      ; Vitesse Portamento                   31
        dc.w    0      ; Période dest pour Portamento         32
ChannelData1:
        ds.w    10
        dc.w    2,0,-1,0,0,0,0
ChannelData2:
        ds.w    10
        dc.w    4,0,-1,0,0,0,0
ChannelData3:
        ds.w    10
        dc.w    8,0,-1,0,0,0,0

NoteTable:
; Accord Instr. = 0
        dc.w    1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
        dc.w    856,808,762,720,678,640,604,570,538,508,480,453
        dc.w    428,404,381,360,339,320,302,285,269,254,240,226
        dc.w    214,202,190,180,170,160,151,143,135,127,120,113
; Accord Instr. = 1
        dc.w    1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900
        dc.w    850,802,757,715,674,637,601,567,535,505,477,450
        dc.w    425,401,379,357,337,318,300,284,268,253,239,225
        dc.w    213,201,189,179,169,159,150,142,134,126,119,113
; Accord Instr. = 2
        dc.w    1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894
        dc.w    844,796,752,709,670,632,597,563,532,502,474,447
        dc.w    422,398,376,355,335,316,298,282,266,251,237,224
        dc.w    211,199,188,177,167,158,149,141,133,125,118,112
; Accord Instr. = 3
        dc.w    1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888
        dc.w    838,791,746,704,665,628,592,559,528,498,470,444
        dc.w    419,395,373,352,332,314,296,280,264,249,235,222
        dc.w    209,198,187,176,166,157,148,140,132,125,118,111
; Accord Instr. = 4
        dc.w    1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882
        dc.w    832,785,741,699,660,623,588,555,524,495,467,441
        dc.w    416,392,370,350,330,312,294,278,262,247,233,220
        dc.w    208,196,185,175,165,156,147,139,131,124,117,110
; Accord Instr. = 5
        dc.w    1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874
        dc.w    826,779,736,694,655,619,584,551,520,491,463,437
        dc.w    413,390,368,347,328,309,292,276,260,245,232,219
        dc.w    206,195,184,174,164,155,146,138,130,123,116,109
; Accord Instr. = 6
        dc.w    1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868
        dc.w    820,774,730,689,651,614,580,547,516,487,460,434
        dc.w    410,387,365,345,325,307,290,274,258,244,230,217
        dc.w    205,193,183,172,163,154,145,137,129,122,115,109
; Accord Instr. = 7
        dc.w    1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862
        dc.w    814,768,725,684,646,610,575,543,513,484,457,431
        dc.w    407,384,363,342,323,305,288,272,256,242,228,216
        dc.w    204,192,181,171,161,152,144,136,128,121,114,108
; Accord Instr. = 8 (-8)
        dc.w    1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960
        dc.w    907,856,808,762,720,678,640,604,570,538,508,480
        dc.w    453,428,404,381,360,339,320,302,285,269,254,240
        dc.w    226,214,202,190,180,170,160,151,143,135,127,120
; Accord Instr. = 9 (-7)
        dc.w    1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954
        dc.w    900,850,802,757,715,675,636,601,567,535,505,477
        dc.w    450,425,401,379,357,337,318,300,284,268,253,238
        dc.w    225,212,200,189,179,169,159,150,142,134,126,119
; Accord Instr. = 10 (-6)
        dc.w    1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948
        dc.w    894,844,796,752,709,670,632,597,563,532,502,474
        dc.w    447,422,398,376,355,335,316,298,282,266,251,237
        dc.w    223,211,199,188,177,167,158,149,141,133,125,118
; Accord Instr. = 11 (-5)
        dc.w    1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940
        dc.w    887,838,791,746,704,665,628,592,559,528,498,470
        dc.w    444,419,395,373,352,332,314,296,280,264,249,235
        dc.w    222,209,198,187,176,166,157,148,140,132,125,118
; Accord Instr. = 12 (-4)
        dc.w    1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934
        dc.w    881,832,785,741,699,660,623,588,555,524,494,467
        dc.w    441,416,392,370,350,330,312,294,278,262,247,233
        dc.w    220,208,196,185,175,165,156,147,139,131,123,117
; Accord Instr. = 13 (-3)
        dc.w    1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926
        dc.w    875,826,779,736,694,655,619,584,551,520,491,463
        dc.w    437,413,390,368,347,328,309,292,276,260,245,232
        dc.w    219,206,195,184,174,164,155,146,138,130,123,116
; Accord Instr. = 14 (-2)
        dc.w    1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920
        dc.w    868,820,774,730,689,651,614,580,547,516,487,460
        dc.w    434,410,387,365,345,325,307,290,274,258,244,230
        dc.w    217,205,193,183,172,163,154,145,137,129,122,115
; Accord Instr. = 15 (-1)
        dc.w    1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914
        dc.w    862,814,768,725,684,646,610,575,543,513,484,457
        dc.w    431,407,384,363,342,323,305,288,272,256,242,228
        dc.w    216,203,192,181,171,161,152,144,136,128,121,114

MaxMinNoteTable: dc.w    DO2,SI5            ; Accord Instr. = 0
                 dc.w    1700,113           ; Accord Instr. = 1
                 dc.w    1688,112           ; Accord Instr. = 2
                 dc.w    1676,111           ; Accord Instr. = 3
                 dc.w    1664,110           ; Accord Instr. = 4
                 dc.w    1652,109           ; Accord Instr. = 5
                 dc.w    1640,109           ; Accord Instr. = 6
                 dc.w    1628,108           ; Accord Instr. = 7
                 dc.w    1814,120           ; Accord Instr. = 8 (-8)
                 dc.w    1800,119           ; Accord Instr. = 9 (-7)
                 dc.w    1788,118           ; Accord Instr. = 10 (-6)
                 dc.w    1774,118           ; Accord Instr. = 11 (-5)
                 dc.w    1762,117           ; Accord Instr. = 12 (-4)
                 dc.w    1750,116           ; Accord Instr. = 13 (-3)
                 dc.w    1736,115           ; Accord Instr. = 14 (-2)
                 dc.w    1724,114           ; Accord Instr. = 15 (-1)

SampleAdrTable: ds.l    31

TimerValue:     dc.l    0
TimerTable:     dc.l    1773447,1789773

_SongPointer:   dc.l    0               ; adresse du Module en mémoire CHIP

;**************************************************************************
;*
;*                       FIN DES DONNEES
;*
;**************************************************************************


                END
