{ 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;