Getting Started
Just by coincidence, one of the user groups in Vienna, the We Love Programming Languages group, dedicated one of its meetups to Assembly. There Angeliki Chrysochou showed a Hello World in Assembly which gave me a good idea what I had to do. During the following research I discovered how to write Hello World in Assembly under Windows on StackOverflow, which gave me all the information I needed to get started, or so I thought. In the end it still took me several hours to create a Hello World that worked, which is why I decided to write this summary.
Language
The modern Intel IA-32 flavour of 80x86 Assembly looks pretty much like the one I worked with in the 90's. I had no problem with that. Of course CPU architectures got more complex and you have to watch out for far more things when optimising for performance, e.g. branch prediction, pipeline stalls and so forth, but the general idea stayed the same.
The Assembler
Angeliki used NASM, the Netwide Assembler in her presentation and it looked good, so I tried it as well. It worked great and I did not check out other tools (like MASM). The command to translate (does Assembly get compiled?) an ASM file for Windows with NASM is
nasm -fwin32 helloworld.asmThis creates an OBJ file which needs to be linked.
Operation System Calls
As soon as you want to do anything regarding input, output or even exiting the current application, you need operation system calls. You have to search the MSDN Windows API Index for the calls you need and have to decorate them according to the Windows Application Binary Interface (ABI). (You can also use C functions, but that would not be pure Assembly, wouldn't it.) The decorated function name has to be
external
and its parameters are put on the stack in CDECL order. (In fact Windows 32 system calls are syscall.) The source of the shortest NASM Windows application isglobal _main extern _ExitProcess@4 section .text _main: push 0 call _ExitProcess@4It took me time to figure out why
ExitProcess
is _ExitProcess@4
but MessageBox
is _MessageBoxA@16
.The Linker
Finally the generated OBJ code has to be combined with the used system libraries into a single unified executable program. I found several ways for doing this under Windows. The native Windows way is to use
link.exe
from Microsoft Visual C++, e.g.link /entry:main helloworld.obj /subsystem:console /nodefaultlib kernel32.libThe
kernel32.lib
is necessary for the ExitProcess
to be found. Getting the linker was a bit of a hassle because you need to install Visual C++ or at least some Windows SDK to get it. I did not want to do that, but I remembered that I had used the command line part or VC 6.0 aka VC98 to compile native extensions for Ruby 1.8.6 before the Ruby DevKit became popular. After recovering VC98.zip
from the depths of my hard-disc, I enabled it by calling its vcvars32.bat
. This sets the PATH
, INCLUDE
and LIB
environment variables. For example the kernel32.lib
is located in the library folder. As far as I know (from StackOverflow), vcvars32.bat
is still available in VC++. I guess I would need a newer Kernel library to use the latest Windows 32 functions, but this was enough for Hello World and I was able to link my OBJ.Another way to link under Windows is to use Unix tool ports like MinGW, a minimalist development environment for native Microsoft Windows applications. MinGW 32 comes with
ld.exe
, e.g.ld -o helloworld.exe helloworld.obj -e_main -lkernel32 --enable-stdcall-fixup %SYSTEMROOT%\system32\kernel32.dllAgain I did not want to download and install huge tools, I just wanted to create a little Assembly Hello World. This should not be so hard. As I said before, MinGW is used to compile native C extensions for Ruby since 1.8.7, and I just used the one that came with the Ruby DevKit. After setting its
devkitvars.bat
I was able to create my helloworld.exe
.There exist standalone linkers like GoLink which might work, but I did not check any of them after I had success with both
link
and ld
. But in general I would prefer something small, like NASM which is just a single executable. (Edit: Yes Golink does work and is just a single executable, just what I need.)Execution
One beauty of Assembly is the size of created application. I remember one of my smallest DOS applications was around 20 bytes (!) in total, a COM application which did not require any linking. Agreed it did not do much, just turned off Num-Lock, but it was useful to me at that time. The Hello World's object file is 445 bytes and the executable
helloworld.exe
created by ld
is around 4kB, the one created by link
12 kB. (I did not check for any settings to optimise for size, remove debug information etc. - anyway the size of the compiled program does not matter.)Code
Here is the complete Assembly source code of the pure Windows IA-32 Hello World, pretty much like it was answered by caffiend,
STD_OUTPUT_HANDLE equ -11 %define NULL dword 0 extern _GetStdHandle@4 extern _WriteFile@20 extern _ExitProcess@4 global _main section .text _main: ; local variable bytesWritten equ -4 mov ebp, esp sub esp, 4 ; hStdOut = GetstdHandle(STD_OUTPUT_HANDLE) push STD_OUTPUT_HANDLE call _GetStdHandle@4 mov ebx, eax ; WriteFile(hstdOut, message, length(message), ; &bytesWritten, null); push NULL lea eax, [ebp + bytesWritten] push eax push (message_end - message) push message push ebx call _WriteFile@20 ; ExitProcess(0) push 0 call _ExitProcess@4 ; never here hlt message: db 'Hello, World', 10 message_end:
No comments:
Post a Comment