home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-06-08 | 64.8 KB | 2,665 lines |
- *****************************************************************************
- * Program: newser.asm - Copyright ©1990,91 by Dan Babcock
- * Function: A "serial.device" compatible driver for the Rockwell 65C52
- * based on the 1.3 RKM driver example.
- *
- * Author: Dan Babcock
- * History: 08/12/90 V0.50 Created
- * 09/06/90 V0.51 Cleaned up "eofdump" code.
- * 11/04/90 V0.52 Misc. minor changes
- * 11/25/90 V0.53 Changed path and name of include file
- * 03/25/91 V1.10 I (Mike Mossman) added code to support the
- * ASDG SetCtrlLines command.
- *
- * [To all: Please don't forget to bump the revision numbers
- * if you do *any* modifications at all. -Jeff]
- *
- * This code is designed to be ROMable (but not position independent).
- *
- * Feel free to contact me:
- *
- * [School address] [Permanent address] People/Link:
- * Dan Babcock Dan Babcock DANBABCOCK
- * Atherton Hall Room 63 P.O. Box 1532
- * University Park, PA 16802 Southgate, MI 48195
- * Voice: (814)-862-2931
- *
- * I am also reachable via internet. I read comp.sys.amiga.tech.
- *
- * General notes
- * =============
- * The macro "PUTDEBUG" is used to output debugging information via the
- * internal serial port at a low level. It may be used anywhere, including
- * critical sections, supervisor mode, and interrupt code. If debugging is
- * not desired, set the INFO_LEVEL equate to zero. Conversely, if debugging
- * is desired, set it to a high value (e.g. 100000)
- *
- * A consistent (for the most part) usage of registers was employed:
- * A1 - pointer to an IORequest structure
- * A3 - pointer to a unit structure (defined below)
- * A4 - pointer to a serprefs structure (defined below)
- * A5 - pointer to the hardware (one channel, that is)
- * A6 - pointer to the device structure (defined below)
- *
- * Known difference(s) between this and serial.device are:
- * 1. Start/Stop do not send XON/XOFF characters to the outside world
- * as implied in the autodoc (I don't think this is important).
- * 2. Reading is never performed in the caller's context.
- * 3. Newser does not currently send an XOFF or otherwise tell the outside
- * world to "shut up" when the driver's input buffer fills; it simply
- * reports a buffer overflow condition and continues as normal.
- *
- * Possible future enhancements:
- * 1. Read the default prefs from memory (set up previously) or a file
- * 2. Check to see whether units 3 & 4, if requested, actually exist
- *
- * Suggestions for additional enhancements are welcome.
- *****************************************************************************
-
-
- VERSION EQU 1
- REVISION EQU 1
- INFO_LEVEL EQU 0
-
-
- ;This define is used to tell which commands should be handled
- ;immediately (on the caller's schedule).
- ;
- ;The immediate commands are Invalid, Reset, Stop, Start, Flush, Clear,
- ;Query, and SetParams
- ;
- ;Note that this method limits you to just 32 device specific commands,
- ;which may not be enough.
-
- IMMEDIATES EQU %00000000000000010000101111110011
- ; --------========--------========
- ; FEDCBA9876543210FEDCBA9876543210
-
-
- INCDIR "include:"
-
- INCLUDE "exec/types.i"
- INCLUDE "exec/funcdef.i"
- INCLUDE "exec/exec.i"
- INCLUDE "exec/exec_lib.i"
- INCLUDE "exec/ables.i"
- INCLUDE "exec/errors.i"
- INCLUDE "exec/interrupts.i"
- INCLUDE "exec/libraries.i"
- INCLUDE "exec/lists.i"
- INCLUDE "exec/initializers.i"
- INCLUDE "exec/resident.i"
- INCLUDE "devices/serial.i"
- INCLUDE "devices/timer.i"
- INCLUDE "hardware/custom.i"
- INCLUDE "hardware/intbits.i"
- INCLUDE "libraries/dos.i"
- INCLUDE "libraries/dosextens.i"
- INCLUDE "ioexp.i"
-
-
- ; Things we define for external reference (for easier debuggging)
- XDEF LibStart
- XDEF RomTag
- XDEF LibName
- XDEF LibIDString
- XDEF LibInit
- XDEF LibOpen
- XDEF LibClose
- XDEF LibExpunge
- XDEF LibExtFunc
- XDEF LibBeginIO
- XDEF LibAbortIO
- XDEF LibEnd
- XDEF inittable
- XDEF functable
- XDEF datatable
- XDEF defaultprefs
- XDEF basetable
- XDEF baudtable
- XDEF cmdtable
- XDEF mdu_Init
- XDEF LibEnd
- XDEF LibStart
- XDEF mydebug
- XDEF InitTask
- XDEF InitDACIA
- XDEF SetUpUnit
- XDEF InitUnit
- XDEF LibBeginIO
- XDEF BeginIO_QueueMsg
- XDEF BeginIO_Immediate
- XDEF PerformIO
- XDEF BeginIO_End
- XDEF BeginIO_NoCmd
- XDEF queuewrt
- XDEF notread
- XDEF ReadTask_Begin
- XDEF ReadSigLoop
- XDEF ReadTask_MainLoop
- XDEF ReadTask_StartHere
- XDEF ReadTask_NextMessage
- XDEF Readignorecmd
- XDEF Readconttl
-
- SECTION CODE
-
- ; Supposed start of executable code. This is where execution
- ; will start if anybody's silly enough to try and run the library
- ; from the command line. Just return the highest error code we
- ; conveniently can to tell them they screwed up.
- LibStart:
- moveq #$7F,d0
- rts
-
- ; Where the magic begins. OpenLibrary actually looks through the
- ; library file contents trying to find this table by locating the
- ; magic RTC_MATCHWORD value followed by its own address. This
- ; table then tells OpenLibrary where to find other things it needs.
- ; This needs to be close to the front of your library file to cut
- ; down on the amount of searching OpenLibrary does; that's why
- ; this object file should be first in the link list.
-
- RomTag: dc.w RTC_MATCHWORD ; Magic value to search for to find table
- dc.l RomTag ; Address of matchword; the two together prevent accidental matches
- dc.l LibEnd ; Address of end of library handler code
- dc.b RTF_AUTOINIT ; Request system to automatically initialize our library
- dc.b VERSION ; Version number of our library (defined below)
- dc.b NT_DEVICE ; Node type = Device
- dc.b 0 ; Node priority = 0 (normal)
- dc.l LibName ; Name of this library file, for debugging info
- dc.l LibIDString ; More debugging info
- dc.l inittable ; Initialization table used by RTF_AUTOINIT
-
- LibName:
- dc.b "newser.device",0
- Task1:
- dc.b "NewserRead",0
- Task2:
- dc.b "NewserWrite",0
- LibIDString:
- dc.b "NewSer 1.1S (21 Mar 1991)",13,10,0
- timername
- dc.b 'timer.device',0
-
- ds.w 0
-
- inittable:
- dc.l md_SIZE ; Size of our library base struct
- dc.l functable ; Where our function addresses are
- dc.l datatable ; Initialization info for our library base struct
- dc.l LibInit ; Library initialization routine address
-
- functable:
- dc.l LibOpen ; Addresses of all library functions
- dc.l LibClose ; First 4 MUST be provided, in this order
- dc.l LibExpunge
- dc.l LibExtFunc
- dc.l LibBeginIO ; The meat of the library.
- dc.l LibAbortIO
- dc.l -1
-
-
-
- ; Things for the system to automatically initialize for us via RTF_AUTOINIT request
- datatable:
- INITBYTE LN_TYPE,NT_DEVICE
- INITLONG LN_NAME,LibName
- INITBYTE LIB_FLAGS,LIBF_SUMUSED | LIBF_CHANGED
- INITWORD LIB_VERSION,VERSION
- INITWORD LIB_REVISION,REVISION
- INITLONG LIB_IDSTRING,LibIDString
- dc.l 0
-
-
- ;This routine gets called after the device has been allocated. The device
- ;pointer is in D0. The AmigaDOS segment list is in a0. If it returns the
- ;device pointer, then the device will be linked into the device list. If it
- ;returns NULL, then the device will be unloaded.
- ;
- ;This call is single-threaded by exec; please read the description for
- ;"Open" below.
- ;
- ; Register Usage
- ; ==============
- ; a3 -- Points to temporary RAM
- ; a4 -- Expansion library base
- ; a5 -- device pointer
- ; a6 -- Exec base
-
- LibInit:
- PUTDEBUG 100,<'Init: called'>
- movem.l d0-d7/a0-a6,-(sp) ;Preserve ALL modified registers
- movea.l d0,a5 ;Device ptr
- move.l a6,md_SysLib(a5) ;Save a pointer to exec
- move.l a0,md_SegList(a5) ;Save pointer to our loaded code
-
- ;Now fill in the prefs in MyDev with reasonable defaults. Jeff has suggested
- ;starting a process and reading them from a file.
-
- movea.l a5,a6
- bsr.b SetDefaultPrefs
- move.l a5,d0 ;a5 set to 0 above if an error occured
- PUTDEBUG 100,<'Init: finished'>
- movem.l (sp)+,d0-d7/a0-a6
- rts
-
- ;Enter with device ptr in a6.
-
- SetDefaultPrefs
- movem.l d0/d1/a0/a1,-(sp)
- lea prefs_unit0(a6),a1
- moveq #MD_NUMUNITS-1,d1
- 1$
- move.w #prefs_SIZE-1,d0
- lea defaultprefs(pc),a0
- 2$
- move.b (a0)+,(a1)+
- dbra d0,2$
- dbra d1,1$
- movem.l (sp)+,d0/d1/a0/a1
- rts
-
- ;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.
- ;
- ;Open sets the IO_ERROR field on an error. If it was successful, we should
- ;also set up the IO_UNIT and LN_TYPE fields. exec takes care of setting up
- ;IO_DEVICE.
- ;
- ;NOTE: We must also copy the current prefs for this unit into the user's
- ;extended iorequest fields.
- ;
- ; Register Usage
- ; ==============
- ; d0 -- unitnum
- ; d1 -- flags
- ; a1 -- iob
- ; a6 -- Device ptr
-
- LibOpen:
- addq.w #1,LIB_OPENCNT(a6) ;Fake an opener for duration of call
- PUTDEBUG 100,<'Open: called'>
- movem.l d2/a2-a4,-(sp)
- movea.l a1,a2 ;Save the iob
- cmp.l #MD_NUMUNITS,d0 ;See if the unit number is in range
- bcc.w Open_Range_Error ;Unit number out of range (BHS)
- move.l d0,d2 ;Save unit number
- lsl.l #2,d0 ;See if the unit is already initialized
- lea md_Units(a6,d0.l),a4
- move.l (a4),d0
- bne.b Open_UnitOK
- ;Try to conjure up a unit
- bsr.w InitUnit ;scratch:a3 unitnum:d2 devpoint:a6
- move.l (a4),d0 ;See if it initialized OK
- beq.w Open_Error
- movea.l d0,a3
- bra.b Open_UnitOK1
-
- Open_UnitOK
- movea.l d0,a3 ;Unit pointer in a3
- tst.b Exclusive(a3) ;Check for an exclusive access violation
- beq.w Open_Error
- Open_UnitOK1
- btst #SERB_SHARED,IO_FLAGS(a2)
- seq Exclusive(a3)
- movea.l mdu_prefs(a3),a4 ;Set the "7WIRE" flag
-
- bclr #SERB_7WIRE,prefs_SERFLAGS(a4)
- btst #SERB_7WIRE,IO_SERFLAGS(a2)
- beq.b 2$
- bset #SERB_7WIRE,prefs_SERFLAGS(a4)
-
- 2$
- movea.l a2,a1 ;Copy the current internal prefs into the iorequest.
- bsr CopyPrefs
- move.l d0,IO_UNIT(a2)
- addq.w #1,LIB_OPENCNT(a6) ;Mark us as having another opener
- addq.w #1,UNIT_OPENCNT(a3) ;Internal bookkeeping
- bclr #LIBB_DELEXP,md_Flags(a6) ;Prevent delayed expunges
- moveq #0,d0
- move.b d0,IO_ERROR(a2)
- move.b #NT_REPLYMSG,LN_TYPE(a2) ;Mark IORequest as "complete"
- Open_End
- subq.w #1,LIB_OPENCNT(a6) ;End of expunge protection
- movem.l (sp)+,d2/a2-a4
- PUTDEBUG 100,<'Open: Finished!'>
- rts
-
- Open_Range_Error:
- Open_Error
- moveq #IOERR_OPENFAIL,d0
- move.b d0,IO_ERROR(a2)
- move.l d0,IO_DEVICE(a2) ;Trash IO_DEVICE on open failure
- PUTDEBUG 100,<'Open: failed'>
- bra.b Open_End
-
- ;Copy prefs from our device struct to an IOrequest. Enter with IORequest
- ;in a1 and unit pointer in a3. All registers are preserved.
-
- CopyPrefs
- movem.l d0/a1/a4,-(sp)
- movea.l mdu_prefs(a3),a4
- lea IO_CTLCHAR(a1),a1
- move.w #prefs_SIZE-1,d0
- 1$
- move.b (a4)+,(a1)+
- dbra d0,1$
-
- movem.l (sp)+,d0/a1/a4
- rts
-
- ;Copy prefs from an IOrequest to our device struct Enter with IORequest
- ;in a1 and unit pointer in a3. All registers are preserved.
-
- SetPrefs
- movem.l d0/a1/a4,-(sp)
- movea.l mdu_prefs(a3),a4
- lea IO_CTLCHAR(a1),a1
- move.w #prefs_SIZE-1,d0
- 1$
- move.b (a1)+,(a4)+
- dbra d0,1$
-
- movem.l (sp)+,d0/a1/a4
- rts
-
- ;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.
- ; ( device:a6, iob:a1 )
-
- LibClose:
- movem.l a2-a3,-(sp)
- PUTDEBUG 100,<'Close: called'>
- movea.l a1,a2
- movea.l IO_UNIT(a2),a3
- moveq #-1,d0
- move.l d0,IO_UNIT(a2) ;We're closed...
- move.l d0,IO_DEVICE(a2) ;Customers not welcome at this IORequest!
- subq.w #1,UNIT_OPENCNT(a3) ;See if the unit is still in use
- bne.b Close_Device
- bsr ExpungeUnit
-
- Close_Device
- moveq #0,d0
- subq.w #1,LIB_OPENCNT(a6) ;Mark us as having one fewer openers
- bne.b Close_End ;See if there is anyone left with us open
- btst #LIBB_DELEXP,md_Flags(a6) ;See if we have a delayed expunge pending
- beq.b Close_End
- bsr LibExpunge ;Do the expunge
- Close_End
- movem.l (sp)+,a2-a3
- PUTDEBUG 100,<'Close: Finished!'>
- rts ;MUST return either zero or the SegList!
-
- ;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.
- ;
- ; A6 - library base (scratch)
- ; D0-D1/A0-A1 - scratch
-
- LibExpunge:
- PUTDEBUG 100,<'Expunge: called'>
- movem.l d1/d2/a5/a6,-(sp) ;Save ALL modified registers
- movea.l a6,a5
- movea.l md_SysLib(a5),a6
- tst.w LIB_OPENCNT(a5) ;See if anyone has us open
- beq.b 1$
- bset #LIBB_DELEXP,md_Flags(a5) ;Set the delayed expunge flag
- moveq #0,d0
- bra.b Expunge_End
-
- 1$
- move.l md_SegList(a5),d2 ;Store our seglist in d2
- movea.l a5,a1 ;Unlink from device list
- mySYS Remove ;Remove first (before FreeMem)
- movea.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
- mySYS FreeMem
- move.l d2,d0 ;Set up our return value
-
- Expunge_End
- movem.l (sp)+,d1/d2/a5/a6
- rts
-
- LibExtFunc:
- PUTDEBUG 100,<'Null: called'>
- moveq #0,d0
- rts ;The "Null" function MUST return NULL.
-
-
- ;This is the main unit initialization routine. It allocates memory for
- ;the device structure, calls SetUpUnit, then calls InitTask.
-
- ; ( d2:unit number, a3:scratch, a6:devptr )
-
- InitUnit
- PUTDEBUG 100,<'InitUnit: called'>
- movem.l d0/d1/a4/a2,-(sp)
- move.l d2,d1
- mulu.w #prefs_SIZE,d1
- lea prefs_unit0(a6),a4
- adda.l d1,a4
-
- ;Now A4 is a ptr to the prefs for this unit
-
- move.l #mdu_SIZE,d0 ;Allocate unit memory
- move.l #MEMF_PUBLIC!MEMF_CLEAR,d1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS AllocMem
- movea.l (sp)+,a6
- movea.l d0,a3
- tst.l d0
- bne continitunit1
-
- ;Couldn't init the unit (out of memory).
-
- ReturnVoid
- move.l d2,d0 ;Unit number
- lsl.l #2,d0
- clr.l md_Units(a6,d0.l) ;Set unit table
- movem.l (sp)+,d0/d1/a4/a2
- rts
-
- BadSetup
- bsr KillTask
- bra.b ReturnVoid
-
- continitunit1
- moveq #0,d0 ;Don't need to re-zero it
- movea.l a3,a2
- lea mdu_Init(pc),a1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS InitStruct
- movea.l (sp)+,a6
-
- move.l a5,-(sp) ;Look up DACIA base address
- lea basetable(pc),a0
- moveq #0,d0
- move.b d2,d0
- lsl.l #2,d0
- movea.l 0(a0,d0.w),a5
- move.l a5,daciabase(a3)
-
- move.b #$7f,IER(a5) ;Disable DACIA interrupts
-
- bsr SetUpUnit
- movea.l (sp)+,a5
- tst.l d0
- bne.b BadSetup
-
- bsr InitTask ;Set up the two tasks for this unit
- tst.l d0
- beq.b BadSetup
-
- ;Done with InitUnit - fill in the proper md_Unit field in the device struct
- ;and return.
- move.l d2,d0 ;Unit number
- lsl.l #2,d0
- move.l a3,md_Units(a6,d0.l) ;Set unit table
- movem.l (sp)+,d0/d1/a4/a2
- PUTDEBUG 100,<'InitUnit: finished! End of silence.'>
- rts
-
- ;This routine does the following:
- ;
- ;1. Allocate read buffer memory
- ;2. Initialize all the special MyDevUnit fields (but NOT the signals -
- ; they must be allocated in the task's context)
- ;3. Call InitDACIA [Initialize/set-up DACIA registers (for this unit).]
- ;4. Install an interrupt server.
- ;
- ;Returns failure code in d0 - zero if OK, -1 if out of memory, 1 if
- ;unable to open timer.device, 2 if invalid parm
- ;
- ;SetUpUnit(d2:unit number, a3:unit, a6:devptr)
- ;AND...ptr to prefs in A4
- ;uses d1,a0,a1,a2,a6
-
- SetUpUnit
- PUTDEBUG 100,<'SetUpUnit: called'>
- movem.l d1/a0/a1/a2,-(sp)
- move.l prefs_RBUFLEN(a4),d0
- moveq #64,d1
- cmp.l d1,d0
- bhi.b SUU
- move.l d1,d0
- move.l d0,prefs_RBUFLEN(a4)
- SUU
- move.l #MEMF_PUBLIC,d1
- myEXEC AllocMem
- tst.l d0
- bne.b continitunit
- moveq #-1,d0 ;Failed to allocate input buffer memory - exit.
- movem.l (sp)+,d1/a0/a1/a2
- rts
-
- ;Initialize all the special MyDevUnit fields (but NOT the signals -
- ;they must be allocated in the task's context)
-
- continitunit
- bsr BufPtrSetup ;Fill in head, tail, startbuf, and endbuf
- move.l a4,mdu_prefs(a3)
- move.l (SysBase).w,mdu_SysLib(a3)
- lea timerport+MP_MSGLIST(a3),a0
- NEWLIST a0 ;Init the unit's timer MsgPort's list
- move.b d2,mdu_UnitNum(a3) ;Initialize unit number
- move.l a6,mdu_Device(a3) ;Initialize device pointer
- move.l a3,mdu_is+IS_DATA(a3) ;Pass unit addr to interrupt server
- move.b #$FF,xstate(a3)
- move.b #$80,frstate(a3)
-
- lea timername(pc),a0 ;Open the timer device.
- moveq #0,d0 ;UNIT_MICROHZ
- lea timeriorequest(a3),a1
- moveq #0,d1 ;no special flags
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS OpenDevice
- movea.l (sp)+,a6
- tst.l d0
- bne OpenTimerFailed
-
- ;Initialize/set-up DACIA registers (for this unit).
- ;(according to the prefs pointed to by a4)
-
- bsr InitDACIA
- tst.l d0
- bne InitDACIAfailed
-
- ;Install an interrupt server.
-
- lea mdu_is(a3),a1
- moveq #INTB_EXTER,d0 ;EXTER (level 6) interrupt (=13)
- movea.l md_SysLib(a6),a6
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l a1,-(sp)
- move.l a3,-(sp)
- PUTDEBUG 200,<'At AddIntServer, unit= %lx and mdu_is= %lx'>
- addq.l #8,sp
- endc
-
- jsr _LVOAddIntServer(a6)
- movea.l mdu_Device(a3),a6
- moveq #0,d0 ;situation under control
- movem.l (sp)+,d1/a0/a1/a2
- PUTDEBUG 100,<'SetUpUnit: Finished!'>
- rts
-
- InitDACIAfailed
- moveq #2,d0
- bra.b EndSetUpUnit
-
- OpenTimerFailed
- moveq #1,d0
- EndSetUpUnit
- move.l d0,-(sp)
- move.l prefs_RBUFLEN(a4),d0
- movea.l startbuf(a3),a1
- myEXEC FreeMem
- move.l (sp)+,d0
- movem.l (sp)+,d1/a0/a1/a2
- rts
-
- ;*** end of SetUpUnit ***
-
- ;Initialize/set-up DACIA registers (for this unit).
- ;(according to the prefs pointed to by a4)
- ;d0.b will mirror CTR, d1.b will mirror FMR
- ;Returns error code in d0 - zero if successful.
- ;uses d1,d3,d5,a0
-
- InitDACIA
- PUTDEBUG 100,<'InitDACIA: Called.'>
- movem.l d1/d3/d5/a0,-(sp)
-
- ;"During power-on initialization, all readable registers should be read to
- ; assure that the status registers are initialized."
-
- move.b ISR(a5),d0
- move.b CSR(a5),d0
- move.b RDR(a5),d0
-
- move.b frstate(a3),d0 ;Set DTR* and RTS* low (assert)
- and.b #$FC,d0 ;Clear bits zero and one
- move.b d0,frstate(a3)
- move.b d0,FMR(a5)
-
- moveq #0,d0
- bset #6,d0 ;Always access ACR, never CDR
- move.b frstate(a3),d1
-
- move.l prefs_BAUD(a4),d3 ;First, set proper baud rate.
- swap d3
- tst.w d3
- bne InvParm
- swap d3
- lea baudtable(pc),a0
- moveq #15,d5
- baudloop
- cmp.w (a0)+,d3
- dbeq d5,baudloop
- bne InvParm
- not.b d5
- and.b #$0F,d5
- or.b d5,d0
-
- ifne INFO_LEVEL
- clr.l -(sp)
- move.b d0,3(sp)
- PUTDEBUG 200,<'InitDACIA: Baud rate = %lx'>
- addq.l #4,sp
- endc
-
- cmpi.b #1,prefs_STOPBITS(a4) ;Next, set number of stop bits per character
- beq.b onestop
- bset #5,d0
- onestop
- move.b d0,CTR(a5)
- move.b prefs_READLEN(a4),d3 ;Set number of data bits per char
- cmp.b prefs_WRITELEN(a4),d3
- bne InvParm ;We don't support different read & write char lengths
- subq.b #5,d3
- bmi InvParm
- cmp.b #3,d3
- bhi InvParm
- lsl.b #5,d3
- or.b d3,d1
-
- btst #SEXTB_MSPON,prefs_EXTFLAGS+3(a4) ;Set parity
- beq.b EvenOdd
- bset #2,d1
- btst #SEXTB_MARK,prefs_EXTFLAGS+3(a4)
- bne.b UseMark
- or.b #24,d1 ;Use space
- bra.b NoParity
-
- UseMark
- or.b #16,d1
- bra.b NoParity
-
- EvenOdd
- btst #SERB_PARTY_ON,prefs_SERFLAGS(a4)
- beq.b NoParity
- bset #2,d1
-
- btst #SERB_PARTY_ODD,prefs_SERFLAGS(a4)
- bne.b UseOdd
- or.b #8,d1
- UseOdd:
- NoParity
- move.b d1,frstate(a3)
- move.b d1,FMR(a5)
- moveq #0,d0
- movem.l (sp)+,d1/d3/d5/a0
- PUTDEBUG 100,<'InitDACIA: Finished!'>
- rts
-
- InvParm
- PUTDEBUG 100,<'InitDACIA: Invalid Parm!'>
- moveq #-1,d0
- movem.l (sp)+,d1/d3/d5/a0
- rts
-
- ;*** end of InitDACIA ***
-
- ;(this is a subroutine for InitUnit)
- ;This routine sets up the two tasks (one for reading, one for writing).
- ;Returns zero in d0.l if an error occured else a ptr to the unit.
- ;
- ;uses d1-d4 and a0-a5
-
- InitTask
- PUTDEBUG 100,<'InitTask: called.'>
- movem.l d1-d4/a0-a5,-(sp)
- lea ReadTask_Begin(pc),a5 ;Set up the read task
- lea mdu_rstack(a3),a0
- lea mdu_rtcb(a3),a1
- movea.l a3,a2 ;Read message port
- bsr.b InitTaskStruct
-
- lea WriteTask_Begin(pc),a5 ;Set up the write task
- lea mdu_wstack(a3),a0
- lea mdu_wtcb(a3),a1
- lea mdu_wport(a3),a2
- move.l a3,TC_EXCEPTDATA(a1)
- lea breakexception(pc),a4
- move.l a4,TC_EXCEPTCODE(a1)
- bsr.b InitTaskStruct
-
- move.l a3,d0 ;Mark us as ready to go
- PUTDEBUG 100,<'InitTask: ok'>
-
- ;Return zero in d0.l if an error occured, else a ptr to the unit
-
- movem.l (sp)+,d1-d4/a0-a5
- rts
-
- ;Start up the unit task. We do a trick here --we set his message port to
- ;PA_IGNORE until the new task has a change to set it up. We cannot go to
- ;sleep here: it would be very nasty if someone else tried to open the unit
- ;(exec's OpenDevice has done a Forbid() for us --we depend on this to become
- ;single threaded).
- ;
- ;Enter with:
- ;a0 - ptr to low end of stack
- ;a1 - ptr to tcb (task control block)
- ;a3 - unit pointer
- ;a5 - starting address of task
- ;a2 - ptr to a (uninitialized) message port
- ;
- ;uses a0-a3 and d0,d1
-
- InitTaskStruct
- movem.l d0/d1/a0-a3,-(sp)
- move.l a0,TC_SPLOWER(a1) ;Initialize the stack information
- lea MYPROCSTACKSIZE(a0),a0 ;High end of stack
- move.l a0,TC_SPUPPER(a1)
- move.l a3,-(a0) ;Argument - unit ptr (send on stack)
- move.l a0,TC_SPREG(a1)
- move.l a1,MP_SIGTASK(a2)
-
- ifge INFO_LEVEL-30
- move.l a1,-(sp)
- move.l a3,-(sp)
- PUTDEBUG 100,<'InitUnit, unit= %lx, task=%lx'>
- addq.l #8,sp
- endc
-
- lea MP_MSGLIST(a2),a0
- NEWLIST a0 ;Init the unit's MsgPort's list
- movea.l a5,a2 ;Startup the task
- lea (-1).l,a3 ;generate address error
- ;if task ever "returns" (we RemTask() it
- ;to get rid of it...)
- moveq #0,d0
- PUTDEBUG 100,<'About to add task'>
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS AddTask
- movea.l (sp)+,a6
- movem.l (sp)+,d0/d1/a0-a3
- rts
-
- ;Get rid of the unit's tasks. We know this is safe because the unit has an
- ;open count of zero, so it is 'guaranteed' not in use.
- ;
- ;Kill the two tasks
- ; ( a3:unitptr, a6:deviceptr )
- ;
- ;uses a0,a1 and d0-d2
-
- KillTask
- movem.l a0/a1/d0-d2,-(sp)
- lea mdu_rtcb(a3),a1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS RemTask
- movea.l (sp)+,a6
-
- lea mdu_wtcb(a3),a1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS RemTask
- movea.l (sp)+,a6
-
- moveq #0,d2
- move.b mdu_UnitNum(a3),d2 ;Save the unit number
- bsr FreeUnit ;Free the unit structure
- lsl.l #2,d2
- clr.l md_Units(a6,d2.l) ;Clear out the unit vector in the device
- movem.l (sp)+,a0/a1/d0-d2
- rts
-
- ; ( a3:unitptr, a6:deviceptr )
- ;uses a0,a1,d0,d1
-
- FreeUnit
- movem.l a0/a1/d0/d1,-(sp)
- movea.l a3,a1
- move.l #mdu_SIZE,d0
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS FreeMem
- movea.l (sp)+,a6
- movem.l (sp)+,a0/a1/d0/d1
- rts
-
- ; ( a3:unitptr, a6:deviceptr )
- ;
- ;uses a0,a1,a5,d0-d2
-
- ExpungeUnit
- movem.l a0/a1/a5/d0-d2,-(sp)
- PUTDEBUG 100,<'ExpungeUnit: called'>
-
- movea.l daciabase(a3),a5
- move.b #$7f,IER(a5) ;shut off all interrupts
- move.b #$83,FMR(a5) ;Deassert DTR* and RTS*
-
- bsr FreeResources
-
- lea mdu_rtcb(a3),a1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS RemTask
- movea.l (sp)+,a6
-
- lea mdu_wtcb(a3),a1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS RemTask
- movea.l (sp)+,a6
-
- moveq #0,d2
- move.b mdu_UnitNum(a3),d2 ;Save the unit number
- bsr FreeUnit ;Free the unit structure.
- lsl.l #2,d2
- clr.l md_Units(a6,d2.l) ;Clear out the unit pointer in the device
-
- movem.l (sp)+,a0/a1/a5/d0-d2
- rts
-
- ;This routines frees up resources specific to this driver (the other
- ;resources are taken care of by the skeleton).
- ;Be careful with this routine, so as not to pull the rug out from
- ;underneath the driver...
- ;
- ;If you can expunge you unit, and each unit has it's own interrupts,
- ;you must remember to remove its interrupt server
- ;
- ;Enter with unit ptr in a3 and device ptr in a6
- ;
- ;uses a0,a1,a4,d0,d1
-
-
- FreeResources
- PUTDEBUG 100,<'FreeResources: called'>
- movem.l a0/a1/a4/d0/d1,-(sp)
- lea mdu_is(a3),a1 ;Point to interrupt structure
- moveq #INTB_EXTER,d0 ;Portia interrupt bit 13
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS RemIntServer ;Now remove the interrupt server
- movea.l (sp)+,a6
-
- movea.l startbuf(a3),a1 ;Free input buffer memory
- movea.l mdu_prefs(a3),a4
- move.l prefs_RBUFLEN(a4),d0
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS FreeMem
- movea.l (sp)+,a6
-
- lea timeriorequest(a3),a1 ;Close timer device
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS CloseDevice
- movea.l (sp)+,a6
- movem.l (sp)+,a0/a1/a4/d0/d1
- PUTDEBUG 100,<'FreeResources: finished!'>
- rts
-
-
- ;BeginIO starts all incoming io. The IO is either queued up for the
- ;unit task or processed immediately.
- ;
- ;BeginIO often is given the responsibility of making devices single
- ;threaded... so two tasks sending commands at the same time don't cause
- ;a problem. Once this has been done, the command is dispatched via
- ;PerformIO.
- ;
- ;There are many ways to do the threading. This example uses the
- ;UNITB_ACTIVE bit. Be sure this is good enough for your device before
- ;using! Any method is ok. If immediate access can not be obtained, the
- ;request is queued for later processing.
- ;
- ;Some IO requests do not need single threading. These can be performed
- ;immediatley.
- ;
- ;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.
- ;
- ;Notes: This routine will look at io_command to determine which task
- ;to use. Break with QUEUEDBRK and Write requests go to the write
- ;task. Read requests (only) go to the read task.
- ;Break requests without QUEUEDBRK initiate a write task exception.
- ;The actual break job is started, and finished, within that exception.
- ;At the end of the exception, the write task resumes whatever it was
- ;doing before the break.
- ;All other requests are performed immediately, i.e. within the caller's
- ;context.
- ;
- ;This routine uses a3 (and a0/a1/d0/d1, but those do not need to be
- ;preserved).
- ; ( iob: a1, device:a6 )
-
- LibBeginIO
- ifge INFO_LEVEL-1
- bchg.b #1,($bfe001).l ;Blink the power LED
- endc
-
- ifge INFO_LEVEL-3
- clr.l -(sp)
- move.w IO_COMMAND(a1),2(sp) ;Get entire word
- PUTDEBUG 100,<'BeginIO -- %ld'>
- addq.l #4,sp
- endc
-
- move.l a3,-(sp)
- andi.b #$f,IO_FLAGS(a1)
- move.b #NT_MESSAGE,LN_TYPE(a1) ;So WaitIO() is guaranteed to work
- movea.l IO_UNIT(a1),a3 ;Bookkeeping -> what unit to play with
- move.w IO_COMMAND(a1),d0
-
- ;Do a range check & make sure bad requests are rejected
-
- cmp.w #MYDEV_END,d0 ;Compare all 16 bits
- bhi BeginIO_NoCmd ;no, reject it. (bcc=bhs - unsigned)
-
- ;Process all immediate commands no matter what
-
- move.l #IMMEDIATES,d1
- myDisable a0 ;<-- Ick, nasty stuff, but needed here.
- btst d0,d1
- bne BeginIO_Immediate
-
- ;See if the unit is STOPPED. If so, queue the msg.
-
- btst #MDUB_STOPPED,MDU_FLAGS(a3)
- bne BeginIO_QueueMsg
-
- ;This is not an immediate command. See if the device is busy. If the device
- ;is not, do the command on the user schedule. Else fire up the task. This
- ;type of arbitration is essential for a device to reliably work with shared
- ;hardware.
- ;
- ;REMEMBER: Never Wait() on the user's schedule in BeginIO()! The only
- ;exception is when the user has indicated it is ok by setting the "quick" bit.
- ;
- ;We need to queue the device. mark us as needing task attention. Clear the
- ;quick flag
- ;
- ;Note: We handle read, write, and break requests seperately and differently.
-
- BeginIO_QueueMsg
- bclr #IOB_QUICK,IO_FLAGS(a1) ;We did NOT complete this quickly
- cmpi.w #CMD_READ,IO_COMMAND(a1)
- bne.b notread
-
- bset #UNITB_INREADTASK,UNIT_FLAGS(a3)
- myEnable a0
-
- ifge INFO_LEVEL-250
- move.l a1,-(sp)
- move.l a3,-(sp)
- PUTDEBUG 100,<'PutMsg: Port=%lx Message=%lx'>
- addq.l #8,sp
- endc
-
- movea.l a3,a0
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS PutMsg ;Port=a0, Message=a1
- movea.l (sp)+,a6
- bra BeginIO_End ;Return to caller before completing
-
-
- notread
- cmpi.w #CMD_WRITE,IO_COMMAND(a1)
- bne.b handlebreak
-
- queuewrt
- bset #UNITB_INWRITETASK,UNIT_FLAGS(a3)
- myEnable a0
- lea mdu_wport(a3),a0
-
-
- ifge INFO_LEVEL-250
- move.l a1,-(sp)
- move.l a0,-(sp)
- PUTDEBUG 100,<'PutMsg: Port=%lx Message=%lx'>
- addq.l #8,sp
- endc
-
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS PutMsg ;Port=a0, Message=a1
- movea.l (sp)+,a6
- bra BeginIO_End ;Return to caller before completing
-
- handlebreak
- btst #SERB_QUEUEDBRK,IO_SERFLAGS(a1)
- bne.b queuewrt ;Handle just like a write request
-
- ;This is the tricky case. We initiate the break immediately. To do this we
- ;cleverly (?) use the under-documented power of task exceptions.
-
- btst #UNITB_INBREAK,UNIT_FLAGS(a3)
- bne breakout
- bset #UNITB_BREAKACTIVE,UNIT_FLAGS(a3)
- myForbid
- bset #ioflagsB_Active,IO_FLAGS(a1)
- move.l a1,breakiorequest(a3)
- myPermit
- move.l breaksig(a3),d0
- lea mdu_wtcb(a3),a1
- myEXEC Signal
- myEnable a0
- bra.b BeginIO_End
-
- ;Do it on the schedule of the calling process
-
- BeginIO_Immediate
- myEnable a0
- bsr PerformIO
-
- BeginIO_End
- PUTDEBUG 100,<'BeginIO_End'>
- movea.l (sp)+,a3
- rts
-
- BeginIO_NoCmd
- PUTDEBUG 100,<'BeginIO_NoCmd!'>
- move.b #IOERR_NOCMD,IO_ERROR(a1)
- bra.b BeginIO_End
-
- breakout
- myEnable a0
- bsr TermIO
- bra BeginIO_End
-
- ;PerformIO actually dispatches an io request. It might be called from the
- ;task, or directly from BeginIO (thus on the callers's schedule)
- ;
- ;It expects a3 to already have the unit pointer in it. a6 has the device
- ;pointer (as always). a1 has the io request. Bounds checking has already
- ;been done on the I/O Request.
- ;
- ;This routine itself uses d0,a0,a4,a5. The command it calls may choose
- ;to use any or all registers.
- ; ( iob:a1, unitptr:a3, devptr:a6 )
-
- PerformIO
- ifge INFO_LEVEL-150
- clr.l -(sp)
- move.w IO_COMMAND(a1),2(sp) ;Get entire word
- PUTDEBUG 100,<'PerformIO -- %ld'>
- addq.l #4,sp
- endc
-
- movem.l d0/a0/a4/a5,-(sp)
- movea.l mdu_prefs(a3),a4
- movea.l daciabase(a3),a5
- moveq #0,d0
- move.b d0,IO_ERROR(a1) ; No error so far
- move.b IO_COMMAND+1(a1),d0 ;Look only at low byte
- lsl.w #2,d0 ; Multiply by 4 to get table offset
- lea cmdtable(pc),a0
- movea.l 0(a0,d0.w),a0
- move.l a0,d0
- cmpi.l #$ffffffff,d0
- bne ImmCont
- PUTDEBUG 200,<'PerformIO_NoCmd!'>
- move.b #IOERR_NOCMD,IO_ERROR(a1)
- movem.l (sp)+,d0/a0/a4/a5
- rts
-
-
- ImmCont
- movem.l d0-d7/a0-a6,-(sp)
- jsr (a0) ;iob:a1 unit:a3 devprt:a6
- movem.l (sp)+,d0-d7/a0-a6
- bsr.b TermIO
- movem.l (sp)+,d0/a0/a4/a5
- PUTDEBUG 100,<'PerformIO -- Finished!'>
- rts
-
-
-
- ;PerformIO
- ; ifge INFO_LEVEL-150
- ; clr.l -(sp)
- ; move.w IO_COMMAND(a1),2(sp) ;Get entire word
- ; PUTDEBUG 150,<'PerformIO -- %ld'>
- ; addq.l #4,sp
- ; endc
-
- ; movem.l d0/a0/a4/a5,-(sp)
- ; movea.l mdu_prefs(a3),a4
- ; movea.l daciabase(a3),a5
- ; moveq #0,d0
- ; move.b d0,IO_ERROR(a1) ; No error so far
- ; move.b IO_COMMAND+1(a1),d0 ;Look only at low byte
- ; lsl.w #2,d0 ; Multiply by 4 to get table offset
- ; lea cmdtable(pc),a0
- ; movea.l 0(a0,d0.w),a0
-
- ; movem.l d0-d7/a0-a6,-(sp)
- ; jsr (a0) ;iob:a1 unit:a3 devprt:a6
- ; movem.l (sp)+,d0-d7/a0-a6
- ; bsr.b TermIO
- ; movem.l (sp)+,d0/a0/a4/a5
- ; PUTDEBUG 15,<'PerformIO -- Finished!'>
- ; rts
-
-
- ;TermIO sends the IO request back to the user. It knows not to mark
- ;the device as inactive if this was an immediate request or if the
- ;request was started from the server task.
- ;
- ; ( iob:a1, unitptr:a3 )
- ;uses d0/d1/a0/a1
-
- TermIO
- movem.l d0/d1/a0/a1,-(sp)
- PUTDEBUG 100,<'TermIO'>
- move.w IO_COMMAND(a1),d0
- move.l #IMMEDIATES,d1
- btst d0,d1
- bne.b TermIO_Immediate ;IO was immediate, don't do task stuff...
-
- cmpi.w #CMD_READ,IO_COMMAND(a1)
- bne.b wrtterm
-
- ;We may need to turn the active bit off.
-
- btst #UNITB_INREADTASK,UNIT_FLAGS(a3)
- bne.b TermIO_Immediate ;IO came from task, don't clear ACTIVE...
-
- ;The task does not have more work to do
-
- bclr #UNITB_READACTIVE,UNIT_FLAGS(a3)
- bra.b TermIO_Immediate
-
- ;We may need to turn the active bit off.
-
- wrtterm
- btst #UNITB_INWRITETASK,UNIT_FLAGS(a3)
- bne.b TermIO_Immediate ;IO came from task, don't clear ACTIVE...
-
- ;The task does not have more work to do
-
- bclr #UNITB_WRITEACTIVE,UNIT_FLAGS(a3)
-
-
- ;If the quick bit is still set then we don't need to reply
- ;- msg - just return to the user.
-
- TermIO_Immediate
- btst #IOB_QUICK,IO_FLAGS(a1)
- bne.b TermIO_End
- myEXEC ReplyMsg ;a1-message
- ;ReplyMsg sets the LN_TYPE to NT_REPLYMSG
- TermIO_End
- movem.l (sp)+,d0/d1/a0/a1
- rts
-
- ;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
- ; a4 -- a pointer to prefs
- ; a5 -- a pointer to the unit hardware
- ; a6 -- a pointer to the device
- ;
- ;Commands that conflict with 68000 instructions have a "My" prepended to them.
-
- ;Read: The handshaking lines don't serve any purpose when reading (except
- ;perhaps to say "shut up" if an exceptional condition occurs).
- ;
- ;d5 is used to count the number of bytes transferred
-
- Read
- moveq #0,d5
- move.l IO_LENGTH(a1),d6
- movea.l IO_DATA(a1),a2
- move.l readsig(a3),d4
- add.l readabortsig(a3),d4
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l d6,-(sp)
- PUTDEBUG 100,<'Read entered -- %ld bytes requested.'>
- addq.l #4,sp
- endc
-
- bra.b ReadEntry
-
- readloop
- move.l d4,d0
- myEXEC Wait ;Wait for one or more bytes to come in
- and.l readabortsig(a3),d0
- bne readabort
- NoErrorAtAll
- PUTDEBUG 100,<'Read: One or more bytes came in.'>
-
- ReadEntry
- bsr GetBytesInReadBuf
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l d0,-(sp)
- PUTDEBUG 100,<'Read: In fact, %ld bytes came in.'>
- addq.l #4,sp
- endc
-
- tst.l d0
- beq readloop
- cmp.l d0,d6
- bls.b AllDone ;Branch if d6 <= d0
- sub.l d0,d6
- bsr DumpReadBuf ;Get all the bytes we can
- beq readloop
- bra.b endread ;Go if TermArray caused early exit
-
- ;The number of bytes in the read buffer equals or exceeds the number of
- ;bytes the user wants.
-
- AllDone
- PUTDEBUG 100,<'Read: AllDone'>
- move.l d6,d0
- bsr DumpReadBuf ;Dump d0 bytes of the read buffer into the user's buffer
- endread
- PUTDEBUG 100,<'Read: Finished!'>
- bclr #MDUB_V,MDU_FLAGS(a3)
- beq.b NoOverflow
- move.b #SerErr_BufOverflow,IO_ERROR(a1)
- NoOverflow
- move.l d5,IO_ACTUAL(a1)
- rts ;Done with read
-
- ;Something exceptional happened. An informative error code should be
- ;returned. first, check for error conditions... Note that the serial.device
- ;standard does not provide a way to inform the caller of simultaneous error
- ;conditions.
-
- readabort
- PUTDEBUG 100,<'readabort: Something exceptional happened.'>
- myDisable
- move.b ISRcopy(a3),d2
- move.b CSRcopy(a3),d3
- myEnable
- btst #ISRB_PAR,d2
- beq.b notpar
-
- move.b #SerErr_ParityErr,IO_ERROR(a1) ;parity error
- bra endread
-
- notpar
- btst #1,d2 ;Check for frame err, overrun, & break
- beq endread ;We infer that an abort has been issued
-
- btst #CSRB_FE,d3 ;Probe further...
- beq.b noframe
-
- move.b #SerErr_LineErr,IO_ERROR(a1) ;Framing Error
- bra endread
-
- noframe
- btst #CSRB_RBRK,d3
- beq NoErrorAtAll
-
- ;Note that we ignore receive overrun errors. Since there is no error
- ;code for it, I assume this is what serial.device does too.
-
- move.b #SerErr_DetectedBreak,IO_ERROR(a1) ;Break
- bra endread
-
- ;d5 is used to count the number of bytes transferred
-
- Write
- moveq #0,d5
- move.l IO_LENGTH(a1),d6
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l d6,-(sp)
- PUTDEBUG 100,<'Write entered -- %ld bytes requested.'>
- addq.l #4,sp
- endc
-
- movea.l IO_DATA(a1),a2
- move.l tdresig(a3),d4
- add.l writeabortsig(a3),d4
- btst #SERB_7WIRE,prefs_SERFLAGS(a4)
- bne.b write.hand
-
- ;Safety check: if CTS* is not asserted, return with an error code. This
- ;usually occurs when the port is not connected to anything.
- ;Note: This is just a hack - something more robust will be done later
- ;(hopefully).
-
- ;Enter with the number of bytes to transmit in d6
- ;Enter with a pointer to the user's data in a2
-
- write.nohand
- btst #CSRB_CTSL,CSR(a5)
- bne.b not_connected
-
- bsr.b transmit
- subq.l #1,d6
- bne.b write.nohand
- endwrite
- move.l d5,IO_ACTUAL(a1)
- rts
-
- not_connected:
- move.b #SerErr_LineErr,IO_ERROR(a1)
- bra.b endwrite
-
- ;Enter with the number of bytes to transmit in d6
- ;Enter with a pointer to the user's data in a2
-
- ;The handshaking protocol implemented here is based on the flowchart
- ;on page 5-66 of "An Introduction to Microcomputers" by Adam Osborne.
-
- write.hand:
- write.hand.loop
- btst #CSRB_DSRL,CSR(a5) ;Test DSR*
- beq.b writehandcont ;If low, proceed
- move.l dsrsig(a3),d0
- add.l writeabortsig(a3),d0
- myEXEC Wait
- and.l writeabortsig(a3),d0
- bne.b aborthand
- bra.b write.hand.loop
-
- ;The 65C52 halts any transmission until CTS* is asserted, so no code
- ;is involved. This can also be a curse if you don't desire handshaking.
-
- writehandcont
- bsr.b transmit ;Transmit a byte
- subq.l #1,d6
- bne.b write.hand.loop
- aborthand
- bra.b endwrite
-
- ;*** Subroutine for the write routines ***
- ;
- ;Returns 1 in d6 if aborted.
-
- transmit
- btst #ISRB_TDRE,ISR(a5) ;test TDRE
- bne oktosend ;If set, we can load up the transmit reg immediately
- PUTDEBUG 100,<'Transmit: Entered.'>
- ori.b #WRITEINTMASK,IERstate(a3)
- move.b #WRITEINT,IER(a5)
- move.l d4,d0
- myEXEC Wait ;Wait for TDRE to be set
- and.l writeabortsig(a3),d0
- bne aborttransmit
- PUTDEBUG 100,<'Transmit: TDRE signal received.'>
- bra transmit ;Just to be sure
-
- oktosend
- btst #SERB_XDISABLED,prefs_SERFLAGS(a4) ;Handle xoff, if requested
- bne.b doit
- txs
- tst.b xstate(a3)
- bne.b doit
-
- PUTDEBUG 100,<'Transmit: Waiting for XON!'>
- move.l xonsig(a3),d0
- add.l writeabortsig(a3),d0
- myEXEC Wait ;Wait for an x-on signal before sending
- and.l writeabortsig(a3),d0
- bne.b aborttransmit
- bra.b txs
-
- doit
- PUTDEBUG 100,<'Transmit: Received XON!'>
- tst.l d6
- bmi.b termzero
- doit1
- move.b (a2)+,TDR(a5)
- addq.l #1,d5
- rts
-
- termzero
- tst.b (a2)
- bne.b doit1
- aborttransmit
- moveq #1,d6
- rts
-
- ;*** end of write routines ***
-
- ;*** Subroutines for the read routine ***
- ;
- ;Return with the number of bytes in the read buffer in d0
-
- GetBytesInReadBuf
- move.l d1,-(sp)
- myDisable
- move.l head(a3),d1
- move.l tail(a3),d0
- sub.l d1,d0
- bcc.b ok.getbytes
- add.l prefs_RBUFLEN(a4),d0
- ok.getbytes
- myEnable
- move.l (sp)+,d1
- rts
-
-
- ;Enter with number of bytes to read in d0
- ;Enter with a pointer to a dump buffer in a2
- ;the pointer in a2 is updated to reflect the current position
- ;
- ;This routine uses as scratch: a0,a6,d0,d3,d6,d7
- ;and updates the following: d5,a2
- ;Returns with zero flag set if all is OK, or zero cleared if a TermArray
- ;match was found.
-
- DumpReadBuf
- myDisable
- movea.l head(a3),a0
- move.l a0,-(sp)
- adda.l d0,a0
- cmpa.l endbuf(a3),a0
- bls.b notend.drb
- suba.l prefs_RBUFLEN(a4),a0
- notend.drb
- move.l a0,head(a3)
- myEnable
- movea.l (sp)+,a0
-
- ;Note that only the buffer pointers are protected. The main copy loop
- ;(below) could, however, return somewhat incoherent data if the buffer
- ;happens to overflow.
-
- btst #SERB_EOFMODE,IO_SERFLAGS(a1)
- bne eofdump
-
- drb.loop
- move.b (a0)+,d6
- bsr.b xcode
- move.b d6,(a2)+
- addq.l #1,d5
- cmpa.l endbuf(a3),a0
- bls.b notend.drb2
- movea.l startbuf(a3),a0
- notend.drb2
- subq.l #1,d0
- bne.b drb.loop
- rts
-
-
- ;Check for xon/xoff
-
- xcode
- ifne INFO_LEVEL
- move.l prefs_CTLCHAR(a4),-(sp)
- PUTDEBUG 100,<'XCODE: prefs_CTLCHAR = %lx'>
- addq.l #4,sp
- endc
-
- ifne INFO_LEVEL
- clr.l -(sp)
- move.b d6,3(sp)
- PUTDEBUG 100,<'XCODE: Current char = %lx'>
- addq.l #4,sp
- endc
-
- cmp.b prefs_CTLCHAR(a4),d6 ;Xon check
- beq.b setxon
- cmp.b prefs_CTLCHAR+1(a4),d6 ;Xoff check
- beq.b setxoff
- rts
-
- setxon
- PUTDEBUG 100,<'XCODE: XON!'>
- tst.b xstate(a3)
- bne.b sxoq ;If already on, don't signal!
- st xstate(a3)
- movem.l d0/d1/a0/a1/a6,-(sp)
- move.l xonsig(a3),d0
- lea mdu_wtcb(a3),a1
- movea.l (SysBase).w,a6
- jsr _LVOSignal(a6)
- movem.l (sp)+,d0/d1/a0/a1/a6
- sxoq
- rts
-
- setxoff
- PUTDEBUG 100,<'XCODE: XOFF!'>
- clr.b xstate(a3)
- rts
-
- eofdump:
- eofdumploop:
- moveq #7,d7
- lea prefs_TERMARRAY(a4),a6
- move.b (a0)+,d6
- bsr xcode
- cmploop
- cmp.b (a6)+,d6
- dbcc d7,cmploop
- beq.b termread
- move.b d6,(a2)+
- addq.l #1,d5
- cmpa.l endbuf(a3),a0
- bls.b notend.drb1
- movea.l startbuf(a3),a0
- notend.drb1:
- subq.l #1,d0
- bne.b eofdumploop
- rts
-
- termread
- moveq #-1,d0
- rts
-
- ;This routine handles queued breaks. Non-queued breaks are handled by an
- ;exception (which uses similar but not identical code).
-
- Break
- PUTDEBUG 100,<'Break: called'>
- bset #UNITB_INBREAK,UNIT_FLAGS(a3)
- move.b #2,ACR(a5) ;start break
- lea timeriorequest(a3),a1
- move.w #TR_ADDREQUEST,IO_COMMAND(a1)
- clr.l TV_SECS(a1)
- move.l prefs_BRKTIME(a4),d0
- ori.b #$FF,d0 ;To avoid the V33/V34 bug
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l d0,-(sp)
- PUTDEBUG 100,<'Break: TV_MICRO=%ld'>
- addq.l #4,sp
- endc
-
- move.l d0,TV_MICRO(a1)
- movea.l MN_REPLYPORT(a1),a0
- move.b MP_SIGBIT(a0),d2
- myEXEC SendIO
- moveq #0,d0
- bset d2,d0
- add.l writeabortsig(a3),d0
-
- PUTDEBUG 100,<'Break: Waiting...'>
-
- myEXEC Wait
- and.l writeabortsig(a3),d0
- beq.b breakOK1
-
- PUTDEBUG 100,<'Break: Aborted!'>
-
- myEXEC AbortIO ;The break was aborted. Clean up.
- myEXEC WaitIO
-
- breakOK1
- move.b #0,ACR(a5) ;stop the break
- bclr #UNITB_INBREAK,UNIT_FLAGS(a3)
-
- PUTDEBUG 100,<'Break: Finished!'>
- rts
-
- ;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.
- ;
- ;Note that AbortIO is called directly, not via BeginIO/PerformIO.
- ;The only other direct functions are Open, Close, Expunge, and of course
- ;BeginIO.
- ;
- ; ( iob: a1, device:a6 )
- ;returns an error code in d0 - zero if successful.
- ;If sucessful, AbortIO returns IOERR_ABORTED in IO_ERROR and sets bit
- ;5 of IO_FLAGS.
- ;
- ;This routine uses d0/d1/a0, but they do not need to be saved.
-
- LibAbortIO
- PUTDEBUG 100,<'AbortIO: called'>
- myForbid
- move.b #IOERR_ABORTED,IO_ERROR(a1) ;We always say we succeed(ed)
- bset #5,IO_FLAGS(a1)
- cmpi.b #NT_REPLYMSG,LN_TYPE(a1) ;Already complete?
- beq.b complete
-
- ;Check to see whether or not the IORequest is being processed
-
- btst #ioflagsB_Active,IO_FLAGS(a1)
- bne.b inprogress
- bset #ioflagsB_Ignore,IO_FLAGS(a1)
-
- complete
- myPermit
- moveq #0,d0
- PUTDEBUG 100,<'AbortIO: Finished!'>
- rts
-
- inprogress
- movea.l IO_UNIT(a1),a3 ;IO is in progress - abort it.
- cmpi.w #CMD_READ,IO_COMMAND(a1)
- bne ip1
- move.l readabortsig(a3),d0
- lea mdu_rtcb(a3),a1
- ip0
- myEXEC Signal
- bra.b complete
-
- ip1
- cmpi.w #CMD_WRITE,IO_COMMAND(a1)
- bne.b ip2
- ip3
- move.l writeabortsig(a3),d0
- lea mdu_wtcb(a3),a1
- bra.b ip0
-
- ;The break command is handled here.
- ;We use the same abort signal (as write), so handle just as with write.
-
- ip2
- bra.b ip3
-
- Invalid
- move.b #IOERR_NOCMD,IO_ERROR(a1)
- rts
-
- ;Clear invalidates all internal buffers.
- ;
- ; a1 -- a pointer to the io request block
- ; a3 -- a pointer to the unit
- ; a4 -- a pointer to prefs
- ; a5 -- a pointer to the unit hardware
- ; a6 -- a pointer to the device
-
- MyClear
- myDisable
- move.l startbuf(a3),d0
- move.l d0,head(a3)
- move.l d0,tail(a3)
- myEnable
- rts
-
- ; a1 -- a pointer to the io request block
- ; a3 -- a pointer to the unit
- ; a4 -- a pointer to prefs
- ; a5 -- a pointer to the unit hardware
- ; a6 -- a pointer to the device
-
- MyReset
- PUTDEBUG 100,<'MyReset: called'>
- myForbid
- move.b IERstate(a3),d7
- clr.b IERstate(a3)
- move.b #$7f,IER(a5) ;Disable ACIA interrupts
- bsr Flush ;Flush pending requests
-
- ;Abort current IO, if any IO is indeed occuring
-
- move.l a1,-(sp)
- btst #UNITB_BREAKACTIVE,UNIT_FLAGS(a3)
- beq NoBreakActive
- movea.l breakiorequest(a3),a1
- bsr LibAbortIO
-
- NoBreakActive
- btst #UNITB_WRITEACTIVE,UNIT_FLAGS(a3)
- beq WriteNotActive
- movea.l WriteRequestPtr(a3),a1
- bsr LibAbortIO
-
- WriteNotActive
- btst #UNITB_READACTIVE,UNIT_FLAGS(a3)
- beq NothingActive
- movea.l ReadRequestPtr(a3),a1
- bsr LibAbortIO
-
- NothingActive
- movea.l (sp)+,a1
- bsr SetDefaultPrefs
- bsr CopyPrefs
- bsr FreeResources
- bsr SetUpUnit
-
- tst.l d0 ;Check for a possible error condition
- bmi.b OutOfMem
- subq.l #1,d0
- beq.b TimerError
- subq.l #1,d0
- beq.b ParamError
-
- move.b d7,IERstate(a3) ;Enable DACIA interrupts again
- bset #7,d7
- move.b d7,IER(a5)
- myPermit
- clr.l IO_ACTUAL(a1)
- PUTDEBUG 100,<'MyReset: Finished!'>
- rts
-
- ParamError
- move.b #SerErr_InvParam,IO_ERROR(a1)
- bra.b MyResetFailed
-
- TimerError
- move.b #SerErr_TimerErr,IO_ERROR(a1)
- bra.b MyResetFailed
-
- OutOfMem
- move.b #SerErr_BufErr,IO_ERROR(a1)
- MyResetFailed
- myPermit
- clr.l IO_ACTUAL(a1)
- PUTDEBUG 100,<'MyReset: Error!'>
- rts
-
- ; a1 -- a pointer to the io request block
- ; a3 -- a pointer to the unit
- ; a4 -- a pointer to prefs
- ; a5 -- a pointer to the unit hardware
- ; a6 -- a pointer to the device
- ;
- ;Return number of chars in buffer in IO_ACTUAL
- ;Fill in IO_STATUS
-
- Query
- PUTDEBUG 100,<'Query: called.'>
- bsr GetBytesInReadBuf
- move.l d0,IO_ACTUAL(a1)
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l d0,-(sp)
- PUTDEBUG 100,<'Query: %ld bytes in buf.'>
- move.l (sp)+,d0
- endc
-
- moveq #0,d0 ;d0 will mirror IO_STATUS
- move.b CSR(a5),d1
- btst #0,d1
- beq.b Q1
- bset #6,d0
- Q1
- btst #1,d0
- beq.b Q2
- bset #7,d0
- Q2
- btst #3,d1
- beq.b Q3
- bset #3,d0
- Q3
- btst #4,d1
- beq.b Q4
- bset #5,d0
- Q4
- btst #5,d1
- beq.b Q5
- bset #4,d0
- Q5
- btst #UNITB_BREAKACTIVE,UNIT_FLAGS(a3)
- beq.b NB
- bset #9,d0
- NB
- btst #UNITB_INBREAK,UNIT_FLAGS(a3)
- beq.b NB1
- bset #9,d0
- NB1
- btst #2,d1
- beq.b NRB
- bset #10,d0
- NRB
- tst.b xstate(a3)
- bne.b xIsOn
- bset #11,d0
- xIsOn
- move.w d0,IO_STATUS(a1)
- rts
-
- ; a1 -- a pointer to the io request block
- ; a3 -- a pointer to the unit
- ; a4 -- a pointer to prefs
- ; a5 -- a pointer to the unit hardware
- ; a6 -- a pointer to the device
-
- SetParams:
- bclr #SERB_XDISABLED,prefs_SERFLAGS(a4)
- btst #SERB_XDISABLED,IO_SERFLAGS(a1) ;Set the XDISABLED thing
- beq.b SetP1
- bset #SERB_XDISABLED,prefs_SERFLAGS(a4)
- SetP1:
- beq.b SetP2
- st xstate(a3)
-
- ;Now check to see whether the device is busy, i.e. any current or pending requests.
-
- SetP2
- myForbid
- move.b UNIT_FLAGS(a3),d0
- and.b #$3c,d0 ;anything going on at the moment?
- bne DevBusy
- lea $14(a3),a0 ;Read port - anything there?
- movea.l (a0),a2
- tst.l (a2)
- bne DevBusy
- lea mdu_wport+$14(a3),a0 ;Write port - anything there?
- movea.l (a0),a2
- tst.l (a2)
- bne DevBusy
-
- ;Ok, the device is not busy. Set all params.
-
- bsr SetPrefs ;First copy the data
-
- ;Note that this is for compatability only; it does not speed up this driver.
-
- btst #SERB_RAD_BOOGIE,IO_SERFLAGS(a1) ;Check for RAD_BOOGIE
- beq.b SkipBoogie
- bclr #SEXTB_MSPON,prefs_EXTFLAGS+3(a4)
- bclr #SERB_PARTY_ON,prefs_SERFLAGS(a4)
- bset #SERB_XDISABLED,prefs_SERFLAGS(a4)
- move.b #8,prefs_READLEN(a4)
- move.b #8,prefs_WRITELEN(a4)
-
- SkipBoogie
- bsr InitDACIA ;Then set up the chip
- tst.l d0
- bne PInvP
-
- ;* Deallocate the old read buffer and allocate a new one. *
-
- myDisable
- move.l a1,-(sp) ;Free input buffer memory
- movea.l startbuf(a3),a1
- movea.l mdu_prefs(a3),a4
- move.l prefs_RBUFLEN(a4),d0
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS FreeMem
- movea.l (sp)+,a6
-
- move.l prefs_RBUFLEN(a4),d0 ;Allocate buffer mem
- moveq #64,d1
- cmp.l d1,d0
- bhi.b SPmem
- move.l d1,d0
- move.l d0,prefs_RBUFLEN(a4)
- SPmem
- move.l #MEMF_PUBLIC,d1
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS AllocMem
- movea.l (sp)+,a6
- movea.l (sp)+,a1
- tst.l d0
- bne.b SPMemOK
-
- move.b #SerErr_BufErr,IO_ERROR(a1)
- moveq #64,d0
- move.l d0,prefs_RBUFLEN(a4)
- lea AltBuf(a3),a0
- move.l a0,d0
- SPMemOK
- bsr.b BufPtrSetup
- myEnable
- SPLx
- myPermit
- rts
-
- PInvP
- move.b #SerErr_InvParam,IO_ERROR(a1)
- bra.b SPLx
-
- BufPtrSetup
- move.l d0,head(a3)
- move.l d0,tail(a3)
- move.l d0,startbuf(a3)
- add.l prefs_RBUFLEN(a4),d0
- subq.l #1,d0
- move.l d0,endbuf(a3)
- rts
-
- DevBusy
- myPermit
- move.b #SerErr_DevBusy,IO_ERROR(a1)
- rts
-
-
- ; This is the code that I added to support the ASDG command SetCtrlLines.
- ; This will change the DTR or the RTS lines without external software
- ; playing directly with the hardware. This (seems to me) to be a better
- ; idea then what you have to do on the serial.device. BBS type programs
- ; need this type of support. Mike Mossman ( March 25, 1991)
-
- ; a1 -- a pointer to the io request block
- ; a3 -- a pointer to the unit
- ; a4 -- a pointer to prefs
- ; a5 -- a pointer to the unit hardware
- ; a6 -- a pointer to the device
-
- SetCtrlLines
- move.l IO_OFFSET(a1),d0 ;Get the mask into d0
- move.l IO_LENGTH(a1),d1 ;Get the bits for Setting/Clearing into d1
- and.l d0,d1 ;And the two, to get the bits that need to be changed
- not.l d1 ;not them for 6552 logic
- ori.b #%11111100,d1 ;set the hi bit for FR access
- move.b frstate(a3),d0 ;Get current state
- bset.l #0,d0
- bset.l #1,d0
- bset.l #7,d0
- and.b d1,d0 ;Set DTR and RTS as requested (a low is assert)
- move.b d0,frstate(a3) ;Save the bit setting in the unit
- move.b d0,FMR(a5) ;Actually set the bits
- rts
-
-
-
- ;The Stop command stop all future io requests from being processed until a
- ;Start command is received. The Stop command is NOT stackable: e.g. no matter
- ;how many stops have been issued, it only takes one Start to restart
- ;processing.
-
- MyStop
- PUTDEBUG 100,<'MyStop: called'>
- bset #MDUB_STOPPED,MDU_FLAGS(a3)
- rts
-
- Start
- PUTDEBUG 100,<'Start: called'>
- bsr.b InternalStart
- rts
-
- ;[A3=unit A6=device]
-
- InternalStart
- PUTDEBUG 100,<'InternalStart: called'>
- movea.l a1,a2
- ;Turn processing back on
- bclr #MDUB_STOPPED,MDU_FLAGS(a3)
-
- ;Kick the tasks to start them moving
-
- move.b MP_SIGBIT(a3),d1 ;First the read task...
- moveq #0,d0
- bset d1,d0 ;Prepared signal mask
- movea.l MP_SIGTASK(a3),a1 ;FIXED: marco-task to signal
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS Signal ;FIXED: marco-a6 not a3
- movea.l (sp)+,a6
-
- movea.l a2,a1 ;Then the write task
- lea mdu_wport(a3),a0
- move.b MP_SIGBIT(a0),d1
- moveq #0,d0
- bset d1,d0 ;Prepared signal mask
- movea.l MP_SIGTASK(a0),a1 ;FIXED: marco-task to signal
- move.l a6,-(sp)
- movea.l md_SysLib(a6),a6
- mySYS Signal ;FIXED: marco-a6 not a3
- movea.l (sp)+,a6
- movea.l a2,a1
- PUTDEBUG 100,<'InternalStart: Finished!'>
- rts
-
- ;Flush pulls all I/O requests off the queue and sends them back. We must be
- ;careful not to destroy work in progress, and also that we do not let some io
- ;requests slip by.
- ;
- ;Some funny magic goes on with the STOPPED bit in here. Stop is defined as
- ;not being reentrant. We therefore save the old state of the bit and then
- ;restore it later. This keeps us from needing to DISABLE in flush. It also
- ;fails miserably if someone does a start in the middle of a flush. (A
- ;semaphore might help...)
-
-
- Flush
- PUTDEBUG 100,<'Flush: called'>
- movem.l d2/a1/a6,-(sp)
- movea.l md_SysLib(a6),a6
- bset #MDUB_STOPPED,MDU_FLAGS(a3)
- sne d2
-
- ReadFlush_Loop
- movea.l a3,a0
- mySYS GetMsg ;Steal messages from task's port
- tst.l d0
- beq.b WriteFlush_Loop
-
- movea.l d0,a1
- move.b #IOERR_ABORTED,IO_ERROR(a1)
- mySYS ReplyMsg
- bra.b ReadFlush_Loop
-
- WriteFlush_Loop
- lea mdu_wport(a3),a0
- mySYS GetMsg ;Steal messages from task's port
- tst.l d0
- beq.b Flush_End
-
- movea.l d0,a1
- move.b #IOERR_ABORTED,IO_ERROR(a1)
- mySYS ReplyMsg
- bra.b WriteFlush_Loop
-
- Flush_End
- move.l d2,d0
- movem.l (sp)+,d2/a1/a6
- tst.b d0
- bne.b 1$
- bsr InternalStart
- 1$
- PUTDEBUG 100,<'Flush: Finished!'>
- rts
-
- ;Here begins the task related routines
- ;
- ;A Task is provided so that queued requests may be processed at
- ;a later time. This is not very justifiable for a ram disk, but
- ;is very useful for "real" hardware devices. Take care with
- ;your arbitration of shared hardware with all the multitasking
- ;programs that might call you at once.
- ;
- ; Register Usage
- ; ==============
- ; a3 -- unit pointer
- ; a6 -- syslib pointer
- ; a5 -- device pointer
- ; a4 -- task (NOT process) pointer
- ; d7 -- wait mask
- ;----------------------------------------------------------------------
- ;
- ;Note: Signals must be allocated within this (the task's) context!
- ;The task is responsible for enabling the right DACIA interrupts...AFTER
- ;it has done its setup (like allocating signals).
- ;
- ;NOTE: We actually have two tasks, each with their own separate
- ; code (but shared data). First comes the read task...:
-
-
- cnop 0,4 ;Long word align
-
- ReadTask_Begin
- PUTDEBUG 200,<'ReadTask_Begin'>
- movea.l (SysBase).w,a6
-
- ;Grab the arguments passed down from our parent
-
- movea.l 4(sp),a3 ;Unit pointer
- movea.l mdu_Device(a3),a5 ;Point to device structure
-
- lea readsig(a3),a2
- moveq #1,d6 ;Number of signals to allocate-1
- ReadSigLoop
- moveq #-1,d0 ;-1 is any signal at all
- mySYS AllocSignal ;Allocate signals for I/O interrupts
- moveq #0,d7 ;Convert bit number signal mask
- bset d0,d7
- move.l d7,(a2)+ ;Save in unit structure
- dbra d6,ReadSigLoop
-
- moveq #-1,d0 ;-1 is any signal at all
- mySYS AllocSignal ;Allocate a signal
- move.b d0,MP_SIGBIT(a3)
- move.b #PA_SIGNAL,MP_FLAGS(a3) ;Make message port "live"
-
- ;Change the bit number into a mask, and save in d7
-
- moveq #0,d7 ;Clear D7
- bset d0,d7
-
- ifge INFO_LEVEL-40
- move.l $114(a6),-(sp)
- move.l a5,-(sp)
- move.l a3,-(sp)
- move.l d0,-(sp)
- PUTDEBUG 200,<'ReadTask -- Signal=%ld, Unit=%lx Device=%lx Task=%lx'>
- adda.l #4*4,sp
- endc
-
- ;Enable read-related ACIA interrupts
-
- move.l a5,-(sp)
- movea.l daciabase(a3),a5
- ori.b #READINTMASK,IERstate(a3)
- move.b #READINT,IER(a5)
- movea.l (sp)+,a5
- bra.b ReadTask_StartHere
-
- ;OK, kids, we are done with initialization. We now can start the main loop
- ;of the driver. It goes like this. Because we had the port marked PA_IGNORE
- ;for a while (in InitUnit) we jump to the getmsg code on entry. (The first
- ;message will probably be posted BEFORE our task gets a chance to run).
- ; wait for a message
- ; lock the device
- ; get a message. If no message, unlock device and loop
- ; dispatch the message
- ; loop back to get a message
-
- ;No more messages. Back ourselves out.
-
-
- ReadTask_Unlock
- andi.b #$ff&(~(UNITF_READACTIVE!UNITF_INREADTASK)),UNIT_FLAGS(a3)
-
- ;Main loop: wait for a new message
-
- ReadTask_MainLoop
- PUTDEBUG 200,<'ReadTask ++Sleep'>
- move.l d7,d0
- mySYS Wait
- ifge INFO_LEVEL-5
- bchg.b #1,($bfe001).l ;Blink the power LED
- endc
-
- ReadTask_StartHere
- PUTDEBUG 200,<'ReadTask ++Wakeup'>
- btst #MDUB_STOPPED,MDU_FLAGS(a3) ;See if we are stopped
- bne ReadTask_MainLoop ;Device is stopped, ignore messages
- bset #UNITB_READACTIVE,UNIT_FLAGS(a3) ;Lock the device
- bne ReadTask_MainLoop ;Device in use (immediate command?)
-
- ReadTask_NextMessage:
- movea.l a3,a0
- mySYS GetMsg ;Get the next request
- PUTDEBUG 200,<'ReadTask GotMsg'>
- tst.l d0
- beq ReadTask_Unlock ; no message?
-
- movea.l d0,a1 ;Do this request
- exg a5,a6 ;Put device ptr in right place
-
- myForbid
- btst #ioflagsB_Ignore,IO_FLAGS(a1)
- bne.b Readignorecmd
- bset #ioflagsB_Active,IO_FLAGS(a1)
- move.l a1,ReadRequestPtr(a3)
- myPermit
- bsr PerformIO ;Do it!
- PUTDEBUG 200,<'Read Did PerformIO'>
- bclr #ioflagsB_Active,IO_FLAGS(a1)
-
- ;No longer active - abort has stopped sending signals. Now we can
- ;(and should) clear the abort signal.
-
- moveq #0,d0
- move.l readabortsig(a3),d1
- myEXEC SetSignal
- bra.b Readconttl
-
- Readignorecmd
- myPermit
- clr.l IO_ACTUAL(a1)
- bsr TermIO
- PUTDEBUG 200,<'Read Did TermIO'>
- Readconttl
- exg a5,a6 ;Get ExecBase back in a6
- bra ReadTask_NextMessage
-
- ;**** End of read task code ****
-
- ;**** Beginning of write task code ****
-
- cnop 0,4 ;Long word align
-
- ; Register Usage
- ; ==============
- ; a3 -- unit pointer
- ; a6 -- syslib pointer
- ; a5 -- device pointer
- ; a4 -- task (NOT process) pointer
- ; d7 -- wait mask
-
- WriteTask_Begin
- PUTDEBUG 100,<'WriteTask_Begin'>
- movea.l (SysBase).w,a6
-
- ;Grab the arguments passed down from our parent
-
- movea.l 4(sp),a3 ;Unit pointer
- movea.l mdu_Device(a3),a5 ;Point to device structure
- lea tdresig(a3),a2
- moveq #4,d6 ;Number of signals to allocate-1
- WriteSigLoop
- moveq #-1,d0 ;-1 is any signal at all
- mySYS AllocSignal ;Allocate signals for I/O interrupts
- moveq #0,d7 ;Convert bit number signal mask
- bset d0,d7
- move.l d7,(a2)+ ;Save in unit structure
- dbra d6,WriteSigLoop
-
- move.l breaksig(a3),d0
- move.l d0,d1
- mySYS SetExcept ;Make breaksig an exception-causing signal
-
- ;Allocate a signal for the timer message port
-
- moveq #-1,d0 ;-1 is any signal at all
- mySYS AllocSignal
- lea timerport(a3),a2
- move.b d0,MP_SIGBIT(a2)
- move.l a2,timeriorequest+MN_REPLYPORT(a3)
- suba.l a1,a1
- mySYS FindTask
- move.l d0,MP_SIGTASK(a2)
- move.b #PA_SIGNAL,MP_FLAGS(a2) ;Make message port "live"
-
- ;Allocate a signal for the cmd message port
-
- moveq #-1,d0 ;-1 is any signal at all
- mySYS AllocSignal
- move.b d0,mdu_wport+MP_SIGBIT(a3)
- move.b #PA_SIGNAL,mdu_wport+MP_FLAGS(a3) ;Make message port "live"
-
- ;Change the bit number into a mask, and save in d7
-
- moveq #0,d7 ;Clear D7
- bset d0,d7
-
- ifge INFO_LEVEL-40
- move.l ThisTask(a6),-(sp)
- move.l a5,-(sp)
- move.l a3,-(sp)
- move.l d0,-(sp)
- PUTDEBUG 100,<'WriteTask -- Signal=%ld, Unit=%lx Device=%lx Task=%lx'>
- adda.l #4*4,sp
- endc
-
- ;Enable write-related ACIA interrupts
-
- move.l a5,-(sp)
- movea.l daciabase(a3),a5
- ori.b #HANDINTMASK,IERstate(a3)
- move.b #HANDINT,IER(a5)
- movea.l (sp)+,a5
- bra.b WriteTask_StartHere
-
- ;No more messages. Back ourselves out.
-
- WriteTask_Unlock
- andi.b #$ff&(~(UNITF_WRITEACTIVE!UNITF_INWRITETASK)),UNIT_FLAGS(a3)
-
- ;Main loop: wait for a new message
-
- WriteTask_MainLoop
- PUTDEBUG 100,<'WriteTask ++Sleep'>
- move.l d7,d0
- mySYS Wait
-
-
- ifge INFO_LEVEL-5
- bchg.b #1,($bfe001).l ;Blink the power LED
- endc
-
- WriteTask_StartHere
- PUTDEBUG 100,<'WriteTask ++Wakeup'>
- btst #MDUB_STOPPED,MDU_FLAGS(a3) ;See if we are stopped
- bne WriteTask_MainLoop ;Device is stopped, ignore messages
- bset #UNITB_WRITEACTIVE,UNIT_FLAGS(a3) ;Lock the device
- bne WriteTask_MainLoop ;Device in use (immediate command?)
-
- WriteTask_NextMessage:
- lea mdu_wport(a3),a0
- mySYS GetMsg ;Get the next request
- PUTDEBUG 100,<'WriteTask GotMsg'>
- tst.l d0
- beq WriteTask_Unlock ;No message?
- movea.l d0,a1 ;Do this request
- exg a5,a6 ;Put device ptr in right place
-
- myForbid
- btst #ioflagsB_Ignore,IO_FLAGS(a1)
- bne.b Writeignorecmd
- bset #ioflagsB_Active,IO_FLAGS(a1)
- move.l a1,WriteRequestPtr(a3)
- myPermit
- bsr PerformIO ;Do it!
- bclr #ioflagsB_Active,IO_FLAGS(a1)
-
- ;No longer active - abort has stopped sending signals. Now we can
- ;(and should) clear the abort signal.
-
- moveq #0,d0
- move.l writeabortsig(a3),d1
- myEXEC SetSignal
- bra.b Writeconttl
-
- Writeignorecmd
- myPermit
- clr.l IO_ACTUAL(a1)
- bsr TermIO
- Writeconttl
- exg a5,a6 ; get syslib back in a6
- bra WriteTask_NextMessage
-
- ;***** end of write task code *****
-
- ;Exception Handler (performs a break command)
- ;Note that this code acts as a handler for the write task only; the
- ;read task has no such handler.
- ;
- ;unit ptr in a1, execbase in a6
- ;All registers are saved/restored by Exec
- ;Note: Not all the usual newser register conventions are used here
-
- breakexception
- PUTDEBUG 100,<'BreakException: called'>
- move.l d0,-(sp) ;Save exception bits
- movea.l a1,a3
- movea.l a1,a2
- btst #UNITB_BREAKACTIVE,UNIT_FLAGS(a3) ;Sanity check
- beq endbreakex
- movea.l daciabase(a3),a5
- move.b #2,ACR(a5) ;start break
- lea timeriorequest(a3),a1
- move.w #TR_ADDREQUEST,IO_COMMAND(a1)
- movea.l mdu_prefs(a3),a4
- clr.l TV_SECS(a1)
- move.l prefs_BRKTIME(a4),d0
- ori.b #$FF,d0 ;To avoid the V33/V34 bug
- move.l d0,TV_MICRO(a1)
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- move.l d0,-(sp)
- PUTDEBUG 100,<'BreakException: TV_MICRO=%ld'>
- addq.l #4,sp
- endc
-
- movea.l MN_REPLYPORT(a1),a0
- move.b MP_SIGBIT(a0),d2
- jsr _LVOSendIO(a6)
-
- ifne INFO_LEVEL ;If any debugging enabled at all
- clr.l -(sp)
- move.b d2,3(sp)
- PUTDEBUG 100,<'BreakException: Waiting for signal #%ld'>
- addq.l #4,sp
- endc
-
- ;Take note - waiting within an exception is tricky!
-
- suba.l a1,a1
- jsr _LVOFindTask(a6)
- movea.l d0,a4 ;ThisTask
- moveq #0,d0
- bset d2,d0
- add.l writeabortsig(a3),d0
- move.l TC_SIGWAIT(a4),d3 ;Save this -- very important!!!
- jsr _LVOWait(a6)
- move.l d3,TC_SIGWAIT(a4) ;Restore -- very important!!!
- and.l writeabortsig(a3),d0
- beq.b breakOK
-
- PUTDEBUG 100,<'BreakException: Aborted!'>
-
- lea timeriorequest(a3),a1 ;The break was aborted. Clean up.
- jsr _LVOAbortIO(a6)
-
- lea timeriorequest(a3),a1
- jsr _LVOWaitIO(a6)
-
- breakOK
- move.b #0,ACR(a5)
- movea.l breakiorequest(a3),a1
- clr.b IO_ERROR(a1)
- btst #IOB_QUICK,IO_FLAGS(a1)
- bne.b endbreakex
- jsr _LVOReplyMsg(a6)
- endbreakex
- move.l (sp)+,d0 ;restore exception bits
- PUTDEBUG 100,<'BreakException: Finished!'>
- rts
-
- ;Initialize the device
-
- mdu_Init ;Initialize read task message port/tcb
- INITBYTE MP_FLAGS,PA_IGNORE ;Unit starts with a message port
- INITBYTE LN_TYPE,NT_MSGPORT
- INITLONG LN_NAME,Task1
- INITLONG mdu_rtcb+LN_NAME,Task1
- INITBYTE mdu_rtcb+LN_TYPE,NT_TASK
- INITBYTE mdu_rtcb+LN_PRI,5
-
- ;Initialize write task message port/tcb
-
- INITBYTE mdu_wport+MP_FLAGS,PA_IGNORE ;Unit starts with a message port
- INITBYTE mdu_wport+LN_TYPE,NT_MSGPORT
- INITLONG mdu_wport+LN_NAME,Task2
- INITLONG mdu_wtcb+LN_NAME,Task2
- INITBYTE mdu_wtcb+LN_TYPE,NT_TASK
- INITBYTE mdu_wtcb+LN_PRI,126
-
- ;Initialize interrupt structure (except for is_data of course).
-
- INITBYTE mdu_is+LN_TYPE,NT_INTERRUPT
- INITBYTE mdu_is+LN_PRI,-127 ;Int priority 11
- INITLONG mdu_is+IS_CODE,int ;Interrupt routine addr
- INITLONG mdu_is+LN_NAME,LibName
-
- ;Initialize timer message port
-
- INITBYTE timerport+MP_FLAGS,PA_IGNORE ;Unit starts with a message port
- INITBYTE timerport+LN_TYPE,NT_MSGPORT
- INITLONG timerport+LN_NAME,LibName
-
- dc.w 0
-
- ;******************** Interrupt code *********************************
-
- ;Notes:
- ;
- ;Guru's Guide #1: Interrupts is helpful.
- ;------ ----- --- ----------
- ;
- ;NOTE: I am using one interrupt server for each unit. I think it's
- ;simpler then using one each for the read and write tasks.
- ;
- ;This is the interrupt server code. It serves four purposes:
- ;1. Read in a byte if available, store it, and signal the read task
- ;2. Check for TDRE-empty condition and signal write task
- ;3. Check for a DSR* transition and signal write task IF handshake is wanted
- ;4. Check for an exceptional condition and signal read task
- ;
- ;Enter with data pointer in a1 (unit pointer)
- ;d0, d1, a0, a1, a5, and a6 are scratch
- ;
- ;Returns Z bit clear if the interrupt was not caused by the dacia,
- ;otherwise return with the Z bit set.
-
- int
- move.l a3,-(sp)
- move.w d7,-(sp)
- movea.l a1,a3
- movea.l daciabase(a3),a5
- move.b ISR(a5),d7 ;This clears CTST, DCDT, and DSRT ints
- and.b IERstate(a3),d7 ;Quick check (note that this masking is very
- ;important)
- bne.b serveint ;Yes, service the dacia
- move.w (sp)+,d7 ;Not for us
- movea.l (sp)+,a3
- PUTDEBUG 200,<'Int: Not for us!'>
- moveq #0,d0
- rts
-
- serveint
- ifne INFO_LEVEL ;If any debugging enabled at all
- clr.l -(a7)
- move.b d7,3(a7)
- PUTDEBUG 200,<'Int: Entered with ISR=%lx'>
- addq.l #4,sp
- endc
-
- ;Read error condition?
- ;(We must check this first because a read of the RDR clears the error flags.)
-
- move.b d7,d0
- and.b #6,d0
- beq.b contint ;No errors
-
- PUTDEBUG 200,<'Int: Read error condition.'>
-
- ;An exceptional condition occured - signal the read task
-
- move.b d7,ISRcopy(a3) ;Useful info
- move.b CSR(a5),CSRcopy(a3) ;More useful info
- move.l readabortsig(a3),d0
- lea mdu_rtcb(a3),a1
- movea.l mdu_SysLib(a3),a6
- jsr _LVOSignal(a6)
-
- move.b RDR(a5),d0 ;This forces a clear of the error bits
- bra rdrfempty
-
- contint
- btst #ISRB_RDRF,d7 ;Test Receive Data Buffer Full
- beq.b rdrfempty
-
- PUTDEBUG 200,<'Int: Read.'>
-
-
- ;Store the byte in the circular buffer. Note that this code *can't* be
- ;interrupted, so we're safe (that's because CIA B, which we're plugging into,
- ;is connected to INT6*).
-
- movea.l tail(a3),a0 ;Read and store
- move.b RDR(a5),(a0)+
- cmpa.l endbuf(a3),a0
- bls.b notend ;endbuf >= tail
- movea.l startbuf(a3),a0 ;Wrap around
- notend
- move.l a0,tail(a3)
- move.l head(a3),d0
- cmpa.l d0,a0
- bne.b nocross
- bset #MDUB_V,MDU_FLAGS(a3) ;set buf overflow flag
- addq.l #1,d0
- move.l d0,head(a3)
- cmp.l endbuf(a3),d0
- bls.b nocross
- move.l startbuf(a3),head(a3)
- nocross
- movea.l mdu_SysLib(a3),a6
- move.l readsig(a3),d0
- lea mdu_rtcb(a3),a1
- jsr _LVOSignal(a6) ;Signal the read task
-
- rdrfempty
- btst #ISRB_TDRE,d7 ;Check TDRE
- beq.b notdre
-
- PUTDEBUG 200,<'Int: Write signal.'>
- andi.b #WRITEOFFMASK,IERstate(a3)
- move.b #WRITEOFF,IER(a5)
- move.l tdresig(a3),d0
- lea mdu_wtcb(a3),a1
- movea.l mdu_SysLib(a3),a6
- jsr _LVOSignal(a6) ;Signal the write task
-
- ;see if handshaking ("7 wire") is enabled
-
- notdre
- movea.l mdu_prefs(a3),a6
- btst #SERB_XDISABLED,prefs_SERFLAGS(a6)
- bne.b nohand
-
- btst #ISRB_DSRT,d7 ;Check for DSR* transition
- beq.b nohand
-
- PUTDEBUG 200,<'Int: DSR transition.'>
-
- move.l dsrsig(a3),d0
- lea mdu_wtcb(a3),a1
- movea.l mdu_SysLib(a3),a6
- jsr _LVOSignal(a6) ;Signal the write task
-
- nohand:
- termchain
- PUTDEBUG 200,<'Int: Finished!'>
- move.w (sp)+,d7
- movea.l (sp)+,a3
- moveq #1,d0 ;Terminate the chain
- rts
-
-
- defaultprefs
- dc.l SER_DEFAULT_CTLCHAR ;prefs_CTLCHAR
- dc.l 16*1024 ;prefs_RBUFLEN
- dc.l 0 ;prefs_EXTFLAGS
- dc.l 1200 ;prefs_BAUD
- dc.l 250000 ;prefs_BRKTIME
- dc.l 0 ;prefs_TERMARRAY
- dc.l 0 ;prefs_TERMARRAY
- dc.b 8 ;prefs_READLEN
- dc.b 8 ;prefs_WRITELEN
- dc.b 1 ;prefs_STOPBITS
- dc.b $88 ;prefs_SERFLAGS
-
- ;Table of DACIA base addresses for the 4 units
-
- basetable
- dc.l ACIA_Base
- dc.l ACIA_Base+UNIT2
- dc.l ACIA_Base+ACIA1
- dc.l ACIA_Base+ACIA1+UNIT2
-
- ;List of supported baud rates
-
- baudtable
- dc.w 50
- dc.w 110
- dc.w 135
- dc.w 150
- dc.w 300
- dc.w 600
- dc.w 1200
- dc.w 1800
- dc.w 2400
- dc.w 3600
- dc.w 4800
- dc.w 7200
- dc.w 9600
- dc.w 19200
- dc.w 38400
- dc.w 31250 ;MIDI (external clock)
-
-
- cmdtable
- dc.l Invalid ;$00000001 ;0 CMD_INVALID
- dc.l MyReset ;$00000002 ;1 CMD_RESET
- dc.l Read ;$00000004 ;2 CMD_READ
- dc.l Write ;$00000008 ;3 CMD_WRITE
- dc.l Invalid ;$00000010 ;4 CMD_UPDATE (update has no meaning here)
- dc.l MyClear ;$00000020 ;5 CMD_CLEAR
- dc.l MyStop ;$00000040 ;6 CMD_STOP
- dc.l Start ;$00000080 ;7 CMD_START
- dc.l Flush ;$00000100 ;8 CMD_FLUSH
- dc.l Query ;$00000200 ;9 SDCMD_QUERY
- dc.l Break ;$00000400 ;10 SDCMD_BREAK
- dc.l SetParams ;$00000800 ;11 SDCMD_SETPARAMS
- dc.l $FFFFFFFF ;$00001000 ;12 NULL
- dc.l $FFFFFFFF ;$00002000 ;13 NULL
- dc.l $FFFFFFFF ;$00004000 ;14 NULL
- dc.l $FFFFFFFF ;$00008000 ;15 NULL
- dc.l SetCtrlLines ;$00010000 ;16 SIOCMD_SETCTRLLINES
- cmdtable_end:
-
- ISDEBUG 'newser' ;This name for debugging use
-
- dc.l 0
-
- LibEnd:
- END
-
-
-