I recently had a discussion with another member in which he indicated that assembly language has no real beginner friendly resources for one to pick it up, an assessment I agree with. I've always wanted to try my hand at making a simple guide to the basics of assembly language that's easy for anybody to understand and my recent discussion has re-ignited that desire. So today I've decided to just do it.
I thought about how I wanted to approach this because I myself tried for quite a long time to pick up assembly but it just wasn't clicking. In the end, it was a very simple article that wasn't really focusing on teaching assembly where it finally clicked for me. I had to really think about why after all materials I've read over the years did this very unassuming article finally got me to understand it. I believe is because of how very basic it was. It wasn't attempting to teach anything about assembly really. The article was showing us how create an executable function by using assembly as the function's source code. The assembly code itself was very bare-bones with a couple comments explaining what each line was doing. I believe it was because that article had very little noise. I believe this extreme simplicity was the key so I'm going to strive for simplicity in this example. No deep explanations or complicated samples.
So why VB6? For two reasons. One, because quite frankly VB6 programmers would likely be more interested in this kind of thing. Secondly, because I want to use The Trick's assembly VB6 Add-In. This Add-In cuts out a lot of the noise from assembly programming. No need to worrying about setting up segments or fiddling around like linkers. You can just jump right in and start writing assembly code. It's quick and easy to use. There's really no fuss.
Let's begin
The very first think I want you to do is install The Trick's assembler Add-In which you can download from this page:-
https://www.vbforums.com/showthread....sembler-Add-in
There's a link down at the bottom of the first post that takes you to a page with a setup file. Download, extract the contents and run Setup.exe. Now it's a RAR file so you will need WinRAR to extract it's contents. Make sure to close all instances of VB6 before running Setup.exe. After you run it and installed the Add-In, open VB6 and go to the Add-Ins menu and click on Add-In Manager. You should see Inline Assembler among the options. Select it and tick the Load on Startup option then click Ok to close the dialog. Restart the VB6 IDE and now you should see Inline Assembler in the Add-Ins menu. Now you're set.
The Basics of Assembly Language
Assembly language is a way of communicating with your computer's processor. To do this, you need to understand some basic concepts.
First, there are registers. These are like variables that the processor uses to store values. In our case, we'll be working with 32-bit registers called EAX, EBX, ECX, EDX, ESI, and EDI. We can perform math operations like addition and subtraction on these registers.
Next, there's the stack. The stack is an area of memory that we can use to store values temporarily. We can use the PUSH instruction to add a value to the top of the stack, and the POP instruction to remove the top value from the stack. For example, if we write "PUSH EAX", we'll add the current value of the EAX register to the top of the stack. Then, if we write "POP EBX", we'll remove the top value from the stack and store it in the EBX register.
There are also two special registers we should mention: EBP and ESP. EBP is the base pointer, which helps us keep track of the current stack frame. ESP is the stack pointer, which points to the top of the stack. We use these registers to keep track of where our data is stored on the stack.
Let's also talk about addressing modes. Addressing modes are used to specify the memory locations where data is stored in a computer's memory. One of the most basic addressing modes is "direct addressing," which involves specifying the memory location directly using its address. For example, "MOV EAX, [0x12345678]" moves the value stored at memory address 0x12345678 into the EAX register.
However, there are other addressing modes that allow for more flexibility. One such mode is "register addressing," which involves specifying a register as the memory location. For instance, "MOV EAX, EBX" moves the value stored in the EBX register into the EAX register.
Other addressing modes include indirect addressing, which involves using a memory address stored in a register or memory location to access the data, and immediate addressing, which involves specifying the data itself as an operand. Each addressing mode has its own advantages and disadvantages, and selecting the appropriate mode depends on the specific needs of the program or instruction being executed.
Let's write some assembly
Open a new instance of VB6, go to the Add-Ins menu and select Inline Assembler. It would then prompt you and create a module called Module1. You can rename this module if you so choose. This module is going to contain stubs which are just empty subs or functions that will be used to execute your assembly code.
A very basic function
Let's create a function that will simply return a Long. Write this stub in the module:-
Now go to the Add-Ins menu and select Inline Assembler again and you should see GetValue in the ComboBox. Select it and in the TextBox below, you can write your assembly code. Put this code in there:-
Now go to Form1 and add the following:-
Press F5 and you should see 23 printed to the immediate window. That's all there is to it. You just wrote a function in assembly language.
Let's do something more interesting
Let's take it up a notch and introduce functions with parameters. Add this stub to the module:-
Now use the Add-In to give Add this assembly code:-
Now test with this in Form_Load:-
Addition notes. Pay attention to the following from the above code:-
The EBP register contains an address and 8 bytes after that address is where we find the first argument. What the above code is doing is dereferencing EBP + 8 to obtain the value of the first argument. The syntax [ebp + 8] is called indirect addressing with a displacement. The value of EBP is the pointer and 8 is the displacement so it means 8 bytes after whatever address is in EBP.
How do we deal with ByRef?
Now lets try dealing with a parameter that is passed by reference. We will read the parameter's value, double it and write it back. Add this stub to the module:-
And give it this assembly using the Add-In:-
Test code for Form_Load:-
Closing remarks
I think I will wrap it up for now. We could do more interesting stuff but I think I will save that for a follow up post. This is really meant to help one get their feet wet in assembly so I don't want to go too wild yet.
You may also find that a lot of things are vague for example, why does [ebp + 8] mean the first argument? Why not 4, 10 or 20? There is a reason but you don't need to know the reason if this is your first foray into assembly. I don't want to confuse anyone with unnecessary details their first time around. I also opted for comments to explain how the assembly functions work instead of trying to explain all the details behind the "whys" or "hows" of it. Remember, I want to keep it as simple as possible. Get comfortable with the language first, play around with it and then you can start going deeper. There is a lot I still didn't cover but enough to get anyone started.
One final thing. I am by no means an assembly language expert, not even close, so please don't ask me about wiring up interrupt handlers or switching to real mode or any of that. That's a lot of expert level stuff that is way beyond my understanding.
I've attached the entire project. Make sure you have the Add-In installed before opening it in the VB6 IDE. Have fun :wave:
I thought about how I wanted to approach this because I myself tried for quite a long time to pick up assembly but it just wasn't clicking. In the end, it was a very simple article that wasn't really focusing on teaching assembly where it finally clicked for me. I had to really think about why after all materials I've read over the years did this very unassuming article finally got me to understand it. I believe is because of how very basic it was. It wasn't attempting to teach anything about assembly really. The article was showing us how create an executable function by using assembly as the function's source code. The assembly code itself was very bare-bones with a couple comments explaining what each line was doing. I believe it was because that article had very little noise. I believe this extreme simplicity was the key so I'm going to strive for simplicity in this example. No deep explanations or complicated samples.
So why VB6? For two reasons. One, because quite frankly VB6 programmers would likely be more interested in this kind of thing. Secondly, because I want to use The Trick's assembly VB6 Add-In. This Add-In cuts out a lot of the noise from assembly programming. No need to worrying about setting up segments or fiddling around like linkers. You can just jump right in and start writing assembly code. It's quick and easy to use. There's really no fuss.
Let's begin
The very first think I want you to do is install The Trick's assembler Add-In which you can download from this page:-
https://www.vbforums.com/showthread....sembler-Add-in
There's a link down at the bottom of the first post that takes you to a page with a setup file. Download, extract the contents and run Setup.exe. Now it's a RAR file so you will need WinRAR to extract it's contents. Make sure to close all instances of VB6 before running Setup.exe. After you run it and installed the Add-In, open VB6 and go to the Add-Ins menu and click on Add-In Manager. You should see Inline Assembler among the options. Select it and tick the Load on Startup option then click Ok to close the dialog. Restart the VB6 IDE and now you should see Inline Assembler in the Add-Ins menu. Now you're set.
The Basics of Assembly Language
Assembly language is a way of communicating with your computer's processor. To do this, you need to understand some basic concepts.
First, there are registers. These are like variables that the processor uses to store values. In our case, we'll be working with 32-bit registers called EAX, EBX, ECX, EDX, ESI, and EDI. We can perform math operations like addition and subtraction on these registers.
Next, there's the stack. The stack is an area of memory that we can use to store values temporarily. We can use the PUSH instruction to add a value to the top of the stack, and the POP instruction to remove the top value from the stack. For example, if we write "PUSH EAX", we'll add the current value of the EAX register to the top of the stack. Then, if we write "POP EBX", we'll remove the top value from the stack and store it in the EBX register.
There are also two special registers we should mention: EBP and ESP. EBP is the base pointer, which helps us keep track of the current stack frame. ESP is the stack pointer, which points to the top of the stack. We use these registers to keep track of where our data is stored on the stack.
Let's also talk about addressing modes. Addressing modes are used to specify the memory locations where data is stored in a computer's memory. One of the most basic addressing modes is "direct addressing," which involves specifying the memory location directly using its address. For example, "MOV EAX, [0x12345678]" moves the value stored at memory address 0x12345678 into the EAX register.
However, there are other addressing modes that allow for more flexibility. One such mode is "register addressing," which involves specifying a register as the memory location. For instance, "MOV EAX, EBX" moves the value stored in the EBX register into the EAX register.
Other addressing modes include indirect addressing, which involves using a memory address stored in a register or memory location to access the data, and immediate addressing, which involves specifying the data itself as an operand. Each addressing mode has its own advantages and disadvantages, and selecting the appropriate mode depends on the specific needs of the program or instruction being executed.
Let's write some assembly
Open a new instance of VB6, go to the Add-Ins menu and select Inline Assembler. It would then prompt you and create a module called Module1. You can rename this module if you so choose. This module is going to contain stubs which are just empty subs or functions that will be used to execute your assembly code.
A very basic function
Let's create a function that will simply return a Long. Write this stub in the module:-
Code:
Function GetValue() As Long
End Function
Code:
use32 ;32 bit code
push ebp ;Save previous frame pointer
mov ebp, esp ;Set up frame pointer for this function
mov eax, 23 ;VB6 expects the return value of the function
;to be in the EAX register.
mov esp, ebp ;Restore the stack pointer
pop ebp ;Restore the frame pointer of the calling function
ret ;Return from this function
Code:
Private Sub Form_Load()
Debug.Print GetValue
End Sub
Let's do something more interesting
Let's take it up a notch and introduce functions with parameters. Add this stub to the module:-
Code:
Function Add(ByVal a As Long, ByVal b As Long) As Long
End Function
Code:
use32 ;32 bit code
push ebp ;Save previous frame pointer
mov ebp, esp ;Set up frame pointer for this function
mov eax, [ebp + 8] ; Pull the first argument off the stack
; and store it in EAX
add eax, [ebp + 12] ;Add the value of the second argument to EAX
;The result will reside in EAX which also
;means it's the return value of the function
mov esp, ebp ;Restore the stack pointer
pop ebp ;Restore the frame pointer of the calling function
ret 8 ;Pop the arguments off the stack and return.
;We passed two Longs which is 8 bytes total so we
;pop 8 bytes.
Code:
Debug.Print Add(10, 4)
Code:
mov eax, [ebp + 8]
How do we deal with ByRef?
Now lets try dealing with a parameter that is passed by reference. We will read the parameter's value, double it and write it back. Add this stub to the module:-
Code:
Sub ChangeValue(ByRef a As Long)
End Sub
Code:
use32 ;32 bit code
push ebp
mov ebp, esp
mov eax, [ebp + 8] ;Read value from 1st argument and store it in EAX.
;Since this argument is passed by reference, what
;resides in EAX now is actually a pointer.
mov ecx, [eax] ;Dereference the pointer in EAX and store it in
;the ECX register
imul ecx, 2 ;Multiply that value by 2. The result is written back
;to ECX
mov [eax], ecx ;Copy the value from ECX to the memory address in
;EAX. This effectively returns the value through
;the ByRef parameter of the function
mov esp, ebp
pop ebp
ret 4 ;Pop the 4 byte argument off the stack.
Code:
Dim a As Long
a = 10
ChangeValue a
Debug.Print a
I think I will wrap it up for now. We could do more interesting stuff but I think I will save that for a follow up post. This is really meant to help one get their feet wet in assembly so I don't want to go too wild yet.
You may also find that a lot of things are vague for example, why does [ebp + 8] mean the first argument? Why not 4, 10 or 20? There is a reason but you don't need to know the reason if this is your first foray into assembly. I don't want to confuse anyone with unnecessary details their first time around. I also opted for comments to explain how the assembly functions work instead of trying to explain all the details behind the "whys" or "hows" of it. Remember, I want to keep it as simple as possible. Get comfortable with the language first, play around with it and then you can start going deeper. There is a lot I still didn't cover but enough to get anyone started.
One final thing. I am by no means an assembly language expert, not even close, so please don't ask me about wiring up interrupt handlers or switching to real mode or any of that. That's a lot of expert level stuff that is way beyond my understanding.
I've attached the entire project. Make sure you have the Add-In installed before opening it in the VB6 IDE. Have fun :wave: