;께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께
;           16-bit Tiny MOD Player for Borland C++ 3.1 C compiler
;                      Version 2.11a  June 15th, 1994
;
;                      Copyright 1993,94 Carlos Hasan
;께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께께

ideal
model   large,c
p386
smart

;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇
; EQUATES AND PUBLICS
;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇

MAXVOICES = 8                           ; number of voices
DMABUFLEN = 1024                        ; DMA buffer length (multiple of 64)
VOLBUFLEN = 66*256                      ; volume table length
MIXBUFLEN = DMABUFLEN+2048              ; mixing/boosting buffer length
TIMERRATE = 17000                       ; timer interrupt rate in ticks

global  MODPlayModule:proc
global  MODStopModule:proc
global  MODPlaySample:proc
global  MODStopSample:proc
global  MODSetPeriod:proc
global  MODSetVolume:proc
global  MODSetMusicVolume:proc
global  MODSetSampleVolume:proc
global  MODDetectCard:proc
global  MODPoll:proc

;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇
; STRUCTURES
;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇

struc   module                          ; module structure
  numtracks     dw      ?               ; number of tracks
  orderlen      dw      ?               ; order length
  orders        db      128 dup (?)     ; order list
  patterns      dd      128 dup (?)     ; pattern addresses
  sampptr       dd      32 dup (?)      ; sample start addresses
  sampend       dd      32 dup (?)      ; sample end addresses
  samploop      dd      32 dup (?)      ; sample loop point addresses
  sampvolume    db      32 dup (?)      ; sample default volumes
ends    module

struc   sample                          ; sample structure
  period        dw      ?               ; default period
  volume        dw      ?               ; default volume
  datalen       dd      ?               ; sample data length
  dataptr       dd      ?               ; sample data address
ends    sample

struc   track                           ; track structure
  note          dw      ?               ; note index
  period        dw      ?               ; period value
  inst          db      ?               ; instrument
  volume        db      ?               ; volume
  effect        dw      ?               ; effect
  destperiod    dw      ?               ; toneporta wanted period
  tonespeed     db      ?               ; toneporta speed
  vibparm       db      ?               ; vibrato depth/rate
  vibpos        db      ?               ; vibrato wave position
  tremparm      db      ?               ; tremolo depth/rate
  trempos       db      ?               ; tremolo wave position
                db      ?               ; alignment
  arptable      dw      3 dup (?)       ; arpeggio periods
ends    track

;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇
; DATA
;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Module Player data
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
udataseg

moduleptr       dd      ?               ; current module address
pattptr         dd      ?               ; current playing pattern address
orderpos        db      ?               ; order position
orderlen        db      ?               ; order length
pattrow         db      ?               ; pattern row
tempo           db      ?               ; tempo
tempocount      db      ?               ; tempo counter
bpm             db      ?               ; beats per minute
musicvolume     db      ?               ; music channels volume
samplevolume    db      ?               ; sample channels volume
numtracks       dw      ?               ; number of tracks
tracks          track   MAXVOICES dup (?)

pitchtable      dd      3425 dup (?)    ; period to pitch table

; Amiga period table
dataseg

periodtable     dw      0
                dw      3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812
                dw      1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
                dw      856,808,762,720,678,640,604,570,538,508,480,453
                dw      428,404,381,360,339,320,302,285,269,254,240,226
                dw      214,202,190,180,170,160,151,143,135,127,120,113
                dw      107,101,95,90,85,80,75,71,67,63,60,56
                dw      53,50,47,45,42,40,37,35,33,31,30,28

; Sinus wave table

sintable        db      0,25,50,74,98,120,142,162,180,197,212,225
                db      236,244,250,254,255,254,250,244,236,225
                db      212,197,180,162,142,120,98,74,50,25

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Sound Blaster driver data
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
udataseg

; Voices programmable parameters

voicepos        dd      MAXVOICES dup (?)
voiceend        dd      MAXVOICES dup (?)
voiceloop       dd      MAXVOICES dup (?)
voicefrac       dd      MAXVOICES dup (?)
voicepitch      dd      MAXVOICES dup (?)
voicevolume     dd      MAXVOICES dup (?)

; Internal driver data
dataseg

mixbuffer       dw      ?               ; mixing buffer address
boosttable      dw      ?               ; boosting table address
voltable        dw      ?               ; volume table address
numvoices       dw      ?               ; number of active voices
mixfreq         dw      ?               ; playback frequency
ioaddr          dw      ?               ; card I/O port address
irqnum          db      ?               ; card IRQ level
drqnum          db      ?               ; card DMA channel
timerproc       dw      ?               ; timer callback address
timeracc        dw      ?               ; timer callback accumulator
timerspeed      dw      ?               ; timer callback speed
bufsel          dw      ?               ; DOS memory block selector
bufptr          dw      ?               ; DMA buffer address
bufoff          dw      ?               ; double buffer offset
oldirqoff       dw      ?               ; old IRQ vector address
oldirqsel       dw      ?
oldtimeroff     dw      ?               ; old timer IRQ0 vector address
oldtimersel     dw      ?
oldtimeracc     dw      ?               ; old timer accumulator
manualmode      db      ?               ; timer/manual polling mode
playing         db      0               ; playing/stopped status

ufardata fardataseg
        db      DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15 dup (?)

;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇
; CODE
;굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇굇
codeseg

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; Copyright Strings
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같

db      '16-bit Tiny MOD Player V2.11 Copyright 1993,94 Carlos Hasan',0
db      'Compiled on: ',??date,' ',??time,0

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Module Player stuff
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODPlayModule - start playing a music module
; In:
;  Song  = module address
;  Chans = number of channels
;  Rate  = playback rate
;  Port  = port address
;  irq   = irq number
;  dma   = dma channel
;  mode  = polling mode
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODPlayModule Song:dword,Chans:byte,Rate:word,Port:word,IRQ:byte,DRQ:byte,Mode:byte
        pushad
        push    es

