Dosseg
;
; Quick 'n' Dirty play    for Surprise! Adlib Tracker SA2 files (version 9)
;
.Model Small
.Stack 40H
.286
locals

.Data

NoAdlib         db 7,"No Adlib found!$"

.Code

include memtools.blk

locals
.286

fname db 130 dup (0)
fsong dw ?

Okt1 equ 0010000000000000B
Okt2 equ 0010010000000000B
Okt3 equ 0010100000000000B
Okt4 equ 0010110000000000B
Okt5 equ 0011000000000000B
Okt6 equ 0011010000000000B
Okt7 equ 0011100000000000B
Okt8 equ 0011110000000000B

_patternorder   equ 966
_songlength     equ 1096
_restart        equ 1097
_bpm            equ 1098
_arpeggio       equ 1100
_acommands      equ 1356
_trackorder     equ 1612
_channels       equ 2188
_trackdata      equ 2190


Adwrite macro r,w
        mov     dx,388h
        mov     al,r
        out     dx,al
        call    waitlong
        mov     dx,389h
        mov     al,w
        out     dx,al
endm

NoteTab dw 0                                                         ; dummy note
        dw Okt1+343,Okt1+363,Okt1+385,Okt1+408,Okt1+432,Okt1+458
        dw Okt1+485,Okt1+514,Okt1+544,Okt1+577,Okt1+611,Okt1+647     ; Oktave3
        dw Okt2+343,Okt2+363,Okt2+385,Okt2+408,Okt2+432,Okt2+458
        dw Okt2+485,Okt2+514,Okt2+544,Okt2+577,Okt2+611,Okt2+647     ; Oktave3
        dw Okt3+343,Okt3+363,Okt3+385,Okt3+408,Okt3+432,Okt3+458
        dw Okt3+485,Okt3+514,Okt3+544,Okt3+577,Okt3+611,Okt3+647     ; Oktave3
        dw Okt4+343,Okt4+363,Okt4+385,Okt4+408,Okt4+432,Okt4+458
        dw Okt4+485,Okt4+514,Okt4+544,Okt4+577,Okt4+611,Okt4+647     ; Oktave4
        dw Okt5+343,Okt5+363,Okt5+385,Okt5+408,Okt5+432,Okt5+458
        dw Okt5+485,Okt5+514,Okt5+544,Okt5+577,Okt5+611,Okt5+647     ; Oktave5
        dw Okt6+343,Okt6+363,Okt6+385,Okt6+408,Okt6+432,Okt6+458
        dw Okt6+485,Okt6+514,Okt6+544,Okt6+577,Okt6+611,Okt6+647     ; Oktave6
        dw Okt7+343,Okt7+363,Okt7+385,Okt7+408,Okt7+432,Okt7+458
        dw Okt7+485,Okt7+514,Okt7+544,Okt7+577,Okt7+611,Okt7+647     ; Oktave7
        dw Okt8+343,Okt8+363,Okt8+385,Okt8+408,Okt8+432,Okt8+458
        dw Okt8+485,Okt8+514,Okt8+544,Okt8+577,Okt8+611,Okt8+647;    ; Oktave8
        dw 16 dup (Okt8+647)

InsTab  dw 32 dup (0)

Register_map db 0,1,2,8,9,10,16,17,18

Vibratotab db    0, 24, 49, 74, 97,120,141,161
           db  180,197,212,224,235,244,250,253
           db  255,253,250,244,235,224,212,197
           db  180,161,141,120, 97, 74, 49, 24

arpeggio_table  db 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1

channel_active  db 9 dup (0)
channel_ins     db 9 dup (0)
channel_note    db 9 dup (0)
channel_note2   db 9 dup (0)
channel_vol2    db 9 dup (0)    ; connector
channel_vol     db 9 dup (0)    ; carrier
channel_vpos    db 9 dup (0)
channel_vspeed  db 9 dup (0)
channel_vdepth  db 9 dup (0)
channel_eff     dw 9 dup (0)
channel_freq    dw 9 dup (0)
channel_fshift  dw 9 dup (0)
channel_pdest   dw 9 dup (0)
channel_pspeed  dw 9 dup (0)
channel_aspeed  db 9 dup (0)
channel_acurpos db 9 dup (0)
channel_acursp  db 9 dup (0)
channel_vsubt   db 9 dup (0)
channel_segment db 9 dup (0)

mainvol         db 64           ; 64 = max, 0 = min
count           db 0
Register_bypass db 246 dup (0)
current_pos     db 9 dup (0)
current_speed   db 9 dup (6)
s_count         db 9 dup (0)
current_line    db 9 dup (0)
work_pattern    dw 9 dup (_trackorder)
position_jump   db 9 dup (0)
new_position    db 9 dup (0)
break_pos       db 9 dup (0)
pattern_break   db 9 dup (0)
polling         db 1
segments        dw 9 dup (0)
stopped         db 9 dup (1)

segnum          dw 0
current_bpm     dw 0

waitlong proc
;
; waits 23.3 ys
;
        rept 35
        in      al,dx
        endm
        ret
waitlong endp


Fastwrite proc
        cmp     cs:Register_bypass[bx],ah
        je      f_w_1

        mov     cs:Register_bypass[bx],ah

        mov     dx,388h
        mov     al,bl
        out     dx,al
        call    waitlong
        mov     dx,389h
        mov     al,ah
        out     dx,al
f_w_1:
        ret
Fastwrite endp


WriteThrough proc
        mov     cs:Register_bypass[bx],ah

        mov     dx,388h
        mov     al,bl
        out     dx,al
        call    waitlong
        mov     dx,389h
        mov     al,ah
        out     dx,al
        ret
