Converting Main procedure in DCAL for DOS to DCAL for Delphi
Posted: Fri May 24, 2019 4:22 pm
Converting the Main procedure in a DCAL for DOS program to a DCAL for Delphi library
The Main procedure in most DCAL for DOS macros is the first procedure to be called and takes care of initializing global variables, setting up the main menu, establishing the initial method of input expected from the user, and what functions will be called based on that input.
Capturing the user's input and calling functions based on that input is typically handled within nested REPEAT loops. This flow of control is not compatible with DCAL for Delphi's re-entrant state machine structure. In particular, setting up the menus and capturing the user's input must occur in the Action Section separate from what functions are called based on that input, which must occur in the Again Section where the State Machine waits for user input.
If multiple input steps are necessary, such as asking for multiple points, or for a distance followed by an angle, nested repeat loops cannot be used. Case statements, defined within specific states, must be used to structure each input step separately. As the name implies, the State Machine uses various states to control the flow of your program. In DCAL for Delphi, these states are AlSize, AFirst, AAgain, and ALast.
AFirst: The Initialization Section
The tasks you include here will only be performed once. So, this is where you'll initialize variables and display the macro's description on the error line. Most importantly, this is where you'll set the initial state of your macro, typically l.state := 1, the first case in the Action Section.
AlSize: The Action Section
This is where memory for your local variable record will be allocated on the memory stack and DataCAD's dispatcher will be invoked. You'll define this 'L' record (a.k.a. Local Variable Record) to carry the state, other variables, and user-input argument results. Here is the L record for the Arrow macro.
This record includes an integer variable for the state. Every L record must have this variable. The other variables are specific to the arrow macro such as the end points and the width of the arrow. This macro includes two input arguments. getp for capturing user points (or menu keys), and getd for getting a distance value (or menu keys). At a minimum, a case for gete: getescarg must be declared to capture menu keys.
You can setup your main menu in case 1 of the Action Section. For example:
Then you'll prompt the user for what they should do when they first run your macro. So, just like DOS, lblson is usually followed by a prompt like wrtmsg('Enter first point of arrow.'). The Arrow macro requires two points to be entered by the user one after the other. In DOS, the first point is captured in the first REPEAT loop and the second point is captured in a nested REPEAT loop. In Delphi, the first point will be captured in Action Case 1. The second point could be captured in Action Case 2, but we've created a variable numpnt in the L record to keep track of which point is to be entered. This way, we can return to Action Case 1 and capture the second point after the first point has be defined.
In DOS, the fist point is captured using the getpoint function. For example:
In Delphi, the fist point is also captured using the getpoint function, but the result is stored in the getpoint argument l.getp. For example:
Once the point is captured (or a menu key is pressed) in Action 1, the Main DCAL function returns to AAgain 1. This is where you'll handle the results of the user's input.
Then, based on the user's action, we'll either go to a different Action State like Arrow Width, or if a point has been captured, we'll store the point(s) in the local record, add the arrow, then return to Action 1.
ALast: The Interrupt Section
When the user is at the main menu, they can select F0: Exit or right-click to exit the macro. In this case, l.state is set to 0 and the value returned to the Main function is XDone which tells DataCAD to close the macro. If, however, the user presses a hot key to 'jump out' of your macro, DataCAD will set the state to ALast where you'll have an opportunity to clean up any temporary data your program may have created.
I hope this helps clear up the relationship between a DCAL for DOS macro and a DCAL for Delphi macro so you might be encouraged to convert and update some of your existing macros. There are a number of benefits including much greater speed of execution, support for double-precision floating point math, and taking advantage of anything the Delphi and Windows development environments have to offer.
The Main procedure in most DCAL for DOS macros is the first procedure to be called and takes care of initializing global variables, setting up the main menu, establishing the initial method of input expected from the user, and what functions will be called based on that input.
Capturing the user's input and calling functions based on that input is typically handled within nested REPEAT loops. This flow of control is not compatible with DCAL for Delphi's re-entrant state machine structure. In particular, setting up the menus and capturing the user's input must occur in the Action Section separate from what functions are called based on that input, which must occur in the Again Section where the State Machine waits for user input.
If multiple input steps are necessary, such as asking for multiple points, or for a distance followed by an angle, nested repeat loops cannot be used. Case statements, defined within specific states, must be used to structure each input step separately. As the name implies, the State Machine uses various states to control the flow of your program. In DCAL for Delphi, these states are AlSize, AFirst, AAgain, and ALast.
AFirst: The Initialization Section
The tasks you include here will only be performed once. So, this is where you'll initialize variables and display the macro's description on the error line. Most importantly, this is where you'll set the initial state of your macro, typically l.state := 1, the first case in the Action Section.
AlSize: The Action Section
This is where memory for your local variable record will be allocated on the memory stack and DataCAD's dispatcher will be invoked. You'll define this 'L' record (a.k.a. Local Variable Record) to carry the state, other variables, and user-input argument results. Here is the L record for the Arrow macro.
Code: Select all
type
ArrowL = record
state: asint;
pnt: array [1 .. 2] of point;
numpnt: asint;
arrwidth: afloat; { the width of the arrow }
case byte of
0: (getp: getpointarg);
1: (getd: getdisarg);
end;
This record includes an integer variable for the state. Every L record must have this variable. The other variables are specific to the arrow macro such as the end points and the width of the arrow. This macro includes two input arguments. getp for capturing user points (or menu keys), and getd for getting a distance value (or menu keys). At a minimum, a case for gete: getescarg must be declared to capture menu keys.
You can setup your main menu in case 1 of the Action Section. For example:
Code: Select all
case l.state of
1: begin
wrtlvl('Arrow'); { Set Menu Title }
lblsinit; { Initialize function key labels }
lblset(1, 'Width'); { Set function key label }
lblset(20, 'Exit'); { Set function key label }
lblson; { Turn on the function keys }
Then you'll prompt the user for what they should do when they first run your macro. So, just like DOS, lblson is usually followed by a prompt like wrtmsg('Enter first point of arrow.'). The Arrow macro requires two points to be entered by the user one after the other. In DOS, the first point is captured in the first REPEAT loop and the second point is captured in a nested REPEAT loop. In Delphi, the first point will be captured in Action Case 1. The second point could be captured in Action Case 2, but we've created a variable numpnt in the L record to keep track of which point is to be entered. This way, we can return to Action Case 1 and capture the second point after the first point has be defined.
In DOS, the fist point is captured using the getpoint function. For example:
Code: Select all
result := getpoint (pnt1, key);
In Delphi, the fist point is also captured using the getpoint function, but the result is stored in the getpoint argument l.getp. For example:
Code: Select all
getpoint (l.getp, retval);
Once the point is captured (or a menu key is pressed) in Action 1, the Main DCAL function returns to AAgain 1. This is where you'll handle the results of the user's input.
Code: Select all
AAgain: The Re-entrant Section – Return to menus and wait for input[/b]
In AAgain 1, we look at the results of the user's input in Action 1. For example:
case l.state of
1: begin
if l.getp.Result = res_escape then begin { User clicked a menu }
case l.getp.key of
f1: l.state := 2; { Width – Go to Action 2 }
s0: l.state := 0; { Exit – Go to Action 0 XDone }
end;
end
else if l.getp.Result = res_normal then begin { User clicked a point }
l.pnt[l.numpnt] := l.getp.curs;
if l.numpnt = 1 then begin
l.numpnt := 2;
end
else begin { Done getting points, add arrow }
addarrow(l.pnt[1], l.pnt[2], l.arrwidth, l.isfill, l.fillcolor);
l.numpnt := 1; { Finished adding arrow, reset numpnt to 1 }
end;
end;
end;
2: l.state := 1; { Finished getting arrow width, go to main menu }
else l.state := 0; { Exit - Go to Action 0 XDone }
end;
Then, based on the user's action, we'll either go to a different Action State like Arrow Width, or if a point has been captured, we'll store the point(s) in the local record, add the arrow, then return to Action 1.
ALast: The Interrupt Section
When the user is at the main menu, they can select F0: Exit or right-click to exit the macro. In this case, l.state is set to 0 and the value returned to the Main function is XDone which tells DataCAD to close the macro. If, however, the user presses a hot key to 'jump out' of your macro, DataCAD will set the state to ALast where you'll have an opportunity to clean up any temporary data your program may have created.
I hope this helps clear up the relationship between a DCAL for DOS macro and a DCAL for Delphi macro so you might be encouraged to convert and update some of your existing macros. There are a number of benefits including much greater speed of execution, support for double-precision floating point math, and taking advantage of anything the Delphi and Windows development environments have to offer.