; ; sound blaster driver ; ; requires DOS 2.0, should run on an 8086 processor ; ; adaptive, either link it in or load it seperately ; ; warning: do NOT set an illegal port/irq/dma spec ; ; DMA buf of zero means auto-pick. Max size in any case is 16K ; (because of EMS) ; ; call far to CS:0 to enter the driver ; ; only AX,FLAGS modified ; ; pro should work :) ; ; sb16 notes: ; don't know about DMA when running 8-bit mode ; don't have an sb16 and haven't tested it. ; freq should probably be truncated to nearest TC ; ; callback for buffer fills: ; ES:DI = buf ; CX = count ; DS carries through except on auto-update ; ; if count on return is NZ DI must be next buffer pos, and remaining ; bytes will be zero filled (16 bit) or 80h filled (8 bit) ; ; 8 bits: baseline = 80h ; 16 bits: baseline = 0 ;*********************************************************************** ; ; api defs ; ; errors ; ERR_NOBLAST = 8001H ; no blaster string ERR_BADSTR = 8002H ; bad blaster string ERR_NOPRO = 8002H ; need a pro (for stereo) ERR_NO16 = 8003H ; need a sb16 (for 16 bits) ERR_NODMA = 8004H ; couldn't allocate DMA buf ERR_NOFUNC = 8005H ; invalid function ERR_ILLSEQ = 8006H ; invalid command sequencing ERR_BADSYS = 8007H ; Must have DOS 2.0+ & a 386 CMD_RESET = 0 CMD_INIT = 1 CMD_EXIT = 2 CMD_PLAY = 3 CMD_PLAYSTOP = 4 CMD_UPDATE = 5 CMD_RESYNC = 6 ; ; mode flags ; F_NOENV EQU 80h ; don't read from env F_AUTOUPDATE EQU 4 ; if they want auto update F_STEREO EQU 2 ; use stereo F_16 EQU 1 ; use 16-bit xfers ; ; user data ; udata struc command dw ? ; command xflags dw ? ; access flags cb dd ? ; user callback sbver dw ? ; return version mfreq dw ? ; gets/returns freq c_dmasiz dw ? ; request/return dmasiz c_dmabuf dw ? ; return dma buf base dw ? ; return base port here irq dw ? ; return irq here ldma dw ? ; return ldma here hdma dw ? ; return hdma here udata ends ; ;*********************************************************************** ; internal defs ; ; DMA port struct ; dmap struc d_base dw ? d_page dw ? d_count dw ? d_ff dw ? d_mask dw ? d_mode dw ? d_command dw ? dmap ends ; ; sb ports ; MIXER EQU 4 MIXER_DAT EQU 5 DSP_RESETREG EQU 6 DSP_DATAIN EQU 0AH DSP_DATAOUT EQU 0CH DSP_STAT EQU 0EH ; ; state bits ; S_PLAYING EQU 1 ; playing enabled S_INITTED EQU 2 ; init routine called S_IN8 EQU 4 ; no recursion on int 8 ; sbdrv segment para public USE16 'CODE' assume cs:sbdrv,ds:nothing,es:nothing,ss:nothing ;*********************************************************************** ; driver jump and driver data ; ; MUST be run with IP = 0!!!! public _SB_ENTRY _SB_ENTRY: jmp sb_entrypoint db '$SOUND$' ; validation string ; ; dsp vars ; dsp_base dw -1 ; port base dsp_irq dw -1 ; port irq dsp_int dw -1 ; port int dsp_lodma dw -1 ; port dma, 8 bit dsp_hidma dw -1 ; port dma, 16 bit dsp_dma dw -1 ; port dma, active dsp_ver dw -1 ; version of board dsp_mixfreq dw -1 ; mixing freq flags dw 0 ; mode flags ; ; psp ; psp dw 0 ; prog PSP ; ; blaster string ; blaster db "BLASTER=" ; ; old intr vect ; oldvect dw 0,0 ; blaster oldvect old8vect dd 0 ; int 8 old vect ; ; user callback ; callback dd 0 ; update callback ; ; dma vars ; dmaptr dw 0 ; pointer to the channel port info dmabuf dw 0 ; pointer to the DMA buf dmasiz dw 0 ; size of DMA buf dma_curr dw 0 ; next write goes here ; state dw 0 ; driver state bits ; ;*********************************************************************** ; dma port defs ; dmas label byte dmap <0,87h,1,0ch,0ah,0bh,8> dmap <2,83h,3,0ch,0ah,0bh,8> dmap <4,81h,5,0ch,0ah,0bh,8> dmap <6,82h,7,0ch,0ah,0bh,8> dmap <0c0h,87h,0c2h,0d8h,0d4h,0d6h,0d0h> dmap <0c4h,87h,0c6h,0d8h,0d4h,0d6h,0d0h> dmap <0c8h,87h,0cah,0d8h,0d4h,0d6h,0d0h> dmap <0cch,87h,0ceh,0d8h,0d4h,0d6h,0d0h> ;*********************************************************************** ; Sound blaster hardware ; ; select mono/stereo (PRO only) ; selms PROC mov al,0eh mov dx,[dsp_base] add dl,MIXER out dx,al inc dl mov ah,byte ptr [flags] and ah,1 shl ah,1 in al,dx or al,ah out dx,al nst: ret selms ENDP ; wait for DSP i/O ready ; wait_write PROC mov cx,-1 mov dx,[dsp_base] add dl,DSP_DATAOUT ww_l: in al,dx or al,al jns ww_ok nop nop nop loop ww_l stc ret ww_ok: clc ret wait_write ENDP wait_read PROC mov cx,-1 mov dx,[dsp_base] add dl,DSP_STAT wr_l: in al,dx or al,al js wr_ok nop nop nop loop wr_l stc ret wr_ok: sub dl,DSP_STAT-DSP_DATAIN ret wait_read ENDP ; ; DSP write reg - data is in the code seg ; dsp_write PROC pop si lods byte ptr cs:[si] mov cl,al sub ch,ch mov ax,si add ax,cx push ax dw_l: push cx call wait_write pop cx jc dw_x lods byte ptr cs:[si] out dx,al loop dw_l dw_x: ret dsp_write ENDP ; ; DSP read reg ; dsp_read PROC call wait_read jc dr_x in al,dx dr_x: ret dsp_read ENDP reset_delay PROC l1: ; these two loops are approximately in al,61h ; a 30uS delay and al,10h jnz l1 l2: in al,61h and al,10h jz l2 loop l1 ret reset_delay ENDP ; resetx PROC sub al,al out dx,al mov cx,1 call reset_delay mov al,1 out dx,al ret resetx ENDP ; ; ; reset and ping ; dsp_reset PROC mov dx,[dsp_base] add dl,DSP_RESETREG call resetx ; do it twice, my card doesn't work right :) mov cx,1 call reset_delay call resetx ; ; now delay 100 us or more ; mov cx,5 call reset_delay ; mov dx,[dsp_base] add dl,DSP_STAT in al,dx cmp al,0AAH jz rd_x stc rd_x: ret dsp_reset ENDP ; ; read version, raw ; dsp_versionx PROC call dsp_write db 1,0e1h jc dv_x call dsp_read jc dv_x mov bh,al call dsp_read jc dv_x xchg al,ah mov al,bh clc dv_x: ret dsp_versionx ENDP ; ; actual version command is a hack to get stable results ; from older DSPs ; dsp_version PROC call dsp_versionx jc dv_x dvl: mov di,ax call dsp_versionx jc dv_x cmp di,ax jnz dvl clc ret dsp_version ENDP ;*********************************************************************** ; Interrupt hardware ; setirq PROC mov bh,0feh xor bl,1 mov ax,[dsp_irq] and ax,0fh mov cx,ax and cl,07h push cx rol bh,cl pop cx shl bl,cl cmp ax,8 jnc pic2 in al,21h and al,bh or al,bl out 21h,al ret pic2: in al,0a1h and al,bh or al,bl out 0a1h,al ret setirq ENDP readint PROC mov bl,al sub bh,bh shl bx,2 sub ax,ax mov es,ax les bx,es:[bx] ret readint ENDP writeint PROC sub ah,ah mov si,ax shl si,2 push ds sub ax,ax mov ds,ax cli mov ds:[si],bx mov ds:[si+2],es sti pop ds ret writeint ENDP ; ; SB IRQ, acknowledge DMA completion and possibly restart (<1.00) ; sb_irqhandle PROC push ax push dx cmp [dsp_ver],200h jnc si_2 push cx push si call dsp_write ; <200, reload the DMA counter to start another run db 3,14h,0FFH,0feh pop si pop cx si_2: mov dx,[dsp_base] ; acknowledge the interrupt add dl,DSP_STAT ; assume 8-bit ack test [flags],F_16 jz ack8 inc dl ; nope 16-bit ack ack8: in al,dx mov al,20h ; acknowledge pic(s) test [dsp_irq],8 jz pic0only out 0a0h,al pic0only: out 020h,al pop dx pop ax iret sb_irqhandle ENDP ; ; int 8 IRQ ; ; calls user to get more stuff in buffer ; int8handler PROC pushf call [old8vect] test [state],S_PLAYING jz no8update test [state],S_IN8 jnz no8update or [state],S_IN8 push ax push bx push cx push dx push si push di push bp call sb_update pop bp pop di pop si pop dx pop cx pop bx pop ax and [state], NOT S_IN8 no8update: iret int8handler ENDP ;*********************************************************************** ; DMA hardware init ; dmah_init PROC cli mov ax,[dsp_dma] ; get ptr to chan info mov bx,SIZE dmap mul bx add ax,offset dmas mov [dmaptr],ax mov si,ax mov dx,cs:[si + dmap.d_mask] ; turn chan off mov ax,cs:[dsp_dma] or al,4 out dx,al mov dx,cs:[si + dmap.d_ff] ; clear FF out dx,al jmp $+2 mov ax,[dmabuf] sub bx,bx shl ax,1 rcl bx,1 shl ax,1 rcl bx,1 shl ax,1 rcl bx,1 shl ax,1 rcl bx,1 mov dx,cs:[si+dmap.d_base] out dx,al jmp $+2 mov al,ah out dx,al jmp $+2 mov dx,cs:[si + dmap.d_page] ; and page mov al,bl out dx,al jmp $+2 mov dx,cs:[si+ dmap.d_count] ; and count mov ax,[dmasiz] test [flags],F_16 ; adjust count for 16-bit chan jz dmah_8 shr ax,1 dmah_8: dec ax out dx,al jmp $+2 xchg al,ah out dx,al mov dx,cs:[si + dmap.d_mode] ; block, autoinit mov ax,[dsp_dma] and al,3 or al,58h out dx,al jmp $+2 and al,3 mov dx,cs:[si + dmap.d_mask] ; turn chan on out dx,al jmp $+2 sti ret dmah_init endp ; ; DMA hardware exit ; dmah_exit PROC mov bx,[dmaptr] mov ax,[dsp_dma] and al,3 or al,4 mov dx,cs:[bx + dmap.d_mask] ; turn chan off out dx,al jmp $+2 ret dmah_exit ENDP ; ; read DMA position pointer ; dmah_posr PROC mov bx,[dmaptr] mov dx,cs:[bx + dmap.d_ff] out dx,al mov dx,cs:[bx + dmap.d_count] in al,dx jmp $+2 xchg al,ah in al,dx jmp $+2 xchg al,ah mov bx,[dmasiz] test [flags],F_16 ; adjust len for 16-bit chan jz dmahp_8 shr bx,1 dmahp_8: sub bx,ax xchg ax,bx dec ax ret dmah_posr ENDP ;*********************************************************************** ; DMA setup subroutines ; ; warning!!!!! if memory is fragmented the dma buf may not wind up ; where we want it!!!! ; ; otherwise it winds up at the end of memory ; usercb PROC call [callback] jcxz cm_x clearmem: cld test [flags],F_16 jz cm_8 sub ax,ax rep stosw cm_x: ret cm_8: mov al,80h rep stosb ret usercb ENDP ; ; allocate DMA buffer (at end of memory) ; then init the DMA hardware ; dma_init PROC push es MOV AX,5800h ; set strategy = last fit int 21h push ax mov AX,5801h mov bx,2 int 21h mov es,[psp] mov bx,es:[2] ; size to rollover in bx and bx,0FFFH mov ax,[dmasiz] ; now calc needed size if they didn't spec or ax,ax jnz nosel2 mov ax,[dsp_mixfreq]; mixfreq /2 test [flags],F_16 ; *2 if 16 bit mode jnz nosel shr ax,1 nosel: test [flags],F_STEREO; *2 if stereo mode jz nosel2 shl ax,1 nosel2: add ax,15 ; actual size is paragraphs and ax,0FFF0H cmp ax,4000H ; limit at 16K because of EMS jbe nosel3 mov ax,4000H nosel3: cmp ax,400h ; minimum buf size is 1K jae nosel4 mov ax,400h nosel4: mov [dmasiz],ax ; adjust in case TOM != 0A000h (hahaha) shr ax,4 or bx,bx ; now calc memory to allocate jz ns_2 ; cmp ax,bx jc ns_2 add ax,bx ; if there would be rollover add in a bunch more ns_2: mov bx,ax ; allocate mem mov ah,48h int 21h jc dmai_x mov [dmabuf],ax call dmah_init ; init the card clc dmai_x: pop bx pushf ; reset strategy mov AX,5801H int 21h popf pop es MOV ax,ERR_NODMA ret dma_init endp dma_exit PROC push es call dmah_exit mov es,[dmabuf] mov ah,49h int 21h pop es ret dma_exit endp ; ; speed up/slow down int 8 ; fast8 PROC cli mov al,36h out 43h,al mov al,000h out 40h,al jmp $+2 mov al,040h out 40h,al sti ret fast8 ENDP slow8 PROC cli mov al,36h out 43h,al mov al,0h out 40h,al jmp $+2 mov al,0h out 40h,al sti ret slow8 ENDP ; ;*********************************************************************** ; Initialize helpers ; ; read a number from blaster string ; readhexnum PROC sub dx,dx sub ah,ah rnl: lodsb sub al,'0' jc rne cmp al,10 jc rnx sub al,7 rnx: cmp al,16 jnc rne shl dx,4 or dl,al jmp rnl rne: dec si ret readhexnum ENDP readdecnum PROC sub dx,dx sub ah,ah rdnl: lodsb sub al,'0' jc rdne cmp al,10 jnc rdne mov cx,dx shl dx,2 add dx,cx shl dx,1 add dx,ax jmp rnl rdne: dec si ret readdecnum ENDP ; ; read entire blaster string ; blasterread PROC push ds ; setup push es mov ds,[psp] mov ds,ds:[2ch] mov si,0 cld br_l: push cs pop es push si ; see if at blaster str mov di,offset blaster mov cx,8 rep cmpsb pop si jz gotb push ds pop es sub al,al mov cx,-1 mov di,si repne scasb ; find next string mov si,di ; intel is idiotic!!!! test byte ptr [si],0ffh ; see if at end of env jnz br_l ; loop if not nosb: mov ax,ERR_NOBLAST pop es pop ds stc ret gotb: add si,8 ; found string, point to value br_l1: lodsb or al,al jz br_done ; quit when at end cmp al,20h ; ignore spaces jz br_l1 cmp al,'a' ; make UC jc noadj sub al,20h noadj: cmp al,'A' ; check for base addr command jnz chi call readhexnum mov [dsp_base],dx jmp br_l1 chi: cmp al,'I' ; check for interrupt command jnz chd call readdecnum mov [dsp_irq],dx jmp br_l1 chd: cmp al,'D' ; check for DMA command jnz chh call readdecnum mov [dsp_lodma],dx jmp br_l1 chh: cmp al,'H' ; HIDMA command (SB16) jnz cht call readdecnum mov [dsp_hidma],dx jmp br_l1 cht: cmp al,'T' ; Type param and MIDI param ignored jz br_l1 cmp al,'P' jz br_l1 chx: mov ax,ERR_BADSTR ; otherwise bad string... pop es pop ds stc ret br_done: cmp [dsp_base],-1 jz nosb cmp [dsp_irq],-1 jz nosb cmp [dsp_lodma],-1 jz nosb mov ax,[dsp_irq] cmp ax,8 jc b_dl add ax,68h b_dl: add ax,8 mov [dsp_int],ax pop es pop ds clc ret blasterread endp ; ; validate 16-bit & stereo options ; sb16ok PROC mov ax,[dsp_lodma] mov [dsp_dma],ax test [flags],F_16 jz s16_ok mov ax,400h cmp ax,[dsp_ver] jc s16_e cmp [dsp_hidma],5 jc s16_e2 cmp [dsp_hidma],8 jnc s16_e2 mov ax,[dsp_hidma] mov [dsp_dma],ax s16_ok: clc ret s16_e: and [flags],NOT F_16 mov ax,ERR_NO16 stc ret s16_e2: mov ax,ERR_NODMA stc ret sb16ok ENDP sbprook PROC test [flags],F_STEREO jz sp_ok mov ax,300h cmp ax,[dsp_ver] jnc sp_ok and [flags],NOT (F_16 OR F_STEREO) mov ax,ERR_NOPRO stc ret sp_ok: clc RET sbprook ENDP ;*********************************************************************** ; Driver routines ; ; init enough to do a ping ; sbvx: mov ax,ERR_BADSYS STC ret sb_there PROC mov ah,30h ; check DOS ver int 21h cmp al,2 jc sbvx mov [dsp_base],-1 ; reset in case... mov [dsp_irq],-1 mov [dsp_lodma],-1 mov [dsp_hidma],-1 mov ah,51h ; get program PSP int 21h mov [psp],bx mov ax,[si + udata.mfreq] mov [dsp_mixfreq],ax mov ax,[si + udata.xflags] mov [flags],ax test ax,F_NOENV jz readstr mov ax,[si + udata.base] ; want to hand init... mov [dsp_base],ax mov ax,[si + udata.irq] mov [dsp_irq],ax mov ax,[si + udata.ldma] mov [dsp_lodma],ax mov ax,[si + udata.hdma] mov [dsp_hidma],ax jmp gotid readstr: push si call blasterread ; else read env pop si jc sbi_x2 gotid: push si call dsp_reset ; reset/ping and version jc nosb2 call dsp_version jc nosb2 pop si mov [dsp_ver],ax mov [si + udata.sbver],ax mov ax,[dsp_base] mov [si + udata.base],ax mov ax,[dsp_irq] mov [si + udata.irq],ax mov ax,[dsp_lodma] mov [si + udata.ldma],ax mov ax,[dsp_hidma] mov [si + udata.hdma],ax SUB AX,AX RET nosb2: pop si mov ax,ERR_NOBLAST sbi_x2: ret sb_there ENDP ; ; initialize driver data ; sb_init PROC test [state],S_INITTED jz sbixec jmp sbixe sbixec: mov ax,word ptr [si + udata.cb] mov word ptr [callback],ax mov ax,word ptr [si + udata.cb+2] mov word ptr [callback+2],ax mov ax,word ptr [si + udata.c_dmasiz] mov word ptr [dmasiz],ax push si call sb_there ; see if there jc sbi_xxx call sb16ok ; val check extended funcs jc sbi_xxx call sbprook jnc sbi_xc sbi_xxx: jmp sbi_x sbi_xc: cmp [dsp_ver],400h ; check for sb-16 mixing jnc mix16 mov ax,16960 ; nope, cal time const mov dx,15 ; by dividing into 1000000 mov bx,[dsp_mixfreq] cmp bx,4000 ; low freq (actual is 3906 :) jnc sbi_kkx mov bx,4000 ; jam in 4000 hz sbi_kkx: test [flags],F_STEREO ; double the xfer freq if stereo jz sbi_nstx shl bx,1 ; sbi_nstx: div bx cmp ax,22 ; limit check (45454) jnc sbi_chkh mov ax,22 jmp short sbi_vv sbi_chkh: cmp [dsp_ver],200h ; limit check (22222) jnc sbi_vv cmp ax,45 jnc sbi_vv mov ax,45 sbi_vv: mov cl,al ; cl = TC test [flags],F_STEREO ; double TC if stereo jz sbi_nst2 shl ax,1 ; sbi_nst2: mov bx,16960 ; calc actual freq xchg ax,bx ; by dividing into 1000000 mov dx,15 div bx test [flags],F_STEREO ; half the xfer freq if stereo jz sbi_nst shr ax,1 ; sbi_nst: neg cl mov [dsp_mixfreq],ax ; patch start routine with TC mov BYTE PTR cs:[patch],2 mov BYTE PTR cs:[patch+1],040h mov BYTE PTR cs:[patch+2],cl mov BYTE PTR cs:[patch+3],90h jmp sb_mixdn mix16: mov ax,[dsp_mixfreq] ; 16-bit, patch start routien with freq mov byte ptr cs:[patch],3 mov byte ptr cs:[patch+1],041h mov byte ptr cs:[patch+2],al mov byte ptr cs:[patch+3],ah mov al,byte ptr [flags] ; now patch the sign & stereo flags and al,3 shl al,4 mov byte ptr cs:[patch2+2],al and al,10h ; patch in 8/16 bit sel add al,0b6h ; remainder bits make autoreinit & fifo mode mov byte ptr cs:[patch2+1],al sb_mixdn: call dma_init ; init dma system jc sbi_x push es ; load vector mov ax,word ptr [dsp_int] call readint mov [oldvect],bx mov [oldvect+2],es mov bx,offset sb_irqhandle push cs pop es mov ax,[dsp_int] call writeint test [flags],F_AUTOUPDATE ; init int 8 if they want auto update JZ sbina mov al,8 call readint mov word ptr [old8vect],bx mov word ptr [old8vect+2],es push cs pop es mov bx,offset int8handler mov al,8 call writeint sbina: pop es or [state],S_INITTED sbi_x: pop si ; reload user data push ax pushf mov ax,[dsp_mixfreq] mov [si + udata.mfreq],ax mov ax,[dmasiz] mov word ptr [si + udata.c_dmasiz],ax mov ax,word ptr [dmabuf] mov word ptr [si + udata.c_dmabuf],ax popf pop ax ret sbixe: mov ax,ERR_ILLSEQ stc ret sb_init ENDP ; ; unload the driver hooks and shut off DMA ; sb_exit PROC test [state],S_INITTED JZ sexe call dma_exit ; stop DMA push es les bx,dword ptr [oldvect] ; unload int mov ax,[dsp_int] call writeint test [flags],F_AUTOUPDATE JZ sbena les bx,dword ptr [old8vect] mov al,8 call writeint sbena: pop es AND [state],NOT S_INITTED ret sexe: mov ax,ERR_ILLSEQ stc ret sb_exit ENDP ; ; start first song ; sb_play PROC test [state],S_INITTED JZ ple mov [dma_curr],0 ; mov cx,[dmasiz] ; fill DMA buf test [flags],F_16 ; size in words if 16-bit xfer jz sbp_n16 shr cx,1 sbp_n16: sub di,di mov es,[dmabuf] call usercb call dsp_write ; common, write freq patch: db 2,40h,46,90h ; patched with timing (uses 41h for SB16) cmp [dsp_ver],0200h jnc sbp_2 call dsp_write ; up to 2.00 db 4,14h,0ffh,0feh,0d1h ; single-burst DMA jmp sbp_x sbp_2: cmp [dsp_ver],0400h jnc sbp_4 cmp [dsp_ver],0300h jnc sbp_2a call selms ; stereo bit (pro only) sbp_2a: call dsp_write ; 2 db 5,48h,0ffh,0feh,0d1h,90h; autoreinit DMA, high speed jmp sbp_x sbp_4: call dsp_write ; do 4 patch2: db 5,0B6h,00,0ffh,0efh,0d1h; stereo, sign, bits, dmacount, fifo ; this is an autoinit command sbp_x: mov bl,1 call setirq or [state],S_PLAYING RET ple: mov ax,ERR_ILLSEQ stc ret endp sb_play ; ; shut down the sound plaster ; but leave driver hooks and DMA intact ; sb_playstop PROC test [state],S_PLAYING jz pse and [state],NOT S_PLAYING mov bl,0 ; PIC off call setirq call dsp_reset ; reset the thing, also disables speaker clc ret pse: mov ax,ERR_ILLSEQ stc ret sb_playstop ENDP ; ; get size of empty part of buf ; get_pos PROC call dmah_posr ; hack because memory managers mov di,ax ; sometimes return bad data call dmah_posr sub ax,di jnz get_pos gpx: mov ax,di ret get_pos ENDP ; ; call this to have the buffer updated ; sb_update proc test [state],S_PLAYING jz pse push es mov es,[dmabuf] call get_pos ; get current index in buf cmp ax,[dma_curr] ; get out if didn't change :) jz noupdate jc wrapped ; see if wrapped mov cx,ax ; no, cx = number free xchg ax,[dma_curr] sub cx,ax mov di,ax ; di = last pos call usercb jmp noupdate wrapped: xchg ax,[dma_curr] ; wrapped, ax = old pos mov di,ax ; di = last pos mov cx,[dmasiz] ; cx = buf end - last pos sub cx,ax call usercb mov cx,[dma_curr] ; now do the bottom sub di,di ; call usercb noclr2: noupdate: pop es clc ret sb_update endp ; ; clear the DMA buffer ; and prepare for new sound ; ; offsets us ahead of the data by 1/16th second ; sb_resync PROC test [state],S_PLAYING jz rse push es mov es,[dmabuf] sub di,di mov cx,[dmasiz] ; clear everything call clearmem call get_pos ; locate DMA position mov cx,ax mov bx,[dsp_mixfreq] ; we are going to offset by 1/16th second shr bx,3 test [flags],F_16 ; if 16 bit two bytes/sample jnz sbr_n shr bx,1 sbr_n: cmp bx,[dmasiz] ; if the result is too big jc okall mov bx,[dmasiz] ; offset by half the DMA buf shr bx,1 okall: add ax,bx ; get new pos cmp ax,[dmasiz] ; take mod jc okx sub ax,[dmasiz] ; ptr > new, find size left to ptr sub cx,ax jmp okvv okx: mov cx,[dmasiz] ; new > ptr, subtract from end of buf sub cx,ax okvv: mov [dma_curr],ax ; curr ptr = here mov di,ax call usercb ; user fill ; already cleared everything ; so, no clear here pop es clc ret rse: MOV AX,ERR_ILLSEQ stc ret sb_resync ENDP ; ; kill all activity and unload the driver ; sb_reset PROC push si test [state],S_PLAYING JZ sbres_2 call sb_playstop sbres_2: test [state],S_INITTED JZ sbres_3 call sb_exit sbres_3: AND [state],S_INITTED OR S_PLAYING pop si call sb_there ret sb_reset ENDP ;*********************************************************************** ; driver main routine ; ; modifies nothing but AX and flags ; sb_jtab dw sb_reset ; kill any activity and perform are-you-there dw sb_init,sb_exit ; initialize parameters dw sb_play,sb_playstop ; play start/stop dw sb_update,sb_resync ; update/start new thread sb_entrypoint PROC FAR push bx push cx push dx push si push di mov bx,[si + udata.command] cmp bx,7 jnc sb_err shl bx,1 call [bx+sb_jtab] jmp sb_x sb_err: MOV AX,ERR_NOFUNC stc sb_x: pop di pop si pop dx pop cx pop bx ret sb_entrypoint ENDP ifdef TESTIT ;*********************************************************************** ; test ; assume ss: rss done dw 0 count1 dw 0 count2 dw 10 pack label byte dw 1 ; command dw F_AUTOUPDATE ; flags dd 0 ; cb dw 0 ; ver dw 0 ; freq dw 0 ; dmasiz dw 0 ; dmabuf dw 0 ; base dw 0 ; irq dw 0 ; ldma dw 0 ; hdma ; ; square qave, approx 4KHZ ; xcb proc far cld mov bx,[count1] add bx,cx cmp bx,4000 jc next mov cx,4000 sub cx,[count1] sub bx,4000 push cs call subx mov cx,bx dec [count2] jnz next inc [done] next: mov [count1],bx subx: jcxz retx test [count2],1 jz slow test di,1 jnz odd1 even1: mov al,0ffh stosb loop odd1 retx: ret odd1: mov al,00 stosb loop even1 ret slow: mov si,di and si,3 shl si,1 jmp cs:[enslow+si] enslow dw sl1,sl2,sl3,sl4 sl1: mov al,0ffh stosb loop sl2 ret sl2: mov al,0ffh stosb loop sl3 ret sl3: mov al,00h stosb loop sl4 ret sl4: mov ax,00h stosb loop sl1 ret xcb endp go PROC push cs pop ds mov bx,400h mov ax,4a00h int 21h mov ax,1000 mov si,offset pack mov [si + udata.mfreq],ax mov word ptr [si + udata.cb + 2],cs mov word ptr [si + udata.cb],offset xcb call sb_entrypoint mov [si + udata.command],3 call sb_entrypoint rrr: ; mov [si + udata.command],5 ; call sb_entrypoint test [done],1 jz rrr mov [si + udata.command],2 call sb_entrypoint mov [si + udata.command],0 call sb_entrypoint mov ax,4c00h int 21h go endp rss segment word public USE16 stack 'STACK' dw 512 DUP (?) rss ends endif sbdrv ends ; ; the following is a cute way to have two end statements, one with ; an address and one without ; ; since an END statement will terminate processing, but an error ; will occur if a conditional is open, we use a silly macro to close ; the conditional and then issue the end statement ; TOEND MACRO addr,E E&NDIF E&ND addr ENDM ifdef TESTIT TOEND go,E else TOEND ,E endif