**  $Revision Header Start *****************************************************
**                                                                            **
**  Name       : 8n1.s                                                        **
**  Copyright  : © Copyright 95                                               **
**  Author     : L.Lucius                                                     **
**  Created    : 10 Jul 95                                                    **
**  Version    : 37.9                                                         **
**  Translator : snma 1.99b 28-Jun-95                                         **
**                                                                            **
**  Date       Version  Comment                                               **
**  ---------  -------  ----------------------------------------------------  **
**  04 Aug 95  37.9     First Aminet Release!                                 **
**  04 Aug 95  37.9     Removed some testing code and prevented unnecessary   **
**                      RTS line updates.                                     **
**  02 Aug 95  37.8     Added the ability to send BREAKs.  Requested by:      **
**                      Dwight Zenzano                                        **
**  29 Jul 95  37.7     Rearranged device init data to allow proper display   **
**                      by VERSION command.  (The device base init data had   **
**                      to be within the checksummed area.)                   **
**  28 Jul 95  37.6     When device was opened multiple times in shared       **
**                      mode, GURU occured on first close.                    **
**  22 Jul 95  37.5     Corrected version number.  8n1.i was not being        **
**                      generated correctly.                                  **
**  22 Jul 95  37.5     Incorrect handling of interrupts caused TBE           **
**                      interrupts to be missed.                              **
**  13 Jul 95  37.4     Rewrote the CMD_WRITE and TBE interrupt routines.     **
**                      Lengths of -1 were not being handled properly.        **
**  13 Jul 95  37.3     Rewrote OpenDevice() and CloseDevice() calls to       **
**                      correct several problems.                             **
**  11 Jul 95  37.2     Added "no handshaking" support.  Requested by:        **
**                      Ronald van Eijck                                      **
**  11 Jul 95  37.1     Rewrote the RBF interrupt routines and offloaded      **
**                      some of the work to a PORTS interrupt to prevent      **
**                      overruns and add the EOFMODE option.                  **
**  07 Jul 95  37.0     Only tried to remove the 'serial.device' instead of   **
**                      removing the CURRENT owner of the serial resources.   **
**                      Reported by: George Kourkoutas                        **
**  06 Jul 95  37.0     Invalid size used for GetPrefs() call causing stack   **
**                      overlay.  Reported by: Paul Harrison                  **
**  04 Jul 95  37.0     Set defaults during OpenDevice() instead of assuming  **
**                      values in I/O request are correct.  Reported by:      **
**                      Koen Peetermans                                       **
**  04 Jul 95  37.0     Initial Version                                       **
**                                                                            **
**  $Revision Header End *******************************************************
         ;
         ;
         ;
         CPU      M68010
         SNMAOPT  P,R
         ;
         ;
         ;
DEBUG    SET       0
         ;
         ;
         ;
         SECTION  text,CODE
         ;
         ;
         ;
DEVICES_SERIAL_I_OBSOLETE  EQU      1
         ;
         ;
         ;
         INCDIR   "include:"
         INCLUDE  "exec/lists.i"
         INCLUDE  "exec/memory.i"
         INCLUDE  "exec/resident.i"
         INCLUDE  "exec/devices.i"
         INCLUDE  "exec/execbase.i"
         INCLUDE  "exec/io.i"
         INCLUDE  "exec/ports.i"
         INCLUDE  "exec/errors.i"
         INCLUDE  "exec/initializers.i"
         INCLUDE  "intuition/preferences.i"
         INCLUDE  "devices/timer.i"
         INCLUDE  "devices/serial.i"
         INCLUDE  "hardware/custom.i"
         INCLUDE  "hardware/cia.i"
         INCLUDE  "hardware/intbits.i"
         INCLUDE  "hardware/adkbits.i"
         INCLUDE  "resources/misc.i"
         INCLUDE  "exec/alerts.i"
         INCLUDE  "8n1.i"
         ;
         ;        Define hardware references
         ;
         XREF     _AbsExecBase
         XREF     _custom
         XREF     _intena,_intenar,_intreq,_intreqr
         XREF     _ciab,_ciabpra
         XREF     _serper,_serdat,_serdatr
         XREF     _adkcon,_adkconr
         ;
         ;        Exec Functions
         ;
         XREF     _LVORemDevice,_LVOOpenDevice,_LVOCloseDevice
         XREF     _LVOSupervisor
         XREF     _LVORemove,_LVORemHead,_LVOAddTail
         XREF     _LVOAllocMem,_LVOFreeMem,_LVOCopyMem
         XREF     _LVOReplyMsg
         XREF     _LVOSendIO,_LVOAbortIO
         XREF     _LVODisable,_LVOEnable
         XREF     _LVOFindName
         XREF     _LVOOpenResource
         XREF     _LVOAddIntServer,_LVORemIntServer
         XREF     _LVOSetIntVector
         XREF     _LVOAlert
         XREF     _LVOOldOpenLibrary,_LVOCloseLibrary
         ;
         ;        Misc Resource Functions
         ;
         XREF     _LVOAllocMiscResource,_LVOFreeMiscResource
         ;
         ;        Intuition Functions
         ;
         XREF     _LVOGetPrefs
         ;
         ;        Mask used to get rid of the printer bits.  Remove CIAF_PRTRSEL
         ;        to enable RI.
         ;
PRTMASK  EQU      (CIAF_PRTRSEL|CIAF_PRTRPOUT|CIAF_PRTRBUSY)
         ;
         ;        Autovector offsets
         ;
LVL1VEC  EQU      (1-1)*4+$64
LVL5VEC  EQU      (5-1)*4+$64
         ;
         ;        Device base
         ;
         STRUCTURE 8n1Base,LIB_SIZE
         UBYTE    vb_SaveDDRA
         UBYTE    vb_SavePRA
         APTR     vb_MiscBase
         APTR     vb_OldLevel1
         APTR     vb_OldLevel5
         ULONG    vb_SegList
         ULONG    vb_DefBaud
         ULONG    vb_DefRBufLen
         ULONG    vb_IntRBuf
         ULONG    vb_IntRBufLen
         ULONG    vb_IntBaud
         UBYTE    vb_SerFlags
         UBYTE    vb_Initialized
         LABEL    sizeof_8n1Base
         ;
         ;
         ;
DISABLE  MACRO
         jsr      _LVODisable(a6)
         ENDM
         ;
ENABLE   MACRO
         jsr      _LVOEnable(a6)
         ENDM

PUTDEBUG macro    ;[level,msg]
;         ifge     DEBUG-\1
         movem.l  a0/a1/d0/d1,-(sp)
         lea      (.msg\@,pc),a0  ;Point to static format string
         lea      (4*4,sp),a1     ;Point to args
         XREF     DPutFmt
         bsr      DPutFmt
         movem.l  (sp)+,d0/d1/a0/a1
         bra.b    .end\@

.msg\@   dc.b     \2,10,0
         CNUL     0,4
.end\@
;         endc
         endm
         ;
         ;
         ;
DEBUG1   MACRO
         IFEQ      DEBUG-1
         XREF     _SendText
         move.l   \2,-(sp)
         pea.l    debug1s\@$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    12(sp),sp
         bra.b    debug1x\@$
debug1s\@$:
         dc.b     \1,0
         CNOP     0,2
debug1x\@$:
         ENDC
         ENDM
DEBUG0   MACRO
         XREF     _SendText
         pea.l    debug1s\@$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    8(sp),sp
         bra.b    debug1x\@$
debug1s\@$:
         dc.b     \1,0
debug1x\@$:
         ENDM
         ;
         ;
         ;
Start:
         moveq    #-1,d0                              ; set return code
         rts                                          ; return
         ;
         ;        RamLib looks for this romtag
         ;
ROMTag   DC.W     RTC_MATCHWORD                       ; RT_MATCHWORD
         DC.L     ROMTag                              ; RT_MATCHTAG
         DC.L     ENDTag                              ; RT_ENDSKIP
         DC.B     RTF_AUTOINIT                        ; RT_FLAGS
         DC.B     VERSION                             ; RT_VERSION
         DC.B     NT_DEVICE                           ; RT_TYPE
         DC.B     0                                   ; RT_PRI
         DC.L     Name                                ; RT_NAME
         DC.L     IdString                            ; RT_IDSTRING
         DC.L     Init                                ; RT_INIT
         ;
         ;        Perform device initialization
         ;
InitRoutine:
         exg      d0,a0                               ; swap seglist and base
         move.l   d0,vb_SegList(a0)                   ; store seglist in base
         move.l   a6,SysBase                          ; store in global storage
         exg      a0,d0                               ; swap them back
         rts                                          ; return
         ;
         ;
         ;
dev_Open:
         DEBUG1   "Devstart %lx",#Start
         move.l   a5,-(sp)                            ; save registers
         movea.l  a6,a5                               ; save base
         movea.l  SysBase(pc),a6                      ; get ExecBase
         ;
         tst.l    d0                                  ; unit 0 specified?
         bne      50$                                 ; nope, error (who cares?)
         ;
         tst.w    LIB_OPENCNT(a5)                     ; currently open?
         bne.b    10$                                 ; yep, go process
         ;
         move.b   IO_SERFLAGS(a1),vb_SerFlags(a5)     ; save flags
         ;
         tst.b    vb_Initialized(a5)                  ; already initialized?
         bne.b    40$                                 ; yep, skip initialization
         ;
         bsr      initResources                       ; go alloc resources
         tst.l    d0                                  ; initialized?
         bne.b    50$                                 ; nope, error
         ;
         bra.b    40$                                 ; go exit
         ;
