home *** CD-ROM | disk | FTP | other *** search
- {
- 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;
-
-
-
-