home *** CD-ROM | disk | FTP | other *** search
- {--------------------------------------------------------------}
- { JED }
- { }
- { Jeff's Editor & Assembly Language Development Environment }
- { }
- { by Jeff Duntemann }
- { Turbo Pascal V5.00 }
- { Last update 4/19/89 }
- { }
- { (c) 1989 by Jeff Duntemann }
- { Binary Editor module (c) 1988 Borland International }
- {--------------------------------------------------------------}
-
- { Version 1.01 -- Rudimentary file error capture }
-
- {$M 16384,8192,148000}
-
-
- PROGRAM JED;
-
- { Note well that this program REQUIRES Turbo Pascal 5.0! }
-
- USES
- Bined, { From the Turbo Pascal Editor Toolbox V4.0 }
- CRT, { Standard Borland unit }
- DOS, { Standard Borland unit }
- TextInfo; { By Jeff Duntemann; published in DDJ 3/89 }
-
- TYPE
- String80 = STRING[80];
-
- CONST
- UP = True; { For forcing strings to uc/lc }
- DOWN = False;
- ConfigFileName = 'JED.CFG';
- DefaultExtension = '.ASM';
- SUBCHAR = '~';
- BlackOnWhite = $70; { Reverse video attribute' color or mono }
-
- {Coordinates of the editor window}
- Windx1 = 1;
- Windy1 = 1;
- Windx2 = 80;
- Windy2 = 25; { 43 for EGA; 50 for VGA; 66 for Genius }
- MakeBackup = True; { When True, JED creates .BAK files }
-
- {Commands other than ^K^D to exit editor}
- ExitCommands : array[0..33] of Char =
- (#2, ^K, ^Q, { Ctrl-KQ: Exit without saving file }
- #2, #0, #33, { Alt-F: Change work file }
- #2, #0, #45, { Alt-X: Save and exit }
- #2, #0, #59, { F1: Show help screen }
- #2, #0, #60, { F2: Save current file }
- #2, #0, #61, { F3: Invoke DEBUG on current .EXE file }
- #2, #0, #62, { F4: Update assemble/link command line }
- #2, #0, #63, { F5: Exec to DOS command line }
- #2, #0, #64, { F6: Examine last Exec screen }
- #2, #0, #67, { F9: Assemble only }
- #2, #0, #68, { F10: Simple MAKE: Assemble, link, and go }
- #0);
-
-
-
- TYPE
- ScreenSaveRec = RECORD
- SaveX,SaveY : Integer;
- SavePtr : Pointer
- END;
-
- ConfigRec = RECORD
- Workfile : String80;
- CursorInset : Word; { Cursor X,Y at last save }
- AssembleCommand : String80; { Command with switches }
- LinkCommand : String80; { Ditto for linker }
- TestParms : String80 { Any parameters for prog }
- END; { under development with JED }
-
- ConfigFile = FILE OF ConfigRec;
-
-
- CONST
- ConfigData : ConfigRec =
- (Workfile : 'NONAME.ASM';
- CursorInset : 0;
- AssembleCommand : 'TASM ~';
- LinkCommand : 'TLINK ~';
- TestParms : '');
-
-
-
-
- VAR
- EdData : EdCB; { Editor control block }
- ExitCode : Word; { Status code set by bin. ed. functions }
- ExitCommand : Integer; { Code for command used to leave editor }
- Fname : STRING; { Input name of file being edited }
- TempName : STRING; { Holds name while changing files }
- Quit : Boolean; { Ends program }
- DOSScreen : ScreenSaveRec; { Saves DOS screen under JED }
- JEDScreen : ScreenSaveRec; { Saves JED screen under help or exec }
- ExecScreen : ScreensaveRec; { Saves Exec screen for later examination }
- BarAttribute : Byte; { Video attribute for prompt bar }
- Now : DateTime; { For the clock display }
- ConfigStore : ConfigFile; { Contains configuration data on disk }
- UpdateConfigData : Boolean; { If True, update JED.CFG on exit }
-
-
- {-------------------------------------------------------------------------}
- { The following EXTERNAL definitions are *not* code, but screen patterns }
- { stored as external assembly language procedures. They are put to the }
- { screen using the VidBlast external machine code procedure. DO NOT TRY }
- { TO EXECUTE THEM! Bizarre machine behavior including lockup WILL occur. }
- {-------------------------------------------------------------------------}
-
- {$L JEDSCRN}
- {$F+}
- PROCEDURE JEDHelp; EXTERNAL; { JED's help screen }
- PROCEDURE JEDBar; EXTERNAL; { The prompt bar at the bottom of the screen }
- PROCEDURE JEDFile; EXTERNAL; { The file name entry box invoked with Alt-F }
- PROCEDURE JEDErr; EXTERNAL; { The JED error message box }
- {$F-}
-
-
- {$L VIDBLAST}
- {$F+}
- PROCEDURE VidBlast(ScreenEnd,StoreEnd : Pointer;
- ScreenX,ScreenY : Integer;
- ULX,ULY : Integer;
- Width,Height : Integer;
- Attribute : Byte;
- DeadLines : Integer);
- EXTERNAL;
- {$F-}
-
-
- {-UhUh-------------------------------------------------------------}
- { }
- { Bored with beeps? Try this one..the name is very characteristic }
- { of the sound. }
- {------------------------------------------------------------------}
-
- PROCEDURE UhUh;
-
- VAR
- I : Integer;
-
- BEGIN
- FOR I := 1 TO 2 DO
- BEGIN
- Sound(50);
- Delay(100);
- NoSound;
- Delay(50)
- END
- END;
-
-
- PROCEDURE StripWhite(VAR Target : String80);
-
- CONST
- WhiteSpace : SET OF Char = [#7,#8,#10,#9,#12,#13,' '];
-
- BEGIN
- WHILE (Length(Target) > 0 ) AND (Target[1] IN Whitespace) DO
- Delete(Target,1,1);
- END;
-
-
- {-ForceCase--------------------------------------------------------}
- { }
- { When Up is True, Target is forced to all upper case. When Up is }
- { False, Target is forced to all lower case. }
- {------------------------------------------------------------------}
-
- FUNCTION ForceCase(Up : Boolean; Target : STRING) : STRING;
-
- CONST
- Uppercase : SET OF Char = ['A'..'Z'];
- Lowercase : SET OF Char = ['a'..'z'];
-
- VAR
- I : INTEGER;
-
- BEGIN
- IF Up THEN FOR I := 1 TO Length(Target) DO
- IF Target[I] IN Lowercase THEN
- Target[I] := UpCase(Target[I])
- ELSE { NULL }
- ELSE FOR I := 1 TO Length(Target) DO
- IF Target[I] IN Uppercase THEN
- Target[I] := Chr(Ord(Target[I])+32);
- ForceCase := Target
- END;
-
-
- {-WriteColor-------------------------------------------------------}
- { }
- { The trick here is to save the current screen attribute, (kept in }
- { the variable TextAttr exported by the Crt unit) set TextAttr to }
- { the attribute passed in InColor, write WriteData to the screen, }
- { and finally restore the contents of TextAttr that were in force }
- { when WriteColor took control. }
- {------------------------------------------------------------------}
-
- PROCEDURE WriteColor(InColor : Byte; WriteData : String);
-
- VAR
- SaveAttr : Byte;
-
- BEGIN
- SaveAttr := Crt.TextAttr;
- Crt.TextAttr := InColor;
- Write(WriteData);
- Crt.TextAttr := SaveAttr
- END;
-
-
- {-SaveScreenOut-and-BringScreenBack--------------------------------}
- { }
- { These two routines are inverses of one another. SaveScreenOut }
- { allocates space on the heap and saves the displayed text buffer }
- { into the allocated space. The current cursor position is saved }
- { in the ScreenSaveRec parameter, and the position is reasserted }
- { when the screen is moved back into the video refresh buffer with }
- { BringScreenBack. The number of bytes moved is determined by the }
- { TextBufferSixe variable exported by the TextInfo unit. The }
- { generic pointer TextBufferOrigin is also exported by TextInfo. }
- {------------------------------------------------------------------}
-
- PROCEDURE SaveScreenOut(VAR OutboundScreen : ScreenSaveRec);
-
- BEGIN
- WITH OutboundScreen DO
- BEGIN
- SaveX := WhereX; SaveY := WhereY; { Save the underlying cursor pos. }
- { Allocate memory for stored screen: }
- GetMem(SavePtr,TextBufferSize);
- { Save screen out to the heap: }
- Move(TextBufferOrigin^,SavePtr^,TextBufferSize);
- END
- END;
-
-
- PROCEDURE BringScreenBack(VAR InboundScreen : ScreenSaveRec);
-
- BEGIN
- WITH InboundScreen DO
- BEGIN
- Move(SavePtr^,TextBufferOrigin^,TextBufferSize); { Bring screen back }
- FreeMem(SavePtr,TextBufferSize); { Free up the meap memory }
- SavePtr := NIL;
- GotoXY(SaveX,SaveY); { Put the cursor back where it was }
- END
- END;
-
-
- {-WaitForAnyKeystroke----------------------------------------------}
- { }
- { All this does is print a centered prompt on the last screen line }
- { and wait for a keystroke. }
- {------------------------------------------------------------------}
-
- PROCEDURE WaitForAnyKeystroke;
-
- VAR
- Dummy : Char;
-
- BEGIN
- GotoXY(20,VisibleY); Write('Press any key to return to JED...');
- REPEAT UNTIL KeyPressed; { Wait for a keystroke }
- Dummy := ReadKey; { Go get pressed key }
- IF Dummy = Chr(0) THEN Dummy := ReadKey;
- END;
-
-
- {-GetString--------------------------------------------------------}
- { }
- { Here's your generic field editor. Pass the string to be edited }
- { in XString, the location of the left character of the field in X }
- { and Y, the maximum length allowable in MaxLen, the attribute for }
- { foreground/background colors in UseColor, and nothing in }
- { ESCPressed--that's a return value, indicating that the user hit }
- { the ESC key. XString will be displayed left-justified in the }
- { field, but if the first character pressed is a printable one, }
- { the field will be blanked, allowing for rapid entry of new }
- { strings. Note that if ESC is pressed, XSTRing is not altered. }
- {------------------------------------------------------------------}
-
- PROCEDURE GetString(X,Y : Integer;
- VAR XString : String80;
- MaxLen : Integer;
- UseColor : Byte;
- VAR EscPressed : Boolean);
-
- CONST Dot : Char = '.';
- Printables : SET OF Char = [' '..'~'];
-
- VAR I,J : Integer;
- Ch : Char;
- ClearIt : String80;
- Worker : String80;
- GotChar : Boolean;
- CR : Boolean;
- Virgin : Boolean;
-
- BEGIN
- CR := False; EscPressed := False; Virgin := True;
- FillChar(ClearIt,SizeOf(ClearIt),'.'); { Fill the clear string }
- ClearIt[0] := Chr(MaxLen); { Set clear string to MaxLen }
-
- { Truncate string value to MaxLen: }
- IF Length(XString) > MaxLen THEN XString[0] := Chr(MaxLen);
- GotoXY(X,Y); WriteColor(UseColor,ClearIt); { Draw the field }
- GotoXY(X,Y); WriteColor(UseColor,XString);
- IF Length(XString) < MaxLen THEN
- GotoXY(X + Length(XString),Y);
-
- Worker := XString; { Fill work string with input string }
-
- REPEAT { Until ESC or (CR) entered }
- { Wait here for keypress: }
- REPEAT
- GotChar := True;
- WHILE NOT KeyPressed DO BEGIN {NULL} END;
- Ch := ReadKey;
- IF Ord(CH) = 0 THEN { If an extended keycode was received.. }
- BEGIN
- Ch := Readkey; { ..get the other half of it to ignore it }
- GotChar := False { Set the flag so we loop & get another }
- END
- UNTIL GotChar;
-
- IF Ch IN Printables THEN { If Ch is printable... }
- BEGIN
- IF Virgin THEN { We clear the field if first char is printable }
- BEGIN
- Worker := '';
- GotoXY(X,Y);
- WriteColor(UseColor,Clearit); { Fill the field with dots }
- Virgin := False;
- END;
- IF Length(Worker) >= MaxLen THEN UhUh ELSE { If we're full... }
- BEGIN
- Worker := CONCAT(Worker,Ch); { Append it to the work string }
- GotoXY(X,Y); WriteColor(UseColor,Worker); { and redisplay it }
- IF Length(Worker) >= MaxLen THEN { Keep hardware cursor within }
- GotoXY(X+MaxLen-1,Y); { the field }
- END
- END
- ELSE { If Ch is NOT printable... }
- BEGIN
- Virgin := False;
- CASE Ord(Ch) OF
- 8,127 : IF Length(Worker) <= 0 THEN UhUh ELSE { Backspace & rubout }
- BEGIN
- Delete(Worker,Length(Worker),1);
- GotoXY(X,Y); WriteColor(UseColor,Worker);
- IF Length(Worker) < MaxLen THEN WriteColor(UseColor,Dot);
- GotoXY(X+Length(Worker),Y);
- END;
-
- 13 : CR := True; { Carriage return; keep changes }
-
- 24 : BEGIN { CTRL-X : Blank the field }
- GotoXY(X,Y); WriteColor(UseColor,ClearIt);
- GotoXY(X,Y);
- Worker := ''; { Blank out work string }
- END;
-
- 27 : EscPressed := True; { ESC; abandon changes }
- ELSE UhUh { CASE ELSE; no other legal control chars }
- END; { CASE }
- END
- UNTIL CR OR EscPressed; { Get keypresses until (CR) or }
- { ESC pressed }
-
- IF CR THEN XString := Worker; { Don't update XString if ESC hit }
-
- END; { GetString }
-
-
-
- PROCEDURE WriteStatus(msg : string);
- {-Write a status message}
-
- BEGIN {WriteStatus}
- GoToXY(1, Windy2);
- TextColor(White);
- Write(msg);
- END; {WriteStatus}
-
-
-
- PROCEDURE ShowJEDErrorMessage(ErrX,ErrY : Integer; Message : STRING);
-
- BEGIN
- VidBlast(TextBufferOrigin,@JEDBar, { Blast in the JED error frame }
- VisibleX,VisibleY, { Dimensions of current screen }
- ErrX,ErrY, { Load it at bottom screen line }
- 62,5, { JEDErr is 62 wide and 5 high }
- 07, { Use the normal attribute }
- 0); { No interspersed blank lines }
- GotoXY(ErrX+3,ErrY+2);
- Write(Message);
- END;
-
-
-
- PROCEDURE SaveConfigFile(ConfigData : ConfigRec);
-
- BEGIN
- { Save the last known cursor inset into the edited file: }
- ConfigData.CursorInset := EdData.CursorPos;
- Assign(ConfigStore,ConfigFileName);
- Rewrite(ConfigStore);
- Write(ConfigStore,ConfigData);
- Close(ConfigStore)
- END;
-
-
- {-GetFileName------------------------------------------------------}
- { }
- { This routine is called when JED starts up, and it returns a file }
- { name to load and edit. It first looks on the parameter line for }
- { a file name. If parameters were entered, the configuration file }
- { is opened, and the name of the last file saved will be loaded }
- { and used. If the config file can't be read, NONAME.ASM will be }
- { used as a filename. }
- {------------------------------------------------------------------}
-
- FUNCTION GetFileName(VAR ConfigData : ConfigRec) : STRING;
-
- VAR
- TempConfigData : ConfigRec;
- TempName : String80;
- I : Integer;
-
-
- PROCEDURE ReadConfigFile(VAR ConfigFromDisk : ConfigRec);
-
- BEGIN
- Assign(ConfigStore,ConfigFileName);
- {$I-} Reset(ConfigStore); {$I+}
- { IF JED.CFG can't be read, reassert defaults: }
- IF IOResult <> 0 THEN
- BEGIN
- WITH ConfigData DO
- BEGIN
- WorkFile := 'NONAME.ASM';
- CursorInset := 0;
- AssembleCommand := 'TASM ~';
- LinkCommand := 'TLINK ~';
- TestParms := ''
- END
- END
- ELSE { Read JED.CFG from disk }
- BEGIN
- Read(ConfigStore,ConfigData);
- Close(ConfigStore);
- END
- END;
-
-
-
- BEGIN { GetFileName }
- IF ParamCount > 0 THEN { If there are parms, read #1 as file name }
- BEGIN
- TempName := ParamStr(1); { Save command parm #1 in temp string }
- { Force the name to upper case: }
- TempName := Forcecase(UP,TempName);
- { If the name has no extentions, append the default extension: }
- IF Pos('.',TempName) = 0 THEN
- TempName := TempName + DefaultExtension;
- ReadConfigFile(TempConfigData); { Read JED.CFG from disk }
- { If the workfile name in JED.CFG matches parm #1, use rest of JED.CFG }
- IF TempName = TempConfigData.WorkFile THEN
- Configdata := TempConfigData
- ELSE { Otherwise, reassert defaults for }
- WITH ConfigData DO { config data other than work file }
- BEGIN
- WorkFile := TempName;
- CursorInset := 0;
- AssembleCommand := 'TASM ~';
- LinkCommand := 'TLINK ~';
- TestParms := ''
- END
- END
- ELSE ReadConfigFile(ConfigData); { No parms; use full JED.CFG data }
- GetFileName := ConfigData.WorkFile;
- END;
-
-
- {-RequestFileName--------------------------------------------------}
- { }
- { If the user needs to change files within a JED session, this }
- { routine takes care of prompting for a new file name. If Enter }
- { is pressed after field entry, the name entered in the field is }
- { returned. If ESC is pressed instead, the name in the config }
- { file is returned instead and is usually the file being edited. }
- {------------------------------------------------------------------}
-
- FUNCTION RequestFileName(ConfigInfo : ConfigRec) : String;
-
- CONST
- BoxX = 20;
- BoxY = 5;
-
- VAR
- ESCPressed : Boolean;
- TempName : String80;
-
- BEGIN
- ESCPressed := False;
- SaveScreenOut(JEDScreen); { Save the underlying screen out to the heap }
-
- VidBlast(TextBufferOrigin,@JEDFile, { Blast in the JED change-file box }
- VisibleX,VisibleY, { Dimensions of current screen }
- BoxX,BoxY, { Put it at the passed X,Y values }
- 38,12, { JEDFile is 38 wide and 12 high }
- BarAttribute, { Use an appropriate attribute }
- 0); { No interspersed blank lines }
-
- TempName := ConfigInfo.WorkFile; { Use current file as default }
- GotoXY(BoxX+19,BoxY+4); WriteColor(BlackOnWhite,FName);
- GetString(BoxX+19,BoxY+6,TempName,12,BlackOnWhite,ESCPressed);
- IF ESCPressed THEN { If ESC pressed, keep the name in the config file }
- RequestFileName := ConfigInfo.WorkFile
- ELSE
- RequestFilename := TempName; { Return the new name }
- BringScreenBack(JEDScreen); { Bring back the underlying screen }
- END;
-
-
- FUNCTION GetProg(CommandLine : String80) : String80;
-
- BEGIN
- StripWhite(CommandLine);
- IF Length(CommandLine) > 0 THEN
- GetProg := Copy(CommandLine,1,Pos(' ',CommandLine)-1) + '.EXE'
- ELSE GetProg := '';
- END;
-
-
- FUNCTION GetParms(CommandLine : String80;
- WorkFile : String80) : String80;
-
- VAR
- Dir : DirStr; { These 3 types are defined in the DOS unit... }
- Name : NameStr;
- Ext : ExtStr;
-
- SubPos : Integer;
-
- BEGIN
- FSplit(WorkFile,Dir,Name,Ext);
- StripWhite(CommandLine);
- IF Length(CommandLine) > 0 THEN
- BEGIN
- Delete(CommandLine,1,Pos(' ',CommandLine));
- SubPos := Pos(SUBCHAR,CommandLine);
- IF SubPos = 0 THEN
- CommandLine := Name + ' ' + CommandLine
- ELSE
- BEGIN
- Delete(CommandLine,SubPos,1);
- Insert(Name,CommandLine,SubPos);
- END;
- GetParms := CommandLine
- END
- ELSE
- GetParms := ''
- END;
-
-
-
- FUNCTION EXEForm(WorkFileName : String80) : String80;
-
- BEGIN
- IF Pos('.',WorkFileName) = 0 THEN EXEForm :=
- WorkFileName + '.EXE'
- ELSE
- EXEForm := Copy(WorkFileName,1,Pos('.',WorkFileName)-1) + '.EXE';
- END;
-
-
- {-ShowHelp---------------------------------------------------------}
- { }
- { When the user pressed F1, this routine gets control and blasts a }
- { single-screen help summary into the video refresh buffer. It }
- { will remain on display until any key is pressed. }
- {------------------------------------------------------------------}
-
- PROCEDURE ShowHelp; { Shows a help screen display on press of F1 }
-
- VAR
- Dummy : Char;
-
-
- BEGIN { ShowHelp }
- SaveScreenOut(JEDScreen); { Save the underlying screen out to the heap }
- ClrScr; { Clear what's on the visible screen }
-
- VidBlast(TextBufferOrigin,@JEDHelp, { Blast in the JED help screen }
- VisibleX,VisibleY, { Dimensions of current screen }
- 1,1, { Load it at screen position 1,1 }
- 80,24, { JEDHelp is 80 wide and 24 high }
- BarAttribute, { Use an appropriate text attribute }
- 0); { No interspersed blank lines }
-
- WaitForAnyKeystroke;
- BringScreenBack(JEDScreen); { Bring back the underlying screen }
- END;
-
-
-
- PROCEDURE AssembleAndLink;
-
- BEGIN
- WITH ConfigData DO
- BEGIN
- Exec(GetProg(AssembleCommand),GetParms(AssembleCommand,WorkFile));
- Exec(GetProg(LinkCommand),GetParms(LinkCommand,WorkFile))
- END
- END;
-
-
-
- PROCEDURE CheckInitBinary(ExitCode : Word);
- {-Check the results of the editor load operation}
-
- BEGIN {CheckInitBinary}
- IF ExitCode <> 0 THEN
- BEGIN
- {Couldn't load editor}
- case ExitCode of
- 1 : WriteStatus('Insufficient heap space for text buffer');
- ELSE
- WriteStatus('Unknown load error');
- END;
- GoToXY(1, Windy2);
- Halt(1);
- END;
- END; {CheckInitBinary}
-
-
- {-CheckReadFile----------------------------------------------------}
- { }
- {------------------------------------------------------------------}
-
- PROCEDURE CheckReadFile(ExitCode : Word; Fname : string);
- {-Check the results of the file read}
- VAR
- f : file;
-
- BEGIN {CheckReadFile}
- IF ExitCode <> 0 THEN
- BEGIN
- {Couldn't read file}
- CASE ExitCode of
- 1 : BEGIN
- {New file, assure valid file name}
- {$I-}
- Assign(f, Fname);
- Rewrite(f);
- IF IOResult <> 0 THEN
- BEGIN
- Close(f);
- WriteStatus('Illegal file name '+Fname);
- END
- ELSE
- BEGIN
- Close(f);
- Erase(f);
- GotoXY(1,1);
- ClrEOL;
- Write('New File');
- Delay(2000);
- GoToXY(1,1);
- ClrEol;
- Exit;
- END;
- {$I+}
- END;
- 2 : WriteStatus('Insufficient text buffer size');
- ELSE WriteStatus('Unknown read error');
- END; { CASE }
- GoToXY(1,Windy2);
- Halt(1);
- END;
- GoToXY(1,1);
- ClrEol;
- UpdateConfigData := True
- END; {CheckReadFile}
-
-
- {-CheckSaveFile----------------------------------------------------}
- { }
- { <this routine is not yet complete> }
- {------------------------------------------------------------------}
-
- PROCEDURE CheckSaveFile(ExitCode : Word; Fname : string);
-
- BEGIN
- IF ExitCode <> 0 THEN
- BEGIN
- {Couldn't save file}
- CASE ExitCode of
- 1 : WriteStatus('Unable to create output file '+Fname);
- 2 : WriteStatus('Error while writing output to '+Fname);
- 3 : WriteStatus('Unable to close output file '+Fname);
- ELSE WriteStatus('Unknown write error');
- END; { CASE }
- GoToXY(1,Windy2);
- Halt(1);
- END
- ELSE UpdateConfigData := True;
- END;
-
-
- {-MustMake---------------------------------------------------------}
- { If this function returns True, the .EXE file is out of date and }
- { must be re-MADE. The decision is based on a comparison of the }
- { source file time stamp to the .EXE file time stamp. }
- {------------------------------------------------------------------}
-
- FUNCTION MustMake(CurrentFile : String80) : Boolean;
-
- VAR
- TimeText,TimeEXE : LongInt; { Time stamps for source & .EXE files }
- Target : File; { Untyped file allows opening files }
- IO : Integer;
-
- BEGIN
- Assign(Target,EXEForm(CurrentFile));
- {$I-} Reset(Target); {$I+}
- IO := IOResult;
- IF IO <> 0 THEN MustMake := True
- ELSE
- BEGIN
- GetFTime(Target,TimeEXE); { Get time stamp of .EXE file }
- Close(Target);
- IF Pos('.',CurrentFile) = 0 THEN
- CurrentFile := CurrentFile + DefaultExtension;
- Assign(Target,CurrentFile);
- {$I-} Reset(Target); {$I+}
- IO := IOREsult;
- IF IO <> 0 THEN MustMake := True
- ELSE
- BEGIN
- GetFTime(Target,TimeText); { Get time stamp of source file }
- Close(Target);
- IF TimeText > TimeEXE THEN MustMake := True
- ELSE MustMake := False
- END
- END
- END;
-
-
- {-Clocker----------------------------------------------------------}
- { }
- { This proc acts as an event handler for BINED's user event }
- { dispatcher. Whenever it isn't busy doing something else, BINED }
- { passes control out to an address placed in the editor control }
- { block by the InitWindow proc. The proc must be FAR and it ought }
- { to be pretty quick about doing whatever it does. Here, all we }
- { want to do is display the time in the upper right corner of the }
- { screen, within the BINED status line. }
- {------------------------------------------------------------------}
-
- {$F+} { All User-Event procedures must be FAR calls!}
- PROCEDURE Clocker(EventNo,Info : Integer);
-
- VAR
- Hours,Minutes,Seconds,Hundredths : word;
- TimeBuf,TimeTemp : String;
-
- BEGIN
- GetTime(Hours,Minutes,Seconds,Hundredths);
- Str(Hours:2,TimeBuf);
- Str(Minutes:2,TimeTemp);
- IF TimeTemp[1] = ' ' THEN TimeTemp[1] := '0';
- TimeBuf := TimeBuf+':'+TimeTemp;
- Str(Seconds:2,TimeTemp);
- IF TimeTemp[1] = ' ' THEN TimeTemp[1] := '0';
- TimeBuf := TimeBuf+':'+TimeTemp;
- CRTPutFast(71,1,TimeBuf);
- IF (EdData.Status AND EdStatTextMod) <> 0 THEN
- CRTPutFast(38,1,'*')
- ELSE
- CRTPutFast(38,1,' ');
- END;
- {$F-}
-
-
- {-InitWindow-------------------------------------------------------}
- { }
- { We're not playing with windows here, but since BINED can be run }
- { as a self-windowing editor, the jargon speaks of windows that }
- { simply aren't used in JED. This proc sets up an editor control }
- { block to receive a new file. It does NOT read any file into }
- { memory. It doesn't even know the name of the file to come, only }
- { that one must be prepared for. }
- {------------------------------------------------------------------}
- FUNCTION InitWindow : Boolean;
-
- BEGIN
- {Initialize a window for the file}
- ExitCode :=
- InitBinaryEditor(
- EdData, { Editor control block }
- MaxFileSize, { Size of data area to reserve for}
- { binary editor text buffer, $FFE0 max}
- Windx1, { X of upper left corner; 1..80}
- Windy1, { Y of upper left corner}
- VisibleX, { X of lower right corner}
- VisibleY-1, { Y of lower right corner}
- True, { True = wait for retrace on CGA cards}
- EdOptInsert+EdOptIndent, { Initial editor toggles}
- DefaultExtension, { Default extension for file names}
- ExitCommands, { Commands which will exit the editor}
- Addr(Clocker)); { Add a clock in the corner}
- CheckInitBinary(ExitCode);
- IF ExitCode = 0 THEN InitWindow := True
- ELSE InitWindow := False;
- END;
-
-
- {-ReadIntoWindow---------------------------------------------------}
- { }
- { This proc reads the actual workfile into memory and resets the }
- { control block to reflect the new file. }
- {------------------------------------------------------------------}
-
- FUNCTION ReadIntoWindow : Boolean;
-
- BEGIN
- { Read the file into memory: }
- ExitCode := ReadFileBinaryEditor(EdData, Fname);
- CheckReadFile(ExitCode,FileNameBinaryEditor(EdData));
- IF ExitCode = 0 THEN
- BEGIN
- ReadIntoWindow := True;
- { Reset the editor for the new file: }
- ResetBinaryEditor(EdData);
- END
- ELSE ReadIntoWindow := False;
- END;
-
-
- {-FileNameIsValid--------------------------------------------------}
- { }
- { All this does is filter out some of the more blatant ways to }
- { enter a bad filename. Strings with 0 length are passed along as }
- { acceptable, since a zero-length string tells JED to exit to DOS. }
- {------------------------------------------------------------------}
-
- FUNCTION FileNameIsValid(TempName : String) : Boolean;
-
- VAR
- TestFile : FILE;
- I : Integer;
-
- BEGIN
- FileNameIsValid := True;
- IF Length(TempName) < 0 THEN
- FilenameIsValid := False
- ELSE
- IF Length(TempName) > 0 THEN
- IF Pos('.',TempName) > 9 THEN
- FileNameIsValid := False
- ELSE
- BEGIN
- Assign(TestFile,TempName);
- {$I-} Reset(TestFile); {$I+}
- I := IOResult;
- CASE I OF
- 0 : Close(TestFile);
- 2 : FileNameIsValid := True;
- ELSE FileNameIsValid := False;
- END; { CASE }
-
- END;
- END;
-
-
-
- FUNCTION ExecWasSuccessful(ProgName,Parameters : STRING) : Boolean;
-
- VAR
- ExecError : Integer;
-
- BEGIN
- ExecError := DOSError;
- IF ExecError <> 0 THEN
- BEGIN
-
- END
- END;
-
-
-
-
- {-ExitBinaryEditor-------------------------------------------------}
- { }
- { This is most important subprogram in the whole system. When one }
- { of a predefined set of "exit commands" is encountered in the }
- { BINED edit stream, BINED lets control return to the caller, with }
- { the editor context retained in a largish dfata structure called }
- { EdData. As long as EdData isn't corrupted, BINED can be re- }
- { entered as though control had never left it. }
- { }
- { During these excursions out of BINED, nearly anything can be }
- { done under the illusion that BINED still has control. On exit, }
- { BINED supplies a code indicating which character sequence caused }
- { the exit. This code can be parsed, and action taken depending }
- { on the exit code. Each JED command is in fact an exit command, }
- { and everything that JED does apart from pure text editing and }
- { changing edit files is done from subprograms called from within }
- { ExitBinaryEditor. }
- {------------------------------------------------------------------}
-
- FUNCTION ExitBinaryEditor(VAR EdData : EdCB;
- ExitCommand : Integer;
- VAR Quit : Boolean) : Boolean;
-
- VAR
- ExitCode : Word;
- FindFile : SearchRec;
- LineLength : Integer;
- Escape : Boolean;
- TempName : String;
-
-
- FUNCTION YesAnswer(prompt : string) : Boolean;
- {-Return true for a yes answer to the prompt}
- VAR
- ch : Char;
-
- BEGIN {YesAnswer}
- WriteStatus(prompt);
- REPEAT
- Ch := UpCase(readkey);
- UNTIL ch in ['Y', 'N'];
- Write(ch);
- YesAnswer := (ch = 'Y');
- END; {YesAnswer}
-
-
- PROCEDURE SaveCurrentFile;
-
- BEGIN
- CRTPutFast(58,1,'Saving...');
- ExitCode := SaveFileBinaryEditor(EdData, MakeBackup);
- CheckSaveFile(ExitCode, FileNameBinaryEditor(EdData));
- CRTPutFast(58,1,' ');
- END;
-
-
-
- BEGIN {ExitBinaryEditor}
- CASE ExitCommand OF
- -1 : BEGIN { ^K^D: Exit & Save file}
- SaveCurrentFile;
- ExitBinaryEditor := True;
- GoToXY(1,VisibleY);
- END;
-
- 0 : BEGIN { ^K^Q: Exit without saving }
- IF ModifiedFileBinaryEditor(EdData) THEN
- IF YesAnswer('File modified. Save it? (Y/N) ') THEN
- SaveCurrentFile;
- ExitBinaryEditor := True;
- GoToXY(1,VisibleY);
- END;
-
- 1 : BEGIN { Alt-F: Change current work file }
- SaveCurrentFile; { Save file's data }
- ConfigData.CursorInset := EdData.CursorPos;
- { The work is done outside the main command loop... }
- ExitBinaryEditor := True
- END;
-
- 2 : BEGIN { Alt-X: Save if necessary and exit }
- IF ModifiedFileBinaryEditor(EdData) THEN
- SaveCurrentFile;
- ExitBinaryEditor := True;
- GotoXY(1,VisibleY);
- END;
-
- 3 : BEGIN { F1: Show help screen }
- ShowHelp;
- ExitBinaryEditor := False
- END;
-
- 4 : BEGIN { F2: Save File }
- SaveCurrentFile;
- ExitBinaryEditor := False
- END;
-
- 5 : BEGIN { F3: Invoke DEBUG on current .EXE file }
- IF ModifiedFileBinaryEditor(EdData) THEN
- SaveCurrentFile; { If modified, save before EXECing! }
- SaveScreenOut(JEDScreen); { Save out JED screen to heap }
- ClrScr; { Clear the screen }
- WITH ConfigData DO { Exec to DEBUG with current .EXE }
- Exec('DEBUG.COM',EXEForm(WorkFile));
- SaveScreenOut(ExecScreen); { Save last screen results }
- WaitForAnyKeystroke; { Wait for a key press }
- BringScreenBack(JEDScreen); { Bring JED screen back from heap }
- ExitBinaryEditor := False { And duck back into BINED }
- END;
-
-
- 6 : BEGIN { F4: Update assemble/link command lines }
- SaveScreenOut(JEDScreen);
- ClrScr;
- GotoXY(17,1);
- Write('\\JED\\ Assemble/link command edit screen');
- GotoXY(30,5); Write('Assemble command:');
- GotoXY(32,9); Write('Link command:');
- GotoXY(1,13); Writeln('Line editing commands:'); Writeln;
- Writeln('CR: Accepts changes and continues');
- Writeln('ESC: Abandons changes and continues');
- Writeln('Ctrl-X: Clears entire field to empty string');
- Writeln('BS: Destructive backspace');
-
- WITH ConfigData DO
- BEGIN
- GotoXY(1,6); Write(AssembleCommand);
- GotoXY(1,10);Write(LinkCommand);
- GetString(1,6,AssembleCommand,80,BlackOnWhite,Escape);
- GetString(1,10,LinkCommand,80,BlackOnWhite,Escape);
- END;
- BringScreenBack(JEDScreen);
- ExitBinaryEditor := False;
- END;
-
- 7 : BEGIN { F5: Exec to DOS command line }
- IF ModifiedFileBinaryEditor(EdData) THEN
- SaveCurrentFile; { If modified, save before EXECing! }
- SaveScreenOut(JEDScreen); { Save out JED screen to heap }
- ClrScr; { Clear the screen }
- Exec(GetEnv('COMSPEC'),''); { Execute the DOS command processor }
- BringScreenBack(JEDScreen); { Bring JED screen back from heap }
- ExitBinaryEditor := False { And duck back into BINED }
- END;
-
- 8 : BEGIN { F6: Examine last Exec screen }
- SaveScreenOut(JEDScreen);
- ClrScr;
- IF ExecScreen.SavePtr <> NIL THEN
- BEGIN
- BringScreenBack(ExecScreen);
- SaveScreenOut(ExecScreen);
- END
- ELSE { NIL SavePtr means no Exec screen has been saved yet }
- BEGIN
- GotoXY(12,11);
- Writeln('No assemble/link display has been generated yet.');
- GotoXY(12,12);
- Writeln('Until you assemble or link by pressing F9 or F10,');
- GotoXY(12,13);
- Writeln('Nothing will be displayable by pressing F6.');
- END;
- WaitForAnyKeystroke;
- BringScreenBack(JEDScreen);
- ExitBinaryEditor := False
- END;
-
-
- 9 : BEGIN { F9: Assemble only }
- IF ModifiedFileBinaryEditor(EdData) THEN
- SaveCurrentFile; { If modified, save before EXECing! }
- SaveScreenOut(JEDScreen); { Save out JED screen to heap }
- ClrScr; { Clear the screen }
- WITH ConfigData DO { Exec to the assembler }
- BEGIN
- Exec(GetProg(AssembleCommand),
- GetParms(AssembleCommand,WorkFile));
- IF ExecWasSuccessful(GetProg(AssembleCommand),
- GetParms(AssembleCommand,WorkFile))
- THEN SaveScreenOut(ExecScreen); { Save assembler results }
- END;
- WaitForAnyKeystroke; { Wait for a key press }
- BringScreenBack(JEDScreen); { Bring JED screen back from heap }
- ExitBinaryEditor := False { And duck back into BINED }
- END;
-
- 10: BEGIN { F10: MAKE: Assemble & link (if necessary), and GO }
- IF ModifiedFileBinaryEditor(EdData) THEN
- SaveCurrentFile; { In case we EXEC something ugly }
- SaveScreenOut(JEDScreen); { Save out JED screen to heap }
- ClrScr;
- { If the workfile has been changed since the last Make, }
- { *OR* if the .EXE file does not exist on disk, reMake: }
- IF MustMake(ConfigData.WorkFile) THEN
- BEGIN
- AssembleAndLink;
- SaveScreenOut(ExecScreen); { Save assemble/link results }
- Exec(EXEForm(ConfigData.WorkFile),ConfigData.TestParms);
- END
- ELSE
- BEGIN
- { If it exists, we run it--if not, reMake and run it: }
- Exec(EXEForm(ConfigData.WorkFile),ConfigData.TestParms);
- IF DOSError <> 0 THEN
- BEGIN
- AssembleAndLink;
- SaveScreenOut(ExecScreen); { Save assemble/link results }
- Exec(EXEForm(ConfigData.WorkFile),ConfigData.TestParms);
- END
- END;
-
- WaitForAnyKeystroke;
- BringScreenBack(JEDScreen);
- ExitBinaryEditor := False { And duck back into BINED }
- END;
-
-
- END; { CASE }
- END; { ExitBinaryEditor }
-
-
-
- {------------------------------------------------------------------}
- { JED }
- { MAIN PROGRAM BLOCK }
- { }
- {------------------------------------------------------------------}
-
- BEGIN
- { The Monochrome Boolean variable is exported by unit TextInfo }
- { It determines the attribute for the prompt bar: }
- IF Monochrome THEN BarAttribute := $70 { Inverse video }
- ELSE BarAttribute := $1E; { Yellow on blue }
-
- DOSScreen.SavePtr := NIL; { Make sure all screen pointers are NIL }
- JEDScreen.SavePtr := NIL;
- ExecScreen.SavePtr := NIL;
-
- {Begin by saving the current DOS screen onto the heap, }
- { so that we can restore the screen upon exiting JED. }
- SaveScreenOut(DOSScreen);
- ClrScr;
-
- Fname := GetFileName(ConfigData); { Get a file name }
- UpdateConfigData := False; { Don't update until we know }
- { the file is good }
-
- {------------------------------------------------------------------}
- { This is the edit loop; it repeats until the user quits to DOS }
- { with Alt-X, Ctrl-KD, or Ctrl-KQ. On each pass through the loop }
- { a different text file is loaded and edited. The name is gotten }
- { from the user via the IF block on the other side of the main }
- { command loop; control then loops back here and the file is }
- { opened for a new edit. }
- {------------------------------------------------------------------}
-
- REPEAT { Given a name in FName, This loop loads & edits a file }
- Quit := False; { When this becomes True, we exit to DOS }
- ExitCommand := 0; { Exit command 0 = quit without saving }
-
- IF InitWindow THEN
- BEGIN
- { Read the file into memory: }
- ExitCode := ReadFileBinaryEditor(EdData, Fname);
- CheckReadFile(ExitCode, FileNameBinaryEditor(EdData));
- { Reset the editor for the new file: }
- ResetBinaryEditor(EdData);
- { Bined allows us to position the cursor by specifying a byte }
- { offset into the text file. We can "remember" this offset & }
- { set the cursor to it before editing: }
- EdData.CursorPos := ConfigData.CursorInset;
- END
- ELSE
- BEGIN
- ShowJEDErrorMessage(5,20,'Not enough heap space to load a file...');
- WaitForAnyKeystroke;
- Quit := True;
- END;
-
- VidBlast(TextBufferOrigin,@JEDBar, { Blast in the JED status line }
- VisibleX,VisibleY, { Dimensions of current screen }
- 1,VisibleY, { Load it at bottom screen line }
- 80,1, { JEDBar is 80 wide and 1 high }
- $1E, { Use the yellow on blue attribute }
- 0); { No interspersed blank lines }
-
-
- {------------------------------------------------------------------}
- { This is the main command loop; within this loop a single file }
- { is edited. }
- {------------------------------------------------------------------}
-
- WHILE NOT Quit DO
- BEGIN
- ExitCommand :=
- UseBinaryEditor(
- EdData, { Editor control block for this window }
- ''); { No startup commands passed to editor }
- Quit := ExitBinaryEditor(EdData,ExitCommand,Quit); { Parse commands }
- END;
-
- {------------------------------------------------------------------}
- { End main command loop }
- {------------------------------------------------------------------}
-
- { We've finished with the file being edited in the loop above; now }
- { release the heap space used by the editor text buffer and data }
- { structure: }
- ReleaseBinaryEditorHeap(EdData);
-
- {------------------------------------------------------------------}
- { This IF statement handles changing of the current work file. }
- { By this point the old file has been disposed from the heap and }
- { a new file needs to be identified and opened. The file is only }
- { *identified* here; the file is opened and loaded with the same }
- { code that does it to the original file loaded when JED first }
- { begins executing
- {------------------------------------------------------------------}
-
- IF ExitCommand = 1 THEN { IF Alt-F was pressed, change file }
- BEGIN
- { Prompt the user for a new file name: }
- REPEAT
- TempName := RequestFileName(ConfigData);
- IF Pos('.',TempName) = 0 THEN
- TempName := TempName + DefaultExtension;
- UNTIL FileNameIsValid(TempName); { Make sure it's a valid name }
- IF Length(TempName) <= 0 THEN Quit := True { Quit to DOS }
- ELSE
- BEGIN { Otherwise, assert new filename }
- FName := ForceCase(UP,TempName);
- IF FName <> Configdata.WorkFile THEN { If same file, skip update }
- WITH ConfigData DO { Otherwise, update configuration record }
- BEGIN
- WorkFile := FName;
- CursorInset := 0;
- END;
- Quit := False { And loop back & work on new file }
- END
- END;
-
- UNTIL Quit; { When we hit this point and Quit is True, it's back to DOS }
-
- { Save the updated configuration data to disk: }
- IF UpdateConfigData THEN SaveConfigFile(ConfigData);
-
- { Finally, we restore the DOS screen saved on the heap before we began: }
- BringScreenBack(DOSScreen)
-
- END.