home *** CD-ROM | disk | FTP | other *** search
- {$R-,S-}
- unit ComIO;
- { Low level serial interface routines using inline assembler
- for interrupt service }
-
- Interface
- uses Dos, Crt;
-
- const
- Baud300 = $40; Baud1200 = $80; Baud2400 = $A0;
- Baud4800 = $C0; Baud9600 = $E0;
- EvenParity = $18; OddParity = $08; NoParity = $00;
- WordSize7 = $02; WordSize8 = $03;
- StopBits1 = $04; StopBits2 = $00;
- COM1Port = $00; COM2Port = $01;
-
- procedure ComInit(BufSize: Word);
- procedure ComDone;
- function ComAvail: Boolean;
- function ComGet: Byte;
- function ComOverflow: Boolean;
- procedure ComPut(B: Byte);
- procedure ComPutString(S: String);
- function ComSetParam(APortNum, AParams: Word): Boolean;
-
- Implementation
- uses Objects;
-
- const
- UART_THR = $00; UART_RBR = $00; UART_IER = $01;
- UART_IIR = $02; UART_LCR = $03; UART_MCR = $04;
- UART_LSR = $05; UART_MSR = $06; I8088_IMR = $21;
- FirstInit: Boolean = True;
-
- var
- Overflow: Boolean;
- PortNum, Base, Max, Head, Tail: Word;
- BufferPtr: PByteArray;
- SaveCom1Int, SaveCom2Int: Pointer;
- IRQ, IntNumber: Byte;
-
- procedure STI; inline($FB);
- procedure CLI; inline($FA);
-
- procedure ComIntHandler; interrupt; assembler;
- asm
- STI
- MOV DX,Base { Receive buffer register is at offset 0 }
- IN AL,DX
- LES DI,BufferPtr { get pointer into the buffer }
- MOV BX,Head
- MOV ES:[DI+BX],AL { put character into buffer }
- INC BX { increment }
- CMP BX,Max { do we need a wrap around ? }
- JL @@1
- MOV BX,0
- @@1:
- CMP Tail,BX { buffer overflow ? }
- JNE @@2
- INC Overflow
- JMP @@3
- @@2:
- MOV Head,BX { put head pointer back }
- @@3:
- MOV AL,$20
- OUT $20,AL { send non-specific EOI to interrupt controller }
- end;
-
- procedure BiosInitCom(APortNum, AParams: Word); assembler;
- asm
- MOV AX,AParams
- MOV DX,APortNum
- XOR AH,AH
- INT 14H
- end;
-
- procedure ComInit(BufSize: Word);
- begin
- if not FirstInit then RunError(255);
- FirstInit:=False;
- GetIntVec($C, SaveCom1Int);
- GetIntVec($B, SaveCom2Int);
- GetMem(BufferPtr, BufSize);
- Max:=BufSize;
- end;
-
- procedure ComDone;
- begin
- if not FirstInit then
- begin
- SetIntVec($C, SaveCom1Int);
- SetIntVec($B, SaveCom2Int);
- FreeMem(BufferPtr, Max);
- CLI;
- Port[I8088_IMR] := Port[I8088_IMR] or (1 shl IRQ);
- Port[UART_IER + Base] := 0;
- Port[UART_MCR + Base] := 0;
- STI;
- end;
- FirstInit := True;
- end;
-
- function ComAvail: Boolean;
- begin
- ComAvail:= Head <> Tail;
- end;
-
- function ComGet: Byte;
- begin
- repeat until Head <> Tail;
- ComGet := BufferPtr^[Tail];
- CLI;
- Inc(Tail);
- if Tail >= Max then Tail := 0;
- STI;
- end;
-
- function ComOverflow: Boolean;
- begin
- ComOverflow:=Overflow;
- end;
-
- procedure ComPut(B: Byte);
- begin
- while (Port[UART_LSR + Base] and $20) = 0 do;
- CLI;
- Port[UART_THR + Base] := B;
- STI;
- end;
-
- procedure ComPutString(S: String);
- var L: Integer;
- begin
- for L := 1 to Length(S) do ComPut(Ord(S[L]));
- end;
-
- function ComSetParam(APortNum, AParams: Word): Boolean;
- var
- BIOSPorts : array[1..2] of Word absolute $40:0;
- Junk: Word;
- begin
- SetIntVec($C, SaveCom1Int);
- SetIntVec($B, SaveCom2Int);
- Overflow := False;
- Head := 0;
- Tail := 0;
- Base := BIOSPorts[APortNum + 1];
- IRQ := Hi(Base) + 1;
- IntNumber := IRQ + $8;
- if (Port[UART_IIR + Base] and $F8) = 0 then
- begin
- SetIntVec(IntNumber, @ComIntHandler); { install interrupt handler }
- PortNum := APortNum;
- BiosInitCom(APortNum, AParams); { use BIOS call for easy param setting }
- CLI;
- Port[UART_LCR + Base] := Port[UART_LCR + Base] and $7F;
- Port[I8088_IMR] := Port[I8088_IMR] and ((1 shl IRQ) xor $FF);
- { enable PIC to recognize IRQ's }
- Port[UART_IER + Base] := $01; { allow UART to generate IRQ's }
- Port[UART_MCR + Base] := Port[UART_MCR + Base] or $0B;
- Junk := Port[UART_LSR + Base]; { clear these registers }
- Junk := Port[UART_RBR + Base];
- STI;
- ComSetParam:=True
- end
- else ComSetParam := False;
- end;
- end.
-