* MultiPlayer
* Copyright (C) 1992 Bryan Ford
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* I (the author of MultiPlayer) can be contacted on the Internet at
* "bryan.ford@m.cc.utah.edu".  See "Player.doc" for other addresses.
*
* Note:  This playroutine was not originally written by me.  In general
* these playroutines are public domain, so I am bringing the versions
* modified for MultiPlayer under the General Public License.  In the
* few cases of already-copyrighted playroutines, the above copyright
* notice applies only to the parts of the file written by me.
*
* $Id: med8play.asm,v 3.1 92/05/25 07:51:25 BAF Exp $
*

        include "exec/types.i"
        include "bry/macros.i"
        include "player.i"

        xref    modmem,dieerrplay,intsongpos,intrepeat,parseauthor,prepstring
        xref    ToUpper

        xdef    med_start8
        xref    med_getsongname,med_getauthorname,med_RelocModule

        code    text

;       mod8player.a - OctaMED V1.00 8-channel player routine
;       Written by Teijo Kinnunen

; the track-data structure definition:
 STRUCTURE      trk,0
        UBYTE   trk_prevnote    ;previous note number
        UBYTE   trk_previnstr   ;previous instrument number
        UBYTE   trk_prevvol     ;previous volume
        UBYTE   trk_prevmidich  ;previous MIDI channel
        UBYTE   trk_cmd         ;command (the 3rd number from right)
        UBYTE   trk_cmdqual     ;command qualifier (infobyte, databyte..)
        UBYTE   trk_prevmidin   ;previous MIDI note
        UBYTE   trk_noteoffcnt  ;note-off counter (hold)
        UBYTE   trk_inithold    ;default hold for this instrument
        UBYTE   trk_initdecay   ;default decay for....
        UBYTE   trk_stransp     ;instrument transpose
        UBYTE   trk_pad
        ULONG   trk_previnstra  ;address of the previous instrument data
;       the following data only on tracks 0 - 3
        UWORD   trk_prevper     ;previous period
        ULONG   trk_audioaddr   ;hardware audio channel base address
        ULONG   trk_sampleptr   ;pointer to sample
        UWORD   trk_samplelen   ;length (>> 1)
        UWORD   trk_porttrgper  ;portamento (cmd 3) target period
        UBYTE   trk_vibroffs    ;vibrato table offset
        UBYTE   trk_vibrspdsz   ;vibrato speed/size (cmd 4 qualifier)
        ULONG   trk_synthptr    ;pointer to synthetic/hybrid instrument
        UWORD   trk_arpgoffs    ;SYNTH: current arpeggio offset
        UWORD   trk_arpsoffs    ;SYNTH: arpeggio restart offset
        UBYTE   trk_volxcnt     ;SYNTH: volume execute counter
        UBYTE   trk_wfxcnt      ;SYNTH: waveform execute counter
        UWORD   trk_volcmd      ;SYNTH: volume command pointer
        UWORD   trk_wfcmd       ;SYNTH: waveform command pointer
        UBYTE   trk_volwait     ;SYNTH: counter for WAI (volume list)
        UBYTE   trk_wfwait      ;SYNTH: counter for WAI (waveform list)
        UWORD   trk_synthvibspd ;SYNTH: vibrato speed
        UWORD   trk_wfchgspd    ;SYNTH: period change
        UBYTE   trk_volchgspd   ;SYNTH: volume change
        UBYTE   trk_prevnote2   ;SYNTH: previous note
        UBYTE   trk_initvolxspd ;SYNTH: volume execute speed
        UBYTE   trk_initwfxspd  ;SYNTH: waveform execute speed
        UWORD   trk_perchg      ;SYNTH: curr. period change from trk_prevper
        UWORD   trk_synviboffs  ;SYNTH: vibrato pointer
        UWORD   trk_synvibdep   ;SYNTH: vibrato depth
        UBYTE   trk_synvol      ;SYNTH: current volume
        UBYTE   trk_synthtype   ;>0 = synth, -1 = hybrid, 0 = no synth
        ULONG   trk_periodtbl   ;pointer to period table
        UWORD   trk_prevportspd ;portamento (cmd 3) speed
        UBYTE   trk_decay       ;decay
        UBYTE   trk_fadespd     ;decay speed

ac_ptr  EQU     $00
ac_len  EQU     $04
ac_per  EQU     $06
ac_vol  EQU     $08
ac_end  EQU     $0C
ac_rest EQU     $10
T03SZ   EQU     72

                EVEN
sinetable:      dc.b    0,25,49,71,90,106,117,125,127,125,117,106,90,71,49
                dc.b    25,0,-25,-49,-71,-90,-106,-117,-125,-127,-125,-117
                dc.b    -106,-90,-71,-49,-25,0

nextblock:      dc.b    0
                even
numtracks:      dc.w    0
numlines:       dc.w    0
_counter:       dc.w    0

*_Wait1line:     move.l  d0,-(sp)
*                moveq   #$79,d0
*wl0:            move.b  $dff007,d1
*wl1:            cmp.b   $dff007,d1
*                beq.s   wl1
*                dbf     d0,wl0
*                move.l  (sp)+,d0
*                rts

pline:          dc.w    0
pblock:         dc.w    0
pseq:           dc.w    0

                even
getinsdata:     moveq   #0,d2
                move.w  4(a0),d0        ;Soitin-struct in a0
                bne.s   iff5or3oct      ;note # in d1 (0 - ...)
                move.l  a0,d0
                lea     _periods(pc),a0
                move.l  a0,trk_periodtbl(a5)
                lsl.b   #1,d1
                move.w  0(a0,d1.w),d5 ;put period to d5
                move.l  d0,a0
                addq.l  #6,d0           ;Skip structure
                move.l  (a0),d1         ;length
                move.w  (a3),d2
                move.w  2(a3),d3
                rts

iff5or3oct:     movem.l d6-d7,-(sp)
                moveq   #0,d7
                move.w  d1,d7
                divu    #12,d7  ;octave #
                move.l  d7,d5
                swap    d5      ;note number in this oct (0-11) is in d5
                move.l  (a0),d1
                cmp.b   #2,d0
                bne.s   no3oct
                addq.l  #6,d7
                divu    #7,d1   ;get length of the 1st octave
                bra.s   no5oct