; setup the music module address

        les     si,[Song]
        mov     [word low moduleptr],si
        mov     [word high moduleptr],es

; setup the sound card driver

        mov     ax,[Rate]
        mov     bl,[Chans]
        mov     dx,[Port]
        mov     cl,[IRQ]
        mov     ch,[DRQ]
        mov     bh,[Mode]
        call    mixinit
        jc      playmoduled0

; build the period to pitch table (16.16 fixed point values)

        movzx   ebx,ax
        mov     eax,8363*428
        xor     edx,edx
        shld    edx,eax,16
        shl     eax,16
        div     ebx
        mov     esi,eax
        lea     di,[pitchtable]
        mov     cx,3425
        xor     bx,bx
playmodulel0:
        inc     bx
        xor     edx,edx
        mov     eax,esi
        div     ebx
        mov     [di],eax
        add     di,4
        loop    playmodulel0

; setup global volumes for music and sample channels

        mov     [musicvolume],255
        mov     [samplevolume],255

; clear the module player track structures

        push    es
        mov     ax,ds
        mov     es,ax
        cld
        lea     di,[tracks]
        mov     cx,MAXVOICES*(size track)
        xor     al,al
        rep     stosb
        pop     es

; check if there is a module to playback

        xor     ax,ax
        mov     [numtracks],ax

        mov     si,[word low moduleptr]
        or      si,[word high moduleptr]
        test    si,si
        clc
        je      playmoduled0

; setup player interpreter variables

        les     si,[moduleptr]
        mov     ax,[es:si+module.orderlen]
        mov     [orderlen],al
        mov     ax,[es:si+module.numtracks]
        mov     [numtracks],ax
        mov     [tempo],6
        mov     [bpm],125
        mov     [orderpos],0
        mov     [tempocount],0
        mov     [pattrow],40h

; setup the player callback timer routine

        lea     dx,[pollmodule]
        call    mixsettimerproc
        mov     dl,[bpm]
        call    mixstarttimer
        clc

playmoduled0:
        pop     es
        popad
        sbb     ax,ax
        ret
endp    MODPlayModule

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODStopModule - shut down the music system
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODStopModule
        pushad
        call    mixstoptimer            ; stop the timer callback
        call    mixdone                 ; shutdown the SB stuff
        popad
        ret
endp    MODStopModule

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODPoll - polls the music system in manual mode
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODPoll
        pushad
        cmp     [manualmode],0          ; call the polling routine only
        je      modpolld0               ; if we are using the manual
        cmp     [playing],0             ; polling mode (and if the driver
        je      modpolld0               ; is active).
        call    mixpoll
modpolld0:
        popad
        ret
endp    MODPoll

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODPlaySample - play sample instrument
; In:
;  voice  = voice number
;  sample = sample address
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODPlaySample Voice:word,SamplePtr:dword
        pushad
        push    es
        cli

; get the voice number and track address

        movzx   ebx,[Voice]
        les     di,[SamplePtr]
        mov     si,bx
        imul    si,size track

; set the voice pitch value

        movzx   eax,[es:di+sample.period]
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax

; set the voice sample parameters

        mov     eax,[es:di+sample.dataptr]
        mov     [4*ebx+voicepos],eax
        add     eax,[es:di+sample.datalen]
        mov     [word low 4*ebx+voiceend],ax
        mov     [word low 4*ebx+voiceloop],ax

; set the voice and track volumes

        mov     ax,[es:di+sample.volume]
        mov     [si+tracks.volume],al
        mul     [samplevolume]
        mov     [byte 4*ebx+voicevolume],ah

        sti
        pop     es
        popad
        ret
endp    MODPlaySample

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODStopSample - stop the playing sample
; In:
;  voice = voice number
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODStopSample Voice:word
        pushad
        cli

; get the voice number

        movzx   ebx,[Voice]
        xor     eax,eax

; clear the voice sample parameters

        mov     [4*ebx+voicepos],eax
        mov     [word low 4*ebx+voiceend],ax
        mov     [word low 4*ebx+voiceloop],ax

        sti
        popad
        ret
endp    MODStopSample

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODSetPeriod -  set the voice period value
; In:
;  voice = voice number
;  period = period value (113-856)
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODSetPeriod Voice:word,Period:word
        pushad
        cli

; get the voice number and period value

        movzx   ebx,[Voice]
        movzx   eax,[Period]

; set the voice pitch value

        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax

        sti
        popad
        ret
endp    MODSetPeriod

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODSetVolume -  set the voice volume level
; In:
;  voice = voice number
;  volume = volume level (0-64)
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODSetVolume Voice:word,Volume:word
        pushad
        cli

; get the voice number and track address

        movzx   ebx,[Voice]
        mov     ax,[Volume]
        mov     si,bx
        imul    si,size track

; set the voice and track volume

        mov     [si+tracks.volume],al
        mul     [samplevolume]
        mov     [byte 4*ebx+voicevolume],ah

        sti
        popad
        ret
endp    MODSetVolume

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODSetMusicVolume - set the global music volume
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODSetMusicVolume Volume:word
        pushad
        cli

; set new music volume

        mov     ax,[Volume]
        mov     [musicvolume],al

; update all the music voices

        lea     si,[tracks]
        xor     ebx,ebx
setmusicvolumel0:
        mov     al,[si+track.volume]
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        add     si,size track
        inc     bx
        cmp     bx,[numtracks]
        jb      setmusicvolumel0

        sti
        popad
        ret
endp    MODSetMusicVolume

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODSetSampleVolume - set the global sample volume
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODSetSampleVolume Volume:word
        pushad
        cli

; set the sample volume

        mov     ax,[Volume]
        mov     [samplevolume],al

; update all the sample voices

        lea     si,[tracks]
        xor     ebx,ebx
setsamplevolumel0:
        cmp     bx,[numtracks]
        jb      setsamplevolumef0
        mov     al,[si+track.volume]
        mul     [samplevolume]
        mov     [byte 4*ebx+voicevolume],ah
