Go to the first, previous, next, last section, table of contents.


ber_serial.a


*** ScR ***

        incdir  include:

        AUTO    wo devs:ber_serial.device\

; Klude for bug in Asm-One (does not handle MEXIT properly!)

EXEC_INITIALIZERS_I     SET     1

INITBYTE        MACRO   ; &offset,&value
                IFLE    (\1)-255        ;If offset <=255
                DC.B    $a0,\1          ;use byte offset
                DC.B    \2,0
                ELSE
                DC.B    $e0,0
                DC.W    \1
                DC.B    \2,0
                ENDC
                ENDM

INITWORD        MACRO   ; &offset,&value
                IFLE    (\1)-255        ;If offset <=255
                DC.B    $90,\1          ;use byte offset
                DC.W    \2
                ELSE
                DC.B    $d0,0
                DC.W    \1
                DC.W    \2
                ENDC
                ENDM

INITLONG        MACRO   ; &offset,&value
                IFLE    (\1)-255        ;If offset <=255
                DC.B    $80,\1          ;use byte offset
                DC.L    \2
                ELSE
                DC.B    $c0,0
                DC.W    \1
                DC.L    \2
                ENDC
                ENDM

; Let's redefine the PRINTF macro too...

                IFND    PRINTF
PRINTF          MACRO   ; level,<string>,...
                IFGE    DEBUG_DETAIL-\1
.PUSHCOUNT      SET     0

                IFNC    '\9',"
                move.l  \9,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC

                IFNC    '\8',"
                move.l  \8,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC

                IFNC    '\7',"
                move.l  \7,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC

                IFNC    '\6',"
                move.l  \6,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC

                IFNC    '\5',"
                move.l  \5,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC

                IFNC    '\4',"
                move.l  \4,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC
        
                IFNC    '\3',"
                move.l  \3,-(sp)
.PUSHCOUNT      SET     .PUSHCOUNT+4
                ENDC

                movem.l a0/a1,-(sp)
                lea.l   .PSS(pc),A0
                lea.l   4*2(SP),A1
                BSR     kprint_macro
                movem.l (sp)+,a0/a1
                bra.s   .PSE

.PSS            dc.b    \2
                IFEQ    (\1&1)  ;If even, add CR/LF par...
                   dc.b 13,10
                ENDC
                dc.b    0
                even
.PSE
                lea.l   .PUSHCOUNT(sp),sp
                ENDC    ;IFGE   DEBUG_DETAIL-\1
                ENDM    ;PRINTF MACRO
                ENDC    ;IFND   PRINTF

; And now the rest of the includes...

        include devices/serial.i
        include dos/dos.i
        include exec/macros.i
        include exec/types.i
        include exec/devices.i
;       include exec/initializers.i             ; See above
        include exec/lists.i
        include exec/memory.i
        include exec/resident.i
        include exec/io.i
        include exec/ables.i
        include exec/errors.i
        include exec/tasks.i
        include hardware/custom.i
        include hardware/cia.i
        include hardware/intbits.i
        include libraries/expansion.i
        include libraries/configvars.i
        include libraries/configregs.i

        include lvo/exec_lib.i
        include lvo/dos_lib.i
        include lvo/expansion_lib.i

;       include macros.i
;       include Projekt:ber_serial/ber.i

        include myinc/macros.i
        include work:coding/asm-source/ber_serial/ber.i

;DEBUG_DETAIL   SET     2

; Number of serial ports supported

UNITS           EQU     32

; Default settings

READBUFFERSIZE  EQU     512
EXTFLAGS        EQU     0
BAUDRATE        EQU     9600
BREAKTIME       EQU     250000
TERMARRAY0      EQU     $00000000
TERMARRAY1      EQU     $00000000
WRITELEN        EQU     8
READLEN         EQU     8
STOPBITS        EQU     1
SERFLAGS        EQU     0
STATUS          EQU     0

_custom         EQU     $dff000

; Our device base

        STRUCTURE BERSERIAL,LIB_SIZE
        UBYTE   berd_Flags
        UBYTE   berd_Pad1
                                ; Now longword aligned
        APTR    berd_SysLib
        APTR    berd_ExpLib
        BPTR    berd_SegList
        STRUCT  berd_Units,UNITS*4
        LABEL   BERSERIAL_SIZEOF

; ..and the Unit structure

        STRUCTURE BERSERIALUBIT,UNIT_SIZE
        UBYTE   berdu_UnitNum
        UBYTE   berdu_Flags     ; See below
                                ; Now longword aligned
        UBYTE   berdu_ErrorCode ; CMD_READ error code 
        UBYTE   berdu_lcrreg    ; Copy of the LCR register
        UBYTE   berdu_mcrreg    ; Copy of the MCR register
        UBYTE   berdu_Pad1
        
        APTR    berdu_Device
        STRUCT  berdu_int,IS_SIZE
        UWORD   berdu_Pad2
                                ; Now longword aligned

        STRUCT  berdu_CmdList,MLH_SIZE
        STRUCT  berdu_ReadList,MLH_SIZE
        STRUCT  berdu_WriteList,MLH_SIZE

        APTR    berdu_ConfigDev
        APTR    berdu_Base      ; Base address of this device's expansion board
        APTR    berdu_ReadBuffer
        APTR    berdu_ReadPointer
        APTR    berdu_WritePointer

        ULONG   berdu_CtrlChars
        ULONG   berdu_ReadBufferSize
        ULONG   berdu_ExtFlags
        ULONG   berdu_BaudRate
        ULONG   berdu_BreakTime
        STRUCT  berdu_TermArray,TERMARRAY_SIZE
        UBYTE   berdu_ReadLen
        UBYTE   berdu_WriteLen
        UBYTE   berdu_StopBits
        UBYTE   berdu_SerFlags
        UWORD   berdu_Status

        LABEL   BERSERIALUNIT_SIZEOF

; berdu_Flags definitions

        ; Bit 1-3 used to fake interrupts
        BITDEF  B,STOPPED,4             ; Set inside CMD_STOP/CMD_START
        BITDEF  B,SENDING,5             ; Set when sending data

*******************************************************************************
**** Entry ********************************************************************
*******************************************************************************

Entry:
        moveq   #-1,d0
        rts

initDDescrip:
                                ; STRUCTURE RT,0
        DC.W    RTC_MATCHWORD   ; UWORD RT_MATCHWORD    (Magic cookie)
        DC.L    initDDescrip    ; APTR  RT_MATCHTAG     (Back pointer)
        DC.L    EndCode         ; APTR  RT_ENDSKIP      (To end of this hunk)
        DC.B    RTF_AUTOINIT    ; UBYTE RT_FLAGS        (magic-see "Init:")
        DC.B    VERSION         ; UBYTE RT_VERSION
        DC.B    NT_DEVICE       ; UBYTE RT_TYPE         (must be correct)
        DC.B    0               ; BYTE  RT_PRI
        DC.L    berName         ; APTR  RT_NAME         (exec name)
        DC.L    idString        ; APTR  RT_IDSTRING     (text string)
        DC.L    Init            ; APTR  RT_INIT
                                ; LABEL RT_SIZE

VERSION         EQU     33
REVISION        EQU     1

berName:
        dc.b    "ber_serial.device",0

        dc.b    "$VER: "
idString:
        dc.b    "ber_serial.device 33.2 (5.6.97)",13,10,0

