Lazarus

Programming => General => Topic started by: uart on January 18, 2020, 03:19:36 pm

Title: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 03:19:36 pm
Previously when I tested accessing the data fields in a simple static fpc object with a x86-32bit target, I could access "self" using the [eax] pointer.

Now I'm trying to move my code to an x86-64bit target and it doesn't appear to work the same way (something different in the calling conventions?).

A simplified example of the code that works ok on the x86 target would be something like this:
Code: [Select]
program testasm;
{$mode objfpc}{$H+}
{$ASMMODE   intel}
uses Classes, sysutils;

Type Ttest = object
               lowD : dword;
               highD : dword;
               procedure getLow(var a : dword); assembler; register;
             end;

procedure Ttest.getLow(var a : dword); assembler; register;
asm
   push ecx;
   mov ecx,[eax];     // get self.lowD
   mov [edx],ecx;     // store result in var param "a"
   pop ecx;
end;

Obviously this is a trivial example just to make sure that I can access parameters as expected, but when I do the equivalent with a Win64 and x86-64 target it compiles ok, but either crashes with a runtime error or returns garbage.

Here is a trivial example of the 64 bit code that doesn't work.
Code: [Select]
program testasm64;
{$mode objfpc}{$H+}
{$ASMMODE   intel}
uses Classes, sysutils;

Type Ttest = object
               lowQ : qword;
               highQ : qword;
               procedure getLow(var a : qword); assembler; register;
             end;

procedure Ttest.getLow(var a : qword); assembler; register;
asm
   push rcx;
   mov rcx,[eax];     // get self.lowQ
   mov [rdx],rcx;     // store result in var param "a"
   pop rcx;
end;

This one compiles ok (fpc 3.04 cross compiler with x86-64 target CPU and Win64 target OS) but the if the method getLow is called it either runtime crashes at the asm line "mov rcx,[rax]" or returns a garbage result (at the same asm line).

So just wondering if the register calling convention different with the x86-64 compiler compared to the regular x86?

BTW. I am running the code on an Intel Core i5, under Windows 7 64 bit OS.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: marcov on January 18, 2020, 03:33:26 pm
[eax] sounds like you are dereferencing a 32-bit pointer?

And 64-bit has stack alignment so random pushing and popping is not so good. I usually use a construct like this:

Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword);
  2. begin
  3. asm
  4.  
  5.    mov rcx,[rax];     // get self.lowQ
  6.    mov [rdx],rcx;     // store result in var param "a"
  7. end['rcx];
  8. end;
  9.  

And in case of doubt look at generated code with -al for original pascal and asm code. Note that small fragments of asm are not inlinable while the pascal equivalent is.  _Benchmark_ before doing asm.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: avk on January 18, 2020, 03:43:30 pm
On win64 first parameter passes in RCX.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: marcov on January 18, 2020, 03:48:45 pm
On win64 first parameter passes in RCX.

True and the parameter ones don't need saving anyway:

// Integer arguments are passed in registers RCX, RDX, R8, and R9.
// Floating point arguments are passed in XMM0L, XMM1L, XMM2L, and XMM3L.
// volatile: RAX, RCX, RDX, R8, R9, R10, R11
// nonvolatile RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile
// volatile parameter registers xmm0-xmm3 and xmm4,5
// https://msdn.microsoft.com/en-us/library/ms235286.aspx
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 03:51:20 pm
I planned to ask about alternate ways to save registers later. Also benchmarking later, right now I'm just learning.

There are some 64 bit features that I'm not sure how to access outside of assembler, so I want to learn how the calling convention for asm works.

The freepascal help said that when register passing was used that the first three parameters where passed in eax, edx and ecx (this was for 32 bit). It further said that if the asm procedure was an object method that "self" was passed as a hidden parameter. It didn't say which one, but I guessed eax and that worked. So [eax] pointed to the first data field in the static object.

The same information apparently doesn't translate to 64bit assembler. [rax] does not seem to point to the first data field in this case, and I'm trying to figure out what does.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 03:52:45 pm
On win64 first parameter passes in RCX.

Good stuff avk. I will try that right away.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 03:56:08 pm

True and the parameter ones don't need saving anyway:

// Integer arguments are passed in registers RCX, RDX, R8, and R9.
// Floating point arguments are passed in XMM0L, XMM1L, XMM2L, and XMM3L.
// volatile: RAX, RCX, RDX, R8, R9, R10, R11
// nonvolatile RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile
// volatile parameter registers xmm0-xmm3 and xmm4,5
// https://msdn.microsoft.com/en-us/library/ms235286.aspx

Good info. Actually I suspected that those register didn't need to be saved but was playing it saffe (I've done this before but not for a very long time). :)
I had planned to ask exactly those type questions once I got some code up and running, but I just got stuck at the first step with the change in register passing convention.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 04:08:50 pm
Thanks all. Just tested accessing the object (self) data field via [rcx] and that works. :)
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Thaddy on January 18, 2020, 04:10:31 pm
Self is in RBX!
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 04:12:42 pm
Note that small fragments of asm are not inlinable while the pascal equivalent is.  _Benchmark_ before doing asm.