10$      moveq    #SerErr_DevBusy,d0                  ; preset error status
         btst.b   #SERB_SHARED,vb_SerFlags(a5)        ; opened shared?
         beq.b    50$                                 ; nope, error
         btst.b   #SERB_SHARED,IO_SERFLAGS(a1)        ; requesting shared?
         beq.b    50$                                 ; nope, error
         ;
         ;        Initialize I/O request
         ;
40$      moveq    #8,d0                               ; get char size
         move.b   d0,IO_READLEN(a1)                   ; set read length
         move.b   d0,IO_WRITELEN(a1)                  ; set write length
         moveq    #1,d0                               ; get stop bits
         move.b   d0,IO_STOPBITS(a1)                  ; set stop bits
         move.l   vb_DefBaud(a5),IO_BAUD(a1)          ; set baud
         move.l   vb_DefRBufLen(a5),IO_RBUFLEN(a1)    ; set read buffer length
         ;
         addq.w   #1,LIB_OPENCNT(a5)                  ; incr open count
         bclr.b   #LIBB_DELEXP,LIB_FLAGS(a5)          ; clear expunge bit
         moveq    #0,d0                               ; no error
         ;
50$      move.b   d0,IO_ERROR(a1)                     ; store error code
         movea.l  a5,a6                               ; restore base
         move.l   (sp)+,a5                            ; restore registers
         rts                                          ; return
         ;
         ;        Attempt to allocate one of the serial resources.
         ;
allocResource:
         move.l   a6,-(sp)                            ; save base pointer
         move.l   d0,-(sp)                            ; save unit
         lea.l    Name(pc),a1                         ; get lock name
         move.l   vb_MiscBase(a5),a6                  ; get MiscBase
         jsr      _LVOAllocMiscResource(a6)           ; go allocate it
         tst.l    d0                                  ; did we get it?
         beq.b    20$                                 ; yep, branch
         ;
         ;        It's in use, so we try to locate the device using the string
         ;        returned and attempt to remove it.
         ;
         movea.l  SysBase(pc),a6                      ; get ExecBase
         ;
         movea.l  d0,a1                               ; get ptr to serial name
         lea.l    DeviceList(a6),a0                   ; get ptr to device list
         jsr      _LVOFindName(a6)                    ; go find it
         tst.l    d0                                  ; found?
         beq.b    10$                                 ; nope, branch
         ;
         movea.l  d0,a1                               ; xfer device ptr
         jsr      _LVORemDevice(a6)                   ; remove it
         ;
         ;        We then retry the allocate.
         ;
10$      move.l   (sp),d0                             ; get unit
         lea.l    Name(pc),a1                         ; get lock name
         movea.l  vb_MiscBase(a5),a6                  ; get MiscBase
         jsr      _LVOAllocMiscResource(a6)           ; go allocate it
         ;
20$      addq.l   #4,sp                               ; restore stack ptr
         movea.l  (sp)+,a6                            ; restore base ptr
         rts                                          ; return
         ;
         ;
         ;
initResources:
         move.l   a1,-(sp)                            ; save register
         ;
         lea.l    miscresource(pc),a1                 ; ptr to resource name
         jsr      _LVOOpenResource(a6)                ; go open it
         move.l   d0,vb_MiscBase(a5)                  ; save base
         ;
         moveq    #MR_SERIALPORT,d0                   ; set unit number
         bsr.b    allocResource                       ; go allocate it
         tst.l    d0                                  ; did we get it?
         bne.b    20$                                 ; nope, error
         ;
         moveq    #MR_SERIALBITS,d0                   ; set unit number
         bsr.b    allocResource                       ; go allocate it
         tst.l    d0                                  ; did we get it?
         bne.b    10$                                 ; nope, error
         ;
         bsr      getPrefs                            ; get default preferences
         tst.l    d0                                  ; got 'em?
         beq.b    30$                                 ; yep, branch
         ;
         moveq    #MR_SERIALPORT,d0                   ; set unit
         movea.l  vb_MiscBase(a5),a6                  ; get MiscBase
         jsr      _LVOFreeMiscResource(a6)            ; release the resource
         ;
10$      moveq    #MR_SERIALPORT,d0                   ; set unit
         movea.l  vb_MiscBase(a5),a6                  ; get MiscBase
         jsr      _LVOFreeMiscResource(a6)            ; release the resource
         movea.l  SysBase(pc),a6                      ; restore ExecBase
         ;
20$      moveq    #SerErr_DevBusy,d0                  ; set error status
         bra      40$                                 ; go return
         ;
30$      moveq    #0,d1                               ; clear flags
         moveq    #UNIT_VBLANK,d0                     ; set unit
         lea.l    timerReq(pc),a1                     ; ptr to timer request
         lea.l    timerdevice(pc),a0                  ; ptr to device name
         jsr      _LVOOpenDevice(a6)                  ; go open it
         ;
         DISABLE                                      ; disable interrupts
         ;
         lea.l    _ciab,a1                            ; get ptr to ciab
         move.b   ciaddra(a1),vb_SaveDDRA(a5)         ; save DDR value
         andi.b   #PRTMASK,ciaddra(a1)                ; make serial bits input
         move.b   ciapra(a1),vb_SavePRA(a5)           ; save PR value
         ori.b    #CIAF_COMDTR|CIAF_COMRTS,ciaddra(a1) ; make DTR/RTS output
         andi.b   #CIAF_COMCTS|CIAF_COMDSR|PRTMASK,ciapra(a1) ; make CTS/DSR input
         andi.b   #CIAF_COMDTR|CIAF_COMRTS|PRTMASK,ciaddra(a1) ; turn on DTR/RTS
         ;
         moveq    #INTB_PORTS,d0                      ; get interrupt number
         lea.l    VBInterrupt(pc),a1                  ; get interrupt ptr
         jsr      _LVOAddIntServer(a6)                ; add it to the list
         ;
         bsr      getVBR                              ; get vector base (in A0)
         ;
         move.l   LVL1VEC(a0),vb_OldLevel1(a5)        ; save original vector
         lea.l    level1(pc),a1                       ; get new vector ptr
         move.l   a1,LVL1VEC(a0)                      ; set new vector
         ;
         move.l   LVL5VEC(a0),vb_OldLevel5(a5)        ; save original vector
         lea.l    level5(pc),a1                       ; get new vector ptr
         move.l   a1,LVL5VEC(a0)                      ; set new vector
         ;
         lea.l    _custom,a1                          ; get ptr to custom chips
         move.w   #INTF_RBF|INTF_TBE,intreq(a1)       ; clear pending interrupts
         move.w   #INTF_SETCLR|INTF_RBF|INTF_TBE,intena(a1) ; enable RBF & TBE
         ;
         addq.b   #1,vb_Initialized(a5)               ; set flag
         moveq    #0,d0                               ; set good status
         ;
         ENABLE                                       ; enable interrupts
         ;
40$      movea.l  (sp)+,a1                            ; restore register
         rts                                          ; return
         ;
         ;        Get system preferences
         ;
getPrefs:
         move.l   a1,-(sp)                            ; save registers
         ;
         lea.l    intuitlib(pc),a1                    ; ptr to library name
         jsr      _LVOOldOpenLibrary(a6)              ; go open it (any version)
         movea.l  d0,a6                               ; get intuition base
         ;
         move.l   #(pf_SerParShk+3)&$fffffffc,d0      ; size we need (aligned)
         suba.l   d0,sp                               ; reserve space
         ;
         movea.l  sp,a0                               ; set data area ptr
         jsr      _LVOGetPrefs(a6)                    ; get preferences
         ;
         movea.l  a6,a1                               ; get intuition base
         movea.l  SysBase(pc),a6                      ; restore ExecBase
         jsr      _LVOCloseLibrary(a6)                ; close it
         ;
         moveq    #$0f,d1                             ; set mask
         and.b    pf_SerStopBuf(sp),d1                ; get bufsize index
         addq.l   #8,d1                               ; calc shift value
         moveq    #2,d0                               ; get 1<<1
         lsl.l    d1,d0                               ; get default bufsize
         move.l   d0,vb_DefRBufLen(a5)                ; and store
         ;
         moveq    #0,d1                               ; clear upper half
         move.w   pf_BaudRate(sp),d1                  ; get baud rate
         add.l    d1,d1                               ; generate offset
         move.w   baudTable(pc,d1.w),d1               ; get default baud
         move.l   d1,vb_DefBaud(a5)                   ; and store
         ;
         bsr      internalReset                       ; go init baud and buffer
         ;
         lea.l    (pf_SerParShk+3)&$fffffffc(sp),sp   ; restore stack
         movea.l  (sp)+,a1                            ; restore registers
         rts                                          ; return ( status in D0 )
         ;
         ;        Preferences baud lookup table
         ;