ExLibName
        dc.b    "expansion.library",0
        even

Init:
        DC.L    BERSERIAL_SIZEOF        ; data space size
        DC.L    funcTable               ; pointer to function initializers
        DC.L    dataTable               ; pointer to data initializers
        DC.L    initRoutine             ; routine to run

funcTable:
        dc.l    Open
        dc.l    Close
        dc.l    Expunge
        dc.l    Null
        dc.l    BeginIO
        dc.l    AbortIO
        dc.l    -1

dataTable:
        INITBYTE        LN_TYPE,NT_DEVICE               ;Must be LN_TYPE!
        INITLONG        LN_NAME,berName
        INITBYTE        LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
        INITWORD        LIB_VERSION,VERSION
        INITWORD        LIB_REVISION,REVISION
        INITLONG        LIB_IDSTRING,idString
        DC.W    0       ;terminate list

        even

*******************************************************************************
**** initRoutine **************************************************************
*******************************************************************************

; This call is single-threaded by exec; please read the description for
; "Open" below.
;
;----------------------------------------------------------------------
initRoutine:
        pushm   std
        move.l  d0,a5

        move.l  a6,berd_SysLib(a5)      ; faster access than move.l 4.w,a6
        move.l  a0,berd_SegList(a5)

        ; Open expansion.library
        lea     ExLibName(pc),A1
        moveq   #0,D0
        call    OpenLibrary
        move.l  d0,berd_ExpLib(a5)
        beq     .error

        lea     dosname(pc),a1
        moveq   #0,d0
        call    OpenLibrary
        move.l  d0,dosbase

        move.l  a5,d0

.exit
        PRINTF  2,"Device initialized: 0x%08lx",d0
        popm    std
        rts
.error:
        moveq   #0,d0
        bra     .exit

dosname:        dc.b    "dos.library",0
        even
dosbase         dc.l    0

;----------------------------------------------------------------------
;
; Here begins the system interface commands. When the user calls
; OpenDevice/CloseDevice/Remdevice, this eventually gets translated
; into a call to the following routines (Open/Close/Expunge).
; Exec has already put our device pointer in a6 for us.
;
; IMPORTANT:
;       These calls are guaranteed to be single-threaded; only one task
;       will execute your Open/Close/Expunge at a time.
;
;       For Kickstart V33/34, the single-threading method involves "Forbid".
;       There is a good chance this will change. Anything inside your
;       Open/Close/Expunge that causes a direct or indirect Wait() will break
;       the Forbid(). If the Forbid() is broken, some other task might
;       manage to enter your Open/Close/Expunge code at the same time.
;       Take care!
;
; Since exec has turned off task switching while in these routines
; (via Forbid/Permit), we should not take too long in them.
;
;----------------------------------------------------------------------

*******************************************************************************
**** Open *********************************************************************
*******************************************************************************

        ; Open sets the IO_ERROR field on an error.     If it was successfull,
        ; we should also set up the IO_UNIT and LN_TYPE fields.
        ; exec takes care of setting up IO_DEVICE.
;in:
* d0    unitnum
* d1    flags
* a1    iob
* a6    device
;out:
; d0    device error code

Open:

        addq.w  #1,LIB_OPENCNT(a6)      ;Fake an opener for duration of call <|>

        pushm   std
        move.l  a6,a5
        move.l  berd_SysLib(a5),a6

        move.l  a1,a2           ; save the iob

        ;------ see if the unit number is in range
        cmp.l   #UNITS,d0
        bhs.w   .range_error    ; unit number out of range (BHS)

        ;------ see if the unit is already initialized
        move.l  d0,d2           ; save unit number
        lsl.l   #2,d0
        lea     berd_Units(a5,d0.l),a4
        move.l  (a4),d0
        beq     .unitFree
        btst.b  #SERB_SHARED,IO_SERFLAGS(a2)
        beq     .busy_error     ; We need exclusive access
        move.l  (a4),a3
        btst    #SERB_SHARED,berdu_SerFlags(a3)
        beq     .busy_error     ; Already opened with exclusive access
        bra     .unitOK

.unitFree
        ;------ try and conjure up a unit
        bsr     InitUnit        ; d2:unitnum a5:devpoint

        ;------ see if it initialized OK
        tst.l   (a4)
        beq     .open_error

        moveq.l #0,d0                           ; Don't need to re-zero it
        lea     IOReqInit(pc),A1
        call    InitStruct                      ; a2: iob

        move.l  a2,a1
        move.l  (a4),a3
        bsr     SetTheParams                    ; Initialize the hardware

.unitOK
        move.l  (a4),a3                         ; unit pointer in a3
        move.l  a3,IO_UNIT(a2)

        move.l  a2,a1
        bsr     UpdateIOReq                     ; Fill in current parameters

        ;------ mark us as having another opener
        addq.w  #1,LIB_OPENCNT(a5)
        addq.w  #1,UNIT_OPENCNT(a3)     ;Internal bookkeeping

        ;------ prevent delayed expunges
        bclr    #LIBB_DELEXP,LIB_FLAGS(a5)

        moveq   #0,d0
        move.b  d0,IO_ERROR(a2)
        move.b  #NT_REPLYMSG,LN_TYPE(a2) ;IMPORTANT: Mark IORequest as "complete"

.end

        subq.w  #1,LIB_OPENCNT(a5)      ;** End of expunge protection <|>
;       PRINTF  2,"Device opened: 0x%08lx, Error=%ld",a2,d0
        popm    std
        rts

.range_error
.open_error
        moveq   #IOERR_OPENFAIL,d0
        bra     .error
.busy_error
        moveq   #SerErr_DevBusy,d0
        bra     .error

.error
        move.b  d0,IO_ERROR(a2)
        move.l  d0,IO_DEVICE(a2)        ;IMPORTANT: trash IO_DEVICE on open failure
        bra     .end

*******************************************************************************
**** Close ********************************************************************
*******************************************************************************

; There are two different things that might be returned from the Close
; routine. If the device wishes to be unloaded, then Close must return
; the segment list (as given to Init). Otherwise close MUST return NULL.

;in:
* a1    iob
* a6    device
;out:
; d0    seglist or NULL

Close:
        pushm   std
        move.l  a6,a5

        move.l  a1,a2

        move.l  IO_UNIT(a2),a3

        ;Use this IORequest again and die!
        moveq.l #-1,d0
        move.l  d0,IO_UNIT(a2)          ;We're closed...
        move.l  d0,IO_DEVICE(a2)        ;customers not welcome at this IORequest!!

        ;------ see if the unit is still in use
        subq.w  #1,UNIT_OPENCNT(a3)

        bne     .close_device
        bsr     ExpungeUnit

.close_device
        moveq   #0,d0
        ;------ mark us as having one fewer openers
        subq.w  #1,LIB_OPENCNT(a5)

        ;------ see if there is anyone left with us open
        bne     .end

        ;------ see if we have a delayed expunge pending
        btst    #LIBB_DELEXP,LIB_FLAGS(a5)
        beq     .end

        ;------ do the expunge
        move.l  a5,a6
        bsr     Expunge

.end
        PRINTF  2,"Device closed: SegList=0x%08lx",d0
        popm    std
        rts                             ;MUST return either zero or the SegList!!!

*******************************************************************************
**** Expunge ******************************************************************
*******************************************************************************