no3oct:         divu    #31,d1  ;get length of the 1st octave (5 octaves)
no5oct:         move.l  d1,d0           ;d0 and d1 = length of the 1st oct
                move.w  (a3),d2
                move.w  2(a3),d3
                moveq   #0,d6
                move.b  shiftcnt(pc,d7.w),d6
                lsl.w   d6,d2
                lsl.w   d6,d3
                lsl.w   d6,d1
                move.b  mullencnt(pc,d7.w),d6
                mulu    d6,d0           ;offset of this oct from 1st oct
                add.l   a0,d0           ;add base address to offset
                addq.l  #6,d0           ;skip structure
                lea     _periods(pc),a1
                add.b   octstart(pc,d7.w),d5
                lsl.b   #1,d5
                move.w  0(a1,d5.w),d5
                movem.l (sp)+,d6-d7
                rts     ;returns period in d5
shiftcnt:       dc.b    4,3,2,1,1,0,2,2,1,1,0,0
mullencnt:      dc.b    15,7,3,1,1,0,3,3,1,1,0,0
octstart:       dc.b    12,12,12,12,24,24,0,12,12,24,24,36
                even

;**************************************************************************
;*
;*              8 CHANNEL PLAY ROUTINE
;*
;**************************************************************************

_ChannelO8:     cmp.b   #8,d0
                bge.s   xco8
                lea     trackdata8(pc),a1
                lsl.b   #2,d0
                adda.w  d0,a1
                movea.l (a1),a1
                movea.l trk_audioaddr(a1),a1
                clr.w   ac_per(a1)
xco8            rts

_PlayNote8:     ;d0(w) = trk #, d1 = note #, d2 = vol, d3(w) = instr # a3 = addr of instr
                movem.l d3-d7,-(sp) ;All right, let's start!!
                movea.l 24(a6),a0
                move.w  d3,d7
                add.w   d3,d3                   ;d3 = instr.num << 2
                add.w   d3,d3
                move.l  0(a0,d3.w),d5           ;get address of instrument
                beq.s   retsn28
inmem8:         add.b   766(a4),d1      ;add play transpose
                add.b   7(a3),d1        ;and instr. transpose
                tst.b   4(a3)
                bne.s   retsn28         ;MIDI
                cmp.w   #8,d0           ;track > 7???
                bge.s   retsn28         ;no Amiga instruments here!!!
                clr.b   trk_vibroffs(a5)        ;clr vibrato offset
                move.l  d5,a0
                subq.b  #1,d1
                tst.w   4(a0)
                bmi.s   retsn28         ;Synth
tlwtst08:       tst.b   d1
                bpl.s   notenot2low8
                add.b   #12,d1  ;note was too low, octave up
                bra.s   tlwtst08
notenot2low8:   cmp.b   #62,d1
                ble.s   endpttest8
                sub.b   #12,d1  ;note was too high, octave down
endpttest8:     move.w  d0,d4   ;d4 = track number
                bsr.w   getinsdata
                movea.l trk_audioaddr(a5),a1 ;base of this channel's regs
                add.w   d4,d4   ;d4 = trk << 1
                move.w  d4,d6
                add.w   d6,d6   ;d6 = trk << 2
                move.l  d0,(a1)         ;put it in ac_ptr
                cmp.w   #1,d3
                bhi.s   repeat8

                add.l   d1,d0
                subq.l  #1,d0
                move.l  d0,ac_end(a1)
                clr.l   ac_rest(a1)
                bra.s   retsn18

repeat8:        lsl.l   #1,d2           ;shift
                lsl.l   #1,d3
                add.l   d2,d0
                move.l  d0,ac_rest(a1)
                add.l   d3,d0
                subq.l  #1,d0
                move.l  d0,ac_end(a1)

retsn18:        move.w  d5,ac_per(a1)   ;getinsdata puts period to d5
                move.w  d5,trk_prevper(a5)
retsn28:        movem.l (sp)+,d3-d7
                rts

t038:           ds.b    18
                dc.l    track0hw
                ds.b    50+18
                dc.l    track1hw
                ds.b    50+18
                dc.l    track2hw
                ds.b    50+18
                dc.l    track3hw
                ds.b    50
t4158:          ds.b    18
                dc.l    track4hw
                ds.b    50+18
                dc.l    track5hw
                ds.b    50+18
                dc.l    track6hw
                ds.b    50+18
                dc.l    track7hw
                ds.b    50

trackdata8:     dc.l    t038,t038+T03SZ,t038+2*T03SZ,t038+3*T03SZ
                dc.l    t4158,t4158+T03SZ,t4158+2*T03SZ,t4158+3*T03SZ

_IntHandler8:   movem.l d2-d7/a2-a5,-(sp)
; ================ 8 channel handling (buffer swap) ======
                move.w  #800,d0
                not.b   whichbuff       ;swap buffer
                bne.s   usebuff1
                lea     _audiobuff,a1
                move.l  a1,$a0(a0)
                lea     800(a1),a5
                move.l  a5,$b0(a0)
                adda.w  d0,a5
                move.l  a5,$c0(a0)
                adda.w  d0,a5
                move.l  a5,$d0(a0)
                bra.s   buffset
usebuff1:       lea     _audiobuff+400,a1
                move.l  a1,$a0(a0)
                lea     800(a1),a5
                move.l  a5,$b0(a0)
                adda.w  d0,a5
                move.l  a5,$c0(a0)
                adda.w  d0,a5
                move.l  a5,$d0(a0)
buffset:        move.w  #1<<7,$9c(a0)   ;clear interrupt
; ============== fill buffers ============
                lea     track0hw(pc),a6
                bsr.s   fillbuf
                adda.w  #400,a1
                lea     track1hw(pc),a6
                bsr.s   fillbuf
                adda.w  #400,a1
                lea     track2hw(pc),a6
                bsr.s   fillbuf
                adda.w  #400,a1
                lea     track3hw(pc),a6
                bsr.s   fillbuf
                bra.w   do_play8

