447 lines
14 KiB
NASM
447 lines
14 KiB
NASM
title ATIMDVR -- Attached timer device driver example
|
|
subttl Copyright (c) Psion PLC 1991
|
|
name ATIMDVR
|
|
;
|
|
; VER DATE BY DESCRIPTION
|
|
; ----- -------- ---- -----------
|
|
; 1.00A 01/02/92 JH Alpha release
|
|
;
|
|
TURBOC equ 1
|
|
;
|
|
include epocdef.inc
|
|
include epocmac.inc
|
|
include epocpan.inc
|
|
include epoclib.inc
|
|
include epocser.inc
|
|
|
|
HandlerDisable equ 000h ; the hnadler should not be called
|
|
HandlerEnable equ 001h ; the handler should be called
|
|
;
|
|
TimerEnt struc
|
|
TimerIo ChanEnt <> ; I/O device driver hdr
|
|
ReadRq RqEnt <> ; read request info
|
|
ReadStat dw ? ; read completion status
|
|
InStrategy dw ? ; internal control
|
|
WaitHandler dw ? ; wait handler handle
|
|
TimerChan dw ? ; open timer channel handle
|
|
Timeout dd ? ; read timout
|
|
TimerStat dw ? ; timer completion status
|
|
TimerEnt ends
|
|
|
|
CodeSeg
|
|
|
|
ProcBegin@ AttachedTimerLDD
|
|
; =================
|
|
; The externally loadable Attached timer device table
|
|
dw LDDSignature
|
|
db 'ATM',0,0,0,0,0
|
|
dw (VectorEnd-Vector)/2
|
|
Vector:
|
|
dw AttachedTimerInstall
|
|
dw AttachedTimerRemove
|
|
dw AttachedTimerHold
|
|
dw AttachedTimerResume
|
|
dw AttachedTimerReset
|
|
dw AttachedTimerUnits
|
|
dw AttachedTimerOpen
|
|
dw AttachedTimerStrategy
|
|
VectorHandler:
|
|
dw AttachedTimerHandler
|
|
VectorEnd:
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerInstall,far
|
|
; =====================
|
|
; Called on device installation by the operating system
|
|
;
|
|
clc ; installed OK
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerRemove,far
|
|
; ====================
|
|
; Always allow to be removed.
|
|
;
|
|
clc ; removed ok.
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerHold,far
|
|
; ==================
|
|
; This does not need to do anything.
|
|
;
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerResume,far
|
|
; ====================
|
|
; This does not need to do anything.
|
|
;
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerReset,far
|
|
; ===================
|
|
; This will never be called since we dont request it to be called.
|
|
; The lower level driver and timer driver will handle the process
|
|
; terminating abnormally.
|
|
;
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerUnits,far
|
|
; ===================
|
|
; Called by device query units operating system service
|
|
;
|
|
mov ax, -1 ; supports many open requests
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ AttachedTimerOpen,far
|
|
; ==================
|
|
; Called by the IoOpen system service
|
|
;
|
|
; In:
|
|
; DX - device handle
|
|
; SI - ptr to OpenEnt struct
|
|
; Out:
|
|
; Carry clear
|
|
; BX - open channel handle
|
|
; DX - device handle as passed
|
|
; Carry set
|
|
; AL - error number
|
|
; DX - device handle as passed
|
|
;
|
|
cld
|
|
mov cx, (size TimerEnt)
|
|
HeapAllocateCell
|
|
jc endOpen ; return that error
|
|
mov bx, ax
|
|
|
|
; Initialise the allocated cell to zero.
|
|
mov di, ax
|
|
shr cx, 1 ; alloc cells should be even no of bytes.
|
|
xor ax, ax
|
|
rep stosw ; initialize all struct to zero
|
|
|
|
; Add the waithandler function
|
|
mov al, (VectorHandler-Vector)/2
|
|
IoAddHandler
|
|
jc freeCellExit ; report the add handler error
|
|
mov [bx].WaitHandler, ax ; handler handle
|
|
|
|
; Open a timer channel
|
|
push bx ; save the alloc cell handle
|
|
mov cx, sp ; save SP for now
|
|
xor ax, ax
|
|
push ax
|
|
mov ax, ':' shl 8 + 'M'
|
|
push ax
|
|
mov ax, 'I' shl 8 + 'T'
|
|
push ax
|
|
mov bx, sp ; BX is pointer to name to 'TIM:'
|
|
IoOpen ; CX and DX are irrelvant for a timer
|
|
mov sp, cx
|
|
pop bx
|
|
jc freeHandlerExit ; report the timer open error.
|
|
mov [bx].TimerChan, ax
|
|
|
|
; Set up the channel header
|
|
mov [bx].ChanSignature, IoChanSignature
|
|
mov [bx].ChanLibHandle, dx
|
|
mov [bx].ChanNext, bx ; were a root driver for now
|
|
|
|
; Attach this driver to the currently open driver Hierarchy
|
|
mov cx, bx ; channel were attaching
|
|
mov al, IoFuncAttach
|
|
mov bx, [si].OpenChan ; driver to attach to
|
|
IoWithWait
|
|
clc ; return BX (ie what attached to)
|
|
|
|
; Finished the open request
|
|
endOpen:
|
|
ret
|
|
|
|
; An error has occured, free the handler then cell in BX and return an error
|
|
freeHandlerExit:
|
|
push ax
|
|
push bx
|
|
mov bx, [bx].WaitHandler
|
|
IoRemoveHandler
|
|
pop bx
|
|
pop ax
|
|
|
|
; An error has occured, free the cell in BX and return an error
|
|
freeCellExit:
|
|
push ax
|
|
HeapFreeCell
|
|
pop ax ; AL has the error code
|
|
stc
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ StrategyVectorTable
|
|
; ===================
|
|
; The strategy vector jump table
|
|
;
|
|
dw StrategyDefault ; IoFuncPanic
|
|
dw StrategyRead ; IoFuncRead
|
|
dw StrategyDefault ; IoFuncWrite
|
|
dw StrategyClose ; IoFuncClose
|
|
dw StrategyCancel ; IoFuncCancel
|
|
dw StrategyDefault ; IoFuncAttach
|
|
dw StrategyDefault ; IoFuncDetach
|
|
dw StrategySet ; IoFuncSet
|
|
dw StrategySense ; IoFuncSense
|
|
StrategyVectorEnd:
|
|
ProcEnd noret
|
|
|
|
|
|
ProcBegin@ CancelTimer
|
|
; ===========
|
|
; Cancel the outstanding timer request
|
|
; In:
|
|
; BX = TimerEnt ptr
|
|
; Out:
|
|
; NONE
|
|
;
|
|
push bx
|
|
mov bx, [bx].TimerChan
|
|
mov al, IoFuncCancel ; cancel the queued timer
|
|
IoWithWait
|
|
pop bx
|
|
lea di, [bx].TimerStat
|
|
IoWaitForStatus ; use up completion signal
|
|
ret
|
|
ProcEnd noret
|
|
|
|
ProcBegin@ CancelRead
|
|
; ==========
|
|
; Cancel the outstanding read request on the lower driver
|
|
; In:
|
|
; BX = TimerEnt ptr
|
|
; Out:
|
|
; NONE
|
|
;
|
|
mov al, IoFuncCancel ; cancel the queued read
|
|
IoWithWait
|
|
lea di, [bx].ReadStat
|
|
IoWaitForStatus ; use up completion signal
|
|
ret
|
|
ProcEnd noret
|
|
|
|
ProcBegin@ SignalReadAX
|
|
; ============
|
|
; Complete the orignal read request with the completion status in AX
|
|
; In:
|
|
; AX = completion status to report
|
|
; BX = control block ptr as allocated by open
|
|
; Out:
|
|
; NONE
|
|
;
|
|
xor di, di ; complete the original read request
|
|
xchg di, [bx].ReadRq.RqStatusPtr
|
|
mov word ptr [di], ax
|
|
IoSignal
|
|
ret
|
|
ProcEnd noret
|
|
|
|
ProcBegin@ CancelAnyRequests
|
|
; =================
|
|
; Cancel any outstanding attached driver and timer requests.
|
|
; In:
|
|
; BX = control block ptr as allocated by open
|
|
; Out:
|
|
; NONE
|
|
;
|
|
cmp [bx].ReadRq.RqStatusPtr, 0
|
|
je noRequestsQueued
|
|
call CancelRead
|
|
call CancelTimer
|
|
push bx
|
|
mov bx, [bx].WaitHandler
|
|
mov cl, HandlerDisable
|
|
IoEnableHandler
|
|
pop bx
|
|
mov ax, CancelErr
|
|
call SignalReadAX
|
|
|
|
noRequestsQueued:
|
|
ret
|
|
ProcEnd noret
|
|
|
|
ProcBegin@ AttachedTimerStrategy,far
|
|
; =====================
|
|
; Called by the applications I/O services.
|
|
; In:
|
|
; BX = control block ptr as allocated by open
|
|
; SI = RqEnt pointer
|
|
; Out:
|
|
; Carry clear
|
|
; The request was sucessfully queued, (may have completed)
|
|
; Carry set
|
|
; The request failed to start
|
|
;
|
|
cld
|
|
mov [bx].InStrategy, 1 ; anti nesting
|
|
mov di, [si].RqStatusPtr
|
|
mov word ptr [di], PendingErr ; request now pending
|
|
mov ax, di ; pre-load AX
|
|
mov di, [si].RqFunction
|
|
cmp di, (StrategyVectorEnd-StrategyVectorTable)/2
|
|
ja StrategyDefault
|
|
mov cx, [si].RqA1Ptr ; pre-load CX and DX
|
|
mov dx, [si].RqA2Ptr
|
|
shl di, 1 ; vector table entry
|
|
jmp word ptr cs:StrategyVectorTable[di]
|
|
|
|
; This device driver does not handle this function, pass it up the driver
|
|
; hierarchy to a driver that may support this function.
|
|
StrategyDefault:
|
|
mov [bx].InStrategy, 0
|
|
IoSuper ; pass request to next dvr
|
|
ret
|
|
|
|
StrategyRead:
|
|
cmp [bx].ReadRq.RqStatusPtr, 0
|
|
jne panicPending ; already a read queued
|
|
mov [bx].ReadRq.RqStatusPtr, ax
|
|
mov [bx].ReadRq.RqA1Ptr, cx
|
|
mov [bx].ReadRq.RqA2Ptr, dx
|
|
lea di, [bx].ReadStat ; Q an attached dvr read
|
|
mov al, IoFuncRead
|
|
IoAsynchronousNoError ; Queue a read on lower dvr
|
|
lea di, [bx].TimerStat
|
|
lea cx, [bx].Timeout
|
|
push bx
|
|
push [bx].WaitHandler
|
|
mov bx, [bx].TimerChan
|
|
mov al, IoFuncRead
|
|
IoAsynchronousNoError ; Queue a read on the timer channel
|
|
; now let the wait handler be callable
|
|
pop bx
|
|
mov cl, HandlerEnable
|
|
IoEnableHandler
|
|
pop bx
|
|
and [bx].InStrategy, 0
|
|
clc
|
|
ret
|
|
|
|
; The process is making a 2nd call of a function that is already outstanding.
|
|
panicPending:
|
|
mov al, PanicIoPending
|
|
ProcPanic ; stop calling process.
|
|
|
|
StrategyCancel:
|
|
call CancelAnyRequests
|
|
and [bx].InStrategy, 0
|
|
jmp short completeRequestOk
|
|
|
|
; This driver defines the IoFuncSet function to set the read timout.
|
|
StrategySet:
|
|
mov di, cx ; RqA1Ptr is a pointer to a long
|
|
mov ax, [di]
|
|
mov word ptr [bx].Timeout, ax
|
|
mov ax, [di+2]
|
|
mov word ptr [bx+2].Timeout, ax
|
|
and [bx].InStrategy, 0
|
|
jmp short completeRequestOk
|
|
|
|
; This driver defines the IoFuncSense function to sense the read timout.
|
|
StrategySense:
|
|
mov di, cx ; RqA1Ptr is a pointer to a long
|
|
mov ax, word ptr [bx].Timeout
|
|
mov word ptr [di], ax
|
|
mov ax, word ptr [bx+2].Timeout
|
|
mov word ptr [di+2], ax
|
|
completeRequestOk:
|
|
mov di, [si].RqStatusPtr
|
|
and word ptr [di], 0 ; complete request OK.
|
|
IoSignal
|
|
and [bx].InStrategy, 0
|
|
clc
|
|
ret
|
|
|
|
StrategyClose:
|
|
call CancelAnyRequests
|
|
push bx
|
|
push [bx].WaitHandler
|
|
mov bx, [bx].TimerChan
|
|
IoClose ; close the timer
|
|
pop bx
|
|
IoRemoveHandler ; remove the waithandler
|
|
pop bx
|
|
mov al, IoFuncDetach ; detach from lower driver
|
|
IoWithWait
|
|
HeapFreeCell ; alloc cell freed !!!
|
|
mov di, [si].RqStatusPtr
|
|
and word ptr [di], 0 ; complete request OK.
|
|
IoSignal
|
|
clc
|
|
ret
|
|
|
|
ProcEnd noret
|
|
|
|
ProcBegin@ AttachedTimerHandler,far
|
|
; ====================
|
|
; Called by the operating system when the applications I/O semaphore has
|
|
; been signalled. See if its one of our requests that have been completed.
|
|
; The operating system will automatically stop any nesting if it called this
|
|
; handler, however we will get called if we are in the strategy vector and
|
|
; use synchronous I/O requests. The driver has set itself up such that this
|
|
; will only ever be called if there is an outstanding request by only
|
|
; enabling the handler when a read request is received.
|
|
;
|
|
; In:
|
|
; BX = as passed to IoAddHandler
|
|
; Out:
|
|
; Carry clear
|
|
; The signal was not for us.
|
|
; Carry set
|
|
; AL = 0 - we used signal, allow handler to be disabled.
|
|
; AL = 1 - we used signal, keep the handler enabled.
|
|
;
|
|
|
|
cmp [bx].InStrategy, 1
|
|
jne notInAttachedTimerStrategy
|
|
signalNotForThisDriver:
|
|
clc ; get out as fast as possible
|
|
ret ; as event definatly not for us.
|
|
|
|
notInAttachedTimerStrategy:
|
|
cmp [bx].ReadStat, PendingErr
|
|
je readRequestNotComplete
|
|
; The read request has completed.
|
|
call CancelTimer
|
|
mov ax, [bx].ReadStat ; read completion result
|
|
jmp short signalAXdisableHandler
|
|
|
|
readRequestNotComplete:
|
|
cmp [bx].TimerStat, PendingErr
|
|
je signalNotForThisDriver
|
|
; The timer request has completed.
|
|
call CancelRead
|
|
mov ax, InActivityErr ; read inactivity on timeout
|
|
signalAXdisableHandler:
|
|
call SignalReadAX
|
|
xor al, al ; we used signal
|
|
stc ; handler now disabled.
|
|
ret
|
|
ProcEnd noret
|
|
|
|
|
|
EndCodeSeg
|
|
;
|
|
stack segment stack para 'data'
|
|
;
|
|
stack ends
|
|
;
|
|
end AttachedTimerLDD
|