www.pudn.com > JED10asm.rar > JED.PAS, change:1993-02-26,size:46626b


{--------------------------------------------------------------} 
{                           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.