Thanks, but can you just clarify this point for me. Do you mean not using
Code: [Select]
procedure x; assembler
asm
...
end

but instead using
Code: [Select]
procedure;
begin
  asm
  ...
  end {asm}
end {procedure}

Is that correct?
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 04:18:27 pm
Self is in RBX!
It doesn't appear to be. Avk's advice to use rcx worked for me. When I tested, [rcx] accessed the low qword field and [rcx+8] accessed the high qword field.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: avk on January 18, 2020, 04:34:09 pm
In your previous topic, I posted a link that may seem interesting to you.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Thaddy on January 18, 2020, 04:39:40 pm
Self is in RBX!
It doesn't appear to be. Avk's advice to use rcx worked for me. When I tested, [rcx] accessed the low qword field and [rcx+8] accessed the high qword field.
Well, try. The reason that ebx / rbx is non-volatile is because it holds self.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: marcov on January 18, 2020, 04:55:51 pm
Note that small fragments of asm are not inlinable while the pascal equivalent is.  _Benchmark_ before doing asm.

Thanks, but can you just clarify this point for me. Do you mean not using

No, more that for many short procedures assembler to microoptimize stack generation and self usage is not worthwhile if you can simply inline the pascal versions, and the compiler will do it for you.

I do some assembler for work, but most of my assembler processes whole images or at least a line, so quite long functions, like this one to rotate an 8-bit image: http://www.stack.nl/~marcov/rot8x8.txt

p.s. 64-bit calling conventions differ between targets. (win64 vs *nix)
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 05:06:22 pm
No, more that for many short procedures assembler to microoptimize stack generation and self usage is not worthwhile if you can simply inline the pascal versions, and the compiler will do it for you.

Ok I see what you mean. Write short procedures/functions is straight pascal where possible and declare them with the inline attribute.

Code: [Select]
{$ INLINE ON}

procedure x; inline;
begin
 ... {straight pascal}
end;
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 18, 2020, 05:11:44 pm
Well, try. The reason that ebx / rbx is non-volatile is because it holds self.

p.s. 64-bit calling conventions differ between targets. (win64 vs *nix)

Thaddy, are you on Linux? That would explain the difference, I'm on Win7-64.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: avk on January 18, 2020, 05:36:10 pm
Extremely useful resource https://www.agner.org/optimize/ (https://www.agner.org/optimize/)

BTW on Linux it passes in RDI. :)
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: PascalDragon on January 18, 2020, 09:21:00 pm
Self is in RBX!
Your answer is wrong. On both Win64 and SysV ABIs. On Win64 the first non-floating point parameter is passed in RCX and on Sys V it's passed in RDI. For both ABIs RBX is considered non-volatile.

