15.3 Programming in DelphiKylix and HLA


15.3 Programming in Delphi/Kylix and HLA

Delphi is a marvelous language for writing Win32 GUI-based applications. Kylix is the companion product that runs under Linux. Their support for Rapid Application Design (RAD) and visual programming is superior to almost every other Windows or Linux programming approach available. However, being Pascal based, there are some things that just cannot be done in Delphi/Kylix and many things that cannot be done as efficiently in Delphi/Kylix as in assembly language. Fortunately, Delphi/Kylix lets you call assembly language procedures and functions so you can overcome their limitations.

Delphi/Kylix provides two ways to use assembly language in the Pascal code: via a built-in assembler (BASM) or by linking in separately compiled assembly language modules. The built-in Borland Assembler (BASM) is a very weak Intelsyntax assembler. It is suitable for injecting a few instructions into your Pascal source code or perhaps writing a very short assembly language function or procedure. It is not suitable for serious assembly language programming. If you know Intel syntax and you only need to execute a few machine instructions, then BASM is perfect. However, because this is a text on assembly language programming, the assumption here is that you want to write some serious assembly code to link with your Pascal/Kylix code. To do that, you will need to write the assembly code and compile it with a different assembler (e.g., HLA) and link the code into your Delphi/Kylix application. That is the approach this section will concentrate on. For more information about BASM, check out the Delphi/Kylix documentation.

Before we get started discussing how to write HLA modules for your Delphi/Kylix programs, you must understand two very important facts:

  • HLA's exception handling facilities are not directly compatible with Delphi/Kylix's. This means that you cannot use the try..endtry and raise statements in the HLA code you intend to link to a Delphi/Kylix program. This also means that you cannot call library functions that contain such statements. Because the HLA Standard Library modules use exception handling statements all over the place, this effectively prevents you from calling HLA Standard Library routines from the code you intend to link with Delphi/Kylix.[3]

  • Although you can write console applications with Delphi/Kylix, 99 percent of Delphi/Kylix applications are GUI applications. You cannot call consolerelated functions (e.g., stdin.xxxx or stdout.xxxx) from a GUI application. Even if HLA's console and standard input/output routines didn't use exception handling, you wouldn't be able to call them from a standard Delphi/Kylix application.

Given the rich set of language features that Delphi/Kylix supports, it should come as no surprise that the interface between Delphi/Kylix's Object Pascal language and assembly language is somewhat complex. Fortunately there are two facts that reduce this problem. First, HLA uses many of the same calling conventions as Pascal; so much of the complexity is hidden from sight by HLA. Second, the other complex stuff you won't use very often, so you may not have to bother with it.

Note

The following sections assume you are already familiar with Delphi/Kylix programming. They make no attempt to explain Delphi/Kylix syntax or features other than as needed to explain the Delphi/Kylix assembly language interface. If you're not familiar with Delphi/Kylix, you will probably want to skip this section.

15.3.1 Linking HLA Modules with Delphi/Kylix Programs

The basic unit of interface between a Delphi/Kylix program and assembly code is the procedure or function. That is, to combine code between the two languages you will write procedures in HLA (that correspond to procedures or functions in Delphi/Kylix) and call these procedures from the Delphi/Kylix program. Of course, there are a few mechanical details you've got to worry about. The following section will cover those.

To begin with, when writing HLA code to link with a Delphi/Kylix program you've got to place your HLA code in an HLA unit. An HLA program module contains start-up code and other information that the operating system uses to determine where to begin program execution when it loads an executable file from disk. However, the Delphi/Kylix program also supplies this information and specifying two starting addresses confuses the linker; therefore, you must place all your HLA code in a unit rather than a program module.

Within the HLA unit you must create @external procedure prototypes for each procedure you wish to call from Delphi/Kylix. If you prefer, you can put these prototype declarations in a header file and #include them in the HLA code, but because you'll probably only reference these declarations from this single file, it's okay to put the @external prototype declarations directly in the HLA unit module. These @external prototype declarations tell HLA that the associated functions will be public so that Delphi/Kylix can access their names during the link process. Here's a typical example:

 unit LinkWithKylix;      procedure prototype; @external;      procedure prototype;      begin prototype;           << Code to implement prototype's functionality >>      end prototype; end LinkWithKylix; 

After creating the module above, you'd compile it using HLA's "-s" (compile to assembly only) command line option. This will produce an ASM file. Were this just about any other language, under Windows you'd then assemble the ASM file with MASM. Unfortunately, Delphi doesn't like the OBJ files that MASM produces. For all but the most trivial of assembly modules, Delphi will reject MASM's output. Borland Delphi expects external assembly modules to be written with Borland's assembler, tasm32.exe (the 32-bit Turbo Assembler). Fortunately, because HLA 1.26, HLA provides an option to produce TASM output that is compatible with TASM 5.3 and later. Unfortunately, Borland doesn't really sell TASM anymore; the only way to get a copy of TASM 5.3 is to obtain a copy of Borland's C++ Builder Professional system, which includes TASM32 5.3. If you don't own Borland C++ and really have no interest in using C++ Builder, Borland has produced an evaluation disk for C++ Builder that includes TASM 5.3, so order the evaluation disk to get a copy of TASM 5.3. Note that earlier versions of TASM32 (e.g., 5.0) do not support MMX and other Pentium-only instructions. You really need TASM 5.3 if you want to link HLA programs containing such code with Delphi programs.

Here is the Windows command that will compile and assemble the module given earlier:

 hla -c -tasm -o:omf LinkWithDelphi.hla 

Of course, if you don't like typing this long command to compile and assemble your HLA code, you can always create a make file or a batch file that will let you do both operations with a single command.

After creating the module above, you'd compile it using HLA's "-c" (compile to object only) command line option. This will produce an object (".obj") file.

Linux/Kylix users have it a little easier. Under Linux, Borland's Kylix program handles ELF object code (the stuff Gas emits) just fine. Therefore, the following is all you need to do when compiling an HLA program to link with Kylix:

 hla -c LinkWithKylix.hla 

Once you've created the HLA code and compiled it to an object file, the next step is to tell Delphi/Kylix that it needs to call the HLA/assembly code. There are two steps needed to achieve this: You've got to inform Delphi/Kylix that a procedure (or function) is written in assembly language (rather than Pascal), and you've got to tell Delphi/Kylix to link in the object file you've created when compiling the Delphi/Kylix code.

