unit Logicev;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls, VBXCtrl, Switch, StdCtrls, Buttons, TabNotBk,
Grids, About;
type
TLogicEval = class(TForm)
Screens: TTabbedNotebook;
A: TBiSwitch; { Input Switches on EXECUTE form. }
B: TBiSwitch;
C: TBiSwitch;
D: TBiSwitch;
W: TShape; { Four LEDs on EXECUTE form. }
X: TShape;
Y: TShape;
Z: TShape;
WLbl: TLabel; {Labels on the LEDs. }
XLbl: TLabel;
YLbl: TLabel;
ZLbl: TLabel;
E: TShape;
F: TShape;
G: TShape;
I: TShape;
J: TShape;
H: TShape;
K: TShape;
SevenSeg: TPanel;
ExecVariables: TStringGrid;
EqnList: TListBox; { List of equations on execute page. }
AInit: TGroupBox; { "Buttons" on the INIT page. }
BInit: TGroupBox;
CInit: TGroupBox;
DInit: TGroupBox;
EInit: TGroupBox;
FInit: TGroupBox;
GInit: TGroupBox;
Hinit: TGroupBox;
IInit: TGroupBox;
Jinit: TGroupBox;
KInit: TGroupBox;
LInit: TGroupBox;
MInit: TGroupBox;
NInit: TGroupBox;
OInit: TGroupBox;
PInit: TGroupBox;
QInit: TGroupBox;
RInit: TGroupBox;
SInit: TGroupBox;
TInit: TGroupBox;
UInit: TGroupBox;
VInit: TGroupBox;
WInit: TGroupBox;
XInit: TGroupBox;
YInit: TGroupBox;
ZInit: TGroupBox;
AValue: TLabel; { Values displayed on the "INIT" page. }
BValue: TLabel;
CValue: TLabel;
DValue: TLabel;
EValue: TLabel;
FValue: TLabel;
GValue: TLabel;
HValue: TLabel;
IValue: TLabel;
JValue: TLabel;
KValue: TLabel;
LValue: TLabel;
MValue: TLabel;
NValue: TLabel;
OValue: TLabel;
PValue: TLabel;
QValue: TLabel;
RValue: TLabel;
SValue: TLabel;
TValue: TLabel;
UValue: TLabel;
VValue: TLabel;
WValue: TLabel;
XValue: TLabel;
YValue: TLabel;
ZValue: TLabel;
Exit: TButton; { Various buttons appearing on the forms. }
ExitBtn1: TButton;
ExitBtn: TButton;
AboutBtn1: TButton;
AboutBtn2: TButton;
AboutBtn3: TButton;
AddEqnBtn: TButton;
DeleteBtn: TButton;
EditBtn: TButton;
PrintBtn: TButton;
PrintBtn2: TButton;
PrintBtn3: TButton;
PulseBtn: TBitBtn;
PrintDialog: TPrintDialog;
InstabilityAnnunc: TPanel;
tt30: TPanel; { Squares in the truth table on the create page }
tt31: TPanel;
tt32: TPanel;
tt33: TPanel;
tt00: TPanel;
tt01: TPanel;
tt02: TPanel;
tt03: TPanel;
tt10: TPanel;
tt20: TPanel;
tt11: TPanel;
tt12: TPanel;
tt13: TPanel;
tt21: TPanel;
tt22: TPanel;
tt23: TPanel;
ctt00: TPanel;
ctt10: TPanel;
ctt20: TPanel;
ctt30: TPanel;
ctt01: TPanel;
ctt02: TPanel;
ctt03: TPanel;
ctt11: TPanel;
ctt12: TPanel;
ctt13: TPanel;
ctt21: TPanel;
ctt22: TPanel;
ctt23: TPanel;
ctt31: TPanel;
ctt32: TPanel;
ctt33: TPanel;
ba00: TLabel; {Labels on the truth table. }
ba01: TLabel;
ba10: TLabel;
ba11: TLabel;
dc00: TLabel;
DC01: TLabel;
DC10: TLabel;
DC11: TLabel;
dc211: TLabel;
dc210: TLabel;
dc201: TLabel;
dc200: TLabel;
RBrace1: TLabel;
ClkLbl1: TLabel;
RBrace2: TLabel;
ClkLbl2: TLabel;
InitInstrsLbl: TLabel;
Instrs2: TLabel;
ExecEqns: TMemo;
procedure COn(Sender: TObject);
procedure COff(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure AOff(Sender: TObject);
procedure AOn(Sender: TObject);
procedure BOff(Sender: TObject);
procedure BOn(Sender: TObject);
procedure DOff(Sender: TObject);
procedure DOn(Sender: TObject);
procedure ExitBtnClick(Sender: TObject);
procedure AboutBtn2Click(Sender: TObject);
procedure InitClick(Sender: TObject);
procedure AddEqnBtnClick(Sender: TObject);
procedure ValueClick(Sender: TObject);
procedure EqnListClick(Sender: TObject);
procedure EditBtnClick(Sender: TObject);
procedure EqnListDblClick(Sender: TObject);
procedure DeleteBtnClick(Sender: TObject);
procedure PrintBtnClick(Sender: TObject);
procedure ScreensChange(Sender: TObject; NewTab: Integer;
var AllowChange: Boolean);
procedure PulseBtnClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
{ TruthType- Data type that holds the binary information of a truth }
{ table. NumVars is the number of variables that provide }
{ indexes into this truth table. theVars[0..3] are the }
{ single character variable names used in this function. }
{ theVars[4] is where the function stores its result. }
{ tt[clk,d,c,b,a] is the actual truth table. }
TruthType = record
NumVars:integer;
theVars:array [0..4] of char;
tt:array [0..1,0..1,0..1,0..1,0..1] of integer;
end;
var
LogicEval: TLogicEval;
{ tt is an array of pointers to the panels that make up the squares }
{ of the truth table on the create page. It provides a convenient }
{ way to access those panels using array notation. }
tt: array [0..1, 0..1, 0..1, 0..1, 0..1] of TPanel;
{ TruthTbls holds the truth tables for each of the functions the }
{ user defines on the Create page. }
TruthTbls: array ['A'..'Z'] of TruthType;
{ EqnSet holds the set of variables that have been defined and for }
{ which truth tables exist. }
EqnSet: set of char;
implementation
{$R *.DFM}
uses EqnEntry;
type
variables= array ['@'..'['] of integer;
var
{ Values holds the current values of all the variables in the system }
{ Values2 is an array of pointers that point at the labels that }
{ display each of the values on the initialization page. }
Values: variables;
Values2: array ['@'..'['] of TLabel;
{ FormCreate does all the initialization when the program starts. }
procedure TLogicEval.FormCreate(Sender: TObject);
var i:integer;
ch:char;
begin
{ Label all the variables on the EXECUTE page. Also, set their }
{ values all to zero. }
for i := 1 to 26 do
begin
ExecVariables.Cells[i,0] := chr(ord('@') + i);
ExecVariables.Cells[i,1] := '0';
end;
ExecVariables.Cells[0,0] := '#';
ExecVariables.Cells[0,1] := '0';
{ Initialize all the variable values to zero. }
for ch := '@' to '[' do Values[ch] := 0;
{ At this point there are no equations defined. Note that here. }
EqnSet := [];
{ Initialize the array of Value pointers so they point at the }
{ value labels on the initialization page. }
Values2['A'] := AValue;
Values2['B'] := BValue;
Values2['C'] := CValue;
Values2['D'] := DValue;
Values2['E'] := EValue;
Values2['F'] := FValue;
Values2['G'] := GValue;
Values2['H'] := HValue;
Values2['I'] := IValue;
Values2['J'] := JValue;
Values2['K'] := KValue;
Values2['L'] := LValue;
Values2['M'] := MValue;
Values2['N'] := NValue;
Values2['O'] := OValue;
Values2['P'] := PValue;
Values2['Q'] := QValue;
Values2['R'] := RValue;
Values2['S'] := SValue;
Values2['T'] := TValue;
Values2['U'] := UValue;
Values2['V'] := VValue;
Values2['W'] := WValue;
Values2['X'] := XValue;
Values2['Y'] := YValue;
Values2['Z'] := ZValue;
{ Initialize the tt array so that each element points at the }
{ appropriate square on the truth table on the CREATE page. }
tt[0, 0,0,0,0]:= tt00;
tt[0, 0,0,0,1]:= tt01;
tt[0, 0,0,1,0]:= tt02;
tt[0, 0,0,1,1]:= tt03;
tt[0, 0,1,0,0]:= tt10;
tt[0, 0,1,0,1]:= tt11;
tt[0, 0,1,1,0]:= tt12;
tt[0, 0,1,1,1]:= tt13;
tt[0, 1,0,0,0]:= tt20;
tt[0, 1,0,0,1]:= tt21;
tt[0, 1,0,1,0]:= tt22;
tt[0, 1,0,1,1]:= tt23;
tt[0, 1,1,0,0]:= tt30;
tt[0, 1,1,0,1]:= tt31;
tt[0, 1,1,1,0]:= tt32;
tt[0, 1,1,1,1]:= tt33;
tt[1, 0,0,0,0]:= ctt00;
tt[1, 0,0,0,1]:= ctt01;
tt[1, 0,0,1,0]:= ctt02;
tt[1, 0,0,1,1]:= ctt03;
tt[1, 0,1,0,0]:= ctt10;
tt[1, 0,1,0,1]:= ctt11;
tt[1, 0,1,1,0]:= ctt12;
tt[1, 0,1,1,1]:= ctt13;
tt[1, 1,0,0,0]:= ctt20;
tt[1, 1,0,0,1]:= ctt21;
tt[1, 1,0,1,0]:= ctt22;
tt[1, 1,0,1,1]:= ctt23;
tt[1, 1,1,0,0]:= ctt30;
tt[1, 1,1,0,1]:= ctt31;
tt[1, 1,1,1,0]:= ctt32;
tt[1, 1,1,1,1]:= ctt33;
{ Initialize the default equation for the equation editor. }
LastEqn := 'F=0';
{ Make the CREATE page show up on the form when we start. }
Screens.ActivePage := 'Create';
end;
{ The following procedure stores "Value" into the variable specified by }
{ "vName". It also updates the variable display and any necessary LEDs }
{ on the EXECUTE page. }
procedure SetVar(vName:char; Value:integer);
begin
with LogicEval do begin
{ Convert "#" to "@" so we can use a compact array ("@" appears }
{ just before "A" in the ASCII character set, "#" is some distance }
{ away from the alphabetic characters). }
if (vName = '#') then vName := '@';
{ Update the value in the Values matrix and the label on the init- }
{ ialization page. }
Values [vName] := Value;
Values2[vName].Caption := chr(Value+ord('0'));
{ Update the value on the EXECUTE page. }
ExecVariables.Cells[ord(vName)-ord('@'),1] :=
chr(ord('0') + Value);
{ If this variable is E..K, then update the corresponding segment }
{ on the seven-segment display. }
if (vName = 'E') then
if (Value = 1) then E.Brush.Color := clRed
else E.Brush.Color := clSilver;
if (vName = 'F') then
if (Value = 1) then F.Brush.Color := clRed
else F.Brush.Color := clSilver;
if (vName = 'G') then
if (Value = 1) then G.Brush.Color := clRed
else G.Brush.Color := clSilver;
if (vName = 'H') then
if (Value = 1) then H.Brush.Color := clRed
else H.Brush.Color := clSilver;
if (vName = 'I') then
if (Value = 1) then I.Brush.Color := clRed
else I.Brush.Color := clSilver;
if (vName = 'J') then
if (Value = 1) then J.Brush.Color := clRed
else J.Brush.Color := clSilver;
if (vName = 'K') then
if (Value = 1) then K.Brush.Color := clRed
else K.Brush.Color := clSilver;
{ If this variable is W..Z, then update the corresponding LED. }
if (vName = 'W') then
if (Value = 1) then W.Brush.Color := clRed
else W.Brush.Color := clWhite;
if (vName = 'X') then
if (Value = 1) then X.Brush.Color := clRed
else X.Brush.Color := clWhite;
if (vName = 'Y') then
if (Value = 1) then Y.Brush.Color := clRed
else Y.Brush.Color := clWhite;
if (vName = 'Z') then
if (Value = 1) then Z.Brush.Color := clRed
else Z.Brush.Color := clWhite;
end;
end;
{ The following function evaluates the system of logic equations. }
procedure Eval;
var
i :integer;
funcCount :integer;
CurVar :char;
prevVals :variables;
{ CmpVars- Compares two arrays of type "variables" and returns }
{ true if they are equal. }
function CmpVars(var v1, v2:Variables):boolean;
assembler;
asm
push ds
les di, v2
lds si, v1
mov cx, 27
repe cmpsw
mov ax, 1
cmp cx, 0
je @@0
mov ax, 0
@@0:
pop ds
end;
{ EvalOnce- Evaluates all the active logic equations one time each. }
{ This function returns the number of active logic equa- }
{ tions in the system. Note that for a complete logic }
{ system evaluation, that is, to allow values to propogate}
{ throughout the system of equations, you must evaluate }
{ the set of equations at least n times where "n" is the }
{ number of logic equations in the system. }
function EvalOnce:integer;
var
funcName: char;
val: integer;
begin
Result := 0;
{ The following loop, in conjunction with the IF stmt, executes once }
{ for each active equation in the system. }
for funcName := 'A' to 'Z' do
if funcName in EqnSet then
begin
{ The following case statement handles the case where the current }
{ equation has zero, one, two, three, or four variables. It com- }
{ putes the new value by using a lookup based on the current vari-}
{ able values. }
case TruthTbls[funcName].NumVars of
0: val := TruthTbls[funcName].tt[Values['@'],0,0,0,0];
1: val := TruthTbls[funcName].tt[Values['@'],0,0,0,
Values[TruthTbls[funcName].theVars[0]]];
2: val := TruthTbls[funcName].tt[Values['@'],0,0,
Values[TruthTbls[funcName].theVars[1]],
Values[TruthTbls[funcName].theVars[0]]];
3: val := TruthTbls[funcName].tt[Values['@'],0,
Values[TruthTbls[funcName].theVars[2]],
Values[TruthTbls[funcName].theVars[1]],
Values[TruthTbls[funcName].theVars[0]]];
4: val := TruthTbls[funcName].tt[Values['@'],
Values[TruthTbls[funcName].theVars[3]],
Values[TruthTbls[funcName].theVars[2]],
Values[TruthTbls[funcName].theVars[1]],
Values[TruthTbls[funcName].theVars[0]]];
end;
{ Update the current function's variable with the value obtained above. }
Values[funcName] := val;
{ Count the number of functions we've processed down here. }
inc(Result);
end;
end;
begin {Eval}
{ Call EvalOnce to obtain the number of functions in the system. }
{ Bump this value by one since we've got the clock variable to worry}
{ about, as well. }
funcCount := EvalOnce + 1;
{ Evaluate the system of equations "funcCount" times. This gives }
{ us a total of "n+2" evaluations where "n" is the number of funcs. }
{ The first extra execution is for the clock variable, the second }
{ extra execution provides a safety margin. }
for i := 1 to funcCount do EvalOnce;
{ Now let's check for instability. Save the current set of values }
{ and execute the set of equations one more time. If the system is }
{ stable, we should obtain the exact same set of values. If the }
{ system is unstable, they will differ. }
prevVals := Values;
EvalOnce;
{ If the system is unstable, turn on the Instability annunciator. }
{ If the system is stable, turn the annunciator off. }
if CmpVars(prevVals,Values) then
LogicEval.InstabilityAnnunc.Color := clGray
else LogicEval.InstabilityAnnunc.Color := clRed;
{ Throughout the operations above, EvalOnce stored results directly }
{ into the Values array rather than (correctly) calling SetVar. The }
{ reason is because EvalOnce would call SetVar a tremendous number }
{ of times and SetVar is rather slow. Therefore, we need to call }
{ SetVar once for each variable to ensure that all LEDs, display }
{ values, etc., are properly updated. }
for CurVar := '@' to 'Z' do
SetVar(CurVar,Values[CurVar]);
end;
{ The following procedures handle button toggle events. Whenever the }
{ user toggles one of the four input values (A..D) the following pro- }
{ cedures adjust the values of the corresponding variables and then }
{ they evaluate the system of logic equations. }
procedure TLogicEval.AOff(Sender: TObject);
begin
SetVar('A',0);
Eval;
end;
procedure TLogicEval.AOn(Sender: TObject);
begin
SetVar('A',1);
Eval;
end;
procedure TLogicEval.BOff(Sender: TObject);
begin
SetVar('B',0);
Eval;
end;
procedure TLogicEval.BOn(Sender: TObject);
begin
SetVar('B',1);
Eval;
end;
procedure TLogicEval.COn(Sender: TObject);
begin
SetVar('C',1);
Eval;
end;
procedure TLogicEval.COff(Sender: TObject);
begin
SetVar('C',0);
Eval;
end;
procedure TLogicEval.DOff(Sender: TObject);
begin
SetVar('D',0);
Eval;
end;
procedure TLogicEval.DOn(Sender: TObject);
begin
SetVar('D',1);
Eval;
end;
{ If the user hits the "Pulse" button, set the clock to zero and eval- }
{ uate the system, set the clock to one and evaluate the system, and }
{ then set the clock back to zero and reevaluate the equations. }
procedure TLogicEval.PulseBtnClick(Sender: TObject);
begin
Values['@'] := 0;
Eval;
Values['@'] := 1;
Eval;
Values['@'] := 0;
Eval;
end;
{ The system calls the following functions whenever the user clicks on }
{ one of the buttons on the initialization page. This code figures }
{ out which button was pressed and toggles its value. The InitClick }
{ procedure runs whenever the user clicks on the panel (button), the }
{ ValueClick procedure runs if the users clicks on the actual value }
{ within the panel. }
procedure TLogicEval.InitClick(Sender: TObject);
var name:char;
begin
{The first character of the Box's Caption is the variable's name. }
name := (Sender as TGroupBox).Caption[1];
{ Fetch that variable's value, invert it, and write it back. }
SetVar(Name, Values[name] xor 1);
end;
procedure TLogicEval.ValueClick(Sender: TObject);
var name:char;
begin
name := ((Sender as TLabel).Parent as TGroupBox).Caption[1];
SetVar(Name, Values[name] xor 1);
end;
{ If the user presses the "ADD" button, bring up the equation dialog }
{ box and let them add a new equation to the system. }
procedure TLogicEval.AddEqnBtnClick(Sender: TObject);
begin
EqnDlg.InputEqn.SelectAll; {Select equation inside dialog box. }
EqnDlg.Show; {Open the equation dialog box. }
end;
{ If the user clicks on an equation in the equation list box, update }
{ the truth table on the create page to display the truth table for }
{ the selected equation. }
procedure TLogicEval.EqnListClick(Sender: TObject);
var ch:char;
begin
if (EqnList.ItemIndex <> -1) then {Make sure there is a selection. }
begin
ch := EqnList.Items[EqnList.ItemIndex][1]; {Get function name. }
DrawTruths(TruthTbls[ch]); {Draw that function's truth table. }
end;
end;
{ If the user double-clicks on an equation in the equation list box, }
{ then bring up the equation editor dialog box and let them edit this }
{ equation. }
procedure TLogicEval.EqnListDblClick(Sender: TObject);
begin
if (EqnList.ItemIndex <> -1) then {Is there a selection? }
begin
{ Grab the equation the user just selected and make it the }
{ default equation in the equation dialog box. }
EqnDlg.InputEqn.Text := EqnList.Items[EqnList.ItemIndex];
{ Select the equation. }
EqnDlg.InputEqn.SelectAll;
{ Bring up the equation editor dialog box. }
EqnDlg.Show;
end;
end;
{ If the user presses the edit button and there is a selected equation }
{ in the equation list box, open up the equation editor dialog box and }
{ let the user edit that equation. }
procedure TLogicEval.EditBtnClick(Sender: TObject);
begin
{ If there is a selected equation, copy it to the default }
{ equation in the equation editor dialog box. If there is no }
{ such equation available, just use the current default equa- }
{ tion when it opens the equation editor dialog box. }
if (EqnList.Items.Count <> 0) then {Are there any equations? }
if (EqnList.ItemIndex <> -1) then {Is an equation selected? }
EqnDlg.InputEqn.Text := EqnList.Items[EqnList.ItemIndex];
{ Select the equation and open up the dialog box. }
EqnDlg.InputEqn.SelectAll;
EqnDlg.Show;
end;
{ If the user has selected and equation and presses the delete button, }
{ the following procedure deletes that equation from the list and re- }
{ moves that function definition from the EqnSet. }
procedure TLogicEval.DeleteBtnClick(Sender: TObject);
var ch: char;
dummy: TruthType;
begin
if (EqnList.ItemIndex <> -1) then {Is there an equation selected? }
begin
{ Make sure the user really wants to do this. }
if MessageDlg('Okay to delete ' +
EqnList.Items[EqnList.ItemIndex] + '?',
mtWarning, [mbYes, mbNo], 0) = mrYes then
begin
{ Remove the equation from our equation set. }
EqnSet := EqnSet - [EqnList.Items[EqnList.ItemIndex][1]];
{ Remove the equation from the list of equations. }
EqnList.Items.Delete(EqnList.ItemIndex);
{ Since there is no longer a selected equation in the }
{ equation list, clear the truth tables. }
dummy.NumVars := 0;
DrawTruths(dummy);
end;
end;
end;
{ Miscellaneous procedures to handle the PRINT, ABOUT, and EXIT buttons }
{ found on each page of the form. }
procedure TLogicEval.PrintBtnClick(Sender: TObject);
begin
if (PrintDialog.Execute) then
begin
LogicEval.Print;
end;
end;
procedure TLogicEval.ExitBtnClick(Sender: TObject);
begin
Halt;
end;
procedure TLogicEval.AboutBtn2Click(Sender: TObject);
begin
AboutBox.Show;
end;
procedure TLogicEval.ScreensChange( Sender: TObject;
NewTab: Integer;
var AllowChange: Boolean);
var
i: integer;
begin
AllowChange := true;
if NewTab = 2 then
begin
ExecEqns.Clear;
for i := 0 to EqnList.Items.Count - 1 do
ExecEqns.Lines.Add(EqnList.Items[i]);
Eval;
end;
end;
end.