setsamplevolumef0:
        add     si,size track
        inc     bx
        cmp     bx,MAXVOICES
        jb      setsamplevolumel0

        sti
        popad
        ret
endp    MODSetSampleVolume

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; MODDetectCard - detect the Sound Blaster configuration
; Out:
;  Port = I/O Port
;  IRQ = IRQ level
;  DRQ = DMA channel
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
proc    MODDetectCard Port:dword,IRQ:dword,DRQ:dword
        pushad
        push    es

; call the lowlevel autodetection routine

        call    mixdetect

; set the parameters in the user variables

        les     di,[Port]
        mov     [es:di],dx
        les     di,[IRQ]
        mov     [es:di],cl
        les     di,[DRQ]
        mov     [es:di],ch

        pop     es
        popad
        sbb     ax,ax
        ret
endp    MODDetectCard

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; pollmodule - polls the module player
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
pollmodule:
        pushad
        push    es gs
        dec     [tempocount]            ; decrease the tempo counter
        jle     pollmodulef0
        lea     si,[tracks]             ; while in the same pattern row
        xor     ebx,ebx                 ; update the track effects.
pollmodulel0:
        call    updatechannel
        add     si,size track
        inc     bx
        cmp     bx,[numtracks]
        jb      pollmodulel0
        pop     gs es
        popad
        ret

pollmodulef0:                           ; advance to the next pattern row.
        mov     al,[tempo]              ; update the tempo counter
        mov     [tempocount],al
        xor     edx,edx
        lgs     dx,[moduleptr]          ; get module and pattern address
        les     di,[pattptr]
        cmp     [pattrow],40h           ; need to advance to the next order?
        jb      pollmodulef2
        xor     eax,eax                 ; reset the pattern row
        mov     [pattrow],al
        mov     al,[orderpos]           ; if we are at the end of the order
        cmp     al,[orderlen]           ; list, loop to the beginning
        jb      pollmodulef1
        xor     al,al
        mov     [orderpos],al
pollmodulef1:
        inc     [orderpos]              ; get the new pattern address
        movzx   eax,[gs:edx+eax+module.orders]
        les     di,[gs:edx+4*eax+module.patterns]
pollmodulef2:
        inc     [pattrow]               ; increase pattern row number
        lea     si,[tracks]
        xor     ebx,ebx                 ; read and interpret the next
pollmodulel1:                           ; pattern row of events
        call    readchannel
        add     si,size track
        add     di,4
        inc     bx
        cmp     bx,[numtracks]
        jb      pollmodulel1
        mov     [word low pattptr],di   ; save pattern row address
        mov     [word high pattptr],es
        pop     gs es
        popad
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; readchannel - read the next note event from the pattern sheet
; In:
;  EBX    = voice number
;  DS:SI  = track address
;  ES:DI  = pattern address
;  GS:EDX = module address
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
readchannel:
        pushad

; check for new sample number . . .

        mov     al,[es:di+1]
        test    al,al
        je      readchannelf0
        mov     [si+track.inst],al
        movzx   eax,al
        mov     al,[gs:edx+eax+module.sampvolume]
        mov     [si+track.volume],al
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah

; check for new note pitch . . .

readchannelf0:
        mov     al,[es:di]
        test    al,al
        je      readchannelf1
        movzx   eax,al
        mov     [si+track.note],ax
        cmp     [byte es:di+3],03h
        je      readchannelf1
        mov     ax,[2*eax+periodtable]
        mov     [si+track.period],ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        movzx   eax,[si+track.inst]
        mov     ecx,[gs:edx+4*eax+module.sampptr]
        mov     [4*ebx+voicepos],ecx
        mov     ecx,[gs:edx+4*eax+module.sampend]
        mov     [word low 4*ebx+voiceend],cx
        mov     ecx,[gs:edx+4*eax+module.samploop]
        mov     [word low 4*ebx+voiceloop],cx

; check the new track effect . . .

readchannelf1:
        mov     dx,[es:di+2]
        mov     [si+track.effect],dx
        movzx   eax,dh
        and     al,0Fh
        call    [2*eax+efxtable]

        popad
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; updatechannel - update the track using the current effect
; In:
;  EBX   = voice number
;  DS:SI = track address
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
updatechannel:
        pushad
        mov     dx,[si+track.effect]
        movzx   eax,dh
        and     al,0Fh
        call    [2*eax+efxtable2]
        popad
        ret

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Protracker effects stuff
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; Effect jump tables
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같

        align   2

label efxtable word
        dw      efxarpeggio             ; 0 - arpeggio
        dw      efxnull                 ; 1 - porta up
        dw      efxnull                 ; 2 - porta down
        dw      efxtoneporta            ; 3 - tone porta
        dw      efxvibrato              ; 4 - vibrato
        dw      efxnull                 ; 5 - tone+slide
        dw      efxnull                 ; 6 - vibrato+slide
        dw      efxtremolo              ; 7 - tremolo
        dw      efxnull                 ; 8 - unused
        dw      efxsampoffset           ; 9 - sample offset
        dw      efxnull                 ; A - volume slide
        dw      efxpattjump             ; B - pattern jump
        dw      efxsetvolume            ; C - set volume
        dw      efxbreak                ; D - break pattern
        dw      efxnull                 ; E - extra effects
        dw      efxsetspeed             ; F - set speed

