{$I-} { I/O hecking OFF } {$R-} { Range checking OFF } {$S-} { Stack checking OFF } {$V-} { Var-str checking OFF} {$B+} {Boolean complete evaluation on} {$N-} {No numeric coprocessor} Unit RS232INT; Interface Uses Crt, Dos; { global declarations for Async} type astr = String[160]; { generic string type for parameters } const buffer_max = 5120; var Async_OriginalVector : pointer; buffer : Array[0..buffer_max] of char; Async_Open_Flag : Boolean; { true if Open but no Close } Async_Port : Integer; { current Open port number (1 or 2) } base : Integer; { base for current open port } Async_Irq : Integer; { irq for current open port } Async_Buffer_Overflow : Boolean; { True if buffer overflow has happened } Async_Buffer_Used : Integer; Async_MaxBufferUsed : Integer; { buffer is empty if Head = Tail } Buffer_head : Integer; { Locn in buffer to put next char } Buffer_tail : Integer; { Locn in buffer to get next char } Buffer_newtail : Integer; { End of Async declarations } procedure async_isr; INTERRUPT; procedure set_baud(r:integer); function charin:char; procedure charout(c:char); function cdet:boolean; procedure Async_Init; procedure Async_Close; Procedure Async_Open(ComPort : Integer; BaudRate : Integer; Parity : Char; WordSize : Integer; StopBits : Integer); Implementation 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; { port address of the Interrupt Mask Register } var Async_BIOS_Port_Table : Array[1..4] of Integer absolute $40:0; const Async_Num_Bauds = 8; Async_Baud_Table : array [1..Async_Num_Bauds] of record Baud, Bits : integer end = ((Baud:110; Bits:$00), (Baud:150; Bits:$20), (Baud:300; Bits:$40), (Baud:600; Bits:$60), (Baud:1200; Bits:$80), (Baud:2400; Bits:$A0), (Baud:4800; Bits:$C0), (Baud:9600; Bits:$E0)); PROCEDURE DisableInterrupts; inline($FA {cli} ); {MACROS} PROCEDURE EnableInterrupts; inline($FB {sti} ); procedure BIOS_RS232_Init(ComPort, ComParm : Integer); var Regs : registers; begin with Regs do begin ax := ComParm and $00FF; { AH=0; AL=ComParm } dx := ComPort; Intr($14, Regs) end end; procedure Async_Isr; {INTERRUPT;} begin Inline( $FB/ { STI } { get the incomming character } { buffer[Buffer_head] := Chr(Port[UART_RBR + base]); } $8B/$16/base/ { MOV DX,base } $EC/ { IN AL,DX } $8B/$1E/Buffer_head/ { MOV BX,Buffer_head } $88/$87/buffer/ { MOV buffer[BX],AL } { Async_Buffer_NewHead := Buffer_head + 1; } $43/ { INC BX } { if Async_Buffer_NewHead > buffer_max then Async_Buffer_NewHead := 0; } $81/$FB/buffer_max/ { CMP BX,buffer_max } $7E/$02/ { JLE L001 } $33/$DB/ { XOR BX,BX } { if Async_Buffer_NewHead = Buffer_tail then Async_Buffer_Overflow := TRUE else } {L001:} $3B/$1E/Buffer_tail/ { CMP BX,Buffer_tail } $75/$08/ { JNE L002 } $C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 } $90/ { NOP generated by assembler for some reason } $EB/$16/ { JMP SHORT L003 } { begin Buffer_head := Async_Buffer_NewHead; Async_Buffer_Used := Async_Buffer_Used + 1; if Async_Buffer_Used > Async_MaxBufferUsed then Async_MaxBufferUsed := Async_Buffer_Used end; } {L002:} $89/$1E/Buffer_head/ { MOV Buffer_head,BX } $FF/$06/Async_Buffer_Used/ { INC Async_Buffer_Used } $8B/$1E/Async_Buffer_Used/ { MOV BX,Async_Buffer_Used } $3B/$1E/Async_MaxBufferUsed/ { CMP BX,Async_MaxBufferUsed } $7E/$04/ { JLE L003 } $89/$1E/Async_MaxBufferUsed/ { MOV Async_MaxBufferUsed,BX } {L003:} { disable interrupts } $FA/ { CLI } { Port[$20] := $20; } { use non-specific EOI } $B0/$20/ { MOV AL,20h } $E6/$20 { OUT 20h,AL } ) end; { Async_Isr } procedure Async_Init; begin Async_Open_Flag := FALSE; Async_Buffer_Overflow := FALSE; Async_Buffer_Used := 0; Async_MaxBufferUsed := 0; end; { Async_Init } procedure Async_Close; var i, m : Integer; begin if Async_Open_Flag then begin { disable the IRQ on the 8259 } DisableInterrupts; i := Port[I8088_IMR]; { get the interrupt mask register } m := 1 shl Async_Irq; { set mask to turn off interrupt } Port[I8088_IMR] := i or m; { disable the 8250 data ready interrupt } Port[UART_IER + base] := 0; { disable OUT2 on the 8250 } Port[UART_MCR + base] := 0; EnableInterrupts; SetIntVec(Async_Irq + 8,Async_OriginalVector); { re-initialize our data areas so we know the port is closed } Async_Open_Flag := FALSE end end; { Async_Close } Procedure Async_Open(ComPort : Integer; BaudRate : Integer; Parity : Char; WordSize : Integer; StopBits : Integer); { open a communications port } var ComParm : Integer; i, m : Integer; begin if Async_Open_Flag then Async_Close; if (ComPort = 4) and (Async_BIOS_Port_Table[4] <> 0) then Async_Port := 4; if (ComPort = 3) and (Async_BIOS_Port_Table[3] <> 0) then Async_Port := 3; if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then Async_Port := 2; if (ComPort = 1) and (Async_BIOS_Port_Table[1] <> 0) then Async_Port := 1; { default to COM1 } base := Async_BIOS_Port_Table[Async_Port]; Async_Irq := Hi(base) + 1; if (Port[UART_IIR + base] and $00F8)=0 then begin Buffer_head := 0; Buffer_tail := 0; Async_Buffer_Overflow := FALSE; { Build the ComParm for RS232_Init } { See Technical Reference Manual for description } ComParm := $0000; { Set up the bits for the baud rate } i := 0; repeat i := i + 1 until (Async_Baud_Table[i].Baud = BaudRate) or (i = Async_Num_Bauds); ComParm := ComParm or Async_Baud_Table[i].Bits; if Parity in ['E', 'e'] then ComParm := ComParm or $0018 else if Parity in ['O', 'o'] then ComParm := ComParm or $0008 else ComParm := ComParm or $0000; { default to No parity } if WordSize = 7 then ComParm := ComParm or $0002 else ComParm := ComParm or $0003; { default to 8 data bits } if StopBits = 2 then ComParm := ComParm or $0004 else ComParm := ComParm or $0000; { default to 1 stop bit } { use the BIOS COM port initialization routine to save typing the code } BIOS_RS232_Init(Async_Port - 1, ComParm); GetIntVec(Async_Irq + 8, Async_OriginalVector); SetIntVec(Async_Irq + 8, @Async_Isr); { read the RBR and reset any possible pending error conditions } { first turn off the Divisor Access Latch Bit to allow access to RBR, etc. } DisableInterrupts; Port[UART_LCR + base] := Port[UART_LCR + base] and $7F; { read the Line Status Register to reset any errors it indicates } i := Port[UART_LSR + base]; { read the Receiver Buffer Register in case it contains a character } i := Port[UART_RBR + base]; { enable the irq on the 8259 controller } i := Port[I8088_IMR]; { get the interrupt mask register } m := (1 shl Async_Irq) xor $00FF; Port[I8088_IMR] := i and m; { enable the data ready interrupt on the 8250 } Port[UART_IER + base] := $01; { enable data ready interrupt } { enable OUT2 on 8250 } i := Port[UART_MCR + base]; Port[UART_MCR + base] := i or $08; EnableInterrupts; Async_Open_Flag := TRUE; {Async_Open := TRUE} end end; { Async_Open } procedure set_baud(r:integer); var rl:real; a:byte; begin if (r>=300) and (r<=9600) then begin rl:=115200.0/r; r:=trunc(rl); a:=port[3+base] or 128; port[base+3]:=a; port[base]:=lo(r); port[1+base]:=hi(r); port[3+base]:=a and 127; end; end; function charin:char; var t:char; begin if buffer_Head = buffer_Tail Then t:=#0 else begin disableinterrupts; t:=buffer[buffer_Tail]; buffer_Tail:=(buffer_Tail+1) mod (buffer_max+1); enableinterrupts; end; charin := t; end; procedure charout(c:char); begin while (port[base+5] and 32)=0 do; port[base]:=ord(c); end; function cdet:boolean; begin cdet:=(port[base+6] and 128)<>0; end; end.