baudTable:
         dc.w     112,300,1200,2400,4800,9600,19200,31250
         ;
         ;        Set exception vectors
         ;
getVBR:
         move.l   a5,-(sp)                            ; save registers
         suba.l   a0,a0                               ; ptr to vector base (68000)
         btst.b   #AFB_68010,AttnFlags+1(a6)          ; 68010 or higher?
         beq.s    10$                                 ; nope, go set vector
         lea.l    20$(pc),a5                          ; ptr to routine
         jsr      _LVOSupervisor(a6)                  ; get into supervisor state
10$      movea.l  (sp)+,a5                            ; restore register
         rts                                          ; return
20$      movec.l  vbr,a0                              ; get vector base
         rte                                          ; return
         ;
         ;
         ;
freeResources:
         move.l   a1,-(sp)                            ; save registers
         ;
         DISABLE                                      ; disable interrupts
         ;
         bsr      getVBR                              ; get vector base (in A0)
         ;
         moveq    #1,d0                               ; set not restored code
         ;
         lea.l    level1(pc),a1                       ; get our vector ptr
         cmpa.l   LVL1VEC(a0),a1                      ; do they match?
         bne      10$                                 ; nope, can't restore
         ;
         lea.l    level5(pc),a1                       ; get our vector ptr
         cmpa.l   LVL5VEC(a0),a1                      ; do they match?
         bne      10$                                 ; nope, can't restore
         ;
         move.l   vb_OldLevel1(a5),LVL1VEC(a0)        ; restore original vector
         move.l   vb_OldLevel5(a5),LVL5VEC(a0)        ; restore original vector
         ;
         moveq    #INTB_PORTS,d0                      ; get interrupt number
         lea.l    VBInterrupt(pc),a1                  ; get interrupt ptr
         jsr      _LVORemIntServer(a6)                ; remove it from the list
         ;
         move.w   #INTF_RBF|INTF_TBE,d0               ; indicate serial interrupts
         move.w   d0,_intena                          ; disable interrupts
         move.w   d0,_intreq                          ; clear pending interrupts
         ;
         bsr      freeBuf                             ; free allocated buffers
         ;
         lea.l    _ciab,a0                            ; get pointer to ciab
         move.b   ciaddra(a0),d0                      ; get DDR value
         andi.b   #PRTMASK,d0                         ; save printer bits
         ori.b    #~PRTMASK,d0                        ; set serial bits to output
         move.b   d0,ciaddra(a0)                      ; store value
         ;
         move.b   ciapra(a0),d0                       ; get PR value
         andi.b   #PRTMASK,d0                         ; mask out serial bits
         move.b   vb_SavePRA(a5),d1                   ; get saved value
         andi.b   #~PRTMASK,d1                        ; mask out printer bits
         or.b     d1,d0                               ; combine the two
         move.b   d0,ciapra(a0)                       ; and store
         ;
         move.b   ciaddra(a0),d0                      ; get DDR value
         andi.b   #PRTMASK,d0                         ; mask out serial bits
         move.b   vb_SaveDDRA(a5),d1                  ; get saved value
         andi.b   #~PRTMASK,d1                        ; mask out printer bits
         or.b     d1,d0                               ; combine the two
         move.b   d0,ciaddra(a0)                      ; and store
         ;
         lea.l    timerReq(pc),a1                     ; get ptr to timer request
         jsr      _LVOCloseDevice(a6)                 ; go close it
         ;
         movea.l  vb_MiscBase(a5),a6                  ; get MiscBase
         moveq    #MR_SERIALBITS,d0                   ; set unit
         jsr      _LVOFreeMiscResource(a6)            ; release the resource
         ;
         moveq    #MR_SERIALPORT,d0                   ; set unit
         jsr      _LVOFreeMiscResource(a6)            ; release the resource
         movea.l  SysBase(pc),a6                      ; restore ExecBase
         ;
         ENABLE                                       ; enable interrupts
         ;
         subq.b   #1,vb_Initialized(a5)               ; clear flag
         moveq    #0,d0                               ; free up everything
         ;
10$      movea.l  (sp)+,a1                            ; restore registers
         rts                                          ; return
         ;
         ;        Device Close routine
         ;
dev_Close:
         ;
         moveq    #-1,d0                              ; invalidate
         move.l   d0,IO_DEVICE(a1)                    ;   device
         ;
         subq.w   #1,LIB_OPENCNT(a6)                  ; decr open count
         bne.b    dev_Null                            ; still open? yep, branch
         ;
         move.l   a5,-(sp)                            ; save registers
         movea.l  a6,a5                               ; save base
         movea.l  SysBase(pc),a6                      ; get ExecBase
         ;
         bsr      freeResources                       ; free allocated resources
         ;
         movea.l  a5,a6                               ; restore base
         movea.l  (sp)+,a5                            ; restore registers
         ;
         tst.l    d0                                  ; freed?
         bne.b    dev_Null                            ; nope, can't expunge, exit
         ;
         clr.b    vb_SerFlags(a6)                     ; clear flags
         ;
         btst.b   #LIBB_DELEXP,LIB_FLAGS(a6)          ; delayed expunge set?
         beq.b    dev_Null                            ; nope, go exit
         ;
         ;        Device Expunge routine (also fall through from dev_Close)
         ;
dev_Expunge:
         bset.b   #LIBB_DELEXP,LIB_FLAGS(a6)          ; Set expunge flag
         tst.w    LIB_OPENCNT(a6)                     ; currently open?
         bne.b    dev_Null                            ; yep, so just exit
         ;
         move.l   vb_SegList(a6),d0                   ; get seglist ptr
         movem.l  d0/a5/a6,-(sp)                      ; save registers (save D0!)
         ;
         movea.l  a6,a5                               ; save base
         movea.l  SysBase(pc),a6                      ; get ExecBase
         movea.l  a5,a1                               ; get base
         jsr      _LVORemove(a6)                      ; Remove it
         ;
         movea.l  a5,a1                               ; get base
         moveq    #0,d0                               ; clear work
         move.w   LIB_NEGSIZE(a5),d0                  ; calculate
         suba.w   d0,a1                               ;   memory address
         add.w    LIB_POSSIZE(a5),d0                  ;     and size
         jsr      _LVOFreeMem(a6)                     ; free it
         ;
         movem.l  (sp)+,d0/a5/a6                      ; restore registers
         rts                                          ; return (seglist in D0!)
         ;
         ;        Device "ExtFunc" routine
         ;
dev_Null:
         moveq    #0,d0                               ; set return code
         rts                                          ; return
         ;
         ;
         ;
         IFEQ     DEBUG-1
printIO:
         move.l   a1,-(sp)
         move.l   a0,-(sp)
         pea.l    0$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    16(sp),sp

         moveq    #0,d0
         move.w   IO_COMMAND(a1),d0
         move.l   d0,-(sp)
         move.l   MN_REPLYPORT(a1),-(sp)
         moveq    #0,d0
         move.b   LN_TYPE(a1),d0
         move.l   d0,-(sp)
         pea.l    1$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    20(sp),sp

         move.l   IO_LENGTH(a1),-(sp)
         moveq    #0,d0
         move.b   IO_ERROR(a1),d0
         move.l   d0,-(sp)
         move.b   IO_FLAGS(a1),d0
         move.l   d0,-(sp)
         pea.l    2$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    20(sp),sp

         move.l   IO_EXTFLAGS(a1),-(sp)
         move.l   IO_RBUFLEN(a1),-(sp)
         move.l   IO_ACTUAL(a1),-(sp)
         pea.l    3$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    20(sp),sp

         moveq    #0,d0
         move.b   IO_WRITELEN(a1),d0
         move.l   d0,-(sp)
         move.b   IO_READLEN(a1),d0
         move.l   d0,-(sp)
         move.l   IO_BAUD(a1),-(sp)
         pea.l    4$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    20(sp),sp

         moveq    #0,d0
         move.w   IO_STATUS(a1),d0
         move.l   d0,-(sp)
         moveq    #0,d0
         move.b   IO_SERFLAGS(a1),d0
         move.l   d0,-(sp)
         move.b   IO_STOPBITS(a1),d0
         move.l   d0,-(sp)
         pea.l    5$
         pea.l    Name
         jsr      _SendText(pc)
         lea.l    20(sp),sp
         rts

0$       dc.b     '%ls - I/O     %8lx',0
1$       DC.B     'Type    %8ld     ReplyPort %8lx    Command  %8ld',0
2$       DC.B     'Flags   %8lx     Error     %8ld    Length   %8ld',0
3$       DC.B     'Actual  %8ld     RBufLen   %8ld    ExtFlags %8lx',0
4$       DC.B     'Baud    %8ld     ReadLen   %8ld    WriteLen %8ld',0
5$       DC.B     'StopBit %8ld     SerFlags  %8lx    Status   %8lx',0
         ;
         ;
         ;
abortIOs dc.b     'Abort Start',0
abortIOe dc.b     'Abort End',0
abortIOa dc.b     'Aborting',0
beginIOs dc.b     'Begin Start',0
beginIOe dc.b     'Begin End',0
         CNOP     0,4
         ENDC
DEBUGIO  MACRO
         IFEQ     DEBUG-1
         lea.l    \1,a0
         jsr      printIO
         ENDC
         ENDM
         ;
