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. |