www.xbdev.net
xbdev - software development
Tuesday October 29, 2024
Home | Contact | Support | Assembly Language What every pc speaks..1010...
     
 

Assembly Language

What every pc speaks..1010...

 

Inline Assembly

 

Simple tutorial on using asm inline...great for learning or improving your asm skills...

 

Now using inline is a great was to test out simple asm algorithms or even learn the basics of assembly.  As you can use your standard C and mix in some cool optimised assembly... Some cool things we can do, it write some simple functions mixed in with asm of course which will copy strings, or get the CPU speed...or even the individual CPU ID number... there hundreds ..thousands of things you can do.

 

Lets begin with a simple explanation of inline assembly..then move on to some simple examples...then onto some really cool and of course useful functions.

 

First we start by making a simple app...here it is:

 

// program entry point

void main()

{

}

 

This gives us our entry point...but I don't see any assembly in there...or anything special?  Well we'll add it in now....

 

 

// program entry point

void main()

{

      // inline assembly

      __asm

      {

            //our asm code goes here

      }

}

 

 

Inline assembly is done using the keywork "__asm" ... now its very important you remember it.  And there's two ways we can do inline assembly...using the { } brackets or put __asm on each line in front of our assembly code.

 

Inside our __asm block, we put our assembly code...our opcodes and mnemonic instructions... an example of a simple set of assembly instructions, which puts 10 into the eax register, then add's 2 to it, so eax has 12.

 

// program entry point

void main()

{

      // inline assembly

      __asm

      {

            mov eax, 10

            add eax, 2

      }

}

 

 

 

8, 16 and 32 Bit Registers.

Now inside our CPU, we have registers, the main one's that you most often use are the general purpose registers, and how they can work with 8, 16 or 32 bit data values is what we'll go over here.

Now today, most machines are 32 bit, and so we use eax, but if you have a 8 bit value, how do you work with that?  Well you use al, which is a 8 bit register.  Hmmm..... now its not really a different register...its the same register, but a sub section of it.. as we can access various parts of our 32 bit register by using different names for it...

 

eax - 32 bits

 ax - 16 bits

 ah  - 8 bits (High 8 Bits)

 al   - 8 bits (Low 8 Bits)

 

1111  1111  1111  1111 1111  1111 1111  1111 (32 bits eax )
1111  1111 1111  1111 (16 bits ax )
1111  1111 (high 8 bits ah)
1111  1111 (low 8 bits al )

 

And the principles of the eax register apply to the other registers of course...

 

eax,  ax,  ah,  al

 

ebx,  bx,  bh,  bl

 

ecx,  cx,  ch,  cl

 

edx,  dx, dh,  dl

 

....

 

Now this is where C or even C++ makes it easy for us, as its doing a lot of the work for us when we call a function.  Its automatically passing the parameters for us.... pushing them onto the stack and popping them off the stack for us... which you'd have to do if you where doing it all in assembly and calling a function.

 

// simple add function using inline asm

int add(int i, int j)

{

      int r;

      __asm

      {

            mov eax, i

            mov ebx, j

            add eax, ebx

            mov r,   eax

      }

      return r;

}

// program entry point

void main()

{

      int result;

 

      result = add(10, 20);

}

 

Wow, that looks tricky... but dont' worry....when you first start to see assembly inline, and especially if your new to assembly it can be scary... but its really simple.  We first put i, into the CPU eax register, then put the j value into the CPU register ebx, using the mov instruction.  Then with that amazing add instruction we add eax and ebx and the result is put back into eax.  And thats our answer, so finally we put this result into r, which is a variable we created and return from our add function.

 

 

inline numbers?

Now on occasion, you'll need to put values inline...not opcodes, but actual values like 2 or 12... now I bet your thinking...hmmm...I can do this

 

// ERROR ** CAUSES ERROR **

__asm

{

      0xfe

}

 

Why would you want to do this?  Well when you really get into asm, and I'm sure you will...you might want to put the actual optcode values into your assembly, as you might know the assembly opcode value for decrement eax, and you can put its value in (e.g. 0xfe, instead of dec ).  To do this, you use the keywork "_emit" and you would use it like this:

 

// WORKS

__asm

{

      _emit 0xfe

}

 

This code doesn't really do much, but it shows you how to use inline values in your code.

 

 

 

 

Using the Stack...push'ing and pop'ing

The best example of the stack is function calls... as when you pass parameters to functions, your in fact pushing them onto the stack and then calling the function.  So the best way for you to see this is with a simple example.  Now how many times have you used the "printf" function call?  You just call it with your string, and it just happens :)  Well let me show you how to do it with assembly:

 

// function declaration would look like this

// int __cdecl printf(const char *, ...);

 

#include <stdio.h>

 

// program entry point

void main()

{

      // Create a simple string

      char string[] = "Test String\n";

      // Set a pointer to our string

      char* pstr = (char*)&string;

 

      // Call function normally

      printf(pstr);

 

     

      // Call function using assembly

      __asm

      {

            push pstr

            call puts

 

            //add esp,4

      }

}

 

 

