abcscnuk@csuna.UUCP (Naoto Kimura) (07/09/88)
I seem to be having problems in detecting the configuration of the RS232 ports under Turbo pascal (version 3.01a). I've looked at the BIOS routines, and all I see are things to set the parameters, look at the status, send and receive (polled) character, but no routines available for checking the parameters. I finally deceded to resort to looking at the port registers, but am having difficulty in getting the correct parameters. I was wondering if anyone knew of a way to get the setup parameters for the RS232 ports without having to resort to this. The problem I am having is that when I use the routines (listings are included in the article) that I have written, I get the wrong baud rate (see code in AUXIO.INC). I am hoping to get this code working for 3.0, and then upgrading the stuff to 4.0 (the code has some strangeness to make upgrading easier, also some strange stuff appears because I want to be able to translate the code to C using a translator). The following are the files that I am using: ------ register.inc ------ (*====================================================================*\ || MODULE NAME: REGISTER.INC || || || || DESCRIPTION: This file declares the record type "registers" that || || is needed for using the BIOS and DOS interrupt || || services. || || || || DEPENDENCIES: (none) || || || || LAST MOD ON: 06/03/88 || \*====================================================================*) type registers = record case integer of 1: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags : integer); 2: (AL,AH,BL,BH,CL,CH,DL,DH : byte) end; ------ machine.inc ------ (*====================================================================*\ || MODULE NAME: MACHINE.INC || || || || DESCRIPTION: This file contains definitions and declarations || || that are unique to the IBM-PC and Turbo Pascal. || || || || DEPENDENCIES: (none) || || || || LAST MOD ON: 06/06/88 || \*====================================================================*) const FBLOCK_SIZE = 128; (* number of bytes in a block used * * by blockread and blockwrite *) CONDENSED = #15; (*code to change to condensed print *) NORMAL = #18; (*these codes are for Epson printer *) EVEN_BITMASK= $fffe; (* mask out lsb -- makes int even * * when converting to C, change this * * to ~ 1 -- makes independent of * * word size, so it'll work better *) LO15MASK = $7fff; (* lower 15 bits of integer *) type pointer = ^byte; charset = set of char; const ALPHA_CHARS : charset = [ 'A'..'Z','a'..'z' ]; FNAME_CHARS : charset = [ 'A'..'Z','a'..'z','0'..'9','\',':','.' ]; WILD_CHARS : charset = [ '*','?' ]; REAL_CHARS : charset = [ '0'..'9','+','-','.' ]; INT_CHARS : charset = [ '0'..'9','+','-' ]; STR_CHARS : charset = [ ' '..#255 ]; YN_CHARS : charset = [ 'Y','N','y','n' ]; (* handy-dandy routine from *Advanced Techniques in Turbo Pascal* *) (* by Charles C. Edwards *) procedure exchange (var S,D; L: integer); begin inline( $1E { PUSH DS } /$C5/$B6/S { LDS SI,S[BP] } /$C4/$BE/D { LES DI,D[BP] } /$8B/$8E/L { MOV CX,[L+BP]} /$FC { CLD } /$26/$8A/$05 {L: MOV AL,ES:[DI]} /$86/$04 { EXCH [SI],AL } /$46 { INC SI } /$AA { STOSB } /$E2/$F7 { LOOP L } /$1F); { POP DS } end; (* exchange *) ------ dos_vecs.inc ------ (*====================================================================*\ || MODULE NAME: DOS_VECS.INC || || || || DESCRIPTION: This library of routines is used to get and set DOS || || interrupt vectors. Hopefully these are compatible || || the routines found in release 4.0 of Turbo Pascal. || || || || DEPENDENCIES: REGISTER.INC, MACHINE.INC || || || || LAST MOD ON: 06/06/88 || \*====================================================================*) procedure GetIntVec( Interrupt_Number : byte; var Interrupt_Handler: pointer ); var regs : registers; begin regs.AH := $35; regs.AL := Interrupt_Number; intr($21,regs); Interrupt_Handler := ptr(regs.ES,regs.BX) end; procedure SetIntVec( Interrupt_Number : byte; Interrupt_Handler : pointer ); var regs : registers; begin regs.AH := $25; regs.AL := Interrupt_Number; regs.DS := seg(Interrupt_Handler^); regs.DX := ofs(Interrupt_Handler^); intr($21,regs) end; ------ data_seg.inc ------ const Interrupt_DS : integer = 0; ------ auxio.inc ------ (*====================================================================*\ || MODULE NAME: AUXIO.INC || || || || DESCRIPTION: This is a TURBO data-communications handler. Much || || of this code was found in the book *Advanced || || Techniques in Turbo Pascal* by Charles C. Edwards, || || but has bee modified somewhat to allow for future || || compatibility with Turbo 4.0 and eventual transla- || || tion of the programs to C. || || || || DEPENDENCIES: REGISTER.INC, MACHINE.INC, DOS_VECS.INC, DATA_SEG.INC|| || || || LAST MOD ON: 07/07/88 || \*====================================================================*) const RS_BUFFER_SIZE = 8191; { Size of bufer - 1 ; change if you want a different buffer size } (*----------------------------------------------------------------*\ | These values are used by the BIOS routines. | \*----------------------------------------------------------------*) _COM_BAUD = $D0; (* ###- ---- *) _COM_110 = $00; (* 000- ---- *) _COM_150 = $20; (* 001- ---- *) _COM_300 = $40; (* 010- ---- *) _COM_600 = $60; (* 011- ---- *) _COM_1200 = $80; (* 100- ---- *) _COM_2400 = $A0; (* 101- ---- *) _COM_4800 = $C0; (* 110- ---- *) _COM_9600 = $D0; (* 111- ---- *) _COM_PARITY = $18; (* ---# #--- *) _COM_NOPARITY = $00; (* ---0 0--- *) _COM_ODDPARITY = $08; (* ---0 1--- *) _COM_NVRMNDPARITY = $10; (* ---1 0--- *) _COM_EVENPARITY = $18; (* ---1 1--- *) _COM_STOPBITS = $04; (* ---- -#-- *) _COM_STOP1 = $00; (* ---- -0-- *) _COM_STOP2 = $04; (* ---- -1-- *) _COM_CHRBITS = $03; (* ---- --## *) _COM_CHR5 = $00; (* ---- --00 *) _COM_CHR6 = $01; (* ---- --01 *) _COM_CHR7 = $02; (* ---- --10 *) _COM_CHR8 = $03; (* ---- --11 *) _COM_NUMBAUD = 8; (* number of supported baud rates *) _COM_BAUD_TBL : array [0..7] of byte = ( _COM_110, _COM_150, _COM_300, _COM_600, _COM_1200, _COM_2400, _COM_4800, _COM_9600 ); _COM_BAUD_STR : array [0..7] of array [1..4] of char = ( ' 110', ' 150', ' 300', ' 600', '1200', '2400', '4800', '9600' ); _COM_NUMPARITY = 4; (* number of supported parity values *) _COM_PARITY_TBL : array [0..3] of byte = ( _COM_NOPARITY, _COM_ODDPARITY, _COM_NVRMNDPARITY, _COM_EVENPARITY ); _COM_PARITY_STR : array [0..3] of array [1..10] of char = ( ' None', ' Odd', 'Don''t care', ' Even' ); _COM_NUMSTOP = 2; (* number of supported stop bits *) _COM_STOP_TBL : array [0..1] of byte = ( _COM_STOP1, _COM_STOP2 ); _COM_STOP_VAL : array [0..1] of byte = ( 1, 2 ); _COM_NUMCHR = 4; (* number of supported chr bit values *) _COM_CHR_TBL : array [0..3] of byte = ( _COM_CHR5, _COM_CHR6, _COM_CHR7, _COM_CHR8 ); _COM_CHR_VAL : array [0..3] of byte = ( 5, 6, 7, 8 ); const (*----------------------------------------------------------------*\ | These are masks to be used with the Line Control Register | \*----------------------------------------------------------------*) WL_MASK = $03; (* word length select *) STB_MASK = $04; (* # Stop BiTs *) PEN_MASK = $08; (* Parity ENable *) EPS_MASK = $10; (* Even Parity Select *) STICKP_MASK = $20; (* Stick Parity *) BRK_ON = $40; (* Set Break *) DLAB_ON = $80; (* Divisor Latch *) BRK_OFF = $BF; (* (~ BRK_ON) *) DLAB_OFF = $7F; (* (~ DLAB_ON) *) (*----------------------------------------------------------------*\ | These masks are for use with the Interrupt Enable Register | \*----------------------------------------------------------------*) EN_DATAAVAIL= $01; (* Data Available *) EN_THREMPTY = $02; (* Transmit Holding Reg EMpty *) EN_RCV = $04; (* Receive Line Status *) EN_MODEM = $08; (* Modem Status *) type INS8250 = record THR: integer; {Transmit Holding Register} RBR: integer; {Recieve holding register } IER: integer; {Interrupt Enable Register} LCR: integer; {Line Control Register } MCR: integer; {Modem Control Register } LSR: integer; {Line Status Register } MSR: integer; {Modem Status Register } DLL: integer; {Divisor Latch Lsb } DLM: integer {Divisor Latch Msb } end; RS_Baud = (B110,B150,B300,B600,B1200,B2400,B4800,B9600); RS_Parity = (P_None,P_Odd,P_Nevermind,P_Even); const RS232 : array [1..2] of INS8250 = ( ( THR:$3F8; RBR:$3F8; IER:$3F9; LCR:$3FB; MCR:$3FC; LSR:$3FD; MSR:$3FE; DLL:$3F8; DLM:$3F9 ), ( THR:$2F8; RBR:$2F8; IER:$2F9; LCR:$2FB; MCR:$2FC; LSR:$2FD; MSR:$2FE; DLL:$2F8; DLM:$2F9 )); Chk_DSR : boolean = FALSE; Chk_CTS : boolean = FALSE; var RS_Error : byte; RS_Buffer : array [0..RS_BUFFER_SIZE] of byte; RS_Buf_Head, RS_Buf_Tail : integer; RS_Com : byte; OldRS_Vec1, OldRS_Vec2 : pointer; AuxIn, AuxOut : integer; (*--------------------------------------------------------------------*\ | NAME: RS232_Interrupt | | | | This is the interrupt service routine written to handle the | | RS232 port. This routine is triggered by data being available for | | reading. | | | | EXTERNALS: const RS232, RS_BUFFER_SIZE | | var Interrupt_DS, RS_Error, RS_Com, RS_Buffer, | | RS_Buf_Tail | \*--------------------------------------------------------------------*) procedure RS232_Interrupt; begin inline ($50/ {PUSH AX} $53/ {PUSH BX} $51/ {PUSH CX} $52/ {PUSH DX} $57/ {PUSH DI} $56/ {PUSH SI} $06/ {PUSH ES} $1E/ {PUSH DS} $FB/ {STI} $2E/$A1/Interrupt_DS/ {MOV AX,CS:Interrupt_DS} $8E/$D8 ); {MOV DS,AX} RS_Error := port[RS232[RS_Com].LSR] and $1E; RS_Buffer[RS_Buf_Tail] := port[RS232[RS_Com].RBR]; RS_Buf_Tail := (RS_Buf_Tail+1) mod (RS_BUFFER_SIZE+1); inline ($FA); {CLI} port[$20] := $20; {clr interrupt flag} inline ($1F/ {POP DS} $07/ {POP ES} $5E/ {POP SI} $5F/ {POP DI} $5A/ {POP DX} $59/ {POP CX} $5B/ {POP BX} $58/ {POP AX} $8B/$E5/ {MOV SP,BP} $5D/ {POP BP} $CF) {IRET} end; {RS232_Interrupt} (*--------------------------------------------------------------------*\ | NAME: RS_Break | | | | This routine is used to generate a break signal on the RS232 | | communications adapter. | | | | EXTERNALS: const RS232 | | var RS_Com | \*--------------------------------------------------------------------*) procedure RS_Break; begin port[RS232[RS_Com].LCR] := port[RS232[RS_Com].LCR] or BRK_ON; delay(200); { 1/5 second } port[RS232[RS_Com].LCR] := port[RS232[RS_Com].LCR] and BRK_OFF end; {RS_Break} (*--------------------------------------------------------------------*\ | NAME: RS232_Avail | | | | This routine is used to check if there is a character available | | in the input queue. | | | | EXTERNALS: var RS_Buf_Head, RS_Buf_Tail | \*--------------------------------------------------------------------*) function RS232_Avail:boolean; begin RS232_Avail := RS_Buf_Head <> RS_Buf_Tail end; (*--------------------------------------------------------------------*\ | NAME: RS232_In | | | | This routine is used by the Turbo text file driver to read a | | single character from the RS232 port. | | | | EXTERNALS: const RS_BUFFER_SIZE | | var RS_Buf_Head, RS_Buf_Tail | \*--------------------------------------------------------------------*) function RS232_In: char; begin while RS_Buf_Head = RS_Buf_Tail do delay(10); RS232_In := char(RS_Buffer[RS_Buf_Head]); RS_Buf_Head := (RS_Buf_Head+1) mod (RS_BUFFER_SIZE+1) end; {RS232_In} (*--------------------------------------------------------------------*\ | NAME: RS232_Out | | | | This routine is used by the Turbo text file driver to output a | | character to the RS232 port. | | | | EXTERNALS: const RS232 | | var RS_Com, Chk_DSR, Chk_CTS | \*--------------------------------------------------------------------*) procedure RS232_Out(Param:char); var i : byte; begin while ((port[RS232[RS_Com].LSR] and $20) <> $20) do delay(1); (* xmit reg not empty *) port[RS232[RS_Com].MCR]:=$0B; (* Set request to send *) if Chk_DSR then if Chk_CTS then while ((port[RS232[RS_Com].MSR] and $30) <> $30) do delay(1) else while ((port[RS232[RS_Com].MSR] and $20) <> $20) do delay(1) else if Chk_CTS then while ((port[RS232[RS_Com].MSR] and $10) <> $10) do delay(1); port[RS232[RS_Com].THR]:=byte(Param); RS_Error := 0 end; {RS232_Out} (*--------------------------------------------------------------------*\ | NAME: RS_parms | | | | This routine returns a byte showing the status of the RS232 port | | in a byte with the same format as is passed to the BIOS RS232 setup | | routine. The baud rate is determined by reading the Divisor latch | | and interpreting the contents (This table could be found in the | | IBM Technical Reference). | | | | EXTERNALS: const RS232, DLAB_ON, DLAB_OFF, _COM_110, _COM_150, | | _COM_300, _COM_600, _COM_1200, _COM_2400, _COM_4800,| | WL_MASK, STB_MASK, PEN_MASK EPS_MASK | \*--------------------------------------------------------------------*) function RS_parms( Com : byte ) : byte; var v : integer; retval : byte; begin (* get baud rate *) port[RS232[Com].LCR] := port[RS232[Com].LCR] or DLAB_ON; v := (port[RS232[Com].DLM] shl 8) or port[RS232[Com].DLL]; port[RS232[Com].LCR] := port[RS232[Com].LCR] and DLAB_OFF; case v of (* ">" mark those not supported by the BIOS *) $0900: retval := 0; (* > 50 *) $0600: retval := 0; (* > 75 *) $0417: retval := _COM_110; (* 110 *) $0359: retval := 0; (* > 134.5 *) $0300: retval := _COM_150; (* 150 *) $0180: retval := _COM_300; (* 300 *) $00C0: retval := _COM_600; (* 600 *) $0060: retval := _COM_1200; (* 1200 *) $0040: retval := 0; (* > 1800 *) $003A: retval := 0; (* > 2000 *) $0030: retval := _COM_2400; (* 2400 *) $0020: retval := 0; (* > 3600 *) $0018: retval := _COM_4800; (* 4800 *) $0010: retval := 0; (* > 7200 *) $000C: retval := _COM_9600 (* 9600 *) else retval := 0 (* > ? *) end; (* get word length, stop bits, and parity *) RS_parms := retval or (port[RS232[Com].LCR] and (WL_MASK or STB_MASK or PEN_MASK or EPS_MASK )); end; {RS_parms} (*--------------------------------------------------------------------*\ | NAME: RS_Initialize | | | | This routine sets up parameters for the RS232 port. This is | | made to be compatible with the BIOS routine, for later translations | | to C. | | | | EXTERNALS: const RS232 | | type registers | | var RS_Buf_Head, RS_Buf_Tail, RS_Com | \*--------------------------------------------------------------------*) procedure RS_Initialize( Com, Parms : byte); var Regs : registers; begin RS_Buf_Head := 0; RS_Buf_Tail := 0; RS_Com := Com; Regs.DX := Com-1; Regs.AX := Parms; intr($14,Regs); if RS_Com = 1 then port[$21] := port[$21] and $EF else port[$21] := port[$21] and $F7; port[RS232[RS_Com].LCR] := port[RS232[RS_com].LCR] and DLAB_OFF; port[RS232[RS_Com].IER] := EN_DATAAVAIL; port[RS232[RS_Com].MCR] := $08 end; (* RS_Initialize *) type RS_Config = record Com, Speed, Parity, Stop, Length : byte end; (*--------------------------------------------------------------------*\ | NAME: Get_RS_setup | | | \*--------------------------------------------------------------------*) procedure Get_RS_Setup(var Setup : RS_Config); var parms : byte; begin Setup.Com := RS_Com; parms := RS_parms(RS_Com); (* get parameters *) Setup.Speed := (parms and _COM_BAUD) shr 5; Setup.Parity := (parms and _COM_PARITY) shr 3; Setup.Stop := (parms and _COM_STOPBITS) shr 2; Setup.Length := parms and _COM_CHRBITS end; (* Get_RS_Setup *) (*--------------------------------------------------------------------*\ | NAME: RS_Install | | | | This routine is used to disable the interrupts from the port and | | to restore the old interrupt vectors. | | | | EXTERNALS: var OldRS_Vec1, OldRS_Vec2, AuxIn, AuxOut | | procedure GetIntVec, SetIntVec, RS232_Interrupt, RS232_In | | RS232_Out | \*--------------------------------------------------------------------*) procedure RS_Install; begin GetIntVec($0C,OldRS_Vec1); GetIntVec($0B,OldRS_Vec2); port[$21] := port[$21] or $18; port[RS232[1].LCR] := port[RS232[1].LCR] and $7F; port[RS232[1].IER] := 0; port[RS232[1].MCR] := 0; port[RS232[2].LCR] := port[RS232[2].LCR] and $7F; port[RS232[2].IER] := 0; port[RS232[2].MCR] := 0; SetIntVec($0C,ptr(cseg,ofs(RS232_Interrupt))); SetIntVec($0B,ptr(cseg,ofs(RS232_Interrupt))); RS_Com := 1; AuxIn := AuxInPtr; AuxOut := AuxOutPtr; AuxInPtr := ofs(RS232_In); AuxOutPtr:= ofs(RS232_Out) end; {RS_Install} (*--------------------------------------------------------------------*\ | NAME: RS_Cleanup | | | | This routine is used to disable the interrupts from the port and | | to restore the old interrupt vectors. | | | | EXTERNALS: const RS232 | | var RS_Com, OldRS_Vec1, OldRS_Vec2, AuxIn, AuxOut | | procedure SetIntVec | \*--------------------------------------------------------------------*) procedure RS_Cleanup; var Regs : registers; begin port[$21] := port[$21] or $18; port[RS232[RS_Com].LCR] := port[RS232[RS_Com].LCR] and $7F; port[RS232[RS_Com].IER] := 0; port[RS232[RS_Com].MCR] := 0; SetIntVec($0C,OldRS_Vec1); SetIntVec($0B,OldRS_Vec2); AuxInPtr := AuxIn; AuxOutPtr:= AuxOut end; {RS_Cleanup} ------ end of listings ------ //-n-\\ Naoto Kimura _____---=======---_____ (csun!csuna!abcscnuk) ====____\ /.. ..\ /____==== // ---\__O__/--- \\ Enterprise... Surrender or we'll \_\ /_/ send back your *&^$% tribbles !!