home *** CD-ROM | disk | FTP | other *** search
Modula Implementation | 1987-10-17 | 13.8 KB | 464 lines |
- IMPLEMENTATION MODULE RS232;
-
- (* (C) Copyright 1987 Fitted Software Tools. All rights reserved.
-
- This module is part of the example multitasking communications program
- provided with the Fitted Software Tools' Modula-2 development system.
-
- Registered users may use this program as is, or they may modify it to
- suit their needs or as an exercise.
-
- If you develop interesting derivatives of this program and would like
- to share it with others, we encourage you to upload a copy to our BBS.
- *)
-
- (* $L+ *)
- (* $S-, $R-, $T- *)
-
- FROM SYSTEM IMPORT ASSEMBLER, ADR, ADDRESS, SEG, OFS;
- FROM System IMPORT TermProcedure, GetVector, ResetVector;
- FROM Storage IMPORT ALLOCATE, DEALLOCATE, Available;
- FROM ASCII IMPORT CtrlS, CtrlQ;
- FROM Kernel IMPORT InitSignal, NewProcess, Signal, WaitIO;
-
- CONST
- Com1IntNo = 12; (* COM1 interrupt vector *)
- Com2IntNo = 11; (* COM2 interrupt vector *)
-
- (* 8250 ports *)
- RX = 0; (* xmit reg. when DLAB = 0 *)
- TX = 0; (* rcv reg. when DLAB = 0 *)
- DivL = 0; (* baud rate divisor when DLAB = 1 *)
- DivH = 1; (* " " " when DLAB = 1 *)
- IntEna = 1; (* interrupt enable *)
- IntId = 2; (* interrupt id *)
- LCR = 3; (* line status register *)
- MCR = 4; (* modem control register *)
- LSR = 5; (* line status register *)
- MSR = 6; (* modem status register *)
-
- TYPE BufferPointer = POINTER TO ARRAY [0..65531] OF CHAR;
-
- VAR ComPort :CARDINAL; (* Com port in use *)
- ComIntNo :CARDINAL; (* Int # for port in use *)
- ComBase :CARDINAL; (* base io adrs for port in use *)
- ComRX :CARDINAL; (* receive register *)
- ComLSR :CARDINAL; (* line status register *)
-
- OldCom1Int :ADDRESS; (* save old ISR adrs here *)
- OldCom2Int :ADDRESS;
- Com1Base :CARDINAL; (* base ports of 8250 ACA *)
- Com2Base :CARDINAL;
-
- ISR1active :BOOLEAN; (* ISR process created? *)
- ISR2active :BOOLEAN;
-
- (* Receive buffer *)
- BuffPtr :BufferPointer;
- BuffSize :CARDINAL;
- InPtr :CARDINAL;
- OutPtr :CARDINAL;
- InCount :CARDINAL;
-
- xon :BOOLEAN; (* use xon/off ? *)
- XoffThreshold :CARDINAL;
- XonThreshold :CARDINAL;
- XoffSent :BOOLEAN;
-
-
- MODULE RS232Receiver[3];
- (*
- RS232Receiver runs at priority 3. The actual COM priority is
- either 3 or 4, depending on the port in use.
- *)
-
- IMPORT ASSEMBLER, WaitIO, Signal, RS232Input,
- ComIntNo, ComBase, LSR, RX, TX, MCR, LCR, IntEna,
- BuffSize, BuffPtr, InCount, InPtr, xon, CtrlS,
- XoffThreshold, XoffSent;
-
- EXPORT ComInt1, ComInt2, StartReading, StopReading;
-
- (*
- Even though we have one ISR for each COM port, only one
- of those ports may be active at any one time!
- *)
- PROCEDURE ComInt1;
- (*
- ISR for COM1
- *)
- BEGIN
- StartReading;
- LOOP
- WaitIO( 12 );
- ASM
- MOV BX, ComBase (* read line status reg *)
- LEA DX, [BX+LSR]
- IN AL, DX
- TEST AL, 1 (* input character? *)
- JZ exit
- LEA DX, [BX+RX] (* grab input character *)
- IN AL, DX
- MOV CX, InCount (* does it fit in the buffer? *)
- CMP CX, BuffSize
- JAE exit
- LES DI, BuffPtr (* buffer with it! *)
- MOV BX, InPtr
- MOV ES:[DI+BX], AL
- INC InCount (* adjust counters *)
- INC BX
- CMP BX, BuffSize
- JB ok
- XOR BX, BX
- ok: MOV InPtr, BX
- TEST xon, 1 (* xon/xoff enabled ? *)
- JZ exit
- CMP CX, XoffThreshold
- JB exit
- TEST XoffSent, 1
- JNZ exit
- MOV BX, ComBase (* send XOFF is xmit reg empty *)
- LEA DX, [BX+LSR]
- IN AL, DX
- TEST AL, 20H
- JZ exit
- MOV AL, CtrlS
- LEA DX, [BX+TX]
- OUT DX, AL
- MOV XoffSent, 1
-
- exit:
- MOV AL, 20H (* send EOI *)
- OUT 20H, AL
- END;
- IF InCount > 0 THEN Signal( RS232Input ) END;
- END;
- END ComInt1;
-
-
- PROCEDURE ComInt2;
- (*
- ISR for COM2
- *)
- BEGIN
- StartReading;
- LOOP
- WaitIO( 11 );
- ASM
- MOV BX, ComBase (* read line status reg *)
- LEA DX, [BX+LSR]
- IN AL, DX
- TEST AL, 1 (* input character? *)
- JZ exit
- LEA DX, [BX+RX] (* grab input character *)
- IN AL, DX
- MOV CX, InCount (* does it fit in the buffer? *)
- CMP CX, BuffSize
- JAE exit
- LES DI, BuffPtr (* buffer with it! *)
- MOV BX, InPtr
- MOV ES:[DI+BX], AL
- INC InCount (* adjust counters *)
- INC BX
- CMP BX, BuffSize
- JB ok
- XOR BX, BX
- ok: MOV InPtr, BX
- TEST xon, 1 (* xon/xoff enabled ? *)
- JZ exit
- CMP CX, XoffThreshold
- JB exit
- TEST XoffSent, 1
- JNZ exit
- MOV BX, ComBase (* send XOFF is xmit reg empty *)
- LEA DX, [BX+LSR]
- IN AL, DX
- TEST AL, 20H
- JZ exit
- MOV AL, CtrlS
- LEA DX, [BX+TX]
- OUT DX, AL
- MOV XoffSent, 1
-
- exit:
- MOV AL, 20H (* send EOI *)
- OUT 20H, AL
- END;
- IF InCount > 0 THEN Signal( RS232Input ) END;
- END;
- END ComInt2;
-
-
- VAR Reading :BOOLEAN;
-
- PROCEDURE StartReading;
- BEGIN
- ASM
- MOV BX, ComBase (* clear all 8250 registers *)
- LEA DX, [BX+LSR]
- IN AL, DX
- LEA DX, [BX+RX]
- IN AL, DX
- LEA DX, [BX+MCR]
- IN AL, DX
-
- MOV BX, ComBase
- LEA DX, [BX+IntEna] (* enable receiver interrupts *)
- MOV AL, 5
- OUT DX, AL
- JMP slownow
- slownow:
-
- LEA DX, [BX+MCR] (* modem control *)
- MOV AL, 0BH (* out2 + RTS + DTR *)
- OUT DX, AL
- JMP slowagain
- slowagain:
-
- LEA DX, [BX+RX] (* read data reg just in case... *)
- IN AL, DX
- END;
- Reading := TRUE;
- END StartReading;
-
-
- PROCEDURE StopReading;
- BEGIN
- IF Reading THEN
- ASM
- MOV BX, ComBase (* disable 8250 interrupts *)
- LEA DX, [BX+IntEna]
- MOV AL, 0
- OUT DX, AL
- END;
- Reading := FALSE;
- XoffSent := FALSE;
- END;
- END StopReading;
-
- BEGIN
- Reading := FALSE;
- END RS232Receiver;
-
-
-
- PROCEDURE Init( portNumber :CARDINAL; (* 1 or 2 *)
- baudRate :CARDINAL; (* 300..38400 *)
- nStopBits :CARDINAL; (* 1 or 2 *)
- parityEnable :BOOLEAN;
- evenParity :BOOLEAN;
- charSize :CARDINAL; (* 5..8 *)
- inBufferSize :CARDINAL; (* input buffer size *)
- VAR done :BOOLEAN (* success *)
- );
- VAR lcr :BITSET;
- baud :CARDINAL;
- BEGIN
- StopReading;
- IF BuffSize > 0 THEN DEALLOCATE( BuffPtr, BuffSize ) END;
- IF portNumber = 1 THEN
- ComBase := Com1Base;
- ComIntNo := Com1IntNo;
- ELSIF portNumber = 2 THEN
- ComBase := Com2Base;
- ComIntNo := Com2IntNo;
- ELSE done := FALSE; RETURN
- END;
- ComPort := portNumber;
- ComRX := ComBase + RX;
- ComLSR := ComBase + LSR;
- lcr := BITSET( charSize - 5 );
- IF nStopBits > 1 THEN lcr := lcr + {2} END;
- IF parityEnable THEN lcr := lcr + {3} END;
- IF evenParity THEN lcr := lcr + {4} END;
- IF baudRate MOD 50 = 0 THEN
- baud := 2304 DIV (baudRate DIV 50);
- ELSIF baudRate = 75 THEN
- baud := 1536
- ELSIF baudRate = 110 THEN
- baud := 1047
- ELSE
- done := FALSE;
- RETURN
- END;
- BuffSize := inBufferSize;
- InPtr := 0;
- OutPtr := 0;
- InCount := 0;
- IF Available( BuffSize ) THEN
- ALLOCATE( BuffPtr, BuffSize )
- ELSE
- done := FALSE;
- RETURN
- END;
- XoffThreshold := BuffSize DIV 4 * 3;
- XonThreshold := BuffSize DIV 2;
- ASM
- MOV BX, ComBase
- LEA DX, [BX+LCR] (* DLAB := 1 *)
- MOV AL, 80H
- OUT DX, AL
-
- MOV AX, baud
- LEA DX, [BX+DivL] (* set divisor *)
- OUT DX, AL
- JMP slow (* for PC/AT *)
- slow:
- INC DX
- MOV AL, AH
- OUT DX, AL
- JMP slow2
- slow2:
-
- LEA DX, [BX+LCR] (* line control *)
- MOV AL, lcr
- OUT DX, AL
- END;
- IF (portNumber = 1) & NOT ISR1active THEN
- NewProcess( ComInt1, 512, TRUE ); (* create the ISR process *)
- ISR1active := TRUE;
- ELSIF NOT ISR2active THEN
- NewProcess( ComInt2, 512, TRUE );
- ISR1active := TRUE;
- END;
- done := TRUE;
- END Init;
-
-
- PROCEDURE ResetPars( baudRate :CARDINAL; (* 300..38400 *)
- nStopBits :CARDINAL; (* 1 or 2 *)
- parityEnable :BOOLEAN;
- evenParity :BOOLEAN;
- charSize :CARDINAL; (* 5..8 *)
- VAR done :BOOLEAN (* success *)
- );
- VAR lcr :BITSET;
- baud :CARDINAL;
- BEGIN
- StopReading;
- ComRX := ComBase + RX;
- ComLSR := ComBase + LSR;
- lcr := BITSET( charSize - 5 );
- IF nStopBits > 1 THEN lcr := lcr + {2} END;
- IF parityEnable THEN lcr := lcr + {3} END;
- IF evenParity THEN lcr := lcr + {4} END;
- IF baudRate MOD 50 = 0 THEN
- baud := 2304 DIV (baudRate DIV 50);
- ELSIF baudRate = 75 THEN
- baud := 1536
- ELSIF baudRate = 110 THEN
- baud := 1047
- ELSE
- done := FALSE;
- RETURN
- END;
- ASM
- MOV BX, ComBase
- LEA DX, [BX+LCR] (* DLAB := 1 *)
- MOV AL, 80H
- OUT DX, AL
-
- MOV AX, baud
- LEA DX, [BX+DivL] (* set divisor *)
- OUT DX, AL
- JMP slow (* for PC/AT *)
- slow:
- INC DX
- MOV AL, AH
- OUT DX, AL
- JMP slow2
- slow2:
-
- LEA DX, [BX+LCR] (* line control *)
- MOV AL, lcr
- OUT DX, AL
- END;
- StartReading;
- done := TRUE;
- END ResetPars;
-
-
- PROCEDURE GetCom( VAR ch :CHAR; VAR received :BOOLEAN );
- BEGIN
- IF InCount > 0 THEN
- ch := BuffPtr^[OutPtr];
- received := TRUE;
- INC( OutPtr );
- IF OutPtr >= BuffSize THEN OutPtr := 0 END;
- ASM (* guarantee single 8086 instruction *)
- DEC InCount
- END;
- ELSE
- ch := 0C;
- received := FALSE;
- END;
- IF XoffSent & (InCount < XonThreshold) THEN
- PutCom( CtrlQ );
- XoffSent := FALSE;
- END;
- END GetCom;
-
-
- PROCEDURE PutCom( ch :CHAR );
- BEGIN
- ASM
- MOV BX, ComBase
- LEA DX, [BX+LSR]
- wait: (* wait for tx buffer to empty *)
- IN AL, DX
- TEST AL, 20H
- JZ wait
- MOV AL, ch
- LEA DX, [BX+TX]
- OUT DX, AL
- END;
- END PutCom;
-
-
- PROCEDURE XON;
- BEGIN
- xon := TRUE;
- END XON;
-
-
- PROCEDURE XOFF;
- BEGIN
- xon := FALSE;
- IF XoffSent THEN PutCom( CtrlQ ); XoffSent := FALSE END;
- END XOFF;
-
-
- PROCEDURE Stop;
- BEGIN
- StopReading;
- ResetVector( Com1IntNo, OldCom1Int );
- ResetVector( Com2IntNo, OldCom2Int );
- END Stop;
-
-
- VAR p :POINTER TO CARDINAL;
- ok :BOOLEAN;
-
- BEGIN
- (* get COM port addresses from BIOS *)
- p.SEG := 40H; (* BIOS DATA segment *)
- p.OFS := 0; Com1Base := p^;
- p.OFS := 2; Com2Base := p^;
-
- (* initialize general variables *)
- XoffSent := FALSE;
- BuffSize := 0;
- xon := FALSE;
-
- ISR1active := FALSE;
- ISR2active := FALSE;
-
- (* save current COM ISRs *)
- GetVector( Com1IntNo, OldCom1Int );
- GetVector( Com2IntNo, OldCom2Int );
- TermProcedure( Stop );
-
- (* setup process stuff *)
- InitSignal( RS232Input ); (* Inititalize our signal *)
-
- END RS232.