WriteThrough endp

empty proc
        ret
empty endp

general_ret:
        sti
        ret

TestAdlib proc
;
; Checks for adlib
; carry flag:
;         1...........no Adlib detected
;         0...........Adlib found (yeah!)
;
        Adwrite 1,0
        Adwrite 4,60H
        Adwrite 4,80H

        mov     dx,388h
        in      al,dx
        and     al,11100000b
        mov     bl,al

        Adwrite 2,0ffH
        Adwrite 4,00100001b

        mov     cx,6
t_a_1:
        call    waitlong
        loop    t_a_1

        mov     dx,388h
        in      al,dx
        and     al,11100000b
        mov     bh,al

        Adwrite 4,60H
        Adwrite 4,80H

        cmp     bl,0
        jne     t_a_2
        cmp     bh,11000000b
        jne     t_a_2

        clc
        ret
t_a_2:
        stc
        mov     ax,4
        ret
TestAdlib endp

Init_Adlib proc
        mov     bx,1
        mov     cx,245
I_a_1:
        mov     ah,0
        call    WriteThrough
        inc     bx
        loop    I_a_1

        mov     bx,1
        mov     ah,00100000b
        call    WriteThrough
        mov     bx,8
        mov     ah,01000000B
        call    WriteThrough
        mov     bx,0bdh
        mov     ah,11000000B
        call    WriteThrough

        lea     bx,InsTab
        mov     ax,5
        mov     cx,31
I_a_3:
        mov     cs:[bx],ax
        add     bx,2
        add     ax,15
        loop    I_a_3


        mov     mainvol,64

        clc
        ret
Init_Adlib endp

Init_Adlib_Tables proc
;
; Initialisiert Tabellen, etc.
;
        mov     si,0
        mov     bx,0
        mov     cx,9
@@l1:
        mov     stopped         [si],1
        mov     channel_active  [si],0
        mov     channel_ins     [si],0
        mov     channel_note    [si],0
        mov     channel_note2   [si],0
        mov     channel_vol     [si],0
        mov     channel_vol2    [si],0
        mov     channel_vpos    [si],0
        mov     channel_vspeed  [si],0
        mov     channel_vdepth  [si],0
        mov     channel_eff     [bx],0
        mov     channel_freq    [bx],0
        mov     channel_fshift  [bx],0
        mov     channel_pdest   [bx],0
        mov     channel_pspeed  [bx],0
        mov     channel_aspeed  [si],0
        mov     channel_acurpos [si],0
        mov     channel_acursp  [si],0
        mov     channel_vsubt   [si],0
        mov     current_speed   [si],6
        mov     current_line    [si],0
        mov     break_pos       [si],0
        mov     pattern_break   [si],0
        mov     position_jump   [si],0
        inc     si
        add     bx,2
        jumps
        loop    @@l1
        nojumps
        mov     mainvol,64
        ret
Init_Adlib_Tables endp

DefineBoth proc
        add     bl,cl
        lodsw
        mov     ch,ah
        mov     ah,al
        call    Fastwrite               ; Connector
        add     bl,3
        mov     ah,ch
        call    Fastwrite               ; Carrier
        ret
DefineBoth endp

CalcVol proc
        sub     al,63
        neg     al
        mov     dl,mainvol
        mul     dl
        shr     ax,6
        sub     al,63
        neg     al
        ret
calcvol endp

Set_Volume proc
        mov     di,bx
        mov     bx,40h
        add     bl,Register_map[di]     ; bl = KSL / TL offset

        mov     cx,ax

        mov     al,ch
        and     al,63
        mov     channel_vol2[di],al
        call    calcvol
        mov     ah,al
        mov     al,register_bypass[bx]
        and     al,11000000b
        or      ah,al
        call    Fastwrite

s_v_1:
        add     bl,3

        mov     al,cl
        and     al,63
        mov     channel_vol[di],al
        call    calcvol
        mov     ah,al
        mov     al,register_bypass[bx]
        and     al,11000000b
        or      ah,al
        jmp     Fastwrite
Set_Volume endp

calc_freq_up proc
        mov     dx,cx
        mov     di,cx
        and     dx,0011110000000000b
        and     cx,1023
        add     cx,ax
        cmp     cx,647
        jl      @@fu_ok
        mov     ax,di
@@fu_2:
        and     ax,0001110000000000b
        cmp     ax,0001110000000000b
        jne     @@next_okt
        mov     cx,di
        jmp     @@fu_ok
@@next_okt:
        sar     cx,1
        add     ax,0000010000000000b
        and     di,0010000000000000b
        or      ax,di
        mov     dx,ax
        cmp     cx,647
        jg      @@fu_2
@@fu_ok:
        or      cx,dx
        ret
calc_freq_up endp

calc_freq_down proc
        mov     dx,cx
        mov     di,cx
        and     dx,0011110000000000b
        and     cx,1023
        sub     cx,ax
        cmp     cx,343
        jg      @@fd_ok
        mov     ax,di
@@fd_2:
        and     ax,0001110000000000b
        cmp     ax,0
        jne     @@prev_okt
        mov     cx,di
        jmp     @@fd_ok
@@prev_okt:
        sal     cx,1
        sar     ah,2
        dec     ah
        sal     ah,2
        and     di,0010000000000000b
        or      ax,di
        mov     dx,ax
        cmp     cx,343
        jl      @@fd_2
@@fd_ok:
        or      cx,dx
        ret
calc_freq_down endp

