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.
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.
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
externaland 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 is
global _main extern _ExitProcess@4 section .text _main: push 0 call _ExitProcess@4It took me time to figure out why
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.exefrom Microsoft Visual C++, e.g.
link /entry:main helloworld.obj /subsystem:console /nodefaultlib kernel32.libThe
kernel32.libis necessary for the
ExitProcessto 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.zipfrom the depths of my hard-disc, I enabled it by calling its
vcvars32.bat. This sets the
LIBenvironment variables. For example the
kernel32.libis located in the library folder. As far as I know (from StackOverflow),
vcvars32.batis 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 -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.batI was able to create my
There exist standalone linkers like GoLink which might work, but I did not check any of them after I had success with both
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.)
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
ldis around 4kB, the one created by
link12 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.)
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: