Important alert: (current site time 10/25/2014 2:39:40 AM EDT)
 

article

Asm coding in VB just got even more powerful!

Email
Submitted on: 4/9/2014 3:39:53 AM
By: Rde 
Level: Advanced
User Rating: By 8 Users
Compatibility: VB 5.0, VB 6.0
Views: 2679
author picture
 
      As a newby to asm programming my first challenge was to work out how to access the parameters I was passing to my asm procedure, and how to successfully exit back to VB on completion without errors.

No sooner had I managed to accomplish this and the goal posts changed.

What had actually changed was the way I was calling my machine code. At first I was using CallWindowProc to fire up my asm, but for small asm routines the over-head of CWP (as fast as it is) nullified the benifit of using asm, and there turned out to be a faster way to call machine code from within VB.

This alternative method of calling an asm routine is to replace the pointer to a defined VB routine to instead point to our machine code. To do this is a quite simple technique of patching the virtual function table of the VB class module.

And as it turns out, there is an added benifit to using this technique (beyond being very fast).



This article has accompanying files


 
 
Terms of Agreement:   
By using this article, you agree to the following terms...   
  1. You may use this article in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.
  2. You MAY NOT redistribute this article (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.   
  3. You may link to this article from another website, but ONLY if it is not wrapped in a frame. 
  4. You will abide by any additional copyright restrictions which the author may have placed in the article or article's description.
				 .
When you call an asm procedure, the value in IP (the instruction pointer) changes to the offset of the first byte of the procedure.

However, the system always pushes the callers instruction pointer value onto the stack after pushing the procedure's parameters so that it can find this value on return. The stack pointer always points to the last thing pushed to the top of the stack.

This is why the first parameter is offset from the stack pointer [esp+4]. Note also that when we push ebp before setting ebp to esp the first parameter becomes [ebp+8]. For asm coders this should look familiar.

When we patch VB's virtual function table of our class module to point to our asm procedure, the loader loads the procedure by pushing both the address of the callers next instruction in IP and also the pointer to the callers object module onto the stack.

The net result is the first parameter is offset from the stack pointer [esp+8]. Therefore, when we push ebp before setting ebp to esp the first parameter becomes [ebp+12]. The new familiar.

The VB function call also effects the number of bytes we must drop with RET when we return control back to VB. The RET instruction will first pop the address of the callers next instruction into IP, then drop any bytes we have specified in the RET instruction, and then exit from our procedure.

With CallWindowProc we just need to account for the number of bytes that are pushed onto the stack for the parameters, which are always 16 (4 * longs) no matter how many params we actually use.

When we patch the vft of our module we must account for the number of bytes used by our parameters as well as whether it is a function or a sub-routine. For a sub we need to add 4 for the caller object pointer plus the number of bytes used by our parameters.

For each long or string parameter we must account for 4 bytes, and with long alignment it's also 4 bytes for an integer or byte parameter unless it's 2 integers in sequence, etc, so care must be taken.

Next we must account for a function, whose return value pointer is pushed onto the stack first before any parameters.

For two parameters it would be param1 = [esp+8], param2 = [esp+12] and the return value pointer would be placed at [esp+16].

Note for ebp it would be param1 = [ebp+12], param2 = [ebp+16] and the return value pointer would be placed at [ebp+20].

  mov esi, [ebp+20] ; get address of the return value
  mov [esi], eax    ; store the function result
  pop esi           ; restore pointer register
  pop ebp           ; restore base pointer register
  xor eax, eax      ; clear eax to tell VB all is okay
  ret 16            ; function return (P1, P2, rc, lpObj)

sub p1
p1=esp+8
ret 8

func rc
rc=esp+8
ret 8

sub p1, p2
p1=esp+8
p2=esp+12
ret 12

func p1 rc
p1=esp+8
rc=esp+12
ret 12

sub p1, p2, p3, p4
p1=esp+8
p2=esp+12
p3=esp+16
p4=esp+20
ret 20

func p1, p2, p3 rc
p1=esp+8
p2=esp+12
p3=esp+16
rc=esp+20
ret 20

Now those who were paying attention may have noticed that our VB procedure is receiving an extra piece of data on the stack than we newby asm coders are used to - not only is it being pushed onto the stack after our parameters but we also have to drop this data off the stack for the stack to be returned to its correct state for the caller on return.

And if I may digress for a moment, my one problem with using asm as a quick and powerful extension of VB is that the asm routine runs pretty much in isolation to my VB project, requiring everything it needs to be passed as parameters. As it turns out we have access to all module level data in the calling class without having to pack it all into custom udt's to pass a pointer to the data as a parameter (Rob Rayment is known to do this trick very effectively).

This extra data pushed onto the stack turns out to be a pointer to the object module of the caller - in VB terms that means that the ObjPtr(cCaller) is being passed as [esp+4]. If, for example, we have a module level variable named lngMyData, and its VarPtr value is 52 bytes greater than the classes ObjPtr value, then the value in [esp+4] + 52 will always point to lngMyData.

  mov esi, [ebp+8]           ; pointer to caller object module
  mov dword ptr [esi+52], 33 ; assign the value 33 to lngMyData

I'm sure that this info will have a limited audience, but just for those who may be interested I decided to post it here on PSC.

winzip iconDownload article

Note: Due to the size or complexity of this submission, the author has submitted it as a .zip file to shorten your download time. Afterdownloading it, you will need a program like Winzip to decompress it.Virus note:All files are scanned once-a-day by Planet Source Code for viruses, but new viruses come out every day, so no prevention program can catch 100% of them. For your own safety, please:
  1. Re-scan downloaded files using your personal virus checker before using it.
  2. NEVER, EVER run compiled files (.exe's, .ocx's, .dll's etc.)--only run source code.
  3. Scan the source code with Minnow's Project Scanner

If you don't have a virus scanner, you can get one at many places on the net including:McAfee.com

 
Terms of Agreement:   
By using this article, you agree to the following terms...   
  1. You may use this article in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.
  2. You MAY NOT redistribute this article (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.   
  3. You may link to this article from another website, but ONLY if it is not wrapped in a frame. 
  4. You will abide by any additional copyright restrictions which the author may have placed in the article or article's description.


Other 54 submission(s) by this author

 


Report Bad Submission
Use this form to tell us if this entry should be deleted (i.e contains no code, is a virus, etc.).
This submission should be removed because:

Your Vote

What do you think of this article (in the Advanced category)?
(The article with your highest vote will win this month's coding contest!)
Excellent  Good  Average  Below Average  Poor (See voting log ...)
 

Other User Comments

4/7/2014 11:10:51 AMBonnie West

Thanks a lot for this excellent article!
(If this comment was disrespectful, please report it.)

 
4/7/2014 12:02:01 PMRobert Rayment

Good luck with ASM Rde. Some useful refs if you haven't seen them:

CodeId=45934 (Bit shifting class)
CodeId=42427 (VBInlineASM)
CodeId=64936(Utility by ULLI)

Just a comment on the 'trick' CallWindowProc, if the m/c is processing a large data set (graphics or whatever) then the pop/push time is negligible. For short code called 1000's of times, clearly the VTab method will be better.



(If this comment was disrespectful, please report it.)

 
4/7/2014 12:21:58 PMCaptain Picard

Don't change the title "Asm coding in VB just got even more powerful!" I will use this on Wikipedia and other sites. 5*****
(If this comment was disrespectful, please report it.)

 
4/7/2014 12:23:33 PMCaptain Picard

Bring more ASM+VB6 it is very well recived ! (but you know that). :)
(If this comment was disrespectful, please report it.)

 
4/8/2014 2:13:38 AMRde

Thanks heaps for the comments all

Thanks also Rob for the references. I am aware that you and Ulli were the for-runners in ASM+VB. Your tip on cwp is much appreciated, I am aware that using cwp is very fast - but for a tiny 'ShiftBits' style routine there is no benefit over VB because of the overhead of the call.

I will not change the title Jean Luke :)

I will attempt some more ASM+VB for submission on PSC.

Happy coding,
Rd :)
(If this comment was disrespectful, please report it.)

 
4/8/2014 2:02:31 PMCaptain Picard


Super and thank you Rde! :)