label efxtable2 word
        dw      efxarpeggio2            ; 0 - arpeggio
        dw      efxportaup              ; 1 - porta up
        dw      efxportadown            ; 2 - porta down
        dw      efxtoneporta2           ; 3 - tone porta
        dw      efxvibrato2             ; 4 - vibrato
        dw      efxtoneslide            ; 5 - tone+slide
        dw      efxvibslide             ; 6 - vibrato+slide
        dw      efxtremolo2             ; 7 - tremolo
        dw      efxnull                 ; 8 - unused
        dw      efxnull                 ; 9 - sample offset
        dw      efxvolslide             ; A - volume slide
        dw      efxnull                 ; B - pattern jump
        dw      efxnull                 ; C - set volume
        dw      efxnull                 ; D - break pattern
        dw      efxnull                 ; E - extra effects
        dw      efxnull                 ; F - set speed

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxnull - dummy effect
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxnull:
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxarpeggio - arpeggio
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxarpeggio:
        test    dl,dl
        je      efxnull
        mov     dh,dl
        and     dl,0Fh
        shr     dh,4
        movzx   eax,[si+track.note]
        mov     cx,[2*eax+periodtable]
        mov     [si+track.arptable],cx
        add     al,dh
        mov     cx,[2*eax+periodtable]
        mov     [si+2+track.arptable],cx
        sub     al,dh
        add     al,dl
        mov     cx,[2*eax+periodtable]
        mov     [si+4+track.arptable],cx
        ret
efxarpeggio2:
        test    dl,dl
        je      efxnull
        movzx   eax,[si+track.arptable]
        xchg    [si+4+track.arptable],ax
        xchg    [si+2+track.arptable],ax
        mov     [si+track.arptable],ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxportaup - slides the pitch up
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxportaup:
        xor     dh,dh
        movzx   eax,[si+track.period]
        sub     ax,dx
        cmp     ax,28
        jge     efxportaupf0
        mov     ax,28
efxportaupf0:
        mov     [si+track.period],ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxportadown - slides the pitch down
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxportadown:
        xor     dh,dh
        movzx   eax,[si+track.period]
        add     ax,dx
        cmp     ax,3424
        jle     efxportadownf0
        mov     ax,3424
efxportadownf0:
        mov     [si+track.period],ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxtoneporta - tone portamento
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxtoneporta:
        test    dl,dl
        jne     efxtoneportaf0
        mov     dl,[si+track.tonespeed]
efxtoneportaf0:
        mov     [si+track.tonespeed],dl
        mov     [si+track.effect],dx
        movzx   eax,[si+track.note]
        mov     ax,[2*eax+periodtable]
        mov     [si+track.destperiod],ax
        ret
efxtoneporta2:
        xor     dh,dh
        movzx   eax,[si+track.period]
        mov     cx,[si+track.destperiod]
        cmp     ax,cx
        je      efxnull
        jg      efxtoneportaf1
        add     ax,dx
        cmp     ax,cx
        jle     efxtoneportaf2
        mov     ax,cx
efxtoneportaf2:
        mov     [si+track.period],ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret
efxtoneportaf1:
        sub     ax,dx
        cmp     ax,cx
        jge     efxtoneportaf3
        mov     ax,cx
efxtoneportaf3:
        mov     [si+track.period],ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxvibrato - pitch vibrato
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxvibrato:
        mov     al,[si+track.vibparm]
        mov     ah,al
        and     ax,0F00Fh
        test    dl,0Fh
        jne     efxvibratof0
        or      dl,al
efxvibratof0:
        test    dl,0F0h
        jne     efxvibratof1
        or      dl,ah
efxvibratof1:
        mov     [si+track.vibparm],dl
        mov     [si+track.effect],dx
        ret
efxvibrato2:
        mov     dh,dl
        and     dx,0F00Fh
        shr     dh,2
        mov     al,[si+track.vibpos]
        add     [si+track.vibpos],dh
        mov     dh,al
        shr     al,2
        and     eax,1Fh
        mov     al,[eax+sintable]
        mul     dl
        shr     ax,7
        test    dh,dh
        jge     efxvibratof2
        neg     ax
efxvibratof2:
        add     ax,[si+track.period]
        cmp     ax,28
        jge     efxvibratof3
        mov     ax,28
efxvibratof3:
        cmp     ax,3424
        jle     efxvibratof4
        mov     ax,3424
efxvibratof4:
        movzx   eax,ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxtoneslide - volume slide and continue last portamento
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxtoneslide:
        call    efxvolslide
        mov     dl,[si+track.tonespeed]
        jmp     efxtoneporta

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxvibslide - volume slide and continue last pitch vibrato
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxvibslide:
        call    efxvolslide
        mov     dl,[si+track.vibparm]
        jmp     efxvibrato2

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxtremolo - volume vibrato
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxtremolo:
        mov     al,[si+track.tremparm]
        mov     ah,al
        and     ax,0F00Fh
        test    dl,0Fh
        jne     efxtremolof0
        or      dl,al
efxtremolof0:
        test    dl,0F0h
        jne     efxtremolof1
        or      dl,ah
efxtremolof1:
        mov     [si+track.tremparm],dl
        mov     [si+track.effect],dx
        ret
efxtremolo2:
        mov     dh,dl
        and     dx,0F00Fh
        shr     dh,2
        mov     al,[si+track.trempos]
        add     [si+track.trempos],dh
        mov     dh,al
        shr     al,2
        and     eax,1Fh
        mov     al,[eax+sintable]
        mul     dl
        shr     ax,6
        test    dh,dh
        jge     efxtremolof2
        neg     ax
efxtremolof2:
        add     al,[si+track.volume]
        jge     efxtremolof3
        xor     al,al
efxtremolof3:
        cmp     al,40h
        jle     efxtremolof4
        mov     al,40h
efxtremolof4:
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxsampoffset - set the sample offset
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxsampoffset:
        movzx   eax,[si+track.inst]
        xor     esi,esi
        lgs     si,[moduleptr]
        mov     eax,[gs:esi+4*eax+module.sampptr]
        mov     dh,dl
        xor     dl,dl
        add     ax,dx
        mov     [word low 4*ebx+voicepos],ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxvolslide - volume slide
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxvolslide:
        mov     al,[si+track.volume]
        mov     dh,dl
        shr     dl,4
        je      efxvolslidef0
        add     al,dl
        cmp     al,40h
        jle     efxvolslidef1
        mov     al,40h
efxvolslidef1:
        mov     [si+track.volume],al
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        ret
efxvolslidef0:
        sub     al,dh
        jge     efxvolslidef2
        xor     al,al