The second step above, telling Delphi/Kylix to include the HLA object module, is the easiest task to achieve. All you've got to do is insert a compiler directive of the form "{$L objectFileName.obj }" in the Delphi/Kylix program before declaring and calling your object module. A good place to put this is after the implementation reserved word in the module that calls your assembly procedure. The code examples a little later in this section will demonstrate this.

The next step is to tell Delphi/Kylix that you're supplying an external procedure or function. This is done using the Delphi/Kylix EXTERNAL directive on a procedure or function prototype. For example, a typical external declaration for the prototype procedure appearing earlier is

      procedure prototype; external; // This may look like HLA code, but it's                                     // really Kylix code! 

As you can see here, Delphi/Kylix's syntax for declaring external procedures is nearly identical to HLA's. This is not an accident; much of HLA's syntax was borrowed directly from Pascal.

The next step is to call the assembly procedure from the Delphi/Kylix code. This is easily accomplished using standard Pascal procedure calling syntax. Listings 15-5 and 15-6, respectively, provide a complete, working example of an HLA procedure that a Delphi/Kylix program can call. This program doesn't accomplish very much other than to demonstrate how to link in an assembly procedure. The Delphi/Kylix program contains a form with a single button on it. Pushing the button calls the HLA procedure, whose body is empty and therefore returns immediately to the Delphi/Kylix code without any visible indication that it was ever called. Nevertheless, this code does provide all the syntactical elements necessary to create and call an assembly language routine from a Delphi/Kylix program.

Listing 15-5: CalledFromDelphi.HLA Module Containing the Assembly Code.

start example
 unit LinkWithDelphi; // Or LinkWithKylix for Linux users.      procedure CalledFromDelphi; @external;      procedure CalledFromDelphi;      begin CalledFromDelphi;      end CalledFromDelphi; end LinkWithDelphi; 
end example

Listing 15-6: DelphiEx1: Delphi Source Code That Calls an Assembly Procedure.

start example
 unit DelphiEx1; interface uses   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,   StdCtrls; type   TDelphiEx1Form = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);   private     { Private declarations }   public     { Public declarations }   end; var   DelphiEx1Form: TDelphiEx1Form; implementation {$R *.DFM} {$L CalledFromDelphi.obj } procedure CalledFromDelphi; external; procedure TDelphiEx1Form.Button1Click(Sender: TObject); begin      CalledFromDelphi(); end; end. 
end example

The full Delphi (for Windows), Kylix (for Linux), and HLA source code for the programs appearing in Listings 15-5 and 15-6 appear on the accompanying CD-ROM. If you've got a copy of Delphi/Kylix 5 or later, you might want to load this module and try compiling it. To compile the HLA code for this example, you would use the following commands from the command prompt:

 hla -tasm -c -o:omf -tasm CalledFromDelphi.hla (for Windows users) hla -c CalledFromKylix.hla (for Linux users) 

After producing the CalledFromDelphi or CalledFromKylix object module (depending on your OS) with the appropriate command, you'd enter the Delphi/Kylix Integrated Development Environment and tell it to compile the DelphiEx1/KylixEx1 code (i.e., you'd load the DelphiEx1Project/KylixEx1Project file into Delphi/Kylix and the compile the code). This process automatically links in the HLA code, and when you run the program you can call the assembly code by simply pressing the single button on the Delphi/Kylix form.

15.3.2 Register Preservation

Delphi/Kylix code expects all procedures to preserve the EBX, ESI, EDI, and EBP registers. Routines written in assembly language may freely modify the contents of EAX, ECX, and EDX without preserving their values. The HLA code will have to modify the ESP register to remove the activation record (and, possibly, some parameters). Of course, HLA procedures (unless you specify the @noframe option) automatically preserve and set up EBP for you, so you don't have to worry about preserving this register's value; of course, you will not usually manipulate EBP's value because it points at your procedure's parameters and local variables.

Although you can modify EAX, ECX, and EDX to your heart's content and not have to worry about preserving their values, don't get the idea that these registers are available for your procedure's exclusive use. In particular, Delphi/Kylix may pass parameters into a procedure within these registers and you may need to return function results in some of these registers. Details on the further use of these registers appear in later sections of this chapter.

Whenever Delphi/Kylix calls a procedure, that procedure can assume that the direction flag is clear. On return, all procedures must ensure that the direction flag is still clear. So if you manipulate the direction flag in your assembly code (or call a routine that might set the direction flag), be sure to clear the direction flag before returning to the Kylix code.

If you use any MMX instructions within your assembly code, be sure to execute the emms instruction before returning. Delphi/Kylix code assumes that it can manipulate the floating point stack without running into problems.

Although the Delphi/Kylix documentation doesn't explicitly state this, experiments with Delphi/Kylix code seem to suggest that you don't have to preserve the FPU (or MMX) registers across a procedure call other than to ensure that you're in FPU mode (versus MMX mode) upon return to Delphi/Kylix.

15.3.3 Function Results

Delphi/Kylix generally expects functions to return their results in a register. For ordinal return results, a function should return a byte value in AL, a word value in AX, or a double word value in EAX. Functions return pointer values in EAX. Functions return real values in ST0 on the FPU stack. The code example in this section demonstrates each of these parameter return locations.

For other return types (e.g., arrays, sets, records, and so on), Delphi/Kylix generally passes an extra reference (var) parameter containing the address of the location where the function should store the return result. We will not consider such return results in this text; see the Delphi/Kylix documentation for more details.

The Delphi/HLA program in Listings 15-7 through 15-12, respectively, demonstrates how to return different types of scalar (ordinal and real) parameters to a Delphi program from an assembly language function. The HLA functions return boolean (one-byte) results, word results, double word results, a pointer (PChar) result, and a floating point result when you press an appropriate button on the form. See the DelphiEx2/KylixEx2 example code on the CD-ROM for the full project. (Linux users: The Kylix code is slightly different, but the functionality is the same; there is no difference between the way Delphi and Kylix interface with assembly language.)

Note that the following code doesn't really do anything useful other than demonstrate how to return function results in EAX and ST0.

Listing 15-7: DelphiEx2: Pascal Code for Assembly Return Results Example.