Frequenz_up2 proc
        shl     bp,1
        mov     cx,cs:channel_freq[bp]
        shr     bp,1
        call    calc_freq_up
        add     bx,0a0h
        mov     ah,cl
        call    Fastwrite
        add     bx,10h
        mov     ah,register_bypass[bx]
        and     ah,32
        and     ch,31
        or      ah,ch
        call    Fastwrite
        ret
Frequenz_up2 endp

Frequenz_down2 proc
        shl     bp,1
        mov     cx,cs:channel_freq[bp]
        shr     bp,1
        add     bx,0a0h
        call    calc_freq_down
        mov     ah,cl
        call    Fastwrite
        add     bx,10h
        mov     ah,register_bypass[bx]
        and     ah,32
        and     ch,31
        or      ah,ch
        call    Fastwrite
        ret
Frequenz_down2 endp

Release_note proc
        add     bx,0b0h
        mov     ah,register_bypass[bx]
        test    ah,32
        jz      @@released
        and     ah,255-32
        jmp     WriteThrough
@@released:
        ret
release_note endp

Retrig_note proc
        add     bx,0b0h
        mov     ah,register_bypass[bx]
        test    ah,32
        jnz     @@still_on
        or      ah,32
        call    WriteThrough
@@released:
        ret
@@still_on:
        mov     bx,bp
        call    release_note
        mov     bx,bp
        jmp     retrig_note
retrig_note endp;

Play_Note2 proc
        pusha
        call    release_note


        sub     bx,10h
        xor     ch,ch
        mov     si,cx
        shl     si,1
        add     si,offset NoteTab
        mov     cx,cs:[si]
        mov     ah,cl
        call    Fastwrite

        add     bx,10h
        mov     ah,ch
        call    WriteThrough

        mov     bx,bp
        shl     bx,1
        mov     channel_freq[bx],cx
        mov     channel_fshift[bx],0

        popa
        ret
play_note2 endp

Set_Freq proc
        add     bx,0a0h

        mov     ah,cl
        call    Fastwrite

        add     bx,10h
        mov     ah,register_bypass[bx]
        and     ah,32
        and     ch,31
        or      ah,ch
        call    FastWrite

        mov     bx,bp
        shl     bx,1
        mov     channel_freq[bx],cx
        ret
Set_Freq endp

Init_Instrument2 proc
        pusha

        mov     bl,ch
        dec     bl
        xor     bh,bh
        shl     bx,1
        mov     si,cs:instab[bx]

;--- spec. arpeggio init ----------------------------------

        mov     bx,si

        add     si,11
        lodsb
        mov     cs:channel_acurpos[bp],al
        lodsb
        cmp     al,0
        jne     @@alok
        inc     al
@@alok:
        mov     cs:channel_acursp[bp],0
        mov     cs:channel_aspeed[bp],al

@@no_spec:
        mov     si,bx

;----------------------------------------------------------


        mov     bx,bp

        mov     cl,bl

        mov     bx,0c0h
        add     bl,cl
        lodsb
        mov     ah,al
        mov     ch,al
        call    Fastwrite

        mov     bx,bp
        add     bx,offset Register_map
        mov     cl,cs:[bx]

        mov     bx,20h
        call    DefineBoth

        mov     bx,60h
        call    DefineBoth

        mov     bx,80h
        call    DefineBoth

        mov     bx,0e0h
        call    DefineBoth

        mov     bx,40h
        add     bl,cl

        lodsw
        mov     cx,ax

        mov     di,bp
        mov     al,byte ptr channel_vol2[di]
        mov     ah,byte ptr channel_vol[di]
        and     ax,0011111100111111b
        and     cx,1100000011000000b
        or      ax,cx
        or      cx,ax

        and     al,63
        call    calcvol
        mov     ah,al
        and     cl,11000000b
        or      ah,cl
        call    Fastwrite

        add     bl,3

        mov     al,ch
        and     al,63
        call    calcvol
        mov     ah,al
        and     ch,11000000b
        or      ah,ch
        call    Fastwrite

        popa
        ret
@@seperate_vol:
        ret
Init_Instrument2 endp

select_wave proc
        mov     bl,register_map[bx]
        xor     bh,bh
        add     bx,0e0h
        mov     ah,cl
        dec     ah
        js      @@none1
        call    fastwrite
@@none1:
        add     bx,3
        mov     ah,ch
        dec     ah
        js      @@none2
        jmp     fastwrite
@@none2:
        ret
select_wave endp

Mute_voice proc
        push    bx
        mov     dl,register_map[bx]
        mov     cl,dl
        add     dl,60h
        mov     bl,dl
        mov     di,bx
        mov     ah,0ffh
        call    fastwrite
        mov     bx,di
        add     bx,3
        mov     ah,0ffh
        call    fastwrite
        mov     bx,di
        add     bx,20h
        mov     ah,0ffh
        call    fastwrite
        mov     bx,di
        add     bx,23h
        mov     ah,0ffh
        call    fastwrite
        pop     bx
        add     bx,0b0h
        mov     di,bx
        mov     ah,032
        call    fastwrite
        mov     bl,cl
        add     bx,40h
        mov     ah,register_bypass[bx]
        or      ah,63
        call    fastwrite
        mov     ah,0
        mov     bx,di
        jmp     fastwrite
m_v:
        ret
Mute_voice endp

Clear_All_Voices proc
        mov     cx,9
        mov     bp,0
c_a_v_2:
        push    cx
        mov     bx,bp
        call    mute_voice
        inc     bp
        pop     cx
        loop    c_a_v_2

        ret
Clear_All_Voices endp

