home *** CD-ROM | disk | FTP | other *** search
- {$C-} {no user interrupts}
- {$U-}
- {$K-} {no stack checking - program works}
- program Modem;
-
- { Written by Jack M. Wierda Chicago Illinois
- Modified by Steve Freeman
-
- LANGUAGE: TURBO Pascal
- This program is in the public domain.
-
- This program is basically a re-write in PASCAL of Ward Christensen's
- Modem Program which was distributed in CP/M User's Group Volume 25. Identical
- and compatible options are provided to allow this program to work directly
- with XMODEM running under CP/M. }
-
- const
- Version = '12-Nov-84';
- FredsPhone = '7-5038';
- SignOnLine = 'ACGM10,RLIP,PSSWD';
- MaxPhoneNums = 26;
- COMport = 1;
-
- NUL = 00; SOH = #$01; EOT = #$04; ACK = #$06;
- TAB = 09; LF = #$0A; CR = #$0D; NAK = #$15;
- Space = ' '; DELete = $7F;
-
- lastbyte = 127;
- timeout = 256;
- errormax = 5;
- retrymax = 5;
- loopspersec = 6500;
- Intseg: integer = 0; {filled with interrupt segment address}
-
- type maxstr = string[255];
- PhoneEntry = string[32];
- PhoneStr = string[20];
- BytePointer = ^byte;
-
- var COMbase: integer; {this will point to the Communications base}
- WorkFile: file;
- PhoneFile: text;
- PhoneList: array[1..MaxPhoneNums] of PhoneEntry;
- option, hangup, return, mode, baudrate : char;
- sector : array[0..lastbyte] of byte;
- base, N_Phones: integer;
-
- { interrupt vectors and pointers to them }
- newvec, oldvec: BytePointer;
- INT3: BytePointer absolute $0000:$002C; {for COM2:}
- INT4: BytePointer absolute $0000:$0030; {for COM1:}
- rcvbuf: array[0..127] of byte;
- inptr, outptr: integer;
- datardy: boolean;
- {.pa}
- type hexstr = string[4];
- function hex(num: integer): hexstr;
- var i, j: integer;
- h: string[16];
- str: hexstr;
- begin
- str := '0000'; h := '0123456789ABCDEF'; j := num;
- for i:=4 downto 1
- do begin
- str[i] := h[(j and 15)+1];
- j := j shr 4;
- end;
- hex := str;
- end;
- {.cp10}
- function GetYN: char;
- var c: char;
- begin
- repeat
- read(kbd,c);
- c := upcase(c);
- until c in ['Y','N'];
- writeln(c);
- GetYN := c
- end;
- {.cp4}
- procedure SetDTR;
- begin
- port[base+4] := $09; {DTR on and INT enabled}
- end;
- {.cp4}
- procedure HangUpPhone; {hang up by terminating the line}
- begin
- port[base+4] := 0;
- end;
- {.cp7}
- function status: integer;
- var st: integer;
- begin
- st := port[base+5];
- st := st shl 8 + port[base+6];
- status := st;
- end;
- {.cp6}
- procedure send(ch: char);
- var s: byte;
- begin
- repeat s := port[base+5] and $20 until (s=$20);
- port[base] := ord(ch);
- end;
- {.cp6}
- function get_rcv_char: char;
- begin
- get_rcv_char := chr(rcvbuf[outptr]);
- outptr := (outptr + 1) and $7F;
- if inptr=outptr then datardy := false;
- end;
- {.cp5}
- function receive: char;
- begin
- repeat until datardy;
- receive := get_rcv_char;
- end;
- {.cp9}
- function ReadLine(seconds:integer): integer;
- var j : integer;
- begin
- j := loopspersec * seconds;
- repeat j := j-1 until datardy or (j = 0);
- if j = 0
- then readline := timeout
- else readline := ord(get_rcv_char);
- end;
- {.cp8}
- procedure PurgeLine; {purge the receive register}
- var c: char;
- begin
- repeat
- if datardy then c := get_rcv_char;
- delay(35); { 300 baud time period for received char }
- until not(datardy)
- end;
- {.cp42}
- procedure Set_RS232_Vector;
-
- procedure Int_Handler;
- { This routine buffers all incoming received data }
- begin
- inline($50/$52/$57/$1E/ {save registers}
- $2E/ {CS:}
- $8E/$1E/Intseg/ {MOV DS,[Intseg]} {get data segment pointer}
- $BA/$FD/$03/ {MOV DX,$3FD} {is character ready?}
- $EC/ {IN AL,DX}
- $24/$01/ {AND AL,01}
- $74/$19/ {JZ here} { no, skip entry}
- $BA/$F8/$03/ {MOV DX,$3F8} { yes, get pointer}
- $A1/inptr/ {MOV AX,[inptr]} {get index to buffer}
- $97/ {XCHG DI,AX}
- $EC/ {IN AL,DX} {get data from receiver}
- $88/$85/rcvbuf/ {MOV [DI+rcvbuf],AL} {put data into buffer}
- $97/ {XCHG DI,AX} {increment pointer}
- $40/ {INC AX}
- $24/$7F/ {AND AL,$7F}
- $A3/inptr/ {MOV [inptr],AX}
- $B8/$01/$00/ {MOV AX,1} {show data is ready}
- $A2/datardy/ {MOV [datardy],AX}
- {here}
- $B0/$64/ {MOV AL,64} {EOI, level 4 on 8259}
- $E6/$20/ {OUT 20,AL}
- $1F/$5F/$5A/$58/$CF); {restore and return}
- end;
-
- begin
- Intseg := Dseg;
- COMbase := $0400 + 2 * (COMport - 1);
- oldvec := INT4;
- newvec := ptr(cseg,ofs(Int_Handler)+7+5);
- INT4 := newvec;
- inline($BA/$3F8/ {MOV DX,BASE}
- $EC/$EC/$EC/$EC/ {IN AL,DX}
- $BA/$3FD/$EC/ {MOV DX,BASE+5 ! IN AL,DX}
- $BA/$3FE/$EC); {MOV DX,BASE+6 ! IN AL,DX}
- datardy := false; inptr := 0; outptr := inptr;
- inline($E4/$21/$24/$EF/$E6/$21); {turn off IRQ mask bit - enabled}
- end;
- {.cp16}
- procedure Setup(md, brc: char);
- var al: integer;
- begin
- base := memw[0:COMbase];
- port[base+3] := $83; {access baud rate divisor and sets
- 8 data, no parity, 1 stop}
- if md='O' then mode:=' ' else mode:='R';
- baudrate := brc;
- if baudrate='1'
- then portw[base] := $0060 {1200 baud}
- else portw[base] := $0180; { 300 baud}
- port[base+3] := $03; {set access for xmt/rcv}
- port[base+1] := $01; {enable receiver interrupts}
- SetDTR; {put station on-line}
- return := 'N';
- end;
- {.cp16}
- procedure Initialize;
- var mode, baudrate: char;
- begin
- repeat
- write('Mode : A(nswer), O(riginate) ? ');
- read(kbd,mode); mode := upcase(mode);
- until mode in ['A','O'];
- writeln(mode);
- repeat
- write('Baud rate : 3(00), 1(200) ? ');
- read(kbd,baudrate);
- until baudrate in ['1','3'];
- writeln(baudrate);
- Setup(mode,baudrate);
- end;
- {.cp19}
- procedure terminal;
- var s, t: byte;
- c: char;
- begin {$I-} {no I/O checking here}
- writeln('Use ctrl-E to exit terminal mode.');
- repeat
- s := port[base+5]; {get status}
- if datardy
- then begin
- t := ord(get_rcv_char); t := t and $7F;
- if t<>$7F then write(chr(t));
- end;
- if keypressed and ((s and $20) = $20)
- then begin
- read(kbd,c);
- port[base] := ord(c);
- end;
- until (c = ^E);
- end; {$I+}
- {.cp5}
- procedure sendtext(str: maxstr);
- var i: integer;
- begin
- for i:=1 to length(str) do send(str[i]);
- end;
- {.cp20}
- function Dial(PhoneNumber: PhoneStr): char;
- var c, kc: char;
- t: integer;
- begin
- HangUpPhone; write(cr,lf,'Dialing: ',PhoneNumber);
- delay(250); SetDTR; delay(250); sendtext(cr); delay(1000);
- sendtext('AT '+mode+'M1V0DT'+PhoneNumber+cr); delay(2000);
- c := receive; c := chr(0); repeat c := get_rcv_char until (c=cr);
- write(', Waiting for carrier ...');
- t := 60 * loopspersec;
- repeat
- t := t - 1;
- if datardy then c := get_rcv_char;
- if keypressed then read(kbd,kc);
- until (c in ['0'..'5']) or (t=0) or (kc=^E);
- if c='1'
- then writeln(' connected.')
- else if (t=0) or (kc=^E) then c := '9';
- Dial := c
- end;
- {.cp15}
- procedure SignOn;
- var i: integer;
- c: char;
- begin
- write('Signing on ... ');
- delay(2000);
- for i:=1 to 7
- do begin
- send('8');
- delay(333);
- end;
- sendtext('('+cr);
- delay(2500); sendtext(SignOnLine+cr);
- writeln('all set !');
- end;
- {.pa}
- procedure SendFile;
- var j, sectornum, counter, checksum : integer;
- filename : string[20];
- c: char;
-
- procedure SendIt;
- begin
- sectornum := 1;
- repeat
- counter := 0;
- blockread(WorkFile,sector,1);
- repeat
- write(cr,'Sending sector ', sectornum);
- send(SOH); send(chr(sectornum)); send(chr(-sectornum-1));
- checksum := 0;
- for j:=0 to lastbyte
- do begin
- send(chr(sector[j]));
- checksum := (checksum + sector[j]) mod 256
- end;
- send(chr(checksum));
- purgeline;
- counter := counter + 1;
- until (readline(10) = ord(ack)) or (counter = retrymax);
- sectornum := sectornum + 1
- until (eof(WorkFile)) or (counter = retrymax);
- if counter = retrymax
- then writeln(cr,lf,'No ACK on sector')
- else begin
- counter := 0;
- repeat
- send(EOT);
- counter := counter + 1
- until (readline(10)=ord(ack)) or (counter=retrymax);
- if counter = retrymax
- then writeln(cr,lf,'No ACK on EOT')
- else writeln(cr,lf,'Transfer complete');
- end;
- end;
-
- begin
- write('Filename.Ext ? '); readln(filename);
- if length(filename)>0
- then begin
- assign(WorkFile,filename);
- reset(WorkFile);
- SendIt;
- close(WorkFile)
- end;
- end;
- {.pa}
- procedure readfile;
- var j, firstchar, sectornum,sectorcurrent, sectorcomp, errors,
- checksum : integer;
- errorflag : boolean;
- filename : string[20];
-
- procedure ReceiveIt;
- begin
- sectornum := 0; errors := 0;
- send(nak); send(nak); { send ready characters }
- repeat
- errorflag := false;
- repeat
- firstchar := readline(20)
- until firstchar in [ord(SOH),ord(EOT),timeout];
- if firstchar = timeout then writeln(cr,lf,'Error - No starting SOH');
- if firstchar = ord(SOH)
- then begin
- sectorcurrent := readline(1); {real sector number}
- sectorcomp := readline(1); {+ inverse of above}
- if (sectorcurrent+sectorcomp)=255 {<-- becomes this #}
- then begin
- if (sectorcurrent=sectornum+1)
- then begin
- checksum := 0;
- for j := 0 to lastbyte
- do begin
- sector[j] := readline(1);
- checksum := (checksum+sector[j]) and $00FF
- end;
- if checksum=readline(1)
- then begin
- blockwrite(WorkFile,sector,1);
- errors := 0;
- sectornum := sectorcurrent;
- write(cr,'Received sector ',sectorcurrent);
- send(ack)
- end
- else begin
- writeln(cr,lf,'Checksum error');
- errorflag := true
- end
- end
- else if (sectorcurrent=sectornum)
- then begin
- repeat until readline(1)=timeout;
- writeln(cr,lf,'Received duplicate sector ', sectorcurrent);
- send(ack)
- end
- else begin
- writeln(cr,lf,'Synchronization error');
- errorflag := true
- end
- end
- else begin
- writeln(cr,lf,'Sector number error');
- errorflag := true
- end
- end;
- if errorflag then begin
- errors := errors+1;
- repeat until readline(1)=timeout;
- send(nak)
- end;
- until (firstchar in [ord(EOT),timeout]) or (errors = errormax);
- if (firstchar=ord(EOT)) and (errors<errormax)
- then begin
- send(ack);
- writeln(cr,lf,'Transfer complete')
- end
- else writeln(cr,lf,'Aborting');
- end;
-
- begin
- write('Filename.Ext ? '); readln(filename);
- if length(filename)>0
- then begin
- assign(WorkFile,filename);
- rewrite(WorkFile);
- ReceiveIt;
- close(WorkFile);
- end;
- end;
- {.cp17}
- function ReadPhoneList: integer;
- var index: integer;
- begin
- assign(PhoneFile,'MODEM.PHN');
- index := 0;
- {$I-} reset(PhoneFile); {$I+}
- if IOresult=0
- then begin
- while (not eof(PhoneFile)) and (index<26)
- do begin
- index := index + 1;
- readln(PhoneFile,PhoneList[index]);
- end;
- close(PhoneFile);
- end;
- ReadPhoneList := index;
- end;
- {.cp41}
- procedure Call;
- var rc: char;
- selection, i, j, k: integer;
- PhoneNo: PhoneStr;
- begin
- if N_Phones>0
- then begin
- clrscr; writeln;
- for i:=1 to N_Phones
- do begin
- if (i mod 2)=0
- then write(' ')
- else writeln;
- write(chr(i+64),' - ',PhoneList[i]);
- end;
- writeln; writeln; write('Enter selection letter: ');
- repeat
- repeat until keypressed;
- read(kbd,rc); rc := upcase(rc);
- selection := ord(rc) - ord('@');
- until (selection in [1..N_Phones]);
- writeln(rc);
- mode := PhoneList[selection][31];
- baudrate := PhoneList[selection][32];
- Setup(mode,baudrate);
- j := 30; PhoneNo := '';
- while PhoneList[selection][j]<>'.' do j:=j-1;
- for k:=j+1 to 30 do PhoneNo := PhoneNo + PhoneList[selection][k];
- rc := Dial(PhoneNo);
- end
- else rc := Dial(FredsPhone);
- if rc='1'
- then begin
- if N_Phones=0
- then SignOn
- else if selection=1 then Signon;
- terminal;
- end
- else HangUpPhone;
- end;
- {.cp22}
- procedure GetOption;
- begin
- clrscr;
- writeln('Modem, ',Version);
- gotoxy(7,4); writeln('Options:');
- writeln;
- writeln(' R - receive a file');
- writeln(' S - send a file');
- writeln(' T - terminal mode');
- writeln;
- writeln(' C - place a call');
- writeln(' H - hang up the phone');
- writeln(' O - option configuration');
- writeln(' X - exit to system');
- writeln; write('which ? ');
- repeat
- read(kbd,option);
- option := upcase(option);
- until option IN ['O','C','R','S','T','H','X'];
- writeln(option);
- end;
- {.cp16}
- begin {Modem}
- Set_RS232_Vector;
- N_Phones := ReadPhoneList;
- Setup('O','1'); { default of Originate/1200 baud }
- repeat
- GetOption;
- case option of
- 'T': Terminal;
- 'R': ReadFile;
- 'S': SendFile;
- 'O': Initialize;
- 'C': Call;
- 'H': HangUpPhone;
- 'X': return := 'Y';
- end;
- until return='Y';
- inline($E4/$21/$0C/$10/$E6/$21); {turn on IRQ mask bit - disabled}
- (* INT4 := oldvec; {restore the old RS232 vector} *)
- end.
-