start example
 unit DelphiEx2; interface uses   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,   StdCtrls; type   TDelphiEx2Form = class(TForm)     BoolBtn: TButton;     BooleanLabel: TLabel;     WordBtn: TButton;     WordLabel: TLabel;     DWordBtn: TButton;     DWordLabel: TLabel;     PtrBtn: TButton;     PCharLabel: TLabel;     FltBtn: TButton;     RealLabel: TLabel;     procedure BoolBtnClick(Sender: TObject);     procedure WordBtnClick(Sender: TObject);     procedure DWordBtnClick(Sender: TObject);     procedure PtrBtnClick(Sender: TObject);     procedure FltBtnClick(Sender: TObject);   private     { Private declarations }   public     { Public declarations }   end; var   DelphiEx2Form: TDelphiEx2Form; implementation {$R *.DFM} // Here are the directives that tell Delphi to link in our // HLA code. {$L ReturnBoolean.obj } {$L ReturnWord.obj } {$L ReturnDWord.obj } {$L ReturnPtr.obj } {$L ReturnReal.obj } // Here are the external function declarations: function ReturnBoolean:boolean; external; function ReturnWord:smallint; external; function ReturnDWord:integer; external; function ReturnPtr:pchar; external; function ReturnReal:real; external; // Demonstration of calling an assembly language // procedure that returns a byte (boolean) result. procedure TDelphiEx2Form.BoolBtnClick(Sender: TObject); var     b:boolean; begin     // Call the assembly code and return its result:     b := ReturnBoolean;     // Display "true" or "false" depending on the return result.     if( b ) then         booleanLabel.caption := 'Boolean result = true '     else         BooleanLabel.caption := 'Boolean result = false'; end; // Demonstrate calling an assembly language function that // returns a word result. procedure TDelphiEx2Form.WordBtnClick(Sender: TObject); var     si:smallint;   // Return result here.     strVal:string; // Used to display return result. begin     si := ReturnWord();     // Get result from assembly code.     str( si, strVal );      // Convert result to a string.     WordLabel.caption := 'Word Result = ' + strVal; end; // Demonstration of a call to an assembly language routine // that returns a 32-bit result in EAX: procedure TDelphiEx2Form.DWordBtnClick(Sender: TObject); var     i:integer;         // Return result goes here.     strVal:string;     // Used to display return result. begin     i := ReturnDWord(); // Get result from assembly code.     str( i, strVal );  // Convert that value to a string.     DWordLabel.caption := 'Double Word Result = ' + strVal; end; // Demonstration of a routine that returns a pointer // as the function result. This demo is kind of lame // because we can't initialize anything inside the // assembly module, but it does demonstrate the mechanism // even if this example isn't very practical. procedure TDelphiEx2Form.PtrBtnClick(Sender: TObject); var     p:pchar;    // Put returned pointer here. begin     // Get the pointer (to a zero byte) from the assembly code.     p := ReturnPtr();     // Display the empty string that ReturnPtr returns.     PCharLabel.caption := 'PChar Result = "' + p + '"'; end; // Quick demonstration of a function that returns a // floating point value as a function result. procedure TDelphiEx2Form.FltBtnClick(Sender: TObject); var     r:real;     strVal:string; begin     // Call the assembly code that returns a real result.     r := ReturnReal();      // Always returns 1.0     // Convert and display the result.     str( r:13:10, strVal );     RealLabel.caption := 'Real Result = ' + strVal; end; end. 
end example

Listing 15-8: ReturnBoolean: Demonstrates Returning a Byte Value in AL.

start example
 // ReturnBooleanUnit // // Provides the ReturnBoolean function for the DelphiEx2 program. unit ReturnBooleanUnit; // Tell HLA that ReturnBoolean is a public symbol: procedure ReturnBoolean; @external; // Demonstration of a function that returns a byte value in AL. // This function simply returns a boolean result that alternates // between true and false on each call. procedure ReturnBoolean; @nodisplay; @noalignstack; @noframe; static b:boolean:=false; begin ReturnBoolean;     xor( 1, b ); // Invert boolean status     and( 1, b ); // Force to zero (false) or one (true).     mov( b, al ); // Function return result comes back in AL.     ret(); end ReturnBoolean; end ReturnBooleanUnit; 
end example

Listing 15-9: ReturnWord: Demonstrates Returning a Word Value in AX.

start example
 // ReturnWordUnit- // // Provides the ReturnWord function for the DelphiEx2 program. unit ReturnWordUnit; procedure ReturnWord; @external; procedure ReturnWord; @nodisplay; @noalignstack; @noframe; static w:int16 := 1234; begin ReturnWord;     // Increment the static value by one on each     // call and return the new result as the function     // return value.     inc( w );     mov( w, ax );     ret(); end ReturnWord; end ReturnWordUnit; 
end example

Listing 15-10: ReturnDWord: Demonstrates Returning a Dword Value in EAX.

start example
 // ReturnDWordUnit- // // Provides the ReturnDWord function for the DelphiEx2 program. unit ReturnDWordUnit; procedure ReturnDWord; @external; // Same code as ReturnWord except this one returns a 32-bit value // in EAX rather than a 16-bit value in AX. procedure ReturnDWord; @nodisplay; @noalignstack; @noframe; static     d:int32 := -7; begin ReturnDWord;     inc( d );     mov( d, eax );     ret(); end ReturnDWord; end ReturnDWordUnit; 
end example

Listing 15-11: ReturnPtr: Demonstrates Returning a 32-Bit Address in EAX.

start example
 // ReturnPtrUnit- // // Provides the ReturnPtr function for the DelphiEx2 program. unit ReturnPtrUnit; procedure ReturnPtr; @external; // This function, which is lame, returns a pointer to a zero // byte in memory (i.e., an empty pchar string). Although // not particularly useful, this code does demonstrate how // to return a pointer in EAX. procedure ReturnPtr; @nodisplay; @noalignstack; @noframe; static     stringData: byte; @nostorage;                 byte "Pchar object", 0; begin ReturnPtr;     lea( eax, stringData );     ret(); end ReturnPtr; end ReturnPtrUnit;
end example

Listing 15-12: ReturnReal: Demonstrates Returning a Real Value in ST0.

start example
 // ReturnRealUnit- // // Provides the ReturnReal function for the DelphiEx2 program. unit ReturnRealUnit; procedure ReturnReal; @external; procedure ReturnReal; @nodisplay; @noalignstack; @noframe; static     realData: real80 := 1.234567890; begin ReturnReal;     fld( realData );     ret(); end ReturnReal; end ReturnRealUnit; 
