        title   A4EXIF -- Example ASIC4 Interface device driver
        subttl  Copyright (c) Psion PLC (1994)
        name    A4EXIF
;
; VER     DATE     BY      DESCRIPTION
; ----------------------------------------
; 1.00F  26/1/95   Mal     Working Version


;BUILDSB=1                       ;S3a build environment (channels=1)
S3c=0                           ;Need to specify no s3c
;BUILDSC=1                       ;S3c build environment (channels=3)
BUILDCH=1                       ;HC build environment (channels=3)
;BUILDHH=1                       ;S3 build environment  (channels=1)

        include ..\inc\epoc.inc
        include ..\inc\epoclib.inc
        include ..\inc\epocsibo.inc
        include ossibo.inc
        include ospack.inc
            
;       Example Logical Device Driver for prototype LED-ASIC4 Interface
;       circuit for Corporate/S3C/Consumer serial port.  
;       Written by Mal Dec 1994/Jan 1995.

;       A4EXIF CONSTANTS AND TYPES
;       ==========================
;       The following constants and type definitions
;       are compiler directives used by the TCEP assembler
;       when it is creating the LDD.

if Consumer
        numberofchannels        equ     1
else
  if Corporate or S3c
        numberofchannels        equ     3
  else
        numberofchannels        equ     2
  endif
endif

;Channel StatusEnt block accessed through CS:DI
A4ExifStatusEnt struc
        A4ExifCSProcessId                 dw      ?       ;Channel parent process id
        A4ExifCSChannelOpen               db      ?       ;Channel open flag
        A4ExifCSChannelRunning            db      ?       ;Channel hardware running flag
        A4ExifCSChanReadCompleted         db      ?       ;Channel request completed flag
        A4ExifCSChannelIntMask            db      ?       ;Channel interrupt mask
        A4ExifCSChannelIntNum             db      ?       ;Channel interrupt number
        A4ExifCSChannelSelect             db      ?       ;SIBO Channel select
        A4ExifCSChannelIntVec             dw      ?       ;Channel int vector number
        A4ExifCSTickHandle                dw      ?       ;TCK channel handle      
        A4ExifCSA1Value                   db      ?       ;Channel strategy A1 parameter
        A4ExifCSA2Value                   db      ?       ;Channel strategy A2 parameter
A4ExifStatusEnt ends

;Open Channel Control block accessed through DS:BX
A4ExifEnt struc                 
        A4ExifDSIo                ChanEnt <>      ;Open channel control block Ent
        A4ExifDSHandlerPtr        dw      ?       ;Cant touch when under
        A4ExifDSStatusPtr         dw      ?       ;Hold, Resume, Reset or
        A4ExifDSA1Ptr             dw      ?       ;interrupt routine
        A4ExifDSA2Ptr             dw      ?
        A4ExifDSStatusEntPtr      dw      ?
A4ExifEnt ends


        A4PERIPH_MASK           equ     0f0h            ;11110000b    
        EXTENDED_INFO_BYTE      equ     090h            ;10010000b
        U5_ENABLE_ON            equ     080h            ;Sets LBO for latch U5
        U5_ENABLE_OFF           equ     000h            ;Deselects LBO for U5
        
;Latch addresses for reading/writing
        U5OUTPUT_LATCH          equ     00000000b       ;Selects A0 for writing
        U3INPUT_BUFFER          equ     00000000b       ;Selects A0 for reading
        U4STATUS_BUFFER         equ     00000001b       ;Selects cs:[di].A4ExifCsA1Valuefor reading
        INTERRUPT_LATCH         equ     00000001b       ;Selects A1 for writing 

;Status byte masks
        S2S3_ON                 equ     00000011b
        S2S3_OFF                equ     00000000b
        S2_ONLY                 equ     00000001b
        S3_ONLY                 equ     00000010b
        INTERRUPT_STATUS_MASK   equ     00000011b

;LED bytes
        SOME_LEDS_ON            equ     01010101b
        SOME_LEDS_OFF           equ     10101010b
        TOP_LEDS_ON             equ     11110000b
        BOTTOM_LEDS_ON          equ     00001111b
        ZERO_BYTE               equ     00h

        dgroup  group  stack
        assume ds:dgroup,es:dgroup,ss:dgroup

        CodeSeg

;       A4EXIF ENTRY TABLE
;       ==================

ProcBegin@ A4ExifLDD
;          =========

dw      LDDSignature
db      'LED',0,0,0,0,0
dw      (VectorEnd-Vector)/2
Vector:
        dw      A4ExifInstall
        dw      A4ExifRemove
        dw      A4ExifHold
        dw      A4ExifResume
        dw      A4ExifReset
        dw      A4ExifUnits
        dw      A4ExifOpen
        dw      A4ExifStrategy
VectorHandler:
        dw      A4ExifHandler
if Asic9
InterruptVectors:
        dw      A4ExifTickInt
endif
VectorEnd:


;       A4EXIF INTERNAL DATA SPACE
;       ==========================
;       Device Driver global variables follow.  
;       These variables reside in the code segment
;       and as such can always be accessed with 
;       the 'cs:' prefix.

if Asic9
  if Consumer
        Channel0        A4ExifStatusEnt<0,0,0,0,mask A9MSlave,HwIrq2Revector,SelectChannel5,IntVec0,0,0>
  else 
    if Corporate or S3c
        Channel0        A4ExifStatusEnt<0,0,0,0,mask A9MExpIntA,HwIrq4Revector,SelectChannel3,IntVec0,0,0>
        Channel1        A4ExifStatusEnt<0,0,0,0,mask A9MExpIntB,HwIrq5Revector,SelectChannel4,IntVec1,0,0>
        Channel2        A4ExifStatusEnt<0,0,0,0,mask A9MSlave,HwIrq2Revector,SelectChannel5,IntVec2,0,0>
    else
        Channel0        A4ExifStatusEnt<0,0,0,0,mask A9MExpIntA,HwIrq4Revector,SelectChannel3,IntVec0,0,0>
        Channel1        A4ExifStatusEnt<0,0,0,0,mask A9MExpIntB,HwIrq5Revector,SelectChannel4,IntVec1,0,0>
    endif
  endif