; =========================================================
;calculate channel A period
fillbuf:        move.l  #3719168,d7
                move.w  ac_per(a6),d6
                beq.s   setpzero0
                move.l  d7,d2
                divu    d6,d2
                moveq   #0,d1
                move.w  d2,d1
                add.l   d1,d1
                add.l   d1,d1
;get channel A addresses
                move.l  ac_end(a6),a5
                move.l  (a6),d0
                beq.s   setpzero0
chA_dfnd        move.l  d0,a3   ;a3 = start address, a5 = end address
;calc bytes before end
                mulu    #1600,d2
                clr.w   d2
                swap    d2
; d2 = # of bytes/fill
                add.l   a3,d2   ;d2 = end position after this fill
                sub.l   a5,d2   ;subtract sample end
                bmi.s   norestart0
                move.l  ac_rest(a6),d0
                beq.s   rst0end
                move.l  d0,(a6)
                move.l  d0,a3
                bra.s   norestart0
rst0end         clr.l   (a6)
setpzero0       lea     zerodata(pc),a3
                moveq   #0,d1
norestart0
;channel B period
                move.w  SIZE4TRKHW+ac_per(a6),d6
                beq.s   setpzero0b
                divu    d6,d7
                moveq   #0,d2
                move.w  d7,d2
                add.l   d2,d2
                add.l   d2,d2
;channel B addresses
                move.l  SIZE4TRKHW+ac_end(a6),a5
                move.l  SIZE4TRKHW(a6),d0
                beq.s   setpzero0b
                move.l  d0,a4
                mulu    #1600,d7
                clr.w   d7
                swap    d7
                add.l   a4,d7
                sub.l   a5,d7
                bmi.s   norestart0b
                move.l  SIZE4TRKHW+ac_rest(a6),d0
                beq.s   rst0endb
                move.l  d0,SIZE4TRKHW(a6)
                move.l  d0,a4
                bra.s   norestart0b
rst0endb        clr.l   SIZE4TRKHW(a6)
setpzero0b      lea     zerodata(pc),a4
                moveq   #0,d2
norestart0b     moveq   #0,d6
                moveq   #0,d7

combine         macro
                swap    d6
                swap    d7
                move.b  0(a3,d6.w),d0
                add.b   0(a4,d7.w),d0
                move.b  d0,(a1)+
                swap    d6
                swap    d7
                add.l   d1,d6
                add.l   d2,d7
                endm

                move.w  #400/8-1,d3
1$              combine
                combine
                combine
                combine
                combine
                combine
                combine
                combine
                dbra    d3,1$

                clr.w   d6
                clr.w   d7
                swap    d6
                swap    d7
                add.l   d6,(a6)
                add.l   d7,SIZE4TRKHW(a6)
                rts

; ========== player starts here...
do_play8        move.l  _module(pc),d0
                beq.w   plr_exit8
                move.l  d0,a6
                move.l  8(a6),a4
                moveq   #0,d3
                lea     _counter(pc),a0
                move.b  (a0),d3
                addq.b  #1,d3
                cmp.b   769(a4),d3
                bge.s   plr_pnewnote8   ;play new note
                move.b  d3,(a0)
                bra.w   nonewnote8      ;do just fx
; --- new note!! first get address of current block
plr_pnewnote8:  clr.b   (a0)
; --- now start to play it
                move.w  pblock(pc),d0
                movea.l 16(a6),a0
                lsl.w   #2,d0
                movea.l 0(a0,d0.w),a2   ;block...
                move.b  (a2)+,numtracks+1
                move.b  (a2)+,numlines+1
                move.w  pline(pc),d0
                move.w  d0,d1
                add.w   d0,d0   ;d0 * 2
                add.w   d1,d0   ;+ d0 = d0 * 3
                mulu    numtracks(pc),d0
                adda.w  d0,a2           ;address of current note
                moveq   #0,d7           ;number of track
                pea     trackdata8(pc)
plr_loop08:     moveq   #0,d5
                move.l  (sp),a1
                movea.l (a1)+,a5        ;get address of this track's struct
                move.l  a1,(sp)
                cmp.b   #8,d7
                bge.w   plr_endloop08
; ---------------- get the note numbers
                move.b  (a2)+,d5        ;get the number of this note
                move.b  (a2)+,d6        ;and the 4 numbers containing fx
                move.b  (a2)+,trk_cmdqual(a5)   ;get & save the fx numbers
; ---------------- clear some instrument # flags
                moveq   #0,d4           ;d4 is a flag: if set, instr. is
                moveq   #0,d3           ;in range G-V. If clr, it's 1-F.
; ---------------- and set them, if needed
                bclr    #7,d5           ;d3 is also a flag. If it's set,
                sne     d4              ;the instr. is in range 10 - 1V
                bclr    #6,d5
                sne     d3
; ---------------- check if there's an instrument number
                move.b  d6,d0
                and.w   #$f0,d0         ;d0 now contains only the # of instr
                bne.s   instnum8        ;instrument number is not 0
                tst.b   d4              ;maybe it's G (instr. #0, d4 set)
                bne.s   instnum8        ;yes, it really was G!!
                tst.b   d3
                beq.s   noinstnum8      ;it wasn't 10 - 1V either..
; ---------------- if there was, GET IT!!
instnum8:       lsr.b   #4,d0           ;shift it right to get number 0-F
                tst.b   d4
                beq.s   nogtov28
                add.w   #16,d0          ;if G-V, add 16 to the number
nogtov28:       tst.b   d3
                beq.s   no10to1v8
                add.w   #32,d0
; ---------------- finally, save the number
no10to1v8:      subq.b  #1,d0
                move.b  d0,trk_previnstr(a5) ;remember instr. number!
                lea     holdvals(pc),a0
                move.b  0(a0,d0.w),trk_inithold(a5) ;rem. hold
; ---------------- get the pointer of data's of this sample in Song-struct
                lsl.w   #3,d0
                lea     0(a4,d0.w),a3
                move.l  a3,trk_previnstra(a5)
