Category : Modula II Source Code
Archive   : TALK3.ZIP
Filename : TALK3.MOD

 
Output of file : TALK3.MOD contained in archive : TALK3.ZIP
MODULE Talk3;

(* (C) Copyright 1987 Fitted Software Tools. All rights reserved.

This module is part of the example multitasking communications program
provided with the Fitted Software Tools' Modula-2 development system.

Registered users may use this program as is, or they may modify it to
suit their needs or as an exercise.

If you develop interesting derivatives of this program and would like
to share it with others, we encourage you to upload a copy to our BBS.
*)


(*$L+*)

IMPORT Terminal, Display;
FROM SYSTEM IMPORT ASSEMBLER, ADDRESS, ADR;
FROM System IMPORT GetArg, GetEnv, TermProcedure, Terminate,
GetVector, ResetVector;
FROM InOut IMPORT WriteString, WriteCard, ReadCard, WriteLn, WriteLine;
FROM Strings IMPORT CompareStr, Concat, Append, Length;
FROM Keyboard IMPORT F10, KeyPressed, GetKeyCh;
FROM RS232 IMPORT RS232Input,
Init, ResetPars, GetCom, PutCom, XON, XOFF;
FROM ASCII IMPORT FF, CR, DEL, BEL, LF, BS, HT, ESC, CtrlS, CtrlQ;
FROM NumberConversion
IMPORT StringToCard, CardToString;
FROM Display IMPORT ScrollUp, DisplayLine, Goto;
FROM Windows IMPORT Window, OpenWindow, CloseCurWindow;
FROM Menu IMPORT PopMenu;
FROM XModem2 IMPORT SendFile, ReceiveFile;
FROM Files IMPORT NORMAL, READ, Open, Create, Close, Read, Write;
FROM Kernel IMPORT SignalHeader, LockHeader, InitSignal, InitLock,
NewProcess, Wait, WaitIO, Signal, Lock, Unlock;
FROM Loader IMPORT Execute;

CONST
comBuffSize = 2048;
attrNormal = 07H;
attrReverse = 70H;

VAR Capturing :BOOLEAN; (* capture file open *)
Sending :BOOLEAN; (* sending a file *)
Xon :BOOLEAN; (* XON/XOFF enabled *)

DisplayLock :LockHeader; (* only one process may write
to the screen at any time *)
SendLock :LockHeader; (* to suspend SendFile when we
receive an XOFF *)

(*** for Zmodem and shell to DOS features ***)