else
  if Consumer
        Channel0        A4ExifStatusEnt<0,0,0,0,mask Asic2Int,HwIrq4Revector,SelectChannel7,IntVec0,0,0>
  else 
    if Corporate
        Channel0        A4ExifStatusEnt<0,0,0,0,mask ExpIntLeftA,HwIrq3Revector,ExpChannelLeftA,IntVec0,0,0>
        Channel1        A4ExifStatusEnt<0,0,0,0,mask ExpIntRightB,HwIrq2Revector,ExpChannelRightB,IntVec1,0,0>
        Channel2        A4ExifStatusEnt<0,0,0,0,mask Asic2Int,HwIrq4Revector,SelectChannel7,IntVec2,0,0>
    else
        Channel0        A4ExifStatusEnt<0,0,0,0,mask ExpIntLeftA,HwIrq3Revector,ExpChannelLeftA,IntVec0,0,0>
        Channel1        A4ExifStatusEnt<0,0,0,0,mask ExpIntRightB,HwIrq2Revector,ExpChannelRightB,IntVec1,0,0>
    endif
  endif
endif

        HoldFlag        db      0       ;Hold/Resume flag
        SwitchStatus    db      ?       ;Holds the masked status byte from U4 after interrupt

        ProcEnd noret
                                

        ProcBegin@ A4ExifInstall,far
;                  =============
;       Installs the device driver.
;       Invoked after a PLIB 'p_loadldd("A4EXIF.LDD")' call to load the LDD
;       All of the fields inside the CS control blocks are preloaded
;       with the correct values at install time. Install therefore does
;       not do any work.
;       IN:  
;               Nothing
;       OUT: 
;               Carry Clear - driver successfully installed
;
        clc                     ;The ChannelOpen fields are set to zero
        ret                     ;in the relevant ChannelStatusEnt headers
        ProcEnd noret


        ProcBegin@ A4ExifRemove,far
;                  ============
;       Removes the device driver.
;       Invoked after a PLIB 'p_devdel("LED",E_LDD)'call to unload the 
;       device driver.
;       A device driver cannot be removed if any of its channels
;       are still open.
;       IN:  
;               Nothing
;       OUT: 
;               Carry clear - successfully removed
;               Carry set - remove failed, error number in AL
;
        mov     cx, numberofchannels                    ;Check that each 
        mov     di, offset Channel0                     ;channel is closed
        xor     ax, ax
CheckAllChannelsClosedLoop:
        cmp     cs:[di].A4ExifCSChannelOpen, al         ;Closed channels will
        jne     WeHaveAChannelOpenSoFail                ;have the value 0 in 
        add     di, (size A4ExifStatusEnt)              ;their ChannelOpen 
        loop    CheckAllChannelsClosedLoop              ;flags
        jmp     FinishedOkay
WeHaveAChannelOpenSoFail:
        mov     al, InUseErr                            ;Fail if not all 
        stc                                             ;closed
        ret
FinishedOkay:
        clc
        ret
        ProcEnd noret                           

        
        ProcBegin@ A4ExifHold,far
;                  ==========
;       Called by the operating system whenever the device driver is 
;       being moved or the machine is powering down.
;       This will also be called by our pack door polling function when
;       it sees that the doors have been opened and our peripheral has
;       lost power.
;       IN:  
;               Reason for the hold in AH
;       OUT: 
;               Nothing
;
        mov     cx, 1                                   ;Hold can be called 
        xchg    cl, HoldFlag                            ;when the driver is 
        cmp     cl, 0                                   ;under a hold so 
        jne     AlreadyHeld                             ;re-entrancy blocking
A4HoldFromTick:                                         ;is required
        mov     cx, numberofchannels 
        mov     di, offset Channel0                     ;Loop because Hold 
HoldAllTheChannelsLoop:                                 ;must stop all the 
        cmp     cs:[di].A4ExifCSChannelOpen, 0          ;channels
        je      DontHoldBecauseNotOpen                  ;Is the channel open?
        push    cx                                      ;If it is then stop 
        call    StopTheChannelRunning                   ;all interrupts
        pop     cx
DontHoldBecauseNotOpen:
        add     di, (size A4ExifStatusEnt)
        loop    HoldAllTheChannelsLoop
AlreadyHeld:
        ret
        ProcEnd noret


        ProcBegin@ A4ExifResume,far
;                  ============
;       Called by the operating system when it has finished moving the
;       device driver in memory or when the machine is switching back on.
;       Also called by our door polling routine when it sees that the doors
;       have been closed and we can resume communication with our peripheral.
;       IN:  
;               Nothing
;       OUT: 
;               Nothing
;
        xor     cx, cx                          ;Block in case of re-entrancy
        xchg    cl, HoldFlag
        cmp     cl, 0
        je      AlreadyResumed
if Asic9
        GenDataSegment                          ;Check to see if the pack 
        HwGetSsdData                            ;doors are still closed
        mov     bx, ax
        cmp     es:[bx].SsdDoorStatus, DoorOpen
        je      DoorsAreOpen
endif
A4ResumeFromTick:                               
        mov     cx, numberofchannels            ;Loop because resume must 
        mov     di, offset Channel0             ;restart each open channel
ResumeAllTheChannelsLoop:
        cmp     cs:[di].A4ExifCSChannelOpen, 0  ;Is the channel open?
        je      DontResumeBecauseNotOpen
        push    cx
        call    StartTheChannelRunning
        pop     cx