end example

15.3.4 Calling Conventions

Delphi/Kylix supports five different calling mechanisms for procedures and functions: register, pascal, cdecl, stdcall, and safecall. The register and pascal calling methods are very similar except that the pascal parameter passing scheme always passes all parameters on the stack while the register calling mechanism passes the first three parameters in CPU registers. We'll return to these two mechanisms shortly because they are the primary mechanisms we'll use. The cdecl calling convention uses the C/C++ programming language calling convention. We'll study this scheme more in the section on interfacing C/C++ with HLA. There is no need to use this scheme when calling HLA procedures from Delphi/Kylix. If you must use this scheme, then see the section on the C/C++ languages for details. The stdcall convention is used to call Windows API functions. Again, there really is no need to use this calling convention, so we will ignore it here. See the Delphi/Kylix documentation for more details. Safecall is another specialized calling convention that we will not use. See, we've already reduced the complexity from five mechanisms to two! Seriously, though, when calling assembly language routines from Delphi/Kylix code that you're writing, you only need to use the pascal and register conventions.

The calling convention options specify how Delphi/Kylix passes parameters between procedures and functions as well as who is responsible for cleaning up the parameters when a function or procedure returns to its caller. The pascal calling convention passes all parameters on the stack and makes it the procedure or function's responsibility to remove those parameters from the stack. The pascal calling convention mandates that the caller push parameters in the order the compiler encounters them in the parameter list (i.e., left to right). This is exactly the calling convention that HLA uses (assuming you don't use the "IN register" parameter option). Here's an example of a Delphi/Kylix external procedure declaration that uses the pascal calling convention:

 procedure UsesPascal( parm1:integer; parm2:integer; parm3:integer ); 

The program in Listings 15-13 and 15-14, respectively, provides a quick example of a Delphi program that calls an HLA procedure (function) using the pascal calling convention (the Kylix code is almost identical).

Listing 15-13: DelphiEx3: Sample Program That Demonstrates the Pascal Calling Convention.

start example
 unit DelphiEx3; interface uses   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,   StdCtrls; type   TForm1 = class(TForm)     callUsesPascalBtn: TButton;     UsesPascalLabel: TLabel;     procedure callUsesPascalBtnClick(Sender: TObject);   private     { Private declarations }   public     { Public declarations }   end; var   Form1: TForm1; implementation {$R *.DFM} {$L usespascal.obj} function UsesPascal (     parm1:integer;     parm2:integer;     parm3:integer ):integer; pascal; external; procedure TForm1.callUsesPascalBtnClick(Sender: TObject); var     i:      integer;     strVal: string; begin     i := UsesPascal( 5, 6, 7 );     str( i, strVal );     UsesPascalLabel.caption := 'Uses Pascal = ' + strVal; end; end. 
end example

Listing 15-14: UsesPascal: HLA Function the Previous Delphi/Kylix Code Will Call.

start example
 // UsesPascalUnit // // Provides the UsesPascal function for the DelphiEx3 program. unit UsesPascalUnit; // Tell HLA that UsesPascal is a public symbol: procedure UsesPascal( parm1:int32; parm2:int32; parm3:int32 ); @external; // Demonstration of a function that uses the PASCAL calling convention. // This function simply computes parm1+parm2-parm3 and returns the // result in EAX. Note that this function does not have the // "NOFRAME" option because it needs to build the activation record // (stack frame) in order to access the parameters. Furthermore, this // code must clean up the parameters upon return (another chore handled // automatically by HLA if the "NOFRAME" option is not present). procedure UsesPascal( parm1:int32; parm2:int32; parm3:int32 );     @nodisplay; @noalignstack; begin UsesPascal;     mov( parm1, eax );     add( parm2, eax );     sub( parm3, eax ); end UsesPascal; end UsesPascalUnit; 
end example

To compile the HLA code under Windows, you would use the following two commands in a command window:

 hla -st UsesPascal.hla tasm32 -mx -m9 UsesPascal.asm 

To compile the HLA code under Linux, you would use the following command from the shell:

 hla -c UsesPascal.hla 

Once you produce the object file with the above two command sequences, you can get into Delphi/Kylix and compile the Pascal code.

The register calling convention also processes parameters from left to right and requires the procedure/function to clean up the parameters upon return; the difference is that procedures and functions that use the register calling convention will pass their first three (ordinal) parameters in the EAX, EDX, and ECX registers (in that order) rather than on the stack. You can use HLA's "IN register" syntax to specify that you want the first three parameters passed in this registers, e.g.,

 procedure UsesRegisters (     parm1:int32 in EAX;     parm2:int32 in EDX;     parm3:int32 in ECX ); 

If your procedure had four or more parameters, you would not specify registers for any parameters beyond the third. Instead, you'd access those parameters on the stack. Because most procedures have three or fewer parameters, the register calling convention will typically pass all of a procedure's parameters in the registers.

Although you can use the register keyword just like pascal to force the use of the register calling convention, the register calling convention is the default mechanism in Delphi/Kylix. Therefore, a Delphi/Kylix declaration like the following will automatically use the register calling convention:

 procedure UsesRegisters (     parm1:integer;     parm2:integer;     parm3:integer ); external; 

The program in Listings 15-15 and 15-16 is a modification of the previous program in this section that uses the register calling convention rather than the pascal calling convention.

Listing 15-15: DelphiEx4: Using the register Calling Convention.

start example
 unit DelphiEx4; interface uses   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,   StdCtrls; type   TForm1 = class(TForm)     callUsesRegisterBtn: TButton;     UsesRegisterLabel: TLabel;     procedure callUsesRegisterBtnClick(Sender: TObject);   private     { Private declarations }   public     { Public declarations }   end; var   Form1: TForm1; implementation {$R *.DFM} {$L usesregister.obj} function UsesRegister (     parm1:integer;     parm2:integer;     parm3:integer;     parm4:integer ):integer; external; procedure TForm1.callUsesRegisterBtnClick(Sender: TObject); var     i:      integer;     strVal: string; begin     i := UsesRegister( 5, 6, 7, 3 );     str( i, strVal );     UsesRegisterLabel.caption := 'Uses Register = ' + strVal; end; end. 