PCZfound :BOOLEAN; (* Shows build PCZname process is done *)
ExitCode :CARDINAL; (* return code from Loader.Execute *)
PCZpath, (* Pathname where PCZ.EXE should reside *)
PCZname, (* Fully qualified name of PCZ.EXE *)
Comspec :ARRAY[0..255] OF CHAR; (* COMMAND.COM's name for SHELL to DOS *)
InitialVectors,
TALKingVectors :ARRAY[0..63] OF CARDINAL; (* INT 0-20H vector save areas *)

(*** Program command and reconfiguration ***)

VAR
port :CARDINAL;
baud :CARDINAL;
parity :BOOLEAN;
evenp :BOOLEAN;
bits :CARDINAL;


PROCEDURE Command;
VAR tempstr :ARRAY [0..10] OF CHAR;
args,
cmdstr :ARRAY [0..255] OF CHAR;
cmd :CARDINAL;
cmdx :CARDINAL; (* wsa *)
fn :ARRAY [0..65] OF CHAR;
fns :ARRAY [0..255] OF CHAR;
done :BOOLEAN;
w :Window;

PROCEDURE XModemCom; (* config COM for XModem *)
BEGIN
XOFF;
ResetPars( baud, 1, FALSE, FALSE, 8, ok );
END XModemCom;

PROCEDURE ResetCom; (* reinit COM after Xmodem *)
BEGIN
ResetPars( baud, 1, parity, evenp, bits, ok );
IF Xon THEN XON END;
END ResetCom;

BEGIN (* Command *)
Lock( DisplayLock );
Concat( "|Parameters|Send Text|Xmit Xmodem|Rcv Xmodem|Zmodem Rcv",
"|Zmodem Xmit|Open capFile|Close capFile|DOS shell|Quit",
cmdstr );
PopMenu( 5,5, cmdstr, 0,FALSE,cmd);
CASE cmd OF
0: ;
|
1: (* Reconfig *)
CloseCurWindow; Reconfig
|
2: (* Send text *)
OpenWindow( w, 5,22, 8,75, TRUE, "" );
Terminal.WriteString( " File name: " );
Terminal.ReadLine( fn );
IF fn[0] <> 0C THEN
Open( SendFD, fn, READ );
IF SendFD = -1 THEN
Terminal.WriteString( " --- cannot open file" );
Terminal.Read( c );
ELSE
Sending := TRUE;
SendPtr := 0; BuffEnd := 0;
Signal( SendTextSignal );
END;
END;
CloseCurWindow;
CloseCurWindow; (* MENU window *)
|
3: (* Xmit file *)
PopMenu( 8,15, "|Checksum|CRC", 0, TRUE, cmdx); (* wsa *)
OpenWindow( w, 5,22, 8,75, TRUE, "" );
Terminal.WriteString( " File to send: " );
Terminal.ReadLine( fn );
IF fn[0] <> 0C THEN
Open( XmodemFD, fn, READ );
IF XmodemFD = -1 THEN
Terminal.WriteString( " --- cannot open file" );
Terminal.Read( c );
ELSE
XModemCom;
SendFile( fn, XmodemFD, (cmdx = 2), done ); (* wsa *)
Close( XmodemFD );
ResetCom;
END;
END;
CloseCurWindow;
CloseCurWindow; (* MENU window *)
|
4: (* Rcv file *)
PopMenu( 9,15, "|Checksum|CRC", 0, TRUE, cmdx); (* wsa *)
OpenWindow( w, 5,22, 8,75, TRUE, "" );
Terminal.WriteString( " File to receive: " );
Terminal.ReadLine( fn );
IF fn[0] <> 0C THEN
Create( XmodemFD, fn, NORMAL );
IF XmodemFD = -1 THEN
Terminal.WriteString( " --- cannot create file" );
Terminal.Read( c );
ELSE
XModemCom;
ReceiveFile( fn, XmodemFD, (cmdx = 2), done ); (* wsa *)
Close( XmodemFD );
ResetCom;
END;
END;
CloseCurWindow;
CloseCurWindow; (* MENU window *)
|
5: (* Zmodem Rcv *)
IF PCZname[0] = 0C THEN
OpenWindow( w, 5,22, 8,77, TRUE, "ERROR" );
Terminal.WriteLine(" PCZ.EXE was not successfully located at startup and");
Terminal.WriteString(" can't be run. (You might try it from the DOS shell.)");
Terminal.Read( c );
CloseCurWindow;
CloseCurWindow; (* MENU window *)
ELSE
IF Capturing THEN StopCapture END;
CloseCurWindow; (* MENU window *)
OpenWindow( w, 0,0, 24,79, FALSE, "" ); (* Saves stuff underneath *)

CardToString(port,tempstr,1);
Concat(tempstr," ",args);
CardToString(baud,tempstr,5);
Append(args,tempstr);
Append(args," rz");

XOFF;
SaveVectors(TALKingVectors);
SetVectors(InitialVectors);

(* PCZ [port (1-4)] [speed (300-9600)] [rz] *)
Execute(PCZname,args,ExitCode);

SetVectors(TALKingVectors);
ResetCom;

Goto(24,0);
Terminal.WriteString( "Press ENTER to return to TALK3..." );
Terminal.Read(c);
CloseCurWindow; (* restores stuff underneath *)
END;
|
6: (* Zmodem Xmit *)
IF PCZname[0] = 0C THEN
OpenWindow( w, 5,22, 8,77, TRUE, "ERROR" );
Terminal.WriteLine(" PCZ.EXE was not successfully located at startup and");
Terminal.WriteString(" can't be run. (You might try it from the DOS shell.)");
Terminal.Read( c );
CloseCurWindow;
CloseCurWindow; (* MENU window *)
ELSE
IF Capturing THEN StopCapture END;
OpenWindow( w, 5,22, 10,75, TRUE, "Zmodem send file(s)" );
Terminal.WriteString(" File name(s): ");
Terminal.ReadLine(fns);
CloseCurWindow;
CloseCurWindow; (* MENU window *)
OpenWindow( w, 0,0, 24,79, FALSE, "" ); (* Saves stuff underneath *)

CardToString(port,tempstr,1);
Concat(tempstr," ",args);
CardToString(baud,tempstr,5);
Append(args,tempstr);
Append(args," sz ");
Append(args,fns);

XOFF;
SaveVectors(TALKingVectors);
SetVectors(InitialVectors);

(* PCZ [port (1-4)] [speed (300-9600)] [rz] *)
Execute(PCZname,args,ExitCode);

SetVectors(TALKingVectors);
ResetCom;

Goto(24,0);
Terminal.WriteString( "Press ENTER to return to TALK3..." );
Terminal.Read(c);
CloseCurWindow; (* restores stuff underneath *)
END;
|
7: (* Open capFile *)
IF Capturing THEN StopCapture END;
OpenWindow( w, 5,22, 8,75, TRUE, "" );
Terminal.WriteString( " File name: " );
Terminal.ReadLine( fn );
IF fn[0] <> 0C THEN
Create( CaptureFD, fn, NORMAL );
IF CaptureFD = -1 THEN
Terminal.WriteString( " --- cannot create file" );
Terminal.Read( c );
ELSE
Capturing := TRUE;
CapPtr := 0;
END;
END;
CloseCurWindow;
CloseCurWindow; (* MENU window *)
|
8: (* Close capFile *)
IF Capturing THEN StopCapture END;
CloseCurWindow;
|
9: (* DOS shell *)
CloseCurWindow;
IF Capturing THEN StopCapture END;
ScrollUp( 0, 0,0, 25,79, attrNormal );
Goto(0,0);
WriteString('Enter "EXIT" at DOS prompt to return to TALK3...');

XOFF;
SaveVectors(TALKingVectors);
SetVectors(InitialVectors);

Execute(Comspec,'',ExitCode);

SetVectors(TALKingVectors);
ResetCom;

Terminal.WriteString('--------------------------(Returned to TALK3 from DOS)--------------------------');
DisplayLine(
" I TALK3 (C)Copyright 1987 F S T. All rights reserved. F10 = Menu",
25, attrReverse);
|
10: (* Quit *)
CloseCurWindow;
IF Capturing THEN StopCapture END;
ScrollUp( 0, 0,0, 25,79, attrNormal );
Terminate(0);
END;
Unlock( DisplayLock );
END Command;


PROCEDURE Reconfig;
VAR item :CARDINAL;
cmd :CARDINAL;
c :CHAR;
w :Window;

PROCEDURE putBaud;
BEGIN
Goto( 1,1 ); WriteString( "Baud Rate >" );

Goto( 1,15 ); WriteString( " " );
Goto( 1,15 ); WriteCard( baud, 1 );
END putBaud;

PROCEDURE putParity;
BEGIN
Goto( 3,1 ); WriteString( "Parity >" );
Goto( 3,15 );
IF parity & evenp THEN WriteString( "EVEN" );
ELSIF parity THEN WriteString( "ODD " );
ELSE WriteString( "NONE" );
END;
END putParity;

PROCEDURE putXon;
BEGIN
Goto( 5,1 ); WriteString( "Xon/Off " );
Goto( 5,15 );
IF Xon THEN WriteString( "enabled " )
ELSE WriteString( "disabled" )
END;
END putXon;

BEGIN (* Reconfig *)
OpenWindow( w, 0,0, 23,40, TRUE, "Terminal Reconfiguration" );
putBaud;
putParity;
putXon;
LOOP
PopMenu( 9,7, "Change|Baud|Parity|Xon/Xoff", 0, FALSE, item );
IF item = 0 THEN
EXIT
ELSE
CASE item OF
1: PopMenu( 10,10, "baud|300|600|1200|2400|4800|9600|19200|38400",
0, TRUE, cmd );
CloseCurWindow; (* loop MENU *)
IF cmd > 0 THEN
baud := 300;
WHILE cmd > 1 DO
INC( baud, baud );
DEC( cmd );
END;
putBaud;
END;
|
2: PopMenu( 11,10, "parity|EVEN|ODD|NONE", 0, TRUE, cmd );
CloseCurWindow; (* loop MENU *)
IF cmd > 0 THEN
parity := cmd < 3;
evenp := cmd = 1;
putParity;
IF parity THEN bits := 7
ELSE bits := 8
END;
END;
|
3: Xon := NOT Xon;
IF Xon THEN XON ELSE XOFF END;
CloseCurWindow; (* loop MENU *)
putXon;
END;
END;
END;
Init( port, baud, 1, parity, evenp, bits, comBuffSize, ok );
IF NOT ok THEN WriteString( "failed rs232 Init" ); Terminate(1) END;
CloseCurWindow;
END Reconfig;



CONST BUFFSIZE = 512;

VAR
XmodemFD :INTEGER;
SendFD :INTEGER;
SendBuff :ARRAY [0..BUFFSIZE-1] OF CHAR;
BuffEnd :CARDINAL;
SendPtr :CARDINAL;

CONST
CapBufferSize = 512;

VAR
CaptureFD :INTEGER;
CapBuffer :ARRAY [0..512] OF CHAR;
CapPtr :CARDINAL;


PROCEDURE Capture( c :CHAR );
BEGIN
CapBuffer[CapPtr] := c;
INC( CapPtr );
IF CapPtr >= CapBufferSize THEN
FlushCaptureBuffer
END;
END Capture;


PROCEDURE FlushCaptureBuffer;
VAR n :CARDINAL;
BEGIN
IF CapPtr > 0 THEN
Write( CaptureFD, ADR(CapBuffer), CapPtr, n );
CapPtr := 0;
END;
END FlushCaptureBuffer;


PROCEDURE StopCapture;
BEGIN
FlushCaptureBuffer;
Close( CaptureFD );
Capturing := FALSE;
END StopCapture;


(*PROCESS*) PROCEDURE ReadRS232;
(*
This process Waits on Signals from the RS232 driver.
On each signal, we try to process a COM input character.
*)
VAR c :CHAR;
ok :BOOLEAN;
lockedSend :BOOLEAN;
BEGIN
lockedSend := FALSE;
LOOP
Wait( RS232Input );
IF Sending THEN
GetCom( c, ok );
IF ok THEN
IF (c = CtrlS) OR (c = CtrlQ) THEN
IF c = CtrlS THEN
Lock( SendLock );
lockedSend := TRUE;
ELSIF c = CtrlQ THEN
Unlock( SendLock );
END;
ELSE
IF Capturing THEN Capture( c ) END;
Display.Write( c );
END;
END;
ELSIF lockedSend THEN Unlock( SendLock )
ELSE
Lock( DisplayLock );
GetCom( c, ok );
IF ok THEN
IF Capturing THEN Capture( c ) END;
Display.Write( c );
END;
Unlock( DisplayLock );
END;
END;
END ReadRS232;


VAR KeyboardInput :SignalHeader;

MODULE KeyboardTrap;
(* We must run with interrupts enabled because the AT's BIOS
ISR depends on these interrupts to talk to the keyboard!
*)
IMPORT ASSEMBLER, ADDRESS, TermProcedure,
GetVector, ResetVector, WaitIO, Signal, KeyboardInput;

EXPORT CheckKeyboard;

VAR KeyboardHandler :ADDRESS;

(*PROCESS*) PROCEDURE CheckKeyboard;
(*
This process Signals ReadKbd whenever a keyboard interrupt occurs.
*)
BEGIN
LOOP
WaitIO( 9 );
ASM
PUSHF
CALL FAR KeyboardHandler
END;
Signal( KeyboardInput );
END;
END CheckKeyboard;

PROCEDURE restoreKeyboard;
BEGIN
ResetVector( 9, KeyboardHandler );
END restoreKeyboard;

BEGIN
GetVector( 9, KeyboardHandler );
TermProcedure( restoreKeyboard );
END KeyboardTrap;


(*PROCESS*) PROCEDURE ReadKbd;
(*
This process Waits for Signals from CheckKeyboard.
On a signal, we poll the keyboard for possible input.
*)
VAR i :CARDINAL;
c :CHAR;
BEGIN
LOOP
Wait( KeyboardInput );
WHILE KeyPressed() DO
(* Because we run the Keyboard Trap w/ interrupts enabled,
it is possible that more than 1 key was pressed for a
given signal sent to us.
*)
GetKeyCh( c );
IF Sending THEN
IF c = ESC THEN
Sending := FALSE;
Signal( RS232Input ); (* wake up in case of locked Send *)
END;
ELSIF c = F10 THEN Command
ELSE
PutCom( c );
END;
END;
END;
END ReadKbd;


VAR SendTextSignal :SignalHeader;

(*PROCESS*) PROCEDURE SendText;
(*
This process Waits on the SendTextSignal.
On receipt of a signal, the process goes to work sending the
text file to the remote system.
During the send loop (WHILE sending), SendLock is used so that
the ReadRS232 process may communicate the receipt of XOFF and XON
characters from the other system.

Notice that Sending can be turned off by the ReadKbd process,
which "shares" this global variable with SendText.
*)
BEGIN
LOOP
Wait( SendTextSignal );
IF Sending THEN
WHILE Sending DO
Lock( SendLock );
IF SendPtr >= BuffEnd THEN
Read( SendFD, ADR(SendBuff), BUFFSIZE, BuffEnd );
SendPtr := 0;
Sending := BuffEnd <> 0;
END;
IF Sending THEN
PutCom( SendBuff[SendPtr] );
INC( SendPtr );
END;
Unlock( SendLock );
END;
Close( SendFD );
END;
END;
END SendText;


PROCEDURE usage;
BEGIN
WriteString( "usage: Talk3 [port# [baud [parity]]]" ); WriteLn;
WriteString( " port: 1 | 2" ); WriteLn;
WriteString( " baud: 50, 110, 300..38400" ); WriteLn;
WriteString( " parity: NONE | EVEN | ODD" ); WriteLn;
Terminate(1);
END usage;


PROCEDURE SaveVectors(VAR SaveArea:ARRAY OF CARDINAL);
BEGIN
ASM
MOV CX,64 (* Count *)
LES DI,SaveArea (* ES:DI ==> destination *)
XOR SI,SI
PUSH DS
MOV DS,SI (* DS:SI (0000:0000) ==> source *)
CLI
CLD
REP MOVSW
STI
POP DS
END;
END SaveVectors;


PROCEDURE SetVectors(VAR SaveArea:ARRAY OF CARDINAL);
BEGIN
ASM
MOV CX,64 (* Count *)
PUSH DS
LDS SI,SaveArea (* DS:SI ==> source *)
XOR DI,DI
MOV ES,DI (* ES:DI (0000:0000) ==> destination *)
CLI
CLD
REP MOVSW
STI
POP DS
END;
END SetVectors;


(*** Main program starts here ***)

VAR
ok :BOOLEAN;
c :CHAR;
par :CARDINAL;
w :Window;
arg :ARRAY [0..10] OF CHAR;
n :CARDINAL;

BEGIN
SaveVectors(InitialVectors);
Sending := FALSE; Capturing := FALSE;
GetArg( arg, n );
IF n > 0 THEN
StringToCard( arg, port, ok );
IF NOT ok OR (port < 1) OR (port > 2) THEN usage END;
ELSE port := 1
END;
GetArg( arg, n );
IF n > 0 THEN
StringToCard( arg, baud, ok );
IF NOT ok THEN usage END;
ELSE baud := 1200
END;
GetArg( arg, n );
IF n > 0 THEN
REPEAT DEC(n); arg[n] := CAP(arg[n])
UNTIL n = 0;
IF CompareStr( arg, "EVEN" ) = 0 THEN
parity := TRUE; evenp := TRUE; bits := 7;
ELSIF CompareStr( arg, "ODD" ) = 0 THEN
parity := TRUE; evenp := FALSE; bits := 7;
ELSIF CompareStr( arg, "NONE" ) = 0 THEN
parity := FALSE; evenp := FALSE; bits := 8;
ELSE
usage
END;
ELSE
parity := FALSE; evenp := FALSE; bits := 8;
END;

GetEnv("PCZ",PCZpath);

IF PCZpath[0] = 0C THEN
Terminal.WriteLn;
Terminal.WriteLine('Environment variable ("PCZ") giving [drive:]pathname');
Terminal.WriteLine('where PCZ.EXE resides was not found.');
Terminal.WriteLn;
END;

PCZfound := FALSE;
REPEAT
IF PCZpath[Length(PCZpath)-1] <> '\' THEN Append(PCZpath,"\") END;
Concat(PCZpath,"PCZ.EXE",PCZname);
Open(XmodemFD, PCZname, READ);
IF XmodemFD = -1 THEN
Terminal.WriteLn;
Terminal.WriteString("Cannot locate file ");
Terminal.WriteString(PCZname);
Terminal.WriteLine(".");
Terminal.WriteLn;
Terminal.WriteLine('Enter "*" to start TALK3 anyway, or enter [drive:]directory path');
Terminal.WriteString("where PCZ.EXE may be found: ");
Terminal.ReadLine(PCZpath);
Terminal.WriteLn;
IF (PCZpath[1] = 0C) AND (PCZpath[0] = '*') THEN
PCZname[0] := 0C;
PCZfound := TRUE;
END;
ELSE
Close(XmodemFD);
Terminal.WriteLn;
Terminal.WriteString(PCZname);
Terminal.WriteLine(" has been located.");
Terminal.WriteLn;
PCZfound := TRUE;
END;
UNTIL PCZfound;

IF PCZname[0] <> 0C THEN
Terminal.WriteString(" Press ENTER to start TALK3...");
Terminal.Read(c);
END;

GetEnv("COMSPEC",Comspec);
IF Comspec[0] = 0C THEN Comspec := "\COMMAND.COM" END; (* we're guessing here *)

DisplayLine(
" I TALK3 (C)Copyright 1987 F S T. All rights reserved. F10 = Menu",
25, attrReverse
);
OpenWindow( w, 0,0, 23,79, FALSE, "" );

InitSignal( KeyboardInput );
InitSignal( SendTextSignal );
InitLock( SendLock );
InitLock( DisplayLock );

NewProcess( CheckKeyboard, 512, TRUE );
NewProcess( ReadRS232, 512, FALSE );
NewProcess( SendText, 512, FALSE );

Init( port, baud, 1, parity, evenp, bits, comBuffSize, ok );
IF NOT ok THEN Terminate(1) END;
XON; Xon := TRUE;

ReadKbd;

END Talk3.

  3 Responses to “Category : Modula II Source Code
Archive   : TALK3.ZIP
Filename : TALK3.MOD

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/