Files
sibo-playground/code/SIBOSDK/ldd/sndfrc.asm
T

522 lines
18 KiB
NASM
Raw Normal View History

2026-07-06 18:01:36 +01:00
title SNDFRC -- Epoc/Os Sound Device Driver Using FRC
subttl Copyright (c) Psion PLC 1992
name SNDFRC
;
; VER DATE BY DESCRIPTION
; ----- -------- ---- -----------
; 1.00A 21/04/92 JH Alpha release
;
TURBOC equ 1
;
include epocdef.inc
include epocmac.inc
include epocpan.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
MAX_NOTES equ 500
; This is allocated in the user space for the I/O channel
SndFrcEnt struc
SndFrcIo ChanEnt <> ; I/O channel hdr, MUST be first
SndFrcWrite RqEnt <>
SndFrcHandler dw ?
SndFrcFlags dw ?
SndFrcPstat dw ?
SndFrcEnt ends
SndChannel struc
Pid dw ? ; owning channel pid
OpenFrcChan db ? ; channel open status (KEEP ORDER)
WriteStat db ? ; write completion status (ORDER)
DataPtr dw ? ; where we are in buffer
DataLength dw ? ; number of notes to play
DataPreBuffer dw ? ; First Frc timout ptr
DataBuffer dw MAX_NOTES dup (?)
SndChannel ends
CodeSeg
ProcBegin@ SndfrcLDD
; ==========
; The externally loadable device table
dw LDDSignature
db 'MUS',0,0,0,0,0
dw (VectorEnd-Vector)/2
Vector:
dw SndfrcInstall
dw SndfrcRemove
dw SndfrcHold
dw SndfrcResume
dw SndfrcReset
dw SndfrcUnits
dw SndfrcOpen
dw SndfrcStrategy
VectorHandler:
dw SndfrcHandler
VectorEnd:
ProcEnd noret
SoundChan SndChannel <>
ProcBegin@ disableFrcInt
; =============
; Disable the generation of Frc interrupts
;
pushf
cli
in al, A1InterruptMask
and al, not (mask FrcExpired)
out A1InterruptMask, al ; stop Interrupt controller from
popf ; generating Frc interrupts
ret
ProcEnd
;
ProcBegin@ enableFrcInt
; ============
; Starts the free running counter.
;
pushf
cli
in al, A1Control
or al, mask FrcSource or mask FrcMode
out A1Control, al ; set 512khz and prescale mode
mov ax, 5120 ; request a FrcInt every 10ms
out A1FrcControl, ax
out A1FrcEoi, al ; clear any junk Frc Interrupt
in al, A1InterruptMask
or al, mask FrcExpired ; allow interrupt controller to
out A1InterruptMask, al ; generate a Frc Interrupt
popf
ret
ProcEnd noret
;
ProcBegin@ FrcInterrupt,far
; ============
; The Frc interrupt routine.
; Although we could get addressability to the clients data space since it is
; possible that the operating system is currently moving the
; data space of the client in which case the data buffer to be played
; may not be valid.
; The ISR is called by the operating system after all registers have been
; preserved.
; We MUST NOT cause a reschedule directly since the ISR would take a long
; time to complete if we did.
;
; Called in the context of whatever was running when the interrupt went off
;
out A1FrcEoi, al ; clear Frc Interrupt
mov si, SoundChan.DataPtr
cmp byte ptr cs:[si+1], 0 ; look at the duration byte
je finishedThatNote ; if zero goto the next note
dec byte ptr cs:[si+1] ; reduce timeout
endFrcInterrupt:
stc ; no potential reschedule required
endFrcInterruptFlags:
out A1NonSpecificEoi, al ; finish the ISR
ret
finishedThatNote:
cmp SoundChan.DataLength, 0
je finishedAllNotes
dec SoundChan.DataLength ; one less note to play
inc si
inc si ; find next note,volume and duration
mov SoundChan.DataPtr, si ; updated ptr
mov al, byte ptr cs:[si]
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 that note
jmp short endFrcInterrupt
finishedAllNotes:
call StopSound
mov SoundChan.WriteStat, 0 ; finished playing that lot
mov bx, SoundChan.Pid ; get the channel owner
IoSignalByPidNoReSched ; signal ourselves, causes handler
clc ; reschedule if possible
jmp short endFrcInterruptFlags
ProcEnd noret
;
ProcBegin@ setFrcIntVector
; ===============
; Set the ISR pointer to our FRC interrupt routine.
; MUST be done with ints disabled so we cant be moved by the operating
; system whilst setting the address.
;
pushf
cli
push bx
mov al, HwIrq5Revector
mov bx, offset FrcInterrupt
mov cx, cs ; this ok as ints disabled.
GenSetRevector
pop bx
popf
ret
ProcEnd noret
;
ProcBegin@ StopSound
; =========
; Stop the sound generation.
;
call disableFrcInt ; Stop Frc interrupts
mov dx, PSoundControl
mov al, 1
out dx, al ; stop sound chip
HwComboOff ; stop amplifier
ret
ProcEnd noret
ProcBegin@ SndfrcInstall,far
; =============
; Install vector called when application calls p_loadldd()
; Set OpenFrcChan to zero.
; In:
; NONE
; Out:
; Carry clear if installed ok
; Carry set if failed to install
;
and SoundChan.OpenFrcChan, 0
clc ; installed OK
ret
ProcEnd noret
;
ProcBegin@ SndfrcRemove,far
; ============
; Remove vector called when application calls p_devdel()
;
cmp SoundChan.OpenFrcChan, 0
je soundRemoved
mov al, InUseErr
stc ; channel open, fail to remove
ret
ProcEnd noret
;
ProcBegin@ SndfrcReset,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 ; disable Frc ints etc
mov al, HwIrq5Revector
GenResetRevector ; restore the default ROM ISR vector
mov al, mask FrcExpired
HwFreeChannel ; free Frc resource
HwFreeCombo ; free up sound usage resource
and word ptr SoundChan.OpenFrcChan, 0 ; all closed now
soundRemoved:
clc ; removed OK.
ret
ProcEnd noret
ProcBegin@ SndfrcHold,far
; ==========
; Hold called when another driver loaded or switched off/power fail.
; In:
; AH - reason.
; Out:
; NONE
cmp SoundChan.OpenFrcChan, 0
je soundHeld ; dont do anything if not open
call StopSound ; shut down ints and sound
soundHeld:
ret
ProcEnd noret
ProcBegin@ SndfrcResume,far
; ============
; Resume called after a Hold to allow the driver to continue.
; If we were running enabling the Frc interrupt will cause the
; FrcInt routine to be called thus allowing us to count down the current
; duration as though nothing had happened before going to the next note.
; In:
; NONE
; Out:
; NONE
;
cmp SoundChan.OpenFrcChan, 0
je soundResumed ; dont do anything if not open
call setFrcIntVector ; we may have moved, set CS correctly
cmp SoundChan.WriteStat, PendingErr
jne soundResumed ; dont enable ints if no write queued
HwComboOn ; switch amplifier on
call enableFrcInt ; continue where we left off
soundResumed:
ret
ProcEnd noret
ProcBegin@ SndfrcUnits,far
; ===========
; We can only handle 1 sound channel at a time.
;
mov ax, 1 ; support 1 unit at a time
ret
ProcEnd noret
ProcBegin@ SndfrcOpen,far
; ==========
; Open the sound device driver.
; This runs is the context of the process that has called p_open("MUF:").
;
; 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 snd already in use, if not grab it
jc sndAlreadyInUse
mov al, mask FrcExpired
HwGetChannel ; see if FRC already in use, if not grab it
jc endFrcInUse
mov cx, (size SndFrcEnt)
HeapAllocateCell
jc endOpenNoMemory ; get users I/O channel
mov bx, ax ; the allocated cell handle
mov al, (VectorHandler-Vector)/2
IoAddHandler ; handler handle in AX
jc endOpenFreeMemory
mov [bx].SndFrcHandler, ax
xchg bx, dx
xor cx, cx
IoRequestReset ; call Reset if client terminates
xchg bx, dx
mov [bx].SndFrcIo.ChanNext, bx ; we are a root device
mov [bx].SndFrcIo.ChanSignature, IoChanSignature
mov [bx].SndFrcIo.ChanLibHandle, dx ; This is our device handle
and [bx].SndFrcPstat, 0 ; no write pending yet
and [bx].SndFrcFlags, 0
ProcId
mov SoundChan.Pid, ax ; remember client id for ISR
mov SoundChan.OpenFrcChan, 1 ; now open
call setFrcIntVector ; set up the ISR address ptr
clc
ret
endOpenFreeMemory:
push ax
HeapFreeCell
pop ax
endOpenNoMemory:
push ax
mov al, mask FrcExpired
HwFreeChannel ; free Frc resource
pop ax
endFrcInUse:
push ax
HwFreeCombo ; free sound resource as failed somewhere
pop ax
stc
sndAlreadyInUse:
ret
ProcEnd noret
ProcBegin@ SndfrcHandler,far
; ==============
; Called when an I/O signal is generated and were enabled. We should only
; enabled when a write request is outstanding.
; We need to check the LOW side drivers completion status to see if all the
; notes have been played yet (SoundChan.WriteStat!=E_FILE_PENDING).
; Typically write the handler defensivly since complex drivers may leave the
; handler enabled other than when a request is outstanding.
;
; In:
; DS,ES,SS == Process data space
; BX == channel control block from open request
; Out:
; Carry clear
; Did not consume signal
; Carry set
; al = 0 - Leave handler disabled
; al = non 0 - Enable handler
;
test [bx].SndFrcFlags, RQ_DISABLE_HANDLER
jnz signalNotForUs ; we are in StategyVector code
cmp [bx].SndFrcPstat, 0
je signalNotForUs ; no write request queued yet
mov al, SoundChan.WriteStat
cmp al, PendingErr
je signalNotForUs ; pending write request not finished
cbw
xor si, si
xchg si, [bx].SndFrcPstat ; no write queued if Pstat is zero
mov [si], ax
IoSignal ; complete the original write request
xor ax, ax ; disable handler now
stc
ret
signalNotForUs:
clc
ret
ProcEnd noret
ProcBegin@ CancelWrite
; ===========
; Cancel any outstanding write request
;
xor di, di
xchg di, [bx].SndFrcPstat
or di, di ; see if any write is currently queued
jz noWriteQueued
push bx
mov bx, [bx].SndFrcHandler
mov cl, HandlerDisable
IoEnableHandler ; make SndfrcHandler non callable
pop bx
call StopSound ; stops Frc ints
mov word ptr [di], CancelErr ; set write completion stat
IoSignal ; and signal write completion
noWriteQueued:
mov SoundChan.WriteStat, 0
ret
ProcEnd noret
;
ProcBegin@ SndfrcStrategy,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 infact no sync calls to the I/O system
; thus the wait handler cannot be run. In more complex drivers it may be
; difficult to determine all possible paths through the code thus the driver
; should be written defensivly (use RQ_DISABLE_HANDLER type mechanism).
; 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
or [bx].SndFrcFlags, RQ_DISABLE_HANDLER
mov al, byte ptr [si].RqFunction
cmp al, IoFuncWrite
jne tryCancel
cmp [bx].SndFrcPstat, 0
jne writeQueued ; panic if write already queued
mov di, [si].RqA2Ptr
mov cx, [di] ; length to write
cmp cx, MAX_NOTES
jae writeQueued ; length too much, panic dvr
mov di, [si].RqStatusPtr
mov word ptr [di], PendingErr
mov [bx].SndFrcPstat, di ; write request now queued
mov si, [si].RqA1Ptr ; ptr to data to write as sound
mov di, offset SoundChan.DataPreBuffer
and word ptr [di], 0 ; set pre buffer len to zero
mov SoundChan.DataPtr, di
inc di
inc di
mov SoundChan.DataLength, cx ; this many notes to play
mov SoundChan.WriteStat, PendingErr
pushf
cli ; we can set ES when ints are off
push cs
pop es ; copy the notes+vol and duration
popf ; into buf we can access in ISR
rep movsw ; dont use CS override
mov es, [bp].IntES
push bx
mov bx, [bx].SndFrcHandler
mov cl, HandlerEnable
IoEnableHandler ; allow waithandler to be called
pop bx
HwComboOn
call enableFrcInt ; start the sound off
and [bx].SndFrcFlags, NOT RQ_DISABLE_HANDLER
xor ax, ax ; queued OK
ret
writeQueued:
mov al, PanicIoPending
ProcPanic ; stop bad applications
tryCancel:
cmp al, IoFuncCancel
jne tryClose
call CancelWrite ; stop any queued write
and [bx].IoRequestFlags, NOT RQ_DISABLE_HANDLER
jmp short signalOk ; cancel completed ok.
tryClose:
cmp al, IoFuncClose
jne notForUs
call CancelWrite ; stop any queued writes
push bx
push [bx].SndFrcHandler
push dx
mov al, HwIrq5Revector
GenResetRevector
pop bx ; channel handle
xor cx, cx
IoRequestResetCancel ; dont call Reset vector now
pop bx ; handler handle
IoRemoveHandler
pop bx ; alloc handle
HeapFreeCell
mov al, mask FrcExpired
HwFreeChannel ; free Frc resource
HwFreeCombo ; not in use any more
and word ptr SoundChan.OpenFrcChan, 0 ; all closed now
signalOk:
xor ax, ax
mov di, [si].RqStatusPtr
stosw ; the I/O request completed OK
IoSignal
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 SndfrcLDD