Old School RS232 as Network Media Layer

Thread Starter

djsfantasi

Joined Apr 11, 2010
9,237
Just reminiscing I’d love to hear your stories pre-Internet and pre-LAN!

In the early days of LAN technology, before Novell Netware (and way before MS’ LAN Manager and IBM’s LAN Server (and Vines and 3-Com Shsre+), I managed a team of PC Software developers. File sharing was via SneakerNet protocol.

I spent a long weekend and coded a file sharing system with a centralized file server and individual workstations connected in a star network via... old-school RS232 cables. Max distance was 20 feet. This was 12V RS232.

The workstations each had an utility that provided copy, download and directory services. There was no security. Just read, write & list folder.

It was successful in that at least it simplified development by providing file sharing. It was replaced in a couple of years when Netware became affordable. And Netware was replaced by Microsoft’s newest offering.

We did consider IBM’s LAN Server until I went to class and could totally hack into the systems at the training center. I never recovered a consulting fee.
 

nsaspook

Joined Aug 27, 2009
16,326
I did this on the old Atari ST. It could use either the serial or MIDI ports for file sharing between computers. The file sharing program was written in Modula-2 as part of a multi-tasking OS project MX2. It was called MX2NET.

https://www.atarimagazines.com/startv4n12/multitaskingonst.html
There are two TOS (non-GEM) multitasking environments for those interested in studying operating systems: MX2 and MINIX.

My old source code is still here in the Atari archive.
mx2src.arc
http://www.umich.edu/~archive/atari/Cli/

edit: found some updates of my network code for midi and serial links

Some of the network code in Modula-2.
(* First Release 12/8/87 *)
(* Added support for use of MIDI or AUX port for network *)
(* 2/6/88 *)
(* *)

DEFINITION MODULE NETWORK;
FROM BIOS IMPORT Device;

TYPE
stat = RECORD
inpackets : LONGCARD;
outpackets : LONGCARD;
retrys : LONGCARD;
checksumerrors : LONGCARD;
timeouts : LONGCARD;
rwabsreqs : LONGCARD;
resets : LONGCARD;
END;

VAR netdevice : Device;
NETTIME : BOOLEAN;
NETMASK : BOOLEAN;
MEMMASK : BOOLEAN;
PHYSLOW : BOOLEAN;

PROCEDURE initnetwork(port: Device);
(* install network, default is network on ,MIDI port *)
PROCEDURE recframe; (* check network for received data *)
PROCEDURE NoCritrecframe; (* check network for received data *)
PROCEDURE networkoff; (* turn on network *)
PROCEDURE networkon; (* turn off network *)
PROCEDURE GetOpt;

END NETWORK.
Code fragment from the Modula-2 network implementation code for MIDI or RS232
IF port=HSS THEN
rbptr:=IORec(MIDI);
ELSE
rbptr:=IORec(RS232);
END;
MX2NET program code. Runs as a background process in the MX2 OS system.
(* First Release 12/8/87 *)
(* *)

MODULE mx2net; (*$T-$S-,$A+ *)
FROM SYSTEM IMPORT CODE,ADDRESS;
FROM NETWORK IMPORT initnetwork,recframe;
FROM GEMX IMPORT BasePageAddress;
FROM GEMDOS IMPORT TermRes,Super;
FROM BIOS IMPORT Device;
VAR
vblptr [456H] : POINTER TO ARRAY [0..7]
OF ADDRESS;
i : CARDINAL;
ssv : ADDRESS;

(* it runs as a background process in a vbl time slot *)
(*$P- *)
PROCEDURE VBLrecframe;
BEGIN
CODE(02f39H,0,04a2H); (* move.l $4a2,-(sp) save BIOS pointer *)
CODE(04b9H,0,02eH,0,04a2H); (* sub 46 from pointer *)
recframe; (* check network for data *)
CODE(023dfH,0,04a2H); (* restore BIOS pointer *)
CODE(4e75H); (* rts *)
END VBLrecframe;
(*$P+ *)

BEGIN
initnetwork(HSS); (* select serial device *)
i:=1; (* don't use 0 because some programs don't check. *)
ssv:=0;
Super(ssv);
WHILE vblptr^#0H DO
INC(i);
END;
(* set up vbl vector for NETWORK input *)
vblptr^ := ADDRESS(VBLrecframe);
Super(ssv);
WITH BasePageAddress^ DO
TermRes(CodeLen+BssLen+LONGCARD(CodeBase-ADDRESS(BasePageAddress)),0);
END;
END mx2net.
The network driver code.
(* First Release 12/8/87 *)
(* Added drive config data routines, clean up VBL code, *)
(* Remote time option 12/17/89 *)
(* *)

IMPLEMENTATION MODULE NETWORK ;

(* --------------------------------------------------------------------------

NETWORK : TWO CPU NETWORK FOR TDI Modula-2/ST

--------------------------------------------------------------------------*)

(*$T-,$S-,$A+ *)

FROM SYSTEM IMPORT ADDRESS, ADR, SETREG, CODE, REGISTER ,BYTE ,TSIZE;
FROM BIOS IMPORT BPB ,BConStat ,BConIn, BCosStat, BConOut, Device,
MediaChange,MCState,GetBPB,RWAbs,RW,DriveSet,DriveMap;
FROM XBIOS IMPORT SuperExec,IORec,IORECPTR,IOREC,SerialDevice,
GetDateTime,ScreenPhysicalBase;
FROM GEMDOS IMPORT TermRes,Open,Close,SetDate,SetTime ;
IMPORT GEMDOS;
FROM ASCII IMPORT SYN,STX,SOH,BEL,CR,LF,ESC;

CONST
MaxSeq = 1;
Maxdrives = 31; (* number of disk drives minus 1 *)
recsize = 511;
retry = 5;
MAGIC = 314159;
Memdrive = 31;
chanwait = 3;
debug = FALSE;
trace = FALSE;
TITLE = "MX2NET Version 2.0";

(* Because we dont know what registers the BIOS is using we must use
the following opcodes to save the registers *)
MOVEMDEC = 48E7H ; (* 68000 opcode for MOVEM <regs>,-(A7) *)
MOVEMINC = 4CDFH ; (* 68000 opcode for MOVEM (A7)+,<regs> *)
SAVEREGS = 07FFCH ; (* Registers D1..A5 for DEC *)
RESTREGS = 03FFEH ; (* Registers D1..A5 for INC *)
RTS = 04E75H ; (* 68000 return from subroutine opcode *)

TYPE
(* Procedure types to mimic correct sequence for "C" BIOS routines *)

CBPBProc = PROCEDURE ( CARDINAL ) ;
CMediaChProc = PROCEDURE ( CARDINAL ) ;
CRWAbsProc = PROCEDURE ( CARDINAL, CARDINAL, CARDINAL, ADDRESS, CARDINAL );
MIDIbuffer = ARRAY [0..1023] OF CARDINAL;
SequenceNr = [0..MaxSeq];
message = ARRAY [0..recsize] OF BYTE;
message1 = ARRAY [0..17] OF BYTE;
FrameKind = (ack,data,callreq,callaccp,clearreq,clearconf,
resetreq,diag);
DataKind = (rdmediareq,rdmediaconf,rdbpbreq,rdbpbconf,
rdrwabsreq,rdrwabsconf,memreq,memconf,timereq,timeconf);
evtype = (framearrival,cksumerr,timeout,hostready,reset,nothing);
channel = (none,local,remote);

frame = RECORD
syn : CHAR; (* these are sync chars *)
stx : CHAR; (* for the frames *)
kind : FrameKind;
seq : SequenceNr;
ack : SequenceNr;
cmd : DataKind;
rw : CARDINAL; (* read or write data *)
recno : CARDINAL; (* sector for data*)
d0 : LONGCARD; (* data return variable *)
info : message;
cksum : CARDINAL;
END;

framecptr = POINTER TO framecmd;

framecmd = RECORD
syn : CHAR; (* these are sync chars *)
stx : CHAR; (* for the frames *)
kind : FrameKind;
seq : SequenceNr;
ack : SequenceNr;
cmd : DataKind;
rw : CARDINAL; (* read or write data *)
recno : CARDINAL; (* sector for data*)
d0 : LONGCARD; (* data return variable *)
info : message1;
cksum : CARDINAL;
END;

control = RECORD
magic : LONGCARD;
reset : BOOLEAN;
networkactive : BOOLEAN;
remotedrive : CARDINAL;
drivemap : DriveSet;
nextframetosend : SequenceNr;
frameexpected : SequenceNr;
sendreset : BOOLEAN;
END;

netmap = RECORD
Remote : CARDINAL;
Local : CARDINAL;
Write : BOOLEAN;
END;

frameptr = POINTER TO ARRAY [0..1024] OF BYTE;

VAR


(* BIOS variables : These can only be accessed with the 68000 in supervisor
mode. The Modula-2 language allows you to fix the location of variables *)

HDBPB [0472H] : ADDRESS ; (* hard disk get Bios Parameter Block *)
HDRWAbs [0476H] : ADDRESS ; (* hard disk read/write abs *)
HDMediaCh [047EH] : ADDRESS ; (* hard disk media change *)
EvtCritic [0404H] : ADDRESS ; (* evt_critic *)
DriveBits [04C2H] : SET OF [0..31]; (* disk drives present map *)
flock [043EH] : LONGCARD; (* disk access in progress *)
hz200 [04baH] : LONGCARD; (* 200hz clock counter *)

NetBits : SET OF [0..31];
Dptr : DriveSet; (* save original drive map *)
Mptr : LONGCARD;
charcount,framesize,cksum,recframesize,sndframesize,
SIZEframe,SIZEframecmd : CARDINAL;

networkconnect : BOOLEAN; (* DCD = 1 TRUE *)
gotframe : BOOLEAN;
framebufferfull : BOOLEAN;
cleartosend : BOOLEAN;
readytosend : BOOLEAN;
requesttosend : BOOLEAN;
framewaiting : BOOLEAN;
OK,installed : BOOLEAN;
gotmediach : ARRAY [0..Maxdrives] OF BOOLEAN;
gotbpb : ARRAY [0..Maxdrives] OF BOOLEAN;
networkerror : BOOLEAN;
shortframe : BOOLEAN;
vblLock : BOOLEAN;
rwabsLock : BOOLEAN;
TIMESET : BOOLEAN;
OneTime : BOOLEAN;
ChannelLock : channel;
NetMap : ARRAY [0..Maxdrives] OF netmap;
NetInfo : ARRAY [0..128] OF CHAR;
statptr : POINTER TO stat;

sframe,rframe,SFRAME,RFRAME : frame;
rframeptr : frameptr;
framecmdptr : framecptr;
sframecmdptr : framecptr;
event : evtype;
C : control;
S : stat;
recchar : LONGCARD;
result,i,i1,mediacount,handle : INTEGER;
D0ptr : POINTER TO LONGCARD;
wsector,drvnr,d,R : CARDINAL;
rbuffer : MIDIbuffer;
rbptr,kbdiorec : IORECPTR;
numBytes,sec,min,hour,time,count : LONGCARD ;
status : LONGINT ;
sframeptr : frameptr;

(* The following are saved copies of the BIOS variables so that the real
hard disk routines can be called if a hard disk access is requested. *)

SaveHDBPB : CBPBProc ; (* hard disk get Bios Parameter Block *)
SaveHDRWAbs : CRWAbsProc ; (* hard disk read/write abs *)
SaveHDMediaCh : CMediaChProc ; (* hard disk media change *)
SaveCritic : PROC;

(* NETWORK control *)

NetworkBPB : ARRAY [0..Maxdrives] OF BPB ; (* BIOS Parameter block for NETWORK *)


PROCEDURE inc(VAR k: SequenceNr); (* increment k circulary *)
BEGIN
IF k<MaxSeq THEN INC(k) ELSE k:=0 END;
END inc;

MODULE NETBIOS;
IMPORT getfromremote,frameptr,ADDRESS,CODE,MOVEMDEC,SAVEREGS,status,
Memdrive,statptr,S,MOVEMINC,RESTREGS,NetBits,wsector,DataKind,
C,NetMap,resetnewdisk,channel,ADR,rwabsLock,frame,
networkerror,SETREG,SaveHDBPB,SaveHDRWAbs,SaveHDMediaCh,TSIZE,
newdisk,gotbpb,gotmediach,MCState,NetworkBPB,BPB;

EXPORT RDRWAbs,RDMediaCh,RDBPB;

VAR i3 : CARDINAL;
bpbptr,nbpbptr : frameptr;
nframe1 : frame;

PROCEDURE MoveMemory ( From, To : ADDRESS ; Bytes : LONGCARD ) ;
(* This routine shows how time critical portions of code can be optimised to
run faster. It relys on the code generation rules of the compiler which
can be checked by dis-assembling the link file with DecLnk.*)

CONST
MOVEB = 12D8H ; (* MOVE.B (A0)+,(A1)+ *)
MOVEL = 22D8H ; (* MOVE.L (A0)+,(A1)+ *)
A0 = 0+8 ; (* register A0 *)
A1 = 1+8 ; (* register A1 *)

BEGIN
SETREG(A0,From) ; (* load From pointer into A0 *)
SETREG(A1,To) ; (* load To pointer into A1 *)

IF ( ODD(From) OR ODD(To) ) THEN (* must do bytes *)
WHILE ( Bytes <> 0 ) DO
CODE(MOVEB) ;
DEC(Bytes) ;
END ;
ELSE (* even addresses so can do long moves *)
WHILE ( Bytes > 3 ) DO
CODE(MOVEL) ;
DEC(Bytes,4) ;
END ;
WHILE ( Bytes <> 0 ) DO
CODE(MOVEB) ; (* clean up remainder *)
DEC(Bytes) ;
END ;
END ;
END MoveMemory ;

(* The following procedures mimic the disk handling routines called by the
BIOS. Their procedure declarations have been written to mimic the "C"
calling sequence. *)

PROCEDURE RDRWAbs ( device, RecordNum, SectorCount : CARDINAL ;
Buffer : ADDRESS ; Flag : CARDINAL ) ;
(* NB. It is assumed that GEMDOS wont call this routine with out of range
parameters *)
CONST D0 = 0 ;
BEGIN
CODE(MOVEMDEC,SAVEREGS) ; (* save registers on stack *)
status := 0;
IF (device=Memdrive) AND (RecordNum=0) THEN (* get network stats *)
statptr:=Buffer;
statptr^:=S;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
RETURN;
END;
IF device IN NetBits THEN (* is NETWORK channel *)
IF ( Flag = 0 ) OR ( Flag = 2 ) (* read *) THEN
FOR wsector:=0 TO (SectorCount-1) DO
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
nframe1.recno:=RecordNum+wsector;
nframe1.rw:=Flag; (* read *)
resetnewdisk;
IF getfromremote(rdrwabsreq,rdrwabsconf,nframe1,local) THEN
MoveMemory(ADR(nframe1.info),Buffer+ADDRESS(wsector)*512,
512);
status:=0;
ELSE
status:=(-11);
END; (* if *)
END; (* for *)
IF networkerror THEN C.sendreset:=TRUE END; (* send network reset to remote cpu *)
SETREG(D0,status) ;
ELSIF ( Flag = 1 ) OR ( Flag = 3 ) THEN (* write *)
IF NetMap[device].Write THEN
FOR wsector:=0 TO (SectorCount-1) DO
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
nframe1.recno:=RecordNum+wsector;
nframe1.rw:=Flag; (* write *)
resetnewdisk;
MoveMemory(Buffer+ADDRESS(wsector)*512,ADR(nframe1.info),512);
IF getfromremote(rdrwabsreq,rdrwabsconf,nframe1,local) THEN
status:=0;
ELSE
status:=(-10);
END;
END; (* for *)
IF networkerror THEN C.sendreset:=TRUE END; (* send network reset to remote cpu *)
ELSE
status:=(-13); (* write protect *)
END;
SETREG(D0,status) ;
ELSE
SETREG(D0,LONGINT(-3)) ;
END ;
ELSE (* not NETWORK *)
rwabsLock:=TRUE;
SaveHDRWAbs (device,RecordNum,SectorCount,Buffer,Flag) ;
rwabsLock:=FALSE;
END ;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
END RDRWAbs ;

PROCEDURE RDMediaCh ( device : CARDINAL ) ;
CONST D0 = 0 ;
BEGIN
CODE(MOVEMDEC,SAVEREGS) ; (* save registers on stack *)
IF device IN NetBits THEN (* is NETWORK channel *)
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
IF newdisk() THEN
gotmediach[NetMap[device].Remote]:=FALSE;
gotbpb[NetMap[device].Remote]:=FALSE;
END;
IF (NOT gotmediach[NetMap[device].Remote]) THEN
IF getfromremote(rdmediareq,rdmediaconf,nframe1,local) THEN
gotmediach[NetMap[device].Remote]:=TRUE;
IF nframe1.d0=1 THEN nframe1.d0:=2 END;
SETREG(D0,nframe1.d0) ; (* "C" uses D0 as return location *)
ELSE
SETREG(D0,Changed);
END;
ELSE
SETREG(D0,NoChange) ; (* "C" uses D0 as return location *)
END;
ELSE (* not NETWORK *)
rwabsLock:=TRUE;
SaveHDMediaCh(device) ;
rwabsLock:=FALSE;
END;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
END RDMediaCh ;

PROCEDURE RDBPB ( device : CARDINAL ) ;
CONST D0 = 0 ;
BEGIN
CODE(MOVEMDEC,SAVEREGS) ; (* save registers on stack *)
IF device IN NetBits THEN (* is NETWORK channel *)
C.remotedrive:=NetMap[device].Remote;
nframe1.d0:=LONGCARD(NetMap[device].Remote);
IF newdisk() THEN
gotbpb[NetMap[device].Remote]:=FALSE;
gotmediach[NetMap[device].Remote]:=FALSE;
END;
IF (NOT gotbpb[NetMap[device].Remote]) THEN
IF getfromremote(rdbpbreq,rdbpbconf,nframe1,local) THEN
gotbpb[NetMap[device].Remote]:=TRUE;
bpbptr:=ADR(nframe1.info);
nbpbptr:=ADR(NetworkBPB[NetMap[device].Remote]);
FOR i3:=0 TO TSIZE(BPB)-1 DO
nbpbptr^[i3]:=bpbptr^[i3];
END;
resetnewdisk;
SETREG(D0,ADR(NetworkBPB[NetMap[device].Remote])); (* D0 returns address of the BPB *)
ELSE
SETREG(D0,0);
END;
ELSE
SETREG(D0,ADR(NetworkBPB[NetMap[device].Remote])); (* D0 returns address of the BPB *)
END;
IF networkerror THEN C.sendreset:=TRUE END; (* send network reset to remote cpu *)
ELSE (* not NETWORK *)
rwabsLock:=TRUE;
SaveHDBPB(device) ;
rwabsLock:=FALSE;
END ;
CODE(MOVEMINC,RESTREGS) ; (* Restore registers from stack *)
END RDBPB ;

BEGIN
END NETBIOS;

MODULE TIME;
IMPORT hz200,CODE,RTS,SuperExec,SETREG,REGISTER,ChannelLock,channel,
chanwait;
EXPORT StartTimer,TimeOut,timer,resetnewdisk,newdisk,freechannel,
resetchannel;

VAR timer : BOOLEAN;
clock : LONGCARD;
timestart,timefortimeout,timeouttime,
timestart2,timefortimeout2,timeouttime2,
timestart1,timefortimeout1,timeouttime1 : LONGCARD;
(*$P- *)
PROCEDURE gettime;
BEGIN
clock:=hz200 DIV 200;
CODE(RTS);
END gettime;
(*$P+ *)

PROCEDURE resetnewdisk;
BEGIN
SuperExec(gettime);
timestart1:=clock;
timefortimeout1:=timestart1;
IncTime(timefortimeout1,2);
END resetnewdisk;

PROCEDURE newdisk(): BOOLEAN;
BEGIN
SuperExec(gettime);
timeouttime1:=clock;
SETREG(0,timeouttime1);
CODE(0280H,0,0FFFFH);
timeouttime1:=LONGCARD(REGISTER(0));
IF timeouttime1>timefortimeout1 THEN
resetnewdisk;
RETURN TRUE;
END;
RETURN FALSE;
END newdisk;

PROCEDURE resetchannel;
BEGIN
SuperExec(gettime);
timestart2:=clock;
timefortimeout2:=timestart2;
IncTime(timefortimeout2,chanwait);
END resetchannel;

PROCEDURE freechannel(): BOOLEAN;
BEGIN
SuperExec(gettime);
timeouttime2:=clock;
SETREG(0,timeouttime2);
CODE(0280H,0,0FFFFH);
timeouttime2:=LONGCARD(REGISTER(0));
IF timeouttime2>timefortimeout2 THEN
resetchannel;
ChannelLock:=none;
RETURN TRUE;
END;
RETURN FALSE;
END freechannel;

PROCEDURE StartTimer;
BEGIN
SuperExec(gettime);
timestart:=clock; (* set to time in seconds *)
timer:=TRUE;
timefortimeout:=timestart;
IncTime(timefortimeout,5);
END StartTimer;

PROCEDURE IncTime(VAR t : LONGCARD; c: CARDINAL);
BEGIN
IF c<1 THEN RETURN END;
t:=t+LONGCARD(c);
END IncTime;

PROCEDURE TimeOut(): BOOLEAN;
BEGIN
IF (NOT timer) THEN RETURN FALSE END;
SuperExec(gettime);
timeouttime:=clock;
SETREG(0,timeouttime);
CODE(0280H,0,0FFFFH);
timeouttime:=LONGCARD(REGISTER(0));
IF timeouttime>timefortimeout THEN
StartTimer;
RETURN TRUE;
END;
RETURN FALSE;
END TimeOut;
BEGIN
END TIME;

MODULE EVENT; (* local module *)
IMPORT C,evtype,R,S,trace,framebufferfull,BConOut,Device,getf,message,
rframe,RFRAME,vblLock,FlushBuffer,framewaiting,recframesize,
rframeptr,requesttosend,cleartosend,TimeOut,frame,FrameKind,
DataKind,MediaChange,GetBPB,ADR,ADDRESS,sendtoremote,event,
ScreenPhysicalBase,TSIZE,BPB,Memdrive,RWAbs,RW,frameptr,timer,
kbdiorec,IORec,SerialDevice,statptr,GetDateTime,senddata,sendf,
gotframe,charcount,Maxdrives,gotmediach,gotbpb,SFRAME,debug,
inc,StartTimer,NETTIME,TIMESET,BEL,getfromremote,SetTime,
SetDate,OneTime,ChannelLock,channel;

EXPORT Nwait,ToHost,HandleEvents,RESET;

VAR nframe2 : frame;
d : CARDINAL;

PROCEDURE Nwait(VAR e: evtype);
VAR i2,cksum : CARDINAL;
BEGIN

IF C.sendreset THEN
e:=reset;
INC(S.resets);
RETURN;
END;

IF framebufferfull THEN
IF trace THEN BConOut(CON,"k") END;
cksum:=0;
FOR i2:=0 TO recframesize-5 DO
cksum:=cksum+CARDINAL(rframeptr^[i2])
END;
IF (cksum=rframe.cksum) THEN
getf(RFRAME);
e:=framearrival;
INC(R);
IF trace THEN BConOut(CON,"u") END;
RETURN;
ELSE
e:=nothing; (* checksum error *)
framebufferfull:=FALSE;
FlushBuffer();
INC(S.retrys);
INC(S.checksumerrors);
IF trace THEN BConOut(CON,"U") END;
END;
RETURN;
END;

IF requesttosend AND cleartosend THEN
e:=hostready;
RETURN;
END;

IF TimeOut() THEN
e:=timeout;
INC(R);
INC(S.retrys);
INC(S.timeouts);
END; (* so sorry no frame ack *)
END Nwait;

PROCEDURE ToHost(VAR f: frame);
VAR i,r : INTEGER;
d : CARDINAL;
bpbptr,nbpbptr : frameptr;
meminfo : POINTER TO message;
screen1 : POINTER TO ARRAY [0..255]
OF CARDINAL;
ibuf,bbuf : POINTER TO ARRAY
[0..32] OF LONGCARD;
BEGIN
IF trace THEN BConOut(CON,"H") END;
IF f.kind=callreq THEN
RETURN;
END;
IF f.kind=clearreq THEN
RETURN;
END;
IF f.kind=diag THEN
RETURN;
END;
IF f.kind=data THEN
IF f.cmd=rdmediareq THEN
IF trace THEN BConOut(CON,"M") END;
nframe2.d0:=LONGCARD(MediaChange(CARDINAL(f.d0)));
sendtoremote(data,rdmediaconf,nframe2,remote);
RETURN;
END;
IF f.cmd=rdbpbreq THEN
IF trace THEN BConOut(CON,"P") END;
nframe2.d0:=LONGCARD(GetBPB(CARDINAL(f.d0)));
bpbptr:=ADDRESS(nframe2.d0);
nbpbptr:=ADR(nframe2.info);
FOR i:=0 TO TSIZE(BPB)-1 DO
nbpbptr^:=bpbptr^;
END;
sendtoremote(data,rdbpbconf,nframe2,remote);
RETURN;
END;
IF f.cmd=rdrwabsreq THEN
IF trace THEN BConOut(CON,"W") END;
INC(S.rwabsreqs);
IF f.d0#Memdrive THEN
nframe2.d0:=LONGCARD(RWAbs(RW(f.rw),ADR(f.info),1,f.recno,
CARDINAL(f.d0)));
END;

IF (f.d0=Memdrive) AND (f.recno>3) THEN
IF trace THEN BConOut(CON,"V") END;
nframe2.d0:=0;
meminfo:=ADDRESS(LONGCARD(f.recno)*LONGCARD(512));
IF (f.rw=0) OR (f.rw=2) THEN (* read *)
f.info:=meminfo^;
ELSE
meminfo^:=f.info; (* write *)
END;
END;

IF (f.d0=Memdrive) AND (f.recno=3) THEN
nframe2.d0:=0;
meminfo:=ScreenPhysicalBase();
screen1:=ADR(f.info);
FOR i:=0 TO 63 DO
screen1^:=0;
FOR r:=0 TO 511 DO
screen1^:=screen1^+CARDINAL(meminfo^[0]);
meminfo:=ADDRESS(LONGCARD(meminfo)+LONGCARD(1));
END;
END;
END;

IF (f.d0=Memdrive) AND (f.recno=2) THEN (* remote ikbd *)
nframe2.d0:=0;
kbdiorec:=IORec(Keyboard); (* length in info[0] *)
ibuf:=kbdiorec^.ibuf;
bbuf:=ADR(f.info);
kbdiorec^.ibufhd:=0;
kbdiorec^.ibuftl:=0;
FOR i:=1 TO INTEGER(bbuf^[0]) DO
ibuf^:=bbuf^;
END;
kbdiorec^.ibufhd:=0;
kbdiorec^.ibuftl:=CARDINAL(bbuf^[0]*4);
END;

IF (f.d0=Memdrive) AND (f.recno=1) THEN
statptr:=ADR(f.info); (* load remote stats *)
statptr^:=S;
END;

IF (f.rw=0) OR (f.rw=2) THEN (* load read buffer *)
nframe2.rw:=f.rw;
nframe2.info:=f.info; (* if rec get buffer to send *)
END;
sendtoremote(data,rdrwabsconf,nframe2,remote);
RETURN;
END;
IF f.cmd=timereq THEN
IF trace THEN BConOut(CON,"c") END;
nframe2.d0:=GetDateTime();
sendtoremote(data,timeconf,nframe2,remote);
RETURN;
END;
END;
END ToHost;

PROCEDURE HandleEvents(VAR event: evtype);
BEGIN
IF event=nothing THEN RETURN END;
IF event=hostready THEN
event:=nothing;
IF trace THEN BConOut(CON,"S") END;
vblLock:=TRUE;
senddata;
requesttosend:=FALSE;
cleartosend:=FALSE;
END;

IF event=reset THEN
event:=nothing;
IF trace THEN BConOut(CON,"I") END;
RESET;
SFRAME.kind:=resetreq;
senddata;
IF NETTIME AND (NOT TIMESET) THEN
NetTime;
TIMESET:=TRUE;
END;
END;

IF event=framearrival THEN
event:=nothing;
IF trace THEN BConOut(CON,"F") END;

IF (RFRAME.ack=C.nextframetosend) OR debug THEN
IF trace THEN BConOut(CON,"K") END;
cleartosend:=TRUE;
StartTimer;
R:=0;
timer:=FALSE;
inc(C.nextframetosend);
END;

IF (RFRAME.seq=C.frameexpected) OR debug THEN
event:=nothing;
IF trace THEN BConOut(CON,"E") END;
IF RFRAME.kind#ack THEN (* try to exec command *)
inc(C.frameexpected);
framewaiting:=TRUE;
R:=0;
framebufferfull:=FALSE;
ToHost(RFRAME);
END;
END;
IF RFRAME.kind=resetreq THEN
event:=nothing;
IF trace THEN BConOut(CON,"*") END;
RESET;
BConOut(CON,BEL);
END;
event:=nothing;
END;

IF event=timeout THEN
event:=nothing;
IF trace THEN BConOut(CON,"R") END;
sendf(SFRAME);
END;
END HandleEvents;

PROCEDURE NetTime;
VAR nettime : ARRAY [0..1] OF CARDINAL;
timeptr : POINTER TO LONGCARD;

BEGIN
OneTime:=TRUE;
IF getfromremote(timereq,timeconf,nframe2,local) THEN
IF trace THEN BConOut(CON,"#") END;
timeptr:=ADR(nettime[0]);
timeptr^:=nframe2.d0;
SetTime(nettime[1]);
SetDate(nettime[0]);
ELSE
BConOut(CON,BEL);
event:=reset;
END;
OneTime:=FALSE;
END NetTime;

PROCEDURE RESET;
BEGIN
charcount:=0;
R:=0;
gotframe:=FALSE;
framebufferfull:=FALSE;
C.nextframetosend:=0;
C.frameexpected:=0;
FOR d:=0 TO Maxdrives DO
gotmediach[d]:=FALSE;
gotbpb[d]:=FALSE;
END;
cleartosend:=TRUE;
requesttosend:=FALSE;
framewaiting:=FALSE;
timer:=FALSE;
C.sendreset:=FALSE;
C.networkactive:=TRUE;
vblLock:=FALSE;
ChannelLock:=none;
END RESET;

BEGIN
END EVENT; (* local module *)


(* ----------------------------------------------------------------------- *)

PROCEDURE Initialise (port: Device) : BOOLEAN ;
(* returns TRUE if NETWORK is to be installed *)
BEGIN
CODE(3f3cH,0017H,4e4eH,548fH); (* gettime *)
CODE(2f00H,3f3cH,0016H,4e4eH,5c8fH); (* settime *)
IF NOT installed THEN
SuperExec(PROC(setcontrol)); (* set address of global control record *)
END;
IF port=HSS THEN
rbptr:=IORec(MIDI);
ELSE
rbptr:=IORec(RS232);
END;
rbptr^.ibuf:=ADR(rbuffer);
rbptr^.ibufsize:=2048;
rbptr^.ibufhd:=0;
rbptr^.ibuftl:=0;
C.magic:=MAGIC;
C.remotedrive:=0;
framesize:=TSIZE(frame);
recframesize:=framesize;
sndframesize:=framesize;
R:=0;
RETURN TRUE;
END Initialise ;

(* The following compiler directive stops the compiler from generating the
normal Modula-2 entry/exit code for the next procedure. This is needed as
this routine is called in supervisor mode by the BIOS function to install
the BIOS vectors. *)
(*$P- Stop entry/exit code for next procedure *)
PROCEDURE InstallVectors ;
BEGIN
(* First save the current hard disk vectors *)
SaveHDBPB := CBPBProc(HDBPB) ;
SaveHDRWAbs := CRWAbsProc(HDRWAbs) ;
SaveHDMediaCh := CMediaChProc(HDMediaCh) ;
SaveCritic := PROC(EvtCritic);
(* Now set the BIOS vectors to our routines *)
HDBPB := ADDRESS(RDBPB) ;
HDRWAbs := ADDRESS(RDRWAbs) ;
HDMediaCh := ADDRESS(RDMediaCh) ;
EvtCritic := ADDRESS(NetCritic);

drvnr:=2; (* start from drive C *)
WHILE drvnr IN DriveBits DO
INC(drvnr);
END; (* while *)
INC(drvnr); (* start of network drives *)
R := 0; (* remote = A *)

GetOpt;

Open("MX2NET.INF",0,handle);
IF handle>0 THEN
count:=128;
GEMDOS.Read(handle,count,ADR(NetInfo));
OK:=Close(handle);
ELSE
count:=0;
END;
OK:=FALSE; (* set to READONLY *)

IF count>0 THEN
FOR d := 0 TO CARDINAL(count) BY 4 DO
IF d<CARDINAL(count) THEN
R := CARDINAL(BITSET(NetInfo[0+d]) * BITSET(31) )-1;
drvnr := CARDINAL(BITSET(NetInfo[1+d]) * BITSET(31) )-1;
IF (R>Maxdrives) OR (drvnr>Maxdrives) THEN R:=0; drvnr:=0; END;
IF (NetInfo[2+d]='W') OR (NetInfo[2+d]='w') THEN
OK := TRUE;
ELSE
OK := FALSE;
END;
NetMap[drvnr].Remote := R;
NetMap[drvnr].Local := drvnr;
NetMap[drvnr].Write := OK;
IF ((NOT (drvnr IN DriveBits)) OR NETMASK) AND (drvnr>1) THEN
INCL(DriveBits,NetMap[drvnr].Local);
INCL(NetBits, NetMap[drvnr].Local);
END;
END;
END;
END;
R:=0;
d:=0;

networkconnect := FALSE;
gotframe := FALSE;
framebufferfull := FALSE;
charcount:=0;
SIZEframe:=TSIZE(frame);
SIZEframecmd:=TSIZE(framecmd);

rframeptr := ADR(rframe);
framecmdptr:=ADR(rframe);
CODE(RTS) ; (* code to return to calling BIOS function *)
END InstallVectors ;
(*$P+ *)

PROCEDURE GetOpt;
VAR d : CARDINAL;
BEGIN
Open("MX2NET.OPT",0,handle);
IF handle>0 THEN
count:=128;
GEMDOS.Read(handle,count,ADR(NetInfo));
OK:=Close(handle);
ELSE
count:=0;
END;
NETTIME:=FALSE;
NETMASK:=FALSE;
MEMMASK:=FALSE;
PHYSLOW:=FALSE;
IF count>0 THEN
FOR d:=0 TO CARDINAL(count) DO

IF NetInfo[d]='t' THEN (* get gemdos time *)
NETTIME:=TRUE;
END;

IF NetInfo[d]='o' THEN (* over-write existing drive map *)
NETMASK:=TRUE;
END;

IF NetInfo[d]='m' THEN (* memory reads-write useing rwabs *)
MEMMASK:=TRUE;
NetMap[Memdrive].Remote := Memdrive;
NetMap[Memdrive].Local := Memdrive;
NetMap[Memdrive].Write := TRUE;
INCL(NetBits, Memdrive);
END;

IF NetInfo[d]='5' THEN (* remote is 520 ST, use low ram screen *)
PHYSLOW:=TRUE;
END;

END;
END;
END GetOpt;

(*$P- *) (* set vector to control record *)
PROCEDURE setcontrol;
BEGIN
IF Mptr#MAGIC THEN
C.drivemap:=DriveMap();
Dptr:=C.drivemap;
END;
C.drivemap:=Dptr;
Mptr:=MAGIC;
CODE(RTS);
END setcontrol;
(*$P+ *)

PROCEDURE FlushBuffer();
BEGIN
rbptr^.ibufhd:=0;
rbptr^.ibuftl:=0;
END FlushBuffer;

PROCEDURE nrecframe;
BEGIN
vblLock:=TRUE;
IF C.networkactive THEN
WHILE (BConStat(netdevice)) AND (NOT framebufferfull) DO
recchar := BConIn(netdevice);
IF (NOT gotframe) AND (CHAR(recchar)=SYN) THEN
gotframe:=TRUE; (* got sync char from data *)
charcount:=0;
END;
IF (charcount=1) AND ((CHAR(recchar)#STX)
AND (CHAR(recchar)#SOH)) THEN
gotframe:=FALSE; (* false start try again *)
charcount:=0;
END;
IF (charcount=1) AND (CHAR(recchar)=STX) THEN
recframesize:=SIZEframe;
END;
IF (charcount=1) AND (CHAR(recchar)=SOH) THEN
recframesize:=SIZEframecmd;
END;
IF gotframe THEN (* put data in buffer *)
rframeptr^[charcount]:=BYTE(recchar);
INC(charcount);
IF charcount=recframesize THEN (* got full frame *)
gotframe := FALSE;
IF trace THEN BConOut(CON,"^") END;
IF recframesize=SIZEframecmd THEN
rframe.cksum:=framecmdptr^.cksum;
END;
framebufferfull := TRUE;
RETURN;
END;
END;
END; (* WHILE *)
END;
END nrecframe;

PROCEDURE getf(VAR f: frame);
BEGIN
INC(S.inpackets);
f:=rframe;
framebufferfull:=FALSE;
END getf;

PROCEDURE senddata;
BEGIN
vblLock:=TRUE;
SFRAME.seq:=C.nextframetosend;
SFRAME.ack:=1-C.frameexpected;
sendf(SFRAME);
IF (SFRAME.kind#ack) AND (SFRAME.kind#resetreq) THEN
StartTimer; (* set timer to wait for frame ack from remote host *)
END;
END senddata;

PROCEDURE sendf(VAR f: frame);
BEGIN
vblLock:=TRUE;
INC(S.outpackets);
sframeptr := ADR(sframe);
sframe:=f;
sframe.cksum:=0;
IF ((sframe.cmd=rdrwabsconf) AND ((sframe.rw=0)
OR (sframe.rw=2))) OR ((sframe.cmd=rdrwabsreq)
AND ((sframe.rw=1) OR (sframe.rw=3))) THEN
sndframesize:=SIZEframe;
sframe.syn := SYN ;
sframe.stx := STX ;
shortframe:=FALSE;
IF trace THEN BConOut(CON,":") END;
ELSE
sndframesize:=SIZEframecmd;
sframe.syn := SYN ;
sframe.stx := SOH ;
sframecmdptr:=ADR(sframe);
shortframe:=TRUE;
IF trace THEN BConOut(CON,".") END;
END;
FOR i1:=0 TO sndframesize-5 DO (* compute checksum *)
sframe.cksum:=sframe.cksum+CARDINAL(sframeptr^[i1])
END;
IF shortframe THEN sframecmdptr^.cksum:=sframe.cksum END;
FOR i1:=0 TO sndframesize-1 DO (* send frame *)
BConOut(netdevice,CHAR(sframeptr^[i1]));
END;
END sendf;

PROCEDURE waitcts(what: BOOLEAN); (* wait for cleartosend state *)
BEGIN
IF what THEN
IF trace THEN BConOut(CON,"+") END;
IF OneTime THEN R:=retry END;
REPEAT
nrecframe;
Nwait(event);
HandleEvents(event);
IF R>retry THEN
networkerror:=TRUE;
RETURN; (* trouble *)
END;
UNTIL cleartosend;
ELSE
IF trace THEN BConOut(CON,"-") END;
Nwait(event);
HandleEvents(event);
END;
IF trace THEN BConOut(CON,"N") END;
END waitcts;

PROCEDURE WaitChannel(chan: channel);
BEGIN
IF trace AND (chan=local) THEN BConOut(CON,"<") END;
IF trace AND (chan=remote) THEN BConOut(CON,">") END;
IF trace AND (chan=none) THEN BConOut(CON,"|") END;
IF (ChannelLock=none) OR (ChannelLock=chan) THEN
ChannelLock:=chan;
resetchannel;
RETURN;
END;
IF trace THEN BConOut(CON,"!") END;
REPEAT
nrecframe;
Nwait(event);
HandleEvents(event);
UNTIL freechannel();
ChannelLock:=chan;
END WaitChannel;

(* request for data from remote hosts disk drives and system *)
(* what wanted in command, the correct reply in reply, data in f *)
PROCEDURE getfromremote(command, reply: DataKind; VAR f: frame;
chan: channel): BOOLEAN;
VAR ticks : CARDINAL;
BEGIN
IF (NOT C.networkactive) THEN RETURN FALSE END; (* error *)
WaitChannel(chan);
vblLock:=TRUE;
networkerror:=FALSE;
R:=0;
StartTimer;
IF trace THEN BConOut(CON,"A") END;
f.kind:=data;
f.cmd:=command;
waitcts(TRUE);
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
IF trace THEN BConOut(CON,"B") END;
SFRAME:=f;
framewaiting:=FALSE;
requesttosend:=TRUE;
waitcts(FALSE);
REPEAT
UNTIL (NOT requesttosend);
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
IF trace THEN BConOut(CON,"C") END;
ticks:=0;
IF OneTime THEN R:=retry END;
REPEAT
INC(ticks);
nrecframe;
Nwait(event);
HandleEvents(event);
IF ticks>64000 THEN networkerror := TRUE END;
IF R>retry THEN networkerror:=TRUE END;
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
UNTIL framewaiting AND (RFRAME.cmd=reply);
IF trace THEN BConOut(CON,"D") END;
f:=RFRAME;
f.rw:=5;
f.kind:=ack;
f.cmd:=reply;
sendf(f); (* send ack for reply *)
IF networkerror THEN
vblLock:=FALSE;
RETURN FALSE;
END;
IF trace THEN BConOut(CON,"Z") END;
vblLock:=FALSE;
RETURN TRUE;
END getfromremote;

PROCEDURE sendtoremote(type: FrameKind; command: DataKind;VAR f: frame;
chan: channel);
BEGIN
WaitChannel(chan);
vblLock:=TRUE;
IF trace THEN BConOut(CON,"T") END;
f.kind:=type;
f.cmd:=command;
IF debug THEN cleartosend:=TRUE END; (* so we can send in loop *)
waitcts(TRUE);
IF trace THEN BConOut(CON,"1") END;
SFRAME:=f;
requesttosend:=TRUE;
waitcts(FALSE);
IF trace THEN BConOut(CON,"2") END;
IF SFRAME.kind=ack THEN cleartosend:=TRUE END;
vblLock:=FALSE;
END sendtoremote;

(*$P- *)
PROCEDURE NetCritic;
BEGIN
CODE(RTS);
END NetCritic;
(*$P+ *)

PROCEDURE recframe;
BEGIN
EvtCritic := ADDRESS(NetCritic);
IF (NOT vblLock) AND (NOT rwabsLock) THEN
vblLock:=TRUE;
nrecframe;
Nwait(event);
HandleEvents(event);
vblLock:=FALSE;
END;
END recframe;

PROCEDURE NoCritrecframe;
BEGIN
IF (NOT vblLock) AND (NOT rwabsLock) THEN
vblLock:=TRUE;
nrecframe;
Nwait(event);
HandleEvents(event);
vblLock:=FALSE;
END;
END NoCritrecframe;

PROCEDURE initnetwork(port: Device);
VAR d : CARDINAL;
BEGIN
netdevice:=port;
IF Initialise(port) THEN

RESET;
rwabsLock:=FALSE;
IF NOT installed THEN
SuperExec(PROC(InstallVectors)) ; (* install the NETWORK *)
installed:=TRUE;
END;
event:=reset;
END ;
END initnetwork;

PROCEDURE networkoff;
BEGIN
C.networkactive:=FALSE;
END networkoff;

PROCEDURE networkon;
BEGIN
C.networkactive:=TRUE;
END networkon;

BEGIN
BConOut(CON,ESC);
BConOut(CON,'E');
GEMDOS.ConWS(TITLE);
BConOut(CON,CR);
BConOut(CON,LF);
END NETWORK.
 
Last edited:

nsaspook

Joined Aug 27, 2009
16,326
Mine was coded in Microsoft QBASIC, a full IDE and compiler ca. 1985
If I remember correctly I wrote two versions of the MX2NET program in the TDI Modula-2 language system for Atari-ST.
https://en.wikipedia.org/wiki/Modula-2
http://archive.org/stream/1986-07-compute-magazine/Compute_Issue_074_1986_Jul#page/n111/mode/2up

A MIDI version for local networking because MIDI was a lot faster and a serial version that was compatible with modem links too. The RS-232 serial version included a sliding-window ACK protocol to improve speed over long links with lots of audio band duplex latency.
https://en.wikipedia.org/wiki/Sliding_window_protocol

http://cd.textfiles.com/atarilibrary/atari_cd10/PAGES/ISSUES/AC7.HTM/PAGES/NETWORK.HTM
 
Last edited:

spinnaker

Joined Oct 29, 2009
7,830
I am trying to remember the name f the early LAN (3Com maybe) that I worked on. It was a disk server not a file server. Everyone had a copy of the FAt in their computer. If someone wrote to the disk that is the copy you got on the disk for everyone. It was really only good for read only for the shared drives.

I wrote sort of a replication code in DBase II. Everyone would write to thier diskette. at the end of the day, the operator would gather all of the disks together and update the main database. Worked really well for a couple of years till Novell andfile sharing came out.
 

spinnaker

Joined Oct 29, 2009
7,830
Speaking of Atari. I could not afford the Atari RS1232 interface so I built my own and wrote my own drivers for the Atari OS. The interface plugged into an old acoustic coupled modem I pulled out of the trash at work.
 

SamR

Joined Mar 19, 2019
5,491
We were running HP 9000 and dedicated terminals. Seems like they were 2 wire telco connected. All plant stores were put on the computer and instead of paper multipart tickets you went to a terminal and executed a stores request and grabbed a copy off the dedicated printers to take to the storeroom to draw materials against a charge code if your login allowed permission to use the code. Then HP came out with a small desktop computer the HP-150 with terminal capability that cost the same as a terminal. It had a single 3 1/4 hard side floppy drive for 160k single sided disks and a touch screen that didn't work worth a damn. It came with terminal software and you could buy "Memo Maker" that was a bare-bones word processor and some graphics program that was also super basic. Or Basic to run on it. Eventually, Lotus 123 came out for it. There was a lot of sneaker-net and especially after they replaced the 150's with HP Vectras and eventually put them all on a LAN after I designed and had a contractor install the plantwide Fiber Optic Backbone for the intranet. At first, I was using a 1200 baud modem and dial-up then Corporate integrated the Internet for corporate-wide communications and SAP connectivity. Hercules bought the entire SAP package including the code for it before going belly up only a few years later.
 

SamR

Joined Mar 19, 2019
5,491
My only recollection of RS-232 was connecting peripheral devices. I did put together a small net of 4 CAD HP Vectras to share a 30"x42" pen and ink plotter for the drafting dept. Bought cable, D-shells, pins and a switch box to connect it all. Quickly found out that 38400 baud was very short range communication limited to about 30 feet if I remember right. The guys would just select their CAD port on the switch box when they set up the ink pens and paper on the plotter and stream their data to the plotter. I also did some code for the secretaries that upon boot up of their Vectra ran my program in the Vectra's autoexec.bat file that checked the last time they had backed up their .txt document files onto the mainframe and popped up a screen if they hadn't to ask if they wanted to do it now and processed the files for them if they selected yes.
 

spinnaker

Joined Oct 29, 2009
7,830
I worked on a multiuser desktop PC that ran CPM. I think it was called TeleVideo? As I recall I worked on a DEC desktop PC multiuser system too, Can't remember if it ran the DEC OS or CPM.

Wow those systems did not last long and I remember how expensive they were. I installed a number of them.

And what was the desktop mini mainframe IBM sold? They were really expensive and did not last long either. I remember installing a couple.
 

SamR

Joined Mar 19, 2019
5,491
Apparently here is where I ran into problems using shielded cable...

"For shielded cable, stray capacitance typically is double the mutual capacitance. As shown in Figure 5, for shielded cable, the maximum line length is 20 meters; for unshielded cable, it is more than 40 meters."

It also depended on baud rate, slower was longer. Didn't have internet access to good data back then so was flying by the seat of my pants so to speak. I did have a small pamphlet/booklet on RS-232 pinout and UART specifications to work from, but that little piece was missing from it. Also the MS-DOS only supported 9600 baud so I had found some 19200/36000 baud drivers on some bulletin board to use to up the datarate.
 
Last edited:

Thread Starter

djsfantasi

Joined Apr 11, 2010
9,237
...
I worked on a multiuser desktop PC that ran CPM. I think it was called TeleVideo? As I recall I worked on a DEC desktop PC multiuser system too, Can't remember if it ran the DEC OS or CPM.

Wow those systems did not last long and I remember how expensive they were. I installed a number of them.

And what was the desktop mini mainframe IBM sold? They were really expensive and did not last long either. I remember installing a couple.
That would be an IBM 5100

You could use RPG on it, and I wrote a system using RPG for a food distributor. They handled Starkist and Clorox for all of eastern Massachusetts. I remember those two brands because they had the most restrictive and unusual requirements.
 

SamR

Joined Mar 19, 2019
5,491
DEC desktop PC
Most of those ran on VMS which never overwrote a file and made and new file each time it wrote back. It also lacked a full-screen editor which was a pain in the ass. So I found a terminal emulator for Windows and hooked it up to the plant fiber network so I could file transfer from the DEC out in a remote control room to the Vectra back in my office and do .txt file editing with notepad and transfer back to the DEC to be compiled into the GSE control system that was running on VMS. Later DEC and Compaq (running MS) merged, can't remember but I think it was Compaq that bought DEC. Eventually, they both were gone.
 

Thread Starter

djsfantasi

Joined Apr 11, 2010
9,237
Yes, Compaq bought DEC.

Compaq got the Alpha technology, a classic example of advanced technology that never caught on. It was the first commercial 64-bit system. I bought two, configured as a high availability system that had 100% availability.All system processors were connected via hardware that endured each operation was executed on its sister system... at the same time. The design actually embedded two systems. One dedicated for IO; the other dedicated for processing. This was a popular design concept at DEC, also realized in their mainframe offering, the DECSystem20.

They also got AltaVista, the most advanced pre-Google search engine in its day. It made an effort. But was swamped by Google. Likely a marketing foobar.

In addition, VMS still exists today. Through a series of sales, it ended up with HP. Until recently, it was a popular in big iron HP systems and supported virtualization, big data and cloud services. It was sold this year to VMS Software, an independent company that still develops and supports VMS (now known as OpenVMS). It’s no longer available from HP.

DEC started out strong. But as the company matured, they had a string of powerful designs that, as Maxwell Smart would say, “Missed it by this much!”
 

SamR

Joined Mar 19, 2019
5,491
IT bought a Compaq "portable" (luggable it was heavy) that you undid a couple of toggle latches on and the end dropped off with the keyboard and coil cable attaching to the BOX and revealing a maybe 8"x8" monochrome CRT. Opening it up it had the standard ISA cards but they were held in place with Mil-Spec hardware. You could call it portable because when you closed it up it did have a handle to drag it around by.
 

Thread Starter

djsfantasi

Joined Apr 11, 2010
9,237
IT bought a Compaq "portable" (luggable it was heavy) that you undid a couple of toggle latches on and the end dropped off with the keyboard and coil cable attaching to the BOX and revealing a maybe 8"x8" monochrome CRT. Opening it up it had the standard ISA cards but they were held in place with Mil-Spec hardware. You could call it portable because when you closed it up it did have a handle to drag it around by.
That machine always reminded me of a sewing machine.
 

SamR

Joined Mar 19, 2019
5,491
Wow DEC Alpha and Alta-Vista brings back old memories. I had forgotten about them. The Alpha was hot stuff until Intel got going with its Pentiums. I actually liked VMS file handling since it basically had undelete by making a new file each time. There was a purge command so you could prune the old iterative files by date or # of copies and keep it manageable. It just didn't have all the neat little utilities that Microsoft gave you.
 

Thread Starter

djsfantasi

Joined Apr 11, 2010
9,237
Wow DEC Alpha and Alta-Vista brings back old memories. I had forgotten about them. The Alpha was hot stuff until Intel got going with its Pentiums. I actually liked VMS file handling since it basically had undelete by making a new file each time. There was a purge command so you could prune the old iterative files by date or # of copies and keep it manageable. It just didn't have all the neat little utilities that Microsoft gave you.
That undelete feature was useful. I accidentally issued a recursive delete from the file system root. I though I lost my job. In actuality, I got rewarded.

I ran into the computer room and hit the big red switch. Any file corruption was better than having zero files.

I got on the phone with Digital support. They walked me through recovery AND WE DIDN'T LOSE ONE FILE.
 

SamR

Joined Mar 19, 2019
5,491
The GSE control system was designed and ran on DEC PDP-11 and was then ported over to the DEC pc running VMS. It took Foxboro DCS a while to port over from Unix workstations to Windows. They first went from workstations to PCs running Venix and Xenix which were mini Unix operating systems which on the PC tended to get hung and require a reboot. Not really good for real-time control. Even when it was ported to Windows NT it was actually running on Unix which NT had an interface for that was named nutcracker. Windows was handling the graphics and interfacing with the Unix core program. Wasn't it NEC that had DEC by that time. My memory is so bad now...
 
Top