Set_Main_Volume proc
        cmp     bl,64
        jbe     s_m_v_ok
        mov     ax,0
        stc
        ret
s_m_v_ok:
        cmp     mainvol,bl
        je      s_m_v_end
        mov     mainvol,bl
        mov     si,0
        mov     cx,9
s_m_v_1:
        cmp     channel_active[si],0
        je      s_m_v_2

        mov     di,si
        add     di,offset channel_vol2

        mov     bx,40h
        add     bl,register_map[si]

        mov     al,cs:[di]
        call    calcvol
        mov     ah,al
        mov     al,register_bypass[bx]
        and     al,11000000b
        or      ah,al
        call    Fastwrite

        add     bl,3
        add     di,9

        mov     al,cs:[di]
        call    calcvol
        mov     ah,al
        mov     al,register_bypass[bx]
        and     al,11000000b
        or      ah,al
        call    Fastwrite

s_m_v_2:
        inc     si
        loop    s_m_v_1
s_m_v_end:
        ret
Set_Main_Volume endp

shift_helper proc
        mov     bx,bp
        cmp     ax,0
        jl      @@down
        call    calc_freq_up
        jmp     @@doit
@@down:
        neg     ax
        call    calc_freq_down
@@doit:
        add     bx,0a0h
        mov     ah,cl
        call    Fastwrite
        add     bx,10h
        mov     al,register_bypass[bx]
        and     al,32
        mov     ah,ch
        and     ah,11011111b
        or      ah,al
        call    Fastwrite

        mov     bx,bp
        shl     bx,1
        mov     channel_freq[bx],cx
        ret
shift_helper endp

effect_arpeggio proc
        mov     cl,cs:channel_note2[bp]
        mov     bl,byte ptr count
        xor     bh,bh
        mov     bl,byte ptr arpeggio_table[bx]
        cmp     bl,0
        je      @@arp_found
        cmp     bl,1
        jne     @@arp2
        shr     ah,4
        add     cl,ah
        jmp     @@arp_found
@@arp2:
        and     ah,15
        add     cl,ah
@@arp_found:
        cmp     cl,96
        jbe     @@note_ok
        mov     cl,96
@@note_ok:
        xor     ch,ch
        mov     bx,cx
        shl     bx,1
        mov     cx,word ptr notetab[bx]
        mov     bx,bp
        call    set_freq
        ret
effect_arpeggio endp

effect_slide_up proc
        mov     bx,bp
        shl     bx,1
        mov     al,ah
        xor     ah,ah
        add     word ptr channel_fshift[bx],ax
        mov     ax,word ptr channel_fshift[bx]
        mov     bl,byte ptr cs:channel_note2[bp]
        xor     bh,bh
        shl     bx,1
        mov     cx,word ptr notetab[bx]

        call    shift_helper
        ret
effect_slide_up endp

effect_slide_down proc
        mov     bx,bp
        shl     bx,1
        mov     al,ah
        xor     ah,ah
        sub     word ptr channel_fshift[bx],ax
        mov     ax,word ptr channel_fshift[bx]
        mov     bl,byte ptr cs:channel_note2[bp]
        xor     bh,bh
        shl     bx,1
        mov     cx,word ptr notetab[bx]

        call    shift_helper
        ret
effect_slide_down endp

effect_portamento proc
        cmp     count,0
        ja      @@not_first
        mov     bx,bp
        shl     bx,1
        cmp     ah,0
        je      @@no_new_speed
        mov     al,ah
        xor     ah,ah
        mov     word ptr channel_pspeed[bx],ax
@@no_new_speed:
        mov     bl,byte ptr cs:channel_note[bp]
        cmp     bl,0
        je      @@not_first
        xor     bh,bh
        shl     bx,1
        mov     dx,word ptr notetab[bx]
        mov     bx,bp
        shl     bx,1
        mov     word ptr channel_pdest[bx],dx
@@not_first:
        jmp     execute_portamento
effect_portamento endp

execute_portamento proc
        mov     bx,bp
        add     bx,0a0h
        mov     al,byte ptr register_bypass[bx]
        add     bx,10h
        mov     ah,byte ptr register_bypass[bx]

        mov     bx,bp
        shl     bx,1
        mov     dx,word ptr channel_pdest[bx]
        mov     cx,word ptr channel_pspeed[bx]
        cmp     ax,dx
        jumps
        je      @@kill_porta
        ja      @@pdown
@@up:
        nojumps
        mov     bx,bp
        mov     ax,cx
        push    dx
        add     bx,0b0h
        mov     ch,byte ptr register_bypass[bx]
        sub     bx,10h
        mov     cl,byte ptr register_bypass[bx]
        call    calc_freq_up
        pop     dx
        mov     bx,cx
        mov     ax,dx
        and     bx,0011100000000000b
        and     ax,0011100000000000b
        cmp     bx,ax
        jb      @@puok
        mov     bx,cx
        mov     ax,dx
        and     bx,0000011111111111b
        and     ax,0000011111111111b
        cmp     bx,ax
        jb      @@puok
        mov     cx,dx
@@puok:
        mov     bx,bp
        call    set_freq
        ret
@@pdown:
        mov     bx,bp
        mov     ax,cx
        push    dx
        add     bx,0b0h
        mov     ch,byte ptr register_bypass[bx]
        sub     bx,10h
        mov     cl,byte ptr register_bypass[bx]
        call    calc_freq_down
        pop     dx
        mov     bx,cx
        mov     ax,dx
        and     bx,0011100000000000b
        and     ax,0011100000000000b
        cmp     bx,ax
        ja      @@pdok
        mov     bx,cx
        mov     ax,dx
        and     bx,0000011111111111b
        and     ax,0000011111111111b
        cmp     bx,ax
        ja      @@pdok
        mov     cx,dx