efxvolslidef2:
        mov     [si+track.volume],al
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxpattjump - jump to order pattern
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxpattjump:
        mov     [orderpos],dl
        mov     [pattrow],40h
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxsetvolume - set volume
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxsetvolume:
        mov     al,dl
        mov     [si+track.volume],al
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxbreak - break pattern
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxbreak:
        mov     [pattrow],40h
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; efxsetspeed - set the tempo or BPM speed
; In:
;  EBX   = voice number
;  DS:SI = track address
;  DL    = effect parameter
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
efxsetspeed:
        test    dl,dl
        je      efxnull
        cmp     dl,20h
        jae     efxsetbpm
        mov     [tempo],dl
        mov     [tempocount],dl
        ret
efxsetbpm:
        mov     [bpm],dl
        call    mixstarttimer
        ret

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Sound Blaster Driver highlevel stuff
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixinit - initialize the sound driver
; In:
;  AX = mixing speed in hertz
;  BL = number of voices
;  DX = I/O port address
;  CL = IRQ level
;  CH = DRQ channel
;  BH = polling mode
; Out:
;  CF = status
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixinit:
        pushad
        push    es

        cmp     [playing],0
        stc
        jne     mixinitd0

; setup sound card parameters

        mov     [manualmode],bh
        xor     bh,bh
        mov     [mixfreq],ax
        mov     [numvoices],bx
        mov     [ioaddr],dx
        mov     [irqnum],cl
        mov     [drqnum],ch

; check if the sound card is present

        call    sbreset
        jc      mixinitd0

; setup timer and double buffer variables

        mov     [timerproc],offset nulltimer
        xor     ax,ax
        mov     [bufoff],ax
        mov     [timeracc],ax
        mov     [timerspeed],256

; clear voice parameters

        push    es
        mov     ax,ds
        mov     es,ax
        cld
        lea     di,[voicepos]
        mov     cx,6*MAXVOICES
        xor     eax,eax
        rep     stosd
        pop     es

; allocate conventional memory for the DMA buffer, the volume table,
; the mixing buffer and the boosting table.

; NOTE: there are problems allocating memory using INT 21h AH=48h under
;       Borland C++ 3.1 so I am using an static data segment.

        mov     ax,fardataseg
        mov     [bufsel],ax
        movzx   eax,ax
        shl     eax,4

; set the address of the mixing buffer and boosting table

        mov     [mixbuffer],0
        mov     [boosttable],DMABUFLEN
        add     eax,DMABUFLEN+2048

; get the address of the DMA buffer and volume table

        mov     ecx,DMABUFLEN
        lea     edx,[eax+ecx]

; check for cross-pages in the DMA buffer and align the Volume table

        mov     esi,eax
        add     si,cx
        jnc     mixinitf0
        mov     edx,eax
        add     eax,VOLBUFLEN
mixinitf0:
        movzx   esi,[bufsel]
        shl     esi,4
        sub     eax,esi
        sub     edx,esi
        add     dx,255
        xor     dl,dl
        mov     [bufptr],ax
        mov     [voltable],dx

; clear DMA buffer with centered samples

        push    es
        cld
        mov     es,[bufsel]
        mov     di,[bufptr]
        mov     cx,DMABUFLEN
        mov     al,80h
        rep     stosb
        pop     es

; build volume table and boosting table

        push    es
        mov     cl,6
        mov     es,[bufsel]
        mov     di,[voltable]
        xor     bx,bx
mixinitl0:
        mov     al,bl
        imul    bh
        sar     ax,cl
        mov     [es:di],al
        inc     di
        inc     bl
        jne     mixinitl0
        inc     bh
        cmp     bh,40h
        jbe     mixinitl0
        pop     es

        push    es
        cld
        mov     es,[bufsel]
        mov     di,[boosttable]
        mov     cx,768
        xor     ax,ax
        rep     stosb
        mov     cx,512
mixinitl1:
        mov     [es:di],ah
        add     ax,80h
        inc     di
        loop    mixinitl1
        mov     cx,768
        dec     al
        rep     stosb
        pop     es

; initialize the sound card for output

        call    dmasetup
        call    irqsetup
        call    sbsetup

; dont use the timer interrupt for manual polling mode

        cmp     [manualmode],0
        jne     mixinitf1

; install timer interrupt to poll the driver

        push    es
        mov     ax,cs
        mov     es,ax
        lea     bx,[mixtimer]
        mov     cl,0
        call    irqsetvect
        mov     [oldtimeroff],bx
        mov     [oldtimersel],es
        pop     es

; set the timer frequency to 70 hertz

        cli
        mov     al,36h
        out     43h,al
        mov     ax,TIMERRATE
        out     40h,al
        mov     al,ah
        out     40h,al
        sti

; set driver playing status

mixinitf1:
        mov     [playing],1
        clc

mixinitd0:
        pop     es
        popad
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixdone - deinitialize the sound driver
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixdone:
        pushad
        push    es

        cmp     [playing],1
        jne     mixdoned0

; the timer interrupt was modified if we are using the timer polling mode

        cmp     [manualmode],0
        jne     mixdonef0

; restore the timer frequency to 18.2 hertz

        cli
        mov     al,36h
        out     43h,al
        xor     al,al
        out     40h,al
        out     40h,al
        sti

; deinstall timer interrupt used to poll the driver

        push    es
        mov     bx,[oldtimeroff]
        mov     es,[oldtimersel]
        mov     cl,0
        call    irqsetvect
        pop     es

; deinitialize the sound card output

mixdonef0:
        call    sbdone
        call    irqdone
        call    dmadone

; set driver stopped status

        mov     [playing],0

mixdoned0:
        pop     es
        popad
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixtimer - timer interrupt routine used to poll the sound driver
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixtimer:
        push    ax
        push    ds
        mov     ax,@data
        mov     ds,ax
        call    mixpoll                 ; poll the sound system
        add     [oldtimeracc],TIMERRATE
        jnc     mixtimerf0              ; time to call the old IRQ0 vector?
        pushf                           ; yes, jump to the old IRQ0 service
        call    [dword oldtimeroff]
        jmp     mixtimerd0