Thanks all. Just tested accessing the object (self) data field via [rcx] and that works. :)
You can simplyfy the assembly code a bit more (FPC's assembler readers are quite capable ;) ):
Code: Pascal  [Select][+][-]
  1. Type Ttest = object
  2.                lowQ : qword;
  3.                highQ : qword;
  4.                { the "nostackframe" is important! }
  5.                procedure getLow(var a : qword); assembler; nostackframe; register;
  6.              end;
  7.  
  8. procedure Ttest.getLow(var a : qword); assembler; nostackframe; register;
  9. asm
  10.    mov rax, Self.lowQ // get self.lowQ
  11.    mov a, rax  // store result in var param "a"
  12. end;
  13.  

This will result in the following assembly code for the whole of getLow:

Code: ASM  [Select][+][-]
  1. # [13] asm
  2. # [15] mov rax, Self.lowQ // get self.lowQ
  3.         movq    %rcx,%rax
  4. # [16] mov a, rax  // store result in var param "a"
  5.         movq    %rax,%rdx
  6. # [17] end;
  7.         ret
  8.  

RAX can be used because it is a volatile register and is intended for the function result anyway. This way you don't even need to manipulate the stack.

Though I don't know why you don't let the compiler simply do its magic and access lowQ directly?

Code: Pascal  [Select][+][-]
  1. var
  2.   t: TTest;
  3.   q: QWord;
  4. begin
  5.   t.lowQ := 42;
  6.   q := t.lowQ;
  7. end.

Code: ASM  [Select][+][-]
  1. # [24] q := t.lowQ;
  2.         movq    U_$P$TASMTEST_$$_T(%rip),%rax
  3.         movq    %rax,U_$P$TASMTEST_$$_Q(%rip)

This way there won't even be a call.

And if you should have more complex code using Pascal code and inline would be better anyway (though as of now inlining won't work if var or out parameters are involved, so you should use Result in that case).
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 19, 2020, 04:00:26 am

Code: ASM  [Select][+][-]
  1. # [24] q := t.lowQ;
  2.         movq    U_$P$TASMTEST_$$_T(%rip),%rax
  3.         movq    %rax,U_$P$TASMTEST_$$_Q(%rip)

This way there won't even be a call.

Thanks for all your help PascalDragon.  :)

I should point out that the code I posted was just a stripped down stub to illustrate my immediate problem (of not knowing what register passed the reference to self). It's not my actual code, and I would never have really used asm for such a simple method. I very much appreciate your help and you've pointed out some valuable things to me, but there's no point critiquing that particular code too much, because it was never going to be used anyway.

The only reason I posted any code in the first place (rather than just asking "hey guys, what register passes the reference to self for asm methods in Win64/X64") was that I wasn't completely sure that it wasn't a more fundamental issue. So, while I was 99% that it was just a change in register calling convention tripping me up, I couldn't totally rule out that it wasn't some other misunderstanding regarding the change to x64. So I just posted a "made up" method that had the code stub which I knew would trigger the same problem.

Hope that makes sense.  :)
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: PascalDragon on January 19, 2020, 10:06:35 am
In that case you should keep in mind what marcov said and only fall back to assembly if you really notice the Pascal code to be slow. By declaring a method as inline and playing with the various optimizations that FPC provides you should in most cases get a good result. And only if that is not sufficient you should go the assembly route. And you should always have a Pascal variant of your code anyway, cause this way you can easily port your code to other platforms (in this case it would already fail on any non-Win64 x86_64 system). A common approach for this is the following:

Code: Pascal  [Select][+][-]
  1. procedure SomeMethod;
  2. {$ifdef PUREPASCAL}
  3. begin
  4. ...
  5. end;
  6. {$else} nostackframe; assembler;
  7. asm
  8. {$if defined(CPU386)}
  9. ...
  10. {$elseif defined(CPUX86_64)}
  11. ...
  12. {$elseif defined(CPUARM)}
  13. ...
  14. {$else}
  15. {$fatal No assembly code defined}
  16. {$endif}
  17. end;{$endif}

Also using the -al you can view the assembly code that the compiler generated (in the unit output directory as (for most platforms) *.s files).
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Mr.Madguy on January 20, 2020, 10:41:09 am
Funny thing. I also wanted to suggest something like this:
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,[Self].lowQ;
  4.    mov [a],rax;
  5. end;    
  6.  
But it doesn't work. It says "Unknown identifier Self".

This one works, but works incorrectly, becuase of "mov rax, rcx" instead of "mov rax, [rcx]"
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,Self.lowQ;
  4.    mov [a],rax;
  5. end;    
  6.  

But all of a sudden this works correctly!
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,Self.highQ;
  4.    mov [a],rax;
  5. end;    
  6.  
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: PascalDragon on January 20, 2020, 10:08:57 pm
This one works, but works incorrectly, becuase of "mov rax, rcx" instead of "mov rax, [rcx]"
Code: Pascal  [Select][+][-]
  1. procedure Ttest.getLow(var a : qword); assembler; register;
  2. asm
  3.    mov rax,Self.lowQ;
  4.    mov [a],rax;
  5. end;    
  6.  
Hadn't noticed that when I adjusted the code. :o That would need to be fixed, probaly it's related to lowQ being at offset 0 as your other example with highQ works...  :'(
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Mr.Madguy on January 21, 2020, 07:02:56 am
Hadn't noticed that when I adjusted the code. :o That would need to be fixed, probaly it's related to lowQ being at offset 0 as your other example with highQ works...  :'(
I will try it in Delphi, when I'll have time for this. This is actually so called TASM syntax. And of course Delphi inherits it. "Self" and "a" are actually declared as var, i.e. they aren't variables themselves - they're pointers to variables. For example Self = rcx and a = rdx. They should be dereferenced via []. But all of a sudden it doesn't work. It works for "a", but doesn't for "Self". "Self + x" somehow forces it to be [Self + x], just because "mov rax, rcx + 8" isn't valid, but "mov rax, [rcx + 8]" is. This is all wrong anyway. "[Self]" just should work. Otherwise only possible crutch is:
Code: Pascal  [Select][+][-]
  1. mov rax, Self
  2. mov rax, [rax]
  3. mov [a], rax
  4.  