cmdTable dc.w     cmd_Invalid-cmdTable                ; CMD_INVALID
         dc.w     cmd_Reset-cmdTable                  ; CMD_RESET
         dc.w     cmd_Read-cmdTable                   ; CMD_READ
         dc.w     cmd_Write-cmdTable                  ; CMD_WRITE
         dc.w     cmd_Invalid-cmdTable                ; CMD_UPDATE
         dc.w     cmd_Clear-cmdTable                  ; CMD_CLEAR
         dc.w     cmd_Invalid-cmdTable                ; CMD_STOP
         dc.w     cmd_Invalid-cmdTable                ; CMD_START
         dc.w     cmd_Flush-cmdTable                  ; CMD_FLUSH
         dc.w     sdcmd_Query-cmdTable                ; SDCMD_QUERY
         dc.w     sdcmd_Break-cmdTable                ; SDCMD_BREAK
endTable dc.w     sdcmd_SetParams-cmdTable            ; SDCMD_SETPARAMS
         ;
         ;        Device BeginIO routine
         ;
dev_BeginIO:
         move.l   a5,-(sp)                            ; save register
         movea.l  a6,a5                               ; save base
         movea.l  SysBase(pc),a6                      ; get ExecBase
         DEBUGIO  beginIOs
         ;
         move.b   #NT_MESSAGE,LN_TYPE(a1)             ; set type
         clr.b    IO_ERROR(a1)                        ; clear error
         ;
         andi.b   #~(IOSERF_QUEUED|IOSERF_ACTIVE),IO_FLAGS(a1) ; clear flags
         ;
         move.w   IO_COMMAND(a1),d0                   ; get command
         cmpi.w   #CMD_READ,d0                        ; read command?
         beq.b    40$                                 ; yep, go process
         cmpi.w   #CMD_WRITE,d0                       ; write command?
         beq.b    50$                                 ; yep, go process
         cmpi.w   #SDCMD_BREAK,d0                     ; break command?
         beq.b    60$                                 ; yep, go process
         add.w    d0,d0                               ; multiply by 2
         cmpi.w   #endTable-cmdTable,d0               ; in range?
         bhi.b    30$                                 ; nope, error
         ;
         move.w   cmdTable(pc,d0.w),d0                ; get routine offset
         jsr      cmdTable(pc,d0.w)                   ; go do it
         ;
10$      DEBUGIO  beginIOe
         btst.b   #IOB_QUICK,IO_FLAGS(a1)             ; need to reply?
         bne.b    20$                                 ; nope, branch
15$      jsr      _LVOReplyMsg(a6)                    ; send it back
         ;
20$      movea.l  a5,a6                               ; restore base
         movea.l  (sp)+,a5                            ; restore register
         rts                                          ; return
         ;
30$      move.b   #IOERR_NOCMD,IO_ERROR(a1)           ; invalid command
         bra.b    10$                                 ; branch
40$      bsr      cmd_Read                            ; go do read
         tst.l    d0                                  ; handled immediately?
         beq.b    10$                                 ; yep, process quick I/O
         bra.b    20$                                 ; nope, go return
50$      bsr      cmd_Write                           ; go do write
         bra.b    20$                                 ; go return
60$      bsr      sdcmd_Break                         ; go do break
         bra.b    20$                                 ; go return
         ;
         ;        Device AbortIO routine
         ;
dev_AbortIO:
         move.l   a5,-(sp)                            ; save registers
         movea.l  a6,a5                               ; save base
         movea.l  SysBase(pc),a6                      ; get ExecBase
         DEBUGIO  abortIOs
         ;
         DISABLE                                      ; disable interrupts
         ;
         btst.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; queued request?
         bne.b    40$                                 ; yep, branch
         ;
         btst.b   #IOSERB_ACTIVE,IO_FLAGS(a1)         ; active request?
         beq.b    10$                                 ; nope, just exit
         ;
         cmpi.w   #CMD_READ,IO_COMMAND(a1)            ; was it a read?
         beq.b    20$                                 ; yep, go process
         ;
         cmpi.w   #CMD_WRITE,IO_COMMAND(a1)           ; was it a write?
         beq.b    30$                                 ; yep, go process
         ;
         cmpi.w   #SDCMD_BREAK,IO_COMMAND(a1)         ; was it a break?
         beq.b    30$                                 ; yep, go process
         ;
         ;        Fall through or enter from below
         ;
10$      ENABLE                                       ; enable ints and return
         movea.l  a5,a6                               ; restore base
         movea.l  (sp)+,a5                            ; restore registers
         rts                                          ; return
         ;
         ;        Abort an active read request
         ;
20$      clr.l    cr_IOReq                            ; no longer active
         bra.b    50$                                 ; go set flags
         ;
         ;        Abort an active write request
         ;
30$      clr.l    cw_Length                           ; no longer active
         clr.l    cw_IOReq                            ; no longer active
         move.l   cw_Buffer(pc),d0                    ; get buffer ptr
         sub.l    IO_DATA(a1),d0                      ; calc number of bytes xfer'd
         move.l   d0,IO_ACTUAL(a1)                    ; store
         ;
         ;        Force a TBE interrupt to get the next write going.
         ;
         move.w   #INTF_SETCLR|INTF_TBE,_intreq       ; make TBE pending
         bra.b    50$                                 ; go set flags
         ;
         ;        Remove I/O from queue.
         ;
40$      move.l   a1,-(sp)                            ; save ptr
         jsr      _LVORemove(a6)                      ; remove it
         movea.l  (sp)+,a1                            ; restore ptr
         ;
         ;        Set error and return I/O
         ;
50$      andi.b   #~(IOSERF_QUEUED|IOSERF_ACTIVE),IO_FLAGS(a1) ; clear flags
         moveq    #IOERR_ABORTED,d0                   ; indicate aborted
         move.b   d0,IO_ERROR(a1)                     ; store status
         jsr      _LVOReplyMsg(a6)                    ; send it back
         bra.b    10$                                 ; branch to return
         ;
         ;        Abort all active/queued commands and reset internal state
         ;
cmd_Reset:
         move.l   a1,-(sp)                            ; save I/O request
         bsr.b    cmd_Flush                           ; go abort queued requests
         ;
         DISABLE                                      ; disable interrupts
         ;
         ;        This must follow the DISABLE
         ;
         exg      a5,a6                               ; exchange base and ExecBase
         ;
         move.l   cr_IOReq(pc),d0                     ; active read?
         beq.b    10$                                 ; nope, branch
         movea.l  d0,a1                               ; get I/O request
         bsr      dev_AbortIO                         ; go abort it
         ;
         ;
         ;
10$      move.l   cw_IOReq(pc),d0                     ; active write?
         beq.b    20$                                 ; nope, branch
         movea.l  d0,a1                               ; get I/O request
         bsr      dev_AbortIO                         ; go abort it
         ;
20$      exg      a5,a6                               ; restore base and ExecBase
         movea.l  (sp)+,a1                            ; restore I/O request
         moveq    #8,d0                               ; get char size
         move.b   d0,IO_READLEN(a1)                   ; set read length
         move.b   d0,IO_WRITELEN(a1)                  ; set write length
         moveq    #1,d0                               ; get stop bits
         move.b   d0,IO_STOPBITS(a1)                  ; set stop bits
         move.l   vb_DefBaud(a5),IO_BAUD(a1)          ; set to default baud
         move.l   vb_DefRBufLen(a5),IO_RBUFLEN(a1)    ; set to default buflen
         bsr      internalReset                       ; go set/verify parameters
         move.b   d0,IO_ERROR(a1)                     ; set error code
         ENABLE                                       ; enable interrupts
         rts                                          ; return
         ;
         ;        Abort all "queued" requests, leaving all active alone.
         ;
cmd_Flush:
         movem.l  a1/a2,-(sp)                         ; save registers
         DISABLE                                      ; disable interrupts
         lea.l    readQ(pc),a2                        ; get ptr to read queue
         bsr.b    20$                                 ; branch to abort
         lea.l    writeQ(pc),a2                       ; get ptr to write queue
         bsr.b    20$                                 ; branch to abort
         ENABLE                                       ; enable interrupts
         movem.l  (sp)+,a1/a2                         ; restore registers
10$      rts                                          ; return ( used below too!! )
         ;
20$      movea.l  a2,a0                               ; get list ptr
         jsr      _LVORemHead(a6)                     ; get I/O request
         tst.l    d0                                  ; end of list?
         beq.b    10$                                 ; yep, branch to return
         movea.l  d0,a1                               ; get I/O request
         bclr.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; no longer queued
         moveq    #IOERR_ABORTED,d0                   ; indicate aborted
         move.b   d0,IO_ERROR(a1)                     ; store status
         jsr      _LVOReplyMsg(a6)                    ; send it back
         bra.b    20$                                 ; continue with next
         ;
         ;
         ;