DontResumeBecauseNotOpen:
        add     di, (size A4ExifStatusEnt)
        loop    ResumeAllTheChannelsLoop
AlreadyResumed:
        ret
DoorsAreOpen:
        mov     HoldFlag, 2
        ret
        ProcEnd noret        


        ProcBegin@ A4ExifReset,far
;                  ===========
;       Called when an application which opened a channel terminates without
;       closing the device driver.
;       A device driver, for each open channel, has to request that the 
;       operating system calls this function when the application 
;       terminates abnormally (ie without closing an open channel).
;       Reset can supply one piece of identifying data which will be
;       passed in CX. This would usually be the channel's number or CS 
;       control block pointer.
;       Reset just needs to clear interrupts, hardware reservations, and free
;       the channel. Any allocated space will be cleaned up for you by the OS.
;       IN:  
;               The device driver's handle in BX
;               The address of the status struc identifying the channel in CX
;       OUT: 
;               Nothing
;
        mov     di, cx
        cmp     cs:[di].A4ExifCSChannelOpen, 0
        je      NotOpenToReset
        call    StopTheChannelRunning                   ;Stop interrupts
        mov     al, cs:[di].A4ExifCSChannelIntMask      ;Free the reserved 
        HwFreeChannel                                   ;hardware
        mov     cs:[di].A4ExifCSChannelOpen, 0          ;The channel is now 
NotOpenToReset:                                         ;free
        ret
        ProcEnd noret


        ProcBegin@ A4ExifUnits,far
;                  ===========
;       Called to find how many open channels the driver will support
;       In:  
;               Nothing
;       Out: 
;               The total number of channels supported in AX
;
        mov     ax, numberofchannels
        ret
        ProcEnd noret


        ProcBegin@ A4ExifOpen,far
;                  ==========
;       Opens a device driver channel.
;       Invoked after the PLIB call 'p_open(&appHandle,"LED:*",-1)'to open a 
;       channel to the device driver.
;       In:  
;               OS device handle of the device driver in DX
;               Pointer to the OpenEnt struc in SI
;               Pointer to the IntEnt struc in BP
;               DS,ES,SS point to the applications data space
;       Out: 
;               Carry clear - BX holds the address of the open channel
;               Carry set - AL holds the error number
;
        cld                                             ;Interrupts off to 
        pushf                                           ;prevent multiple apps
        cli                                             ;calling open
        mov     si, [si].OpenNamePtr                    ;simultaneously
        mov     al, [si+1]
        CharToFoldedChar                                ;Read the unit no.
        cmp     al, 'A'                                 ;part of device name
        jb      OpenNameErr                             ;to find which channel
        sub     al, 'A'                                 ;to open. eg "LED:A"
        cmp     al, numberofchannels
        jae     OpenNameErr
        xor     ah, ah
        push    dx                                      ;Map the channel no.
        mov     dx, (size A4ExifStatusEnt)              ;to a channel control
        mul     dx                                      ;block in our CS
        pop     dx                                      ;space
        mov     di, ax
        add     di, offset Channel0                     ;Offset in DI
        cmp     cs:[di].A4ExifCSChannelOpen, 0           
        je      GetATickChannel                         ;Check to see if
        popf                                            ;channel is already
        mov     ax, AlreadyOpenErr                      ;open
        jmp     ChannelAlreadyOpen
GetATickChannel:
        mov     cs:[di].A4ExifCSChannelOpen, 1          ;Obtain a TCK channel so 
        mov     cs:[di].A4ExifCSChannelRunning,0        ;we can poll the door state
        popf
ife Asic9
        jmp     GetHardwareChannel                      ;If HC, we don't need 
else                                                    ;to set up the TCK 
        push    ax                                      ;routine
        mov     ax, ((':' shl 8)+'K')
        push    ax
        mov     ax, (('C' shl 8)+'T')                   ;Try to open "TCK:"
        push    ax                                      ;channel with "TCK:" 
        mov     bx, sp                                  ;string on stack
        IoOpen
        jnc     GotATickChannel                         ;TCK will call our 
        add     sp, 6                                   ;door-polling function 
        mov     ax, LockedErr                           ;32 times a second
        jmp     OpenFailed
GotATickChannel:
        add     sp, 6
        mov     cs:[di].A4ExifCSTickHandle, ax          ;Start our 
        push    dx                                      ;door-polling function 
        mov     bx, ax                                  ;running by starting 
        mov     ax, IoFuncStart                         ;the "TCK:" channel
        mov     cx, 1
        push    cx                                      ;Frequency 1 tick
        push    cx                                      ;Data irrelevant
        push    dx                                      ;Handle to driver
        mov     cx, (InterruptVectors-Vector)/2         ;TCK function to call
        push    cx
        mov     cx, sp
        IoWithWait
        add     sp, 8
        pop     dx
        jmp     GetHardwareChannel
endif
OpenNameErr:
        popf
        mov     ax, NameErr
        jmp     ChannelAlreadyOpen
GetHardwareChannel:                                     ;Check the hardware 
        mov     al, cs:[di].A4ExifCSChannelIntMask      ;is available then 
        HwGetChannel                                    ;reserve it
        jnc     CheckHardwareNowThatSIBOChannelIsOpen   ;Returns with carry 
        mov     ax, InUseErr                            ;clear if OK
        jmp     FreeTckAndExit
FreeTckAndChannelHardware:
        mov     al, cs:[di].A4ExifCSChannelIntMask
        HwFreeChannel
        mov     ax, DeviceErr
FreeTckAndExit:
if Asic9
        push    ax
        mov     bx, cs:[di].A4ExifCSTickHandle
        IoClose
        pop     ax