; Expunge is called by the memory allocator when the system is low on
; memory.
;
; There are two different things that might be returned from the Expunge
; routine. If the device is no longer open then Expunge may return the
; segment list (as given to Init). Otherwise Expunge may set the
; delayed expunge flag and return NULL.
;
; One other important note: because Expunge is called from the memory
; allocator, it may NEVER Wait() or otherwise take long time to complete.

;in:
* a6    device
;out
* d0    seglist/NULL

Expunge:
        pushm   std
        move.l  a6,a5
        move.l  berd_SysLib(a5),a6

        ;------ see if anyone has us open
        tst.w   LIB_OPENCNT(a5)
        beq     .1

        ;------ it is still open. Set the delayed expunge flag
        bset    #LIBB_DELEXP,LIB_FLAGS(a5)
        moveq   #0,d0
        bra     .end

.1
        ;------ go ahead and get rid of us.     Store our seglist in d2
        move.l  berd_SegList(a5),d2

        ;------ unlink from device list
        move.l  a5,a1
        call    Remove          ;Remove first (before FreeMem)

        move.l  berd_ExpLib(a5),d0
        beq     .noexp
        move.l  d0,a1
        call    CloseLibrary
.noexp

        ;------ free our memory (must calculate from LIB_POSSIZE & LIB_NEGSIZE)
        move.l  a5,a1           ;Devicebase
        moveq   #0,d0
        move.w  LIB_NEGSIZE(a5),d0
        suba.l  d0,a1           ;Calculate base of functions
        add.w   LIB_POSSIZE(a5),d0      ;Calculate size of functions + data area
        call    FreeMem

        ;------ set up our return value
        move.l  d2,d0

.end
        PRINTF  2,"Device expunged: SegList=0x%08lx",d0
        popm    std
        rts

*******************************************************************************
**** Null *********************************************************************
*******************************************************************************

Null:
        moveq   #0,d0
        rts             ;The "Null" function MUST return NULL.

*******************************************************************************
**** InitUnit *****************************************************************
*******************************************************************************

;in:
* d2    unit #
* a5    device

InitUnit:
        pushm   std

        move.l  berd_SysLib(a5),a6

        ;------ allocate unit memory
        move.l  #BERSERIALUNIT_SIZEOF,d0
        move.l  #MEMF_PUBLIC!MEMF_CLEAR,d1
        call    AllocMem
        tst.l   d0
        move.l  d0,a3
        beq     .error

        moveq.l #0,d0                   ; Don't need to re-zero it
        move.l  a3,a2                   ; InitStruct is initializing the UNIT
        lea.l   .unitinit(pc),A1
        call    InitStruct

        move.l  d2,d0
        bsr     iu_findcard
        tst.l   d0
        beq     .error

        move.b  d2,berdu_UnitNum(a3)    ;initialize unit number
        move.l  a5,berdu_Device(a3)     ;initialize device pointer

        ;------ initialize the unit's lists
        lea     MP_MSGLIST(a3),a0
        NEWLIST a0
        lea     berdu_CmdList(a3),a0
        NEWLIST a0
        lea     berdu_ReadList(a3),a0
        NEWLIST a0
        lea     berdu_WriteList(a3),a0
        NEWLIST a0

        move.l  a3,berdu_int+IS_DATA(a3); Pass unit addr to interrupt server

        lea     berdu_int(a3),a1
        moveq   #INTB_PORTS,d0
        call    AddIntServer

        ;------ mark us as ready to go
        move.l  d2,d0                   ; unit number
        lsl.l   #2,d0
        move.l  a3,berd_Units(a5,d0.l)  ; set unit table

.end
        popm    std
        rts
.error
        bsr     FreeUnit
        bra     .end

.unitinit
        INITBYTE        MP_FLAGS,PA_IGNORE ; Unit starts with a message port
        INITBYTE        LN_TYPE,NT_MSGPORT
        INITLONG        LN_NAME,berName
        INITBYTE        berdu_int+LN_PRI,0
        INITLONG        berdu_int+IS_CODE,InterruptCode
        INITLONG        berdu_int+LN_NAME,berName
        DC.W    0

**** iu_findcard **************************************************************

; Find the serial card. This routine will scan for the unit:th usable
; 16550 register map. Each card can have several 16550s on board, but they
; must be placed right after each other in the address space.

;in:
* d0    Unit
;out:
* d0    NULL on failure

iu_findcard:
        pushm   std

        move.l  d0,d2

        move.l  berd_ExpLib(a5),a6
        clr.l   berdu_ConfigDev(a3)

.cardloop
        move.l  #BER_MANUID,d0
        move.l  #BER_PRODID,d1
        move.l  berdu_ConfigDev(a3),a0
        call    FindConfigDev
        move.l  d0,berdu_ConfigDev(a3)
        beq     .error

        move.l  d0,a0
        move.l  cd_BoardAddr(a0),a1
        beq     .error

;       move.l  a1,a2
;.uartloop
;       subq.l  #1,d2
;       bmi     .gotuart
;
;       clr.b   ber_spr+ber_SIZEOF(a1)
;       move.b  #$AB,ber_spr(a2)                ; Store in first register map
;       cmp.b   #$AB,ber_spr+ber_SIZEOF(a1)     ; Read in current
;       beq     .cardloop
;       add.w   #ber_SIZEOF,a1
;       bra     .uartloop
;.gotuart

; Small selftest

;       move.b  #BERMCRF_DIAG,ber_mcr(a1)
;       tst.b   ber_msr(a0)
;       tst.b   ber_msr(a0)
;
;       move.b  #BERMCRF_DIAG!BERMCRF_DTR,ber_mcr(a1)
;       cmp.b   #BERMSRF_DSR!BERMSRF_DSRCHG,ber_msr(a1)
;       bne     .selftesterror
;
;       move.b  #BERMCRF_DIAG!BERMCRF_RTS,ber_mcr(a1)
;       cmp.b   #BERMSRF_CTS!BERMSRF_CTSCHG!BERMSRF_DSRCHG,ber_msr(a1)
;       bne     .selftesterror
;
;       move.b  #BERMCRF_DIAG!BERMCRF_OP1,ber_mcr(a1)
;       cmp.b   #BERMSRF_RI!BERMSRF_CTSCHG,ber_msr(a1)  ; WHY??
;       bne     .selftesterror
;
;       move.b  #BERMCRF_DIAG!BERMCRF_OP2,ber_mcr(a1)
;       cmp.b   #BERMSRF_CD!BERMSRF_CDCHG!BERMSRF_RICHG,ber_msr(a1)
;       bne     .selftesterror
;
;       move.b  #BERMCRF_DIAG,ber_mcr(a1)
;       cmp.b   #BERMSRF_CDCHG,ber_msr(a1)
;       bne     .selftesterror
;
;       move.b  #0,ber_mcr(a1)

        move.l  a1,d0

.exit
        move.l  d0,berdu_Base(a3)
        popm    std
        rts

.error
        moveq   #0,d0
        bra     .exit

.selftesterror
        move.b  #0,ber_mcr(a1)
        bra     .error  

*******************************************************************************
**** FreeUnit *****************************************************************
*******************************************************************************

;in:
* a3    unit
* a5    device

FreeUnit:
        pushm   std

        move.l  a3,d0
        beq     .exit

        bsr     FreeHW

        move.l  berd_SysLib(a5),a6

        move.l  berdu_ReadBuffer(a3),d0
        beq     .noreadbuff
        move.l  d0,a1
        move.l  berdu_ReadBufferSize(a3),d0
        call    FreeMem