end example

Listing 15-16: HLA Code to Support the DelphiEx4 Program.

start example
 // UsesRegisterUnit- // // Provides the UsesRegister function for the DelphiEx4 program. unit UsesRegisterUnit; // Tell HLA that UsesRegister is a public symbol: procedure UsesRegister (     parm1:int32 in eax;     parm2:int32 in edx;     parm3:int32 in ecx;     parm4:int32 ); @external; // Demonstration of a function that uses the REGISTER calling convention. // This function simply computes (parm1+parm2-parm3)*parm4 and returns the // result in EAX. Note that this function does not have the // "NOFRAME" option because it needs to build the activation record // (stack frame) in order to access the fourth parameter. Furthermore, this // code must clean up the fourth parameter upon return (another chore handled // automatically by HLA if the "NOFRAME" option is not present). procedure UsesRegister (     parm1:int32 in eax;     parm2:int32 in edx;     parm3:int32 in ecx;     parm4:int32 ); @nodisplay; @noalignstack; begin UsesRegister;     mov( parm1, eax );     add( parm2, eax );     sub( parm3, eax );     intmul( parm4, eax ); end UsesRegister; end UsesRegisterUnit; 
end example

To compile the HLA code under Windows, you would use the following two commands in a command window:

 hla -st UsesRegister.hla tasm32 -mx -m9 UsesRegister.hla 

Once you produce the OBJ file with the preceding command, you can get into Delphi and compile the Pascal code.

To compile the HLA code under Linux, you would use the following shell command:

 hla -c UsesRegister.hla 

Once you produce the .o file with the preceding command, you can get into Kylix and compile the Pascal code.

15.3.5 Pass by Value, Reference, CONST, and OUT in Kylix

A Delphi/Kylix program can pass parameters to a procedure or function using one of four different mechanisms:

  • pass by value

  • pass by reference

  • CONST parameters

  • OUT parameters

The examples up to this point in this chapter have all used Delphi/Kylix's (and HLA's) default pass by value mechanism. In this section we'll look at the other parameter passing mechanisms.

HLA and Delphi/Kylix also share a (mostly) common syntax for pass by reference parameters. The following two lines provide an external declaration in Delphi/Kylix and the corresponding external (public) declaration in HLA for a pass by reference parameter using the pascal calling convention:

 procedure HasRefParm( var refparm: integer ); pascal; external; // Delphi/Kylix procedure HasRefParm( var refparm: int32 ); @external;          // HLA 

Like HLA, Delphi/Kylix will pass the 32-bit address of whatever actual parameter you specify when calling the HasRefParm procedure. Don't forget, inside the HLA code, that you must dereference this pointer to access the actual parameter data. See the chapter on procedures for more details.

The Delphi/Kylix CONST and OUT parameter passing mechanisms are virtually identical to pass by reference. Like pass by reference these two schemes pass a 32-bit address of their actual parameter. The difference is that the procedure is not supposed to write to CONST objects because they're, presumably, constant. Conversely, the procedure is supposed to write to an OUT parameter (and not assume that it contains any initial value of consequence) because the whole purpose of an OUT parameter is to return data from a procedure or function. Other than the fact that the Delphi/Kylix compiler will check procedures and functions for compliance with these rules, there is no difference between CONST, OUT, and reference parameters. Delphi/Kylix passes all such parameters by reference to the procedure or function. Note that in HLA you would declare all CONST and OUT parameters as pass by reference parameters. HLA does not enforce the read-only attribute of the CONST object nor does it check for an attempt to access an uninitialized OUT parameter; those checks are the responsibility of the assembly language programmer.

As you've learned in the previous section, by default Delphi/Kylix uses the register calling convention. If you pass one of the first three parameters by reference to a procedure or function, Delphi/Kylix will pass the address of that parameter in the EAX, EDX, or ECX register. This is very convenient as you can immediately apply the register indirect addressing mode without first loading the parameter into a 32-bit register.

Like HLA, Delphi/Kylix lets you pass untyped parameters by reference (or by CONST or OUT). The syntax to achieve this in Delphi/Kylix is the following:

 procedure UntypedRefParm( var parm1; const parm2; out parm3 ); external; 

Note that you do not supply a type specification for these parameters. Delphi/Kylix will compute the 32-bit address of these objects and pass them on to the UntypedRefParm procedure without any further type checking. In HLA, you can use the var keyword as the data type to specify that you want an untyped reference parameter. Here's the corresponding prototype for the UntypedRefParm procedure in HLA:

 procedure UntypedRefParm( var parm1:var; var parm2:var; var parm3:var ); @external; 

As noted above, you use the var keyword (pass by reference) when passing CONST and OUT parameters. Inside the HLA procedure it's your responsibility to use these pointers in a manner that is reasonable given the expectations of the Delphi/Kylix code.

15.3.6 Scalar Data Type Correspondence Between Delphi/Kylix and HLA

When passing parameters between Delphi/Kylix and HLA procedures and functions, it's very important that the calling code and the called code agree on the basic data types for the parameters. In this section we will draw a correspondence between the Delphi/Kylix scalar data types and the HLA (1.x) data types.[4]

Assembly language supports any possible data format, so HLA's data type capabilities will always be a superset of Kylix's. Therefore, there may be some objects you can create in HLA, which have no counterpart in Kylix, but the reverse is not true. Because the assembly functions and procedures you write are generally manipulating data that Kylix provides, you don't have to worry about not being able to process some data passed to an HLA procedure by Kylix.[5]

Delphi/Kylix provides a wide range of different integer data types. Table 15-1 lists the Delphi/Kylix types and their HLA equivalents.

Table 15-1: Kylix and HLA Integer Types

Kylix

HLA Equivalent

Range


Minimum

Maximum


integer

int32[1]

2147483648

2147483647


cardinal

uns32[2]

0

4294967295


shortint

int8

128

127


smallint

int16

32768

32767


longint

int32

2147483648

2147483647


int64

int64

263

(263-1)


byte

uns8

0

255


word

uns16

0

65535


longword

uns32

0

4294967295


subrange types

Depends on range

Minimum range value

Maximum range value

[1]Int32 is the implementation of integer in Delphi/Kylix, though this may change in later releases.

[2]Uns32 is the implementation of cardinal in Delphi/Kylix, though this may change in later releases.

