home *** CD-ROM | disk | FTP | other *** search
- (* ====================================================== *)
- (* REMOTE.PAS *)
- (* TSR-Master-Programm zur paralellen Übertragung *)
- (* (c) 1993 Ralf Hensmann & DMV-Verlag *)
- (* ====================================================== *)
- {$A-,B-,D+,L+,E-,F-,I-,N-,O-,R-,S-,V-}
- {$M 2048,128,1000}
-
- {$DEFINE LapLink} (* wird auch in den Units benötigt ! *)
- (* unter [O]ptions im [C]ompilermenü *)
- (* bei [C]onditional defines auch *)
- (* setzen. Sonst tut's nicht ! *)
-
- PROGRAM Remote_Drive;
-
- USES Dos, Crt, CRC, ParData, ParCRC, Strings, Rem_Type;
-
- (* ====================================================== *)
- (* Der Interrupt-Handler *)
- (*
- Der Interrupt-Handler besteht aus zwei Teilen. Die eigent-
- liche ISR ist in einem Byte-Array abgespeichert und über-
- nimmt das Sichern der Stack-Register, von CS:IP und den
- Flags -- eine Aufgabe, die in Turbo Pascal nicht gelöst
- werden kann. Der Redirector ist in Turbo-Pascal geschrieben
- und verteilt die Aufgaben je nach Funktionsnummer. Die
- ISR ist ohne Veränderung aus dem Buch "undocumented DOS"
- übernommen.
- Die folgenden Adressen müssen während der Laufzeit
- eingetragen werden: *)
- (* ====================================================== *)
-
- CONST
- Prev_Hndlr = 99; { Alter Interrupt-Einsrung $2F }
- Redir_Entry = 49; { Einsprung des Redirectors }
- Our_SP_Ofs = 45; { Turbo-Pascal Stack-Segment }
- Our_SS_Ofs = 40;
-
- (* weitere Werte können beim Compilieren eingetragen *)
- (* werden. Sie hängen vom Codeende ab. *)
- ISR_Code_Max = 102; { Nummer des letzten Bytes }
- Save_SS_Ofs = ISR_Code_Max+1;{ Stack-Segment des Callers}
- Save_SP_Ofs = ISR_Code_Max+3;{ Stack-Offset des Callers }
- Save_Rf_Ofs = ISR_Code_Max+5; { Flags bei ISR-Einsprung }
- Save_Fl_Ofs = ISR_Code_Max+7; { Flags auf dem Stack }
- Save_CS_Ofs = ISR_Code_Max+9; { CS auf dem Stack }
- Save_IP_Ofs = ISR_Code_Max+11;{ IP auf dem Stack }
- Our_Drv_Ofs = ISR_Code_Max+13;{ Hilfsvariable }
-
- TYPE { Array für ISR-Routine }
- ISR_Code_Buffer = ARRAY [0..ISR_Code_Max] OF BYTE;
- { ISR-Struktur enthält zusätzlich auch die }
- { Sicherungs-Variablen }
- ISRptr = ^ISR_Rec;
- ISR_Rec = RECORD
- ic : ISR_Code_Buffer;{ Der eigentliche ISR-Code }
- Save_SS, { Altes Stack-Segment beim Aufruf }
- Save_SP, { Alter Stack-Offset beim Aufruf }
- Real_Fl, { Flags bei Aufruf der ISR-Routine}
- Save_Fl, { Auf dem Stack
- zwischengespeicherte Flags }
- Save_CS, { Auf dem Stack abgelegtes CS }
- Save_IP : WORD; { Auf dem Stack abgelegter IP }
- Our_Drive : BOOLEAN; { Durchreichen des Interrupts
- bzw. Rückkehr }
- END;
-
- { Der ISR-Code hat zwei Aufgaben. 1. reicht er unbekannte }
- { Funktionen ohne weitere Probleme weiter und 2. erledigt }
- { er das für Turbo Pascal dringend notwendige }
- { Stack-Switching. Außerdem erlaubt er die Veränderung }
- { aller Variablen, die auf dem Stack liegen. }
-
- CONST
- ISR_Code : ISR_Code_Buffer = { entry: }
- ( $90,
- { nop OR int 3 ; for debugging }
- $9C,
- { pushf ; save flags }
- $80,$FC,$11,
- { cmp ah,11h ; our fxn? }
- $75,$5A,
- { jne not_ours ; bypass }
- $2E,$8F,$06, Save_Rf_Ofs, 0,
- { pop cs:real_fl ; store act flgs}
- $2E,$8F,$06, Save_IP_Ofs, 0,
- { pop cs:save_ip ; store cs:ip }
- $2E,$8F,$06, Save_CS_Ofs, 0,
- { pop cs:save_cs ; and flags }
- $2E,$8F,$06, Save_Fl_Ofs, 0,
- { pop cs:save_fl ; from stack }
- $2E,$89,$26, Save_SP_Ofs, 0,
- { mov cs:save_sp,sp ; save stack }
- $8C,$D4,
- { mov sp,ss }
- $2E,$89,$26, Save_SS_Ofs, 0,
- { mov cs:save_ss,sp }
-
- $BC, 0,0,
- { mov sp,SSEG ; set our stack }
- $8E,$D4,
- { mov ss,sp }
- $BC, 0,0,
- { mov sp,SPTR }
- $9C,
- { pushf ; call our }
- $9A, 0,0,0,0,
- { call redir ; intr proc. }
- $2E,$8B,$26, Save_SS_Ofs, 0,
- { mov sp,cs:save_ss ; put back }
- $8E,$D4,
- { mov ss,sp ; caller's stack}
- $2E,$8B,$26, Save_SP_Ofs, 0,
- { mov sp,cs:save_sp }
- $2E,$FF,$36, Save_Fl_Ofs, 0,
- { push cs:save_fl ; restore }
- $2E,$FF,$36, Save_CS_Ofs, 0,
- { push cs:save_cs ; restore }
- $2E,$FF,$36, Save_IP_Ofs, 0,
- { push cs:save_ip ; return addr. }
- $2E,$FF,$36, Save_Rf_Ofs, 0,
- { push cs:real_fl ; save act flgs }
- $2E,$80,$3E, Our_Drv_Ofs,0,0,
- { cmp cs:our_drive,0; not our drive?}
- $74,$04,
- { je not_ours ; no, jump }
- $9D,
- { popf ; yes, restore }
- $CA,$02,$00,
- { retf 2 ; & return flags}
- { not_ours: }
- $9D,
- { popf ; restore flags }
- $EA, 0,0,0,0
- { jmp far prev_hndlr ; pass the buck }
- );
-
- VAR
- ISR : ISRptr; { Der Zeiger auf die ISR-Routine }
- Our_SP : WORD; { Wert der Stackpointers }
-
- PROCEDURE Redirector(_flags,_cs,_ip,_ax,_bx,_cx,_dx,
- _si,_di,_ds,_es,_bp : WORD);
- INTERRUPT; FORWARD;
-
- PROCEDURE Init_ISR_Code;
- (* init_isr_code richtet den ISR-Handler ein und nimmt *)
- (* die notwendigen Änderungen vor. Wichtig ist, daß *)
- (* die Routine an einer Paragraphengrenze beginnt, so *)
- (* daß die Offsets innerhalb der ISR-Routine stimmig *)
- (* sind. Der Redirector ist als forward deklariert, da *)
- (* er so etwa die Hälfte des Programms aufruft und *)
- (* deshalb am Ende steht. *)
- TYPE
- OS = RECORD
- o, s : WORD;
- END;
- VAR
- P : POINTER;
- i : POINTER ABSOLUTE ISR;
- BEGIN
- { Speicher reservieren und an
- Paragraphengrenze ausrichten }
- GetMem(ISR, SizeOf(ISR_Rec)+15);
- Inc(os(ISR).s, (os(ISR).o+15) SHR 4);
- { Code eintragen und modifizieren }
- ISR^.ic := ISR_Code;
- GetIntVec($2F, P);
- os(ISR).o := Redir_Entry;
- Pointer(i^) := @Redirector;
- os(ISR).o := Our_SS_Ofs; Word(i^) := SSeg;
- os(ISR).o := Our_Sp_Ofs; Word(i^) := Our_SP;
- os(ISR).o := Prev_Hndlr; Pointer(i^) := P;
- { Speicher beginnt an Paragraphengrenze }
- os(ISR).o := 0;
- END;
-
- (* ====================================================== *)
- (* Prozeduren für Redirector *)
-
- VAR
- Head : fx_Command_Head; { Variable für Befehl }
- Drive_No : BYTE; { Nummer des Laufwerks (F: --> 5) }
- Sda_fn1 : ^ASCIIZ; { Zeiger auf ersten ASCIIZ-Par. }
- Sda_fn2 : ^ASCIIZ; { Zgr. auf 2. Parameter (Rename) }
- Sda_SrchAttr : ^BYTE; { gewünschtes Suchattribut }
- Sda_Sdb : Sdb_Ptr; { Zeiger auf Suchdatenblock }
- Sda_Dib : Dir_Ptr; { Zeiger auf Directory-Info }
- Sda_CurrDta : ^POINTER;{ Zeiger auf aktuelle DTA }
- Sda_CurrPsp : ^WORD; { Zeiger auf aktuelen PSP }
- Sda_SpecPop : ^tSpecPop; { Zeiger für extended open }
- Sda_OpenMode : ^BYTE; { Zeiger auf gewünschten Openmode }
- Cds_CurrPath : ^PathPtr; { Zeiger auf akt. Pfad im Lw. }
- Buf : ASCIIZ; { Puffer (128 Byte) }
- R : RegSet; { Register beim Aufruf d. Prog. }
-
- {$F+}
-
- PROCEDURE p_RemDir;
- (* Remove Directory - subfunction 01h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständiger Directory-Name *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* 3 : Suchweg nicht gefunden - Verzeichnis nicht *)
- (* lokalisierbar *)
- (* 5 : Zugriff verweigert (Verz. z.B. nicht löschbar) *)
- VAR
- Ans : Ans_RemDir ABSOLUTE Buf;
- BEGIN
- Head.Command := _RemDir;
- Head.Fn1 := Sda_fn1^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_RemDir));
- R.Flags := Ans.Flags;
- R.AX := Ans.AX;
- END;
-
- PROCEDURE p_MakeDir;
- (* Make Directory - subfunction 03h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständiger Directory-Name *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* 3 : Suchweg nicht gefunden - Verzeichnis nicht *)
- (* lokalisierbar *)
- (* 5 : Zugriff verweigert (Verz. z.B. nicht löschbar) *)
- VAR
- Ans : Ans_MakeDir ABSOLUTE Buf;
- BEGIN
- Head.Command := _MakeDir;
- Head.Fn1 := Sda_fn1^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_MakeDir));
- R.Flags := Ans.Flags;
- R.AX := Ans.AX;
- END;
-
- PROCEDURE p_ChDir;
- (* Change Directory - subfunction 05h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständiger Directory-Name *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* 3 : Suchweg nicht gefunden - Verzeichnis nicht *)
- (* lokalisierbar *)
- (* CDS.curr_path muß auf den aktuellen Wert gesetzt *)
- (* werden. Außer beim Root-Verzeichnis darf kein '\' *)
- (* am Ende stehen. *)
- VAR
- Ans : Ans_ChDir ABSOLUTE Buf;
- BEGIN
- Head.Command := _ChDir;
- Head.Fn1 := Sda_fn1^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_ChDir));
- R.Flags := Ans.Flags;
- R.AX := Ans.AX;
- IF (R.Flags AND FCarry) = 0 THEN
- Cds_CurrPath^^ := Ans.Curr_Path;
- END;
-
- PROCEDURE p_Close;
- (* Close File - subfunction 06h *)
- (* Eingabeparameter: *)
- (* es:di zeigen auf SFT des Files, das geschlossen *)
- (* werden soll. *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* -Fehler entsprechen DOS-Fehlern Schäpers S. 692 ff. *)
- (* SFT muß ausgewertet werden *)
- (* (handle_cnt nicht (?) ändern): *)
- (* z.B. f_date und f_time korrekt eintragen *)
- VAR
- Ans : Ans_Close ABSOLUTE Buf;
- Sp : SFT_Ptr;
- BEGIN
- Sp := Ptr(R.ES, R.DI);
- Head.Command := _Close;
- Head.SFT := Sp^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_Close));
- R.Flags := Ans.Flags;
- R.AX := Ans.AX;
- Sp^ := Ans.SFT;
- END;
-
- PROCEDURE p_Commit;
- (* Commit File - subfunction 07h *)
- (* Eingabeparameter: *)
- (* es:di zeigen auf SFT des Files, das geflusht *)
- (* werden soll. *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* _Fehler entsprechen DOS-Fehlern Schäpers S. 692 ff. *)
- (* Puffer müssen geleert werden. *)
- BEGIN
- END;
-
- PROCEDURE p_Read;
- (* Read from File - subfunction 08h *)
- (* Eingabeparameter: *)
- (* es:di zeigen auf SFT des Files, das gelesen werden *)
- (* soll. *)
- (* cx Anzahl der Bytes, die aus dem File gelesen *)
- (* werden sollen *)
- (* SDA.curr_dta zeigt auf Datenpuffer *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* Fehler entsprechen DOS-Fehlern Schäpers S. 692 ff. *)
- (* CX : wirklich gelesene Bytes *)
- (* Die SFT muß entsprechend upgedated werden. *)
- (* (f_pos) *)
- VAR
- Ans : Ans_Read ABSOLUTE Buf;
- Sp : SFT_Ptr;
- BEGIN
- Sp := Ptr(R.ES,R.DI);
- Head.Command := _Read; { Befehl }
- Head.SFT := Sp^; { SFT }
- Head.Param1 := R.CX; { Größe }
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_Read));
- R.Flags := Ans.Flags;
- R.AX := Ans.AX;
- Sp^ := Ans.SFT;
- R.CX := Ans.Size;
- { Block lesen }
- IF ((R.Flags AND FCarry) = 0) AND (Ans.Size <> 0) THEN
- ReceiveCRCBuf(Sda_CurrDta^^, R.CX);
- END;
-
- PROCEDURE p_Write;
- (* Write to File - subfunction 09h *)
- (* Eingabeparameter: *)
- (* es:di zeigen auf SFT des Files, in das geschrieben *)
- (* werden soll. *)
- (* cx Anzahl der Bytes, die in das File *)
- (* geschrieben werden sollen *)
- (* SDA.curr_dta zeigt auf Datenpuffer *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* Fehler entsprechen DOS-Fehlern Schäpers S. 692 ff. *)
- (* CX : wirklich gelesene Bytes *)
- (* Die SFT muß entsprechend upgedated werden. *)
- (* (f_pos, f_size, dev_info Bit 6 löschen) *)
- VAR
- Ans : Ans_Write ABSOLUTE Buf;
- Sp : SFT_Ptr;
- BEGIN
- Sp := Ptr(R.ES,R.DI);
- Head.Command := _Write; { Befehl }
- Head.SFT := Sp^; { SFT }
- Head.Param1 := R.CX; { Größe }
- SendCRCBuf(Head, SizeOf(Head)); { Header senden }
- IF R.CX > 0 THEN
- SendCRCBuf(Sda_CurrDta^^, R.CX);{ Daten senden }
- ReceiveCRCBuf(Buf, SizeOf(Ans_Write));
- R.Flags := Ans.Flags;
- R.AX := Ans.AX;
- Sp^ := Ans.SFT;
- R.CX := Ans.Size;
- END;
-
- PROCEDURE p_GetSpace;
- (* Get Disk Space - subfunction 0Ch *)
- (* Eingabeparameter: *)
- (* es:di : Zeiger auf CDS-Struktur *)
- (* Ausgabeparameter: *)
- (* al : Sektoren pro Cluster *)
- (* bx : Gesamtzahl der Cluster *)
- (* cx : Bytes pro Sektor *)
- (* dx : Verfügbare Cluster *)
- (* Benutzt werden al*bx*cx und al*cx*dx *)
- VAR
- P : ^PathArray;
- Ans : Ans_GetSpace ABSOLUTE Buf;
- BEGIN
- P := Ptr(R.ES, R.DI);
- Head.Command := _GetSpace;
- Move(P^, Head.Fn1, 67);
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_GetSpace));
- R.AX := Ans.SPC;
- R.BX := Ans.Totc;
- R.CX := Ans.Bps;
- R.DX := Ans.Freec;
- END;
-
- PROCEDURE p_SetAttr;
- (* Set File Attributes - subfunction 0Eh *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : Vollständiger Dateiname *)
- (* word auf dem TopOfStack: Neue File-Attribute *)
- (* byte(ptr(r.ss,r.sp)^) *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* Fehler entsprechen DOS-Fehlern Schäpers S. 692 ff. *)
- (* 2 : Datei nicht gefunden *)
- (* 5 : Zugriff verweigert *)
- VAR
- Ans : Ans_SetAttr ABSOLUTE Buf;
- BEGIN
- Head.Command := _SetAttr; { Kommando }
- Head.Fn1 := Sda_fn1^; { File }
- Head.Param0 := BYTE(Ptr(R.SS,R.Sp)^); { neues Attr. }
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_SetAttr));
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- END;
-
- PROCEDURE p_GetAttr;
- (* Get File Attributes - subfunction 0Fh *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : Vollständiger Dateiname *)
- (* word auf dem TopOfStack: Neue File-Attribute *)
- (* byte(ptr(r.ss,r.sp)^) *)
- (* Ausgabeparameter: *)
- (* AX ohne Carry: File Attribut *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* Fehler entsprechen DOS-Fehlern Schäpers S. 692 ff. *)
- (* 2 : Datei nicht gefunden *)
- (* 5 : Zugriff verweigert *)
- VAR
- Ans : Ans_GetAttr ABSOLUTE Buf;
- BEGIN
- Head.Command := _GetAttr; { Kommando }
- Head.Fn1 := Sda_fn1^; { File }
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_GetAttr));
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- R.BX := Ans.BX;
- R.DI := Ans.DI;
- END;
-
- PROCEDURE p_Rename;
- (* Rename File - subfunction 11h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : Vollständiger Dateiname (mit Wildcards!) *)
- (* SDA.fn2 : Neuer vollständiger Dateiname *)
- (* (Zugriff über ss:fn2_csofs) *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- VAR
- Ans : Ans_Rename ABSOLUTE Buf;
- BEGIN
- Head.Command := _Rename;
- Head.Fn1 := Sda_fn1^;
- SendCRCBuf(Head, SizeOf(Head));
- Buf := Sda_fn2^;
- SendCRCBuf(Buf, SizeOf(Buf));
- ReceiveCRCBuf(Buf, SizeOf(Ans_Rename));
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- END;
-
- PROCEDURE p_Delete;
- (* Delete File - subfunction 13h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : Vollständiger Dateiname (mit Wildcards!) *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- VAR
- Ans : Ans_Delete ABSOLUTE Buf;
- BEGIN
- Head.Command := _Delete;
- Head.Fn1 := Sda_fn1^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_Delete));
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- END;
-
- PROCEDURE p_Open;
- (* Open Existing File - subfunction 16h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständiger Dateiname *)
- (* es:di : Zeiger auf uninitialisierten SFT *)
- (* SDA.open_mode : gewünschter OpenMode für File *)
- (* (0: Lesen, 1:Schreiben, *)
- (* 2: Lesen/Schreiben *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* SFT komplettiert *)
- (* aus File übernehmen: f_size, f_date, f_time, *)
- (* fcb_fn, attr_byte *)
- (* ändern: open_mode Bit 7 löschen *)
- (* setzen: dev_info ($8040 or drive_no), *)
- (* dir_sector (0), dir_entryno (0), *)
- (* dev_drvptr (nil), f_pos (0) *)
- VAR
- Ans : Ans_Open ABSOLUTE Buf;
- Sp : ^SFT_Rec;
- BP : ^BYTE;
- BEGIN
- BP := Ptr(R.SS, R.Sp);
- Sp := Ptr(R.ES, R.DI);
- Head.Command := _Open;
- Head.Fn1 := Sda_fn1^;
- Head.Param0 := Sda_OpenMode^ + BP^ SHL 8;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_Open));
- Ans.SFT.Dev_Info := $8040 OR Drive_No;
- Sp^ := Ans.SFT;
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- END;
-
- PROCEDURE p_Create;
- (* Truncate/Create File - subfunction 17h *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständiger Dateiname *)
- (* es:di : Zeiger auf uninitialisierten SFT *)
- (* word auf TOS : Fileattribut + 'openwish' *)
- (* (0:normal 1:create new) *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* SFT komplettiert *)
- (* aus File übernehmen: f_size, f_date, f_time, *)
- (* fcb_fn, attr_byte (?) *)
- (* ändern: open_mode Bit 7 löschen *)
- (* setzen: dev_info ($8040 or drive_no), *)
- (* dir_sector (0), dir_entryno (0), *)
- (* dev_drvptr (nil), f_pos (0) *)
- (* !!! keine Lösung für DOS Funktion $5B !!! *)
- VAR
- Ans : Ans_Create ABSOLUTE Buf;
- Sp : SFT_Ptr;
- BEGIN
- Sp := Ptr(R.ES, R.DI);
- Head.Command := _Create;
- Head.Fn1 := Sda_fn1^;
- Head.Param0 := WORD(Ptr(R.SS, R.Sp)^);
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_Create));
- Ans.SFT.Dev_Info := $8040 OR Drive_No;
- Sp^ := Ans.SFT;
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- END;
-
- PROCEDURE p_FindFirst;
- (* FindFirst - subfunction 1Bh *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständige Spezifikation der Datei*)
- (* SDA.sdb : Suchdatenblock (uninitialisiert) *)
- (* SDA.curr_dta : zeigt auf Directory-Info-Buffer *)
- (* SDA.srch_attr: Such-Attributmaske *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* zusätzlich: 18 : kein File gefunden *)
- (* initialisierter SDB und Directory-Info-Buffer *)
- VAR
- Ans : Ans_FindFirst ABSOLUTE Buf;
- BEGIN
- Head.Command := _FindFirst;
- Head.Fn1 := Sda_fn1^;
- Head.Param0 := Sda_SrchAttr^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_FindFirst));
- Ans.Sdb.Drv_Lett := Drive_No OR $80;
- Sda_Sdb^ := Ans.Sdb;
- Sda_Dib^ := Ans.Dib;
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- END;
-
- PROCEDURE p_FindNext;
- (* FindNext - subfunction 1Ch *)
- (* Eingabeparameter: *)
- (* SDA.sdb : Suchdatenblock (initialisiert) *)
- (* SDA.curr_dta : zeigt auf Directory-Info-Buffer *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- (* zusätzlich: 18 : kein File gefunden *)
- (* initialisierter SDB und Directory-Info-Buffer *)
- VAR
- Ans : Ans_FindNext ABSOLUTE Buf;
- BEGIN
- Head.Command := _FindNext;
- Head.Sdb := Sda_Sdb^;
- Head.Dir := Sda_Dib^;
- SendCRCBuf(Head, SizeOf(Head));
- ReceiveCRCBuf(Buf, SizeOf(Ans_FindNext));
- Ans.Sdb.Drv_Lett := $80 OR Drive_No;
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- Sda_Sdb^ := Ans.Sdb;
- Sda_Dib^ := Ans.Dib;
- END;
-
- PROCEDURE p_SeekEnd;
- (* Seek From End Of File - subfunction 21h *)
- (* Eingabeparameter: *)
- (* es:di : Zeiger auf SFT des Files *)
- (* cx:dx : Offset, auf das positioniert werden soll. *)
- (* Ausgabeparameter: *)
- (* AX+Carry, falls Fehler aufgetreten *)
- VAR
- Sp : SFT_Ptr;
- Long : LongInt;
- BEGIN
- Sp := Ptr(R.ES, R.DI);
- os(Long).s := R.CX;
- os(Long).o := R.DX;
- Sp^.f_Pos := Sp^.f_Size-Long;
- R.DX := os(Long).s;
- R.AX := os(Long).o;
- END;
-
- PROCEDURE p_Hook;
- (* Process Termination Hook - subfunction 22h *)
- (* Der Process Termination Hook wird immer aufgerufen, *)
- (* wenn ein Programm beendet wird. Dies ist notwendig, *)
- (* da viele Programm (z.B. Windows) gar nicht daran *)
- (* denken, ihre eigenen Dateien zu schließen... *)
- BEGIN
- Head.Command := _Hook;
- SendCRCBuf(Head,SizeOf(Head));
- END;
-
- PROCEDURE p_ExtendOpen;
- (* Special Multi-Purpose Open File - subfunction 2Eh *)
- (* Diese Funktion wird u.a. von Windows aufgerufen. Sie *)
- (* hat wie die Funktion 17h das Problem der nicht *)
- (* bekannten Mitteilung des Fehlerfalles. *)
- (* Eingabeparameter: *)
- (* SDA.fn1 : vollständiger Dateiname *)
- (* es:di : uninitialisierte SFT für File *)
- (* SDA.spop_act : Action-Code (siehe Schäpers) *)
- (* SDA.spop_attr : Fileattribut *)
- (* SDA.spop_mode : Modus *)
- (* Ausgabeparameter: *)
- (* Carry und AX im Fehlerfall *)
- (* initialisierte SFT *)
- VAR
- Ans : Ans_ExtendOpen ABSOLUTE Buf;
- Sp : ^SFT_Rec;
- BEGIN
- Sp := Ptr(R.ES, R.DI);
- Head.Command := _ExtendOpen;
- Head.Fn1 := Sda_fn1^;
- SendCRCBuf(Head, SizeOf(Head));
- SendCRCBuf(Sda_SpecPop^, SizeOf(tSpecPop));
- ReceiveCRCBuf(Buf, SizeOf(Ans_ExtendOpen));
- Ans.SFT.Dev_Info := $8040 OR Drive_No;
- Sp^ := Ans.SFT;
- R.AX := Ans.AX;
- R.Flags := Ans.Flags;
- R.CX := Ans.CX;
- END;
-
- PROCEDURE p_Inquiry;
- BEGIN
- R.AX := $00FF;
- END;
-
- {$F-}
-
- (* Der Redirector *)
-
- CONST
- Fxn_Map_Max = $2E;
-
- TYPE
- SubFunction = PROCEDURE;
- Proc_Tbl = ARRAY [0..Fxn_Map_Max] OF SubFunction;
-
- CONST
- FuncTbl : Proc_Tbl =
- (p_Inquiry, p_RemDir, NIL, p_MakeDir, { 00-03 }
- NIL, p_ChDir, p_Close, p_Commit, { 04-07 }
- p_Read, p_Write, NIL, NIL, { 08-0B }
- p_GetSpace, NIL, p_SetAttr, p_GetAttr, { 0C-0F }
- NIL, p_Rename, NIL, p_Delete, { 10-13 }
- NIL, NIL, p_Open, p_Create, { 14-17 }
- NIL, NIL, NIL, p_FindFirst, { 18-1B }
- p_FindNext, p_Hook, NIL, NIL, { 1C-1F }
- NIL, p_SeekEnd, p_Hook, NIL, { 20-23 }
- NIL, NIL, NIL, NIL, { 24-27 }
- NIL, NIL, NIL, NIL, { 28-2B }
- NIL, NIL, p_ExtendOpen); { 2C-2E }
-
- VAR
- Func : SubFunction;
- { gewünschte Unterfunktion }
- fPtr : POINTER ABSOLUTE Func;
- { Zeiger auf dieselbe }
- TransferBreak : WaitResult; { Flag (Kanalüberwachung) }
-
- { Der Redirector ist der zweite Teil des ISR-Handlers, }
- { diesmal in Turbo Pascal. Die Routine ist als }
- { "interrupt" deklariert und sichert daher automatisch }
- { fast alle Register. Die restlichen Register wurden }
- { schon durch die ISR-Routine gesichert. Wird der }
- { Redirector benötigt, speichert er alle Register in der }
- { Variablen "r" ab, die im Gegensatz zum Typ "Registers" }
- { wirklich alle Register enthält. Die werden bei Ver- }
- { lassen wieder restauriert, so daß jedes Register }
- { geändert werden kann. }
-
- PROCEDURE Redirector(_flags,_cs,_ip,_ax,_bx,_cx,_dx,
- _si,_di,_ds,_es,_bp : WORD);
- BEGIN
- WITH R DO BEGIN
- { our_drive = false --> call wird durchgereicht }
- ISR^.Our_Drive:=FALSE;
- { wenn wir den Call nicht unterstützen,
- wird er durchgereicht }
- IF Lo(_ax) > Fxn_Map_Max THEN
- Exit
- ELSE
- Func := FuncTbl[Lo(_ax)];
- IF fPtr = NIL THEN Exit;
-
- { wenn der call nicht für uns ist,
- wird er ebenfalls durchgereicht }
- IF ((Lo(_ax) >= $6) AND (Lo(_ax) <= $B)) OR
- (Lo(_ax) = $21) THEN BEGIN
- { SFT-Funktionen }
- IF (SFT_Rec(Ptr(_es,_di)^).Dev_Info AND
- $1F) <> Drive_No THEN BEGIN
- Exit;
- END;
- END ELSE
- { Funktionen, in denen currpath auf die
- korrekte cds zeigt }
- IF (Lo(_ax) > 0) AND (Lo(_ax) <> $1C) AND
- (Lo(_ax) <> $1D) AND (Lo(_ax) <> $22) THEN BEGIN
- IF StrLComp(Cds_CurrPath^^,
- ID_Drv, ID_Max-1) <> 0 THEN Exit;
- END;
-
- ISR^.Our_Drive := TRUE;
- { Speichern der Register }
- Move(_bp, BP, 18);
- SS := ISR^.Save_SS;
- Sp := ISR^.Save_SP;
- CS := ISR^.Save_CS;
- IP := ISR^.Save_IP;
- Flags := ISR^.Real_FL;
- { alles ok ... }
- { Überprüfung des Übertragungskanals ... }
- AX := 0;
- Flags := Flags AND NOT FCarry;
- { lokale Verarbeitung }
- Head.Current_PSP := Sda_CurrPsp^;
- Func;
- { Register wiederherstellen }
- Move(BP, _bp, 18);
- ISR^.Save_SS := SS;
- ISR^.Save_SP := Sp;
- ISR^.Save_CS := CS;
- ISR^.Save_IP := IP;
- ISR^.Real_FL := Flags;
- END;
- END;
-
- (* TSR-Routinen *)
-
- TYPE
- Sig_Rec = RECORD
- Signature : STRING[7];
- { Signatur zum Wiederfinden des Interrupts }
- PSP : WORD; { Segment des PSPs zum Löschen }
- Drive_No : BYTE; { Laufwerksnummer }
- LptAdr : WORD; { Adresse der Schnittstelle }
- tMode : TransMode;{ 4- oder 8-Bit Transfer }
- tLast : ^BYTE; { senden oder empfangen... }
- OldLpt : ARRAY [1..4] OF WORD;
- END;
- SigPtr = ^Sig_Rec;
- OS = RECORD { Zur Bearbeitung von Zeigern }
- o, s : WORD;
- END;
-
- CONST
- Our : Sig_Rec = (Signature : 'TOOLBOX';
- PSP : 0;
- Drive_No : 0;
- LptAdr : 0;
- tMode : Transfer4;
- tLast : NIL);
-
- VAR
- LoL : ^LoL_Rec; (* List of Lists *)
- Drive : STRING[3];
-
- PROCEDURE FailProg(st : STRING);
- BEGIN
- WriteLn(st);
- Halt(1);
- END;
-
- FUNCTION Get_Dos_Vars : BYTE;
- (* get_dos_vars holt die DOS-Version, die Adresse der *)
- (* SDA und der LoL. Da das Programm nur DOS-Versionen *)
- (* ab 3.10 unterstützt, wird ggf. abgebrochen. Als *)
- (* Funktionsergebnis wird die Hauptversion von DOS *)
- (* zurückgegeben. *)
- VAR
- R : Registers;
- Ver : WORD;
- SDA : POINTER;
- BEGIN
- { DOS-Version ermitteln }
- Ver := Swap(DosVersion);
- IF (Ver < $30A) OR (Ver > $0A00 { OS/2 }) THEN
- FailProg('DOS Version must be 3.10 or greater');
- WITH R DO BEGIN
- AX := $5D06;
- MsDos(R);
- SDA := Ptr(DS, SI); { Get SDA pointer }
- AX := $5200;
- MsDos(R);
- LoL := Ptr(ES, BX); { Get LoL pointer }
- END;
- { Variablen initialisieren }
- IF Ver >= $400 THEN BEGIN
- Sda_fn1 := @Sda4_Rec(SDA^).Fn1;
- Sda_fn2 := @Sda4_Rec(SDA^).Fn2;
- Sda_SrchAttr := @Sda4_Rec(SDA^).Srch_Attr;
- Sda_Sdb := @Sda4_Rec(SDA^).Sdb;
- Sda_Dib := @Sda4_Rec(SDA^).Found_File;
- Sda_CurrDta := @Sda4_Rec(SDA^).Curr_DTA;
- Sda_CurrPsp := @Sda4_Rec(SDA^).Curr_PSP;
- Sda_SpecPop := @Sda4_Rec(SDA^).sPop;
- Sda_OpenMode := @Sda4_Rec(SDA^).Open_Mode;
- Cds_CurrPath := @Sda4_Rec(SDA^).Drive_CdsPtr;
- END ELSE BEGIN
- Sda_fn1 := @Sda3_Rec(SDA^).Fn1;
- Sda_fn2 := @Sda3_Rec(SDA^).Fn2;
- Sda_SrchAttr := @Sda3_Rec(SDA^).Srch_Attr;
- Sda_Sdb := @Sda3_Rec(SDA^).Sdb;
- Sda_Dib := @Sda3_Rec(SDA^).Drive_CdsPtr;
- Sda_CurrDta := @Sda3_Rec(SDA^).Curr_DTA;
- Sda_CurrPsp := @Sda3_Rec(SDA^).Curr_PSP;
- Sda_OpenMode := @Sda3_Rec(SDA^).Open_Mode;
- Cds_CurrPath := @Sda3_Rec(SDA^).Drive_CdsPtr;
- END;
- Get_Dos_Vars := Hi(Ver);
- END;
-
- FUNCTION Installed_2f:BYTE; ASSEMBLER;
- (* installed_2f überprüft, ob überhaupt ein
- Redirector gesetzt werden kann *)
- ASM
- MOV AX,1100H
- INT 2FH
- END;
-
- PROCEDURE Set_Path_Entry(Dos_Major : BYTE);
- (* set_path_entry trägt das Laufwerk in die CDS-Tabelle *)
- (* ein. Es überprüft in der LoL, ob der Eintrag möglich *)
- (* ist und das Laufwerk nicht schon benutzt. Die Flags, *)
- (* der current_path und der root_ofs müssen gesetzt *)
- (* werden. Außerdem initialisiert set_path_entry den *)
- (* Int-2F-Hook *)
- VAR
- Our_Cds : ^Cds3_Rec;
- BEGIN
- { Installieren des $2F-Handlers }
- IF Installed_2f=1 THEN
- FailProg('Not OK to install a redirector...');
-
- (* !!! Achtung: Es wird angenommen, daß auf dem Stack *)
- (* !!! 256 Bytes belegt sind. Sollte das *)
- (* !!! Programm nicht klappen, könnte es *)
- (* !!! daran liegen. *)
- Our_SP := sPtr + $100;
-
- { Initialisierung des ISR-Codes: }
- Init_ISR_Code;
-
- { A = 1, B = 2, C = 3, ... }
- Drive_No := BYTE(Drive[1]) - BYTE('@');
-
- { Laufwerk überhaupt in CDS enthalten ? }
- IF Drive_No > LoL^.Last_Drive THEN
- FailProg('in CONFIG.SYS »LASTDRIVE=' + Drive[1] +
- '« eintragen');
-
- { CDS-Eintrag bestimmen ... }
- IF Dos_Major = 3 THEN
- Our_Cds := @LoL^.Cds^.Cds3[Drive_No]
- ELSE
- Our_Cds := @LoL^.Cds^.Cds4[Drive_No];
-
- { CDS-Eintrag für Programm modifizieren ... }
- WITH Our_Cds^ DO BEGIN
- IF (Flags AND $C000)<>0 THEN { Laufwerk unbenutzt ? }
- FailProg('Drive already assigned.');
- Flags := Flags OR $C000;
- { Network und Physical Bit an }
- { Laufwerksname sei "RMT.F:\" }
- ID_Drv[ID_Max-3] := CHAR(BYTE('@') + Drive_No);
- { Laufwerk in CDS eintragen }
- StrCopy(Curr_Path, ID_Drv);
- Root_Ofs := ID_Max-1;
- END;
- END;
-
- PROCEDURE Kill_SFT;
- (* kill_sft löscht alle Einträge in der SFT, die von *)
- (* unserem Installationsprogramm benutzt werden. *)
- VAR
- jPtr : ^BYTE;
- jftSize : WORD;
- BEGIN
- jPtr := Pointer(Ptr(PrefixSeg,$34)^);
- jftSize := Mem[PrefixSeg:$32];
- ASM
- MOV CX,jftSize
- LES DI,jPtr
- ADD DI,CX
- @LOOP:
- Dec DI
- MOV BL,ES:[DI]
- CMP BL,0FFH
- JZ @NoHndl
- MOV BX,CX
- DEC BX
- MOV AH,3EH
- INT 21H
- @NoHndl:
- LOOP @LOOP
- END;
- END;
-
- PROCEDURE TSR;
- (* tsr ersetzt die Keep-Routine. Sie gibt das *)
- (* Environment frei und reserviert lediglich den *)
- (* Speicherbereich zwischen aktuellem HeapPtr und *)
- (* Präfix. Absolute Vorsicht also bei new während des *)
- (* residenten Zustands! *)
- VAR
- R : Registers;
- BEGIN
- SwapVectors;
- { Tauscht SYSTEM-Interrupt-Vektoren mit DOS-Vektoren aus }
- R.AX := $4900;
- { folgenden Speicherblock freigeben : }
- R.ES := MemW[PrefixSeg:$2C]; { Environment-Segment }
- MsDos(R);
- R.AX := $3100;
- { Terminate & Stay Resident für folg. Block: }
- R.DX := os(HeapPtr).s - PrefixSeg + 1;
- { Bereich von Präfix - akt. HeapPtr }
- MsDos(R);
- END;
-
- PROCEDURE Settle_Down;
- (* settle_down hängt das Programm in den Interrupt $2F *)
- (* und speichert an der erstbesten Stelle im Bereich *)
- (* $60 bis $67 einen Verweis auf das TSR-Programm ab. *)
- (* Dieser kann später zum Löschen eingesetzt werden. *)
- VAR
- i : INTEGER;
- BEGIN
- SetIntVec($2F,ISR);
- { Einhängen der ISR in Int $2F }
- { Suchen eines freien Interrupt im Bereich $60 bis $67 }
- { zum Abspeichern der TSR-Daten, die zum Unload }
- { benötigt werden. }
- i := $60;
- WHILE (i <= $67) AND
- (Pointer(Ptr(0, i SHL 2)^) <> NIL) DO Inc(i);
- IF i = $68 THEN BEGIN
- { Kein Interrupt zum Unloaden gefunden ... }
- WriteLn('No user interrupt available. ',
- 'REMOTE not unloadable...');
- Kill_SFT;
- TSR;
- END;
- { Abspeichern des Signatur-Records in der Kommandozeile}
- { des PSP (Offset $80). Der Zeiger auf die Signatur }
- { wird im Int-i abgelegt. Anschließend wird das }
- { Programm resident gemacht. }
- SetIntVec(i, Ptr(PrefixSeg, $80));
- Our.PSP := PrefixSeg;
- Our.Drive_No := Drive_No;
- Sig_Rec(Ptr(PrefixSeg, $80)^) := Our;
- Kill_SFT;
- TSR;
- END;
-
- PROCEDURE Do_Unload(Dos_Major : BYTE);
- (* do_unload sucht das letzte installierte Laufwerk und *)
- (* hängt es aus der INT-2F-Kette aus. Anschließend wird *)
- (* der Speicher freigegeben und in der CDS das Laufwerk *)
- (* als nicht vorhanden markiert. *)
- VAR
- i : INTEGER;
- P, Cds : POINTER;
- R : Registers;
- BEGIN
- { Signatur-Record von hinten her suchen }
- i := $67;
- WHILE (i >= $60) AND
- (SigPtr(Ptr(0, i SHL 2)^)^.Signature <> Our.Signature)
- DO Dec(i);
- { Signatur-Record nicht gefunden ... }
- IF i = $5F THEN
- FailProg(Our.Signature + ' nicht gefunden...');
- GetIntVec($2F, P);
- { nicht unser 2F-Handler... }
- IF os(P).o <> 0 THEN
- FailProg('2F überschrieben...');
- { alten Handler eintragen ... }
- os(P).o := Prev_Hndlr;
- SetIntVec($2F, Pointer(P^));
- GetIntVec(i, P);
- Drive_No := Sig_Rec(P^).Drive_No;
- { Speicher freigeben ... }
- WITH R DO BEGIN
- { Funktion $49 gibt Speicherblock "ES" frei }
- AX := $4900;
- ES := Sig_Rec(P^).PSP;
- MsDos(R);
- IF Boolean(Flags AND FCarry) THEN
- WriteLn('Freigabe des Hauptspeichers nicht ',
- 'möglich...');
- END;
- { User-Interrupt freigeben ... }
- SetIntVec(i,NIL);
- { Selbstmordbefehl an den Remote-Rechner }
- UnitInit(Sig_Rec(P^).LptAdr);
- TransferMode := Sig_Rec(P^).tMode;
- LastMode := Sig_Rec(P^).tLast^;
- { BIOS wieder reparieren ... }
- Move(Sig_Rec(P^).OldLpt, Mem[$40:$08],
- SizeOf(Our.OldLpt));
- Write('«Taste»');
- SetKbdWatchdog;
- Head.Command := _KillContact;
- SendCRCBuf(Head,SizeOf(Head));
- ClrKbdWatchdog;
- WriteLn(^H^H^H^H^H^H^H^H, ' ');
- WHILE KeyPressed AND (ReadKey = #0) DO (* nothing *) ;
- { CDS-Struktur aus der LoL bestimmen }
- Cds := LoL_Rec(LoL^).Cds;
- IF Dos_Major = 3 THEN
- Inc(os(Cds).o, SizeOf(Cds3_Rec) * Pred(Drive_No))
- ELSE
- Inc(os(Cds).o, SizeOf(Cds4_rec) * Pred(Drive_No));
- { Laufwerk als gelöscht markieren ... }
- WITH Cds3_Rec(Cds^) DO
- Flags := Flags AND $3FFF;
- { oberste 5 Bit geben Typ des Lw an... }
- WriteLn('Laufwerk ', CHAR(BYTE('@') + Drive_No),
- ': nicht mehr vorhanden.');
- END;
-
- FUNCTION GetHex(VAR s : STRING; VAR Val : WORD) : WORD;
- VAR
- hw, w : WORD;
- i : BYTE;
- BEGIN
- IF Length(s) > 4 THEN BEGIN
- GetHex := 5;
- Exit;
- END;
- w := 0;
- FOR i := 1 TO Length(s) DO BEGIN
- hw := 0; s[i] := UpCase(s[i]);
- IF (s[i] >= '0') AND (s[i] <= '9') THEN
- hw := Ord(s[i]) - Byte('0')
- ELSE IF (s[i] >= 'A') AND (s[i] <= 'F') THEN
- hw := Ord(s[i]) - Byte('A') + 10
- ELSE BEGIN
- GetHex := i;
- Exit;
- END;
- w := w SHL 4 + hw;
- END;
- Val := w;
- GetHex := 0;
- END;
-
- VAR
- Error, Nr, Dos_Major : WORD;
- st : STRING[6];
- i, j : WORD;
- BEGIN
- IF (ParamCount <> 1) AND (ParamCount <> 2) THEN
- FailProg('Benutzung: REMOTE drive-letter: '+
- 'lptnr oder REMOTE -u zum deinstallieren');
- Drive := ParamStr(1);
- Drive[1] := UpCase(Drive[1]);
- Dos_Major := Get_Dos_Vars;
- { Aufforderung zum Unloaden }
- IF (Drive = '-u') OR (Drive = '-U') THEN
- { Entfernen der residenten Version }
- Do_Unload(Dos_Major)
- ELSE BEGIN
- { Überprüfung auf 'korrektes' Verzeichnis }
- IF (Length(Drive) > 2) OR
- (Drive[1] < 'A') OR (Drive[1] > 'Z') OR
- ((Length(Drive) = 2) AND (Drive[2] <> ':')) THEN
- FailProg('Benutzung: REMOTE drive-letter: lptnr oder'+
- ' REMOTE -u zum Deinstallieren')
- ELSE BEGIN
- st := ParamStr(2);
- IF st[1] = '$' THEN BEGIN
- Delete(st, 1, 1);
- Error := GetHex(st, Nr);
- END ELSE BEGIN
- Val(st, Nr, Error);
- IF Error = 0 THEN Nr := GetLPTAdress(Nr);
- END;
- IF Error <> 0 THEN BEGIN
- FailProg('Benutzung: REMOTE drive-letter: '+
- 'lptnr oder REMOTE -u zum Deinstallieren');
- END;
- UnitInit(Nr);
- Our.LptAdr := Nr;
- { Nun klauen wir die Adresse... }
- Move(Mem[Seg0040:$08], Our.OldLpt, SizeOf(Our.OldLpt));
- i := 0;
- j := 0;
- WHILE i <= 3 DO BEGIN
- IF MemW[Seg0040:$08+2*i] <> Nr THEN BEGIN
- MemW[Seg0040:$08+2*j] := MemW[Seg0040:$08+2*i];
- Inc(j);
- END;
- Inc(i);
- END;
- WHILE j <= 3 DO BEGIN
- MemW[Seg0040:$08+2*j] := 0;
- Inc(j);
- END;
- WriteLn('Warten auf Verbindung ',
- '(Tastendruck zum Abbruch) ...');
- SetKbdWatchdog;
- StartSend;
- Our.tMode := TransferMode;
- Our.tLast := @LastMode;
- IF ParaResult = UserBreak THEN BEGIN
- WriteLn('...abgebrochen');
- Halt(1);
- END;
- ClrKbdWatchdog;
- TransferBreak := LastResult;
- Set_Path_Entry(Dos_Major);
- SendCRCBuf(ID_Drv, SizeOf(ID_Drv));
- WriteLn('Remote-Laufwerk installiert als ',
- Drive[1], ':');
- Settle_Down;
- END;
- END;
- END.
- (* ====================================================== *)
- (* Ende von REMOTE.PAS *)
-