.noreadbuff

        move.l  a3,a1
        move.l  #BERSERIALUNIT_SIZEOF,d0
        call    FreeMem
.exit
        popm    std
        rts

*******************************************************************************
**** ExpungeUnit **************************************************************
*******************************************************************************

;in:
* a3    unit
* a5    device

ExpungeUnit:
        pushm   std
        move.l  berd_SysLib(a5),a6

        lea     berdu_int(a3),a1
        moveq   #INTB_PORTS,d0
        call    RemIntServer            ;Now remove the interrupt server

        ;------ save the unit number
        moveq   #0,d2
        move.b  berdu_UnitNum(a3),d2

        ;------ free the unit structure.
        bsr     FreeUnit

        ;------ clear out the unit vector in the device
        lsl.l   #2,d2
        clr.l   berd_Units(a5,d2.l)

        popm    std
        rts

*****************************************************************************

; cmdtable is used to look up the address of a routine that will
; implement the device command.

cmdtable:
        dc.l    Invalid         ; 0     CMD_INVALID
        dc.l    Reset           ; 1     CMD_RESET
        dc.l    Read            ; 2     CMD_READ
        dc.l    Write           ; 3     CMD_WRITE
        dc.l    Update          ; 4     CMD_UPDATE
        dc.l    Clear           ; 5     CMD_CLEAR
        dc.l    Stop            ; 6     CMD_STOP
        dc.l    Start           ; 7     CMD_START
        dc.l    Flush           ; 8     CMD_FLUSH
        dc.l    Query           ; 9     SDCMD_QUERY
        dc.l    Break           ; 10    SDCMD_BREAK
        dc.l    SetParams       ; 11    SDCMD_SETPARAMS
cmdtable_end:

;                                            S
;                                            E
;                                            T          I
;                                            P      U   N
;                                            ABQFS CPW RV
;                                            RRULTSLDRREA
;                                            AEEUATEAIESL
;                                            MARSROATTAEI
;                                            SKYHTPREEDTD
;                        --------========--------========
IMMEDIATES      EQU     %00000000000000000000101100110011
;                        --------========--------========
;                        FEDCBA9876543210FEDCBA9876543210

*******************************************************************************
**** BeginIO ******************************************************************
*******************************************************************************

; BeginIO starts all incoming IO. The IO is always processed immediately,
; but only the immediate commands ReplyMsg() it at once.
;
; IMPORTANT:
;       The exec WaitIO() function uses the IORequest node type (LN_TYPE)
;       as a flag. If set to NT_MESSAGE, it assumes the request is
;       still pending and will wait. If set to NT_REPLYMSG, it assumes the
;       request is finished. It's the responsibility of the device driver
;       to set the node type to NT_MESSAGE before returning to the user.

;in:
* a1    iob
* a6    device

BeginIO:
        PRINTF  2,"BeginIO 0x%08lx",a1
        pushm   std
        move.l  a6,a5

        move.b  #NT_MESSAGE,LN_TYPE(a1) ;So WaitIO() is guaranteed to work
        move.l  IO_UNIT(a1),a3
        move.w  IO_COMMAND(a1),d0

        cmp.w   #SER_DEVFINISH,d0       ;legal command?
        bhi     .nocmd                  ;no, reject it.

        ; Immediate command?
        move.w  #IMMEDIATES,d1
        btst    d0,d1                   ;Had there been less than 8 commands
                                        ;to check, we could have used the
                                        ;mighty "btst d0,#IMMEDIATES" instr!
        bne     .perform

        bclr    #IOB_QUICK,IO_FLAGS(a1) ;We did NOT complete this quickly

.perform
        bsr     PerformIO

.end
        popm    std
        rts

.nocmd
        move.b  #IOERR_NOCMD,IO_ERROR(a1)
        bra     .end

*******************************************************************************
**** PerformIO ****************************************************************
*******************************************************************************

