Немногие смело признают, что могут ошибиться в идентификации ассемблера, ведь это по-своему особенный язык. Однако не спешите с выводами, а лучше пройдите небольшой тест, который не просто позволит взгялуть на него в ином свете, но и проверит вашу осведомленность в этой сфере.
Программирование на ассемблере сегодня в лучшем случае занимает нишевое положение и чаще воспринимается как неоправданно педантичное, требовательное и затратное даже для своей ниши.
Ассемблер непрост. Он недружелюбен. Программирование на этом языке происходит медленно и зачастую сопряжено с ошибками — таково общепринятое мнение.
К сожалению, в современной цифровой среде исходит это мнение от людей, которые, как правило, плохо представляют, как реально выглядят современные языки ассемблера. Этот стиль программирования не застрял в 50-х, он развивался вместе с высокоуровневыми языками, вбирая в себя структурные, функциональные и объектно-ориентированные элементы. Он отлично дружит с современными API и DOM. Конечно же, принципиально это низкоуровневый язык, но вы можете с тем же успехом создавать поверх него и высокоуровневые абстракции.
Честно говоря, я даже не уверен, что кто-нибудь сможет легко отличить код ассемблера от какого-нибудь высокоуровневого кода без помощи гугла. Вот вы сможете?
1. GUI
Ниже приведен фрагмент кода. Он создает окно с WinAPI и запускает для него цикл обработки сообщений.
Прошу вас, ознакомьтесь с ним и ответьте, написан ли он на одном из видов ассемблера или же на высокоуровневом языке?
nMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX ; создает локальные переменные в стеке
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX ; заполняет значения в членах 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 ; регистрирует класс window
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 ; отображает окно на рабочем столе
invoke UpdateWindow, hwnd ; обновляет клиентскую область
.WHILE TRUE ; вход в цикл сообщений
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam ; возврат кода выхода в eax
ret
WinMain endp
Источник: Iczelion's Win32 Assembly Homepage, Tutorial 3: A Simple Window. win32assembly.programminghorizon.com/tut3.html
Источник: Iczelion's Win32 Assembly Homepage, Tutorial 3: A Simple Window. win32assembly.programminghorizon.com/tut3.html
2. Библиотеки
Ниже дан пример функциональной библиотеки. Функция “add” просто складывает два целых числа и возвращает их сумму.
(module
(func $add (param $lhs i32) (param $rhs i32) (result i32)
get_local $lhs
get_local $rhs
i32.add)
(export "add" (func $add))
)
Источник: примеры WebAssembly с официального сайта: developer.mozilla.org/en-US/docs/WebAssembly.
Источник: примеры WebAssembly с официального сайта: developer.mozilla.org/en-US/docs/WebAssembly.
3. Алгоритмы
Это реализация алгоритма TPK. Она содержит функцию, несколько циклов, массив и инструкцию вывода в консоль.
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
источник: The Early Development Of Programming Languages by Donald E. Knuth, Luis Trabb Pardo, 1976.
Кстати, TPK означает «Typical Pardo Knuth» (типичный Пардо Кнут, от имени его создателя) Это не настоящий алгоритм, и создавался он для демонстрации нескольких языков в одном примере.
источник: The Early Development Of Programming Languages by Donald E. Knuth, Luis Trabb Pardo, 1976.
Кстати, TPK означает «Typical Pardo Knuth» (типичный Пардо Кнут, от имени его создателя) Это не настоящий алгоритм, и создавался он для демонстрации нескольких языков в одном примере.
4. Структурное программирование
Вот пример вычисления суперскалярной суммы.
v0 = my_vector // нам нужна горизонтальная сумма следующего
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
}
// Теперь сумма представлена скаляром в v0
Источник: примеры кода из ForwardCom: An open-standard instruction set for high-performance microprocessors by Agner Fog.
Источник: примеры кода из ForwardCom: An open-standard instruction set for high-performance microprocessors by Agner Fog.
5. Еще структурное программирование
Это решение задачи о восьми ферзях с выводом в консоль. Его платформенная зависимость минимальна, но при этом оно не обогащено высокоуровневыми возможностями вроде классов, шаблонов или встроенных контейнеров.
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
$)
Источник: BCPL From Wikipedia, the free encyclopedia.
Источник: BCPL From Wikipedia, the free encyclopedia.
6. ООП (с классами и методами)
Вот ассемблер .NET (не путать с ассемблером в «языке ассемблера»). Он состоит из одного модуля с одним классом, имеющим один метод, который выводит в консоль “Hello World”.
// 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
// Размер кода 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
} // конец метода Hello::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Размер кода 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // конец метода Hello::.ctor
} // конец класса Hello
Вы вольны использовать все возможности .NET: GUI, обращение к базе данных, сетевые функции – все это при одновременном наличии низкоуровневого контроля деталей. Да, такой вариант может показаться чересчур многословным и неоправданно подробным, но все равно он представляет весьма мощный вариант ассемблера. Помимо классов и методов в качестве встроенных типов он предлагает исключения и строки.
Источник: docs.microsoft.com/en-us/dotnet/framework/tools/ilasm-exe-il-assembler.
Бессовестная реклама: пару лет назад я начал вводить в ILAsm макросы, создавая MILasm. Это вполне рабочее доказательство концепции. С ним интересно играться, хотя для продакшена он не совсем готов ввиду наследственных проблем с производительностью.
Вы вольны использовать все возможности .NET: GUI, обращение к базе данных, сетевые функции – все это при одновременном наличии низкоуровневого контроля деталей. Да, такой вариант может показаться чересчур многословным и неоправданно подробным, но все равно он представляет весьма мощный вариант ассемблера. Помимо классов и методов в качестве встроенных типов он предлагает исключения и строки.
Источник: docs.microsoft.com/en-us/dotnet/framework/tools/ilasm-exe-il-assembler.
Бессовестная реклама: пару лет назад я начал вводить в ILAsm макросы, создавая MILasm. Это вполне рабочее доказательство концепции. С ним интересно играться, хотя для продакшена он не совсем готов ввиду наследственных проблем с производительностью.
7. ООП (с объектами и сообщениями)
Это пример TCP-сервера. В нем есть объекты и методы, а работает он в собственной среде.
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
!!
Источник: Building a simple chat server with GNU Smalltalk using class inheritance из smalltalk.gnu.org/wiki/examples
Источник: Building a simple chat server with GNU Smalltalk using class inheritance из smalltalk.gnu.org/wiki/examples
Заключение
Современное программирование на ассемблере не обязательно связано с инструкциями процессора и регистрами. Да, код всегда начинается с низов, но его можно оснастить функциями, классами и макросами, сделав до нужной степени высокоуровневым.
Программировать на этом языке не всегда трудно, и он не всегда оказывается чрезмерно педантичен. Просто каждому нужно подобрать подходящий для работы уровень.
Автор: Дмитрий Брайт