And the output would be:

Test String

Test String

Its not much... but it shows how you call a function... but what about that __cdecl thing?  Whats that do I hear you say?  Well that describes the calling convention, and describes who is responsible for tidying up the stack after we push'ed things onto it...the called function or us..  And if things are to be push'ed onto the stack from right to left or left to right... the 3 main types of calling convention are:

__cdecl

__stdcall

__fastcall

Thiscall

 

With the __cdecl calling convention, where responsible for restoring the stack, so on returning from our call we have to add 4 to the stack or pop the stack.  In the above example I've commented the 'add esp,4' out, where esp is the stack pointer register.  As we add to the stack, we decrement the stack pointer.  Of course commenting it out won't harm as our program ends after our call.

 

Something I discovered though, which is due to how Visual Studio generates the code, but if I pushed 'string' onto the stack and called the function, it would cause an error during execution.  As I could call the function with just 'string' and it would work...hmmm...but if I get the address of 'string' then pushed that it would work.  Something to be looked into I guess...

 

With a few extra lines, I thought I'd describe the various calling conventions... as its amazing how many times I get asked... "what does __stdcall mean" or "what is __cdecl"... etc ... so these few quick descriptions will help you with the stack and how to use the various calling conventions...

 

 

int __cdecl sumExample (int a, int b);

The main characteristics of __cdecl calling convention are:

  1. Arguments are passed from right to left, and placed on the stack.
  2. Stack cleanup is performed by the caller.
  3. Function name is decorated by prefixing it with an underscore character '_' .

 

 

int __stdcall sumExample (int a, int b);

The main characteristics of __stdcall calling convention are:

  1. Arguments are passed from right to left, and placed on the stack.
  2. Stack cleanup is performed by the called function.
  3. Function name is decorated by prepending an underscore character and appending a '@' character and the number of bytes of stack space required.

 

 

 

int __fastcall sumExample (int a, int b);

The main characteristics of __fastcall calling convention are:

  1. The first two function arguments that require 32 bits or less are placed into registers ECX and EDX. The rest of them are pushed on the stack from right to left.
  2. Arguments are popped from the stack by the called function.
  3. Function name is decorated by by prepending a '@' character and appending a '@' and the number of bytes (decimal) of space required by the arguments.

 

 

Thiscall is the default calling convention for calling member functions of C++ classes (except for those with a variable number of arguments).

The main characteristics of thiscall calling convention are:

  1. Arguments are passed from right to left, and placed on the stack. this is placed in ECX.
  2. Stack cleanup is performed by the called function.

 

 

On we go with our stack... another simple example of the stack in in order I think..... but this time...lets explain how we would push something on, then pop it off again.

 

 

 

#include <stdio.h>

 

// program entry point

void main()

{

      int i=10;

      // Display feedback output

      printf("start - i: %d \n", i);

      // Call function using assembly - push the value in i, onto the stack

      __asm

      {

            push i

      }

      i=30;

      printf("middle - i: %d \n", i);

 

      // now we restore the value back into i from the stack

      __asm

      {

            pop i

      }

      printf("end - i: %d \n", i);

}

 

 

And the output would look like this:

start - i: 10

middle - i: 30

end - i: 10

Its relatively simple to see what's happening, as we put a value into i, then display it...so you can see its actually there...  then using our assembly instruction, we pop it onto our stack.  Change variables value to 30... and of course display it... you never know... its always good to double check... and then we pop whats on the stack back into i ... which is our value from earlier!  And yup, its 10... the value we stuck in there at the start.... cool?

 

 

Simple Functions

Example's are always the best way to see how things work I thing...well well commented examples :)

 

Adding 2 Numbers With Assembly

#include <stdio.h>

 

// add two numbers using assembly

int addtwo(int x, int y)

{

      int r;

      __asm

      {

            mov eax, x

            mov ebx, y

            add eax, ebx

            mov r,   eax

      }

      return r;

}

 

// program entry point

void main()

{

      int i=10, j=20;

 

      int k = addtwo(i, j);

 

      printf("10 + 20 = %d", k);

}

 

 

Adding 3 Numbers With Assembly

#include <stdio.h>

 

// add two numbers using assembly

int addtwo(int x, int y, int z)

{

      int r;

      __asm

      {

            mov eax, x

            mov ebx, y

            mov ecx, z

            add eax, ebx

            add eax, ecx

            mov r,   eax

      }

      return r;

}

 

// program entry point

void main()

{

      int sum = addtwo(10, 20, 30);

 

      printf("sum = %d", sum);

}

 

 

AND'ing 2 Numbers Together With Assembly

int andtwo(int x, int y)

{

      int r;

      __asm

      {

            mov eax, x

            mov ebx, y

            and eax, ebx

            mov r,   eax

      }

      return r;

}

 

 

Well that's the basics for you.... you'll be not be so afraid to now and then put in a few lines of assembly code in your code... either to give that extra speed you need, or to show off to your friends..hehe.

 

 

 

 

 

 

 

 

 

 

 

 

 

 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.