@@pdok:
        mov     bx,bp
        call    set_freq
@@kill_porta:
        ret
execute_portamento endp

effect_vibrato proc
        cmp     count,0
        ja      @@not_first
        cmp     ah,0
        je      @@not_first
        mov     al,ah
        and     al,15
        shr     ah,4
        mov     cs:channel_vspeed[bp],ah
        mov     cs:channel_vdepth[bp],al
        mov     dl,ah
        jmp     do_vibrato
@@not_first:
        jmp     execute_vibrato
effect_vibrato endp

execute_vibrato proc
        mov     dl,cs:channel_vspeed[bp]
        mov     al,cs:channel_vdepth[bp]
do_vibrato:
        mov     bl,cs:channel_vpos[bp]
        xor     bh,bh
        add     bl,dl
        mov     cs:channel_vpos[bp],bl
        mov     dh,bl
        and     bx,1fh
        mov     dl,byte ptr vibratotab[bx]
        mul     dl
        rol     ax,1
        xchg    ah,al
        and     ah,1
        test    dh,32
        jne     @@vibup
        jmp     @@vibdown
@@vibup:
        mov     bx,bp
        call    frequenz_up2
        ret
@@vibdown:
        mov     bx,bp
        call    frequenz_down2
        ret
execute_vibrato endp

effect_volume_slide proc
        cmp     count,0
        jne     @@just_vslide
        mov     dl,ah
        cmp     dl,16
        jb      @@dlok
        shr     dl,4
        neg     dl
@@dlok:
        mov     bx,bp
        shl     bx,1
        mov     byte ptr channel_eff[bx+1],dl
        mov     ah,dl
        mov     byte ptr cs:channel_vsubt[bp],0
@@just_vslide:
        mov     dl,ah
        mov     dh,byte ptr cs:channel_vsubt[bp]
        add     dh,dl
        mov     ch,dh
        sar     ch,2
        mov     ah,byte ptr cs:channel_vol2[bp]
        mov     al,byte ptr cs:channel_vol[bp]
        add     ah,ch
        add     al,ch
        cmp     al,0
        jge     @@alok
        xor     al,al
@@alok:
        cmp     ah,0
        jge     @@ahok
        xor     ah,ah
@@ahok:
        cmp     ah,63
        jb      @@ahok2
        mov     ah,63
@@ahok2:
        cmp     al,63
        jb      @@alok2
        mov     al,63
@@alok2:
        sal     ch,2
        sub     dh,ch
        mov     byte ptr cs:channel_vsubt[bp],dh
        mov     bx,bp
        call    set_volume
        ret
effect_volume_slide endp

effect_portamento_volume_slide proc
        call    effect_volume_slide
        call    execute_portamento
        ret
effect_portamento_volume_slide endp

effect_vibrato_volume_slide proc
        call    effect_volume_slide
        call    execute_vibrato
        ret
effect_vibrato_volume_slide endp

effect_release_note proc
        mov     bx,bp
        call    release_note
        mov     bx,bp
        shl     bx,1
        mov     word ptr channel_eff[bx],0
        ret
effect_release_note endp

effect_position_jump proc
        mov     bx,segnum
        mov     position_jump[bx],1
        mov     al,ah
        xor     ah,ah
        mov     cs:new_position[bx],al
        mov     cs:break_pos[bx],0
        mov     cs:pattern_break[bx],1
        mov     bx,bp
        shl     bx,1
        mov     word ptr channel_eff[bx],0
        ret
effect_position_jump endp

effect_set_volume proc
        cmp     ah,63
        jb      @@ahok
        mov     ah,63
@@ahok:
        mov     al,63
        sub     al,ah
        mov     ah,al
        mov     bx,bp
        call    set_volume
        ret
effect_set_volume endp

effect_pattern_break proc
        mov     bx,segnum
        mov     cs:pattern_break[bx],1
        mov     bx,ax
        mov     al,ah
        shr     al,4
        mov     cl,10
        mul     cl
        and     bh,0fh
        add     al,bh
        xor     ah,ah
        mov     cs:break_pos[bx],al
        mov     bx,bp
        shl     bx,1
        mov     word ptr channel_eff[bx],0
        ret
effect_pattern_break endp

effect_set_speed proc
        mov     bx,segnum
        cmp     ah,0
        ja      @@ahok
        mov     ah,1
@@ahok:
        cmp     ah,31
        ja      @@high_speed
        mov     current_speed[bx],ah
        jmp     @@aout
@@high_speed:
        cmp     polling,1
        je      @@aout
        mov     bl,ah
        xor     bh,bh
        cmp     bx,50
        ja      @@bxok
        mov     bx,50
@@bxok:
;        mov     current_bpm,bx
;        add     bx,3

        cli
        mov     dx,2dh
        mov     ax,8426h
        div     bx
        push    ax
        mov     al,54
        out     43h,al

        pop     ax
        out     40h,al
        xchg    ah,al
        out     40h,al
        sti
@@aout:
        mov     bx,bp
        shl     bx,1
        mov     word ptr channel_eff[bx],0
        ret
effect_set_speed endp

spec_arpeggio proc
        pusha
        cmp     byte ptr cs:channel_acurpos[bp],0
        jumps
        je      @@no_arp
        dec     byte ptr cs:channel_acursp[bp]
        jg      @@no_arp
        nojumps

        mov     al,byte ptr cs:channel_acurpos[bp]
        xor     ah,ah
        mov     di,ax
        mov     al,byte ptr ds:[_acommands+di]
        cmp     al,254
        jb      @@no_verw
        cmp     al,255
        jne     @@no_end
        mov     byte ptr cs:channel_acurpos[bp],0
        jmp     @@no_arp