cmd_Read:
         ;
         ;        Zero length requests just get returned.
         ;
         clr.l    IO_ACTUAL(a1)                       ; clear bytes read
         move.l   IO_LENGTH(a1),d0                    ; get length and test
         beq      10$                                 ; yep, leave
         DEBUG1   "read IOREQ %lx",cr_IOReq(pc)
         ;
         ;
         ;
         move.l   cr_IOReq(pc),d1                     ; have an active request?
         bne.b    50$                                 ; yep, go queue this one
         ;
         ;
         ;
         DEBUG1   "incnt = %ld",i_InCnt
         cmp.l    i_InCnt(pc),d0                      ; length > current bytes?
         bgt.b    50$                                 ; yep, go queue it
         DEBUG1   "copying",#0
         ;
         ;        Setup fields and go copy the data
         ;
         not.b    disableRead
         move.l   IO_DATA(a1),cr_OutPtr               ; get/set output ptr
         move.l   IO_LENGTH(a1),cr_Length             ; get/set output count
         bsr      copyData                            ; go copy 'em
         not.b    disableRead
         moveq    #0,d0
         ;
         ;
         ;
10$      rts                                          ; return
         ;
         ;        Just set flags and queue.  The VB interrupt will handle it.
         ;
50$      DEBUG1   "Queueing",#0
         bset.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; indicate queued
         bclr.b   #IOB_QUICK,IO_FLAGS(a1)             ; clear quick I/O flag
         ;
         lea.l    readQ(pc),a0                        ; get pointer to read queue
         move.l   a1,-(sp)                            ; save I/O request
         DISABLE                                      ; disable interrupts
         jsr      _LVOAddTail(a6)                     ; and queue it
         ENABLE                                       ; enable interrupts
         movea.l  (sp)+,a1                            ; restore I/O request
         moveq    #1,d0                               ; not handled immediately
         bra.b    10$                                 ; branch to return
         ;
         ;
         ;
cmd_Write:
         ;
         ;        Zero length requests just get returned.
         ;
         clr.l    IO_ACTUAL(a1)                       ; clear bytes written
         move.l   IO_LENGTH(a1),d0                    ; have anything to write?
         beq.b    wbexit                              ; nope, exit
         ;
         ;        Entry point for Break command and fall through.
         ;
sdcmd_Break:
         ;
         ;        Just set flags and queue. The TBE interrupt will handle it.
         ;
         bset.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; indicate queued
         bclr.b   #IOB_QUICK,IO_FLAGS(a1)             ; clear quick I/O flag
         ;
         DISABLE                                      ; disable interrupts
         ;
         lea.l    writeQ(pc),a0                       ; get queue list ptr
         move.l   a1,-(sp)                            ; save I/O request
         jsr      _LVOAddTail(a6)                     ; and queue it
         movea.l  (sp)+,a1                            ; restore I/O request
         ;
         ;        If we have an active request, don't force interrupt.
         ;
         move.l   cw_IOReq(pc),d0                     ; have an active request?
         bne.b    10$                                 ; yep, branch
         ;
         ;        Force a TBE interrupt to get the writes going.
         ;
         move.w   #INTF_SETCLR|INTF_TBE,_intreq       ; make TBE pending
         ;
10$      ENABLE                                       ; enable interrupts
wbexit   rts                                          ; return
         ;
         ;        Resets serial read buffer
         ;
         ;        Since this routine is called internally, it must NOT reference
         ;        the I/O request.
         ;
cmd_Clear
         DISABLE                                      ; disable interrupts
         lea.l    i_BufPtr(pc),a0                     ; get ptr internal control

         move.l   vb_IntRBuf(a5),d0                   ; get internal buffer ptr
         move.l   vb_IntRBufLen(a5),d1                ; and internal buffer len

         move.l   d0,(a0)+                            ; store buffer ptr
         move.l   d0,(a0)+                            ; set current input ptr
         move.l   d0,(a0)+                            ; set current output ptr
         add.l    d1,d0                               ; add buffer length
         move.l   d0,(a0)+                            ; store ptr to end of buffer
         clr.l    (a0)+                               ; clear byte cnt
         move.l   vb_IntBaud(a5),d0                   ; get internal baud
         lsr.l    #4,d0                               ; divide by 16
         sub.l    d0,d1                               ; subtract from length
         move.l   d1,(a0)                             ; set threshold
         ENABLE                                       ; enable interrupts
         DEBUG1   "IntRBuf = %lx",vb_IntRBuf(a5)
         DEBUG1   "IntRBufLen = %ld",vb_IntRBufLen(a5)
         DEBUG1   "IntBaud = %ld",vb_IntBaud(a5)
         DEBUG1   "IntThreshold = %ld",i_Thresh
         rts                                          ; return
         ;
         ;
         ;
cmd_Invalid
         move.b   #IOERR_NOCMD,IO_ERROR(a1)           ; set bad status
         rts                                          ; return
         ;
         ;
         ;
sdcmd_Query:
         DISABLE                                      ; disable interrupts
         moveq    #0,d0                               ; clear d0
         move.b   _ciabpra,d0                         ; get PR register
         andi.w   #~PRTMASK,d0                        ; zap printer bits
         move.w   d0,IO_STATUS(a1)                    ; store status
         move.l   i_InCnt(pc),IO_ACTUAL(a1)           ; byte left in buffer
         ENABLE                                       ; restore interrupts
         rts                                          ; return
         ;
         ;
         ;
sdcmd_SetParams:
         ;
         moveq    #8,d0                               ; get char length
         cmp.b    IO_READLEN(a1),d0                   ; 8 bit chars for read?
         bne.b    40$                                 ; nope, branch
         cmp.b    IO_WRITELEN(a1),d0                  ; 8 bit chars for write?
         bne.b    40$                                 ; nope, branch
         moveq    #1,d0                               ; get stop bits
         cmp.b    IO_STOPBITS(a1),d0                  ; 1 stop bit?
         bne.b    40$                                 ; nope, branch
         ;
         ;        If the 7wire bit is not on, we will only use 3-wire protocol
         ;
         moveq    #0,d0                               ; clear flag
         btst.b   #SERB_7WIRE,IO_SERFLAGS(a1)         ; use 7wire handshaking?
         beq.b    10$                                 ; nope, branch
         moveq    #1,d0                               ; set flag
10$      move.b   d0,Handshake                        ; store flag
         ;
         move.l   IO_BAUD(a1),d1                      ; get baud from I/O req
         bne.b    20$                                 ; specified?
         move.l   vb_IntBaud(a5),d1                   ; get current baud from base
         bne.b    20$                                 ; specified?
         move.l   vb_DefBaud(a5),d1                   ; get default baud from base
20$      cmpi.l   #110,d1                             ; too low?
         blt.b    40$                                 ; error
         cmpi.l   #292000,d1                          ; too high?
         bgt.b    40$                                 ; error
         ;
         move.l   d1,IO_BAUD(a1)                      ; save baud in I/O req
         ;
         move.l   IO_RBUFLEN(a1),d0                   ; get buffer length
         bne.b    30$                                 ; specified?
         move.l   vb_IntRBufLen(a5),d0                ; get current from base
         bne.b    30$                                 ; specified?
         move.l   vb_DefRBufLen(a5),d0                ; get default from base
         ;
30$      bsr.b    internalReset                       ; go init baud and buffer
35$      move.b   d0,IO_ERROR(a1)                     ; set error code
         rts                                          ; return
         ;
40$      moveq    #SerErr_InvParam,d0                 ; set error
         bra.b    35$                                 ; go return
         ;
         ;        Reset the buffer and baud rate
         ;
         ;        Registers:  D0 = Buffer length
         ;                    D1 = Baud rate
         ;
internalReset:
         ;
         move.l   d0,-(sp)                            ; save D0
         move.l   d1,d0                               ; get baud rate
         bsr.b    setPeriod                           ; go set the serper register
         move.l   (sp)+,d0                            ; restore D0
         ;
         move.l   vb_IntBaud(a5),d1                   ; get current baud
         lsr.l    #$3,d1                              ; divide by 8
         cmp.l    d1,d0                               ; buflen > CPS
         bhi.b    10$                                 ; yep, branch
         move.l   #65536,d0                           ; else use 8K
         ;
10$      bsr.b    allocBuf                            ; go allocate a new buffer
         rts                                          ; return (D0 has status)
         ;
         ;        Set serial period register
         ;
setPeriod:
         cmp.l    vb_IntBaud(a5),d0                   ; current baud = new baud?
         beq.s    40$                                 ; yep, just exit
         move.l   d0,vb_IntBaud(a5)                   ; save new baud
         move.l   d0,d1                               ; save again
         lsl.l    #3,d0                               ; baud *= 8
         sub.l    d1,d0                               ; baud -= saved baud
         move.l   #25000000,d1                        ; XXX BUG XXX right?????
         cmpi.b   #50,PowerSupplyFrequency(a6)        ; PAL machine?
         bne.s    10$                                 ; nope, branch
         move.l   #24772416,d1                        ; XXX BUG XXX right?????
10$      cmpi.l   #$FFFF,d0                           ; Divide
         ble.b    20$                                 ;
         lsr.l    #5,d0                               ;
         divu.w   d0,d1                               ;
         andi.l   #$FFFF,d1                           ;
         lsr.l    #5,d1                               ;
         bra.b    30$                                 ;
20$      divu.w   d0,d1                               ;
30$      move.w   d1,_serper                          ; set period value
40$      rts
         ;
         ;        Allocate new internal buffer
         ;