mixtimerf0:
        mov     al,20h                  ; nope, send PIC acknowledge and exit
        out     20h,al
mixtimerd0:
        pop     ds
        pop     ax
        iret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixdetect - detect the sound card configuration
; Out:
;  DX = I/O Port
;  CL = IRQ level
;  CH = DMA channel
;  CF = status
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixdetect:
        cmp     [playing],1             ; do not try to autodetect
        stc                             ; if we are already playing
        je      mixdetectd0
        call    sbdetect
mixdetectd0:
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixsettimerproc - set the timer procedure
; In:
;  CS:DX = timer routine address
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixsettimerproc:
        mov     [timerproc],dx          ; set the timer callback address
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixstarttimer - start the timer at the specified speed
; In:
;  DL = timer speed in beats per minute (BPMs)
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixstarttimer:
        push    ax
        push    bx
        push    dx
        mov     bh,dl                   ; set the timer callback speed
        xor     bl,bl                   ; to 24/60*BPM hertz
        mov     ax,[mixfreq]
        mov     dx,0280h
        mul     dx
        div     bx
        mov     [timerspeed],ax
        pop     dx
        pop     bx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixstoptimer - stop the timer routine
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixstoptimer:
        mov     [timerproc],offset nulltimer
nulltimer:
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixvoice - mixes the voice samples
; In:
;  EBX   = voice number (*4)
;  CX    = number of samples
;  ES:DI = buffer address
; Out:
;  ES:DI = buffer end address
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixvoice:

        macro   mixcode OPCODE
        local   mixcodel0,mixjmptable

        push    eax
        push    bx
        push    cx
        push    dx
        push    bp
        push    si

        push    ds
        push    es
        push    bx
        mov     ax,[voltable]
        mov     dl,[byte bx+3+voicefrac]
        mov     dh,[byte bx+1+voicepitch]
        mov     bp,[word bx+2+voicepitch]
        add     ah,[byte bx+voicevolume]
        les     si,[bx+voicepos]
        mov     ds,[bufsel]
        mov     bx,ax

        movzx   eax,cl
        and     al,31
        shr     cx,5
        movzx   edi,di
        lea     edi,[edi+2*eax-2*32]
        jmp     [2*eax+mixjmptable]

        align   2
mixcodel0:
        I=0
        rept    32
CODESTART=$
        mov     bl,[es:si]
        add     dl,dh
        movsx   ax,[byte ds:bx]
        adc     si,bp
        OPCODE  [ds:di+I],ax
CODELEN=$-CODESTART
        I=I+2
        endm
        add     di,2*32
        dec     cx
        jge     mixcodel0

        pop     bx
        pop     es
        pop     ds
        mov     [word low bx+voicepos],si
        mov     [byte bx+3+voicefrac],dl

        pop     si
        pop     bp
        pop     dx
        pop     cx
        pop     bx
        pop     eax
        ret

        align   2
        label mixjmptable word
        I=CODESTART+CODELEN
        rept    32
        dw      I
        I=I-CODELEN
        endm
        endm

        test    bx,bx
        je      mixvoicef0
        mixcode add
mixvoicef0:
        mixcode mov

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixvoices - mixes all the voices
; In:
;  ES:DI = buffer address
;  CX    = number of samples
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixvoices:
        pushad

        xor     ebx,ebx
mixvoicesl0:
        push    bx
        push    cx
        push    di
        shl     bx,2
        mov     bp,cx

mixvoicesl1:
        mov     cx,bp
        mov     dx,[word low bx+voicepos]
        mov     ax,[word low bx+voiceend]
        cmp     dx,ax
        jb      mixvoicesf0
        sub     dx,ax
        add     dx,[word low bx+voiceloop]
        cmp     dx,ax
        jae     mixvoicesc0
        mov     [word low bx+voicepos],dx

mixvoicesf0:
        sub     ax,dx
        shl     eax,16
        mov     edx,[bx+voicefrac]
        shr     edx,16
        sub     eax,edx

        movzx   edx,cx
        mov     esi,[bx+voicepitch]
        imul    edx,esi
        cmp     edx,eax
        jbe     mixvoicesf1
        dec     eax
        xor     edx,edx
        add     eax,esi
        adc     edx,edx
        div     esi
        mov     cx,ax

mixvoicesf1:
        call    mixvoice
        sub     bp,cx
        jg      mixvoicesl1

        pop     di
        pop     cx
        pop     bx
        inc     bx
        cmp     bx,[numvoices]
        jb      mixvoicesl0

        popad
        ret

mixvoicesc0:
        test    bx,bx
        jne     mixvoicesc1
        cld
        xor     ax,ax
        rep     stosw

mixvoicesc1:
        pop     di
        pop     cx
        pop     bx
        inc     bx
        cmp     bx,[numvoices]
        jb      mixvoicesl0

        popad
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; mixpoll - updates the output buffer
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
mixpoll:
        pushad
        push    es

; get the 16 bit mixing buffer address

        mov     cx,DMABUFLEN/2
        mov     es,[bufsel]
        mov     di,[mixbuffer]

; check if we can fill the current half buffer with samples

        call    dmagetpos
        cmp     ax,cx
        jae     mixpollf3
        cmp     [bufoff],cx
        je      mixpollf4
        jmp     mixpolld0
mixpollf3:
        cmp     [bufoff],cx
        je      mixpolld0

; fill the mixing buffer and polls the timer callback routine

mixpollf4:
        mov     ax,[timeracc]
        mov     bp,cx
mixpolll0:
        test    ax,ax
        jg      mixpollf0
        call    [timerproc]
        add     ax,[timerspeed]
mixpollf0:
        mov     cx,ax
        add     cx,63
        and     cl,not 63
        cmp     cx,bp
        jle     mixpollf1
        mov     cx,bp