(If this comment was disrespectful, please report it.)

 
4/9/2014 3:31:23 PMdzzie

linking in pre-compiled object files created in C or asm is another good way to go. You can replace individual functions in modules, or the entire module. You can also add an export to the exe and an obj file that includes it and it will get compiled in, then access it through a declare, although this method has runtime overhead and translation like any declare.
(If this comment was disrespectful, please report it.)

 
4/10/2014 5:27:38 AMRde

Thanks for the info dzzie, this info is good to help realize VB's full potential

I'm not sure compiled C object code has any real benefit over VB's object code, but my interest lies squarely on hand optimized asmbler!

I guess its quite obvious that my main goal is to insert my asmbled machine code into my project with the absolute minimal amount of overhead as possible :)

Happy coding,
Rd :)
(If this comment was disrespectful, please report it.)

 
4/14/2014 10:04:07 AMvbaddicts

Its good to see another vb guy thats figured out how to use vb+asm. Look forward to seeing more from you! Five stars from me definately!
(If this comment was disrespectful, please report it.)

 
4/14/2014 10:10:00 AMvbaddicts

Hey Rde/Authur, I tried to send you an email but I guess you didn't register one on here? Anyways, my name is david but I go by vbad or vbaddicts on here. I've been programming for a long time and one of the first languages I became good with was vb6. In the past, I have written vb+asm code and it can be useful in certain circumstances for sure. The latest vb+asm code I released was a crc32 routine written completely in asm, designed to be called from vb6 with vtable call patch. You can check that project out here:

http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=74450&lngWI d=1

Anyways,
I thought maybe you would be interested in collaborating on a vb+asm project of some kind. If you are interested please feel free to email me (vbaddicts@gmail.com) or message me on aim (vbaddict),

I look forward to hearing from you.... Thanks - David Fritts
(If this comment was disrespectful, please report it.)

 
4/15/2014 3:20:49 AMRde

Just as a follow up to dzzie's comment above:

A good tool to help would be the Visual Basic Compile Interceptor found here on PSC at Code Id=54324
(If this comment was disrespectful, please report it.)

 
5/5/2014 4:35:22 PMdzzie

here are some of the links i compiled while i was working along those lines:

http://sandsprite.com/blogs/index.php?uid=11&pid=293

and the one I extended a bit for my experiments

https://github.com/dzzie/addins/tree/master/LinkTool

You can use inline asm or C with this method and directly compile the code into your vb6 apps. When running In the IDE, you access the code seamlessly as an external dll which is fine because VB6 cant debug asm anyway.
(If this comment was disrespectful, please report it.)

 

Add Your Feedback
Your feedback will be posted below and an email sent to the author. Please remember that the author was kind enough to share this with you, so any criticisms must be stated politely, or they will be deleted. (For feedback not related to this particular article, please click here instead.)
 

To post feedback, first please login.