@@no_end:
        mov     al,byte ptr ds:[_arpeggio+di]
        mov     di,ax
        mov     byte ptr cs:channel_acurpos[bp],al
        mov     al,byte ptr ds:[_acommands+di]
@@no_verw:

        cmp     al,0
        je      @@no_command
        cmp     al,44
        ja      @@no_wave
        xor     ah,ah
        mov     bl,10
        div     bl
        xchg    al,ah
        mov     cx,ax
        mov     bx,bp
        call    select_wave
        jmp     @@no_command
@@no_wave:
        cmp     al,253
        jne     @@no_release
        mov     bx,bp
        call    release_note
@@no_release:
        cmp     al,252
        jne     @@no_setvol
        mov     ah,byte ptr ds:[_arpeggio+di]
        call    effect_set_volume
@@no_setvol:
        cmp     al,251
        jne     @@no_retrig_vol
        mov     ah,byte ptr ds:[_arpeggio+di]
        call    effect_set_volume
        mov     bx,bp
        call    retrig_note
        jmp     @@no_half
@@no_retrig_vol:

@@no_command:
        mov     al,byte ptr ds:[_arpeggio+di]
        cmp     al,100
        ja      @@abs_note_found
        mov     ah,byte ptr cs:channel_note[bp]
        add     al,ah
        jmp     @@note_check
@@abs_note_found:
        sub     al,99
@@note_check:
        cmp     al,96
        jbe     @@note_found
        mov     al,96
@@note_found:
        xor     ah,ah
        mov     byte ptr cs:channel_note2[bp],al
        mov     bx,ax
        shl     bx,1
        mov     cx,word ptr notetab[bx]

        mov     bx,bp
        shl     bx,1
        mov     ax,word ptr channel_fshift[bx]

        call    shift_helper

@@no_half:
        mov     al,byte ptr cs:channel_aspeed[bp]
        mov     byte ptr cs:channel_acursp[bp],al
        inc     byte ptr cs:channel_acurpos[bp]

@@no_arp:
        popa
        ret
spec_arpeggio endp

do_volume proc
        pusha
        mov     bl,al
        dec     bl
        xor     bh,bh
        shl     bx,1
        mov     si,word ptr InsTab[bx]
        add     si,9
        mov     al,[si]
        mov     ah,al
        mov     al,[si+1]
        and     ax,3F3FH
        mov     bx,bp
        call    set_volume
dov2:
        popa
        ret
do_volume endp

calc_work_pattern proc
        xor     ah,ah
        mov     bp,ax
        add     bp,_patternorder
        mov     al,ds:[bp]
        mov     dx,ax
        shl     dx,3
        add     ax,dx
        add     ax,_trackorder
        mov     work_pattern[bx],ax
        ret
calc_work_pattern endp

effect_calls    dw offset effect_arpeggio                       ; 0
                dw offset effect_slide_up                       ; 1
                dw offset effect_slide_down                     ; 2
                dw offset effect_portamento                     ; 3
                dw offset effect_vibrato                        ; 4
                dw offset effect_portamento_volume_slide        ; 5
                dw offset effect_vibrato_volume_slide           ; 6
                dw offset empty                                 ; 7
                dw offset effect_release_note                   ; 8
                dw offset empty                                 ; 9
                dw offset effect_volume_slide                   ; A
                dw offset effect_position_jump                  ; B
                dw offset effect_set_volume                     ; C
                dw offset effect_pattern_break                  ; D
                dw offset empty                                 ; E
                dw offset effect_set_speed                      ; F
                

_eff  db 0
_para db 0

play_line proc

        mov     cx,9
        mov     bp,0
@@l1:
        push    cx

        cmp     byte ptr cs:channel_active[bp],1
        je      @@play
        jmp     @@no_effect

@@play:
        mov     bl,cs:channel_segment[bp]
        xor     bh,bh
        mov     segnum,bx
        mov     al,s_count[bx]
        mov     count,al
        shl     bx,1
        mov     dx,cs:segments[bx]
        mov     ds,dx
        shr     bx,1
        cmp     al,0
        jumps
        jne     @@effects
        nojumps
        xor     ah,ah
        mov     al,current_line[bx]
        mov     si,ax
        shl     si,1
        add     si,ax
        shl     bx,1
        mov     bx,work_pattern[bx]
        add     bx,bp
        mov     al,[bx]
        cmp     al,0
        jumps
        je      @@no_effect
        nojumps
        dec     ax
        mov     bx,192
        mul     bx
        add     si,ax
        add     si,_trackdata

        cmp     count,0
        jbe     @@do_note
        jmp     @@effects
@@do_note:
        lodsb
        mov     cl,al
        lodsb
        mov     ah,al
        lodsb

        mov     _para,al
        mov     ch,ah
        and     ch,15
        mov     _eff,ch
        and     ah,0f0h
        shr     cl,1
        rcr     ah,4
        mov     al,ah

        mov     ch,al
        jcxz    @@nothing_there
        cmp     al,0
        je      @@no_ins
        mov     byte ptr cs:channel_ins[bp],al
        call    do_volume
@@no_ins:
        cmp     cl,0
        je      @@nothing_there
        mov     byte ptr cs:channel_note[bp],cl
        mov     byte ptr cs:channel_note2[bp],cl
        cmp     _eff,3
        je      @@nothing_there
        mov     ch,byte ptr cs:channel_ins[bp]
        call    init_instrument2
        mov     bx,bp
        mov     byte ptr cs:channel_vpos[bp],0
        call    play_note2