endif
OpenFailed:
        mov     cs:[di].A4ExifCSChannelOpen, 0
ChannelAlreadyOpen:
        stc
OpenExit:
        ret
CheckHardwareNowThatSIBOChannelIsOpen:
        ProcId                                          ;Set up the calling
        mov     cs:[di].A4ExifCSProcessId, ax           ;process ID
        mov     cs:[di].A4ExifCSChanReadCompleted, 0    
        call    CheckHardwarePresent                    ;Returns with carry 
        jc      FreeTckAndChannelHardware               ;clear if successful
        mov     cx, (size A4ExifEnt)                    ;Allocate a control 
        HeapAllocateCell                                ;block in our app's 
        jc      FreeTckAndChannelHardware               ;data space
        mov     bx, ax
        push    bx                              ;IoAddHandler trashes BX
        mov     al, (VectorHandler-Vector)/2    ;Set up our wait handler
        IoAddHandler                            ;Leaves the address of 
        pop     bx                              ;handler in AX
        jnc     GotHandler
        push    ax                              ;If no handler, open fails
        HeapFreeCell
        pop     ax
        jmp     FreeTckAndChannelHardware
GotHandler:
        mov     [bx].A4ExifDSStatusEntPtr, di          
        mov     [bx].A4ExifDSHandlerPtr, ax         
        mov     [bx].A4ExifDSStatusPtr, 0            
        mov     [bx].A4ExifDSIo.ChanNext, bx        
        mov     [bx].A4ExifDSIo.ChanSignature, IoChanSignature
        mov     [bx].A4ExifDSIo.ChanLibHandle, dx                 
        mov     cx, di                          ;IoRequestReset takes
        xchg    bx, dx                          ;the device handle in 
        IoRequestReset                          ;BX and the channel 
        xchg    bx, dx                          ;handle in CX
        xor     ax, ax
        call    StartTheChannelRunning
ReturnCLC:
        clc
        ret
        ProcEnd


if Asic9
        ProcBegin@ A4ExifTickInt,far
;                  =============
;       This function is called by the tick handler on every tick of
;       the system clock. This happens 32 times a second.
;       The operating system will call a device driver to hold when memory is
;       being moved and when the machine is being powered down. It will not
;       call the device driver when the pack doors are opened.
;       Opening the pack doors will cause power to the peripheral to be cut,
;       and therefore the driver needs to be held in the way it would be if
;       the machine powered down.  Only for Asic9 based machines.
;       This function checks the state of the doors on every tick and calls
;       Hold and resume when it sees the status of the doors change.
;       IN:  
;               The state of the door in SI
;       OUT: 
;               Nothing
;
        cmp     si, DoorOpen                    ;Is the door open
        je      TheDoorIsOpen                   ;or closed?
        cmp     HoldFlag, 2                     ;Closed Door, HoldFlag=2
        je      NeedToDoTheResume               ;means we do a resume
        ret                                     ;Closed Door, HoldFlag!=2        
NeedToDoTheHold:
        mov     HoldFlag, 2
        jmp     A4HoldFromTick
NeedToDoTheResume:
        mov     HoldFlag, 0
        jmp     A4ResumeFromTick
TheDoorIsOpen:
        xor     ax, ax
        cmp     HoldFlag, al
        je      NeedToDoTheHold
        ret
        ProcEnd noret
endif


        ProcBegin@ A4ExifHandler,far
;                  =============
;       When an application is waiting within an iowait and a signal is
;       generated then before passing that signal to the application the
;       OS first runs any wait handlers belonging to the device driver
;       channels that the application has open.
;       The interrupt routine can generate a signal but cannot fill in any
;       status words or pass values back to the application because the
;       applications DS space is not available. The handler can consume a
;       signal generated by an interrupt and then fill any status words
;       before re-signalling the application. A handler always has access
;       to the application's DS space.  The wait handler can consume the 
;       signal which is then no longer passed back to the application.
;       IN:  
;               Pointer to our control block in applications DS space in BX
;               DS,ES,SS point at application's data space
;       OUT: 
;               Carry clear - do not consume the signal, signal not for us
;               Carry set - consume the signal, re-enable handler if AL
;                           non-zero else don't re-enable handler if AL=0.
;
        cld
        pushf
        cli
        mov     di, [bx].A4ExifDSStatusEntPtr           ;Is the signal for us?
        cmp     cs:[di].A4ExifCSChanReadCompleted, 1    
        jne     ExitHandlerSignalNotForUs               ;If it is, copy 
        mov     cs:[di].A4ExifCSChanReadCompleted, 0    ;the values read in 
        mov     al, cs:[di].A4ExifCSA1Value             ;the interrupt 
        mov     ah, cs:[di].A4ExifCSA2Value             ;routine back to 
        mov     di, [bx].A4ExifDSA1Ptr                  ;the application
        mov     [di], al                                ;Asynchronous read  
        mov     di, [bx].A4ExifDSA2Ptr                  ;has been completed
        mov     [di], ah
        xor     di, di
        xchg    [bx].A4ExifDSStatusPtr, di              ;Clear the status 
        mov     word ptr [di], 0                        ;word
        popf                                            
        IoSignal                                        
        xor     ax, ax
        stc                                             ;STC and AL!=0 =>
        ret                                             ;consume signal and
ExitHandlerSignalNotForUs:                              ;don't re-enable 
        popf                                            ;handler
        clc
        ret
        ProcEnd


StrategyVectorTable     label   word
        dw      offset A4ExifDefault    ;StrategyPanic
        dw      offset A4ExifRead       ;StrategyRead           ie P_FREAD
        dw      offset A4ExifWrite      ;StrategyWrite          ie P_FWRITE
        dw      offset A4ExifClose      ;StrategyClose          ie P_FCLOSE
        dw      offset A4ExifCancel     ;StrategyCancel         ie P_FCANCEL
        dw      offset A4ExifDefault    ;StrategyAttach
        dw      offset A4ExifDefault    ;StrategyDetach
        dw      offset A4ExifSet        ;StrategySet            ie P_FSET
        dw      offset A4ExifSense      ;StrategySense          ie P_FSENSE
        

        ProcBegin@ A4ExifStrategy,far
