home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / assemblr / asm / tchasm / tchasm.pas < prev    next >
Pascal/Delphi Source File  |  1986-01-25  |  45KB  |  1,847 lines

  1. {$C-,I-,K-}
  2. Program TChasm;
  3.  
  4. {**** Here are the hooks into the procedures for the Editor Toolbox ****}
  5.  
  6. {$I VARS.ED }    { Toolbox global variables and data structure definitions }
  7.  
  8. procedure UserCommand(var ch : byte);
  9. { user command processor hook }
  10. begin
  11. end;
  12.  
  13. procedure UserError(var Msgno : byte);
  14. { user error handler hook }
  15. begin
  16. end;
  17.  
  18. procedure userStatusline(Var TWindow:Byte;
  19.                          Column,line:Integer;
  20.                          Insertflag:Insflag;
  21.                          WW,AI: boolean);
  22. { user status line handler }
  23. begin
  24. end;
  25.  
  26. procedure UserReplace(var ch : byte);
  27. { user replace handler hook }
  28. begin
  29. end;
  30.  
  31. procedure UserTask;
  32. { user multi-tasking hook }
  33. begin
  34. end;
  35.  
  36. {$I USER.ED      }  { Editor kernel and primitive level helper routines }
  37. {$I SCREEN.ED    }  { Screen updating routines                          }
  38.  
  39. {$I INIT.ED      }  { initialization code      }
  40. {$I KCMD.ED      }  { Ctrl-K routines          }
  41. {$I QCMD.ED      }  { Ctrl-Q routines          }
  42. {$I CMD.ED       }  { general editing commands }
  43.  
  44. {$I K.ED         }  { Ctrl-K dispatcher and interface             }
  45. {$I Q.ED         }  { Ctrl-Q dispatcher and interface             }
  46. {$I DISP.ED      }  { General command dispatcher                  }
  47. {$I TASK.ED      }  { Scheduling subsystem and central dispatcher }
  48. {$I INPUT.ED     }  { Input routines                              }
  49.  
  50. Function Exist(FileN: AnyString): boolean; {Checks to see if file exists }
  51. var F: file;
  52. begin
  53.    {$I-}
  54.    assign(F,FileN);
  55.    reset(F);
  56.    {$I+}
  57.    if IOResult<>0 then Exist:=false
  58.    else
  59.    begin
  60.      Exist:=true;
  61.      close(F);
  62.    end
  63. end;
  64.  
  65. Procedure LowVideo;
  66.    begin
  67.      TextColor(BlockColor);
  68.    end;
  69. Procedure NormVideo;
  70.    begin
  71.      TextColor(TxtColor);
  72.    end;
  73.  
  74. Procedure GetLine;  {Gets next line of Source from either memory or from
  75. disk, depending on SourceLoc which may be the disk if there wasn't enough
  76. memory for it in the editor.  I think this would be the place to install
  77. the hooks for a macro processor }
  78. var
  79.   L : integer;
  80.  
  81. Begin {GetLine }
  82.  
  83.   Case SourceLoc of
  84.     Disk : If NOT EOF(SourceFile) then
  85.              Readln(SourceFile,InpLine)
  86.            Else
  87.              EndOfSource := true;
  88.  
  89.     Memory : With Curwin^ Do
  90.                If CurLine <> NIL then
  91.                  begin
  92.                    L := CurLine^.BuffLen;                {Set Length of line }
  93.                    Move(CurLine^.Txt^[1],InpLine[1],L);  {and trim its end }
  94.                    While (L > 0) AND (InpLine[L] = ' ') Do L := Pred(L);
  95.                    InpLine[0] := Chr(L);
  96.                    CurLine := CurLine^.FwdLink;          {move forward for }
  97.                  end                                     {next line }
  98.                Else
  99.                  EndOfSource := true;
  100.  
  101.   End; {Case SourceLoc }
  102.  
  103.   LineNum := Succ(LineNum); {set various assembler vars }
  104.   NeedOffset := NONE;
  105.   DSFlag := false;
  106.   ObjLen := 0;
  107.  
  108. End;  {GetLine }
  109.  
  110. {***** NextWord returns tokens or words separated by delimiters that    *****
  111.        separate them, such as commas or spaces, sent as [' ',','], etc.
  112.        starting at StartPos.  Is included only for reading Tchasm.Dat }
  113.  
  114. Procedure NextWord(Line : AnyString; var Word : AnyString;
  115.                    ParsePos : byte ; DelimSet : SetOfChar);
  116. var
  117.   StartPos : byte;
  118.  
  119. Begin {NextWord }
  120.  
  121.   {Skip any leading characters that aren't wanted }
  122.   While (Line[ParsePos] in DelimSet) AND (ParsePos < Length(Line)) Do
  123.     ParsePos := Succ(ParsePos);
  124.   StartPos := ParsePos;
  125.  
  126.   While NOT (Line[ParsePos] in DelimSet) AND (ParsePos <= Length(Line)) DO
  127.     ParsePos := Succ(ParsePos); {move one past token }
  128.  
  129.   Word := Copy(Line,StartPos,ParsePos-StartPos);
  130.       ParsePos := Succ(ParsePos);
  131.  
  132. End;  {NextWord }
  133.  
  134. Procedure ErrorMessage(ErrMsg : AnyString); {Assembler error messages }
  135.  
  136. Begin
  137.   If ListLoc <> NoIO then
  138.     Writeln(ListFile,'** Error: ',ErrMsg,' **   ',LineNum);
  139.   Errs := Succ(Errs);
  140. End;
  141.  
  142. Procedure DiagMessage(DiagMsg : AnyString); {Assembler Diagnostics }
  143.  
  144. Begin
  145.   If ListLoc <> NoIO then
  146.     Writeln(ListFile,'** Diagnostic: ',DiagMsg,' **  ',LineNum);
  147.   Diag := Succ(Diag);
  148. End;
  149.  
  150. Function Hex(Num : integer) : AnyString; {same as ConvertBase, but takes
  151.                                           number rather than string }
  152. CONST
  153.   R1 = 10; {base to convert from }
  154.   R2 = 16; {base to convert to   }
  155.  
  156. var
  157.   V,V2,T  : Real;
  158.   C       : byte;
  159.   TempHex : AnyString;
  160.  
  161. Begin {Hex }
  162.  
  163.   Str(Num,TempHex);
  164.   T := 0;
  165.   For C := 1 to Length(TempHex) Do
  166.     begin
  167.       V := Ord(TempHex[C]);
  168.       If (V > 47) AND (V < 58) then V2 := V - 48;
  169.       If (V > 64) AND (V < 91) then V2 := V - 55;
  170.       If (V > 96) AND (V < 123) then V2 := V - 85;
  171.       T := T * R1 + V2;
  172.     end;
  173.  
  174.   TempHex := ''; {Don't need it anymore }
  175.   While T <> 0 Do
  176.     begin
  177.       V2 := T - Trunc(T/R2)*R2;
  178.       T := (T - V2)/R2;
  179.       If V2 < 10 then V := V2 + 48;
  180.       If V2 > 9  then V := V2 + 55;
  181.       TempHex := Chr(Round(V)) + TempHex;
  182.     end;
  183.  
  184.   If Length(TempHex) = 0 then TempHex := '0';
  185.   Hex := TempHex;
  186.  
  187. End;  {Hex }
  188.  
  189. Function ConvertBase(Num : AnyString; FrBase,ToBase : NumTypes) : AnyString;
  190. {Converts numbers from FrBase to ToBase }
  191. var
  192.   V,V2,T  : Real;
  193.   C,R1,R2 : byte;      {R2 is base to convert to }
  194.  
  195. Begin {ConvertBase }
  196.  
  197.   Case FrBase of
  198.     Hexadecimal : R1 := 16;
  199.     BaseTen     : R1 := 10;
  200.     Binary      : R1 := 2;
  201.   End; {Case }
  202.  
  203.   Case ToBase of
  204.     Hexadecimal : R2 := 16;
  205.     BaseTen     : R2 := 10;
  206.     Binary      : R2 := 2;
  207.   End; {Case }
  208.  
  209.   T := 0;
  210.   For C := 1 to Length(Num) Do
  211.     begin
  212.       V := Ord(Num[C]);
  213.       If (V > 47) AND (V < 58) then V2 := V - 48;
  214.       If (V > 64) AND (V < 91) then V2 := V - 55;
  215.       If (V > 96) AND (V < 123) then V2 := V - 85;
  216.       T := T * R1 + V2;
  217.     end;
  218.  
  219.   Num := ''; {Don't need it anymore }
  220.   While T <> 0 Do
  221.     begin
  222.       V2 := T - Trunc(T/R2)*R2;
  223.       T := (T - V2)/R2;
  224.       If V2 < 10 then V := V2 + 48;
  225.       If V2 > 9  then V := V2 + 55;
  226.       Num := Chr(Round(V)) + Num;
  227.     end;
  228.  
  229.   If Length(Num) = 0 then Num := '0';
  230.   ConvertBase := Num;
  231.  
  232. End;  {ConvertBase }
  233.  
  234. Function Caps(CapStr : AnyString) : AnyString; {Returns a string with
  235.   all characters, except those within quotes, converted to uppercase }
  236.  
  237. var
  238.   Quoted : boolean;
  239.   i      : integer;
  240.  
  241. Begin {Caps }
  242.  
  243.   Quoted := false;
  244.  
  245.   for i := 1 to Length(CapStr) do
  246.     begin
  247.       if CapStr[i] = Quote then Quoted := NOT Quoted;
  248.       if NOT Quoted then CapStr[i] := UpCase(CapStr[i]);
  249.     end;
  250.   Caps := CapStr;
  251.  
  252. End;  {Caps }
  253.  
  254. Procedure GetField; {Starting at LinePtr, trys to return next field in FldStr
  255.                      sets Found if successful. (similar to NextWord) }
  256. var
  257.   QuotedString : boolean;
  258.  
  259. Begin {GetField }
  260.  
  261.   While (LinePtr <= EndPtr) AND (InpLine[LinePtr] in [' ',',']) Do
  262.     LinePtr := Succ(LinePtr); {strip unwanted chars }
  263.  
  264.   If LinePtr > EndPtr then
  265.     begin
  266.       Found := false;
  267.       EXIT;
  268.     end;
  269.  
  270.   If InpLine[LinePtr] = Quote then  {Strings enclosed in quotes }
  271.     begin
  272.       Delete(InpLine,LinePtr,1);
  273.       StrgEnd := Pos(Quote,InpLine);
  274.       If StrgEnd <> 0 then StrgEnd := Succ(StrgEnd);
  275.       Insert(Quote,InpLine,LinePtr);
  276.       If StrgEnd > 0 then LinePtr2 := Succ(StrgEnd);
  277.       QuotedString := true;
  278.     end;
  279.  
  280.   If NOT QuotedString then
  281.     begin
  282.       LinePtr2 := LinePtr;
  283.       While (LinePtr2 <= EndPtr) AND NOT (InpLine[LinePtr2] in [' ',',']) Do
  284.         LinePtr2 := Succ(LinePtr2);
  285.     end;
  286.  
  287.   FldStr := Copy(InpLine,LinePtr,LinePtr2 - LinePtr);
  288.  
  289.   LinePtr := LinePtr2;
  290.   Found   := true;
  291.  
  292. End;  {GetField }
  293.  
  294. Procedure ParseLine; {Parses InpLine for Label, OpStr, SourceStr, DestStr }
  295.  
  296. Begin {ParseLine }
  297.  
  298.   LinePtr   := 1;
  299.   LinePtr2  := 1;
  300.   LabelStr  := '';
  301.   OpStr     := '';
  302.   SourceStr := '';
  303.   DestStr   := '';
  304.  
  305.   EndPtr    := Pos(';',InpLine) - 1;     {Ignore comment after ";" }
  306.   If EndPtr =  -1 then EndPtr := Length(InpLine);
  307.   If EndPtr =  0 then EXIT; {No source code on line }
  308.  
  309.   InpLine := Caps(InpLine); {Convert to all CAPS, except quoted strings }
  310.  
  311.   {Label? }
  312.   If InpLine[1] <> ' ' then
  313.     begin
  314.       GetField;
  315.       LabelStr := Copy(FldStr,1,25);
  316.       If LabelStr[Length(LabelStr)] = ':' then
  317.         Delete(LabelStr,Length(LabelStr),1);
  318.     end;
  319.  
  320.   {OpCode? }
  321.   GetField;
  322.   If NOT Found then EXIT;
  323.   OpStr := FldStr;
  324.  
  325.   {Save Ptr to start of operands }
  326.   OpdPtr := LinePtr;
  327.  
  328.   {Destination operand, if any }
  329.   GetField;
  330.   If NOT Found then EXIT;
  331.   DestStr := FldStr;
  332.  
  333.   {Source operand, if any }
  334.   GetField;
  335.   If Found then SourceStr := FldStr;
  336.  
  337. End;  {ParseLine }
  338.  
  339. Procedure OperandLookup(OLSym : AnyString); {Look up OLSym in SymTable }
  340.  
  341. Begin {OperandLookup, really a Symbol Lookup, but...}
  342.  
  343.   TablePtr := 1;
  344.   While (SymTable[TablePtr].Symbol <> OLSym) AND (TablePtr < NumSym) Do
  345.     TablePtr := Succ(TablePtr);
  346.  
  347.   If SymTable[TablePtr].Symbol = OLSym then Found := true
  348.   Else Found := false;
  349.  
  350. End;  {OperandLookup }
  351.  
  352. Procedure LookupOp; {Search for OpCode }
  353.  
  354. var
  355.   Move  : Real;
  356.   Start : integer;
  357.  
  358. Begin {LookupOp }
  359.  
  360. {Use binary search to speed up process }
  361.   Move := NumOp;
  362.   Start := Round(Move/2);
  363.  
  364.   While Move >= 2 Do
  365.     begin
  366.       Move := Move/2;
  367.       If OpStr > OpCodes[Start].Mnemonic Then Start := Start + Round(Move)
  368.       Else Start := Start - Round(Move);
  369.       If Start < 1 then Start := 1;
  370.       If Start > NumOp then Start := NumOp;
  371.     end;
  372.  
  373.   OpPtr := Start;
  374.   Found := false;
  375.   While (OpPtr <= NumOp) AND NOT Found Do
  376.     With OpCodes[OpPtr] Do
  377.       begin
  378.         If Mnemonic > OpStr then Found := true; {Not really, but... }
  379.         If Mnemonic = OpStr then
  380.           If SrcType AND SType <> 0 then
  381.             If DstType AND DType <> 0 then Found := true;
  382.         If NOT Found then OpPtr := Succ(OpPtr);
  383.       end;
  384.  
  385.   If OpCodes[OpPtr].Mnemonic <> OpStr then Found := false;{Fix earlier mistake}
  386.  
  387. End;  {LookupOp }
  388.  
  389. Procedure NewEntry(NewSymbol : AnyString); {Add a symbol to SymTable }
  390.  
  391. Begin {NewEntry }
  392.  
  393.   {Already in table? }
  394.   OperandLookup(NewSymbol);
  395.   If Found then
  396.     begin
  397.       ErrorMessage('Dup definition of '+NewSymbol);
  398.       EXIT;
  399.     end;
  400.  
  401.   {Too many labels? }
  402.   If NumSym >= MAXSYM then
  403.     begin
  404.       ErrorMessage('Too many user symbols');
  405.       EXIT;
  406.     end;
  407.  
  408.   {Make new entry }
  409.   NumSym := Succ(NumSym);
  410.   With SymTable[NumSym] Do
  411.     begin
  412.       Symbol  := NewSymbol;
  413.       Val1    := Loctr;
  414.       SymType := NEAR;
  415.     end;
  416.  
  417. End;  {NewEntry }
  418.  
  419. Procedure TestNumber(TNStr : AnyString); {Trys to interpret TNStr as a
  420.                                number; may be in base ten, hex, or binary }
  421. var
  422.   ValError : integer;
  423.  
  424. Begin {TestNumber }
  425.  
  426.   Found := false;
  427.  
  428.   {Hex? }
  429.   If TnStr[Length(TnStr)] = 'H' then
  430.     begin
  431.       Delete(TnStr,Length(TnStr),1);
  432.       Val('$'+TnStr,NumVal,ValError);
  433.     end
  434.   Else
  435.   {Binary?}
  436.   If TnStr[Length(TnStr)] = 'B' then
  437.     begin
  438.       Delete(TnStr,Length(TnStr),1);
  439.       TnStr := ConvertBase(TnStr,Binary,BaseTen);
  440.       Val(TnStr,NumVal,ValError);
  441.     end
  442.   Else
  443.   {Decimal?}
  444.     Val(TnStr,NumVal,ValError);
  445.  
  446.   If ValError = 0 then
  447.         begin
  448.           Found := true;
  449.           If Length(Hex(NumVal)) < 3 then NumType := IMMED16 OR IMMED8
  450.           Else NumType := IMMED16;
  451.         end
  452.  
  453. End;  {TestNumber }
  454.  
  455. Procedure MemRef(DataType : integer); {Builds memory address word }
  456.  
  457. Begin {MemRef }
  458.  
  459.   If DataType = MEMY then DataType := DVal1 Else DataType := Sval1;
  460.  
  461.   ObjLen := ObjLen + 2;
  462.   Obj[ObjLen-1] := Lo(DataType);
  463.   Obj[ObjLen]   := Hi(DataType);
  464.  
  465. End;  {MemRef }
  466.  
  467. Procedure MemoryRef(MemStr : AnyString); {Trys to interpret MemStr as direct
  468.                                           memory reference }
  469. var
  470.   MR : AnyString;
  471.  
  472. Begin {MemoryRef }
  473.  
  474.   If (MemStr[1] = '[') AND (MemStr[Length(MemStr)] = ']') then
  475.     begin
  476.       MemStr := Copy(MemStr,2,Length(MemStr) - 2);
  477.       TestNumber(MemStr);
  478.       If Found then
  479.         MemAddr := NumVal
  480.       Else
  481.         begin
  482.           OperandLookup(MemStr);
  483.           If Found then
  484.             If (SymTable[TablePtr].SymType AND IMMED16) <> 0 then
  485.               MemAddr := SymTable[TablePtr].Val1
  486.             Else
  487.               Found := false;
  488.         end
  489.     end
  490.  
  491. End;  {MemoryRef }
  492.  
  493. Procedure ProcOffset(OS : AnyString); {interpret OS as an offset operand }
  494.  
  495. Begin {ProcOffset }
  496.  
  497.   Found := true;
  498.  
  499.   If Copy(OS,1,7) <> 'OFFSET(' then
  500.     Found := False
  501.   Else
  502.     begin
  503.       If Pass = 1 then
  504.         OffsetType := IMMED16
  505.       Else
  506.         begin
  507.           OS := Copy(OS,8,Length(OS) - 8);
  508.           OperandLookup(OS);
  509.           If Found AND (SymTable[TablePtr].SymType AND (MEMY OR NEAR) <> 0) then
  510.             begin
  511.               OffsetVal := SymTable[TablePtr].Val1;
  512.               OffsetType := IMMED16;
  513.             end
  514.           Else
  515.             begin
  516.               ErrorMessage('Illegal or Undefined arg. for Offset');
  517.               OffsetVal := 0;
  518.               Found := true;
  519.               OffsetType := IMMED16;
  520.             end
  521.         end
  522.     end
  523.  
  524. End;  {ProcOffset }
  525.  
  526. Procedure ParseDispOffReg(PDOR : AnyString); {interpret PDOR as offset off
  527.                                               of a register }
  528. var
  529.   RegStr : AnyString;
  530.   Pointer : integer;
  531.  
  532.   Procedure ParseDisp(DispStr : AnyString);
  533.     Begin {internal ParseDisp }
  534.       DispStr := Copy(DispStr,1,Pointer - 1);
  535.       OperandLookup(DispStr);
  536.       If Found AND
  537.          (SymTable[TablePtr].SymType AND (IMMED16 OR IMMED8) <> 0) then
  538.             begin
  539.               NeedOffset := SymTable[TablePtr].SymType;
  540.               Offset     := SymTable[TablePtr].Val1;
  541.               EXIT;
  542.             end;
  543.  
  544.       TestNumber(DispStr);
  545.       If Found then
  546.         begin
  547.           NeedOffset := NumType;
  548.           Offset := NumVal;
  549.           EXIT;
  550.         end;
  551.  
  552.       ProcOffset(DispStr);
  553.       If Found then
  554.         begin
  555.           NeedOffset := OffsetType;
  556.           Offset := OffsetVal;
  557.         end
  558.  
  559.   end; {internal ParseDisp }
  560.  
  561. Begin {ParseDispOffReg }
  562.  
  563.   If PDOR = '[BP]' then
  564.     begin
  565.       RegVal := 6;
  566.       NeedOffset := IMMED8;
  567.       Offset := 0;
  568.       Found := true;
  569.     end
  570.   Else
  571.     begin
  572.       Pointer := Pos('[',PDOR);
  573.       If Pointer <= 1 then
  574.         begin
  575.           Found := false;
  576.           EXIT;
  577.         end;
  578.       RegStr := Copy(PDOR,Pointer,Length(PDOR) - Pointer + 1);
  579.       If RegStr <> '[BP]' then
  580.         begin
  581.           OperandLookup(RegStr);
  582.           If NOT Found OR (SymTable[TablePtr].SymType <> MemReg) then
  583.             begin
  584.               Found := false;
  585.               EXIT;
  586.             end
  587.           Else
  588.             begin
  589.               RegVal := SymTable[TablePtr].Val1;
  590.               ParseDisp(RegStr);
  591.             end
  592.         end
  593.       Else
  594.         begin
  595.           RegVal := 6;
  596.           ParseDisp(RegStr);
  597.         end
  598.     end
  599.  
  600. End;  {ParseDispOffReg }
  601.  
  602. Procedure Charactor(ch : AnyString); {checks to see if ch is quoted char }
  603.  
  604. Begin {Charactor }
  605.  
  606.   Found := false;
  607.   If Length(ch) = 3 then
  608.     If ch[1] = Quote then
  609.       If ch[Length(ch)] = Quote then
  610.         begin
  611.           Found := true;
  612.           CharVal := Ord(ch[2]);
  613.         end;
  614.  
  615. End;  {Charactor }
  616.  
  617. Procedure TypeOperand(OperStr : AnyString); {checks type of operand }
  618.  
  619. Begin {TypeOperand }
  620.  
  621.   {Any operand? }
  622.   If Length(OperStr) = 0 then
  623.     begin
  624.       TargType := NONE;
  625.       EXIT;
  626.     end;
  627.  
  628.   {In Symbol Table? }
  629.   OperandLookup(OperStr);
  630.   If Found then
  631.     begin
  632.       TargType := SymTable[TablePtr].SymType;
  633.       TargVal1 := SymTable[TablePtr].Val1;
  634.       If TablePtr <= Predef then TargVal2 := SymTable[TablePtr].Val2;
  635.       EXIT;
  636.     end;
  637.  
  638.   {Number? }
  639.   TestNumber(OperStr);
  640.   If Found then
  641.     begin
  642.       TargType := NumType;
  643.       TargVal1 := NumVal;
  644.       EXIT;
  645.     end;
  646.  
  647.   {Direct memory reference? }
  648.   MemoryRef(OperStr);
  649.   If Found then
  650.     begin
  651.       TargType := MEMY;
  652.       TargVal1 := MemAddr;
  653.       EXIT;
  654.     end;
  655.  
  656.   {Offset off register? }
  657.   ParseDispOffReg(OperStr);
  658.   If Found then
  659.     begin
  660.       TargType := MEMREG;
  661.       TargVal1 := RegVal;
  662.       EXIT;
  663.     end;
  664.  
  665.   {Offset? }
  666.   ProcOffset(OperStr);
  667.   If Found then
  668.     begin
  669.       TargType := OffSetType;
  670.       TargVal1 := OffsetVal;
  671.       EXIT;
  672.     end;
  673.  
  674.   {Character? }
  675.   Charactor(OperStr);
  676.   If Found then
  677.     begin
  678.       TargType := IMMED8 OR IMMED16;
  679.       TargVal1 := CharVal;
  680.       EXIT;
  681.     end;
  682.  
  683.   {String? }
  684.   If OperStr[1] = Quote then
  685.     begin
  686.       TargType := STRG;
  687.       EXIT;
  688.     end;
  689.  
  690.   {Not found? Assume Near Label or Memory Reference, (error on Pass 2) }
  691.   If Pass = 2 then ErrorMessage('Undefined Symbol '+ OperStr);
  692.  
  693.   TargType := NEAR OR MEMY;
  694.  
  695. End;  {TypeOperand }
  696.  
  697. Procedure OpType; {Decides between word and byte operands }
  698.  
  699. Begin {OpType }
  700.  
  701.   If ((DType OR SType) AND (REG16 OR ACUM16 OR SEGMNT OR C_S) <> 0) then
  702.     Word := true
  703.   Else
  704.     If ((DType OR SType) AND (REG8 OR ACUM8) <> 0) then
  705.       Word := false
  706.     Else
  707.       If OpStr[Length(OpStr)] = 'B' then
  708.         Word := false
  709.       Else
  710.         Word := true;
  711.  
  712. End;  {OpType }
  713.  
  714. Procedure BuildOpCode; {builds the op code }
  715.  
  716. Begin {BuildOpCode }
  717.  
  718.   ObjLen := Succ(ObjLen);
  719.   Obj[ObjLen] := OpCodes[OpPtr].OpCodeVal;
  720.  
  721.   If (Flag AND ADDREG) <> 0 then
  722.     If (DType AND (SEGMNT OR C_S)) <> 0 then
  723.       Obj[ObjLen] := Obj[ObjLen] + DVal2
  724.     Else
  725.       If (Flag AND DIRECTION) <> 0 then
  726.         Obj[ObjLen] := Obj[ObjLen] + SVal2 DIV 8
  727.       Else
  728.         Obj[ObjLen] := Obj[ObjLen] + Dval2 DIV 8;
  729.  
  730.   If ((Flag AND AUTOW) <> 0) AND Word then Obj[ObjLen] := Succ(Obj[ObjLen]);
  731.  
  732.   If ((Flag AND AUTOC) <> 0) AND (SType AND CL <> 0) then
  733.     Obj[ObjLen] := Obj[ObjLen] + 2;
  734.  
  735. End;  {BuildOpCode }
  736.  
  737. Procedure BuildModeByte; {builds addressing mode byte, and if necessary
  738.                           the displacement byte(s) }
  739. var
  740.   M : integer;
  741.  
  742. Begin {BuildModeByte }
  743.  
  744.   ObjLen := Succ(ObjLen);
  745.  
  746.   If ((DType OR SType) AND MEMY) <> 0 then
  747.     begin
  748.       If DType = MEMY then M := SVal2 else M := DVal2;
  749.       Obj[ObjLen] := 6 + M;
  750.       MemRef(DType);
  751.       EXIT;
  752.     end;
  753.  
  754.   If (Flag AND DIRECTION) <> 0 then M := SVal1 + DVal2 Else M := DVal1 + SVal2;
  755.   Obj[ObjLen] := M;
  756.  
  757.   If NeedOffset <> NONE then
  758.     begin
  759.       If (Offset <= 127) AND (Offset >= -128) then
  760.         begin
  761.           Obj[ObjLen] := Obj[ObjLen] + 64;
  762.           If Offset < 0 then Offset := Offset AND $FF;
  763.           ObjLen := Succ(ObjLen);
  764.           Obj[ObjLen] := Offset;
  765.         end
  766.       Else
  767.         begin
  768.           Obj[ObjLen] := Obj[ObjLen] + 128;
  769.           ObjLen := ObjLen + 2;
  770.           Obj[ObjLen-1] := Lo(Offset);
  771.           Obj[ObjLen]   := Hi(Offset);
  772.         end;
  773.     end;
  774.  
  775. End;  {BuildModeByte }
  776.  
  777. Procedure BuildExtensionByte; {builds the opcode extension from bits 3-5 of
  778.                                the flag word }
  779. Var
  780.   Ext,Mask : integer;
  781.  
  782. Begin {BuildExtensionByte }
  783.  
  784.   Mask := $38;
  785.   Ext := Flag AND Mask;
  786.  
  787.   If (Flag AND DIRECTION) <> 0 then DVal2 := Ext Else SVal2 := Ext;
  788.  
  789.   BuildModeByte;
  790.  
  791. End;  {BuildExtensionByte }
  792.  
  793. Procedure BuildDisp8; {calcs displacement from present location to
  794.                        location given as operand }
  795. var
  796.   D : integer;
  797.  
  798. Begin {BuildDisp8 }
  799.  
  800.   D := DVal1 - Loctr;
  801.   If ABS(D) >= 128 then
  802.     begin
  803.       D := 0;
  804.       If Pass = 2 then ErrorMessage('Too far for short jump');
  805.     end;
  806.  
  807.   If D < 0 then D := D AND $FF;
  808.  
  809.   ObjLen := Succ(ObjLen);
  810.   Obj[ObjLen] := D;
  811.  
  812. End;  {BuildDisp8 }
  813.  
  814. Procedure BuildDisp16; {calcs disp from loc to loc }
  815.  
  816. var
  817.   D : integer;
  818.  
  819. Begin {BuildDisp16 }
  820.  
  821.   D := DVal1 - Loctr;
  822.   If (OpStr = 'JMP') AND (D <= 128) then DiagMessage('Could use JMPS');
  823.  
  824.   If (D < 0) AND (OpStr <> 'CALL') then
  825.     begin
  826.       D := 0;
  827.       If Pass = 2 then ErrorMessage('Illegal reverse long jump');
  828.     end;
  829.  
  830.   ObjLen := ObjLen + 2;
  831.   Obj[ObjLen-1] := Lo(D);
  832.   Obj[ObjLen]   := Hi(D);
  833.  
  834. End;  {BuildDisp16 }
  835.  
  836. Procedure BuildImmed8; {builds byte of immediate data }
  837.  
  838. var
  839.   IVal : integer;
  840.  
  841.   Procedure SubImmed8;
  842.   Begin {internal SubImmed8 }
  843.  
  844.     If NOT (IVal in [0..255]) then
  845.       begin
  846.         IVal := 0;
  847.         If Pass = 2 then ErrorMessage('Data too long');
  848.       end;
  849.       ObjLen := Succ(ObjLen);
  850.       Obj[ObjLen] := IVal;
  851.  
  852.   End;  {internal SubImmed8 }
  853.  
  854. Begin {BuildImmed8 }
  855.  
  856.   If (DType AND IMMED8) <> 0 then
  857.     begin
  858.       IVal := DVal1;
  859.       SubImmed8;
  860.     end;
  861.  
  862.   If (SType AND IMMED8) <> 0 then
  863.     begin
  864.       IVal := SVal1;
  865.       SubImmed8;
  866.     end;
  867.  
  868. End;  {BuildImmed8 }
  869.  
  870. Procedure BuildImmed16; {builds immediate word(s) }
  871.  
  872. Begin {BuildImmed16 }
  873.  
  874.   If (DType AND IMMED16) <> 0 then
  875.     begin
  876.       ObjLen := ObjLen + 2;
  877.       Obj[ObjLen-1] := Lo(DVal1);
  878.       Obj[ObjLen]   := Hi(DVal1);
  879.     end;
  880.  
  881.   If (SType AND IMMED16) <> 0 then
  882.     begin
  883.       ObjLen := ObjLen + 2;
  884.       Obj[ObjLen-1] := Lo(SVal1);
  885.       Obj[ObjLen]   := Hi(SVal1);
  886.     end;
  887.  
  888. End;  {BuildImmed16 }
  889.  
  890. Procedure ProcMachOp; {updates Loctr based on op length, and makes obj code }
  891.  
  892. Begin {ProcMachOp }
  893.  
  894.   OpType;
  895.   Loctr := Succ(Loctr);
  896.  
  897.   If Pass = 2 then BuildOpCode;
  898.   If (OpCodes[OpPtr].OpCodeVal = $D5) OR (OpCodes[OpPtr].OpCodeVal = $D4) then
  899.     begin
  900.       Loctr := Succ(Loctr);
  901.       If Pass = 2 then
  902.         begin
  903.           ObjLen := Succ(ObjLen);
  904.           Obj[ObjLen] := $A;
  905.         end;
  906.     end;
  907.  
  908.   If NeedOffset <> NONE then
  909.     If (NeedOffset AND IMMED8 <> 0) then Loctr := Succ(Loctr)
  910.     Else Loctr := Loctr + 2;
  911.  
  912.   If (Flag AND (NEEDMODEBYTE OR NEEDEXT)) <> 0 then
  913.     If ((DType OR SType) AND MEMY) <> 0 then Loctr := Loctr + 2;
  914.  
  915.   If (Flag AND NEEDEXT) <> 0 then
  916.     begin
  917.       Loctr := Succ(Loctr);
  918.       If Pass = 2 then BuildExtensionByte;
  919.     end;
  920.  
  921.   If (Flag AND NEEDMODEBYTE) <> 0 then
  922.     begin
  923.       Loctr := Succ(Loctr);
  924.       If Pass = 2 then BuildModeByte;
  925.     end;
  926.  
  927.   If (Flag AND NEEDISP8) <> 0 then
  928.     begin
  929.       Loctr := Succ(Loctr);
  930.       If Pass = 2 then BuildDisp8;
  931.     end;
  932.  
  933.   If (Flag AND NEEDISP16) <> 0 then
  934.     begin
  935.       Loctr := Loctr + 2;
  936.       If Pass = 2 then BuildDisp16;
  937.     end;
  938.  
  939.   If (Flag AND NEEDIMMED8) <> 0 then
  940.     begin
  941.       Loctr := Succ(Loctr);
  942.       If Pass = 2 then BuildImmed8;
  943.     end;
  944.  
  945.   If NOT Word AND ((Flag AND NEEDIMMED) <> 0) then
  946.     begin
  947.       Loctr := Succ(Loctr);
  948.       If Pass = 2 then BuildImmed8;
  949.     end;
  950.  
  951.   If Word AND ((Flag AND NEEDIMMED) <> 0) then
  952.     begin
  953.       If DType = IMMED16 then Loctr := Loctr + 4 Else Loctr := Loctr + 2;
  954.       If Pass = 2 then BuildImmed16;
  955.     end;
  956.  
  957.   If (Flag AND NEEDMEM) <> 0 then
  958.     begin
  959.       Loctr := Loctr + 2;
  960.       If Pass = 2 then MemRef(DType);
  961.     end;
  962.  
  963. End;  {ProcMachOp }
  964.  
  965. Procedure ProcEQU; {EQU pseudo op }
  966.  
  967. Begin {ProcEQU }
  968.  
  969.   If LabelStr = '' then
  970.     begin
  971.       If Pass = 2 then ErrorMessage('EQU without symbol');
  972.     end
  973.   Else
  974.     If Pass <> 2 then
  975.       begin
  976.         If DType = (NEAR OR MEMY) then
  977.           ErrorMessage('EQU with forward reference')
  978.         Else
  979.           begin
  980.             SymTable[NumSym].Val1 := DVal1;
  981.             SymTable[NumSym].SymType := DType;
  982.           end;
  983.       end;
  984.  
  985. End;  {ProcEQU }
  986.  
  987. Procedure ProcORG; {ORG pseudo op }
  988.  
  989. Begin {ProcORG }
  990.  
  991.   Loctr := DVal1;
  992.  
  993. End;  {ProcORG }
  994.  
  995. Procedure ProcDB; {DB pseudo op }
  996.  
  997.   Procedure BuildByte;
  998.  
  999.   Begin {internal BuildByte }
  1000.  
  1001.     ObjLen := Succ(ObjLen);
  1002.     Obj[ObjLen] := NumVal;
  1003.  
  1004.   End;  {internal BuildByte }
  1005.  
  1006.   Procedure BuildStg;
  1007.  
  1008.   Begin {internal BuildStg }
  1009.  
  1010.     FldStr := Copy(FldStr,2,Length(FldStr)-2);
  1011.     For i := 1 to Length(FldStr) Do
  1012.       begin
  1013.         ObjLen := Succ(ObjLen);
  1014.         Obj[ObjLen] := ord(FldStr[i]);
  1015.       end;
  1016.  
  1017.   End;  {internal BuildStg }
  1018.  
  1019. Begin {ProcDB }
  1020.  
  1021.   If LabelStr <> '' then SymTable[NumSym].SymType := MEMY;
  1022.  
  1023.   LinePtr  := OpdPtr;
  1024.   LinePtr2 := OpdPtr;
  1025.  
  1026.   While LinePtr < EndPtr Do
  1027.     begin
  1028.       GetField;
  1029.       If NOT Found then
  1030.         begin
  1031.           Loctr := Loctr + ObjLen;
  1032.           EXIT;
  1033.         end;
  1034.  
  1035.       TargetStr := FldStr;
  1036.       TestNumber(TargetStr);
  1037.       If Found AND (NumType AND IMMED8 <> 0) then
  1038.         BuildByte
  1039.       Else
  1040.         If FldStr[1] = Quote then
  1041.           BuildStg
  1042.         Else
  1043.           If Pass = 2 then ErrorMessage('Unrecognized operand '+FldStr);
  1044.     end; {While..Do }
  1045.  
  1046.   Loctr := Loctr + ObjLen;
  1047.  
  1048. End;  {ProcDB }
  1049.  
  1050. Procedure ProcDS; {DS pseudo op }
  1051.  
  1052. var
  1053.   DSVal : integer;
  1054.  
  1055. Begin {ProcDS }
  1056.  
  1057.   DSFlag := true;
  1058.   If LabelStr <> '' then SymTable[NumSym].SymType := MEMY;
  1059.   If (SType AND IMMED8) <> 0 then DSVal := SVal1 Else DSVal := 0;
  1060.   If Pass <> 1 then For i := 1 to DVal1 do Write(ObjFile,chr(DSVal));
  1061.  
  1062.   Loctr := Loctr + DVal1;
  1063.  
  1064. End;  {ProcDS }
  1065.  
  1066. Procedure ProcPROC; {PROC pseudo op }
  1067.  
  1068. Begin {ProcPROC }
  1069.  
  1070.   If StkTop < MAXSTK then
  1071.     begin
  1072.       StkTop := Succ(StkTop);
  1073.       ProcType[StkTop] := DType;
  1074.     end
  1075.   Else
  1076.     If Pass <> 1 then ErrorMessage('Procedures nested too deeply');
  1077.  
  1078. End;  {ProcPROC }
  1079.  
  1080. Procedure ProcENDP; {ENDP pseudo op }
  1081.  
  1082. Begin {ProcENDP }
  1083.  
  1084.   If StkTop > 0 then StkTop := Pred(StkTop)
  1085.   Else If Pass <> 1 then ErrorMessage('ENDP without PROC');
  1086.  
  1087. End;  {ProcENDP }
  1088.  
  1089. Procedure PseudoOp;
  1090.  
  1091. Begin {PseudoOp }
  1092.  
  1093.   Case OpCodes[OpPtr].OpCodeVal of
  1094.     1 : ProcEQU;
  1095.     2 : ProcORG;
  1096.     3 : ProcDB;
  1097.     4 : ProcDS;
  1098.     5 : ProcPROC;
  1099.     6 : ProcENDP;
  1100.   End;
  1101.  
  1102. End;  {PseudoOp }
  1103.  
  1104. Procedure UpdateLoctr; {decodes operation and advances Loctr }
  1105.  
  1106. Begin {UpdateLoctr }
  1107.  
  1108.   TypeOperand(DestStr);
  1109.   DType := TargType;
  1110.   DVal1 := TargVal1;
  1111.   DVal2 := TargVal2;
  1112.  
  1113.   If OpStr = 'RET' then
  1114.     SType := ProcType[StkTop]
  1115.   Else
  1116.     begin
  1117.       TypeOperand(SourceStr);
  1118.       SType := TargType;
  1119.       SVal1 := TargVal1;
  1120.       SVal2 := TargVal2;
  1121.     end;
  1122.  
  1123.   TargetStr := OpStr;
  1124.   LookupOp;
  1125.   If Found then
  1126.     begin
  1127.       Flag := OpCodes[OpPtr].Flagss;
  1128.       If (Flag AND MACHOP) <> 0 then
  1129.         ProcMachOp
  1130.       Else
  1131.         PseudoOp;
  1132.     end
  1133.   Else
  1134.     If Pass <> 1 then
  1135.       begin
  1136.         ErrorMessage('Syntax Error: '+OpStr);
  1137.         If ((ACUM8 OR ACUM16 OR REG8 OR REG16 OR SEGMNT OR C_S) AND
  1138.            (DType OR SType)) = 0 Then
  1139.           If (SType AND (NONE OR IMMED8 OR IMMED16)) <> 0 Then
  1140.             If NOT (OpStr[Length(OpStr)] in ['B','W']) then
  1141.               DiagMessage('Specify Word or Byte Operation');
  1142.       end;
  1143.  
  1144. End;  {UpdateLoctr }
  1145.  
  1146. Procedure Progress; {Gives user status of assemble }
  1147.  
  1148. var
  1149.   X,Y : integer;
  1150.  
  1151. Begin {Progress }
  1152.  
  1153.   If LineNum MOD 6 = 0 then {Only update every fourth line, saves time.. }
  1154.     begin
  1155.       LowVideo;
  1156.       X := WhereX;
  1157.       Y := WhereY;
  1158.       GoToXY(60,1);
  1159.       If Pass = 1 then Write('Pass: 1  Line: ',LineNum)
  1160.       Else Write('Pass: 2  Line: ',LineNum);
  1161.       ClrEol;
  1162.       GoToXY(X,Y);
  1163.       NormVideo;
  1164.     end;
  1165.  
  1166. End;  {Progress }
  1167.  
  1168. Procedure CheckPhase; {label value same on both passes? }
  1169.  
  1170. Begin {CheckPhase }
  1171.  
  1172.   If OpStr <> 'EQU' then
  1173.     begin
  1174.       TargetStr := LabelStr;
  1175.       OperandLookup(TargetStr);
  1176.  
  1177.       With SymTable[TablePtr] Do
  1178.         If ((SymType AND (NEAR OR MEMY)) <> 0) AND (Val1 <> Loctr) Then
  1179.           ErrorMessage('Phase Error');
  1180.     end;
  1181.  
  1182. End;  {CheckPhase }
  1183.  
  1184. Procedure WrOutput; {Write the object code and then listing }
  1185.  
  1186. var
  1187.   H : AnyString;
  1188.   Spacing : byte;
  1189.  
  1190. Begin {WrOutput }
  1191.  
  1192.   For i := 1 to ObjLen Do Write(ObjFile,Chr(Obj[i]));
  1193.   CodeSize := CodeSize + ObjLen;
  1194.  
  1195.   If ListLoc <> NoIO then
  1196.     begin
  1197.       If DSFlag then H := Hex(Loctr-DVal1) Else H := Hex(Loctr-ObjLen);
  1198.  
  1199.      {Pad hex number }
  1200.       Case Length(H) of
  1201.         0 : H := '0000';
  1202.         1 : H := '000' + H;
  1203.         2 : H := '00' + H;
  1204.         3 : H := '0' + H;
  1205.       End; {Case }
  1206.       Write(ListFile,H,' ');
  1207.  
  1208.       Spacing := 0;
  1209.       For i := 1 to ObjLen Do
  1210.         begin
  1211.           H := Hex(Obj[i]);
  1212.           If Length(H) = 1 then H := '0' + H;
  1213.           Write(ListFile,H);
  1214.           Spacing := Spacing + Length(H);
  1215.         end;
  1216.  
  1217.       Writeln(ListFile,'':16-Spacing,LineNum:4,' ',InpLine);
  1218.     end;
  1219.  
  1220. End;  {WrOutput }
  1221.  
  1222. Procedure FirstPass;  {Adds user-defined symbols to symbol table }
  1223.  
  1224. Begin {FirstPass }
  1225.  
  1226.   Pass := 1;
  1227.   Loctr := 256;
  1228.   LineNum := 0;
  1229.   EndofSource := false;
  1230.  
  1231.   If SourceLoc = Memory then
  1232.     begin {reset CurLine to point to first line in text stream }
  1233.       With Curwin^ Do
  1234.         begin
  1235.           CurLine := TopLine;
  1236.           While CurLine^.BackLink <> NIL Do
  1237.             CurLine := CurLine^.BackLink;
  1238.         end
  1239.     end
  1240.   Else
  1241.     begin
  1242.       Assign(SourceFile,SourceName);
  1243.       Reset(SourceFile);
  1244.     end;
  1245.  
  1246.   While NOT EndOfSource Do
  1247.     begin
  1248.       GetLine;
  1249.       ParseLine;
  1250.       If LabelStr <> '' then NewEntry(LabelStr);
  1251.       If OpStr    <> '' then UpdateLoctr;
  1252.       Progress;
  1253.     end;
  1254.  
  1255.   If SourceLoc = Disk then Close(SourceFile);
  1256.  
  1257. End;  {FirstPass }
  1258.  
  1259. Procedure PassTwo; {Generates object code }
  1260.  
  1261. Begin {PassTwo }
  1262.  
  1263.   Pass := 2;
  1264.   Loctr := 256;
  1265.   LineNum := 0;
  1266.   EndofSource := false;
  1267.  
  1268.   If SourceLoc = Memory then
  1269.     begin {reset CurLine to point to first line in text stream }
  1270.       With Curwin^ Do
  1271.         begin
  1272.           CurLine := TopLine;
  1273.           While CurLine^.BackLink <> NIL Do
  1274.             CurLine := CurLine^.BackLink;
  1275.         end
  1276.     end
  1277.   Else
  1278.     begin
  1279.       Assign(SourceFile,SourceName);
  1280.       Reset(SourceFile);
  1281.     end;
  1282.  
  1283.   While NOT EndOfSource Do
  1284.     begin
  1285.       GetLine;
  1286.       If NOT EndOfSource then
  1287.         begin
  1288.           ParseLine;
  1289.           If LabelStr <> '' then CheckPhase;
  1290.           If OpStr    <> '' then UpdateLoctr;
  1291.           WrOutput;
  1292.           Progress;
  1293.         end;
  1294.     end;
  1295.  
  1296.   If SourceLoc = Disk then Close(SourceFile);
  1297.  
  1298. End;  {PassTwo }
  1299.  
  1300. Procedure DumpSymTable; {show the symbol table }
  1301.  
  1302. Begin {DumpSymTable }
  1303.  
  1304.   If ListLoc <> NoIO then
  1305.     begin
  1306.       Writeln(ListFile);
  1307.       Writeln('Symbol Table Dump');
  1308.       i := Predef + 1;
  1309.  
  1310.       While i <= NumSym Do
  1311.         With SymTable[i] Do
  1312.           begin
  1313.             Writeln(ListFile,Symbol:20,Hex(Val1):8);
  1314.             i := Succ(i);
  1315.           end;
  1316.     end;
  1317.  
  1318. End;  {DumpSymTable }
  1319.  
  1320. Procedure FinalProc; {we must always finish what we started }
  1321.  
  1322. Begin {FinalProc }
  1323.  
  1324.   If StkTop > 0 then ErrorMessage('missing ENDP');
  1325.  
  1326.   Writeln(ListFile);
  1327.   Writeln(ListFile,Pred(LineNum),' Lines Assembled');
  1328.   Writeln(ListFile,'CodeSize: ',CodeSize,' Bytes');
  1329.   Writeln(ListFile,Errs,' Error(s) detected');
  1330.   Writeln(ListFile,Diag,' Diagnostic(s) offered');
  1331.  
  1332.   DumpSymTable;
  1333.  
  1334.   Close(ObjFile);
  1335.   If SourceLoc = Disk then Close(SourceFile);
  1336.   If ListLoc = Disk then Close(ListFile);
  1337.  
  1338. End;  {FinalProc }
  1339.  
  1340. Procedure SetUpTables;  {Reads the file TChasm.Dat into the OpCode and
  1341.                          Symbol tables. }
  1342.  
  1343. Var
  1344.   i : integer;
  1345.  
  1346. Begin {SetUpTables }
  1347.  
  1348.   Repeat
  1349.     If NOT Exist(OPCODEFILE) then
  1350.       begin
  1351.         Writeln('"',OPCODEFILE,'" is not on this disk.');
  1352.         Writeln('[I]nsert new disk or [Q]uit? ');
  1353.         Repeat Read(Kbd,ch) Until UpCase(ch) in ['I','Q'];
  1354.         If UpCase(ch) = 'Q' then HALT;
  1355.       end;
  1356.   Until Exist(OPCODEFILE);
  1357.  
  1358.   Assign(DataFile,OPCODEFILE);
  1359.   Reset(DataFile);
  1360.  
  1361.   Readln(DataFile,NumOp);  {Read NumOp from file to see if too large }
  1362.   if NumOp > MAXNUMOP then
  1363.     begin
  1364.       Writeln('Number of defined OpCodes is too large');
  1365.       HALT;
  1366.     end;
  1367.  
  1368.   Readln(DataFile,InpLine); {Read Quote character and skip comments in file }
  1369.   Quote := InpLine[1]; {get quote character }
  1370.  
  1371.   For i := 1 to NumOp do
  1372.     With OpCodes[i] Do
  1373.       begin
  1374.         Readln(DataFile,OpCodeVal,DstType,
  1375.                SrcType,Flagss,InpLine);
  1376.         {$V-}
  1377.         NextWord(InpLine,Mnemonic,1,[' ',',']);
  1378.         {$V+}
  1379.       end;
  1380.  
  1381.   Readln(DataFile,Predef);  {Read Predef from file to see if it matches }
  1382.   if Predef > MAXSYM then
  1383.     begin
  1384.       Writeln('Number of defined Symbols is too large');
  1385.       HALT;
  1386.     end;
  1387.  
  1388.   Readln(DataFile,InpLine); {skip comments in file }
  1389.  
  1390.   For i := 1 to Predef do
  1391.     With SymTable[i] Do
  1392.       begin
  1393.         Readln(DataFile,Val1,Val2,SymType,InpLine);
  1394.         {$V-}
  1395.         NextWord(InpLine,Symbol,1,[' ',',']);
  1396.         {$V+}
  1397.       end;
  1398.  
  1399.   NumSym := Predef;
  1400.   Close(DataFile);
  1401.  
  1402. End;  {SetUpTables }
  1403.  
  1404. Procedure TitleStart;
  1405.  
  1406. Begin {TitleStart }
  1407.  
  1408. {Print Title }
  1409.   NormVideo;
  1410.   ClrScr;
  1411.   GoToXY(18,25);
  1412.   Writeln('┌────────────────────────────────────────────┐');GoToXY(18,25);
  1413.   Writeln('│                                            │');GoToXY(18,25);
  1414.   Writeln('│          "Turbo" Cheap Assembler  1.0      │');GoToXY(18,25);
  1415.   Writeln('│           by Mark Streich                  │');GoToXY(18,25);
  1416.   Writeln('│           based upon CHASM (tm)            │');GoToXY(18,25);
  1417.   Writeln('│           by Dave Whitman                  │');GoToXY(18,25);
  1418.   Writeln('│                                            │');GoToXY(18,25);
  1419.   Writeln('│                                            │');GoToXY(18,25);
  1420.   Writeln('└────────────────────────────────────────────┘');
  1421.  
  1422.   Write(#10#10#10#10#10#10#10#10#10#10);
  1423.  
  1424. End;  {TitleStart }
  1425.  
  1426. {********************** Main Screen Procedures *****************************}
  1427.  
  1428. Procedure ShowDirectory; {from Turbo Tutor (tm) - GREAT book and disk,
  1429.                           This is just one of many programs included }
  1430. type
  1431.   Char12arr            = array [ 1..12 ] of Char;
  1432.   String20             = string[ 20 ];
  1433.   RegRec =
  1434.     record
  1435.       AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : Integer;
  1436.     end;
  1437.  
  1438. var
  1439.   Regs                 : RegRec;
  1440.   DTA                  : array [ 1..43 ] of Byte;
  1441.   Mask                 : Char12arr;
  1442.   NamR                 : AnyString;
  1443.   Drive,
  1444.   Error, I             : Integer;
  1445.  
  1446. begin {ShowDirectory }
  1447.  
  1448.   FillChar(DTA,SizeOf(DTA),0);        { Initialize the DTA buffer }
  1449.   FillChar(Mask,SizeOf(Mask),0);      { Initialize the mask }
  1450.   FillChar(NamR,SizeOf(NamR),0);      { Initialize the file name }
  1451.  
  1452.   Write( 'Directory Mask? ' );
  1453.   Readln(NamR);
  1454.   NamR := Caps(NamR);
  1455.   If NamR = '' then NamR := LogDir + '*.*'
  1456.   Else
  1457.   if (Length(NamR) = 2) AND (Pos(':',NamR) = 2) then NamR := NamR + '\*.*'
  1458.   Else
  1459.   if Length(NamR) = 1 then NamR := NamR + ':\*.*'
  1460.   Else
  1461.   if Pos(':',NamR) = 0 then NamR := LogDir + NamR;
  1462.  
  1463.   Drive := Ord(NamR[1])-64;
  1464.  
  1465.   Writeln('Directory of ',NamR);
  1466.   For I := 1 to Length(NamR) Do Mask[I] := NamR[I];
  1467.   Regs.AX := $1A00;         { Function used to set the DTA }
  1468.   Regs.DS := Seg(DTA);      { store the parameter segment in DS }
  1469.   Regs.DX := Ofs(DTA);      {   "    "      "     offset in DX }
  1470.   MSDos(Regs);              { Set DTA location }
  1471.   Error := 0;
  1472.   Regs.AX := $4E00;          { Get first directory entry }
  1473.   Regs.DS := Seg(Mask);      { Point to the file Mask }
  1474.   Regs.DX := Ofs(Mask);
  1475.   Regs.CX := 1;             { Store the option }
  1476.   MSDos(Regs);               { Execute MSDos call }
  1477.   Error := Regs.AX and $FF;  { Get Error return }
  1478.   I := 1;                    { initialize 'I' to the first element }
  1479.   if (Error = 0) then
  1480.     repeat
  1481.       NamR[I] := Chr(Mem[Seg(DTA):Ofs(DTA)+29+I]);
  1482.       I := I + 1;
  1483.     until not (NamR[I-1] in [' '..'~']) or (I>20);
  1484.  
  1485.   NamR[0] := Chr(I-1);          { set string length because assigning }
  1486.                                 { by element does not set length }
  1487.   while (Error = 0) do begin
  1488.     Error := 0;
  1489.     Regs.AX := $4F00;           { Function used to get the next }
  1490.                                 { directory entry }
  1491.     Regs.CX := 22;              { Set the file option }
  1492.     MSDos( Regs );              { Call MSDos }
  1493.     Error := Regs.AX and $FF;   { get the Error return }
  1494.     I := 1;
  1495.     repeat
  1496.       NamR[I] := Chr(Mem[Seg(DTA):Ofs(DTA)+29+I]);
  1497.       I := I + 1;
  1498.     until not (NamR[I-1] in [' '..'~'] ) or (I > 20);
  1499.     NamR[0] := Chr(I-1);
  1500.     if (Error = 0)
  1501.       then Write(NamR,'':16-Length(NamR));
  1502.   end;
  1503.   Writeln;
  1504.  
  1505.   If Drive in [1..26] then
  1506.     begin
  1507.       Regs.AX := $3600;               { Get Disk free space }
  1508.       Regs.DX := Drive;               { Store Drive number }
  1509.       MSDos( Regs );                  { Call MSDos to get disk info }
  1510.       Writeln(((Regs.AX*Regs.CX*1.0)*Regs.BX):1:0,' Bytes Free');
  1511.     end;
  1512.  
  1513.   Write('>');
  1514.  
  1515. End;  {ShowDirectory }
  1516.  
  1517. Procedure Say(S: AnyString); {Will write to the screen a string passed
  1518.    as a parameter.  Special control sequences can be embedded into the
  1519.    string to set the Normal Video (%!) and Low Video and do a Carriage
  1520.    Return (%@). For example, '%!Hello' will highlight the letter H and
  1521.    write the rest of the word in normal video }
  1522.  
  1523.     Var
  1524.       I: Integer;
  1525.  
  1526.     Begin
  1527.       I:=1;
  1528.       While I<=Length(S) Do
  1529.        Begin
  1530.         If Ord(S[I])<32 Then Write('^',Chr(Ord(S[I])+64))
  1531.         Else If S[I]<>'%' Then Write(S[I])
  1532.              Else If S[I+1]='@' Then          {%@ = Carriage Return         }
  1533.                Begin
  1534.                  WriteLn;
  1535.                  LowVideo;
  1536.                  I:=I+1;
  1537.                End
  1538.              Else If S[I+1]='!' Then          {%! = HighVideo for next char }
  1539.                     Begin
  1540.                       NormVideo;
  1541.                       Write(S[I+2]);
  1542.                       LowVideo;
  1543.                       I:=I+2;
  1544.                     End
  1545.              Else If S[I+1]='#' Then          {%# = Set HighVideo until unset}
  1546.                     Begin
  1547.                       NormVideo;
  1548.                       I := I+1;
  1549.                     End
  1550.                   Else Write('%');
  1551.                   I:=I+1;
  1552.       End; { While I<=Length(S) }
  1553.  
  1554.       NormVideo;
  1555.     End; { Say }
  1556.  
  1557. Procedure MainScreen; {Handles the Main Control Screen }
  1558.  
  1559. Begin {MainScreen }
  1560.  
  1561.   ClrScr;
  1562.   LowVideo;
  1563.   Say('Turbo Cheap Assembler%@%@');
  1564.   GetDir(0,LogDir); { Get the current directory of the current drive. }
  1565.   Say('%!Logged Directory: %#'+LogDir+'%@%@');
  1566.   Say('%!Work File: %#'+SourceName+'%@');
  1567.  
  1568.   Case SourceLoc of
  1569.     Disk   : Say('%@File Location: Disk%@');
  1570.     Memory : Say('%@File Location: Memory%@');
  1571.   End; {Case SourceLoc }
  1572.  
  1573.   Case ListLoc of
  1574.     Scrn    : Say('%!Output Location: %#Screen%@%@');
  1575.     Printer : Say('%!Output Location: %#Printer%@%@');
  1576.     NoIO    : Say('%!Output Location: %#None%@%@');
  1577.     Else      Say('%!Output Location: %#'+ListName+'%@%@');
  1578.   End; {Case ListType }
  1579.  
  1580.   Say('%!Edit        %!Save%@');
  1581.   Say('%!Directory   %!Assemble   %!Quit%@%@%!>');
  1582.  
  1583. End;  {MainScreen }
  1584.  
  1585. Procedure ChangeDirectory;
  1586.  
  1587. Var
  1588.   TempDir : AnyString;
  1589.  
  1590. Begin {ChangeDirectory }
  1591.   TempDir := LogDir;
  1592.   Repeat
  1593.     Write('New Directory? ');
  1594.     Readln(LogDir);
  1595.     LogDir := Caps(LogDir);
  1596.     if LogDir = '' then LogDir := TempDir
  1597.     Else
  1598.     if (LogDir='A') OR (LogDir='B') OR (LogDir='C') then
  1599.       LogDir := LogDir + ':';
  1600.     {$I-}
  1601.     ChDir(LogDir);
  1602.     {$I+}
  1603.   Until IOresult = 0;
  1604.  
  1605.   Writeln;
  1606.   Write('>');
  1607.  
  1608. End;  {ChangeDirectory }
  1609.  
  1610. Procedure ChangeWorkFile;
  1611.  
  1612. Var
  1613.   TempWorkFile : AnyString;
  1614.  
  1615. Begin {ChangeWorkFile }
  1616.  
  1617.   If SourceLoc = Memory then
  1618.     begin
  1619.       if EditChangeFlag then
  1620.         begin
  1621.           Write('Save changes? [Y/N] ');
  1622.           Repeat Read(Kbd,ch) Until UpCase(ch) in ['Y','N'];
  1623.           Writeln(UpCase(ch));
  1624.           If UpCase(ch) = 'Y' then if EditFileWrite(SourceName) then;
  1625.           EditChangeFlag := false; {reset for new file }
  1626.         end;
  1627.       EditWindowDeleteText;
  1628.     end;
  1629.  
  1630.   OK := true;
  1631.   SourceLoc := Disk;           {reset for new file }
  1632.   Write('New Work File? ');
  1633.   Readln(SourceName);
  1634.   If SourceName <> '' then
  1635.     begin
  1636.       SourceName := Caps(SourceName);
  1637.       If Pos('.',SourceName) = 0 then SourceName := SourceName + '.ASM';
  1638.       If Exist(SourceName) then
  1639.         begin
  1640.           Write('Loading...');
  1641.           CurWin^.FileName := SourceName;
  1642.           EditReaTxtFil(SourceName);
  1643.           If NOT OK then
  1644.             begin
  1645.               Write('Source too large - Will assemble from Disk');
  1646.               EditWindowDeleteText;
  1647.             end
  1648.           Else
  1649.             SourceLoc := Memory;
  1650.           Writeln;
  1651.         end
  1652.       Else
  1653.         begin
  1654.           Writeln('New file');
  1655.           SourceLoc := Memory;
  1656.           Curwin^.FileName := SourceName;
  1657.         end
  1658.     end;
  1659.  
  1660.   Write('>');
  1661.  
  1662. End;  {ChangeWorkFile }
  1663.  
  1664. Procedure ChangeListLoc; {changes where output listing will go }
  1665.  
  1666. Begin {ChangeListLoc }
  1667.  
  1668.   Write('Send Output to [P]rinter, [S]creen, [D]isk file, [CR]-None ');
  1669.   Repeat Read(Kbd,ch) Until UpCase(ch) in ['P','S','D',#13 {CR} ];
  1670.   Writeln(UpCase(ch));
  1671.   Write('>');
  1672.   Case UpCase(ch) of
  1673.     'P' : ListLoc := Printer;
  1674.     'S' : ListLoc := Scrn;
  1675.     #13 : ListLoc := NoIO;
  1676.     'D' : Begin
  1677.             ListLoc := Disk;
  1678.             Write('List File name? [.LST] ');
  1679.             Readln(ListName);
  1680.             ListName := Caps(ListName);
  1681.             Write('>');
  1682.             if Length(ListName) = 0 then
  1683.               begin
  1684.                 If SourceName <> '' then
  1685.                  ListName := Copy(SourceName,1,Pos('.',SourceName)-1) + '.LST'
  1686.                 Else ListLoc := NoIO;
  1687.               end
  1688.             else
  1689.               if Pos('.',ListName) = 0 then ListName := ListName + '.LST';
  1690.           End;
  1691.   End; {Case ch }
  1692.  
  1693. End;  {ChangeListLoc }
  1694.  
  1695. Procedure Assemble; {starts the whole mess a goin' }
  1696.  
  1697. Begin {Assemble }
  1698.  
  1699.   If SourceName <> '' then
  1700.     begin
  1701.       {Initialize Variables }
  1702.        For i := 0 to MAXOBJ do Obj[i] := 0;
  1703.        For i := 0 to MAXSTK do ProcType[i] := 0;
  1704.        StkTop   := 0;
  1705.        Errs     := 0;
  1706.        Diag     := 0;
  1707.        CodeSize := 0;
  1708.        NumSym   := Predef;
  1709.  
  1710.       {Init Object file }
  1711.        ObjName := Copy(SourceName,1,Pos('.',SourceName)-1) + '.COM';
  1712.        Assign(ObjFile,ObjName);
  1713.        Rewrite(ObjFile);
  1714.  
  1715.       {Init List file, if any }
  1716.        Case ListLoc of
  1717.          Scrn,
  1718.          NoIO    : Assign(ListFile,'TRM:');
  1719.          Printer : Assign(ListFile,'LST:');
  1720.          Disk    : begin
  1721.                      Assign(ListFile,ListName);
  1722.                      Rewrite(ListFile);
  1723.                    end;
  1724.        End; {Case }
  1725.  
  1726.        FirstPass;
  1727.        PassTwo;
  1728.        FinalProc;
  1729.     end
  1730.   Else
  1731.     Writeln('No Source File Specified');
  1732.  
  1733.   Write('>');
  1734.  
  1735. End;  {Assemble }
  1736.  
  1737. Procedure Quit;
  1738.  
  1739. Begin {Quit }
  1740.  
  1741.   Quitting := true;
  1742.   if SourceLoc = Memory then
  1743.     begin
  1744.       if EditChangeFlag then
  1745.         begin
  1746.           Write('Save changes? [Y/N] ');
  1747.           Repeat Read(Kbd,ch) Until UpCase(ch) in ['Y','N'];
  1748.           Writeln(UpCase(ch));
  1749.           If UpCase(ch) = 'Y' then
  1750.             begin
  1751.               Write('Saving...');
  1752.               if EditFileWrite(SourceName) then;
  1753.             end;
  1754.         end;
  1755.       EditWindowDeleteText;
  1756.     end
  1757.  
  1758. End;  {Quit }
  1759.  
  1760. Procedure EditFile; { Calls the editor functions}
  1761.  
  1762. var
  1763.   r,c : byte;
  1764.  
  1765. Begin {EditFile }
  1766.   If (SourceName = '') OR (SourceLoc = Disk) then
  1767.     begin
  1768.       if SourceName = '' then Writeln('No Work file specified')
  1769.       else Writeln('Cannot edit ',SourceName);
  1770.       Write('>');
  1771.       EXIT;
  1772.     end;
  1773.  
  1774.   { Initialize screen array and other stuff }
  1775.  
  1776.   for r := 1 to Defnorows do
  1777.     for c := 1 to Defnocols do
  1778.       with Screen [r,c] do
  1779.         begin
  1780.           Ch := chr (0);            {Have the editor clean up the screen}
  1781.           Color := Txtcolor
  1782.         end;
  1783.  
  1784.   RunDown := false;
  1785.   EditWindowTopFile;
  1786.   EditUpdPhyScr;
  1787.   EditSystem;
  1788.   MainScreen;
  1789.  
  1790. End;  {EditFile }
  1791.  
  1792. Procedure SaveFile; {Calls the editor save function }
  1793.  
  1794. Begin {SaveFile }
  1795.  
  1796.   If (SourceName = '') OR (SourceLoc = Disk) then
  1797.     begin
  1798.       if SourceLoc = Disk then Writeln('File not in memory')
  1799.       else Writeln('No Work file specified');
  1800.       Write('>');
  1801.       EXIT;
  1802.     end;
  1803.  
  1804.   If Exist(SourceName) then
  1805.     begin
  1806.       Write('Overwrite existing file? [Y/N] ');
  1807.       Repeat Read(Kbd,ch) Until UpCase(ch) in ['Y','N'];
  1808.       If UpCase(ch) = 'N' then
  1809.         begin
  1810.           Writeln;
  1811.           Write('>');
  1812.           EXIT;
  1813.         end
  1814.     end;
  1815.  
  1816.   Write('Saving...');
  1817.   If EditFileWrite(SourceName) then;
  1818.   Writeln;
  1819.   Write('>');
  1820.  
  1821. End;  {SaveFile }
  1822.  
  1823. BEGIN
  1824.  
  1825.   TitleStart;
  1826.   SetUpTables;
  1827.   EditInitialize;
  1828.  
  1829.   MainScreen;
  1830.  
  1831.   While NOT Quitting Do
  1832.     begin
  1833.       Read(Kbd,ch);
  1834.       Case UpCase(ch) of
  1835.         'W' : ChangeWorkFile;
  1836.         'S' : SaveFile;
  1837.         'D' : ShowDirectory;
  1838.         'E' : EditFile;
  1839.         'A' : Assemble;
  1840.         'O' : ChangeListLoc;
  1841.         'Q' : Quit;
  1842.         'L' : ChangeDirectory;
  1843.         Else  MainScreen;
  1844.       End; {Case ch }
  1845.     end;
  1846.  
  1847. END.