Information and Links
Join the fray by commenting, tracking what others have to say, or linking to it from your blog.
- Other Posts
- So, what now?
- FB == C++?
An Introduction to Inline Assembly and Registers
Without much dispute, FreeBASIC is one of the most powerfull BASIC compilers created to date. With the combination of its standard BASIC features to its advance features, (pointers, C/C++ library support, etc…) FreeBASIC supplies its users with the familiarity of classic BASIC, but ability beyond what BASIC users have experienced. However, even with FreeBASIC’s array of potent features, few of its users have ever embraced its most powerful feature, ‘Inline Assembly’.
Assembly is the first PC programming language. It is considered “low-level”, because it’s functionality and syntax is greatly dependant on the target system’s architecture. Whereas BASIC is considered “high-level”, because it’s functionality and syntax is designed to remain consistent despite its targeted system’s architecture. The advantage of high-level languages is their syntax and grammar greater resembles that of spoken languages. The greatest advantage of low-level languages is their speed. However, low-level languages, (such as Assembly) hardly resemble spoken languages. For this reason, they are often intimidating to even the more experienced programmers. Fortunately, programming FreeBASIC’s inline assembly is notably simpler than programming Assembly alone.
To inform FreeBASIC that a line of code is written in Assembly, it must be preceded by the keyword Asm. Such as:
Asm ...
If more than one line of Assembly is being used, it can be included with an Asm block:
Asm
...
End Asm
Keep in mind, all variables and procedures declared previous to the Asm statement or block are accessible by the Asmstatement or block. Which will be demonstrated.
As with any language, one of Assembly’s fundamentals is data transferring. However, in Assembly, data can never be transferred directly from one memory location to another. It must first be transferred to hardware-embedded variables known as registers. These ‘variables’ are Intel created. These registers have varying sizes, that range from 8bit to 32bit. The 8bit registers include AL, AH, BL, BH, CL, CH, DL, DH. The 16bit registers are AX, BX, CX, DX, BP, SP, DI, SI. Lastly, the 32bit registers consist of EAX, EBX, ECX, EDX, EBP, ESP, EDI, ESI. Each of these registers, (or hardware-embedded variables) serve specific purposes during certain circumstances. To avoid confusion their situation-specific purposes will be mentioned only when necessary.
In order to store data within any register, the chosen register’s size must be equal or greater than that of the data. For instance, an 8bit register can not hold 32bits of data, but a 32-bit register can, logically, hold 8 bits of data. To transfer data, one would use the Mov command, (also know as a “mnemonic” in Assembly). Its format is:
Mov SizeOverride Operand1, Operand2
SizeOverride is not required, but if included specifies the amount of data being transferred. The possible size overrides are:
- Byte (move 8 bits)
- Word (move 16 bits)
- DWord (move 32 bits, or a Single)
- QWord (move 64 bits, or a Double)
Both Operand1 and Operand2 can either be registers, immediate data, or memory locations. Immediate data is either a literal number, (such as 5 or 3), or a FreeBASIC variable’s name. How to specify memory locations will be covered shortly. There are certain criteria when transferring data that must be satisfied:
Operand1is always the destination of the moved dataOperand2is always the source of the moved data- both
Operand1andOperand2can be registers, (even the same register) but both registers must be of the same size Operand1can never be immediate dataOperand1andOperand2can not both be memory locations
A memory location is specified by simply including a register, certain combinations of registers, immediate data, or a combination of these within braces ([ ]). The only registers that can be used to specify a memory location on their own or in combination with immediate data include BX, SI, DI, EAX, EBX, ECX, EDX, EBP, ESI, EDI. However, the following pairs of registers can be used to specify a memory location by themselves or with immediate data:
- BX+SI
- BX+DI
- BP+SI
- BP+DI
In the case of a pair of registers the sum, (and only the sum) of the specified registers’ values will determine the memory location. To better understand data transferring let us study the various scenarios. Throughout these examples various registers will be used. For simplicity’s sake, assume these registers’s values have been preset as necessary. Suppose we need to move data from one register to another.
Asm Mov BL, AL
Now the register BL contains the same data as that of AL. Remember, when transferring data amongst registers both registers must be the same size. Now, let us see how to move the contents of a register to the memory location indicated by the register EAX:
Asm Mov [EAX], AL
When a register’s data is being transferred to memory, the register’s size dictates how much memory is effected. Also, notice that the registers’ sizes do not match. This is permitted, because the contents of register AL are not being transferred to register EAX, but rather to the memory location specified by EAX. Now, study this example:
Mov [BX+SI], AL
This example is quite similar to the previous. In this example though, the contents of register AL are stored at the memory location indicated by the sum of the registers BX and SI. Any of the valid pairs of registers previously mentioned may be used. These were randomly chosen solely for example. A register’s data can even be stored in a variable. Let us assume there is a FreeBASIC variable named Number, of data type Long (32-bit), that has the value 5 and that register CL contains the value 10.
Dim Number As Long = 5
Asm Mov [Number], CL
The intention would be to store the value 10 in the variable Number. Keep in mind, though, when transferring data from a register to memory the register’s size dictates how much memory is stored and effected. In this example the contents of register CL is stored in the variable Number. However, since Number is 32 bits and CL is 16 bits only half of Number is altered. This can cause different results than expected. To ensure that the appropriate value is stored in Number, use a register of the same size. Let us now assume ECX contains the value 10.
Asm Mov [Number], ECX
Now, since both Number and register ECX are 32-bit, all of Number is effected and the expected outcome of this statement would be accurate. The variable Number now holds the value 10. One can use a literal number to specify a memory location. Such as:
Asm Mov [012345], ECX
However, this can be dangerous. Remember, the operating system and other software are using memory as well. Altering their reserved memory can have unpredictable results. Let us now view how to transfer data from memory to a register.
Asm Mov ECX, [BX+SI]
By default, The register’s size determines the amount of data that will be transferred to it. Therefore, in this example, 32 bits, (from the memory location indicated by the sum of registers BX and SI) are stored in register ECX. To ensure that a certain number of bytes is transferred use a size-override.
Asm Mov Word ECX, [BX+SI]
Now 16 bits of data would be transferred from the specified memory location to the chosen register. However, 16 bits is only half of register EAX’s size. The outcome of this statement may still not be as predicted.
Asm Mov DWord ECX, [BX+SI]
In this example 32 bits of data would be transferred from the specified memory location to register ECX. Since the register’s size and the size of data being stored into the register are the same this statements outcome would be accurate. Of course, a variable’s data can be transferred to a register as well. Let us assume, again, there is a FreeBASIC variable named Number, of data type Long, that has the value 5.
Dim Number As Long = 5
Asm Mov ECX, [Number]
Register ECX would now contain the value 5. Now, assume there is a variable named Number, of data type Double, that has the value 5.1234 . Since variables of data type Double are 64 bits in size, and none of the registers mentioned are 64 bits, one could not transfer the contents of a variable of data type double to any of the mentioned registers. However, the address of a variable of data type Double could, indeed, be stored into a 32-bit register. Although mathematics (both integer and floating-point) will not be discussed as of yet, this is important to remember when the subject is discussed.
Dim Number As Double = 5.1234
Dim Adr As Long = @Number
Asm Mov EAX, [Adr]
Register EAX now contains the address of the FreeBASIC variable Number. Notice the use of a 32-bit register. Obviously, 32-bit operating systems use 32-bit addresses. Using a 32-bit register ensures the whole address is stored. The case of variables of data type String is similar. Since strings can be of virtually any size, only a string’s address should be stored in a 32-bit register. Such as:
Dim MyString As String = "some text"
Dim Adr As Long = StrPtr(MyString)
Asm Mov EAX, [Adr]
Notice that the FreeBASIC function StrPtr was used to retrieve the string’s address. This is because strings in FreeBASIC include headers, (or ‘descriptors’). To retrieve the actually location of the string’s characters, StrPtr must be used.
This concludes general data transferring using FreeBASIC’s inline Assembly. With this knowledge, one is prepared for Integer Mathematics using inline Assembly. Which will be discussed in the next lesson of this tutorial.