;                  ==============
;       Called by the operating system when an I/O request is made on
;       the device driver.
;       Calls to this funcion from an owning application will usually take
;       the form p_ioc(pcb,func,&Stat,&A1,&A2);
;       The strategy function is called with a function number specifying
;       the action which the driver is to take, a status word to fill when
;       the action is complete, and two arguments A1 and A2.
;       All strategy functions must complete with a signal to the application.
;       Functions can be asynchronous and need not complete immediately.
;       IN:  
;               Pointer to our control block in the applications DS in BX
;               Device driver handle in DX
;               Pointer to the RqEnt struct in SI
;               Pointer to a IntEnt struct in BP
;               DS,ES,SS point at the applications data space
;       OUT: 
;               Returned value in AX
;               Must call IoSignal somewhere to signal completion 
;                       of the I/O request.
;
        mov     ax, [si].RqFunction             ;Get the function number
        mov     dx, [si].RqA1Ptr                ;DX holds the first argument
        mov     di, [si].RqStatusPtr            ;for convenience
        mov     word ptr [di], PendingErr       ;Status word holds 
        shl     ax, 1                           ;E_FILE_PENDING
        mov     di, ax
        push    StrategyVectorTable[di]         ;Jump to required function 
        mov     di, [bx].A4ExifDSStatusEntPtr   ;with our CS control block
        retn                                    ;pointer in DI
A4ExifDefault:
        IoRoot
        ret
ExitWithCompletionStatusZero:
        xor     ax, ax                          ;Common exit points
ExitWithOtherCompletionStatus:
        mov     di, [si].RqStatusPtr
        mov     word ptr [di], ax
        IoSignal
ExitStillPending:
        xor     ax, ax
        clc
        ret
        ProcEnd noret               


;       STRATEGY VECTOR TABLE FUNCTIONS
;       ===============================
;IN:
;       DX holds the pointer to the first argument in 'p_iow(...)' call
;       BX holds the address of the open channel control block
;       CS:DI holds the address of status struc identifying the open channel
;

        ProcBegin@ A4ExifRead,far
;                  ==========
;       Strategy vector table function that handles asynchronous byte reads
;       from the LEDs and switches.
;       The corresponding PLIB call is p_ioc(pcb,P_FREAD,&Stat,&Arg1,&Arg2);
;       The request is completed when the interrupt routine signals the
;       handler which in turn signals the application passing back the values
;       read at the time of the interrupt through the A1Ptr and A2Ptr.
;       IN:  
;               Pointer to control block in applications data space in BX
;               Pointer to control block in our CS space in DI
;               A1 (pointer to Arg1) in DX
;       OUT: 
;               Jumps to common exit point
;               Panics if multiple requests
;
        cmp     [bx].A4ExifDSStatusPtr, 0       ;Panic if we already 
        jne     PanicPending                    ;have an I/O read request 
        pushf                                   ;pending on the channel
        cli                                     ;Disable interrupts
        mov     cs:[di].A4ExifCSChanReadCompleted, 2
        mov     ax, [si].RqA1Ptr                 
        mov     [bx].A4ExifDSA1Ptr, ax          ;Store the locations to 
        mov     ax, [si].RqA2Ptr                ;put the data when we get it
        mov     [bx].A4ExifDSA2Ptr, ax
        mov     di, [si].RqStatusPtr
        mov     [bx].A4ExifDSStatusPtr, di      ;DI holds the address 
        mov     word ptr [di], PendingErr       ;of status word and we
        popf                                    ;signal that we are waiting
        mov     bx, [bx].A4ExifDSHandlerPtr     ;for completion of read
        mov     cl, 1
        IoEnableHandler                         ;Enable Wait Handler
        jmp     short ExitStillPending          ;No IoSignal because 
PanicPending:                                   ;we are still waiting
        mov     al, PanicIoPending
        ProcPanic
        ProcEnd noret 


        ProcBegin@ A4ExifWrite,far
;                  ===========
;       This function sets the state of the LEDs.
;       It completes immediately after setting the state as it has nothing
;       to wait for.
;       Write is the same as set.
;       IN:  
;               Pointer to control block in applications data space in BX
;               Pointer to control block in our CS space in DI
;               A1 in DX
;       OUT: 
;               Jumps to Set
;
        jmp     WriteAndSetAreTheSame
        ProcEnd noret 


        ProcBegin@ A4ExifClose,far
;                  ===========
;       Handles the request to close a channel.
;       called by the PLIB call p_iow(pcb,P_FCLOSE) or p_close(pcb)
;       IN:  
;               Pointer to control block in applications data space in BX
;               Pointer to control block in our CS space in DI
;       OUT: 
;               Jumps to common exit point
;
        call    StopTheChannelRunning                   ;Stop interrupts
if Asic9
        push    bx                                      ;Close down the 
        mov     bx, cs:[di].A4ExifCSTickHandle          ;"TCK:" channel
        IoClose
        pop     bx
endif
        push    bx                                  
        mov     ax, [bx].A4ExifDSIo.ChanLibHandle       ;Remove the wait
        push    ax                                      ;handler
        mov     bx, [bx].A4ExifDSHandlerPtr     
        IoRemoveHandler                                 
        cmp     cs:[di].A4ExifCSChanReadCompleted, 0    ;If the interrupt
        je      NoSignalToConsumeFromInterrupt          ;has signalled the 
        IoWaitForSignal                                 ;handler we need to 