; ---------------- set volume to 64
                movea.l trk_audioaddr(a5),a0
                movea.l ac_vol(a0),a0   ;ptr to volume hardware register
                moveq   #64,d1
                move.b  d1,(a0)
                move.b  d1,trk_prevvol(a5)
; ---------------- remember the some values of this instrument
                move.b  7(a3),trk_stransp(a5)   ;rem. transpose
                moveq   #0,d0
; ---------------- check the commands
noinstnum8:     move.b  d6,d0           ;effect again...
                and.b   #$0f,d0         ;now check only the effect part
                move.b  d0,trk_cmd(a5)  ;save the effect number
                beq.w   plr_nocmd8      ;no effect
                move.b  trk_cmdqual(a5),d6      ;get qualifier...
; ---------------- there was a command (effect), but which one??
                cmp.b   #$0f,d0         ;yes effect...is it Tempo???
                bne.s   not0f8          ;not Tempo
; ---------------- it was tempo (F)
                tst.b   d6              ;test effect qual..
                beq.s   fx0fchgblck8    ;if effect qualifier (last 2 #'s)..
                cmp.b   #$f0,d6         ;..is zero, go to next block
                bls.w   plr_nocmd8      ;01 - F0, not supported on 8 channels
; ---------------- no, it was FFx, something special will happen!!
fx0fspecial8:   cmp.b   #$f2,d6 ; | rest - play | SpecialFX#2: no note..yet
                bne.s   isfxfe8 ;not SpecFX2
; ---------------- it was FF2, nothing to do now
                move.b  d5,(a5) ;Yes!!! Save the note number
                moveq   #0,d5   ; clear the number for awhile
                bra.w   plr_nocmd8
isfxfe8:        cmp.b   #$fe,d6
                bne.s   notcmdfe8
; ---------------- it was FFE, stop playing
                bsr.w   _End8Play
                bra.w   plr_nocmd8
notcmdfe8:      cmp.b   #$fd,d6 ;change period
                bne.s   isfxff8
; ---------------- FFD, change the period, don't replay the note
                cmp.w   #8,d7 ;no tracks 8 - 15, thank you!!
                bge.w   plr_nocmd8
                movea.l trk_periodtbl(a5),a0    ;period table
                subq.b  #1,d5    ;sub 1 to make "real" note number
                bmi.w   plr_endloop08   ;under zero, do nothing
                add.b   d5,d5
                move.w  0(a0,d5.w),d0 ;get the period
                movea.l trk_audioaddr(a5),a0
                move.w  d0,ac_per(a0) ;push the period
                moveq   #0,d5 ;and clear it so that it won't be replayed
                bra.w   plr_nocmd8      ;done!!
isfxff8:        cmp.b   #$ff,d6         ;note off??
                bne.w   plr_nocmd8
                move.w  d7,d0
                bsr.w   _ChannelO8
                bra.w   plr_nocmd8
; ---------------- F00, called Pattern Break in ST
fx0fchgblck8:   addq.b  #1,nextblock    ;next block????...YES!!!! (F00)
                bra.w   plr_nocmd8
; ---------------- was not Fxx, then it's something else!!
not0f8:         cmp.b   #$0e,d0
                bne.s   not0e8
                move.b  d6,trk_wfcmd+1(a5) ;set waveform command position ptr
                bra.w   plr_nocmd8
not0e8:         cmp.b   #$0c,d0         ;new volume???
                bne.s   not0c8          ;NO!!!!!!!!!!!!!!!!!!!!!!
; ---------------- change volume
                move.b  d6,d0
                btst    #4,767(a4)      ;look at flags
                bne.s   volhex8
                lsr.b   #4,d0           ;get number from left
                mulu    #10,d0          ;number of tens
                move.b  d6,d1           ;get again
                and.b   #$0f,d1         ;this time don't get tens
                add.b   d1,d0           ;add them
volhex8         cmp.b   #64,d0
                bls.s   novolov648
                moveq   #64,d0
novolov648:     movea.l trk_audioaddr(a5),a0
                movea.l ac_vol(a0),a0
                move.b  d0,(a0)
                move.b  d0,trk_prevvol(a5)
                bra.s   plr_nocmd8
; ---------------- tempo2 change??
not0c8:         cmp.b   #$09,d0
                bne.s   not098
                and.b   #$1F,d6
                bne.s   fx9chk8
                moveq   #$20,d6
fx9chk8         move.b  d6,769(a4)
                bra.s   plr_nocmd8
; ---------------- note off time set??
not098:         cmp.b   #$08,d0
                bne.s   not088
                and.b   #$0f,d6         ; extract right only
                move.b  d6,trk_inithold(a5)     ;right = hold
                bra.s   plr_nocmd8
; ---------------- cmd Bxx, "position jump", like Goto, yäk!!
not088          cmp.b   #$0b,d0
                bne.s   not0b8
                move.w  d6,d0
                and.w   #$00ff,d0
                cmp.w   506(a4),d0      ;test the song length
                bhi.s   plr_nocmd8
                move.w  d0,pseq
                st      nextblock       ; = 1
                bra.s   plr_nocmd8
; ---------------- try portamento (3)
not0b8:         cmp.b   #$03,d0
                bne.s   plr_nocmd8
                subq.b  #1,d5           ;subtract note number
                bpl.s   plr_fx3note8    ;there's a note...
                tst.b   d6              ;qual??
                beq.s   plr_endloop08   ;0 -> do nothing
                bra.s   plr_setfx3spd8  ;not 0 -> set new speed
plr_fx3note8:   cmp.w   #8,d7
                bge.s   plr_endloop08   ;hey, what are you trying to do??
                movea.l trk_periodtbl(a5),a0
                add.b   766(a4),d5      ;play transpose
                add.b   trk_stransp(a5),d5 ;and instrument transpose
                bmi.s   plr_endloop08   ;again.. too low
                add.w   d5,d5
                move.w  0(a0,d5.w),trk_porttrgper(a5) ;period of this note is the target
plr_setfx3spd8: move.b  d6,trk_prevportspd(a5)  ;remember size
                moveq   #0,d5   ;don't play this one
; ---------------- everything is checked now: play or not to play??
plr_nocmd8:     tst.b   d5      ;Now we'll check if we have to play a note
                beq.s   plr_endloop08   ;no.
; ---------------- we decided to play
                move.b  d5,(a5)
                move.w  d7,d0
                move.w  d5,d1
                moveq   #0,d2
                moveq   #0,d3
                move.b  trk_previnstr(a5),d3    ;instr #
                movea.l trk_previnstra(a5),a3   ;instr data address
; ---------------- does this instrument have holding??
                move.b  trk_inithold(a5),trk_noteoffcnt(a5) ;initialize hold
                bne.s   plr_holdok8     ;not 0 -> OK
                st      trk_noteoffcnt(a5)      ;0 -> hold = 0xff (-1)
; ---------------- and finally:
plr_holdok8:
                bsr     _PlayNote8      ;play it!!!!!!!!!!!
; ---------------- end of loop: handle next track, or quit
plr_endloop08:  addq.b  #1,d7
                cmp.w   numtracks(pc),d7
                blt.w   plr_loop08
                addq.l  #4,sp           ;trackdata8

; and advance song pointers
                lea     pline(pc),a3
                move.w  (a3),d1         ;pline
                addq.w  #1,d1           ;very important!!! advance line!!
                cmp.w   numlines(pc),d1 ;important too!!! advance block??
                bgt.s   plr_chgblock8   ;yes!!!
                tst.b   nextblock       ;command F00 ??
                beq.s   plr_nochgblock8 ;no, don't change block
plr_chgblock8:  moveq   #0,d1           ;clear line number
                move.w  4(a3),d0        ;get play sequence number
                tst.b   nextblock
                bmi.s   plr_noadvseq8   ;Bxx sets nextblock to 0xff (= neg)
                addq.w  #1,d0           ;advance sequence number
plr_noadvseq8:  cmp.w   506(a4),d0      ;is this the highest seq number??
                blt.s   plr_notagain8   ;no.
                moveq   #0,d0           ;yes: play song again
                moveq   #0,d1                   ;...forever!!!
plr_notagain8:  move.b  d0,5(a3)        ;remember new playseq-#
                lea     508(a4),a0      ;offset of sequence table
                move.b  0(a0,d0.w),d0   ;get number of the block
                cmp.b   505(a4),d0      ;beyond last block??
                blt.s   plr_nolstblk8   ;no..
                moveq   #0,d0           ;play block 0
plr_nolstblk8:  move.b  d0,3(a3)        ;store pblock
plr_nonewseq8:  clr.b   nextblock       ;clear this if F00 set it
plr_nochgblock8 move.w  d1,(a3)         ;set new pline
                movea.l 16(a6),a0
                move.w  2(a3),d0        ;pblock
                lsl.w   #2,d0
                movea.l 0(a0,d0.w),a2   ;block...
                move.b  (a2),d7         ;# of tracks
                move.w  (a3),d0         ;play line
                move.w  d0,d1
                add.w   d0,d0   ;d0 * 2
                add.w   d1,d0   ;+ d0 = d0 * 3
                mulu    d7,d0
                lea     2(a2,d0.w),a2
                move.b  769(a4),d3      ;interrupts/note
                lea     trackdata8(pc),a0
                subq.b  #1,d7
plr_chkhold8:   movea.l (a0)+,a1        ;track data
                tst.b   7(a1)           ;hold??
                bmi.s   plr_holdend8    ;no.
                move.b  (a2),d1         ;get the 1st byte..
                bne.s   plr_hold18
                move.b  1(a2),d1
                and.b   #$f0,d1
                beq.s   plr_holdend8    ;don't hold
                bra.s   plr_hold28
plr_hold18:     and.b   #$3f,d1         ;note??
                beq.s   plr_hold28      ;no, cont hold..
                move.b  1(a2),d1
                and.b   #$0f,d1         ;get cmd
                subq.b  #3,d1           ;is there command 3 (slide)
                bne.s   plr_holdend8    ;no -> end holding
plr_hold28:     add.b   d3,7(a1)        ;continue holding...
plr_holdend8:   addq.l  #3,a2           ;next note
                dbf     d7,plr_chkhold8
                btst    #5,767(a4)      ;FLAG_STSLIDE??
                bne.w   plr_endfx8      ;yes, no effects this time...
                moveq   #0,d3           ;counter = 0!!!
nonewnote8:

;       *********************** This code produces the effects **
                moveq   #0,d7   ;clear track count
                moveq   #0,d6
                lea     trackdata8(pc),a2
plr_loop18:     movea.l (a2)+,a5
                moveq   #0,d5
                moveq   #0,d4
                move.b  trk_cmd(a5),d6  ;get the fx number
                move.b  trk_cmdqual(a5),d4      ;and the last 2 #'s
                cmp.w   #8,d7
                bge.w   endl8   ;no non-MIDI effects in tracks 8 - 15
                tst.b   trk_noteoffcnt(a5)
                bmi.s   plr_nowaitoff8
                subq.b  #1,trk_noteoffcnt(a5)
                bpl.s   plr_nowaitoff8
                movea.l trk_audioaddr(a5),a1
                clr.w   ac_per(a1)      ;if ac_per is 0, sound'll be stopped
plr_nowaitoff8: add.b   d6,d6   ;* 2
                move.w  fx_table8(pc,d6.w),d0
                jmp     fxs8(pc,d0.w)
fx_table8:      dc.w    fx_008-fxs8,fx_018-fxs8,fx_028-fxs8,fx_038-fxs8,fx_048-fxs8
                dc.w    fx_058-fxs8,fx_xx8-fxs8,fx_xx8-fxs8,fx_xx8-fxs8,fx_xx8-fxs8
                dc.w    fx_0a8-fxs8,fx_xx8-fxs8,fx_xx8-fxs8,fx_0d8-fxs8,fx_xx8-fxs8
                dc.w    fx_0f8-fxs8
fxs8:
;       **************************************** Effect 01 ******
fx_018:         sub.w   d4,trk_prevper(a5)      ;slide it up!!!
                move.w  trk_prevper(a5),d5
                cmp.w   #113,d5         ;too high???
                bge     newvals8
                move.w  #113,d5         ;yes, too high!!!
                move.w  d5,trk_prevper(a5)
                bra     newvals8
;       **************************************** Effect 02 ******
fx_028:         add.w   d4,trk_prevper(a5)      ;slide it down!!!!!!!!!
                move.w  trk_prevper(a5),d5
                bra.w   newvals8
;       **************************************** Effect 00 ******
fx_008:         tst.b   d4      ;both fxqualifiers are 0s: no arpeggio!!
                beq.w   endl8
                move.l  d3,d0
                divu    #3,d0
                swap    d0
                tst.w   d0
                bne.s   fx_arp128
                and.b   #$0f,d4
                add.b   (a5),d4
                bra.s   fx_doarp8
fx_arp128:      subq.b  #1,d0
                bne.s   fx_arp28
                lsr.b   #4,d4
                add.b   (a5),d4
                bra.s   fx_doarp8
fx_arp28:       move.b  (a5),d4
fx_doarp8:      subq.b  #1,d4           ;-1 to make it 0 - 127
                add.b   766(a4),d4      ;add play transpose
                add.b   trk_stransp(a5),d4      ;add instrument transpose
                lsl.b   #1,d4           ;shift to make index for UWORD
                movea.l trk_periodtbl(a5),a1
                move.w  0(a1,d4.w),d5
                bra.w   newvals8
;       **************************************** Effect 0D/0A ***
fx_0a8:
fx_0d8:         move.b  d4,d1
                move.b  trk_prevvol(a5),d0      ;move previous vol to d0
                and.b   #$f0,d1
                bne.s   crescendo8
                sub.b   d4,d0   ;sub from prev. vol
                bpl.s   novolund08
                moveq   #0,d0   ;volumes under zero not accepted!!!
novolund08:     move.b  d0,trk_prevvol(a5)      ;put new vol back
                bra.s   setvc8
crescendo8:     lsr.b   #4,d1
                add.b   d1,d0
                cmp.b   #64,d0
                ble.s   novolover648
                moveq   #64,d0
novolover648:   move.b  d0,trk_prevvol(a5)
setvc8          movea.l trk_audioaddr(a5),a0
                movea.l ac_vol(a0),a0
                move.b  d0,(a0)
                bra.w   fx_xx8
;       **************************************** Effect 05 ******
fx_058:         move.w  trk_prevper(a5),d5 ;this is very simple: get the old period
                cmp.b   #3,d3           ;and..
                bge.w   newvals8                ;if counter < 3
                sub.w   d4,d5   ;subtract effect qualifier
                bra.w   newvals8
;       **************************************** Effect 03 ******
fx_038:         move.w  trk_porttrgper(a5),d0   ;d0 = target period
                beq.w   newvals8        ;no target period specified
                move.w  trk_prevper(a5),d1      ;d1 = curr. period
                move.b  trk_prevportspd(a5),d4  ;get prev. speed
                cmp.w   d0,d1
                bhi.s   subper8 ;curr. period > target period
                add.w   d4,d1   ;add the period
                cmp.w   d0,d1
                bge.s   targreached8
                bra.s   targnreach8
subper8:        sub.w   d4,d1   ;subtract
                cmp.w   d0,d1   ;compare current period to target period
                bhi.s   targnreach8
targreached8:   move.w  trk_porttrgper(a5),d1 ;eventually push target period
                clr.w   trk_porttrgper(a5) ;now we can forget everything
targnreach8:    move.w  d1,trk_prevper(a5)
                move.w  d1,d5
                bra.w   newvals8
;       **************************************** Effect 04 ******
fx_048:         tst.b   d4
                beq.s   nonvib8
                move.b  d4,trk_vibrspdsz(a5)
nonvib8:        move.b  trk_vibroffs(a5),d0
                lsr.b   #2,d0
                and.w   #$1f,d0
                moveq   #0,d1
                lea     sinetable(pc),a0
                move.b  0(a0,d0.w),d5
                ext.w   d5
                move.b  trk_vibrspdsz(a5),d0
                and.w   #$000f,d0
                muls    d0,d5
                asr.w   #5,d5
                add.w   trk_prevper(a5),d5
                move.b  trk_vibrspdsz(a5),d0
                lsr.b   #3,d0
                and.b   #$3e,d0
                add.b   d0,trk_vibroffs(a5)
                bra.w   newvals8
;       **************************************** Effect 0F ******
fx_0f8:         cmp.b   #$f1,d4
                bne.s   no0ff18
                cmp.b   #3,d3
                bne.w   endl8
                bra.s   playfxnote8
no0ff18:        cmp.b   #$f2,d4
                bne.s   no0ff28
                cmp.b   #3,d3
                bne.w   endl8
                bra.s   playfxnote8
no0ff28:        cmp.b   #$f3,d4
                bne.s   no0ff38
                move.b  d3,d0
                and.b   #2+4,d0         ;is 2 or 4
                beq.w   endl8
playfxnote8:    move.w  d7,d0           ;track # to d0...
                moveq   #0,d1
                move.b  (a5),d1         ;get note # of previous note
                move.w  d3,-(sp)
                moveq   #0,d3
                move.b  trk_previnstr(a5),d3    ;and prev. sample #
                movea.l trk_previnstra(a5),a3
                bsr     _PlayNote8
                move.w  (sp)+,d3
                bra.s   endl8
no0ff38:        cmp.b   #$f8,d4         ;f8 = filter off
                beq.s   plr_filteroff8
                cmp.b   #$f9,d4         ;f9 = filter on
                bne.s   endl8
                bclr    #1,$bfe001
                bra.s   plr_xff38
plr_filteroff8: bset    #1,$bfe001
plr_xff38:      bra.s   endl8
;       *********************************************************
newvals8        movea.l trk_audioaddr(a5),a1    ;get channel address
                tst.w   d5      ;now: do the effects!!!
                beq.s   fx_xx8
plr_newper8:    move.w  d5,ac_per(a1)   ;push period
fx_xx8:
endl8:          addq.b  #1,d7   ;increment channel number
                cmp.w   numtracks(pc),d7        ;all channels done???
                blt.w   plr_loop18      ;not yet!!!
plr_endfx8:
plr_exit8:      movem.l (sp)+,d2-d7/a2-a5
                rts

zerodata        dc.w    0

whichbuff:      dc.w    0

track0hw:       dc.l    0,0,$dff0a9,0,0
track1hw:       dc.l    0,0,$dff0b9,0,0
track2hw:       dc.l    0,0,$dff0c9,0,0
track3hw:       dc.l    0,0,$dff0d9,0,0
track4hw:       dc.l    0,0,$dff0a9,0,0
track5hw:       dc.l    0,0,$dff0b9,0,0
track6hw:       dc.l    0,0,$dff0c9,0,0
track7hw:       dc.l    0,0,$dff0d9,0,0
SIZE4TRKHW      equ     4*$14

*audintname:     dc.b    'OctaMED AudioInterrupt',0
*                even
*audiointerrupt: dc.w    0,0,0,0,0
*                dc.l    audintname,_audiobuff,_IntHandler8
*prevaud:        dc.l    0
play8:          dc.b    0
*eightrkon:      dc.b    0
                even
*_Init8chan:     move.l  a6,-(sp)
*                move.w  #1<<7,$dff09a
*                moveq   #7,d0           ;Audio channel 0 interrupt
*                lea     audiointerrupt(pc),a1
*                move.l  4,a6
*                jsr     -$a2(a6)        ;SetIntVector()
*                move.l  d0,prevaud
*                st      eightrkon
*                move.l  (sp)+,a6
*                rts
*
*_Rem8chan:      move.l  a6,-(sp)
*                move.b  eightrkon(pc),d0
*                beq.s   no8init
*                clr.b   eightrkon
*                move.w  #1<<7,$dff09a
*                moveq   #7,d0
*                move.l  prevaud(pc),a1
*                move.l  4,a6
*                jsr     -$a2(a6)
*no8init         move.l  (sp)+,a6
*                rts

_Start8Play:    ;d1 = pstate
                lea     _audiobuff,a0
                move.w  #799,d1
clrbuffloop:    clr.l   (a0)+           ;clear track buffers
                dbf     d1,clrbuffloop
                lea     $dff000,a0
                move.w  #227,d1
                move.w  d1,$a6(a0)
                move.w  d1,$b6(a0)
                move.w  d1,$c6(a0)
                move.w  d1,$d6(a0)
                move.w  #200,d1
                move.w  d1,$a4(a0)
                move.w  d1,$b4(a0)
                move.w  d1,$c4(a0)
                move.w  d1,$d4(a0)
                move.l  #_audiobuff,$a0(a0)
                move.l  #_audiobuff+800,$b0(a0)
                move.l  #_audiobuff+1600,$c0(a0)
                move.l  #_audiobuff+2400,$d0(a0)
                moveq   #64,d1
                move.w  d1,$a8(a0)
                move.w  d1,$b8(a0)
                move.w  d1,$c8(a0)
                move.w  d1,$d8(a0)
                clr.b   whichbuff
*                move.w  #$4000,$9a(a0)
                lea     track0hw,a1
                moveq   #7,d1
clrtrkloop      clr.l   (a1)
                clr.w   ac_per(a1)
                adda.w  #$14,a1
                dbf     d1,clrtrkloop
*                move.w  #$F,$dff096     ;audio DMA off
*                bsr.w   _Wait1line      ;wait until all stopped
*                st      play8
*                move.w  #$c080,$9a(a0)
                move.w  #$800F,$96(a0)
                rts

_End8Play:      move.b  play8(pc),d0
                beq.s   noend8play
*                move.w  #1<<7,$dff09a
                move.w  #$F,$dff096
                clr.b   play8
noend8play      rts

*_AudioInit:     movem.l a4/a6/d2-d3,-(sp)
*                moveq   #0,d2
*                movea.l 4,a6
*;       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ alloc signal bit
*                addq.l  #1,d2
*                moveq   #-1,d0
*                jsr     -$14a(a6)       ;AllocSignal()
*                tst.b   d0
*                bmi.w   initerr
*                move.b  d0,sigbitnum
*;       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ prepare IORequest
*                lea     allocport(pc),a1
*                move.b  d0,15(a1)       ;set mp_SigBit
*                move.l  a1,-(sp)
*                suba.l  a1,a1
*                jsr     -$126(a6)       ;FindTask(0)
*                move.l  (sp)+,a1
*                move.l  d0,16(a1)       ;set mp_SigTask
*                lea     reqlist(pc),a0
*                move.l  a0,(a0)         ;NEWLIST begins...
*                addq.l  #4,(a0)
*                clr.l   4(a0)
*                move.l  a0,8(a0)        ;NEWLIST ends...
*;       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ open audio.device
*                addq.l  #1,d2
*                lea     allocreq(pc),a1
*                lea     audiodevname(pc),a0
*                moveq   #0,d0
*                moveq   #0,d1
*                movea.l 4,a6
*                jsr     -$1bc(a6)       ;OpenDevice()
*                tst.b   d0
*                bne.w   initerr
*                st.b    audiodevopen
*;       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ open ciaa.resource
*                bsr.w   _Init8chan
*                moveq   #0,d0
*initret:        movem.l (sp)+,a4/a6/d2-d3
*                rts
*initerr:        move.l  d2,d0
*                bra.s   initret
*
*_AudioRem:      move.l  a6,-(sp)
*                bsr.w   _Rem8chan
*                movea.l 4,a6
*                tst.b   audiodevopen
*                beq.s   rem2
*                move.w  #$000f,$dff096  ;stop audio DMA
*;       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ close audio.device
*                lea     allocreq(pc),a1
*                jsr     -$1c2(a6)       ;CloseDevice()
*rem2:           moveq   #0,d0
*                move.b  sigbitnum(pc),d0
*                bmi.s   rem3
*;       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ free signal bit
*                jsr     -$150(a6)       ;FreeSignal()
*rem3:           move.l  (sp)+,a6
*                rts
; *************************************************************************
; *************************************************************************
; ***********          P U B L I C   F U N C T I O N S          ***********
; *************************************************************************
; *************************************************************************

; *************************************************************************
; InitModule8(a0 = module) -- extract expansion data etc.. from V3.00 module
; *************************************************************************

_InitModule8:   movem.l a2-a3/d2,-(sp)
                move.l  a0,d0
                beq.s   IM_exit         ;0 => xit
                lea     holdvals(pc),a2
                move.l  32(a0),d0       ;expdata...
                beq.s   IM_clrhlddec    ;none here
                move.l  d0,a1
                move.l  4(a1),d0        ;exp_smp
                beq.s   IM_clrhlddec    ;again.. nothing
                move.l  d0,a0           ;InstrExt...
                move.w  8(a1),d2        ;# of entries
                beq.s   IM_clrhlddec
                subq.w  #1,d2           ;- 1 (for dbf)
                move.w  10(a1),d0       ;entry size
IM_loop1        move.b  (a0),(a2)+      ;InstrExt.hold -> holdvals
                adda.w  d0,a0           ;ptr to next InstrExt
                dbf     d2,IM_loop1
                bra.s   IM_exit
IM_clrhlddec    moveq   #62,d0          ;no InstrExt => clear holdvals/decays
IM_loop2        clr.w   (a2)+
                dbf     d0,IM_loop2
IM_exit         movem.l (sp)+,a2-a3/d2
                rts
; *************************************************************************
; InitPlayer8() -- allocate interrupt, audio, serial port etc...
; *************************************************************************
*_InitPlayer8:   bsr.w   _AudioInit
*                tst.l   d0
*                bne.s   IP_error
*                moveq   #0,d0
*                rts
*IP_error        bsr.w   _RemPlayer8
*                moveq   #-1,d0
*                rts
; *************************************************************************
; RemPlayer8() -- free interrupt, audio, serial port etc..
; *************************************************************************
_RemPlayer8:    bsr.s   _StopPlayer8
*                bsr.w   _AudioRem
PM_end          rts
; *************************************************************************
; PlayModule(a0 = module)  -- initialize & play it!!
; PlayModule2(a0 = module) -- play module (must be initialized)
; *************************************************************************
_PlayModule8:   move.l  a0,-(sp)
                bsr     _InitModule8
                move.l  (sp)+,a0
_PlayModule82:
*                move.b  audiodevopen(pc),d0
*                beq.s   PM_end          ;resource allocation failure
                move.l  a0,d0
                beq.s   PM_end          ;module failure
                clr.l   _module
                move.l  a0,-(sp)
                movea.l 8(a0),a0        ;song
                move.b  769(a0),_counter        ;init counter
                btst    #0,767(a0)
                bne.s   PM_filon
                bset    #1,$bfe001
                bra.s   PM_filset
PM_filon        bclr    #1,$bfe001
PM_filset       moveq   #0,d1
                move.b  508(a0),d1      ;get first playseq entry
                lea     pline(pc),a0    ;clear pline etc..
                move.l  d1,(a0)+
                clr.w   (a0)
                move.l  (sp)+,_module
                bra.w   _Start8Play

; *************************************************************************
; StopPlayer() -- stop music
; *************************************************************************
_StopPlayer8:   clr.l   _module
                bra.w   _End8Play
; *************************************************************************


*audiodevopen    dc.b    0
*sigbitnum       dc.b    -1
*allocmsk        dc.b    $0f,$00
*                even
*allocport       dc.l    0,0     ;succ, pred
*                dc.b    4,0     ;NT_MSGPORT
*                dc.l    0       ;name
*                dc.b    0,0     ;flags = PA_SIGNAL
*                dc.l    0       ;task
*reqlist         dc.l    0,0,0   ;list head, tail and tailpred
*                dc.b    5,0
*allocreq        dc.l    0,0
*                dc.b    5,127   ;NT_MESSAGE, use maximum priority (127)
*                dc.l    0,allocport     ;name, replyport
*                dc.w    68              ;length
*                dc.l    0       ;io_Device
*                dc.l    0       ;io_Unit
*                dc.w    0       ;io_Command
*                dc.b    0,0     ;io_Flags, io_Error
*                dc.w    0       ;ioa_AllocKey
*                dc.l    allocmsk ;ioa_Data
*                dc.l    1       ;ioa_Length
*                dc.w    0,0,0   ;ioa_Period, Volume, Cycles
*                dc.w    0,0,0,0,0,0,0,0,0,0     ;ioa_WriteMsg
*audiodevname    dc.b    'audio.device',0

                even
_periods:       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
                dc.w 214,202,190,180,170,160,151,143,135,127,120,113
                dc.w 214,202,190,180,170,160,151,143,135,127,120,113
                dc.w 214,202,190,180,170,160,151,143,135,127,120,113

_module         dc.l    0
holdvals:       ds.b 63

*** Entrypoint from 4-channel player

        ds.w    0

med_start8:
        plstartret 9$

2$      move.l  modmem(a4),a0           ; Relocate the module
        bsr     med_RelocModule

        cq      d0
        rts

3$      move.l  modmem(a4),a0           ; Start playing the module
        bra     _PlayModule8

4$      lea     name(pc),a0             ; GetMakerName
        move.l  a0,d0
        rts

        dc.l    gmod_Jump
9$      gmodbra 2$                      ; InitMusic
        gmodbra 3$                      ; StartMusic
        gmodbra _StopPlayer8            ; StopMusic
        gmodbra _StopPlayer8            ; EndMusic
        gmodnop
        gmodnop
        gmodnop
        gmodnop
        gmodbra _IntHandler8            ; Channel 0 interrupt
        gmodnop
        gmodnop
        gmodnop
        gmodnop                         ; OctaMED 1.00 only supports one song
        gmodbra med_getsongname         ; GetSongName
        gmodbra med_getauthorname       ; GetAuthorName
        gmodnop
        gmodnop
        gmodbra 4$                      ; GetMakerName
        move.l  #GMODHF_SEQUENCE!GMODHF_REPEAT,d0       ; Hook
        rts

name            dc.b    "OctaMED 1.00",0

                bss     "chip",chip

_audiobuff:     ds.b    400*8

                end