mixpollf1:
        call    mixvoices
        add     di,cx
        add     di,cx
        sub     ax,cx
        sub     bp,cx
        jg      mixpolll0
        mov     [timeracc],ax

; translate 16-bit signed samples to 8-bit unsigned samples

        push    ds
        mov     cx,DMABUFLEN/2
        mov     si,[mixbuffer]
        movzx   eax,[bufptr]
        add     ax,[bufoff]
        xor     [bufoff],cx
        mov     di,[boosttable]
        add     di,1024
        shr     cx,4
        mov     ds,[bufsel]
mixpolll2:
        I=0
        rept    4
        mov     bx,[si+8*I+4]
        mov     dl,[di+bx]
        mov     bx,[si+8*I+6]
        mov     dh,[di+bx]
        shl     edx,16
        mov     bx,[si+8*I]
        mov     dl,[di+bx]
        mov     bx,[si+8*I+2]
        mov     dh,[di+bx]
        mov     [eax+4*I],edx
        I=I+1
        endm
        add     si,2*16
        add     ax,16
        dec     cx
        jg      mixpolll2
        pop     ds

mixpolld0:
        pop     es
        popad
        ret

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Sound Blaster DSP lowlevel stuff
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; sbwrite - send a command/data byte to the DSP chip
; In:
;  AL = command/data byte
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
sbwrite:
        push    ax
        push    cx
        push    dx
        mov     dx,[ioaddr]
        add     dx,0Ch
        mov     ah,al
        xor     cx,cx                   ; wait until the write buffer
sbwritel0:                              ; status port (2XCh) bit 7 is clear
        in      al,dx
        and     al,80h
        loopnz  sbwritel0
        mov     al,ah                   ; write value in the write
        out     dx,al                   ; data port (2XCh)
        pop     dx
        pop     cx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; sbreset - reset the Sound Blaster DSP chip
; Out:
;  CF = status
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
sbreset:
        push    ax
        push    bx
        push    cx
        push    dx
        mov     bx,64                   ; try to reset upto 64 times
sbresetl1:
        mov     dx,[ioaddr]
        add     dx,06h
        mov     al,1                    ; write 1 to the reset port (2X6h)
        out     dx,al
        xor     ah,ah                   ; wait at least 3 microseconds
sbresetl2:
        in      al,dx
        dec     ah
        jne     sbresetl2
        xor     al,al                   ; write 0 to the reset port (2X6h)
        out     dx,al
        add     dx,08h
        mov     cx,0400h                ; wait until the data available
sbresetl0:                              ; status port (2XEh) bit 7 is set
        in      al,dx
        and     al,80h
        loopz   sbresetl0
        sub     dx,04h                  ; read the read data port (2XAh)
        in      al,dx
        cmp     al,0AAh
        clc
        je      sbresetd0               ; check the ready byte value
        dec     bx
        jne     sbresetl1
        stc
sbresetd0:
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; sbsetup - start the DMA output
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
sbsetup:
        push    ax
        push    dx
        mov     al,0D1h                 ; turn on the speaker
        call    sbwrite
        mov     al,40h                  ; set the playback rate
        call    sbwrite
        mov     ax,1000
        mul     ax
        div     [mixfreq]
        neg     ax
        call    sbwrite
        mov     al,14h                  ; start the lowspeed 8 bit DMA
        call    sbwrite                 ; mode transfer to the DAC
        mov     al,0FFh
        call    sbwrite
        call    sbwrite
        pop     dx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; sbdone - shut down the DMA output
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
sbdone:
        push    ax
        call    sbreset                 ; reset the DSP chip
        mov     al,0D3h
        call    sbwrite                 ; turn off the speaker
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; sbdetect - Detect the Sound Blaster I/O Port, IRQ level and DMA channel
; Out:
;  DX = I/O port address
;  CL = IRQ level
;  CH = DMA channel
;  CF = status
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
sbdetect:
        mov     dx,210h                 ; scan the ports 210h..260h
sbdetectl0:
        mov     [ioaddr],dx             ; check if there is a SB card
        call    sbreset                 ; trying to reset the DSP chip
        jnc     sbdetectf0
        add     dx,10h
        cmp     dx,260h
        jbe     sbdetectl0
        xor     dx,dx
        xor     cx,cx
        stc
        ret
sbdetectf0:
        push    ax
        push    bx
        push    cx
        push    es

        irp     I,<2,3,5,7,10>          ; install IRQ traps
        push    cs
        pop     es
        lea     bx,[irqtest&I]
        mov     cx,I
        call    irqsetvect
        call    irqsetmask
        push    bx
        push    es
        endm

        mov     [irqnum],0

        mov     al,0F2h                 ; ask to the DSP to raise a IRQ
        call    sbwrite

        xor     cx,cx                   ; wait until some IRQ occurs
sbdetectl1:
        cmp     [irqnum],0
        loope   sbdetectl1

        irp     I,<10,7,5,3,2>          ; deinstall IRQ traps
        pop     es
        pop     bx
        mov     cx,0100h+I
        call    irqsetmask
        call    irqsetvect
        endm

        pop     es
        pop     cx
        pop     bx
        pop     ax
        mov     dx,[ioaddr]             ; return the SB parameters
        mov     cl,[irqnum]
        xor     ch,ch
        sub     ch,cl
        mov     ch,1
        cmc
        ret

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Sound Blaster DMA lowlevel stuff
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; dmasetup - setup the DMA buffer parameters
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
dmasetup:
        push    eax
        push    bx
        push    cx
        push    dx

        mov     bl,[drqnum]
        mov     al,bl
        or      al,04h                  ; reset the DMA channel
        out     0Ah,al
        out     0Ch,al                  ; clear the flip flop
        mov     al,bl
        or      al,58h                  ; set the autoinit mode
        out     0Bh,al
        movzx   dx,bl
        add     dx,dx
        push    edx                     ; set the buffer address
        movzx   eax,[bufsel]
        movzx   edx,[bufptr]
        shl     eax,4
        add     eax,edx
        pop     edx
        out     dx,al
        mov     al,ah
        out     dx,al
        inc     dx
        mov     ax,DMABUFLEN            ; set the buffer length
        dec     ax
        out     dx,al
        mov     al,ah
        out     dx,al
        mov     edx,82818387h           ; set the buffer page
        mov     cl,bl
        shl     cl,3
        shr     edx,cl
        xor     dh,dh
        shr     eax,16
        out     dx,al
        mov     al,bl                   ; unlock the DMA channel
        out     0Ah,al

        pop     dx
        pop     cx
        pop     bx
        pop     eax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; dmadone - shut down the DMA controller
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
dmadone:
        push    ax
        mov     al,[drqnum]             ; reset the DMA channel
        or      al,04h
        out     0Ah,al
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; dmagetpos - return the DMA buffer relative position
; Out:
;  AX = buffer relative position
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
dmagetpos:
        push    cx
        push    dx
        out     0Ch,al                  ; clear the flip flop
        mov     dl,[drqnum]
        xor     dh,dh
        add     dl,dl
        inc     dl
        in      al,dx                   ; read the DMA counter
        mov     ah,al
        in      al,dx
        xchg    al,ah
