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.
from Hacker News https://ift.tt/2QslBWa
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.