Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
{$APPTYPE CONSOLE}
program asmdoc1; uses sysutils;
{
Delphi assembler doc example #1 - simple parameters, simple function result
things like integers, char, pointer - anything 4 bytes or less which is not
an FP type
}
function addthreeintegers1(num1, num2, num3: integer): integer; assembler;
{ num1 in EAX, num2 in EDX, num3 in ECX, result in EAX }
asm
ADD EAX, EDX // EAX := EAX + EDX;
ADD EAX, ECX // EAX := EAX + ECX;
end;
procedure addthreeintegers2(var num1: integer; num2, num3: integer); assembler;
{ num1 is a pointer to contents of data, put in EAX, num2 in EDX, num3 in ECX }
asm
ADD EDX, ECX // EDX := EDX + ECX;
MOV ECX, [EAX] // ECX := DWord(EAX^);
ADD EDX, ECX // EDX := EDX + ECX;
MOV [EAX], EDX // DWord(EAX)^ := EDX;
end;
var
a: integer;
begin
writeln('Tek-tips Delphi ASM FAQ example #1 - simple parameters');
writeln;
a := addthreeintegers1(2, 3, 6);
writeln('Answer is ', a);
addthreeintegers2(a, 2, 1);
writeln('Answer is ', a);
write('Press ENTER to exit.');
readln;
{$APPTYPE CONSOLE}
program asmdoc2; uses sysutils;
{ Delphi assembler doc example #2 - ShortStrings }
function AChangeChar(instr: ShortString): ShortString; assembler;
{ increments each ASCII character by 1 and returns the string
EAX is pointer to string block represented by instr,
output function result is in EDX as a pointer,
String function results are pushed as an additional VAR parameter
}
asm
// Delphi procedures/functions must preserve EBX, ESI, and EDI if used
PUSH EBX
// first byte of ShortString is a length. Copy that to result
MOV CL, Byte [EAX] // CL := Byte(EAX^);
MOV Byte [EDX], CL // Byte(EDX^) := CL;
// now process the string
@Loop1: // for i := CL downto 0 do
CMP CL, 0
JE @end
MOV BL, Byte [EAX+1] // BL := Byte((EAX+1)^);
ADD BL, 1 // BL := BL + 1;
MOV Byte [EDX+1], BL // Byte((EDX+1)^) := BL;
INC EAX // EAX := EAX + 1;
INC EDX // EDX := EDX + 1;
DEC CL // CL := CL - 1;
JMP @loop1
@end:
POP EBX
end;
var
a: ShortString;
b: ShortString;
begin
writeln('Tek-tips Delphi ASM FAQ example #2 - ShortStrings');
writeln;
a := 'ABCDEFGHIJK';
writeln('Before the string is: ', a);
b := AChangeChar(A);
writeln(' After the string is: ', b); // answer should be 'BCDEFGHIJKL'
write('Press ENTER to exit.');
readln;
end.
{$APPTYPE CONSOLE}
program asmdoc3; uses sysutils;
{ Delphi assembler doc example #3 - var AnsiStrings
applies to WideStrings as well }
procedure AFillCharString(var instr: AnsiString; fchar: AnsiChar); assembler;
{
fills string with character "fchar"
instr is address in EAX
fchar is in EDX (specifically DL) as pointer to the AnsiString
}
asm
MOV EAX, [EAX] // get contents of string
CMP EAX, 0 // check whether the string is nil
JE @exit // exit if string is nil
// AnsiStrings are passed as pointers to the DATA, so length is before
MOV ECX, [EAX-4]
@loop1:
CMP ECX, 0 // for i := ECX downto 1 do
JE @exit
MOV Byte [EAX], DL // Byte(EAX^) := DL;
DEC ECX // ECX := ECX - 1;
INC EAX // EAX := EAX + 1;
JMP @loop1
@exit:
end;
var
a: AnsiString;
begin
writeln('Tek-tips Delphi ASM FAQ example #3 - AnsiStrings');
writeln;
a := 'ABCDEFGHIJK';
writeln('Before the string is: ', a);
AFillCharString(a, '+');
writeln(' After the string is: ', a);
write('Press ENTER to exit.');
readln;
end.
{$APPTYPE CONSOLE}
program asmdoc4; uses sysutils;
{ Delphi assembler doc example #4 - record types }
type
MyRec = record
x: integer;
y: char;
end;
procedure ARecordHandle1(var InRecord: myRec); assembler;
{ add 10 to InRecord.X and increment InRecord.Y by 1
Record passed in EAX as address,
using record typecasting
}
asm
MOV EDX, MyRec([EAX]).x // EDX := EAX^.x;
ADD EDX, 10 // EDX := EDX + 10;
MOV MyRec([EAX]).x, EDX // EAX^.X := EDX;
MOV DL, MyRec([EAX]).y // DL := EAX^.y;
INC DL // DL := DL + 1;
MOV MyRec([EAX]).y, DL // EAX^.y := DL;
end;
function ARecordHandle2(InRecord: myRec): MyRec; assembler;
{ add 10 to InRecord.X and increment InRecord.Y by 1
Record passed in EAX as address always and can be changed always
Record address for output in EDX
using memory positioning, watch value alignment on records
if you do it this way
}
asm
MOV ECX, [EAX] // ECX := DWord(EAX^);
ADD ECX, 10 // ECX := EDX + 10;
MOV [EDX], ECX // EDX^ := ECX;
MOV CL, [EAX+4] // DL := Byte((EAX+4)^);
INC CL // DL := DL + 1;
MOV [EDX+4], CL // Byte((EAX+4)^) := DL;
end;
var
a, b: MyRec;
begin
writeln('Tek-tips Delphi ASM FAQ example #4 - record types');
writeln;
A.x := 30;
A.y := 'C';
ARecordHandle1(a);
writeln('A.x is : ', A.x);
writeln('A.y is : ', A.y);
b := ARecordHandle2(a);
writeln('B.x is : ', B.x);
writeln('B.y is : ', B.y);
write('Press ENTER to exit.');
readln;
end.
{$APPTYPE CONSOLE}
program asmdoc5; uses sysutils;
{ Delphi assembler doc example #5 - FP types }
function AFPAddTwoNumbers1(In1, in2: Double): Double; assembler;
{ add FP numbers in1 and in2, return as result
FP numbers are always passed on the stack in blocks divisible by 4,
so extended always takes up 12 bytes, lower
FP numbers returned in ST(0) for functions
}
asm
FLD Double(SS:[ESP+8]) // Load in2 on FP stack, can use alias
FLD Double(SS:[ESP+16]) // Load in1 on FP stack, can use alias
FADD ST(0), ST(1) // ST(0) := ST(0) + ST(1);
end;
procedure AFPAddTwoNumbers2(var In1: Double; in2: Double); assembler;
{ add FP numbers in1 and in2, return result in in1
var value is a pointer to memory data.
}
asm
FLD Double(SS:[ESP+8]) // Load in2 on FP stack, can use alias
FLD Double([EAX]) // Load in1 contents on FP stack
FADD ST(0), ST(1) // ST(0) := ST(0) + ST(1);
FSTP Double([EAX]) // Store result back to in1 contents
end;
var
a, b, c: Double;
begin
writeln('Tek-tips Delphi ASM FAQ example #6');
writeln;
a := 1.1;
b := 2.2;
c := AFPAddTwoNumbers1(a, b);
writeln('C is: ', c);
AFPAddTwoNumbers2(c, b);
writeln('C is: ', c);
write('Press ENTER to exit.');
readln;
end.
{$APPTYPE CONSOLE}
program asmdoc6; uses sysutils;
{ Delphi assembler doc example #6 - Complex example #1 }
function AAverageFiveInts(in1, in2, in3, in4, in5: integer): Extended; assembler;
{ in1 in EAX, in2 in EDX, in3 in ECX, in4 & in5 are on stack.
Extended value returned on FP stack }
var
Res1: integer;
asm
ADD EAX, SS:[ESP+12] // add in4 to in1, can use alias
ADD EAX, SS:[ESP+16] // add in5 to in1, can use alias
ADD EAX, ECX // add in3 to in1
ADD EAX, EDX // add in2 to in1
{ you can not directly interact with the FPU with the CPU registers, so the
memory store is a requirement }
MOV Res1, 5 // move 5 to local memory
FILD Res1 // load integer from memory
MOV Res1, EAX // move EAX to memory
FILD Res1 // load memory to register
FDIV ST(0), ST(1) // f-point division by 5
end;
var
a: Extended;
begin
writeln('Tek-tips Delphi ASM FAQ example #6 - Complex Example #1');
writeln;
a := AAverageFiveInts(1, 2, 3, 4, 7);
writeln('Answer is ', a:0:20);
write('Press ENTER to exit.');
readln;
end.
{$APPTYPE CONSOLE}
program asmdoc7; uses sysutils;
{ Delphi assembler doc example #7 - Complex example #2 }
procedure ASomeRandomStuff(in1: extended; in2: integer; var in3: extended;
var in4: char; in5: word); assembler;
{ does some random junk stuff, shows how something like this procedure
header is handled in ASM
1) adds in1, in2, in3, and in5, stores result in in3.
2) increments in4 by 2.
in1 = comes from stack in2 = EAX in3 = address on EDX
in4 = address on ECX in5 = on stack
}
var
Res1: integer;
asm
ADD EAX, DWord(in5) // in2 := in2 + in5;
FLD Extended([EDX]) // load in3
FLD in1
FADD ST(0), ST(1)
MOV Res1, EAX
FILD Res1
FADD ST(0), ST(1)
FSTP Extended([EDX])
// work on the character part
MOV AL, Byte [ECX]
ADD AL, 2
MOV Byte[ECX], AL
end;
var
a: Extended;
b: char;
begin
writeln('Tek-tips Delphi ASM FAQ example #7 - Complex Example #2');
writeln;
a := 3.14;
b := 'C';
ASomeRandomStuff(0.2, 20, a, b, 12);
writeln('A is: ', a:0:20);
writeln('B is: ', b);
write('Press ENTER to exit.');
readln;
end.