396 lines
12 KiB
NASM
396 lines
12 KiB
NASM
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
|
|
|