This is Words and Buttons Online — a collection of interactive #tutorials, #demos, and #quizzes about #mathematics, #algorithms and #programming.

Can you tell an assembly language when you see one?

Assembly programming is nowadays seen as niche at best. And more often than not as needlessly meticulous, demanding, and wasteful even for its niches.

Assembly is hard. It is unfriendly. Programming in assembly language is slow and error-prone. This is conventional wisdom.

Unfortunately, these days this wisdom is mostly nurtured by people who have little or no idea of what modern assembly languages look like. Assembly programming didn't stay in the 50s, it evolved along with high-level languages incorporating structural, functional, and objective-oriented programming elements. It plays well with modern APIs and DOMs. It is, of course, conceptually low-level but you can build rather high-level abstractions on top of it as well.

In fact, I'm not even sure that anyone can easily tell assembly code from some high-level code without googling. Can you?

1. GUI

Here is a piece of code. It creates a window with WinAPI and starts a message processing loop for it.

Please read it and conclude if it's written in some variant of assembly or in some high-level language.

nMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX       ; create local variables on stack 
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX      ; fill values in members of wc 
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc        ; register our window class 
    invoke CreateWindowEx,NULL,
        ADDR ClassName, ADDR AppName,\
        WS_OVERLAPPEDWINDOW,\
        CW_USEDEFAULT, CW_USEDEFAULT,\
        CW_USEDEFAULT, CW_USEDEFAULT,\
        NULL, NULL, hInst, NULL
    mov   hwnd,eax
    invoke ShowWindow, hwnd,CmdShow        ; display our window on desktop 
    invoke UpdateWindow, hwnd              ; refresh the client area

    .WHILE TRUE                            ; Enter message loop 
                invoke GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                invoke TranslateMessage, ADDR msg
                invoke DispatchMessage, ADDR msg
   .ENDW
    mov     eax,msg.wParam                 ; return exit code in eax 
    ret
WinMain endp

2. Libraries

This is an example of one function library. The function “add” simply adds two integer numbers and returns their sum.

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add)
  (export "add" (func $add))
)

3. Algorithms

This is a TPK algorithm implementation. It contains a function, a few loops, an array, and some console output.

1   c@VA t@IC x@½C y@RC z@NC
2   INTEGERS +5 →c
3           →t
4       +t      TESTA Z
5       -t
6               ENTRY Z
7   SUBROUTINE 6→z
8       +tt→y→z
9       +tx→y→x
10  +z+cx   CLOSE WRITE 1

11  a@/½ b@MA c@GA d@OA e@PA f#HA i@VE x@ME
12  INTEGERS +20 →b +10 →c +400 →d +999 →e +1 →f
13  LOOP 10n
14      n→x
15  +b-x→x
16      x→q
17  SUBROUTINE 5 →aq
18  REPEAT n
16      +c →i
20  LOOP 10n
21      +an SUBROUTINE 1 →y
22      +d-y TESTA Z
23      +i SUBROUTINE 3
24      +e SUBROUTINE 4
25              CONTROL X
26              ENTRY Z
27      +i SUBROUTINE 3
28      +y SUBROUTINE 4
29              ENTRY X
30      +i→f→i
31  REPEAT n
32  ENTRY A CONTROL A WRITE 2 START 2

4. Structural programming

Here is an example of super-scalar sum calculation.

v0 = my_vector              // we want the horizontal sum of this
int64 r0 = get_len ( v0 )
int64 r0 = round_u2 ( r0 )
float v0 = set_len ( r0 , v0 )
while ( uint64 r0 > 4) {
	uint64 r0 > >= 1
	float v1 = shift_reduce ( r0 , v0 )
	float v0 = v1 + v0
}
// The sum is now a scalar in v0  

5. More structural programming

This is a queen's problem solution with console output. It has very little platform dependency, but it doesn't have a lot of high-level features like classes, templates, or built-in containers either.

GET "LIBHDR"

GLOBAL $(
        COUNT: 200
        ALL: 201
$)

LET TRY(LD, ROW, RD) BE
        TEST ROW = ALL THEN
                COUNT := COUNT + 1
        ELSE $(
                LET POSS = ALL & ~(LD | ROW | RD)
                UNTIL POSS = 0 DO $(
                        LET P = POSS & -POSS
                        POSS := POSS - P
                        TRY(LD + P << 1, ROW + P, RD + P >> 1)
                $)
        $)

LET START() = VALOF $(
        ALL := 1
        FOR I = 1 TO 12 DO $(
                COUNT := 0
                TRY(0, 0, 0)
                WRITEF("%I2-QUEENS PROBLEM HAS %I5 SOLUTIONS*N", I, COUNT)
                ALL := 2 * ALL + 1
        $)
        RESULTIS 0
$)

6. OOP (the one with classes and methods)

Here is a .NET assembly (not to be confused with assembly as in “assembly language”). It consists of one module with one class having one method that prints “Hello World” to the console output.

// Metadata version: v2.0.50215
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
  .ver 2:0:0:0
}
.assembly sample
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices
    .CompilationRelaxationsAttribute::.ctor(int32) =
      ( 01 00 08 00 00 00 00 00 )
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module sample.exe
// MVID: {A224F460-A049-4A03-9E71-80A36DBBBCD3}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x02F20000

// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Hello
       extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World!"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  } // end of method Hello::Main

  .method public hidebysig specialname rtspecialname
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Hello::.ctor

} // end of class Hello

7. OOP (the one with objects and messages)

This is a TCP server example. It has objects, methods and it works in its own environment.

Namespace current addSubspace: #SimpleTCP!
Namespace current: SimpleTCP!

"A simple TCP server"
Object subclass: #Server
  instanceVariableNames: 'serverSocket socketHandler'
  classVariableNames: ''
  poolDictionaries: ''
  category: ''!

!Server class methodsFor: 'instance creation'!

new: aServerSocket handler: aHandler
  | simpleServer |
  simpleServer := super new.
  simpleServer socket: aServerSocket.
  simpleServer handler: aHandler.
  simpleServer init.
  ^simpleServer
!!

!Server methodsFor: 'initialization'!

init
  ^self
!!

!Server methodsFor: 'accessing'!

socket
  ^serverSocket
!

socket: aServerSocket
  serverSocket := aServerSocket.
  ^self
!

handler
  ^socketHandler
!

handler: aHandler
  socketHandler := aHandler.
  ^self
!!

!Server methodsFor: 'running'!

run
  | s |
  [
    serverSocket waitForConnection.
    s := (serverSocket accept).
    self handle: s
  ] repeat
!

!Server methodsFor: 'handling'!

handle: aSocket
  socketHandler handle: aSocket
!!
    

Conclusion

Assembly programming is not necessary all about processor instructions and registers anymore. Yes, it always starts ground-low, but it can be leveraged with functions, classes, and macros to be as high-level as you want.

Programming in assembly is not always that hard, and it is not always that meticulous. You just have to pick the right level to be at.