In addition to the integer values, Delphi/Kylix supports several non-integer ordinal types. Table 15-2 provides their HLA equivalents.

Table 15-2: Non-integer Ordinal Types in Kylix and HLA

Kylix

HLA

Range


Minimum

Maximum


char

char

#0

#255


widechar

wchar

#0

#65535


boolean

boolean

false (0)

true( 1 )


bytebool

byte

0( false )

255 (non-zero is true)


wordbool

word

0 (false )

65535 (non-zero is true)


longbool

dword

0 (false)

4294967295 (nonzero is true)


enumerated types

enum, byte, or word

0

Depends on number of items in the enumeration list. Usually the upper limit is 256 symbols.

Like the integer types, Delphi/Kylix supports a wide range of real numeric formats. Table 15-3 presents these types and their HLA equivalents.

Table 15-3: Real Types in Kylix and HLA

Kylix

HLA

Range


Minimum

Maximum


real

real64

5.0 E-324

1.7 E+308


single

real32

1.5 E-45

3.4 E+38


double

real64

5.0 E-324

1.7 E+308


extended

real80

3.6 E-4951

1.1 E+4932


comp

real80

263+1

2631


currency

real80

922337203685477.5808

922337203685477.5807


real48[1]

byte[6]

2.9 E-39

1.7 E+38

[1]real48 is an obsolete type that depends upon a software floating point library. You should never use this type in assembly code. If you do, you are responsible for writing the necessary floating point subroutines to manipulate the data.

The last scalar type of interest is the pointer type. Both HLA and Delphi/Kylix use a 32-bit address to represent pointers, so these data types are completely equivalent in both languages.

15.3.7 Passing String Data Between Delphi/Kylix and HLA Code

Delphi/Kylix supports a couple of different string formats. The native string format is actually very similar to HLA's string format. A string object is a pointer that points at a zero terminated sequence of characters. In the four bytes preceding the first character of the string, Delphi/Kylix stores the current dynamic length of the string (just like HLA). In the four bytes before the length, Delphi/Kylix stores a reference count (unlike HLA, which stores a maximum length value in this location). Delphi/Kylix uses the reference count to keep track of how many different pointers contain the address of this particular string object. Delphi/Kylix will automatically free the storage associated with a string object when the reference count drops to zero (this is known as garbage collection).

The Delphi/Kylix string format is just close enough to HLA's to tempt you to use some HLA string functions in the HLA Standard Library. This will fail for two reasons: (1) many of the HLA Standard Library string functions check the maximum length field, so they will not work properly when they access Delphi/Kylix's reference count field; (2) HLA Standard Library string functions have a habit of raising string overflow (and other) exceptions if they detect a problem (such as exceeding the maximum string length value). Remember, the HLA exception handling facility is not directly compatible with Delphi/Kylix's, so you should never call any HLA code that might raise an exception.

Of course, you can always grab the source code to some HLA Standard Library string function and strip out the code that raises exceptions and checks the maximum length field (this is usually the same code that raises exceptions). However, you could still run into problems if you attempt to manipulate some Delphi/Kylix string. In general, it's okay to read the data from a string parameter that Delphi/Kylix passes to your assembly code, but you should never change the value of such a string. To understand the problem, consider the following HLA code sequence:

 static      s:string := "Hello World";      sref:string;      scopy:string;           .           .           .      str.a_cpy( s, scopy ); // scopy has its own copy of "Hello World"      mov( s, eax );         // After this sequence, s and sref point at      mov( eax, sref );      // the same character string in memory. 

After the code sequence above, any change you would make to the scopy string would affect only scopy because it has its own copy of the "Hello World" string. On the other hand, if you make any changes to the characters that s points at, you'll also be changing the string that sref points at because sref contains the same pointer value as s; in other words, s and sref are aliases of the same data. Although this aliasing process can lead to the creation of some killer defects in your code, there is a big advantage to using copy by reference rather than copy by value: copy by reference is much quicker because it only involves copying a single four-byte pointer. If you rarely change a string variable after you assign one string to that variable, copy by reference can be very efficient.

What happens if you use copy by reference to copy s to sref and then you want to modify the string that sref points at without changing the string that s points at? One way to do this is to make a copy of the string at the time you want to change sref and then modify the copy. This is known as copy on write semantics. In the average program, copy on write tends to produce faster running programs because the typical program tends to assign one string to another without modification more often that it assigns a string value and then modifies it later. Of course, the real problem is, how do you know whether multiple string variables are pointing at the same string in memory? After all, if only one string variable is pointing at the string data, you don't have to make a copy of the data, you can manipulate the string data directly. The reference counter field that Delphi/Kylix attaches to the string data solves this problem. Each time a Delphi/Kylix program assigns one string variable to another, the Delphi/Kylix code simply copies a pointer and then increments the reference counter. Similarly, if you assign a string address to some Delphi/Kylix string variable and that variable was previously pointing at some other string data, Delphi/Kylix decrements the reference counter field of that previous string value. When the reference count hits zero, Delphi/Kylix automatically deallocates storage for the string (this is the garbage collection operation).

Note that Delphi/Kylix strings don't need a maximum length field because Delphi/Kylix dynamically allocates (standard) strings whenever you create a new string. Hence, string overflow doesn't occur and there is no need to check for string overflow (and, therefore, no need for the maximum length field). For literal string constants (which the compiler allocates statically, not dynamically on the heap), Delphi/Kylix uses a reference count field of -1 so that the compiler will not attempt to deallocate the static object.

It wouldn't be that hard to take the HLA Standard Library strings module and modify it to use Delphi/Kylix's dynamically allocated string format. There is, however, one problem with this approach: Borland has not published the internal string format for Delphi/Kylix strings (the information appearing above is the result of sleuthing through memory with a debugger). They have probably withheld this information because they want the ability to change the internal representation of their string data type without breaking existing Delphi/Kylix programs. So if you poke around in memory and modify Delphi/Kylix string data (or allocate or deallocate these strings on your own), don't be surprised if your program malfunctions when a later version of Delphi/Kylix appears (indeed, this information may already be obsolete).