dmagetposl0:
        mov     cx,ax                   ; read again the DMA counter
        in      al,dx
        mov     ah,al
        in      al,dx
        xchg    al,ah
        sub     cx,ax
        cmp     cx,+16                  ; both values are near?
        jg      dmagetposl0             ; nope, try again
        cmp     cx,-16
        jl      dmagetposl0
        neg     ax                      ; get the position relative
        add     ax,DMABUFLEN            ; to the start of the buffer
        pop     dx
        pop     cx
        ret

;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚
; Sound Blaster IRQ lowlevel stuff
;갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚갚

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; irqsetvect - set the IRQ handler routine
; In:
;  ES:BX = IRQ handler routine address
;  CL = IRQ level
; Out:
;  ES:BX = previous IRQ handler address
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
irqsetvect:
        push    ax
        push    cx
        push    dx

; get the PIC interrupt master and slave base address

        mov     dx,0870h

; get the IDT interrupt slot number for the IRQ number

        mov     al,cl
        cmp     al,08h
        jb      irqsetvectf0
        mov     dh,dl
        sub     al,08h
irqsetvectf0:
        add     al,dh

; saves and change the IRQ handler routine

        push    ds
        push    es
        push    bx
        mov     ah,35h
        int     21h
        pop     dx
        pop     ds
        push    es
        push    bx
        mov     ah,25h
        int     21h
        pop     bx
        pop     es
        pop     ds

        pop     dx
        pop     cx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; irqsetmask - enable or disable the IRQ in the interrupt mask registers
; In:
;  CL = IRQ level
;  CH = enable (=0) or disable (=1)
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
irqsetmask:
        push    ax
        push    dx

        cli
        in      al,0A1h                 ; enable or disable the specified
        mov     ah,al                   ; IRQ using the PIC interrupt
        in      al,21h                  ; mask registers
        mov     dx,1
        shl     dx,cl
        not     dx
        and     ax,dx
        mov     dl,ch
        shl     dx,cl
        or      ax,dx
        out     21h,al
        mov     al,ah
        out     0A1h,al
        sti

        pop     dx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; irqsetup - install the IRQ handler routine
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
irqsetup:
        push    ax
        push    bx
        push    cx

; set the IRQ handler routine and saves the previous vector

        push    es
        mov     ax,cs
        mov     es,ax
        lea     bx,[irqhandler]
        mov     cl,[irqnum]
        call    irqsetvect
        mov     [oldirqoff],bx
        mov     [oldirqsel],es
        pop     es

; enable the IRQ signals in the PIC interrupt mask

        mov     cl,[irqnum]
        mov     ch,0
        call    irqsetmask

        pop     cx
        pop     bx
        pop     ax
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; irqdone - restores the old IRQ handler routine
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
irqdone:
        push    bx
        push    cx

; disable IRQ signals in the PIC interrupt mask register

        mov     cl,[irqnum]
        mov     ch,1
        call    irqsetmask

; restore the old IRQ handler routine

        push    es
        mov     bx,[oldirqoff]
        mov     es,[oldirqsel]
        mov     cl,[irqnum]
        call    irqsetvect
        pop     es

        pop     cx
        pop     bx
        ret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; irqhandler - hardware IRQ handler routine
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
irqhandler:
        push    ax
        push    dx
        push    ds
        mov     ax,@data
        mov     ds,ax

; send acknowledge to the PIC controller

        mov     al,20h
        cmp     [irqnum],08h
        jb      irqhandlerf0
        out     0A0h,al
irqhandlerf0:
        out     20h,al

; send acknowledge to the DSP chip reading the 8 bit ack port (2XEh)

        mov     dx,[ioaddr]
        add     dx,0Eh
        in      al,dx

; restart the 8 bit DMA mode playback transfer

        mov     al,14h
        call    sbwrite
        mov     al,0FFh
        call    sbwrite
        call    sbwrite

        pop     ds
        pop     dx
        pop     ax
        iret

;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
; irqtest - testing hardware IRQ handler routine
;같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같같
irqtest:
        push    dx                      ; common IRQ test handler code
        push    ds                      ; used for autodetection
        mov     dx,@data
        mov     ds,dx
        mov     [irqnum],al             ; save the IRQ level number
        mov     dx,[ioaddr]             ; send acknowledge signal to the
        add     dx,0Eh                  ; DSP reading the ack port (2XEh)
        in      al,dx
        mov     al,20h                  ; send acknowledge to the PIC
        cmp     [irqnum],08h            ; controllers
        jb      irqtestf0
        out     0A0h,al
irqtestf0:
        out     20h,al
        pop     ds
        pop     dx
        pop     ax
        iret

        irp     I,<2,3,5,7,10>          ; IRQ test handlers for each
irqtest&I:                              ; possible IRQ levels
        push    ax
        mov     ax,I
        jmp     irqtest
        endm

end