@@nothing_there:
        mov     al,_eff
        mov     ah,_para
        mov     bx,bp
        shl     bx,1
        mov     word ptr channel_eff[bx],ax
@@effects:

        call    spec_arpeggio

        mov     bx,bp
        shl     bx,1
        mov     ax,word ptr channel_eff[bx]
        mov     bx,ax
        cmp     bx,0
        je      @@no_effect
        xor     bh,bh
        shl     bx,1
        mov     bx,effect_calls[bx]
        call    bx

@@no_effect:
        inc     bp
        pop     cx
        jumps
        loop    @@l1
        nojumps

        mov     cx,9
        xor     bx,bx
        xor     si,si
@@l2:
        mov     ax,segments[bx]
        cmp     ax,0
        je      @@fuck_off
        cmp     stopped[si],1
        je      @@fuck_off
        mov     ds,ax
        inc     s_count[si]
        mov     al,current_speed[si]
        cmp     s_count[si],al
        jl      @@no_advance
        mov     s_count[si],0
        inc     current_line[si]
        cmp     current_line[si],64
        je      @@advance
        cmp     pattern_break[si],1
        je      @@advance
        jmp     @@no_advance
@@advance:
        inc     current_pos[si]
        mov     al,current_pos[si]
        cmp     al,ds:[_songlength]
        jb      @@norestart
        mov     al,ds:[_restart]
@@norestart:
        cmp     position_jump[si],0
        je      @@noposjump
        mov     al,new_position[si]
@@noposjump:
        mov     current_pos[si],al
        
        call    calc_work_pattern
        mov     al,break_pos[si]
        mov     current_line[si],al
        mov     break_pos[si],0
        mov     pattern_break[si],0
        mov     position_jump[si],0
@@no_advance:
@@fuck_off:
        inc     si
        add     bx,2
        jumps
        loop    @@l2
        nojumps

        clc

        ret
play_line endp

test_device proc
        call    testadlib
        ret
test_device endp

oldone_got db 0

init_device proc
        call    init_adlib
        call    init_adlib_tables
        cmp     oldone_got,0
        jne     @@got
        call    getold
@@got:
        mov     oldone_got,1
        clc
        ret
init_device endp

stop_all proc
        cli
        call    clear_all_voices
        call    init_adlib
        call    init_adlib_tables
        sti
        cmp     oldone_got,0
        je      @@not
        call    setold
@@not:
        call    reset_clock_ticks
        clc
        ret
stop_all endp

load_song proc
;
; bl = num (0..8)
; dx = segment
;
        xor     bh,bh
        cmp     bx,8
        ja      @@err
        mov     si,bx
        shl     si,1
        cmp     segments[si],0
        jne     @@err
        mov     ds,dx
        mov     ax,ds:[_channels]
        xor     si,si
        mov     cx,9
@@l1:
        shl     ax,1
        jnc     @@nop
        cmp     channel_active[si],0
        jne     @@err
@@nop:
        inc     si
        loop    @@l1
        mov     ax,ds:[_channels]
        xor     si,si
        mov     cx,9
@@l2:
        shl     ax,1
        jnc     @@nop2
        mov     channel_active[si],0
        mov     channel_segment[si],bl
@@nop2:
        inc     si
        loop    @@l2
        mov     stopped[bx],1
        shl     bx,1
        mov     segments[bx],dx
        mov     ax,ds:[_BPM]
        mov     current_bpm,ax
@@noerr:
        clc
        ret
@@err:
        stc
        ret
load_song endp

unload_song proc
;
; bl = num (0..8)
;
        xor     bh,bh
        cmp     bx,8
        ja      merr
        mov     si,bx
        shl     si,1
        cmp     segments[si],0
        je      merr
        mov     dx,segments[si]
        mov     segments[si],0
        mov     ds,dx
        mov     ax,ds:[_channels]
        xor     si,si
        mov     cx,9
@@l1:
        shl     ax,1
        jnc     @@nop
        cmp     channel_active[si],0
        je      merr
        cmp     channel_segment[si],bl
        jne     merr
@@nop:
        inc     si
        loop    @@l1
        mov     ax,ds:[_channels]
        xor     si,si
        mov     cx,9
@@l2:
        shl     ax,1
        jnc     @@nop2
        mov     channel_active[si],0
        mov     channel_segment[si],0
        pusha
        mov     bx,si
        call    mute_voice
        popa
@@nop2:
        inc     si
        loop    @@l2
        mov     stopped[bx],1
        shl     bx,1
        mov     segments[bx],0
noerr:
        clc
        ret
merr:
        stc
        ret
unload_song endp

start_song proc
;
; bl = num
;
        xor     bh,bh
        cmp     bl,8
        ja      merr
        shl     bx,1
        mov     ax,segments[bx]
        cmp     ax,0
        je      merr
        mov     ds,ax
        mov     al,0
        push    bx
        call    calc_work_pattern
        pop     bx
        shr     bx,1
        mov     current_line[bx],0
        mov     current_speed[bx],6
        mov     pattern_break[bx],0
        mov     current_pos[bx],0
        mov     s_count[bx],0
        mov     position_jump[bx],0
        mov     stopped[bx],0
        mov     ax,ds:[_channels]
        xor     si,si
        mov     cx,9
@@l2:
        shl     ax,1
        jnc     @@nop2
        mov     channel_active[si],1
@@nop2:
        inc     si
        loop    @@l2
        clc
        ret