Like HLA strings, a Delphi/Kylix string is a pointer that happens to contain the address of the first character of a zero terminated string in memory. As long as you don't modify this pointer, you don't modify any of the characters in that string, and you don't attempt to access any bytes before the first character of the string or after the zero terminating byte, you can safely access the string data in your HLA programs. Just remember that you cannot use any Standard Library routines that check the maximum string length or raise any exceptions. If you need the length of a Delphi/Kylix string that you pass as a parameter to an HLA procedure, it would be wise to use the Delphi/Kylix Length function to compute the length and pass this value as an additional parameter to your procedure. This will keep your code working should Borland ever decide to change their internal string representation.

Delphi/Kylix also supports a ShortString data type. This data type provides backward compatibility with older versions of Borland's Turbo Pascal (Borland Object Pascal) product. ShortString objects are traditional length prefixed strings (see Chapter 4). A short string variable is a sequence of 1 to 256 bytes in which the first byte contains the current dynamic string length (a value in the range 0..255) and the following n bytes hold the actual characters in the string (n being the value found in the first byte of the string data). If you need to manipulate the value of a string variable within an assembly language module, you should pass that parameter as a ShortString variable (assuming, of course, that you don't need to handle strings longer than 256 characters). For efficiency reasons, you should always pass ShortString variables by reference (or CONST or OUT) rather than by value. If you pass a short string by value, Delphi/Kylix must copy all the characters allocated for that string (even if the current length is shorter) into the procedure's activation record. This can be very slow. If you pass a ShortString by reference, then Delphi/Kylix will only need to pass a pointer to the string's data; this is very efficient.

Note that ShortString objects do not have a zero terminating byte following the string data. Therefore, your assembly code must use the length prefix byte to determine the end of the string. It should not search for a zero byte in the string.