allocBuf:
         DEBUG1   "AllocBuf %ld",d0
         cmp.l    vb_IntRBufLen(a5),d0                ; len same as previous?
         beq.b    10$                                 ; yep, so no need to alloc
         move.l   d0,d1                               ; save length
         movem.l  d1/a1,-(sp)                         ; save registers
         moveq    #MEMF_PUBLIC,d1                     ; public memory
         jsr      _LVOAllocMem(a6)                    ; go allocate it
         movem.l  (sp)+,d1/a1                         ; restore registers
         tst.l    d0                                  ; did we get it?
         beq.b    20$                                 ; if zero, error
         bsr.b    freeBuf                             ; go free previous buffer
         move.l   d0,vb_IntRBuf(a5)                   ; store new ptr
         move.l   d1,vb_IntRBufLen(a5)                ; and length
         bsr      cmd_Clear                           ; go setup buffer
10$      moveq    #0,d0                               ; success
         rts                                          ; return
20$      moveq    #SerErr_BufErr,d0                   ; set error status
         rts                                          ; return
         ;
         ;        Free internal buffer
         ;
freeBuf:
         movem.l  d0-d1/a0-a1,-(sp)                   ; save registers
         move.l   vb_IntRBuf(a5),d0                   ; is one there?
         beq.b    10$                                 ; no so branch
         movea.l  d0,a1                               ; get ptr
         move.l   vb_IntRBufLen(a5),d0                ; get length
         clr.l    vb_IntRBuf(a5)                      ; clear
         clr.l    vb_IntRBufLen(a5)                   ; clear
         jsr      _LVOFreeMem(a6)                     ; free it
10$      movem.l  (sp)+,d0-d1/a0-a1                   ; restore registers
         rts                                          ; return
         ;
         ;        Checks CTS status and if clear generates a TBE interrupt or
         ;        requeues the timer request.
         ;
         ;        Entered from Exec using the MsgPort callback.
         ;
         ;        Input:   a6 = ExecBase
         ;        Output:  none
         ;
         ;        No need to preserve d0/d1/a0/a1
         ;
timerRtn:
         lea.l    timerReq(pc),a1                     ; get ptr to timer request
         jsr      _LVORemove(a6)                      ; remove request
         ;
         ;        If we were breaking, reset adkcon.
         ;
         btst.b   #ADKB_UARTBRK,_adkconr              ; were we breaking?
         beq.b    10$                                 ; nope, skip reset
         ;
         move.w   #ADKF_UARTBRK,_adkcon               ; stop breaking
         ;
         ;
         ;
10$      move.l   Handshake(pc),d0                    ; are we handshaking?
         beq.b    20$                                 ; nope, generate interrupt
         btst.b   #CIAB_COMCTS,_ciabpra               ; clear to send?
         beq.b    20$                                 ; yep, go start writing
         ;
         lea.l    timerReq(pc),a1                     ; get ptr to timer request
         move.l   #1000,IOTV_TIME+TV_MICRO(a1)        ; wait for .001 seconds
         jsr      _LVOSendIO(a6)                      ; go queue it
         bra.b    30$                                 ; branch to return
         ;
         ;        CTS is clear so generate TBE interrupt to restart writing
         ;
20$      move.w   #INTF_SETCLR|INTF_TBE,_intreq       ; set TBE interrupt
30$      rts                                          ; return
         ;
         ;        Non serial interrupt
         ;
level1n:
         movem.l  d0-d1/a0-a1/a5-a6,-(sp)             ; save registers
         lea      _custom,a0                          ; get ptr to custom regs
         move.w   intenar(a0),d1                      ; get enabled interrupts
         btst.w   #INTB_INTEN,d1                      ; interrupts enabled?
         beq.b    20$                                 ; nope, just exit
         and.w    intreqr(a0),d1                      ; and in requested interrupts
         movea.l  SysBase(pc),a6                      ; get ExecBase
         ;
         btst.w   #INTB_DSKBLK,d1                     ; Disk block done?
         beq.b    10$                                 ; nope, branch
         ;
         movem.l  IVDSKBLK(a6),a1/a5                  ; get data and code ptrs
         pea.l    20$(pc)                             ; push return address
         jmp      (a5)                                ; jump to routine
         ;
10$      btst.w   #INTB_SOFTINT,d1                    ; software interrupt?
         beq.b    20$                                 ; nope, branch
         ;
         movem.l  IVSOFTINT(a6),a1/a5                 ; get data and code ptrs
         pea.l    20$(pc)                             ; push return address
         jmp      (a5)                                ; jump to routine
         ;
20$      movem.l  (sp)+,d0-d1/a0-a1/a5-a6             ; restore registers
         rte                                          ; return
         ;
         ;        Level 1 interrupt handler
         ;
level1:
         btst.b   #INTB_TBE,_intreqr+1                ; xmit buffer empty?
         beq.b    level1n                             ; nope, invoke old handler
         ;
         ;        Handle "Transmit Buffer Empty" interrupt (write)
         ;
         move.w   #INTF_TBE,_intreq                   ; clear interrupt
         move.l   d0,-(sp)                            ; save D0 (faster than MOVEM)
         move.l   a0,-(sp)                            ; save A0 (faster than MOVEM)
         ;
         ;        If we're not handshaking, bypass it.
         ;
10$      move.b   Handshake(pc),d0                    ; are we handshaking?
         beq.b    20$                                 ; nope, skip CTS test
         btst.b   #CIAB_COMCTS,_ciabpra               ; clear to send?
         bne.b    40$                                 ; nope, branch
         ;
         ;        If cw_Length goes negative here, we are either done with a
         ;        request or we were called as a result of a fake interrupt
         ;        to force us to get the next request going.
         ;
20$      subq.l   #1,cw_Length                        ; decr write length
         blt.b    60$                                 ; < zero, done, branch
         ;
         ;        Currently processing a request.
         ;
         movea.l  cw_Buffer(pc),a0                    ; get buffer ptr
         move.w   #256,d0                             ; set stop bit
         move.b   (a0)+,d0                            ; get next byte
         move.l   a0,cw_Buffer                        ; store buffer ptr
         move.w   d0,_serdat                          ; store in serdat reg
         ;
30$      movea.l  (sp)+,a0                            ; restore registers
         move.l   (sp)+,d0                            ; restore registers
         rte                                          ; return
         ;
         ;        Queue a timer request to recheck CTS status
         ;
40$      movem.l  d1/a1/a6,-(sp)                      ; save registers
         lea.l    timerReq(pc),a1                     ; get ptr to timer request
         move.l   #1000,IOTV_TIME+TV_MICRO(a1)        ; wait for .001 seconds
         movea.l  SysBase(pc),a6                      ; get ExecBase
         jsr      _LVOSendIO(a6)                      ; queue the request
         movem.l  (sp)+,d1/a1/a6                      ; restore registers
         bra.b    30$                                 ; go return
         ;
         ;        There aren't anymore requests, so clear and exit
         ;
50$      clr.l    cw_Length-Start(a6)                 ; clear length
         clr.l    cw_IOReq-Start(a6)                  ; clear
         movem.l  (sp)+,d1/a1/a6                      ; restore registers
         bra.b    30$                                 ; go return
         ;
         ;        Write request completed
         ;
60$
         movem.l  d1/a1/a6,-(sp)                      ; save registers
         ;
         move.l   cw_IOReq(pc),d0                     ; active I/O request?
         beq.b    70$                                 ; nope, branch
         ;
         ;        Reply it and setup for next
         ;
         movea.l  d0,a1                               ; get I/O request
         bclr.b   #IOSERB_ACTIVE,IO_FLAGS(a1)         ; no longer active
         clr.b    IO_ERROR(a1)                        ; no error
         movea.l  SysBase(pc),a6                      ; get ExecBase
         jsr      _LVOReplyMsg(a6)                    ; return I/O request
         ;
70$      lea.l    Start(pc),a6                        ; get section base
         ;
         lea.l    writeQ(pc),a1                       ; get ptr to write queue
         move.l   (a1),a0                             ; get head of list
         move.l   (a0),d0                             ; get successor
         beq      50$                                 ; end of list? yep, branch
         ;
         ;        Remove the node from the list
         ;
         move.l   d0,(a1)                             ; make new head
         exg.l    d0,a0                               ; swap nodes
         move.l   a1,LN_PRED(a0)                      ; store predecessor
         ;
         move.l   d0,a1                               ; get I/O request
         ;
         ;        If it's a BREAK, then branch to process as such.
         ;
         cmpi.w   #SDCMD_BREAK,IO_COMMAND(a1)         ; BREAK command?
         beq.b    100$                                ; yep, branch
         ;
         ;        Check for absolute length
         ;
         move.l   IO_DATA(a1),a0                      ; get data ptr
         moveq    #-1,d1                              ; get value
         move.l   IO_LENGTH(a1),d0                    ; get length
         cmp.l    d1,d0                               ; length = -1?
         bne.b    90$                                 ; yep, go scan
         ;
         ;        Scan the data for null to calc the length
         ;
         move.l   a0,d1                               ; save data ptr
