|
VxDemo: Demonstrates How to Use VX.EXE
{$A+}
{$G+}
{$M 2500, 0, 0}
program VxDemo;
uses
VxUtils;
begin
(*
This is an interactive demo program for VX.EXE. Read it over
carefully until you are sure you understand each step.
To run the demo, load VX.EXE into memory, then load this demo
program into Borland's TD.EXE or TD286.EXE. (TD286 is preferred
since it leaves more base 640k ram for your program.)
Breakpoints are used to show the results at each step.
To set a breakpoint in the debugger, place the cursor on the desired
line and press F2.
When you are ready to proceed, press F9. The program will stop at
the desired breakpoint, and you can view the results.
If you need to take a break, just remember the last breakpoint you
tested, and start there next time.
Step 1: The Interapplication Control Area (ICA)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Two routines are provided to pass values in the ICA. The first uses
a 32-bit linear address. The routine is:
ICALong(marker, address);
The desired marker, 1 to 4, is selected with the "marker" variable.
The address is a 32-bit longint, which can point anywhere in memory.
The second routine uses a pointer, which is limited to the first meg
of ram:
ICAPtr(marker, pointer);
The "@" sign can be used to load the address of an identifier:
ICAPtr(marker, @Arry);
We demonstrate loading the ICA, at $40:F0. Since this is a fixed
address, we use the longint version.
Set a breakpoint at "BreakPoint #1" below.
When you are ready, press F9.
*)
ClearICA; {clear any previous markers}
ICALong(1, $4F0); {load Marker F1 to the start of the ICA}
BreakPoint; {BreakPoint #1}
(*
Now, activate VX by pressing Ctrl, and Left-Shift.
When VX comes up, press F1. You should see the following line:
000004F0 : F0 04 00 00 FF FF FF FF - FF FF FF FF FF FF FF FF ð
Hex Addr <Marker #1> <Marker #2> <Marker #3> <Marker #4>
The first hex character, "F0" and the ASCII character "ð", should be
hilited in black with yellow text.
This is the address of the ICA in DOS. Marker F1 was set to address
$40:F0, and markers F2..F4 have been cleared by setting them to
$FFFFFFFF.
Next, we demonstrate arrays.
Step 2: Viewing Arrays
~~~~~~~~~~~~~~~~~~~~~~
We use an array named "Arry" which holds 16,200 longints. This
requires
4 * 16,200 = 64,800 bytes
You might expect the maximum size would be 65,536 bytes, but it is
reduced by other program variables that share the Data Segment along
with the array.
We start by filling the array with "0", then load the first item
with the hex value $74736554. (This will show up as "Test" when we
view the memory.)
Set a breakpoint on BreakPoint #2 below.
When you are ready, press F9.
*)
FillChar(Arry, SizeOf(Arry), #0); {clear array}
Arry[1] := $74736554; {load the word "Test" in hex}
ICAPtr(2, @Arry[1]); {set Marker F2 to the first item in the array}
BreakPoint; {BreakPoint #2}
(*
Now, activate VX by pressing Ctrl, and Left-Shift.
When VX comes up, press F2.
You should see the following line (your address column may be different)
00030690 : 54 65 73 74 00 00 00 00 - 00 00 00 00 00 00 00 00 Test
This is the first item the longint array, "Arry"
The hex byte, "54", and the letter "T" should be hilited in red with
yellow text.
You can press F1 and F2 to switch between the two markers.
Step 3: Allocating DOS Memory
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The 64k barrier is a chronic problem in Pascal. Suppose we need more
than 16,200 longints? Suppose we need as many as possible?
The textbook response is to use pointers and linked lists - but
a pointer takes up as much room as a longint. This wastes space.
However, if we look at an array, we find it is very much like a
linked list. If we know the location of any item in the array, we
can easily calculate the location of any other item. We don't need
pointers!
We can break the 64k barrier by simply allocating all of DOS memory
and using it however we want.
We now use the procedure "GetArrayMem" to allocate memory and set
some global variables.
Then we clear the previous markers, and set Marker F1 to the start
of memory, and F2 to the end of memory.
Set a breakpoint at "BreakPoint #3" below.
When you are ready, press F9.
*)
GetArrayMem; {get all available DOS memory}
if (MaxItem < 20000) then {check for a reasonable amount of space}
begin
writeln('Huston, we have a problem'); {we forgot to release memory last time}
FreeArrayMem; {give back whatever we took}
halt; {we have to reboot}
end;
ICALong(1, BaseMem); {load Marker F1 to start of memory}
ICALong(2, BaseMem + MaxMem); {load Marker F2 to end of memory}
BreakPoint; {BreakPoint #3}
(*
Place a Watch on "MaxItem" above to see how many longints you can
store.
Remember, TD286 takes about 79k, and VX uses about 15k. Plus, we
have room for another 16,200 longints in the array "Arry".
So if we got rid of the array, and ran this program in plain DOS,
there would be room for about 40,000 more longints.
If you activate VX and look between markers F1 and F2, you will see
whatever was loaded in memory before running this program. It looks
messy, but never mind, we will clean it up by writing over it.
Step 4: Writing to DOS Memory
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now, we load the first 4 entries in memory with the word "Test".
To speed access, we use the following global variables:
Variable Definition
Basemem Start of memory {set in GetArrayMem}
MaxItem Max number of longints {calculated in GetArrayMem}
Index Index to our huge array {we set this value in our program}
LNum Value we wish to store {we set this value in our program}
We use the procedure "WriteLong" to store the data.
Set a breakpoint at "BreakPoint #4" below.
When you are ready, press F9.
*)
LNum := $74736554; {load the word "Test" in hex}
for Index := 0 to 4 do {load the first 4 locations in memory}
WriteLong;
BreakPoint; {BreakPoint #4}
(*
Now activate VX and press F1. You should see the word "Test"
repeated 4 times as shown below (your address will be different):
00040DD0 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
Step 5: Filling DOS Memory
~~~~~~~~~~~~~~~~~~~~~~~~~~
We can fill memory and see how long it takes.
Set a breakpoint at "BreakPoint #5" below.
When you are ready, press F9.
*)
Index := 5; {start at next memory location}
repeat
WriteLong; {LNum is still set to $74736554}
Index := Index + 1; {get next entry}
until (Index > MaxItem); {check for end of DOS array}
BreakPoint; {BreakPoint #5}
(*
That didn't take long, did it!
Now activate VX and check memory between markers F1 and F2.
You should see the same thing everywhere between the two markers.
00040DD0 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040DE0 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040DF0 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E00 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E10 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E20 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E30 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E40 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E50 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E60 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E70 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E80 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040E90 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040EA0 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
00040EB0 : 54 65 73 74 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTestTest
Step 6: BASM Code
~~~~~~~~~~~~~~~~~
Now for a little change of pace.
Assembly code is often used to increase performance, or accomplish
functions that may be difficult to do in Pascal.
We can take advantage of the 32-bit registers in modern cpu's to
speed up the code.
TD286 is an excellent debugger, but switching it into 32-bit mode
each time you need to debug a snippet of code gets tedious. There
is, to my knowledge, no method to set a config. parameter to keep it
in 32-bit mode.
However, VX has an easy way to do this: Function F6.
Set a breakpoint on the "ReadLong; BreakPoint #6" line below.
When you are ready, press F9.
*)
Index := 0; {point to first item in DOS array}
LNum := 0; {we want to set it to zero}
WriteLong; {make it zero}
LNum := - 1; {set LNum to $FFFFFFFF to show we can read mem}
LNum := ReadLong; {BreakPoint #6}
(*
Activate VX, and press F1. You should see the following line (your
address will be different):
00041390 : 00 00 00 00 54 65 73 74 - 54 65 73 74 54 65 73 74 TestTestTest
We have written a zero to the first item in our DOS array.
Now, while you are still in VX, press F6. You will return here
immediately.
Press F7 to trace into the call to "ReadLong", then press the TD286
function F6 to switch to CPU mode.
The display should switch to a full-screen CPU View, and you should
see the following assembly code. (If you are viewing this in a
browser, the funny characters are line-drawing characters used in
the ASCII display.)
ð File Edit View Run Breakpoints Data Options Window Help READY
ÉÍ[þ]ÍCPU 80486ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍds:FD48 = 00000000ÑÍÍÍÍÍÍÍ3ÍÍÍÍ[ ]Í»
ºVXUTILS.READLONG: db $66; mov cx, word ptr Index {where to p eax 00000000³c=0º
º cs:0161 668B0E48FD mov ecx,[VXUTILS.INDEX] þebx 00040000³z=1º
ºVXUTILS.289: db $66; mov bx, word ptr Basemem {get start of ±ecx 00004139³s=0º
º cs:0166 668B1E0200 mov ebx,[VXUTILS.BASEMEM] ±edx 00000001³o=0º
ºVXUTILS.291: db $66; shl cx, 2 {longint is 4 bytes} ±esi 00000040³p=1º
º cs:016B 66C1E102 shl ecx,02 ±edi 00000060³a=0º
ºVXUTILS.292: db $66; add bx, cx {get 32-bit address} ±ebp 000009C2³i=1º
º cs:016F 6601CB add ebx,ecx ±esp 000009A4³d=0º
ºVXUTILS.294: db $66; mov cx, bx {copy so we can convert to p± ds 309D ³ º
º cs:0172 6689D9 mov ecx,ebx ± es 4139 ³ º
ºVXUTILS.295: db $66; shr cx, 4 {div 16 to get segment value}± fs 0000 ³ º
º cs:0175 66C1E904 shr ecx,04 ± gs 0000 ³ º
ºVXUTILS.297: and bx, $0F {get offset} ± ss 4097 ³ º
º cs:0179 81E30F00 and bx,000F ± cs 2FCC ³ º
ºVXUTILS.298: mov es, cx {load segment register} ± ip 0161 ³ º
º cs:017D 8EC1 mov es,cx ±ÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄĶ
ºVXUTILS.299: db $66; mov ax, es:[bx] {get value in DOS ram} ± ss:09DE 2073 º
º cs:017F 66268B07 mov eax,es:[bx] ± ss:09DC 2765 º
ºVXUTILS.301: db $66; mov dx, ax {convert to longint format} ± ss:09DA 7265 º
º cs:0183 6689C2 mov edx,eax ± ss:09D8 6874 º
ºVXUTILS.302: db $66; shr dx, 16 {convert to longint format} ± ss:09D6 2020 º
º cs:0186 66C1EA10 shr edx,10 ± ss:09D4 2020 º
ºVXUTILS.303: end; ± ss:09D2 0A6B º
º cs:018A CB retf ± ss:09D0 6E69 º
This is how we read an item stored in our DOS array.
Press F8 to single-step through the code until you hit the "retf".
Return to the Code Pane by pressing F6 twice.
Press F8, and you should be back here.
We are almost done.
Step 7: Restoring DOS ram
~~~~~~~~~~~~~~~~~~~~~~~~~
Before we leave the program, we should release the memory back to
DOS. Set a breakpoint on breakpoint #7.
When you are ready, press F9.
*)
FreeArrayMem; {give memory back to DOS before we leave}
BreakPoint; {BreakPoint #7}
(*
Summary
~~~~~~~
Pointers are not needed to address memory in Pascal. The largest
arrays are obtained by simply allocating all available DOS memory,
then using a Base Address and Offset to locate an item in the array.
A simple extension to this approach can be used to address any item
in a database of fixed-length records. Variable-length records are
more complicated, and may require a separate list showing the start
of each item and the length of the item.
Routines to perform data management, such as deleting records and
sorting the database, are not difficult to write, but they must be
tailored to the type of data used in the database.
Developing these routines is much easier when you can view the
contents of memory, and can verify the routines are reading and
writing to the correct location.
VX.EXE can assist program development by automatically displaying
the contents of memory using simple debug statements.
Step 8: Coffee Break
~~~~~~~~~~~~~~~~~~~~
That's it for now. Thanks for your time and effort in working though
this strange demo, and I hope it was worth it. Meanwhile, I think
there's some coffee and donuts in the cafeteria for you.
If you have any problems with the information covered here, please
contact me at
Michael R. Monett
add.automation@sympatico.ca
*)
end.
|