Files
sibo-playground/code/SIBOSDK/ldd/snddvr.asm
T
2026-07-06 18:01:36 +01:00

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