NoSignalToConsumeFromInterrupt:                         ;consume its signal 
        pop     bx                                      ;now the handler
        mov     cx, di                                  ;has been removed
        IoRequestResetCancel
        pop     bx                                      ;No longer need reset
        HeapFreeCell                                    ;Free our control 
        mov     al, cs:[di].A4ExifCSChannelIntMask      ;block in app's DS
        HwFreeChannel
        mov     cs:[di].A4ExifCSChannelOpen, 0
        jmp     ExitWithCompletionStatusZero            ;To signal completion 
        ProcEnd noret                                   ;of close request


        ProcBegin@ A4ExifCancel,far
;                  ============
;       Cancel any pending asynchronous read.
;       Called by the PLIB function p_iow(pcb,P_FCANCEL);
;       In:  
;               Pointer to control block in applications data space in BX
;               Pointer to control block in our CS space in DI
;       Out: 
;               Jumps to common exit point
;
        pushf
        cli
        cmp     [bx].A4ExifDSStatusPtr, 0               ;Check that there is
        je      NothingToCancel                         ;a request pending 
        push    bx
        mov     bx, [bx].A4ExifDSHandlerPtr
        sub     cl, cl
        IoEnableHandler                                 ;Disable Wait Handler
        pop     bx
        cmp     cs:[di].A4ExifCSChanReadCompleted, 1
        jne     NoSignalFromInterrupt                   ;To consume any stray
        IoWaitForSignal                                 ;signal from the 
NoSignalFromInterrupt:                                  ;interrupt routine
        mov     cs:[di].A4ExifCSChanReadCompleted, 0    
        xor     di, di                                  ;To signal completion
        xchg    di, [bx].A4ExifDSStatusPtr              ;of the outstanding
        mov     word ptr [di], CancelErr                ;async read request
        IoSignal                                        ;Signal to p_waitstat
NothingToCancel:
        popf
        jmp     ExitWithCompletionStatusZero            ;To signal completion 
        ProcEnd noret                                   ;of the cancel request


        ProcBegin@ A4ExifSet,far
;                  =========
;       Write a value to the LED latch.
;       Can be called by the PLIB call p_iow(pcb,P_FSET,&Arg1); where A1
;       is an unsigned byte.
;       Write is the same as set.
;       IN:  
;               Pointer to control block in applications data space in BX
;               Pointer to control block in our CS space in DI
;               A1 (pointer to Arg1) in DX
;       OUT: 
;               Jumps to common exit point
;
WriteAndSetAreTheSame:
        pushf
        cli
        mov     al, cs:[di].A4ExifCSChannelSelect       ;Select our SIBO 
        HwSelectChannel                                 ;serial channel
        push    ax                                      ;Store old channel
        mov     bx, dx                        
        mov     dl, U5OUTPUT_LATCH                      ;DX (now BX) points
        mov     al, [bx]                                ;to the value to 
        call    OutputByte                              ;output to our 
        pop     ax                                      ;peripheral
        HwSelectChannel                                 ;Return old channel
        popf
        jmp     ExitWithCompletionStatusZero            ;To signal completion
        ProcEnd noret                                   ;of set request


        ProcBegin@ A4ExifSense,far
;                  ===========
;       Reads the state of the LEDS and Switches.
;       Can be called from PLIB using p_iow(pcb,P_FSENSE,&Arg1,&Arg2)
;       Where Arg1 and Arg2 are unsigned bytes.
;       In:  
;               Pointer to control block in applications data space in BX
;               Pointer to control block in our CS space in DI
;               A1 (pointer to Arg1) in DX
;       Out: 
;               Jumps to common exit point
;
        pushf
        cli                                             ;Select our SIBO
        mov     al, cs:[di].A4ExifCSChannelSelect       ;serial channel
        HwSelectChannel                                 ;Store old channel
        push    ax
        mov     bx, dx                                  ;DX (now BX) points 
        mov     dl, U4STATUS_BUFFER                     ;to the variable in 
        call    InputByte                               ;which to place the 
        mov     [bx], al                                ;value read from U4.
        mov     bx, [si].RqA2Ptr                        ;BX now points to 
        mov     dl, U3INPUT_BUFFER                      ;the variable in 
        call    InputByte                               ;which to place the 
        mov     [bx], al                                ;value read from U3.
        pop     ax
        HwSelectChannel                                 ;Return old channel
        popf
        jmp     ExitWithCompletionStatusZero            ;To signal completion 
        ProcEnd noret                                   ;of a sense request


;       LOCAL DEVICE DRIVER FUNCTIONS
;       =============================

        ProcBegin@ InputByte
;                  =========
;       Interrupts must be off prior to the call to this function.
;       IN: 
;               DL has address to which Asic4 is to write
;               AL holds the value to output
;
        mov     al, (SerialWriteSingle or A4Address)
        SBUSY
        SCONTOUT
        mov     al, dl
        SBUSY
        SDATAOUT
        mov     al, (SerialReadSingle or A4Data)
        SBUSY
        SCONTOUT
        SBUSY
        SDATAIN
        ret
        ProcEnd noret


        ProcBegin@ OutputByte
;                  ==========
;       Interrupts must be off prior to the call to this function.
;       IN: 
;               DL has address to which Asic4 is to write
;               AL has the value to output
;
        push    ax
        mov     al, (SerialWriteSingle or A4Address)
        SBUSY
        SCONTOUT
        mov     al, dl
        SBUSY
        SDATAOUT
        mov     al, (SerialWriteSingle or A4Data)
        SBUSY
        SCONTOUT
        SBUSY
        XNOP
        pop     ax
        SDATAOUT
        ret
        ProcEnd noret


        ProcBegin@ IntVec0,far