; PerformIO actually dispatches an io request.  It is always called
; from BeginIO (thus on the callers's schedule).
;
; It expects a3 to already have the unit pointer in it.
; a5 has the device pointer (as always).
; a1 has the io request.
; Bounds checking has already been done on the I/O Request.

;in:
* a1    iob
* a3    unit
* a5    device

PerformIO:
        moveq   #0,d0
        move.b  d0,IO_ERROR(A1)         ; No error so far
        move.w  IO_COMMAND(a1),d0
        lsl.w   #2,d0                   ; Multiply by 4 to get table offset
        lea.l   cmdtable(pc),a0
        move.l  0(a0,d0.w),a0
        jmp     (a0)                    ;a1:iob  a3:unit  a5:devprt

*******************************************************************************
**** TermIO *******************************************************************
*******************************************************************************

; TermIO sends the IO request back to the user.

;in:
* a1    iob
* a3    unit
* a5    device

TermIO:
        pushm   std
        move.l  berd_SysLib(a5),a6

        PRINTF  2,"Terminating 0x%08lx",a1

        ; Clear some status flags in case of read error...
        cmp.w   #CMD_READ,IO_COMMAND(a1)
        bne     .readend

;; Should we enclose it with Disable()/Enable()?

        move.b  berdu_ErrorCode(a3),d0
        beq     .noreaderror
        clr.b   berdu_ErrorCode(a3)
        move.b  d0,IO_ERROR(a1)
.noreaderror
        tst.b   IO_ERROR(a1)
        beq     .readend
        bclr.b  #IOSTB_WROTEBREAK,berdu_Status+1(a3)
        bclr.b  #IOSTB_READBREAK,berdu_Status+1(a3)
.readend

        btst    #IOB_QUICK,IO_FLAGS(a1)
        bne     .end
        call    ReplyMsg                ;a1:message
.end

        popm    std
        rts

*******************************************************************************
**** AbortIO ******************************************************************
*******************************************************************************

;AbortIO() is a REQUEST to "hurry up" processing of an IORequest.
;If the IORequest was already complete, nothing happens (if an IORequest
;is quick or LN_TYPE=NT_REPLYMSG, the IORequest is complete).
;The message must be replied with ReplyMsg(), as normal.

;in:
* a1    iob
* a6    device
;out:
* d0    device error code

AbortIO:
        pushm   std
        move.l  a6,a5
        move.l  a1,a2
        move.l  berd_SysLib(a5),a6
        call    Disable

        btst    #IOB_QUICK,IO_FLAGS(a2)
        bne     .error                  ; Cannot abort quick IO requests
        cmp.b   #NT_REPLYMSG,LN_TYPE(a2)
        beq     .error                  ; Cannot abort completed IO requests

        move.w  IO_COMMAND(a2),d0
        move.w  #IMMEDIATES,d1          ; Remove if non-quick command
        btst    d0,d1
        beq     .remove
        bra     .error
.remove
        move.b  #IOERR_ABORTED,IO_ERROR(a1)
        move.l  a2,a1
        call    Remove
        move.l  a2,a1
        bsr     TermIO                  ; Terminate it
        moveq   #IOERR_ABORTED,d0
.exit
        PRINTF  2,"Aborted 0x%08lx: Error %ld",a2,d0
        call    Enable
        popm    std
        rts
.error
        moveq   #IOERR_NOCMD,d0         ; Return "AbortIO() request failed"
        bra     .exit

*****************************************************************************
;
; Here begins the functions that implement the device commands
; all functions are called with:
;       a1 -- a pointer to the io request block
;       a3 -- a pointer to the unit
;       a5 -- a pointer to the device
;
;----------------------------------------------------------------------

*******************************************************************************
**** Invalid/unimplemented commands  ******************************************
*******************************************************************************

Invalid:
Update:
        PRINTF  2,"CMD_INVALID/CMD_UPDATE"
        move.b  #IOERR_NOCMD,IO_ERROR(a1)
        bra     TermIO

*******************************************************************************
**** Reset ********************************************************************
*******************************************************************************

Reset:
        pushm   std
        PRINTF  2,"CMD_RESET"
        move.l  berd_SysLib(a5),a6
        move.l  a1,a2

        bsr     RemoveAll

        moveq.l #0,d0                   ; Don't need to re-zero it
                                        ; InitStruct is initializing the UNIT
        lea     IOReqInit(pc),A1
        call    InitStruct
        move.b  #SERFLAGS,IO_SERFLAGS(a2)

        move.l  a2,a1
        bsr     SetTheParams

        move.l  a2,a1
        bsr     UpdateIOReq

        move.l  a2,a1
        bsr     TermIO

        popm    std
        rts

IOReqInit:
        ; Default settings
        INITLONG        IO_CTLCHAR,SER_DEFAULT_CTLCHAR
        INITLONG        IO_RBUFLEN,READBUFFERSIZE
        INITLONG        IO_EXTFLAGS,EXTFLAGS
        INITLONG        IO_BAUD,BAUDRATE
        INITLONG        IO_BRKTIME,BREAKTIME
        INITLONG        IO_TERMARRAY+TERMARRAY_0,TERMARRAY0
        INITLONG        IO_TERMARRAY+TERMARRAY_1,TERMARRAY1
        INITBYTE        IO_READLEN,READLEN
        INITBYTE        IO_WRITELEN,WRITELEN
        INITBYTE        IO_STOPBITS,STOPBITS
        DC.W    0       ;terminate list

**** UpdateIOReq **************************************************************

UpdateIOReq:
        PRINTF  2,"UpdateIOReq"

        move.l  berdu_CtrlChars(a3),IO_CTLCHAR(a1)
        move.l  berdu_ReadBufferSize(a3),IO_RBUFLEN(a1)
        move.l  berdu_ExtFlags(a3),IO_EXTFLAGS(a1)
        move.l  berdu_BaudRate(a3),IO_BAUD(a1)
        move.l  berdu_BreakTime(a3),IO_BRKTIME(a1)
        move.l  berdu_TermArray+TERMARRAY_0(a3),IO_TERMARRAY+TERMARRAY_0(a1)
        move.l  berdu_TermArray+TERMARRAY_1(a3),IO_TERMARRAY+TERMARRAY_1(a1)
        move.b  berdu_ReadLen(a3),IO_READLEN(a1)
        move.b  berdu_WriteLen(a3),IO_WRITELEN(a1)
        move.b  berdu_StopBits(a3),IO_STOPBITS(a1)
        move.b  berdu_SerFlags(a3),IO_SERFLAGS(a1)
        bsr     GetStatus
        move.w  d0,IO_STATUS(a1)
        rts

**** RemoveAll ****************************************************************

; Aborts all IO requests. Used by CMD_RESET and CMD_FLUSH.

RemoveAll:
        PRINTF  2,"RemoveAll"
        pushm   std

        move.l  a5,a6

.readloop
        move.l  berdu_ReadList+MLH_HEAD(a3),a1
        tst.l   LN_SUCC(a1)
        beq     .readremoved
        jsr     DEV_ABORTIO(a6)
        bra     .readloop
.readremoved

.writeloop
        move.l  berdu_WriteList+MLH_HEAD(a3),a1
        tst.l   LN_SUCC(a1)
        beq     .writeremoved
        jsr     DEV_ABORTIO(a6)
        bra     .writeloop
.writeremoved

.cmdloop
        move.l  berdu_CmdList+MLH_HEAD(a3),a1
        tst.l   LN_SUCC(a1)
        beq     .cmdremoved
        jsr     DEV_ABORTIO(a6)
        bra     .cmdloop
.cmdremoved

        popm    std
        rts

*******************************************************************************
**** Read *********************************************************************
*******************************************************************************

Read:
        pushm   std
        move.l  berd_SysLib(a5),a6
        move.l  a1,a2
        clr.l   IO_ACTUAL(a2)

;; Fill from readbuffer first
        move.l  berdu_ReadPointer(a3),a0
.loop
        cmp.l   berdu_WritePointer(a3),a0
        beq     .bufferempty

        PRINTF  2,"CMD_READ (%ld): läs: 0x%08lx skriv: 0x%08lx",IO_LENGTH(a2),berdu_ReadPointer(a3),berdu_WritePointer(a3)

        move.w  #$ff0,$dff180

        move.l  IO_DATA(a2),a1
        move.l  IO_ACTUAL(a2),d1

;; Should check for XON/XOFF here
        move.b  (a0)+,(a1,d1.l)

        move.l  berdu_ReadBuffer(a3),d0
        add.l   berdu_ReadBufferSize(a3),d0
        cmp.l   d0,a0
        bne     .a0ok
        move.l  berdu_ReadBuffer(a3),a0
.a0ok
        move.l  a0,berdu_ReadPointer(a3)

        addq.l  #1,d1
        move.l  d1,IO_ACTUAL(a2)
        cmp.l   IO_LENGTH(a2),d1
        blo     .loop

        move.l  a2,a1
        bsr     TermIO
        bra     .exit

.bufferempty
        call    Disable
        lea     berdu_ReadList(a3),a0
        move.l  a2,a1                   ; a1:iob/message
        call    AddTail
        call    Enable
.exit
        popm    std
        rts

*******************************************************************************
**** Write ********************************************************************
*******************************************************************************

Write:
        PRINTF  2,"CMD_WRITE"
        pushm   std
        move.l  berd_SysLib(a5),a6
        call    Disable
        clr.l   IO_ACTUAL(a1)
        lea     berdu_WriteList(a3),a0
        call    AddTail                 ; a1:iob/message
        call    Enable

        btst    #BB_SENDING,berdu_Flags(a3)
        bne     .exit

        ; We are not sending any data, so we have to trigger the interrupt
        ; code manually

        move.b  berdu_Flags(a3),d0
        and.b   #~BERISRF_MASK,d0
        or.b    #BERISRF_TXRDY,d0
        move.b  d0,berdu_Flags(a3)
        move.w  #INTF_SETCLR!INTF_PORTS,_custom+intreq
.exit
        popm    std
        rts

*******************************************************************************
**** Clear ********************************************************************
*******************************************************************************

Clear:
        PRINTF  2,"CMD_CLEAR"
        ; Atomic, no need to disable the interrupt
        move.l  berdu_WritePointer(a3),berdu_ReadPointer(a3)
        clr.l   IO_ACTUAL(a1)
        bra     TermIO

*******************************************************************************
**** Stop *********************************************************************
*******************************************************************************

Stop:
        PRINTF  2,"CMD_STOP"
        pushm   std
        move.l  berd_SysLib(a5),a6
        call    Disable
        clr.l   IO_ACTUAL(a1)
        lea     berdu_CmdList(a3),a0
        call    AddTail                 ; a1:iob/message
        call    Enable

        btst    #BB_SENDING,berdu_Flags(a3)
        bne     .exit

        ; We are not sending any data, so we have to trigger the interrupt
        ; code manually

        move.b  berdu_Flags(a3),d0
        and.b   #~BERISRF_MASK,d0
        or.b    #BERISRF_TXRDY,d0
        move.b  d0,berdu_Flags(a3)
        move.w  #INTF_SETCLR!INTF_PORTS,_custom+intreq
.exit
        popm    std
        rts

*******************************************************************************
**** Start ********************************************************************
*******************************************************************************

Start:
        PRINTF  2,"CMD_START"
        pushm   std
        move.l  berd_SysLib(a5),a6
        call    Disable
        clr.l   IO_ACTUAL(a1)
        lea     berdu_CmdList(a3),a0
        call    AddTail                 ; a1:iob/message
        call    Enable 

        btst    #BB_SENDING,berdu_Flags(a3)
        bne     .exit

        ; We are not sending any data, so we have to trigger the interrupt
        ; code manually

        move.b  berdu_Flags(a3),d0
        and.b   #~BERISRF_MASK,d0
        or.b    #BERISRF_TXRDY,d0
        move.b  d0,berdu_Flags(a3)
        move.w  #INTF_SETCLR!INTF_PORTS,_custom+intreq
.exit
        popm    std
        rts

*******************************************************************************
**** Flush ********************************************************************
*******************************************************************************

Flush:
        PRINTF  2,"CMD_FLUSH"
        pushm   std
        move.l  a1,a2
        move.l  berd_SysLib(a5),a6
        call    Disable

        moveq   #0,d6
        moveq   #0,d7

        move.l  berdu_ReadList+MLH_HEAD(a3),a1  ; First node in list
        tst.l   LN_SUCC(a1)
        beq     .noreadreq
        move.l  a1,d6
        call    Remove                          ; Remove it temporary...
.noreadreq
        move.l  berdu_WriteList+MLH_HEAD(a3),a1 ; First node in list
        tst.l   LN_SUCC(a1)
        beq     .nowritereq
        move.l  a1,d7
        call    Remove                          ; Remove it temporary...
.nowritereq

        bsr     RemoveAll                       ; Cancel all IO requests

        lea     berdu_ReadList(a3),a0           ; ..and add it back
        move.l  d6,a1
        beq     .noreadreq2
        call    AddHead
.noreadreq2

        lea     berdu_WriteList(a3),a0          ; ..and add it back
        move.l  a7,a1
        beq     .nowritereq2
        call    AddHead
.nowritereq2

        call    Enable
        clr.l   IO_ACTUAL(a1)
        move.l  a2,a1
        bsr     TermIO
        popm    std
        rts

*******************************************************************************
**** Query ********************************************************************
*******************************************************************************

Query:
        PRINTF  2,"SDCMD_QUERY"
        pushm   std
        move.l  berd_SysLib(a5),a6
        call    Disable

        move.l  a1,a2

        bsr     GetStatus
        move.w  d0,IO_STATUS(a2)

        bclr.b  #IOSTB_WROTEBREAK,berdu_Status+1(a3)
        bclr.b  #IOSTB_READBREAK,berdu_Status+1(a3)

        move.l  berdu_WritePointer(a3),d0
        sub.l   berdu_ReadPointer(a3),d0
        bpl     .plus
        add.l   berdu_ReadBufferSize(a3),d0
.plus
        move.l  d0,IO_ACTUAL(a2)
a
        call    Enable

        PRINTF  2,"Tecken i bufferten: %ld",d0
        clr.l   IO_ACTUAL(a2)
        move.l  a2,a1
        bsr     TermIO
        popm    std
        rts

*******************************************************************************
**** Break ********************************************************************
*******************************************************************************

Break:
        PRINTF  2,"SDCMD_BREAK"
        pushm   std
        move.l  berd_SysLib(a5),a6
        call    Disable
        clr.l   IO_ACTUAL(a1)
        lea     berdu_CmdList(a3),a0
        btst    #SERB_QUEUEDBRK,IO_SERFLAGS(a1)
        beq     .no_queued
        lea     berdu_WriteList(a3),a0
.no_queued
        call    AddTail                 ; a1:iob/message
        call    Enable

        btst    #BB_SENDING,berdu_Flags(a3)
        bne     .exit

        ; We are not sending any data, so we have to trigger the interrupt
        ; code manually

        move.b  berdu_Flags(a3),d0
        and.b   #~BERISRF_MASK,d0
        or.b    #BERISRF_TXRDY,d0
        move.b  d0,berdu_Flags(a3)
        move.w  #INTF_SETCLR!INTF_PORTS,_custom+intreq
.exit
        popm    std
        rts

*******************************************************************************
**** SetParams ****************************************************************
*******************************************************************************

SetParams:
        PRINTF  2,"SDCMD_SETPARAMS"
        pushm   std
        move.l  a1,a2

        bsr     SetTheParams

        move.l  a2,a1
        bsr     TermIO
        popm    std
        rts

**** SetTheParams *************************************************************

; Initalizes the hardware to the supplied IO request. Called by CMD_RESET
; and SDCMD_SETPARAMS.

SetTheParams:
        PRINTF  2,"SetTheParams"
stp:
        pushm   std
        move.l  a1,a2
        move.l  berd_SysLib(a5),a6
        call    Disable
        moveq   #FALSE,d7               ; Call InitHW?
        ; The only parameter that may be changed when the device is in use...

        move.b  IO_SERFLAGS(a2),d0
        and.b   #SERF_XDISABLED,d0
        move.b  berdu_SerFlags(a3),d1
        eor.b   d1,d0
        beq     .nonewxonoff
        moveq   #1,d7                   ; Flag update
        move.b  IO_SERFLAGS(a2),d0
        and.b   #SERF_XDISABLED,d0
        move.b  berdu_SerFlags(a3),d1
        and.b   #~SERF_XDISABLED,d1
        or.b    d1,d0
        move.b  d0,berdu_SerFlags(a3)
        bra     .testrest
.nonewxonoff

        ; Fail if the unit is active

        move.b  #SerErr_DevBusy,IO_ERROR(a2)
;;      move.b  #SerErr_UnitBusy,IO_ERROR(a2)   ; Not defined???
        TSTLST2 berdu_ReadList(a3),a0
        bne     .error
        TSTLST2 berdu_WriteList(a3),a0
        bne     .error

        ; Device is not in use now, check the rest of the parameters
.testrest

        move.b  IO_SERFLAGS(a2),d0
        cmp.b   berdu_SerFlags(a3),d0
        beq     .nonewserflags
        moveq   #1,d7                   ; Flag update

        btst    #SERB_RAD_BOOGIE,d0
        beq     .noboogie
        bset    #SERB_XDISABLED,d0
        bclr    #SERB_PARTY_ON,d0
        move.b  #8,IO_READLEN(a2)
        move.b  #8,IO_WRITELEN(a2)
.noboogie

        move.b  d0,berdu_SerFlags(a3)

.nonewserflags

        move.l  IO_CTLCHAR(a2),berdu_CtrlChars(a3)

        move.l  IO_RBUFLEN(a2),d0
        cmp.l   berdu_ReadBufferSize(a3),d0
        beq     .nonewreadbuffer
        move.l  berdu_ReadBuffer(a3),d0
        beq     .noreadbuff
        move.l  d0,a1
        move.l  berdu_ReadBufferSize(a3),d0
        call    FreeMem
.noreadbuff
        move.b  #SerErr_BufErr,IO_ERROR(a2)
        move.l  IO_RBUFLEN(a2),d0
        move.l  #MEMF_PUBLIC!MEMF_CLEAR,d1
        call    AllocMem
        move.l  d0,berdu_ReadBuffer(a3)
        move.l  d0,berdu_ReadPointer(a3)
        move.l  d0,berdu_WritePointer(a3)
        beq     .error
        move.l  IO_RBUFLEN(a2),berdu_ReadBufferSize(a3)
.nonewreadbuffer

        move.l  IO_EXTFLAGS(a2),d0
        cmp.l   berdu_ExtFlags(a3),d0
        beq     .nonewextflags
        moveq   #1,d7                   ; Flag update
        move.l  d0,berdu_ExtFlags(a3)
.nonewextflags

        move.l  IO_BAUD(a2),d0
        cmp.l   berdu_BaudRate(a3),d0
        beq     .nonewbaudrate
        moveq   #1,d7                   ; Flag update
        move.l  d0,berdu_BaudRate(a3)
.nonewbaudrate

        move.l  IO_BRKTIME(a2),berdu_BreakTime(a3)

        move.l  IO_TERMARRAY+TERMARRAY_0(a2),berdu_TermArray+TERMARRAY_0(a3)
        move.l  IO_TERMARRAY+TERMARRAY_1(a2),berdu_TermArray+TERMARRAY_1(a3)

        move.b  IO_READLEN(a2),d0
        cmp.b   berdu_ReadLen(a3),d0
        beq     .nonewreadlen
        moveq   #1,d7                   ; Flag update
        move.b  d0,berdu_ReadLen(a3)
.nonewreadlen

        move.b  IO_WRITELEN(a2),d0
        cmp.b   berdu_WriteLen(a3),d0
        beq     .nonewwritelen
        moveq   #1,d7                   ; Flag update
        move.b  d0,berdu_WriteLen(a3)
.nonewwritelen

        move.b  IO_STOPBITS(a2),d0
        cmp.b   berdu_StopBits(a3),d0
        beq     .nonewstopbits
        moveq   #1,d7                   ; Flag update
        move.b  d0,berdu_StopBits(a3)
.nonewstopbits

        moveq   #0,d0
        tst     d7
        beq     .dontupdatehw
        bsr     InitHW
        PRINTF  2,"InitWH returned: %ld",d0
.dontupdatehw

        move.b  d0,IO_ERROR(a2)
        clr.l   IO_ACTUAL(a2)

.error
        call    Enable
        popm    std
        rts

*******************************************************************************
**** InterruptCode ************************************************************
*******************************************************************************

;in:
* d0    scratch
* d1    scratch
* a0    scratch
* a1    unit (scratch)
* a5    InterruptCode (scratch)
* a6    scratch

InterruptCode:
        pushm   std
        move.l  a1,a3
        move.l  berdu_Device(a3),a5
        move.l  berd_SysLib(a5),a6

        move.l  berdu_Base(a3),a0
        move.b  ber_isr(a0),d2

        move.b  berdu_Flags(a3),d0
        move.l  d0,d1
        and.b   #BERISRF_MASK,d0
        beq     .dontfake
        bclr    #BERISRB_NOIRQ,d2
.dontfake
        or.b    d0,d2
        and.b   #~BERISRF_MASK,d1
        move.b  d1,berdu_Flags(a3)

        and.b   #BERISRF_MASK,d2

        cmp.b   #BERISRF_LSR,d2
        bne     .notlsr
        move.b  ber_lsr(a0),d0
.notlsr
        cmp.b   #BERISRF_RXRDY,d2
        bne     .notrx
        bsr     ic_read
.notrx
        cmp.b   #BERISRF_TXRDY,d2
        bne     .nottx
        bsr     ic_write
.nottx
        cmp.b   #BERISRF_MSR,d2
        bne     .notmsr
        tst.b   ber_msr(a0)
.notmsr

ic_exit:
        moveq   #0,d0                   ; Call next server in chain
        popm    std                     ; Must not change the Z flag!
        rts

*** CMD_READ

ic_read
        pushm   std
        move.l  berdu_Base(a3),a4

        bsr     GetStatus
        btst    #IOSTB_XOFFREAD+8,d0
        bne     .noreader

.loop
        btst    #BERLSRB_RX,ber_lsr(a4)
        beq     .dontread

        move.l  berdu_ReadList+MLH_HEAD(a3),a2
        tst.l   LN_SUCC(a2)
        beq     .noreader
        move.l  IO_DATA(a2),a0
        move.l  IO_ACTUAL(a2),d1

;; Should check for XON/XOFF here
        move.b  ber_rhr(a4),(a0,d1.l)

        addq.l  #1,d1
        move.l  d1,IO_ACTUAL(a2)
        cmp.l   IO_LENGTH(a2),d1
        blo     .dontterm

        move.l  a2,a1
        call    Remove
        move.l  a2,a1
        bsr     TermIO
.dontterm
        bra     .loop
.noreader
        ; Fill circular buffer....
        move.b  ber_rhr(a4),d1
        move.l  berdu_ReadPointer(a3),d0
        sub.l   berdu_WritePointer(a3),d0
        bpl     .1
        add.l   berdu_ReadBufferSize(a3),d0
.1
        subq.l  #1,d0
        bne     .savechar
        move.b  #SerErr_BufOverflow,berdu_ErrorCode(a3) ; Signal error
        tst.b   ber_rhr(a4)
        bra     .loop
.savechar
        move.l  berdu_WritePointer(a3),a0
        move.b  ber_rhr(a4),(a0)+

        move.l  berdu_ReadBuffer(a3),d0
        add.l   berdu_ReadBufferSize(a3),d0
        cmp.l   a0,d0
        bne     .2
        move.l  berdu_ReadBuffer(a3),a0
.2
        move.l  a0,berdu_WritePointer(a3)

        bra     .loop

.dontread
        popm    std
        rts

*** CMD_WRITE

ic_write:
        pushm   std

        bclr    #BB_SENDING,berdu_Flags(a3)

        move.l  berdu_Base(a3),a4

        bsr     GetStatus
        btst    #IOSTB_XOFFWRITE+8,d0
        bne     .dontwrite
.loop
        btst    #BERLSRB_THREMPTY,ber_lsr(a4)
        beq     .dontwrite

        move.l  berdu_WriteList+MLH_HEAD(a3),a2
        tst.l   LN_SUCC(a2)
        beq     .nowriter
        move.l  IO_DATA(a2),a0
        move.l  IO_ACTUAL(a2),d1

        move.b  (a0,d1.l),d0
        move.b  d0,ber_thr(a4)
        bset    #BB_SENDING,berdu_Flags(a3)

        and.l   #$ff,d0
        PRINTF  2,"%lc",d0

        addq.l  #1,d1
        move.l  d1,IO_ACTUAL(a2)

        tst.b   d0
        bne     .notnull
        cmp.l   #-1,IO_LENGTH(a2)
        beq     .terminate
.notnull

        cmp.l   IO_LENGTH(a2),d1
        blo     .dontterm

.terminate
;       PRINTF  2,"INT: Writer 0x%08lx (0x%08lx, %ld)",a2,IO_DATA(a2),IO_LENGTH(a2)
        move.l  a2,a1
        call    Remove

.w8
        btst    #BERLSRB_THREMPTY,ber_lsr(a4)
        beq     .w8

        move.l  a2,a1
        bsr     TermIO
.dontterm
        bra     .loop
.nowriter
.dontwrite
        popm    std
        rts

*******************************************************************************
**** Hardware code ************************************************************
*******************************************************************************

;in:
* a3    unit
* a5    device
;out:
* d0    status bits (as in IO_STATUS)
GetStatus:
        moveq   #0,d0
        rts

        PRINTF  2,"GetStatus"
        pushm   std
        move.w  berdu_Status(a3),d0
        move.b  #%00011111,d0                   ; All flags inactive
        move.l  berdu_Base(a3),a0
        move.b  ber_lsr(a0),d1

        btst    #BERLSRB_OVERRUN,d1
        beq     .nooverrun
        move.b  #SerErr_LineErr,berdu_ErrorCode(a3)
.nooverrun

;; Is this the correct way to detect status word overruns?
        btst    #BERLSRB_ERROR,d1
        beq     .noerror

        move.b  d1,d2
        and.w   #BERLSRF_PARITY!BERLSRF_FRAME!BERLSRF_BREAK,d2
        bne     .goterror
        bset    #IOSTB_OVERRUN+8,d1

.goterror
        btst    #BERLSRB_PARITY,d1
        beq     .noparityerr
        move.b  #SerErr_ParityErr,berdu_ErrorCode(a3)
.noparityerr
        btst    #BERLSRB_FRAME,d1
        beq     .noframingerr
        move.b  #SerErr_LineErr,berdu_ErrorCode(a3)
.noframingerr
        btst    #BERLSRB_BREAK,d1
        beq     .nobreakerr
        bset    #IOSTB_READBREAK+8,d0
.nobreakerr

.noerror

        move.b  ber_msr(a0),d1
        btst    #BERMSRB_CTS,d1
        beq     .nocts
        bclr    #CIAB_COMCTS,d0                 ; Active low
.nocts
        btst    #BERMSRB_DSR,d1
        beq     .nodsr
        bclr    #CIAB_COMDSR,d0                 ; Active low
.nodsr
        btst    #BERMSRB_RI,d1
        beq     .nori
        bset    #CIAB_PRTRSEL,d0                ; Active high
.nori
        btst    #BERMSRB_CD,d1
        beq     .nocd
        bclr    #CIAB_COMCD,d0                  ; Active low
.nocd

        move.b  berdu_mcrreg(a3),d1
        btst    #BERMCRB_DTR,d1
        beq     .nodtr
        bclr    #CIAB_COMDTR,d0                 ; Active low
.nodtr
        popm    std
        rts

;in:
* a3    unit
* a5    device
InitHW:

        move.l  berdu_Base(a3),a0
        PRINTF  2,"InitHW 0x%08lx",a0

        ; Enable all interrupts
        move.b  #BERIERF_RXENA!BERIERF_TXENA!BERIERF_LSENA!BERIERF_MSENA,ber_ier(a0)

        moveq   #SerErr_InvParam,d0
        tst.l   berdu_ExtFlags(a3)
        bne     .exit

        move.b  #BERLCRF_DLABENA,ber_lcr(a0)    ;enable dlab
        move.l  #1843200,d0
        move.l  berdu_BaudRate(a3),d1
        beq     .bauderror
        divu    d1,d0                           ; Max 64 kbps!
        addq.l  #8,d0                           ; Round (+= 0.5)
        move.w  d0,d1

        lsr.w   #4,d0
        move.b  d0,ber_dll(a0)
        lsr.w   #8,d0
        move.b  d0,ber_dlm(a0)

; Check if legal baud rate
        and.w   #~15,d1
        beq     .bauderror
        move.l  #1843200,d0
        divu    d1,d0
        and.l   #$ffff,d0
        sub.l   berdu_BaudRate(a3),d0
        bpl     .1
        neg.l   d0
.1
        lsl.l   #4,d0                           ; Allow approx 6% rate error
        cmp.l   berdu_BaudRate(a3),d0
        blo     .baudOK
.bauderror
        moveq   #SerErr_BaudMismatch,d0
        bra     .exit
.baudOK
        

        move.b  berdu_ReadLen(a3),d0
        cmp.b   berdu_WriteLen(a3),d0
        beq     .samebits
        moveq   #SerErr_InvParam,d0
        bra     .exit
.samebits
        moveq   #BERLCRF_LEN5,d1
        cmp.b   #5,d0
        beq     .bitsok
        moveq   #BERLCRF_LEN6,d1
        cmp.b   #6,d0
        beq     .bitsok
        moveq   #BERLCRF_LEN7,d1
        cmp.b   #7,d0
        beq     .bitsok
        moveq   #BERLCRF_LEN8,d1
        cmp.b   #8,d0
        beq     .bitsok
        moveq   #SerErr_InvParam,d0
        bra     .exit
.bitsok

;       moveq   #SerErr_InvParam,d0
;       cmp.b   #1,berdu_StopBits(a3)
;       beq     .stopsok
;       cmp.b   #2,berdu_StopBits(a3)
;       bne     .exit
;       or.b    #BERLCRF_LONGSTOP,d1
;.stopsok
;
;       btst    #SERB_PARTY_ON,berdu_SerFlags(a3)
;       beq     .parityok
;       or.b    #BERLCRF_PARITYENA,d1
;.parityok
;       btst    #SERB_PARTY_ODD,berdu_SerFlags(a3)
;       bne     .parityodd
;       or.b    #BERLCRF_PARITYEVEN,d1
;.parityodd
        move.b  d1,ber_lcr(a0)
        move.b  d1,berdu_lcrreg(a3)     ; Store for BREAK signal usage later

        move.b  #BERMCRF_DTR,d1
        move.b  d1,ber_mcr(a0)
        move.b  d1,berdu_mcrreg(a3)     ; Store for RTS signal usage later

        moveq   #0,d0
.exit
        rts     

;in:
* a3    unit
* a5    device
FreeHW:
        move.l  berdu_Base(a3),a0
        PRINTF  2,"FreeHW 0x%08lx",a0

;.w8a
;       btst    #BB_SENDING,berdu_Flags(a3)
;       bne     .w8a
.w8b
        btst    #BERLSRB_THREMPTY,ber_lsr(a0)
        beq     .w8b

        ; Disable all interrupts
        move.b  #0,ber_ier(a0)
        move.b  #0,ber_lcr(a0)
        move.b  #0,ber_mcr(a0)
        rts

*******************************************************************************
**** Debug code ***************************************************************
*******************************************************************************

_LVORawPutChar  EQU     -$204

kprint_macro:
        pushm   d0-d1/a0-a1/a6
        bsr     KPrintF
;       move.l  dosbase,a6
;       moveq   #10,d1
;       call    Delay
;       moveq   #10,d1
;.l2
;       moveq   #-1,d0
;.l
;       move.w  d0,$dff180
;       dbf     d0,.l
;       dbf     d1,.l2
        popm    d0-d1/a0-a1/a6
        rts

KPrintF:
        pushm   a2/a6
        lea     KPutChar(pc),a2
        base    exec
        call    RawDoFmt
        popm    a2/a6
        rts

KPutChar:
        push    a6
        base    exec
        call    RawPutChar
        pop     a6
        rts

EndCode:        END


Go to the first, previous, next, last section, table of contents.