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.

1