Asm - por que usar em delphi
|Top Previous Next|
We have always found OP (ObjectPascal) to produce fast and more efficient code, add this to the RAD Environment of Delphi and Kylix, so therefore the need to use assembler becomes a question mark.
The article is somewhat an extract of the excellent paper "Learning Assembler with Delphi" from Ian Hodger at:
It was the wish of my students to shorten it and say a few words about debugging.
In all of our work with OP, we faced just five situations where we have felt one should consider the use of low level system code:
1. Stepping and processing large quantities of data. Of course I exclude from this any situation where a data query language with a query optimizer is employed, but not always in a automation- or micro-controller environment.
2. For scientific reason to provide high speed simulations or just
for education with Compiler-Assembler-Linker-Loader steps. I call that CALL
3. For controller programming or to develop or test peripherals like COM-devices, e.g. on how to detect free COM ports (as far as Windows knows..) and call some functions Windows doesn't allow. I.e, does not detect a COM port if a mouse is attached. Uses a DPMI call with assembler
4. High speed display routines; here we want quick and easy routines that comes with OP, not the strange C++ headers, external function libraries or confused hardware demands of DirectX
5. Strong and fast encryption algorithm like ciphers, hashes or checksums, so
the core encoding and decoding routines are written in highly optimized assembler code.
To say that writing machine code is cosy would be an understatement, and as for debugging an assembler language is just an easy way of remembering what machine code operations are available. The job of converting to machine code is done by an assembler, so Borland's Turbo Assembler is built into Delphi.
Let's practice a little "Bit":
If we look at adding an integer 15 to the register eax, the appropriate assembler instruction is
add eax,15 //a := a + 15
Almost the same, to subtract the value of ebx from eax
sub eax,ebx //a := a - b
To save a value for a happy day, we can move it to another register
mov eax,ecx //a := c
or even better, save the value to a memory address
mov ,eax //store value of eax at address 1733
and of course to retrieve it with
This means also the the largest number we can store in a register, e.g. eax is 2 to the power 32 minus 1, or exactly 4294967295.
Bear in mind the size of the values you are moving about; the mov ,eax instruction affects not only memory address 1733, but 1734,1735 and 1736 as well, because as you will recall eax is 32 bits long, or rather 4 bytes, therefore memory is always addressed in bytes!
Next step is the example: step:= step + 1;
we would get something like this:
mov eax, step
add eax, 1
mov step, eax
cause at least one parameter of almost each instruction must be a register.
Now wer'e ready for Our first snippet of assembler, but let aside the simple nature of this example. Consider the following lines of OP code:
function BigSum(A, B: integer): integer;
result := A+B;
OP provides the asm .. end block as a method of introducing just plain assembler to our code of life. So we could rewrite the function BigSum
function BigSum(A, B: integer): integer;
This works fine, but there is a point to consider. There is no speed gain and we've lost the readability of our code. But the fact is, all our sophisticated class design is "broken" bi an assembler.
You can also write complete procedures and functions using inline assembler code, without including a begin...end statement, e.g:
function LongMul(X, Y: Integer): Longint;
mov eax, X
The compiler performs several optimizations on these routines so no code is generated to copy value parameters into local variables. This affects all string-type value parameters and other value parameters whose size isn’t
1, 2, or 4 bytes. Within the routine, such parameters must be treated as if they were var parameters.
If we are going to produce really useful code, at some point we shall need to implement more deeper routines
E.G. we have to display the output of some function dependant upon two variables. You might imagine this as a three-dimensional map, where the coordinates [X,Y] correspond to a height H.
When we plot the point [X,Y] on the screen we need to give the imagination of depth. This can be achieved by using colours of differing intensity, blue below sea level and green above. What is needed is a function that will convert a given height into the appropriate depth of color for a given sea level.
To finish this article, let's say few words about debugging in order. It is very easy to set up watches, program break's, and traverse OP programs a line at a time. The same is true, even when using assembler.
All we need to do is add the four 32bit general registers eax, ebx, ecx and edx to one's watch list, and see the effect of each line of assembler.
The built-in assembler allows you to write Intel assembler code within OP programs. It implements a large subset of the syntax supported by Turbo Assembler and Microsoft’s Macro Assembler, including all 8086/8087 and 80386/80387 opcodes and all but a few of Turbo Assembler’s expression operators.
Assembler functions return their results as follows.
- Ordinal values are returned in AL (8-bit values)
- AX (16-bit values), or EAX (32-bit values).
- Real values are returned in ST(0) on the coprocessor’s register stack.
- Pointers, including long strings, are returned in EAX.
- Short strings and all the variants are returned in the temp. location pointed to by @result.
So I hope you feel a little "Bit" the speed of Delphi and thanks Ian for his fundamentals.