80$      tst.b    (a0)+                               ; does it equal 0?
         bne.b    80$                                 ; nope, so continue
         suba.l   d1,a0                               ; calc # of bytes+1
         move.l   a0,d0                               ; xfer
         subq.l   #1,d0                               ; cal # of bytes
         movea.l  d1,a0                               ; get data ptr
         ;
90$      move.l   d0,IO_ACTUAL(a1)                    ; go ahead and set it
         bclr.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; no longer queued
         bset.b   #IOSERB_ACTIVE,IO_FLAGS(a1)         ; make it active
         move.l   d0,cw_Length-Start(a6)              ; store length
         move.l   a0,cw_Buffer-Start(a6)              ; store buffer ptr
         move.l   a1,cw_IOReq-Start(a6)               ; store I/O request ptr
         ;
         movem.l  (sp)+,d1/a1/a6                      ; restore registers
         bra      10$                                 ; go start request
         ;
         ;        Start the BREAK.
         ;
100$     bclr.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; no longer queued
         bset.b   #IOSERB_ACTIVE,IO_FLAGS(a1)         ; make it active
         clr.l    cw_Length-Start(a6)                 ; clear length
         move.l   a1,cw_IOReq-Start(a6)               ; store I/O request ptr
         move.w   #ADKF_SETCLR|ADKF_UARTBRK,_adkcon   ; start break
         move.l   IO_BRKTIME(a1),d0                   ; get break time
         lea.l    timerReq(pc),a1                     ; get ptr to timer request
         move.l   d0,IOTV_TIME+TV_MICRO(a1)           ; set the timeout
         movea.l  SysBase(pc),a6                      ; get ExecBase
         jsr      _LVOSendIO(a6)                      ; queue the request
         movem.l  (sp)+,d1/a1/a6                      ; restore registers
         bra      30$                                 ; go exit
         ;
         ;        Non serial interrupt (xfers control to old handler)
         ;
level5n:
         movem.l  d0-d1/a0-a1/a5-a6,-(sp)             ; save registers
         lea      _custom,a0                          ; get ptr to custom regs
         move.w   intenar(a0),d1                      ; get enabled interrupts
         btst.w   #INTB_INTEN,d1                      ; interrupts enabled?
         beq.b    10$                                 ; nope, just exit
         and.w    intreqr(a0),d1                      ; and in requested interrupts
         movea.l  SysBase(pc),a6                      ; get ExecBase
         btst.w   #INTB_DSKSYNC,d1                    ; Disk synchronized?
         beq.b    10$                                 ; nope, branch
         movem.l  IVDSKSYNC(A6),a1/a5                 ; get data and code ptrs
         jsr      (a5)                                ; branch to routine
10$      movem.l  (sp)+,d0-d1/a0-a1/a5-a6             ; restore registers
         rte                                          ; return
         ;
         ;        Default Level 5 handler (no I/O requests active)
         ;
level5:
         btst.b   #INTB_RBF,_intreqr                  ; receive buffer full?
         beq.b    level5n                             ; nope, invoke old handler
         ;
         move.l   a0,-(sp)                            ; save registers
         ;
10$      btst.b   #7,_serdatr                         ; Overrun?
         bne.b    50$                                 ; yep, branch
         ;
20$      movea.l  i_BufIn(pc),a0                      ; get current ptr
         move.b   _serdatr+1,(a0)+                    ; store received byte
         move.w   #INTF_RBF,_intreq                   ; clear RBF interrupt
         addq.l   #1,i_InCnt                          ; incr bytes in buffer
         ;
         cmpa.l   i_BufEnd(pc),a0                     ; hit end of buffer?
         beq.b    60$                                 ; yep, branch
30$      move.l   a0,i_BufIn                          ; store input ptr
         ;
         subq.l   #1,i_Thresh                         ; close to full buffer?
         beq.b    70$                                 ; yep, branch
         ;
40$      btst.b   #INTB_RBF,_intreqr                  ; receive buffer full?
         bne.b    10$                                 ; yep, go get another byte
         ;
         movea.l  (sp)+,a0                            ; restore registers
         rte                                          ; return
         ;
         ;        We've missed some data, so set overrun flag.
         ;
50$      addq.b   #1,Overrun                          ; set overrun flag
         bra      20$                                 ; continue
         ;
         ;        Hit physical end of buffer, so wrap to the start of the buffer.
         ;
60$      movea.l  i_BufPtr(pc),a0                     ; get buffer ptr
         bra.b    30$                                 ; continue
         ;
         ;        Hit buffer threshold, so tell other end not to send any more
         ;        data.
         ;
70$      tst.b    Handshake                           ; are we handshaking?
         beq.b    40$                                 ; nope, skip RTS
         bset.b   #CIAB_COMRTS,_ciabpra               ; block further input
         bra.b    40$                                 ; go return
         ;
         ;
         ;
VBIntRtn:
         ;
         ;        If there's nothing in the buffer, there's no point in going
         ;        any further.
         ;
         move.l   i_InCnt(pc),d0                      ; anything in the buffer?
         bne.b    20$                                 ; yep, branch
10$      lea.l    _custom,a0                          ; restore chip ptr
         moveq    #0,d0                               ; set Z flag
         rts                                          ; return
         ;
         ;        If we have been "disabled" then get out.  XXX FIX XXX
         ;
20$      move.b   disableRead(pc),d0                  ; internally disabled?
         bne.b    10$                                 ; yep, get out of here
         lea.l    Start(pc),a5                        ; get base
         ;
         ;        If we have an active request, branch down to try and fulfill it.
         ;
         move.l   cr_IOReq(pc),d0                     ; get and test active I/O
         bne.b    30$                                 ; nzero, active, branch
         ;
         ;        Get first node in list and test if empty.
         ;
         move.l   (a1),a0                             ; get head of list
         move.l   (a0),d0                             ; get successor
         beq.b    10$                                 ; end of list? yep, branch
         ;
         ;        Remove the node from the list
         ;
         move.l   d0,(a1)                             ; make new head
         exg.l    d0,a0                               ; swap nodes
         move.l   a1,LN_PRED(a0)                      ; store predecessor
         ;
         ;        Setup fields for processing a read request
         ;
         movea.l  d0,a1                               ; get I/O request
         bclr.b   #IOSERB_QUEUED,IO_FLAGS(a1)         ; no longer queued
         bset.b   #IOSERB_ACTIVE,IO_FLAGS(a1)         ; make it active
         move.l   a1,cr_IOReq-Start(a5)               ; store I/O request
         move.l   IO_DATA(a1),cr_OutPtr-Start(a5)     ; get/set output ptr
         move.l   IO_LENGTH(a1),cr_Length-Start(a5)   ; get/set output count
         ;
         ;        Process an active I/O request
         ;
30$      movea.l  SysBase(pc),a6                      ; get ExecBase
         move.l   d0,a1                               ; get I/O request
         bsr      copyData                            ; go copy 'em
         tst.l    d0                                  ; done with request?
         beq.b    10$                                 ; nope, branch
         ;
         ;        the request has been satisfied, so return it.
         ;
         jsr      _LVOReplyMsg(a6)                    ; return I/O
         bra.b    10$
         ;
         ;        Registers:
         ;        Entry:   A1       Ptr to I/O Request
         ;                 A6       ExecBase
         ;        Exit:    D0       ZERO - request not done, do not reply it
         ;                          NZERO - request done, reply it
         ;
copyData:
         ;
         ;        Process an active I/O request (or fall through from above)
         ;
         movem.l  d2-d5/a1-a5,-(sp)                   ; save registers
         lea.l    Start(pc),a5                        ; get base
         movea.l  a1,a4                               ; get I/O request
         ;
         movea.l  i_BufOut(pc),a2                     ; get current bufout ptr
         movea.l  cr_OutPtr(pc),a3                    ; get current output ptr
         move.l   cr_Length(pc),d2                    ; get current bytes needed
         move.l   i_InCnt(pc),d3                      ; get current bytes in buffer
         ;
         ;        If we don't have enough bytes to satisfy the request,
         ;        set the length to the number of bytes we do have.
         ;
         cmp.l    d2,d3                               ; enuf to satisfy request?
         bge.b    15$                                 ; yep, so branch
         move.l   d3,d2                               ; # to copy = # in buffer
         bra.b    15$                                 ; branch to loop entry
         ;
         ;        Start of copy loop.
         ;
10$      movea.l  i_BufPtr(pc),a2                     ; reset bufout to start
         ;
         ;        Entry point of copy loop.
         ;
15$      move.l   d2,d3                               ; xfer # of bytes to copy
         ;
         ;        If the copy will extend past the end of the buffer, we can
         ;        only copy the number of bytes to the end this go around.
         ;
         move.l   i_BufEnd(pc),d0                     ; get ptr to end of buf
         sub.l    a2,d0                               ; calc # of bytes to end
         cmp.l    d0,d3                               ; # to copy < # to end?
         blt.b    20$                                 ; yep, branch
         move.l   d0,d3                               ; get bytes to end
         ;
         ;        Registers:
         ;
         ;        A2 = pointer from which data will be copied
         ;        A3 = pointer to which data will be copied
         ;        D2 = number of bytes that need to be copied
         ;        D3 = number of bytes to copy this iteration
         ;
