clw@hprnd.HP.COM (Carl Wuebker) (02/06/89)
I wrote a (stupid) terminal emulator program in Turbo Pascal 3.0. To make
sure that I caught all the characters, I coded a Turbo Pascal ISR for the UART
receive side. It worked pretty well on the IBM-PC. Here's what's happening
when I try to run it on a PS/2:
* The main program uses calls 35 and 25 to save the values for the
existing COM port 2 ISR.
* The interrupts are disabled briefly (CLI) then re-enabled (STI).
* A character is written to the modem.
* The ISR gets called, saves registers, gets the character (or characters)
from the UART and stuffs the character in the FIFO.
* The ISR does an EOI (Port[$20]=$20), restores registers, and IRETs.
* The program prints a character on the screen.
* Either the 2nd character never gets sent to the com port (com port not
ready), the ISR never gets called for the echoed character, or the
ISR gets called but doesn't return.
I realize that this isn't a lot to go on; but does anyone have any clues
as to what might be causing problems on the PS/2?
Thanks,
Carl "upgrade, (n), spend 2 months running in place" Wuebker
clw@hprnd * HP Roseville Networks Division * (916) 785-4296clw@hprnd.HP.COM (Carl Wuebker) (02/10/89)
I've received two responses to this question, one asking me to post the
source of the program and one telling me that, while the IBM PC had edge-
sensitive interrupts, the PS/2 has level sensitive interrupts. The second
response asked me if I read the status register to clear the interrupts.
(Yes, see PROCEDURE isr, below). Thanks to both responders (our mailers
couldn't get mail to the second responder.
FYI, here is the source of the terminal emulator/stock info getter. If
you modify the program to include your phone numbers/ids, and start the
program with the line:
STERM <password> <stock input file> <stock output file>
the thing will dial up The Source, get into stock mode and pick up stock
prices. The stock info will be put into the stock output file, which is
suitable for loading into a spreadsheet. If you type a character (a space
will do) after the program types ATZ, it skips the standard procedure and
turns into a terminal emulator. I've used it successfully with Turbo Pascal
3.0 and a standard IBM PC with a RS232 card/Hayes compatable modem on Com
Port 1. My friend cannot use it with Turbo 3.0, a standard IBM PS/2 and a
RS232 card/Hayes compatable modem on Com Port 2. Any suggestions or fixes
would really be appreciated!!
Thanks,
Carl Wuebker * clw@hprnd * HP Roseville Networks Division * (916) 785-4296
--- cut here
{$C-}
PROGRAM sterm;
CONST
ds: INTEGER = 0;
maxc = 255;
cr = #13;
lf = #10;
cport = 1; { COM port = 1 or 2 }
timeo = 3000; { aprox. # of ms to wait for terminal quiet }
TYPE
str255 = STRING[255];
pstklist = ^stklist;
stklist = PACKED RECORD
nex: pstklist;
ss: str255;
END;
queue = PACKED RECORD
getp,putp: INTEGER;
curc,hi_c: INTEGER;
buf: PACKED ARRAY[1..maxc] OF CHAR
END;
cap_s = PACKED ARRAY[-32767..32767] OF CHAR;
cap_t = ^cap_s;
VAR
pbas: INTEGER;
c: CHAR;
i,j,isro,isrs: INTEGER;
rg: RECORD AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags: INTEGER END;
tq,rq: queue;
running: BOOLEAN;
cap_b: cap_t;
cap_p: INTEGER;
cap_r: BOOLEAN;
ff: STRING[80];
lctl: INTEGER;
crlf: STRING[2];
rx_time,rx_nchr: INTEGER;
hs,ps: pstklist;
stkstr: str255;
fi,fo: TEXT;
PROCEDURE getq(VAR q: queue; VAR c: CHAR);
{ get a character from a queue }
BEGIN
WITH q DO
IF (curc<=0) THEN
{ no characters left }
c := #0
ELSE
BEGIN
c := buf[getp];
curc := curc-1;
getp := getp+1;
IF (getp>maxc) THEN
getp := 1;
END;
END;
PROCEDURE putq(VAR q: queue; c: CHAR);
{ put a character into a queue }
BEGIN
WITH q DO
IF (curc<maxc) THEN
BEGIN
{ queue space available }
buf[putp] := c;
curc := curc+1;
IF (curc>hi_c) THEN
hi_c := curc;
putp := putp+1;
IF (putp>maxc) THEN
putp := 1;
END;
END;
PROCEDURE isr;
BEGIN
inline(
$50/$53/$51/$52/$56/$57/$1E/$06/ { PUSH AX,BX,CX,DX,SI,DI,DS,ES }
$2E/$8E/$1E/ds); { MOV DS,CS[ds] }
WHILE (Port[pbas+$A]=4) DO { 0?,1 or 2 characters here }
putq(rq,CHAR(Port[pbas+$8]));
Port[$20] := $20; { EOI }
inline(
$07/$1F/$5F/$5E/$5A/$59/$5B/$58/ { POP ES,DS,DI,SI,DX,CX,BX,AX }
$8B/$E5/$5D/$CF); { MOV SP,BP / POP BP / IRET }
END;
PROCEDURE subs(pat,repl: str255; VAR source: str255);
VAR sp: INTEGER;
BEGIN
sp := POS(pat,source);
WHILE (sp>0) DO
BEGIN
Delete(source,sp,Length(pat));
Insert(repl,source,sp);
sp := POS(pat,source)
END;
END;
PROCEDURE r2s;
VAR
rc: INTEGER;
rx: CHAR;
cap_char: BOOLEAN;
BEGIN
{ Receiver -> Screen & possibly Capture Buffer }
IF (rq.curc>0) THEN
BEGIN
{ Rx char received => zero Rx timeout, inc Rx character count }
rx_time := 0;
IF (rx_nchr<32767) THEN
rx_nchr := rx_nchr+1;
{ get Rx character }
inline($FA); getq(rq,rx); inline($FB);
rc := INTEGER(rx) AND $7F; rx := CHAR(rc);
{ Rx character -> Screen }
cap_char := TRUE;
CASE rc OF
10: WRITELN;
8,9,13,32..127: WRITE(rx);
ELSE cap_char := FALSE;
END;
IF (rc=17) THEN
WRITE(' *D1* ');
{ Rx character -> Capture Buffer }
IF cap_char AND cap_r AND (cap_p<32767) THEN
BEGIN
cap_b^[cap_p] := rx;
cap_p := cap_p+1
END;
END
ELSE
{ No Rx char => Increment Rx timeout }
IF (rx_time<32767) THEN
rx_time := rx_time+1;
END;
PROCEDURE k2x;
BEGIN
{ Keyboard | Xmit Queue -> Transmitter }
IF ODD(Port[pbas+$D] SHR 5) THEN
IF Keypressed THEN
BEGIN
READ(KBD,c);
IF ((c=#27) AND KeyPressed) THEN
BEGIN
READ(KBD,c);
CASE ORD(c) OF
62: { F4 };
71: { Home }
GOTOXY(1,1);
73: { PgUp }
ClrScr;
79: { End }
running := FALSE;
83: BEGIN
{ Break }
Port[pbas+$B] := Port[pbas+$B] OR $40;
Delay(100);
Port[pbas+$B] := Port[pbas+$B] AND $BF
END
ELSE
END;
END
ELSE
Port[pbas+$8] := ORD(c);
END;
END;
PROCEDURE write_(s: str255);
VAR cp: INTEGER;
BEGIN
cp := 1;
WHILE running AND (NOT Keypressed) AND (cp<=Length(s)) DO
IF ODD(Port[pbas+$D] SHR 5) THEN
BEGIN
{ Tx ready; send out character }
Port[pbas+$8] := ORD(s[cp]);
cp := cp+1;
END
ELSE
r2s;
END;
PROCEDURE wai(tim: INTEGER);
BEGIN
WHILE running AND (NOT Keypressed) AND (rx_time<tim) DO
BEGIN
r2s;
Delay(1)
END;
END;
PROCEDURE write_2(s: str255; tim: INTEGER);
{ Write, then wait for characters to stop coming back }
BEGIN
write_(s);
wai(tim);
END;
PROCEDURE write_1(s: str255; nchr,tim: INTEGER);
{ Write, then wait for characters to start coming back, then wait for characters
to stop coming back }
BEGIN
write_(s);
rx_nchr := 0;
WHILE running AND (NOT Keypressed) AND (rx_nchr<nchr) DO
r2s;
wai(tim);
END;
{ Main }
BEGIN
IF (ParamCount<>3) THEN
BEGIN
WRITELN('Usage: STERM <PASSWORD> <STOCK NAME FILE> <OUTPUT FILE>');
HALT;
END;
ff := ParamStr(2);
ASSIGN(fi,ff);
RESET(fi);
hs := NIL;
ps := NIL;
WHILE NOT EOF(fi) DO
BEGIN
READLN(fi,ff);
WHILE POS(' ',ff)>0 DO
Delete(ff,POS(' ',ff),1);
IF (Length(ff)>0) THEN
BEGIN
IF (hs=NIL) THEN
BEGIN
NEW(hs);
ps := hs;
ps^.nex := NIL;
ps^.ss := '';
END;
IF (Length(ps^.ss)+Length(ff)>76) THEN
BEGIN
NEW(ps^.nex);
ps := ps^.nex;
ps^.nex := NIL;
ps^.ss := '';
END;
IF (Length(ps^.ss)>0) THEN
ps^.ss := ps^.ss+' ';
ps^.ss := ps^.ss+ff
END;
END;
CLOSE(fi);
IF cport = 1 THEN
pbas := $3F0
ELSE
pbas := $2F0;
crlf := #13+#10;
rq.getp := 1; { initialize receive queue }
rq.putp := 1;
rq.curc := 0;
rq.hi_c := 0;
rx_time := 0; { initialize Rx activity info }
rx_nchr := 0;
NEW(cap_b); { allocate capture buffer }
cap_p := -32767;
cap_r := FALSE;
ds := Dseg; { save DS for ISR communications }
rg.AX := $350D-cport; { save existing ISR data (1->C,2->B) }
INTR($21,rg);
isrs := rg.ES; isro := rg.BX;
rg.AX := $250D-cport; { set our ISR }
rg.DS := Cseg; rg.DX := Ofs(isr);
INTR($21,rg);
inline($FA); { disable interrupts }
{ Set up RS232 chip }
lctl := 30; { 3 => 8 bits, 1 stop bit, no parity }
{ 30 => 7 bits, 2 stop bits, even parity }
Port[pbas+$9] := 0; { disable chip interrupts }
Port[pbas+$C] := 3; { set DTR, RTS; clear chip interrupt path }
Port[pbas+$B] := $80; { access divisor latch }
Port[pbas+$9] := 0; { div 300 - 1 1200 - 0 }
Port[pbas+$8] := 96; { div 300 - 128 1200 - 96 }
Port[pbas+$B] := lctl; { bits, stop bit(s) & parity }
i := Port[pbas+$8]; { clear out garbage }
Port[pbas+$9] := 1; { enable receiver interrupts }
Port[pbas+$C] := 11;
IF cport = 1 THEN
Port[$21] := Port[$21] AND $EF
ELSE
Port[$21] := Port[$21] AND $F7;
inline($FB); { enable interrupts }
running := TRUE;
write_2('ATZ'+cr,timeo);
write_1('ATZ'+cr,2,timeo);
write_1('ATDTnnnnnnn'+cr ,4,timeo); { replace w/your phone number }
{ Telenet Sign-On Procedure }
Delay(500); write_(cr);
Delay(500); write_1(cr,2,timeo);
write_1('D1'+cr,2,timeo);
write_1('C XXXXX'+cr,8,timeo); { replace with your C # }
write_1('ID XXXXXX'+cr,8,timeo); { replace with your ID # }
write_1(ParamStr(1)+cr,4,timeo);
write_1('Q'+cr,3,timeo);
write_1('STOCKCHECK 1'+cr,25,timeo);
cap_r := TRUE;
ps := hs;
WHILE (ps<>NIL) DO
BEGIN
write_1(ps^.ss+cr,25,timeo);
ps := ps^.nex
END;
cap_r := FALSE;
write_1(cr,25,timeo);
write_1('Q'+cr,3,timeo);
write_1('OFF'+cr,10,timeo);
Delay(1500);
write_('+++');
Delay(1500);
write_1('ATH'+cr,2,timeo);
{ Terminal Emulation Loop }
IF Keypressed THEN
WHILE running DO
BEGIN
r2s;
k2x
END;
{ Restore original ISR }
rg.AX := $250D-cport; { restore original ISR (1->C,2->B) }
rg.DS := isrs; rg.DX := isro;
INTR($21,rg);
inline($FA); { disable interrupts }
IF cport = 1 THEN
Port[$21] := Port[$21] OR $10
ELSE
Port[$21] := Port[$21] OR $08;
inline($FB); { enable interrupts }
{ Save capture buffer if applicable }
IF (cap_p>-32767) THEN
BEGIN
ff := ParamStr(3);
ASSIGN(fo,ff);
REWRITE(fo);
i := -32767;
WHILE (i<cap_p) DO
BEGIN
{ Scan for LF }
WHILE (i<cap_p) AND (cap_b^[i]<>#10) DO
i := i+1;
{ Inspect char after LF }
i := i+1;
IF (i<cap_p) AND (cap_b^[i] IN ['0','1']) THEN
BEGIN
{ Copy line to string }
stkstr := '';
WHILE (i<cap_p) AND (cap_b^[i]<>#13) DO
BEGIN
stkstr := stkstr+cap_b^[i];
i := i+1
END;
{ Edit string }
stkstr := stkstr+' '; { handle fraction at end of line }
subs(' .. ',' 0 ',stkstr); { strange notation for no change }
subs(' 1/8 ','.125 ',stkstr); { cvt stock fractions to decimal }
subs(' 1/4 ','.25 ' ,stkstr);
subs(' 3/8 ','.375 ',stkstr);
subs(' 1/2 ','.5 ' ,stkstr);
subs(' 5/8 ','.625 ',stkstr);
subs(' 3/4 ','.75 ',stkstr);
subs(' 7/8 ','.875 ',stkstr);
{ Edit string; quote 1st 3 fields (right to left preserves char #) }
j := 13;
WHILE (j<=Length(stkstr)) AND (stkstr[j]<>' ') DO
j := j+1;
Insert('"',stkstr,j);
Insert('"',stkstr,13);
Insert('"',stkstr,12);
Insert('"',stkstr, 7);
Insert('"',stkstr, 6);
Insert('"',stkstr, 1);
{ trim rightmost blanks, delete extraneous blanks & emit string }
WHILE (stkstr[Length(stkstr)]=' ') DO
Delete(stkstr,Length(stkstr),1);
subs(' ',' ',stkstr);
WRITELN(fo,stkstr);
END;
END;
CLOSE(fo);
cap_p := -32767;
END;
{ Report buffer fullness }
WRITELN;
WRITELN('Receive Buffer Utilization (Max): ',rq.hi_c)
END.