;                  ===========
;       Interrupt on serial channel0
;       Calls Comint with channel control block pointer in DI
;
        mov     di, offset Channel0
        jmp     ComInt
        ProcEnd noret


if Corporate or S3c
        ProcBegin@ IntVec1,far
;                  ===========
;       Interrupt on serial channel1
;       Calls Comint with channel control block pointer in DI
;
        mov     di, offset Channel1
        jmp     ComInt
        ProcEnd noret


        ProcBegin@ IntVec2,far
;                  ===========
;       Interrupt on serial channel2
;       Calls Comint with channel control block pointer in DI
;
        mov     di, offset Channel2
;       FALL THROUGH
        ProcEnd noret
endif


        ProcBegin@ ComInt,far
;                  ==========
;       The common interrupt service routine code.
;       When an interrupt occurs, the first task is to read the status
;       buffer of latch U4.  The subsequent action is dependent on the
;       postion of switches S1 and S2.  For the purposes of this example,
;       the four posibilities for the switch values correspond somewhat
;       arbitrarily to four different byte values that are written to U5.
;       in the 8086, interrupts cannot occur while in an interrupt routine.
;       If an asynchronous read is pending then the handler is signalled.
;       IN:  
;               Channel's CS based control block pointer in DI
;
        cmp     cs:[di].A4ExifCSChanReadCompleted, 2    ;Only if 2 do we have
        jne     NotAsynchronousRead                     ;asynch read completed
        mov     cs:[di].A4ExifCSChanReadCompleted, 1
        mov     bx, cs:[di].A4ExifCSProcessId           ;Signals completion
        IoSignalByPidNoReSched                          ;of read to OS so as
NotAsynchronousRead:                                    ;to invoke handler
        mov     al, cs:[di].A4ExifCSChannelSelect 
        HwSelectChannel
        push    ax
        mov     dl, U3INPUT_BUFFER
        call    InputByte
        mov     cs:[di].A4ExifCSA2Value, al             ;LED byte
        mov     dl, U4STATUS_BUFFER
        call    InputByte                        
        mov     cs:[di].A4ExifCSA1Value, al             ;Status byte
        and     al, INTERRUPT_STATUS_MASK
        xchg    al, SwitchStatus                        
        cmp     SwitchStatus, S2S3_ON                   ;S2 and S3 on => turn
        je      AllLEDsOn                               ;on alternate LEDs
        cmp     SwitchStatus, S2S3_OFF                  ;S2 and S3 off => not
        je      AllLEDsOff                              ;the alternate LEDs
        cmp     SwitchStatus, S2_ONLY                   ;S2 on, S3 off => turn
        je      TopLEDsOn                               ;on top four LEDs
        cmp     SwitchStatus, S3_ONLY                   ;S3 on, S2 off => turn
        je      BottomLEDsOn                            ;on bottom four LEDs
ErrorInSwitchStatusByte:
        jmp     ClearInterruptAndReschedule             
AllLEDsOn:
        mov     al, SOME_LEDS_ON
        jmp     OutputLEDByte
AllLEDsOff:
        mov     al, SOME_LEDS_OFF
        jmp     OutputLEDByte
TopLEDsOn:
        mov     al, TOP_LEDS_ON
        jmp     OutputLEDByte
BottomLEDsOn:
        mov     al, BOTTOM_LEDS_ON
OutputLEDByte:
        mov     dl, U5OUTPUT_LATCH      
        call    OutputByte                 
ClearInterruptAndReschedule:
        mov     dl, INTERRUPT_LATCH          
        call    OutputByte        
if	Asic9                                   ;A write to this location
        out     A9BNonSpecificEoiW, al          ;informs the interrupt
else                                            ;controller that the 
        out     A1NonSpecificEoi, al            ;installed interrupt 
endif                                           ;service routine has 
        pop     ax                              ;finished
        HwSelectChannel
        clc                                     ;Reschedule if necessary
        ret
        ProcEnd noret


        ProcBegin@ StartInterrupts
;                  ===============
;       Start interrupts assuming interrupts are  
;       off prior to call.
;       The EPOC GenSetRevector service loads in 
;       a user-specified interrupt service routine 
;       located at the address given in cx:bx
;       (segment cx, offset bx) for the interrupt 
;       vector number given in AL.  Note that the 
;       variable A4ExifCSChannelIntVec holds the 
;       name of the appropriate required interrupt 
;       vector routine for the channel.
;       IN:  
;               Our CS control block pointer in DI
;       OUT: 
;               Nothing
;
        mov     al, cs:[di].A4ExifCSChannelIntNum       ;Get the OS to call
        mov     cx, cs                                  ;the function in 
        push    bx                                      ;CS:BX every time 
        mov     bx, cs:[di].A4ExifCSChannelIntVec       ;that the interrupt
        GenSetRevector                                  ;whose number is in
        pop     bx                                      ;AL occurs
ife Asic9
        in      al, A1InterruptMask
        or      al, cs:[di].A4ExifCSChannelIntMask      ;Set the mask  
        out     A1InterruptMask, al                     ;location so as                                                        ;interrupt
else                                                    ;to enable that
        in      al, A9BInterruptMaskRW                  ;interrupt
        or      al, cs:[di].A4ExifCSChannelIntMask
        out     A9BInterruptMaskRW, al

endif
        ret
        ProcEnd noret


        ProcBegin@ StopInterrupts
;                  ==============
;       Stop interrupts assuming that interrupts are off
;       The EPOC GenResetRevector OS service 
;       replaces the previously loaded user-specified 
;       interrupt service routine with the original 
;       routine and interrupt mask for the vector 
;       given in AL.
;       IN:  
;               Our CS control block pointer in DI
;       OUT: 
;               Nothing
;
ife Asic9
        in      al, A1InterruptMask                     ;Disable the 
        mov     ah, cs:[di].A4ExifCSChannelIntMask      ;interrupt
        not     ah
        and     al, ah
        out     A1InterruptMask, al