20$      btst.b   #SERB_EOFMODE,IO_SERFLAGS(a4)       ; EOFMODE requested?
         beq.b    30$                                 ; nope, just go copy
         ;
         ;        EOFMODE was specified so copy characters 1 at a time until
         ;        we hit an EOF character, the output butter has filled, or
         ;        the input buffer has drained.
         ;
         move.l   d3,d0                               ; get length
         ;
21$      move.b   (a2)+,d1                            ; get byte
         move.b   d1,(a3)+                            ; put in output buffer
         ;
         lea.l    IO_TERMARRAY(a4),a1                 ; get termarry ptr
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bge.b    22$                                 ; possibly, branch
         cmp.b    (a1)+,d1                            ; found term char?
         bgt.b    23$                                 ; nope, branch
22$      beq.b    24$                                 ; term char found?
         ;
         ;        Didn't find a term character, so continue with the copy loop
         ;
23$      subq.l   #1,d0                               ; decr length counter
         bne.b    21$                                 ; continue if more
         bra.b    40$                                 ; done with copy, branch
         ;
         ;        We've found a termination character.
         ;
24$      clr.l    cr_Length-Start(a5)                 ; done with request
         bra.b    50$                                 ; branch
         ;
         ;        EOFMODE not specified, so just do a bulk copy.
         ;
         ;        XXX POSSIBLE SPEEDUP XXX
         ;
         ;        For short copies it would be quicker to have a simple inline
         ;        loop, but what's short???  It would be different by CPU.
         ;
30$      move.l   d3,d0                               ; get length
         movea.l  a3,a1                               ; where to put it
         movea.l  a2,a0                               ; where to get it
         jsr      _LVOCopyMem(a6)                     ; copy it
         adda.l   d3,a2                               ; update bufout
         adda.l   d3,a3                               ; update outptr
         ;
         ;        Fall through and entered from EOFMODE loop
         ;
         ;        If the following calculation results in a value greater than
         ;        zero, then we have a buffer wrap and need to process the
         ;        remaining bytes at the beginning of the buffer.
         ;
40$      sub.l    d3,d2                               ; calc bytes left to copy
         bgt      10$                                 ; >0, more to copy, branch
         ;
         ;
         ;
50$      move.l   a3,d1                               ; get outptr
         sub.l    cr_OutPtr(pc),d1                    ; calc length
         move.l   a3,cr_OutPtr-Start(a5)              ; update outptr
         ;
         ;        Update output ptr and bytes left in buffer
         ;
         move.l   a2,i_BufOut-Start(a5)               ; store bufout ptr
         sub.l    d1,i_InCnt-Start(a5)                ; calc bytes left in buffer
         add.l    d1,IO_ACTUAL(a4)                    ; update I/O request
         ;
         ;        If the threshold becomes positive here, then, if requested,
         ;        tell the other end that it's okay to start sending more data.
         ;
         add.l    d1,i_Thresh-Start(a5)               ; calc thresh and test
         ble.b    60$                                 ; <= 0, need more data, skip
         tst.b    Handshake-Start(a5)                 ; are we handshaking?
         beq.b    60$                                 ; nope, skip RTS
         btst.b   #CIAB_COMRTS,_ciabpra               ; currently stopped?
         beq.b    60$                                 ; nope, skip clear
         bclr.b   #CIAB_COMRTS,_ciabpra               ; ready to receive more data
         ;
         ;        Set error if we've had any overruns.
         ;
60$      tst.b    Overrun-Start(a5)                   ; did overrun occur?
         beq.b    70$                                 ; nope, branch
         clr.b    Overrun-Start(a5)                   ; reset overrun flag
         move.b   #SerErr_LineErr,IO_ERROR(a4)        ; set error code
         ;
         ;        Calc number of bytes left to copy.  If the result is greater
         ;        than zero, then we have more to copy so return a "noreply"
         ;        status.
         ;
70$      moveq    #0,d0                               ; assume noreply
         sub.l    d1,cr_Length-Start(a5)              ; update length and test
         bgt.b    90$                                 ; >0, more to do, branch
         ;
         ;        We've completed the I/O request either by copying the requested
         ;        of bytes or by finding an EOFMODE character, so return a "reply"
         ;        status.
         ;
         moveq    #1,d0                               ; indicate reply
         clr.l    cr_IOReq-Start(a5)                  ; clear request
         ;
         ;        Restore and exit
         ;
90$      movem.l  (sp)+,d2-d5/a1-a5                   ; save registers
         rts                                          ; return (Reply status in D0)
         ;
         ;        Align data
         ;
         DS.L     0
         ;
         ;
         ;
Init:
         DC.L     sizeof_8n1Base
         DC.L     funcTab
         DC.L     dataTab
         DC.L     InitRoutine
         ;
         ;
         ;
funcTab:
         DC.W     -1
         DC.W     dev_Open-funcTab
         DC.W     dev_Close-funcTab
         DC.W     dev_Expunge-funcTab
         DC.W     dev_Null-funcTab
         DC.W     dev_BeginIO-funcTab
         DC.W     dev_AbortIO-funcTab
         DC.W     -1
         ;
         ;
         ;
dataTab:
         INITBYTE LN_TYPE,NT_DEVICE
         INITLONG LN_NAME,Name
         INITBYTE LIB_FLAGS,LIBF_SUMUSED|LIBF_CHANGED
         INITWORD LIB_VERSION,VERSION
         INITWORD LIB_REVISION,REVISION
         INITLONG LIB_IDSTRING,IdString
         DC.W     0
         ;
         ;        String Constants
         ;
miscresource:
         DC.B     "misc.resource",0
timerdevice:
         DC.B     "timer.device",0
intuitlib:
         DC.B     "intuition.library",0
Name:
         DC.B     "8n1.device",0
IdString:
         VSTRING
         ;
         ;        End of checksummed area.  (Realigns data too!)
         ;
ENDTag:
         DS.L     0
         ;
         ;        Global SysBase (Use instead of AbsExecBase for speed)
         ;
SysBase:
         DC.L     0
         ;
         ;        Internal buffer tracking (DO NOT CHANGE THE ORDER!!!!)
         ;
i_BufPtr:
         DC.L     0
i_BufIn:
         DC.L     0
i_BufOut:
         DC.L     0
i_BufEnd:
         DC.L     0
i_InCnt:
         DC.L     0
i_Thresh:
         DC.L     0
         ;
         ;        Used while processing a read request.
         ;
cr_IOReq:
         DC.L     0
cr_OutPtr:
         DC.L     0
cr_Length:
         DC.L     0
         ;
         ;        List head for read requests
         ;
readQ:
         DC.L     readQ+MLH_TAIL
         DC.L     0
         DC.L     readQ
         ;
         ;        Write control.
         ;
cw_Length:
         DC.L     0
cw_Buffer:
         DC.L     0
cw_IOReq:
         DC.L     0
         ;
         ;        List head for write requests
         ;
writeQ:
         DC.L     writeQ+MLH_TAIL
         DC.L     0
         DC.L     writeQ
         ;
         ;
         ;
timerPort:
         DC.L     0                                   ; LN_SUCC
         DC.L     0                                   ; LN_PRED
         DC.B     NT_MSGPORT                          ; LN_TYPE
         DC.B     0                                   ; LN_PRI
         DC.L     0                                   ; LN_NAME
         DC.B     3                                   ; MP_FLAGS (undoc'ed)
         DC.B     0                                   ; MP_SIGBIT
         DC.L     timerRtn                            ; MP_SIGTASK
         DC.L     timerPort+MP_MSGLIST+LH_TAIL        ; LH_HEAD
         DC.L     0                                   ; LH_TAIL
         DC.L     timerPort+MP_MSGLIST                ; LH_TAILPRED
         DC.B     0                                   ; LH_TYPE
         DC.B     0                                   ; LH_pad
         DC.W     0                                   ; long align
         ;
         ;
         ;
timerReq:
         DC.L     0                                   ; LN_SUCC
         DC.L     0                                   ; LN_PRED
         DC.B     NT_MESSAGE                          ; LN_TYPE
         DC.B     0                                   ; LN_PRI
         DC.L     0                                   ; LN_NAME
         DC.L     timerPort                           ; MN_REPLYPORT
         DC.W     IOTV_SIZE                           ; MN_LENGTH
         DC.L     0                                   ; IO_DEVICE
         DC.L     0                                   ; IO_UNIT
         DC.W     TR_ADDREQUEST                       ; IO_COMMAND
         DC.B     0                                   ; IO_FLAGS
         DC.B     0                                   ; IO_ERROR
         DC.L     0                                   ; TV_SECS
         DC.L     0                                   ; TV_MICROS
         ;
         ;
         ;
VBInterrupt:
         DC.L     0                                   ; LN_SUCC
         DC.L     0                                   ; LN_PRED
         DC.B     NT_INTERRUPT                        ; LN_TYPE
         DC.B     127                                 ; LN_PRI
         DC.L     Name                                ; LN_NAME
         DC.L     readQ                               ; IS_DATA
         DC.L     VBIntRtn                            ; IS_CODE
         ;
         ;        Global flags
         ;
Overrun:
         DC.B     0
Handshake:
         DC.B     0
disableRead:
         DC.B     0
         ;
         ;
         ;
         END

