title SNDDVR -- Epoc/Os Sound Device Driver Example subttl Copyright (c) Psion PLC 1992 name SNDDVR ; ; VER DATE BY DESCRIPTION ; ----- -------- ---- ----------- ; 1.00A 21/04/92 JH Alpha release ; TURBOC equ 1 ; include epocdef.inc include epocmac.inc include epoclib.inc include epocser.inc include ossibo.inc HandlerDisable equ 000h ; the hnadler should not be called HandlerEnable equ 001h ; the handler should be called CodeSeg ProcBegin@ SnddvrLDD ; ========== ; The externally loadable device table dw LDDSignature db 'MUS',0,0,0,0,0 dw (VectorEnd-Vector)/2 Vector: dw SnddvrInstall dw SnddvrRemove dw SnddvrHold dw SnddvrResume dw SnddvrReset dw SnddvrUnits dw SnddvrOpen dw SnddvrStrategy VectorHandler: dw SnddvrHandler VectorEnd: ProcEnd noret SoundChanOpen db ? ProcBegin@ CancelWrite ; =========== ; Cancel any outstanding I/O write request. ; Set the completion status word to E_FILE_CANCEL and signal the I/O ; semaphore. ; In: ; BX - sound control block ptr ; Out: ; NONE ; mov dl, CancelErr IoSerSignalUserWrite ; complete any write with E_FILE_CANCEL IoSerTimerCancel ; harmless if nothing queued mov al, HandlerDisable IoSerSetHandler ; make SnddvrHandler non callable ; FALL THROUGH to StopSound ProcEnd noret ; ProcBegin@ StopSound ; ========= ; Stop the sound by turning off the sound chip and shuting down the ; amplifier. ; In: ; BX - sound control block ptr ; Out: ; NONE ; mov dx, PSoundControl mov al, 1 out dx, al ; stop sound chip HwComboOff ; stop amplifier ret ProcEnd noret ; ProcBegin@ PlaySounds ; ========== ; Play any more sounds available. ; Set the note volume from bits 6 and 7 of the first byte. ; Write the note to the chip from bits 0-5 of the first byte. ; Queue the timer to expire on timeout specified in 2nd byte. ; In: ; BX - channel control blk ; Out: ; AL as required by SnddvrHandler routine ; cmp [bx].IoRequestRqWrite.RqA2Ptr, 0 je finishedAllNotes cld dec [bx].IoRequestRqWrite.RqA2Ptr ; one less note to play mov si, [bx].IoRequestRqWrite.RqA1Ptr lodsw ; note+volume to AL, duration to AH mov [bx].IoRequestRqWrite.RqA1Ptr, si xor cx, cx mov cl, ah ; the duration (in 1/10ths) push ax shr ax, 1 ; factor out the volume bits and al, 60h ; into bits 5+6 for the hardware mov ah, al mov al, A2IWrite out A2Index, al mov al, ah out A2Control, al ; set the volume for that note pop ax mov dx, PSoundControl and al, 3fh ; bottom 6 bits are note info out dx, al ; and play the note xor dx, dx IoSerQueueTimer ; DX:CX is timeout in 1/10ths mov al, 1 ; enable handler ret finishedAllNotes: call StopSound ; stop sound activity IoSerSignalUserWriteOk ; write request completed ok xor ax, ax ; disable handler ret ProcEnd noret ; ProcBegin@ SnddvrInstall,far ; ============= ; Install vector called when application calls p_loadldd() ; In: ; NONE ; Out: ; Carry clear if installed ok ; Carry set if failed to install ; and SoundChanOpen, 0 ; no channel open yet clc ; installed OK ret ProcEnd noret ; ProcBegin@ SnddvrRemove,far ; ============ ; Remove vector called when application calls p_devdel() ; In: ; NONE ; Out: ; Carry clear if removed ok ; Carry set if failed to remove ; cmp SoundChanOpen, 0 je soundRemoved ; dont do anything if not open mov al, InUseErr stc ; channel open, fail to remove ret ProcEnd noret ; ProcBegin@ SnddvrReset,far ; =========== ; Reset vector called if the client terminated. ; We can only get a reset if weve got channel open and client died ; since we cancel the reset request when the channel is closed. ; NOTE: The clients data space no longer exists thus all data required to ; close down the channel must be avaliable in the drivers space. ; In: ; NONE ; Out: ; NONE ; call StopSound HwFreeCombo ; allow other sound components to work. and SoundChanOpen, 0 ; no channel open now soundRemoved: clc ; removed OK. ret ProcEnd noret ProcBegin@ SnddvrHold,far ; ========== ; Hold called when another driver loaded or switched off/power fail. ; In: ; AH - reason. ; Out: ; NONE cmp SoundChanOpen, 0 je soundHeld ; dont do anything if not open call StopSound ; stop any current sounds. soundHeld: ret ProcEnd noret ProcBegin@ SnddvrResume,far ; ============ ; Resume called after a Hold to allow the driver to continue. ; If we were running the outstanding timer request will complete at some ; in the future at which point we will play the next note. ; Note - we dont immediatly start playing the note that was being played ; when the hold was called - this may be a bug for some applications. ; In: ; NONE ; Out: ; NONE cmp SoundChanOpen, 0 je soundResumed ; dont do anything if not open HwComboOn ; waithandler will run eventually soundResumed: ret ; when the timer expires if we were active. ProcEnd noret ProcBegin@ SnddvrUnits,far ; =========== ; We can only handle 1 sound channel at a time. ; mov ax, 1 ; support 1 unit at a time ret ProcEnd noret ProcBegin@ SnddvrOpen,far ; ========== ; Open the sound device driver. ; This runs is the context of the process that has called p_open("MUS:"). ; ; In: ; DS=ES=SS - Process data space. ; DX - OS device handle ; SI - ptr to OpenEnt struct ; Out: ; DX - OS device handle ; Carry Clear ; BX - channel, DX is as passed (device handle) ; Carry Set ; AL - error number ; ; All device drivers MUST have a ChanEnt as first item at returned BX ; HwGetCombo ; see if already in use, if not grab it jc sndAlreadyInUse ; allocate in clients heap the I/O control block, add vector 9 as a ; waithandler and open a timer channel. Tidies up all resources if there ; are any errors. mov cx, (size IoRequestEnt) IoSerOpenTimerHandler jc endOpenNoMemory ; sets up ChanEnt, returns BX ; request reset vector be called if client terminates before closing channel xchg bx, dx xor cx, cx IoRequestReset ; BX=device handle, CX= channel indicator xchg bx, dx mov SoundChanOpen, 1 clc ; Opened Ok, BX=channel, DX=device ret ; endOpenNoMemory: push ax HwFreeCombo ; free up as failed pop ax stc sndAlreadyInUse: ret ProcEnd noret ProcBegin@ SnddvrHandler,far ; ============== ; Called when an I/O signal is generated, it may be our timer to indicate ; that we should play the next note if any. ; In: ; DS,ES,SS == Process data space ; BX = the open sound channel ; Out: ; Carry clear ; Did not consume signal ; Carry set ; al = 0 - Leave handler disabled ; al = non 0 - Enable handler ; ; check that were not currently in any StategyVector code, (anti nesting) test [bx].IoRequestFlags, RQ_DISABLE_HANDLER jnz signalNotForUs ; see if we currently have a timer request outstanding test [bx].IoRequestFlags, RQ_TIMER jz signalNotForUs ; timer not queued ; see if the outstanding timer has completed cmp [bx].IoRequestTimerStat, PendingErr je signalNotForUs ; timer still pending ; timer completed, play next sound if any and re-queue timer and [bx].IoRequestFlags, NOT RQ_TIMER call PlaySounds ; returns AL as required stc ret signalNotForUs: clc ; we didnt use the signal ret ProcEnd noret ProcBegin@ SnddvrStrategy,far ; ============== ; Strategy vector called by an I/O request on the opened sound device driver ; channel. ; The wait handler may be called (when enabled ) unless we block it when we ; call a sync I/O request. Allowing the waithandler to run whilst in the ; strategy vector is generally not a good thing to do. ; In this particular example there are synchronous calls to the I/O system ; to cancel the outstanding timer (if queued) and use the signal generated ; by the cancel. ; ; In: ; DS,ES,SS == Process data space ; BX == channel control block from open request ; DX == device handle ; SI == RqEnt pointer ; Out: ; Carry clear ; - request queued sucessfully (may have completed) ; Carry set ; - error queuing the I/O request ; cld ; Stop the WaitHandler from being called temporarily or [bx].IoRequestFlags, RQ_DISABLE_HANDLER mov al, byte ptr [si].RqFunction cmp al, IoFuncWrite jne tryCancel ; P_FWRITE I/O function request mov di, [si].RqA2Ptr mov cx, [di] ; length to write mov di, [si].RqA1Ptr ; ptr to data to write mov si, [si].RqStatusPtr IoSerCheckWriteSI ; set *SI to Pending, PendingPanic's mov [bx].IoRequestRqWrite.RqA2Ptr, cx mov [bx].IoRequestRqWrite.RqA1Ptr, di mov al, HandlerEnable IoSerSetHandler ; make SnddvrHandler callable HwComboOn ; start the amplifier call PlaySounds ; start the sound and [bx].IoRequestFlags, NOT RQ_DISABLE_HANDLER ret tryCancel: cmp al, IoFuncCancel jne tryClose ; P_FCANCEL I/O function request call CancelWrite ; cancel any outstanding requests and [bx].IoRequestFlags, NOT RQ_DISABLE_HANDLER jmp short signalOk ; cancel completed ok. tryClose: cmp al, IoFuncClose jne notForUs ; P_FCLOSE I/O function request push bx push dx call CancelWrite ; cancel any outstanding requests ; cancel the request to call the Reset vector pop bx ; our I/O device handle xor cx, cx ; MUST be same as passed to IoRequestReset IoRequestResetCancel ; dont call reset vector now ; close timer, remove handler, free alloc pop bx IoSerCloseTimerHandler HwFreeCombo ; not in use any more and SoundChanOpen, 0 ; no channel open now signalOk: xor ax, ax mov di, [si].RqStatusPtr stosw ; set completion status word to zero IoSignal ; complete the I/O request xor ax, ax ; also clears carry ret notForUs: and [bx].IoRequestFlags, NOT RQ_DISABLE_HANDLER IoRoot ; pass on request as not for us ret ProcEnd noret EndCodeSeg ; stack segment stack para 'data' ; stack ends ; end SnddvrLDD