else
        in      al, A9BInterruptMaskRW
        mov     ah, cs:[di].A4ExifCSChannelIntMask
        not     ah
        and     al, ah
        out     A9BInterruptMaskRW, al
endif
        mov     al, cs:[di].A4ExifCSChannelIntNum       ;Return the interrupt
        GenResetRevector                                ;vector to the OS
        ret                                             ;default
        ProcEnd noret


        ProcBegin@ StartTheChannelRunning
;                  ======================
;       Starts the hardware and interrupts going
;       If the hardware is aready running then there is nothing to do
;       Must check that the hardware has not vanished before restarting it
;       IN:  
;               Our CS control block pointer in DI
;       OUT: 
;               Nothing
;
        pushf
        cli
        cmp     byte ptr cs:[di].A4ExifCSChannelRunning, 0
        jne     ChannelAlreadyRunning
        call    CheckHardwarePresent
        jc      HardwareNotPresent
        mov     al, cs:[di].A4ExifCSChannelSelect
        HwSelectChannel
        push    ax
        mov     al, (SerialWriteSingle or A4Control)    ;Switch on the 
        SBUSY                                           ;output latch U5
        SCONTOUT                                        ;by asserting 
        mov     al, U5_ENABLE_ON                        ;the ASIC4 LBO line
        SBUSY                                           ;(LBO is inverted)
        SDATAOUT
        xor     ax,ax
        mov     dl, INTERRUPT_LATCH                     ;Clear any pending
        call    OutputByte                              ;interrupt on the
        mov     dl, U5OUTPUT_LATCH                      ;peripheral
        call    OutputByte                              ;Preset the LEDs
        pop     ax                                      ;to all off
        HwSelectChannel
        call    StartInterrupts
        mov     cs:[di].A4ExifCSChannelRunning, 1
ChannelAlreadyRunning:
        popf
        clc
        ret
HardwareNotPresent:                     
        popf
        stc
        ret
        ProcEnd noret


        ProcBegin@ StopTheChannelRunning
;                  =====================
;       Stops the hardware and interrupts
;       Checks to see if the hardware is really running to start with
;       IN:  
;               Our CS control block pointer in DI
;       OUT: 
;               Nothing
;
        pushf
        cli
        cmp     byte ptr cs:[di].A4ExifCSChannelRunning, 0
        je      ChannelNotRunning
        mov     al, cs:[di].A4ExifCSChannelSelect
        HwSelectChannel
        push    ax
        mov     al, (SerialWriteSingle or A4Control)
        SBUSY
        SCONTOUT
        mov     al, U5_ENABLE_OFF 
        SBUSY
        SDATAOUT
        xor     ax, ax
        mov     dl, U5OUTPUT_LATCH
        call    OutputByte
        pop     ax
        HwSelectChannel
        call    StopInterrupts
        mov     cs:[di].A4ExifCSChannelRunning, 0
ChannelNotRunning:
        popf
        ret
        ProcEnd noret


        ProcBegin@ CheckHardwarePresent
;	           ====================
;       Used to determine whether the correct hardware is present 
;       on the successfully procured serial channel.
;       IN:  
;               CS control block pointer in DI
;       OUT: 
;               Carry clear - correct hardware is there
;               Carry set - wrong or no hardware
;
	pushf
        cli                                             ;Select the correct
        mov     al, cs:[di].A4ExifCSChannelSelect       ;SIBO channel that 
	HwSelectChannel					;the peripheral is
        push    ax                                      ;attached to
	HwNullFrame		
	mov	al,(SerialSelect or Asic4Id)
	SBUSY						;First look for an
	SCONTOUT					;ASIC4 at the other
	XNOP						;end of the link
	SBUSY                                           
	SDATAIN                                         ;If the returned
	test	al,al                                   ;value is non-zero
	je	ConnectionFailedA4NotPresent            ;we have an ASIC4
	mov	al,(SerialReadSingle or A4InfoR)	
	SBUSY					        ;Now see if we have 
	SCONTOUT	    				;the right peripheral
	XNOP                                            ;XNOP allows the busy
	SBUSY                                           ;signal to come 
	SDATAIN                                         ;through for the wait
	and     al, A4PERIPH_MASK                     
        cmp     al, EXTENDED_INFO_BYTE                  ;Mask out the bottom
        jne     ConnectionFailed                        ;four bits as the 
	pop     ax                                      ;upper four contain
        HwSelectChannel                                 ;the peripheral ID
        popf
	clc                                             ;If it is our hardware
	ret                                             ;exit with carry clear
ConnectionFailedA4NotPresent:                        
	mov	al,(SerialSelect or Asic5NormalId)	;Its not an ASIC4
	SBUSY						;peripheral
	SCONTOUT                                        ;By selecting a non
	XNOP                                            ;ASIC4 as an ASIC4,
	SDATAIN                                         ;we effectively
	test	al,al                                   ;disable whatever is
        jne     ConnectionFailed                        ;out there so we do
	mov	al,(SerialSelect or Asic5PackId)	;a select for all 
	SBUSY		                                ;possibilities so 
	SCONTOUT                                        ;that we don't end
	XNOP                                            ;up disabling 
	SDATAIN                                         ;anything that we
	test	al,al                                   ;can't control.
        jne     ConnectionFailed
	mov	al,(SerialSelect or Asic8Id)            ;Modem chip id
	SBUSY	
	SCONTOUT
	XNOP
	SDATAIN
ConnectionFailed:
        pop     ax
        HwSelectChannel
	popf
	stc
	ret
	ProcEnd noret

        EndCodeSeg
        stack segment stack para 'data'
        stack ends
        end A4ExifLDD




        