That is waste of one instruction.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 22, 2020, 07:17:39 am
Thanks for finding that Mr Madguy. It definitely looks like a bug.

In summary, it appears that it fails to dereference the member variable if that member variable happens to coincide (in address) with the base reference to the object. That is, it seems to treat self.lowQ identically with self.

I also tested this on an older version (fpc 2.6.0) with 32 bit code (and of course dwords instead of qwords, but otherwise identical), and the results were the same.  I even tried making the member variable a different size to a pointer (eg byte or word), but it still failed to dereference the base member variable.

BTW. When I tested this on Delphi 7 (again with 32bit asm as above), it worked exactly as expected. It correctly dereferenced both self.lowD and self.highD, but (as expected) didn't dereference self alone (eg "mov eax,self").

Just wondering if this bug should be reported somewhere?
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Mr.Madguy on January 22, 2020, 08:20:40 am
Thanks for finding that Mr Madguy. It definitely looks like a bug.

In summary, it appears that it fails to dereference the member variable if that member variable happens to coincide (in address) with the base reference to the object. That is, it seems to treat self.lowQ identically with self.

I also tested this on an older version (fpc 2.6.0) with 32 bit code (and of course dwords instead of qwords, but otherwise identical), and the results were the same.  I even tried making the member variable a different size to a pointer (eg byte or word), but it still failed to dereference the base member variable.

BTW. When I tested this on Delphi 7 (again with 32bit asm as above), it worked exactly as expected. It correctly dereferenced both self.lowD and self.highD, but (as expected) didn't dereference self alone (eg "mov eax,self").

Just wondering if this bug should be reported somewhere?
It does simple thing. Something like this:
Code: Text  [Select][+][-]
  1. Self equ rcx
  2. lowQ equ 0
  3. highQ equ 8
  4.  
May be typed, because otherwise something like "mov [Self].lowQ, 0" would generate "ambiguous type" error. I just don't remember exact TASM syntax for it. It was so long ago. All other things should be done by you. I.e. if you want to dereference pointer - you need to use []. And "[Self].lowQ" is simply equal to "[Self.lowQ]" and "[Self + lowQ]". It's just some syntax sugar to make code more readable.

There should be some bug in FPC, because in Delphi everything works as expected.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: PascalDragon on January 22, 2020, 09:30:33 am
Please report a bug (with an example that should work) so that it isn't forgotten.
Maybe you can also try whether the AT&T syntax is handling this correctly?
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Mr.Madguy on January 22, 2020, 10:10:23 am
Please report a bug (with an example that should work) so that it isn't forgotten.
Maybe you can also try whether the AT&T syntax is handling this correctly?
Code: Pascal  [Select][+][-]
  1. procedure TTest.GetLo(var A:QWord);assembler;nostackframe;
  2. asm
  3.   mov rax,[Self].lo
  4.   mov [A],rax
  5. end;
  6.  
Doesn't work, but should. Says "Unknown identifier "Self"".

AT&T syntax doesn't work at all. If I use (A) - it says "Invalid reference syntax". And I don't know, how to do it correctly. If it's possible.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 22, 2020, 10:14:35 am
I'm not really familiar with AT&T syntax, but I had a go and it just says "invalid reference" whatever I try (when trying to access either "self" or "a").

eg (32 bit code) move register value to var parameter "a".

mov %eax, (a)     <-----  "invalid reference"
mov %eax, 0(a)   <-----  "invalid reference"
mov %eax, a       <-----  "invalid reference"

Edit: You replied while I was typing Mr Madguy, but it looks like we came to the same conclusion.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: PascalDragon on January 23, 2020, 10:03:11 am
The following works, though I couldn't get Self to work either, so maybe that's a shortcoming of the AT&T reader.

Code: Pascal  [Select][+][-]
  1. procedure TTest.GetHi(var A: LongInt); assembler;
  2. asm
  3.   mov TTest.Hi(%eax), %eax
  4.   mov %eax, A
  5. end;
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: uart on January 23, 2020, 11:08:35 am
The following works, though I couldn't get Self to work either, so maybe that's a shortcoming of the AT&T reader.

Code: Pascal  [Select][+][-]
  1. procedure TTest.GetHi(var A: LongInt); assembler;
  2. asm
  3.   mov TTest.Hi(%eax), %eax
  4.   mov %eax, A
  5. end;

