home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
turbopas
/
io5150.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1992-09-08
|
9KB
|
265 lines
{
Device driver for IBM 5150 PC's serial port (AUX).
By J. Eric Roskos
Public Domain, may not be sold for profit.
This driver is initialized by calling AuxInit, and MUST be
deactivated by calling AuxOff before the program exits (or a sub-
sequent serial interrupt will cause the system to hang).
It uses serial interrupts, and thus works considerably better than
the standard AUX device driver supplied with the IBM PC.
Once AuxInit is called, you can read and write to the predefined
Turbo Pascal file "Aux" to access the serial port. The procedure
AuxSt returns True if a character is presently available at the
serial port, equivalent to the KeyPressed procedure for the console.
You don't have to (and shouldn't) call any of the other procedures
in this file other than AuxInit, AuxOff, and AuxSt.
Presently, only input is done via interrupts; output is via
conventional polling, and a write to the serial port will cause
a busy-wait until any previous character is completely transmitted.
The input buffer's size is defined by the constant BUFSIZ below,
which must be a power of 2 (or the wrap-around algorithm won't work).
The serial port is presently initialized to 1200 baud; if you want
to set it via the MODE command of DOS instead, you can comment out
the statements indicated below.
If the input buffer becomes full, a ^S is sent to the remote machine;
a ^Q is then sent when it empties sufficiently to resume. This
is indeed used at 1200 baud if a number of escape sequences are
sent in succession.
Only the top 24 lines of the screen are used, in order to comply with
most standard terminals. However, for this to work you have to
compile the program with Turbo Pascal version 2.0 or later, and use
an IBM PC version of the compiler (not the generic MS-DOS version).
}
(*
unit IO5150;
interface
procedure AuxInit; { Initialize the unit; must be called at start }
procedure AuxOff; { Terminates the unit; must be called at exit }
procedure AuxSt; { Returns True if char immed. avail from Aux }
implementation
*)
{$R-}
{$C-}
const
BUFSIZ = 256; { Buffer size -- must be a power of 2 }
{ Port addresses for COM1: you must change these to use COM2 }
RX = 1016; { Receiver Buffer Register }
TX = 1016; { Transmitter Buffer Register }
IE = 1017; { Interrupt Enable Register }
II = 1018; { Interrupt Identification Register }
LC = 1019; { Line Control Register }
MC = 1020; { Modem Control Register }
LS = 1021; { Line Status Register }
MS = 1022; { Modem Status Register }
DLL = 1016; { Divisor Latch, Low Order Byte }
DLH = 1017; { Divisor Latch, High Order Byte }
type
{ The input buffer structure }
buffer=record
buf: packed array[0..BUFSIZ] of char; { The character buffer }
ip,op,cnt: integer; { Input pointer, output pointer, char count }
end;
{ Turbo Pascal's DOS/BIOS call parameter block }
regpack=record
ax,bx,cx,dx,bp,si,di,ds,es,flags: integer;
end;
var
Buf: buffer; { The input buffer }
AuxOset, AuxSeg: integer; { Saved orig. serial int vector }
SvOset: integer absolute $0000:$0030; { The serial interrupt vector }
SvSeg: integer absolute $0000:$0032; { " " " " }
SavDs: integer absolute $0000:$002E; { Program's DS addr is saved here }
Run: boolean; { True while emulator is to run }
XedOff: boolean; { True if IntSer Xed off remote }
c: char; { Current input character }
{
Interrupt service routine.
This routine saves all registers, and then sets up its Data Segment
from the value saved in 0:$2E by AuxInit (since, contrary to what the
Turbo manual tells you, the DS may be changed by the ROM BIOS and thus
may be incorrect on entry to the interrupt routine). It then gets the
input character (if the interrupt was due to a received character) and
stores it in the buffer, handling XOFFs if necessary.
}
procedure IntSer;
var
i: integer;
begin
{ Save all registers and set up our Data Segment }
inline($50/$53/$51/$52/$57/$56/$06/$1e);
inline($06/$50/$B8/$00/$00/$8e/$C0/$26/$8E/$1E/$2E/$00/$58/$07);
{ Turn interrupts back on }
inline($FB);
Port[$20] := $20;
{ Process the interrupt }
case Port[II] of
0: i := Port[MS]; { Modem Status Intr }
1: ; { No Interrupt }
2: writeln('Error: THRE interrupt'); { Transmit Intr }
4: { Receive Intr }
begin
Buf.buf[Buf.ip] := chr(Port[RX]);
Buf.ip := (Buf.ip + 1) and (BUFSIZ-1);
inline($FA); { CLI }
Buf.cnt := Buf.cnt + 1;
inline($FB); { STI }
if (Buf.cnt >= BUFSIZ-25) and not Xedoff then
begin
Xedoff := true;
Port[TX] := ord(^S);
end;
end;
6: i := Port[LS]; { Line Status Intr }
end {case};
{ Restore saved registers and do IRET }
inline($1F/$07/$5E/$5F/$5A/$59/$5B/$58/$8B/$E5/$5D/$CF);
end;
{
AUX port status. Returns True if there is a character available
for reading, same as KeyPressed for console.
}
function AuxSt: boolean;
begin
AuxSt := (Buf.cnt > 0);
end;
{
AUX input routine. Not called by the user: called by the Turbo
runtime system when you do a read from the Aux file.
}
function AuxIn: char;
begin
while Buf.cnt = 0 do ;
AuxIn := Buf.buf[Buf.op];
Buf.op := (Buf.op + 1) and (BUFSIZ-1);
inline($fa); { CLI }
Buf.cnt := Buf.cnt - 1;
inline($fb); { STI }
if (Buf.cnt < 25) and Xedoff then
begin
Xedoff := false;
Port[TX] := ord(^Q);
end;
end;
{
AUX port output routine. Not called by the user: called by the
Turbo runtime system when you do a write to the Aux file.
}
procedure AuxOut(c:char);
begin
while (Port[LS] and $20) = 0 do ; { Busy Wait }
Port[TX] := ord(c);
end;
{
AUX device driver initialization. You must call this before accessing
the Aux file if you want to use these device drivers.
}
procedure AuxInit;
begin
inline($fa); { CLI }
{ Initialize interrupt vector and 8259A }
SavDs := Dseg;
AuxOset := SvOset;
AuxSeg := SvSeg;
SvOset := ofs(IntSer);
SvSeg := Cseg;
Port[$21] := $ac; { enable ints 8, 9, C, E }
{ Initialize 8250 UART }
{ Comment the starred lines out if you want to use MODE to init AUX: }
Port[MC] := $00; { *** } { Negate DTR and RTS }
Port[LC] := $80; { *** } { Set baud rate to 1200 baud }
Port[DLL] := $60; { *** } { Use $80 here for 300 baud }
Port[DLH] := $00; { *** } { Use $01 here for 300 baud }
Port[LC] := $2a; { *** } { Set stick parity 7 data 1 stop }
Port[IE] := $05; { Turn on interrupts }
Port[MC] := $0b; { Turn on int gate, DTR, and RTS }
{ Initialize buffer }
Buf.ip := 0;
Buf.op := 0;
Buf.cnt := 0;
Xedoff := false;
{ Initialize I/O system }
AuxInPtr := ofs(AuxIn);
AuxOutPtr := ofs(AuxOut);
inline($fb); { STI }
end;
{
Reset the AUX port to non-interrupt mode. You MUST call this routine
before you exit.
}
procedure AuxOff;
begin
inline($fa); { CLI }
SvOset := AuxOset;{ Restore serial int vector to its original value }
SvSeg := AuxSeg;
Port[IE] := $00; { Turn off UART's interrupt enables }
Port[MC] := $00; { Negate RTS and CTS, and turn off interrupt gate }
Port[$21] := $BC; { Set 8259 PIC back to disabling serial interrupts }
Port[$20] := $20; { Send EOI to PIC }
inline($fb); { STI }
end;