.TITLE MO V04.00 .IDENT /V04.00/ .enabl lc ; ; RT-11 SPECIAL MODEM DRIVER FOR TSX COMMUNICATIONS ; ; Rev 8209.021 ; ; Note: To use this driver with TSX you must also create an ; RT-11 driver MO.SYS on your system. If you do not ; have memory management support included in your ; RT drivers, then change the value of MMG$T in the ; following statement to 0. (It must still be 1 for ; the TSX driver MO.TSX.) ; MMG$T = 0 ;18-BIT I/O ; ; This handler was written to allow a user to communicate through ; a serial (modem) port from TSX at up to 1200 baud. It does ; not contain any special modem support such as automatic dial-up. ; ; It is assumed that this handler will always be used in ; conjunction with a program to provide modem services, but the ; handler will work with system service routines as a write-only ; handler. On a write request the handler moves the data to an ; internal buffer, starts the transfer, and returns with a ; completion call. This is done so that the special read mode ; can process requests immediately. Normally read requests cause ; no action, but the special read mode is entered by envoking ; special function "377. This causes all data from the modem ; to be put in an internal buffer of 256 bytes. Then each time ; a read request is made the handler returns immediately with as ; much valid data from the internal read buffer as it can transfer. ; The first two bytes of the data returned have a special meaning. ; The first byte is status information. If it is nonzero, then ; the internal read buffer overflowed. The second byte is the ; number of valid bytes of data to follow. Any additional bytes ; in the user buffer after the valid bytes will contain garbage. ; The special read mode should be terminated by envoking special ; function "376 before the user program exits. ; ; The special function "375 may be used to see if a write is ; in progress. A non-zero value returned in the first byte of the ; user buffer indicates a write is in progress. ; ; The modem CSR and interrupt vector addresses are specified ; in the .DRDEF call on the next page. If your modem interface ; is at a different address, all you need to do is change the ; values in that call to the correct ones. ; ; Created by: Robert Walraven ; Applied Science ; University of California, Davis 95616 ; (916) 752-0360 .SBTTL MACRO DEFINITIONS .MACRO ADDR ADDRESS,REG MOV PC,REG ADD #ADDRESS-.,REG .ENDM .SBTTL PREAMBLE SECTION .MCALL .DRDEF .DRDEF MO,377,HNDLR$!SPFUN$,0,176560,360 .QELDF .SBTTL REGISTER DEFINITIONS MR$CSR == MO$CSR ;READ CONTROL REGISTER MR$VEC == MO$VEC ;READ VECTOR MRB == MR$CSR+2 ;READ DATA BUFFER MW$VEC == MR$VEC+4 ;WRITE VECTOR MW$CSR == MRB+2 ;WRITE CONTROL REGISTER MWB == MW$CSR+2 ;WRITE DATA BUFFER .SBTTL SPECIAL FUNCTION VALUES RD$ON = 377 ;Turn read interrupt on RD$OF = 376 ;Turn read interrupt off WRT$Q = 375 ;Write in progress query .SBTTL OTHER VALUES CTRLS = 23 ;Control-S code (XOFF) CTRLQ = 21 ;Control-Q code (XON) RMIN = 100. ;If XOFF, then issue XON when number of ; characters in read buffer falls below RMIN. RMAX = 200. ;If XON, then issue XOFF when number of ; characters in read buffer exceeds RMAX. .SBTTL HEADER SECTION .DRBEG MO .SBTTL I/O INITIATION SECTION MOV MOCQE,R4 ;R4 points to CQE MOVB Q$FUNC(R4),R5 ;Pick up special function BNE SPFUN ;Branch if it is special MOV Q$WCNT(R4),R5 ;Get word count ASL R5 ;Make word count a byte count BCS MW ;If negative, WRITE BNE MR ;IF non-zero, READ JMP MOFIN ;IF zero , SEEK MR: TST READFL ;Special read mode? BEQ RDDONE ;Return if not TST -(R5) ;Subtract 2 from byte count BLT RDDONE ;Return if invalid length MOV NEXT,R3 ;R3 = address of read buffer MOV RDPTR,R2 ;R2 = address of last byte +1 SUB R3,R2 ;R2 = # of bytes available TST R2 ;Check for negative value BGE 5$ ;Branch if positive ADD #RLEN,R2 ;Else correct for wrap around 5$: CMP R2,R5 ;Compare with room in user buffer BLOS 10$ ;Branch if enough room MOV R5,R2 ;Shorten # of bytes to move 10$: .IF EQ MMG$T ;If memory management off MOVB OVFFLG,@Q$BUFF(R4) ;Load status in byte 1 INC Q$BUFF(R4) ;Increment buffer pointer MOVB R2,@Q$BUFF(R4) ;Put bytes available in byte 2 INC Q$BUFF(R4) ;Increment buffer pointer TST R2 ;Any bytes to move? BEQ RDDONE ;Branch if not 20$: MOVB (R3)+,@Q$BUFF(R4) ;Put char in user buffer INC Q$BUFF(R4) ;Increment buffer pointer .IFF ;If memory management on MOVB OVFFLG,-(SP) ;Put status on stack JSR PC,@$PTBYT ;Move to user buffer MOVB R2,-(SP) ;Put bytes available on stack JSR PC,@$PTBYT ;Move to user buffer TST R2 ;Any bytes to move? BEQ RDDONE ;Branch if not 20$: MOVB (R3)+,-(SP) ;Put char on stack JSR PC,@$PTBYT ;Move to user buffer .ENDC DEC RCOUNT ;Decrease read buffer byte count CMP R3,RDBEND ;Past end of buffer? BLO 30$ ;Branch if not SUB #RLEN,R3 ;Else wrap around 30$: SOB R2,20$ ;Loop until done TST XFLAG ;Has XOFF been issued? BEQ 40$ ;Branch if not CMP RCOUNT,#RMIN ;Are we below lower fence? BGE 40$ ;Branch if not MOV #CTRLQ,XCHAR ;Send XON character BIS #100,@#MW$CSR ; using interrupt service CLR XFLAG ;Indicate XON mode 40$: CLR OVFFLG ;Clear for next time MOV R3,NEXT ;Reset next available byte pointer RDDONE: CLR Q$WCNT(R4) ;Clear the word count BR FINISH ;ELSE QUIT IMMEDIATELY WAIT: RTS PC MW: TST WRITFL ;Write already in progress? BNE WAIT ; If so, return immediately INC WRITFL ;Turn write flag on ASL Q$WCNT(R4) ;Make word count a byte count MOV Q$WCNT(R4),WCOUNT ;Save byte count ADDR WRTBUF,R5 ;R5 = address of WRTBUF MOV R5,WRTPTR ;Store it in WRTPTR MOV #WLEN,R3 ;R3 = Length of write buffer .IF EQ MMG$T ;If no memory management ADD #Q$WCNT,R4 ;Point to word count 50$: TST (R4) ;More chars to output? BEQ 60$ ; Branch if not INC (R4) ;Dec byte count (It's negative) MOVB @-(R4),(R5)+ ;Move char to internal buffer INC (R4)+ ;Bump pointer .IFF ;If memory management 50$: TST Q$WCNT(R4) ;More chars to output? BEQ 60$ ; Branch if not INC Q$WCNT(R4) ;Dec byte count (It's negative) JSR PC,@$GTBYT ;Get byte from user MOVB (SP)+,(R5)+ ;Put in internal buffer .ENDC DEC R3 ;Dec buffer count BNE 50$ ;Branch if still room MOV #WLEN,WCOUNT ;Else shorten count and go NEG WCOUNT ;After negating count 60$: TSTB -(R5) ;Was the last char zero? BNE 70$ ;Branch if not INC WCOUNT ;Else back up one char 70$: BIS #100,@#MW$CSR ;Enable interrupt FINISH: JMP MOFIN ;Clear queue element SPFUN: CMPB R5,#RD$ON ;Turn on special read mode? BNE 80$ ;Branch if no ADDR RDBUFR,R3 ;R3 = address of read buffer MOV R3,RDPTR ;Put it in RDPTR MOV R3,NEXT ;And in next available byte pointer ADDR RDBEND,R3 ;R3 = address of end of buffer +1 MOV R3,RDBEND ;Put it in RDBEND INC READFL ;Set special read mode on BIS #100,@#MR$CSR ;Enable read interrupts BR 90$ ;Clean up XON/XOFF stuff 80$: CMPB R5,#RD$OF ;Turn off special read mode? BNE 100$ ;Branch if no CLR @#MR$CSR ;Disable read interrupts CLR READFL ;Turn off special read mode 90$: CLR RCOUNT ;Clear read buffer byte count CLR XFLAG ;Clear XON/XOFF flag CLR XCHAR ;Clear XON/XOFF character BR FINISH ;Return to user 100$: CMPB R5,#WRT$Q ;Write in progress query? BNE FINISH ;Branch if not .IF EQ MMG$T ;If memory management off MOVB WRITFL,@Q$BUFF(R4) ;Return write status flag .IFF ;If memory management on MOVB WRITFL,-(SP) ;Put flag on stack JSR PC,@$PTBYT ;Move it to user buffer .ENDC BR FINISH ;Return to user .SBTTL INTERRUPT SERVICE SECTION ;WRITE-READ VECTOR TABLE .DRVTB MO,MR$VEC,MOINT .DRVTB ,MW$VEC,MWINT ;WRITE INTERRUPT SERVICE .DRAST MW,4,MOABRT TST XCHAR ;Is there an XON or XOFF to send? BEQ 1$ ;Branch if not MOVB XCHAR,@#MWB ;Otherwise send it CLR XCHAR ;And clear XCHAR for next time RTS PC 1$: TST WCOUNT ;More chars to output? BEQ MWDONE ;If not, branch INC WCOUNT ;Dec byte count (it is negative) MOV WRTPTR,R4 ;Point to next char INC WRTPTR ;Increment pointer MOVB (R4),@#MWB ;Write the character RTS PC MWDONE: CLR @#MW$CSR ;Disable interrupts CLR WRITFL ;Clear write flag RTS PC ; READ INTERRUPT SERVICE .DRAST MO,4,MOABRT MOV RDPTR,R5 ;R5 = address for next byte MOVB @#MRB,(R5) ;Move char to read buffer INC RCOUNT ;Increment read buffer byte count CMP RCOUNT,#RMAX ;Is it time to send XOFF? BLE 10$ ;Branch if not TST XFLAG ;Has an XOFF already been sent? BNE 10$ ;Branch if it has MOV #CTRLS,XCHAR ;Otherwise send the XOFF BIS #100,@#MW$CSR ; using interrupt handling INC XFLAG ;Indicate XOFF mode 10$: INC R5 ;Advance pointer CMP R5,RDBEND ;Beyond end of buffer? BLO 20$ ; Branch if not SUB #RLEN,R5 ; Else wrap around 20$: CMP R5,NEXT ;Out of room in buffer? BNE 30$ ;Branch if not INC OVFFLG ;Flag an overflow RTS PC ;and return 30$: MOV R5,RDPTR ;Save updated pointer RTS PC MOABRT: CLR @#MW$CSR ;TURN OFF INTERRUPTS CLR @#MR$CSR CLR WRITFL ;Clear write flag CLR READFL ;Clear read flag RTS PC .SBTTL DATA STORAGE RDBUFR: .BLKB 256. ;Special read mode buffer RDBEND: 0 ;End of buffer address put here RLEN = RDBEND-RDBUFR ;Length of read buffer RDPTR: 0 ;Pointer to next free byte RCOUNT: 0 ;Number of chars in read buffer XFLAG: 0 ;XON/XOFF flag XCHAR: 0 ;XON/XOFF character to send NEXT: 0 ;Pointer to next available byte OVFFLG: 0 ;Overflow flag READFL: 0 ;Special read mode flag WRITFL: 0 ;Write in progress flag WCOUNT: 0 ;-(# of bytes to write) WRTPTR: 0 ;Pointer to next byte to write WRTBUF: .BLKB 256. ;Write buffer WBFEND: WLEN = WBFEND-WRTBUF .SBTTL I/O COMPLETION SECTION MOFIN: .DRFIN MO ;GO TO I/O COMPLETION .DREND MO .END