That code compliles ok, but it doesn't run correctly for me (this is my older 32bit Windows computer with fpc 2.6.0). It does not transfer the correct value to the "var A" variable.

I'm not 100% certain, but I think that the line "mov %eax, A" actually just does the same as "mov %eax, %edx". That is, a register to register move instead of register to memory.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: Mr.Madguy on January 23, 2020, 01:57:22 pm
That code compliles ok, but it doesn't run correctly for me (this is my older 32bit Windows computer with fpc 2.6.0). It does not transfer the correct value to the "var A" variable.

I'm not 100% certain, but I think that the line "mov %eax, A" actually just does the same as "mov %eax, %edx". That is, a register to register move instead of register to memory.
If () is analog of [] in AT&T syntax, then I expect "mov %eax,(A)" to work, but it looks like, that AT&T implementation is even less compatible with Delphi, than Intel one.
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: PascalDragon on January 24, 2020, 09:42:49 am
The following works, though I couldn't get Self to work either, so maybe that's a shortcoming of the AT&T reader.

Code: Pascal  [Select][+][-]
  1. procedure TTest.GetHi(var A: LongInt); assembler;
  2. asm
  3.   mov TTest.Hi(%eax), %eax
  4.   mov %eax, A
  5. end;

That code compliles ok, but it doesn't run correctly for me (this is my older 32bit Windows computer with fpc 2.6.0). It does not transfer the correct value to the "var A" variable.

I'm not 100% certain, but I think that the line "mov %eax, A" actually just does the same as "mov %eax, %edx". That is, a register to register move instead of register to memory.

Hmm, right... Dang it... Probably the AT&T reader would deserve some extensions as well...

That code compliles ok, but it doesn't run correctly for me (this is my older 32bit Windows computer with fpc 2.6.0). It does not transfer the correct value to the "var A" variable.

I'm not 100% certain, but I think that the line "mov %eax, A" actually just does the same as "mov %eax, %edx". That is, a register to register move instead of register to memory.
If () is analog of [] in AT&T syntax, then I expect "mov %eax,(A)" to work, but it looks like, that AT&T implementation is even less compatible with Delphi, than Intel one.

Of course it's less compatible, because Delphi does not support the AT&T syntax at all. So the implementation of such things is completely up to us and probably wasn't that needed. I'll need to check the readers of the other platforms if they allow for more already and if those could be used for the x86 one as well...
Title: Re: Accessing an object (self) from x86-64 asm?
Post by: BrunoK on January 24, 2020, 11:06:42 am
This code seems to work
Code: Pascal  [Select][+][-]
  1. program pgmTestAsm;
  2.  
  3. // https://forum.lazarus.freepascal.org/index.php/topic,48176.0.html
  4. {$mode objfpc}{$H+}
  5. {$ASMMODE  ATT}
  6. uses Classes, SysUtils;
  7. type
  8.   TTest = class
  9.     lowD:  dword;
  10.     highD: dword;
  11.     procedure getLow(var a: dword); assembler; register;
  12.   end;
  13. {$DEFINE ASM_1}
  14. {$IFDEF ASM_0}
  15. procedure TTest.getLow(var a: dword); assembler; nostackframe;
  16. asm
  17.   push    TTest.lowD(%eax) // Save Self.lowD on stack
  18.   pop     %eax             // Pop the value to %eax
  19.   mov     %eax, (%edx)     // Move the value to %edx^
  20. end;
  21. {$ENDIF ASM_0}
  22. {$IFDEF ASM_1}
  23. { I386
  24.   %eax : first call param (Self is the implicit first parameter)
  25.   %edx : second call param, pointer to a since a is passed as var }
  26. procedure TTest.getLow(var a: dword); assembler; nostackframe;
  27. asm
  28.   mov     TTest.lowD(%eax), %eax      // Move Self^.lowD to %eax
  29.   mov     %eax, (%edx)                // Move the value to %edx^ (var a)
  30. end;
  31. {$ENDIF ASM_1}
  32.  
  33. var
  34.   vTest : TTest;
  35. begin
  36.   vTest := TTest.create;
  37.   vTest.lowD := 123456789;
  38.   writeln('vTest.lowD=',vTest.lowD);
  39.   writeln('vTest.highD=',vTest.highD);
  40.   vTest.getLow(vTest.highD);
  41.   writeln('vTest.highD=',vTest.highD);
  42.  
  43.   Write('Press enter > '); ReadLn;
  44.  
  45.   vTest.Free;
  46. end.

its the
mov     %eax, (%edx)  // <- parenthesis to write to var pointed by %edx
that makes the difference.
TinyPortal © 2005-2018