start_song endp

play_independent proc
        mov     polling,0
        mov     ax,current_bpm
        mov     ah,al
        cli
        mov     bp,0
        call    effect_set_speed
        call    setnew
        sti
        ret
play_independent endp

service_calls dw offset test_device             ; 0
              dw offset init_device             ; 1
              dw offset load_song               ; 2
              dw offset unload_song             ; 3
              dw offset start_song              ; 4
              dw offset play_line               ; 5
              dw offset play_independent        ; 6
              dw offset set_main_volume         ; 7
              dw offset empty                   ; 8
              dw offset empty                   ; 9
              dw offset stop_all                ; 10

adlib proc
        push    ds
        cmp     al,10
        jg      @@finish
        xor     ah,ah
        mov     si,ax
        shl     si,1
        mov     bp,service_calls[si]
        call    bp
        pop     ds
        ret
@@finish:
        pop     ds
        stc
        ret
adlib endp

;-------------------------------------------------------------------------------

old08_seg  dw ?
old08_offs dw ?

GetOld PROC
        mov     ah,35h
        mov     al,8
        int     21h
        mov     ax,es
        mov     old08_seg,ax
        mov     old08_offs,bx
        ret
GetOld ENDP

SetOld PROC
        mov     ax,old08_seg
        mov     ds,ax
        mov     dx,old08_offs
        mov     ah,25h
        mov     al,8
        int     21h
        ret
SetOld  ENDP

SetNew PROC
        push    cs
        push    cs
        pop     ds
        lea     dx,new08
        mov     ah,25h
        mov     al,8
        int     21h
        pop     ds
        ret
SetNew ENDP

New08 PROC
        pusha
        push    ds
        push    es

        call    play_line

        mov     al,20h
        out     20h,al

        pop     es
        pop     ds
        popa
        iret
New08 ENDP

Reset_Clock_Ticks PROC
        cli
        mov     al,54
        out     43h,al
        mov     al,0
        out     40h,al
        out     40h,al
        sti
        ret
Reset_Clock_Ticks ENDP


;-------------------------------------------------------------------------------

usage db 'Usage: qdplay <filename>',13,10,'$'

bad_use:
        push    cs
        pop     ds
        mov     ah,9
        lea     dx,usage
        int     21h
        mov     ah,4ch
        int     21h

get_file proc
        mov     ah,62h
        int     21h
        mov     ds,bx
        mov     si,80h
        lodsb
        xor     ah,ah
        mov     cx,ax
        jcxz    bad_use
        dec     cx
        jcxz    bad_use
        push    cs
        pop     es
        inc     si
        lea     di,fname
        rep     movsb
        ret
get_file endp

vol db 64

start:
        call    setfree                 ; init memory..

        call    get_file

        push    cs
        pop     ds
        lea     dx,fname
        lea     di,fsong
        call    loadseg

        mov     al,1
        call    adlib                   ; init

        mov     bl,0
        push    fsong
        pop     dx
        mov     al,2
        call    adlib                   ; loadsong

        mov     al,4
        mov     bl,0
        call    adlib                   ; startsong

        mov     al,6
        call    adlib                   ; play using timer interrupt...

again:
        sti

        mov     ah,11
        int     21h
        cmp     al,0
        je      again
        mov     ah,8
        int     21h
        cmp     al,'+'
        jne     @@no_p
        cmp     vol,64
        jge     again
        inc     vol
        mov     bl,vol
        mov     al,7
        call    adlib
        jmp     again
@@no_p:
        cmp     al,'-'
        jne     @@no_m
        cmp     vol,0
        jle     again
        dec     vol
        mov     bl,vol
        call    set_main_volume                 ; change main volume
        jmp     again
@@no_m:

        mov     al,10
        call    adlib                           ; end of all

end_of_all:

        mov     ax,4c00H
        int     21H
end start


;    Üßßßßßßßß²ßßßßßßßß²   ²ßßßßßßßßß²ÜÜ
;   ² ÜÛ²ßßßß   ÜÛÛÛÛÜ ßßÛ Û ±²Û ±²Û   Û
;   Û  ßßßßÛÜ  ÞÛÛ  ÛÛÝ  ² Û ²ÛÛ ²ÛÛßß ÛÜÜ
;   Û  ÜÜÝ ÛÛÝ ÛÛ²  ÛÛÛ  ² Û ²ÛÛ ²ÛÛ ÞÜÜ þ²
;   Û ÛÛ²  ÛÛÛ ÛÛ²  ÛÛÛ  ²Ü² ÛÛÛ ÛÛÛ  ÛÛÛ Û
;   Û ÛÛ²  ÛÛÛ ÛÛÛÜÜÛÛÛ  ÜÜÜÜÛÛÛ ÛÛÛ  ÛÛÛ Û
;   Û ÛÛÛ  ÛÛ² ÛÛÛ  ÛÛ² ÞÛÛ  ÛÛ² ÛÛÛ  ÛÛ² Û
;   Û ÛÛÛ  ÛÛ² ÛÛÛ  ÛÛ² ÛÛÛ  ÛÛ² ÛÛÛ  ÛÛ² Û
;   ÞÝÞÛÛ± ÛÛ² ÛÛÛ± ÛÛ² ÛÛÛ± ÛÛ² ÞÛÛ± ÛÛ² Û
;    Û ßÛ²ÜÛ²± ÛÛÛ² Û²± ßÛÛ²ÜÛ²±  ßÛ²ÜÛ²± Û
;    ²þÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ²ÜÜÜÜÜÜÜÜ² v2.00 - Quick'n'Dirty Play