If you need the maximum length of a ShortString object, you can use the Delphi/Kylix high function to obtain this information and pass it to your HLA code as another parameter. Note that the high function is a compiler-intrinsic function much like HLA's @size function. Delphi/Kylix simply replaces this "function call" with the equivalent constant at compile time; this isn't a true function you can call. This maximum size information is not available at runtime (unless you've used the Delphi/Kylix high function) and you cannot compute this information within your HLA code.

15.3.8 Passing Record Data Between HLA and Kylix

Records in HLA are (mostly) compatible with Delphi/Kylix records. Syntactically their declarations are very similar, and if you've specified the correct Delphi/Kylix compiler options you can easily translate a Delphi/Kylix record to an HLA record. In this section we'll explore how to do this and learn about the incompatibilities that exist between HLA records and Delphi/Kylix records.

For the most part, translating Delphi/Kylix records to HLA is a no-brainer. The two record declarations are so similar syntactically that conversion is trivial. The only time you really run into a problem in the conversion process is when you encounter case variant records in Delphi/Kylix; fortunately, these don't occur very often and when they do, HLA's anonymous unions within a record come to the rescue.

Consider the following Pascal record type declaration:

 type      recType =           record                day: byte;                month:byte;                year:integer;                dayOfWeek:byte;           end; 

The translation to an HLA record is, for the most part, very straight-forward. Just translate the field types accordingly and use the HLA record syntax and you're in business. The translation is the following:

 type      recType:           record                day: byte;                month: byte;                year:int32;                dayOfWeek:byte;           endrecord; 

There is one minor problem with this example: data alignment. By default Delphi/Kylix aligns each field of a record on the size of that object and pads the entire record so its size is an even multiple of the largest (scalar) object in the record. This means that the Delphi/Kylix declaration above is really equivalent to the following HLA declaration:

 type      recType:           record                day: byte;                month: byte;                padding:byte[2];      // Align year on a four-byte boundary.                year:int32;                dayOfWeek:byte;                morePadding: byte[3]; // Make record an even multiple of four bytes.           endrecord; 

Of course, a better solution is to use HLA's align directive to automatically align the fields in the record:[6]

 type      recType:           record                day: byte;                month: byte;                align( 4 );      // Align year on a four-byte boundary.                year:int32;                dayOfWeek:byte;                align(4);        // Make record an even multiple of four bytes.           endrecord; 

Alignment of the fields is good insofar as access to the fields is faster if they are aligned appropriately. However, aligning records in this fashion does consume extra space (five bytes in the examples above), and that can be expensive if you have a large array of records whose fields need padding for alignment.

Table 15-4 lists how the alignment parameters for an HLA record appear.

Table 15-4: Alignment of Record Fields

Data Type

Alignment


Ordinal types

Size of the type: 1, 2, or 4 bytes

Real types

2 for real48 and extended, 4 bytes for other real types

ShortString

1

Arrays

Same as the element size

Records

Same as the largest alignment of all the fields

Sets

1 or two if the set has fewer than 8 or 16 elements, 4 otherwise

All other types

4

Another possibility is to tell Delphi/Kylix not to align the fields in the record. There are two ways to do this: use the "packed" reserved word or use the {$A-} compiler directive.

The packed keyword tells Delphi/Kylix not to add padding to a specific record. For example, you could declare the original Delphi/Kylix record as follows:

 type      recType =           packed record                day: byte;                month:byte;                year:integer;                dayOfWeek:byte;           end; 

With the packed reserved word present, Delphi/Kylix does not add any padding to the fields in the record. The corresponding HLA code would be the original record declaration above. For example:

 type      recType:           record                day: byte;                month: byte;                year:int32;                dayOfWeek:byte;           endrecord; 

The nice thing about the packed keyword is that it lets you explicitly state whether you want data alignment/padding in a record. On the other hand, if you've got a lot of records and you don't want field alignment on any of them, you'll probably want to use the "{$A-}" (turn data alignment off) option rather than add the packed reserved word to each record definition. Note that you can turn data alignment back on with the "{$A+"} directive if you want a sequence of records to be packed and the rest of them to be aligned.

While it's far easier (and syntactically safer) to used packed records when passing record data between assembly language and Delphi/Kylix, you will have to determine on a case-by-case basis whether you're willing to give up the performance gain in exchange for using less memory (and a simpler interface). It is certainly the case that packed records are easier to maintain in HLA than aligned records (because you don't have to carefully place align directives throughout the record in the HLA code). Furthermore, on new 80x86 processors most misaligned data accesses aren't particularly expensive (the cache takes care of this). However, if performance really matters you will have to measure the performance of your program and determine the cost of using packed records.

Case variant records in Delphi/Kylix let you add mutually exclusive fields to a record with an optional tag field. Here are two examples:

 type      r1=           record                stdField: integer;                case choice:boolean of                     true:( i:integer );                     false:( r:real );           end;      r2=           record                s2:real;                case boolean of // Notice no tag object here.                     true:( s:string );                     false:( c:char );           end; 

HLA does not support the case variant syntax, but it does support anonymous unions in a record that let you achieve the same semantics. The two preceding examples, converted to HLA (assuming "{A-}") are:

 type      r1:           record                stdField: int32;                choice: boolean;      // Notice that the tag field is just another field                union                     i:int32;                     r:real64;                endunion;           endrecord;      r2:           record                s2:real64;                union                     s: string;                     c: char;                endunion;           endrecord; 

Again, you should insert appropriate align directives if you're not creating a packed record. Note that you shouldn't place any align directives inside the anonymous union section; instead, place a single align directive before the union reserved word that specifies the size of the largest (scalar) object in the union.

In general, if the size of a record exceeds about 16 to 32 bytes, you should pass the record by reference rather than by value.

15.3.9 Passing Set Data Between Delphi/Kylix and HLA

Sets in Delphi/Kylix can have between 1 and 256 elements. Delphi/Kylix implements sets using an array of bits, exactly as HLA implements character sets. Delphi/Kylix reserves one to 32 bytes for each set; the size of the set (in bytes) is (Number_of_elements + 7) div 8. Like HLA's character sets, Delphi/Kylix uses a set bit to indicate that a particular object is a member of the set and a zero bit indicates absence from the set. You can use the bit test (and set/complement/reset) instructions and all the other bit manipulation operations to manipulate character sets. Furthermore, the MMX instructions might provide a little added performance boost to your set operations. For more details on the possibilities, consult the Delphi/Kylix documentation and the chapters on character sets and the MMX instructions in this text.

Generally, sets are sufficiently short (maximum of 32 bytes) that passing them by value isn't totally horrible. However, you will get slightly better performance if you pass larger sets by reference. Note that HLA often passes character sets by value (16 bytes per set) to various Standard Library routines, so don't be totally afraid of passing sets by value.

15.3.10 Passing Array Data Between HLA and Delphi/Kylix

Passing array data between some procedures written in Delphi/Kylix and HLA is little different than passing array data between two HLA procedures. Generally, if the arrays are large, you'll want to pass the arrays by reference rather than value. Other than that, you should declare an appropriate array type in HLA to match the type you're passing in from Delphi/Kylix and have at it. The following code fragments provide a simple example:

 type      PascalArray = array[0..127, 0..3] of integer; procedure PassedArrray( var ary: PascalArray ); external; Corresponding HLA code: type      PascalArray: int32[ 128, 4]; procedure PassedArray( var ary: PascalArray ); @external; 

As the above examples demonstrate, Delphi/Kylix's array declarations specify the starting and ending indices while HLA's array bounds specify the number of elements for each dimension. Other than this difference, however, you can see that the two declarations are very similar.

Delphi/Kylix uses row major ordering for arrays. So if you're accessing elements of a Delphi/Kylix multidimensional array in HLA code, be sure to use the row major order computation (see Chapter 4 for details).

15.3.11 Referencing Delphi/Kylix Objects from HLA Code

Symbols you declare in the INTERFACE section of a Delphi/Kylix program are public. Therefore, you can access these objects from HLA code if you declare those objects as external in the HLA program. The sample program in Listings 15-17 and 15-18, respectively, demonstrate this fact by declaring a structured constant (y) and a function (callme) that the HLA code uses when you press the button on a form. The HLA code calls the callme function (which returns the value 10), and then the HLA code stores the function return result into the y structured constant (which is really just a static variable).

Listing 15-17: DelphiEx5: Static Data and Delphi Public Symbols Demonstration.

start example
 unit DelphiEx5; interface uses   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,   StdCtrls; type   TDataTable = class(TForm)     GetDataBtn: TButton;     DataLabel: TLabel;     procedure GetDataBtnClick(Sender: TObject);   private     { Private declarations }   public     { Public declarations }   end; // Here's a static variable that we will export to // the HLA source code (in Delphi, structured constants // are initialized static variables). const     y:integer = 12345; var   DataTable: TDataTable;   // Here's the function we will export to the HLA code:   function callme:integer; implementation {$R *.DFM} {$L TableData.obj } function TableData:integer; external; // This function will simply return 10 as the function // result (remember, function results come back in EAX). function callme; begin     callme := 10; end; procedure TDataTable.GetDataBtnClick(Sender: TObject); var     strVal: string;     yVal:   string; begin     // Display the value that TableData returns.     // Also display the value of y, which TableValue modifies     str( TableData(), strVal );     str( y, yVal );     DataLabel.caption := 'Data = ' + strVal + ' y=' + yVal; end; end. 
end example

Listing 15-18: HLA Code for DelphiEx5 Example.

start example
 unit TableDataUnit; static     y:int32; @external;      // Static object from Delphi code     index: dword := -1;      // index initial value;     TheTable: dword[12] :=         [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]; // TheTable values. // Interface to "callme" procedure found in the Delphi code: procedure callme; @external; // Declare the procedure we're supplying to the Delphi code: procedure TableData; @external; procedure TableData; @nodisplay; @noalignstack; @noframe; begin TableData;     callme();           // Call Delphi code.     mov( eax, y );      // Store return result in Y.     // Okay, on each successive call to this function, return     // the next element (or wraparound to the first element) from     // the "TheTable" array:     inc( index );     mov( index, eax );      if( eax > 11 ) then         xor( eax, eax );         mov( eax, index );     endif;     mov( TheTable[ eax*4 ], eax );     ret(); end TableData; end TableDataUnit; 
end example

[3]Note that the HLA Standard Library source code is available; feel free to modify the routines you want to use and remove any exception handling statements contained therein.

[4]Scalar data types are the ordinal, pointer, and real types. They do not include strings or other composite data types.

[5]Delphi/Kylix string objects are an exception. For reasons that have nothing to do with data representation, you should not manipulate string parameters passed in from Delphi/Kylix to an HLA routine. This section will explain the problems more fully a little later.

[6]Actually, an even better solution is to use HLA's record field alignment options. See the HLA Reference Manual on the accompanying CD-ROM for more details.




The Art of Assembly Language
The Art of Assembly Language
ISBN: 1593272073
EAN: 2147483647
Year: 2005
Pages: 246
Authors: Randall Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net