Ms-dos и tasm 2.0. часть 7. данные

Параметры функции.

Функции в ассемблере — это часть кода, которая решает конкретную задачу или несколько, объединённых одной целью задач. Функция может вызываться без дополнительного дублирования кода. Человек способен помнить, воспринимать  и использовать ограниченное число информации. Для облегчения понимания и создания кода его структурируют — дробят на определенные части. Функция — один из вариантов дробления кода — первый шаг к абстракции программы, упрощающий её структуру. Функция включает параметры.

Параметры функции:

  • Параметры ввода (Input Parameters или просто In) — может быть сколько угодно.
  • Параметры вывода (Output Parameters или просто Out) — может быть сколько угодно.
  • Возвращаемое значение (Return value или просто Return) — только одно.

Таким образом, «стандартная» функция «MyFunc» имеет вид:

return MyFunc (In,  In, Out, Out, In, Out,…);

Указанная (Си-подобная) структура функции условна. Любой из параметров может отсутствовать. Функция вообще может не иметь параметров.

Стейтменты

В ассемблере есть три вида стейтментов:

   Выполняемые инструкции (или просто «инструкции») — сообщают процессору, что нужно делать. Каждая инструкция хранит в себе код операции (или «опкод») и генерирует одну инструкцию на машинном языке.

   Директивы ассемблера — сообщают программе об аспектах компиляции. Они не генерируют инструкции на машинном языке.

   Макросы — являются простым механизмом вставки кода.

В ассемблере на одну строку приходится один стейтмент, который должен соответствовать следующему формату:

mnemonic

1 меткаmnemonicоперанды;комментарий

Базовая инструкция состоит из названия инструкции () и операндов (они же «параметры»). Вот примеры типичных стейтментов ассемблера:

INC COUNT ; выполняем инкремент переменной памяти COUNT

MOV TOTAL, 48 ; перемещаем значение 48 в переменную памяти TOTAL

ADD AH, BH ; добавляем содержимое регистра BH к регистру AH

AND MASK1, 128 ; выполняем операцию AND с переменной MASK1 и 128

ADD MARKS, 10 ; добавляем 10 к переменной MARKS
MOV AL, 10 ; перемещаем значение 10 в регистр AL

1
2
3
4
5
6
7
8
9
10

INC COUNT;выполняеминкрементпеременнойпамятиCOUNT

MOV TOTAL,48;перемещаемзначение48впеременнуюпамятиTOTAL

ADD AH,BH;добавляемсодержимоерегистраBHкрегиструAH

ANDMASK1,128;выполняемоперациюANDспеременнойMASK1и128

ADD MARKS,10;добавляем10кпеременнойMARKS

MOV AL,10;перемещаемзначение10врегистрAL

Макросредства Ассемблера (макросы) и Си.

Для облегчения написания кода на Ассемблере, часто (а практически всегда) используются так называемые «макро средства» или «макросы». Благодаря им низкоуровневый язык программирования превращается в своеобразный структурный язык программирования, где основным блоком кода является функция. Ассемблер с использованием макросов очень похож на более высокоуровневый язык программирования Си. Язык Си,  в принципе, является следствием обобщения наиболее часто используемых ассемблерных макросов.

Простейшее приложение Win32 API на языке Си (Pelles C):

#include <windows.h>
#pragma comment(linker, «/ENTRY:_WinMain»)

void __cdecl _WinMain(void);

void _WinMain()
{
MessageBox(NULL,»Text»,»Caption»,MB_OK);
return;
}

1
2
3
4
5
6
7
8
9
10

#include <windows.h>
#pragma comment(linker, «/ENTRY:_WinMain»)
 

void__cdecl   _WinMain(void);

void_WinMain()

{

MessageBox(NULL,»Text»,»Caption»,MB_OK);

return;

}

Простейшее приложение Win32 API на языке Ассемблер (MASM32):

.586
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc

includelib user32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD

.data
szText db «Text»,0
szCaption db «Caption»,0

.code
start:
invoke MessageBox,NULL,addr szText,addr szCaption,MB_OK
ret
end start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

.586

.modelflat,stdcall

optioncasemapnone

   includewindows.inc

   includeuser32.inc

   includelibuser32.lib

WinMainprotoDWORD,DWORD,DWORD,DWORD

.data

   szTextdb»Text»,

   szCaptiondb»Caption»,

.code

start

invokeMessageBox,NULL,addrszText,addrszCaption,MB_OK

ret

endstart

Сегментная адресация.

Вы уже заметили, что указатель на код и данные состоит из двух частей: пары сегмента и смещения, разделённых двоеточием. Это связано с так называемой сегментной адресацией, используемой микропроцессора 8088, на базе которого работают современные Intel и  AMD процессоры. В этом микропроцессоре используется сегментная организация памяти.

Фрагмент нашей программы, пропущенной через отладчик.

Имеется четыре сегментных регистра, к которым микропроцессор имеет одновременный доступ. Базовые адреса сегментов находятся в сегментных регистрах:

  • CS Регистр сегмента кода
  • DS Регистр сегмента данных
  • SS Регистр сегмента стека
  • ES Регистр дополнительного сегмента

Для доступа к данным внутри сегмента обращение производится относительно начала сегмента линейно, т.е. начиная с 0 и заканчивая адресом, равным размеру сегмента. Для обращения к любому адресу в программе, компьютер складывает адрес в регистре сегмента и смещение. Например, первый байт в сегменте кодов имеет смещение 0, второй байт – 1 и так далее.

Физический адрес, то есть непосредственный указатель на область памяти, принято записывать парой этих значений, разделенных двоеточием СЕГМЕНТ:СМЕЩЕНИЕ, например:
сs:014А
сs:0151
сs:0153

Как неоднократно говорилось, программа типа *.COM представляет собой практически живой образ данных и машинных команд, предназначенных для выполнения процессором. Отображается в оперативную память без изменений. Начальный адрес загрузки в память — 0100h. Все сегменты имеют одно значение — работают с одним блоком памяти (в нашем примере он находится по адресу 4CEAh).

Указатель на память|Значение указателя |Значение указателя
(смещение) |(цифры для машины) |(код или данные для нас)
——————-|——————-|
сs:014F |8BF8 |mov di,ax
сs:0151 |8A3D |mov bh,
сs:0153 |33C0 |xor ax,ax

ds:0248 |24h |$
ds:0249 |00h |
ds:024A |3Ah |:

1
2
3
4
5
6
7
8
9
10

Указательнапамять|Значениеуказателя|Значениеуказателя

(смещение)|(цифрыдлямашины)|(кодилиданныедлянас)

——————-|——————-|

сs014F|8BF8|movdi,ax

сs0151|8A3D|movbh,di

сs0153|33C0|xorax,ax

ds0248|24h|$

ds0249|00h|

ds024A|3Ah|

Указатель в программировании — одно из основополагающих понятий, уяснение которого является обязательным для написания кода с применением любого языка. К программированию в системе Windows переходите, только основательно проштудировав данный вопрос.

Разница между директивами и командами Ассемблера

Итак, из п. 2.4.1 мы уже знаем, что в языке Ассемблера существуют команды и директивы, формат которых практически одинаков — см. рис. 2.12. В командах Имя интерпретируется как метка, поэтому за ней всегда ставится символ двоеточия ». Для Ассемблера в качестве имени (идентификатора) допускаются следующие символы:

  • Латинские буквы от A(a) до Z(z) — РЕГИСТР БЕЗРАЗЛИЧЕН. Ассемблер сам по себе НЕ различает ПРОПИСНЫЕ и строчные буквы. Но при использовании ассемблерных модулей в программе на алгоритмическом языке, чувствительном к регистру букв, например, в С/С++, буквы в разных регистрах будут разными.
  • Цифры от 0 до 9.
  • Специальные символы: ? . @ _ $.

Имя в Ассемблере может начинаться с любого допустимого символа, кроме цифры. Если имя содержит символ точки ‘.’, то он должен быть ПЕРВЫМ символом. Имя НЕ может быть зарезервированным в Ассемблере словом (имя машинной команды или директивы).

С некоторыми директивами мы с вами уже успели и поработать — см. примеры 2.9 и 2.10.

При ассемблировании между директивами и командами существует одно принципиальное различие. Директивы (псевдооператоры, псевдокоманды)управляют работой компилятора или компоновщика, а НЕ микропроцессора. Они используются, например, для сообщения компилятору, какие константы и переменные применяются в программе и какие имена мы им дали, какой сегмент является кодовым, а какой — сегментом данных или стека, в каком формате выводить листинг исходного кода программы и прочее. Большинство директив НЕ генерирует машинных команд (объектный код).

Команда Ассемблера всегда генерирует машинный код.

Директивы имеют разный синтаксис в режимах MASM (поддерживается компиляторами Microsoft Assembler — masm и Borland (Turbo) Assembler — tasm) и Ideal (поддерживается компилятором tasm) — см. приложение 5.

При описании синтаксиса директив и команд обычно используют специальный язык, известный всем профессиональным программистам, — язык Бэкуса-Наура (названный так в честь этих двух достойных ученых). Мы будем использовать его упрощенный вариант:

  • Терминальные элементы языка (название директив и команд, разделительные знаки) будем выделять жирным шрифтом.
  • Нетерминальные элементы (название параметров, операндов и других атрибутов) выделим курсивом.
  • Комментарии будем писать обычным шрифтом.
  • Необязательные элементы заключаются в квадратные скобки []. В синтаксисе языка Ассемблера есть и терминальный элемент квадратные скобки [] — в этом случае это будет особо ПОДЧЕРКНУТО.
  • Повторяющиеся элементы синтаксической структуры описываются многоточием (…).

Директив достаточно много. Практически каждая версия компилятора что-то из директив добавляет или немного изменяет синтаксис. Для определенности мы в данной главе остановимся на синтаксисе основных директив в более универсальном формате MASM для компиляторов masm-6.12 (или выше) и tasm-3.1 (или выше). Кроме того, известные компиляторы с языка программирования С/С++ (Borland C++, Visual C++) выдают ассемблерный листинг именно в формате MASM. И очень скоро мы научимся его читать…

О компиляторах

Какую операционную систему вы бы хотели использовать?

Windows DOS Linux BSD QNX MacOS, работающий на
процессоре Intel/AMD
FASM x x x x
GAS x x x x x x
GoAsm x
HLA x x
MASM x x
NASM x x x x x x
RosAsm x
TASM x x

Качество документации

Документация Комментарии
FASM Хорошая Большую часть свободного времени автор отдает в разработку инновационного FASMG. Тем не менее, автор обеспечивает поддержку FASM время от времени обновляет мануалы, а новые функции описывает на собственном форуме. Документацию можно считать достаточно хорошей. Веб-страница документации.
Gas Плохая документирован слабо и документация, скорее, имеет «общий вид». gas ― это ассемблер, который был разработан, чтобы можно было легко писать код для разных процессоров. Документация, которая существует, в основном описывает псевдо коды и ассемблерные директивы. В режиме работы «intel_syntax» документация практически отсутствует. Книги, в которых используется синтаксис «AT&T»: «Программирование с нуля» Джонатона Бартлетта и «Профессиональный язык ассемблера» Ричарда Блюма, Konstantin Boldyshev asmutils — Linux Assembly.
GoAsm Слабая Большая часть синтаксиса описана в руководстве, и опытный пользователь найдет то, что ищет. Множество руководств и размещено на сайте (http://www.godevtool.com/). Несколько учебников GoAsm:
  • Bill Aitken’s tutorials for using GoAsm and the IDE
  • мануал Роберта Cordonnier на французском
  • справочник Патрика Ruiz
HLA Обширая HLA имеет справочное руководство на 500 страниц. Сайт содержит десятки статей и документацию по HLA.
MASM Хорошая Компанией Microsoft написано значительное количество документацию для MASM, существует большое количество справочников написанных для этого диалекта.
NASM Хорошая Авторы NASM больше пишут программное обеспечение для этого диалекта, оставляя написание руководства на «потом». NASM существует достаточно долго, поэтому несколько авторов написали руководство для NASM Джефф Дунтеман (Jeff Duntemann) «Assembly Language Step-by-Step: Programming with Linux», Jonathan Leto «Writing A Useful Program With NASM», на русском языке есть книга Столярова (Сайт А.В. Столярова).
RosAsm Слабая не очень интересные «онлайновые учебники».
TASM Хорошая Компания Borland в свое время выпускала отличные справочные руководства, для TASM были написаны справочные руководства авторами-энтузиастами не связанными с фирмой Borland. Но Borland больше не поддерживает TASM, поэтому большая часть документации, предназначенная для TASM, не печатается и ее становится всё труднее и труднее найти.

Учебники и учебные материалы

Комментарии
FASM Несколько учебников, в которых описывается программирование на FASM:
  • FASM на asmworld
  • Цикл статей «Ассемблер под Windows для чайников»
  • Сайт на narod’е
  • Уроки Iczelion’а от Sulaiman Chang на диалекте FASM
  • Понимание FASM
  • Программирование на языке Assembler в FASM
  • Создание заплаток на ассемблере FASM
  • Норсеев С.А. «Разработка оконных приложений на FASMе»
  • Руслан Аблязов «Программирование на ассемблере на платформе x86-64»
Gas Учебник с использованием синтаксиса AT&TУчебник Ассемблер в Linux для программистов C
HLA 32-разрядная версия «The Art of Assembly Language Programming» (существует и в электронной, и в печатной форме), программирование под Windows или Linux
MASM большое количество книг по обучению программированию под DOS. Не очень много книг о программировании под Win32/64 Пирогов, Юров, Зубков, Фленов
NASM много книг, посвященных программированию в DOS, Linux, Windows. В книге Джеффа Дунтемана «Assembly Language Step-by-Step: Programming with Linux» используется NASM для Linux и DOS. Учебник Пола Картера использует NASM (DOS, Linux).
TASM Как и для MASM, для TASM было написано большое количество книг на основе DOS. Но, так как Borland больше не поддерживает этот продукт, писать книги об использовании TASM перестали. Том Сван написал учебник, посвященный TASM, в котором было несколько глав о программировании под Windows.

3.4.2 Символьные константы

Символьная константа содержит от одного до четырех символов, заключенных в
одиночные или двойные кавычки. Тип кавычек для NASM несущественен, поэтому если
используются одинарные кавычки, двойные могут выступать в роли символа и, соответственно,
наоборот.

Символьная константа, содержащая более одного символа, будет загружаться в
обратном порядке следования байт: если вы пишете

сгенерированной константой будет не 0x61626364,
а 0x64636261, поэтому если сохранить эту константу
в память, а затем прочитать, получится снова abcd,
но никак не dcba. Это также влияет на инструкцию
CPUID Пентиумов (см. ).

Константа в ассемблере.

Мы уже знаем, что значение (конкретное число) можно присвоить переменной, предварительно определив размер этой переменной в байт, слово, двойное слово и т.д.:

  • DB — Define Bite.
  • DW — Define Word.
  • DD — Define Double Word.
  • DQ — Define Quatro Bites.
  • DT — Define Tetro Bites.

Константа — символ, синоним конкретного числа (выражения, строки), которое, в отличие от переменной нельзя изменить.

Для задания констант применяются обозначения:

  • .const — все данные будут восприниматься как константы, до момента изменения сегмента (.data, .code и т.п.).
  • = (знак равно).
  • equ (может использоваться для создания идентификатора, константного выражения, строки).

2.5 Константы

NASM знает четыре различных типа констант: числовые, символьные, строковые и с плавающей точкой.

2.5.1 Числовые константы

Числовая константа — это просто число. NASM позволят определять числа в различных системах счисления и различными способами: вы можете использовать суффиксы H, Q или O, и B для шестнадцатеричных, восьмеричных и двоичных чисел соответственно; можете использовать для шестнадцатеричных чисел префикс 0x в стиле С, а также префикс $ в стиле Borland Pascal. Однако имейте в виду, что префикс $ может быть также префиксом идентификаторов (см. параграф ), поэтому первой цифрой шестнадцатеричного числа при использовании этого префикса должна быть обязательно цифра, а не буква.

Некоторые примеры числовых констант:

mov ax,100 ; десятичная
mov ax,0a2h ; шестнадцатеричная
mov ax,$0a2 ; снова шестнадцатеричная: нужен 0
mov ax,0xa2 ; опять шестнадцатеричная
mov ax,777q ; восьмеричная
mov ax,777o ; снова восьмеричная
mov ax,10010011b ; двоичная

2.5.2 Символьные константы

Символьная константа содержит от одного до четырех символов, заключенных в одиночные или двойные кавычки. Тип кавычек для NASM несущественен, поэтому если используются одинарные кавычки, двойные могут выступать в роли символа и, соответственно, наоборот. Символьная константа, содержащая более одного символа, будет загружаться в обратном порядке следования байт: если вы пишете

mov eax,’abcd’

сгенерированной константой будет не 0x61626364, а 0x64636261, поэтому если сохранить эту константу в память, а затем прочитать, получится снова abcd, но никак не dcba. Это также влияет на инструкцию CPUID Пентиумов.

2.5.3 Строковые константы

Строковые константы допустимы только в некоторых псевдо-инструкциях, а именно в семействе DB и инструкции INCBIN.
Строковые константы похожи на символьные, только длиннее. Они обрабатываются как сцепленные друг с другом символьные константы. Так, например, следующие строки кода эквивалентны.

db ‘hello’ ; строковая константа
db ‘h’,’e’,’l’,’l’,’o’ ; эквивалент из символьных констант

Следующие строки также эквивалентны:

dd ‘ninechars’ ; строковая константа в двойное слово
dd ‘nine’,’char’,’s’ ; три двойных слова
db ‘ninechars’,0,0,0 ; и действительно похоже

Обратите внимание, что когда используется db, константа типа ‘ab’ обрабатывается как строковая, хотя и достаточно коротка, чтобы быть символьной, потому что иначе db ‘ab’ имело бы тот же смысл, какой и db ‘a’, что глупо. Соответственно, трех- или четырехсимвольные константы, являющиеся операндами инструкции dw, обрабатываются также как строки

2.5.4 Константы с плавающей точкой

Константы с плавающей точкой допустимы только в качестве аргументов DW, DD, DQ и DT. Выражаются они традиционно: цифры, затем точка, затем возможно цифры после точки, и наконец, необязательная E с последующей степенью. Точка обязательна, т.к. dd 1 NASM воспримет как объявление целой константы, в то время как dd 1.0 будет воспринята им правильно.

Несколько примеров:

dw -0.5 ;
dd 1.2 ; «простое» число
dq 1.e10 ; 10,000,000,000
dq 1.e+10 ; синоним 1.e10
dq 1.e-10 ; 0.000 000 000 1
dt 3.141592653589793238462 ; число pi

В процессе компиляции NASM не может проводить вычисления над константами с плавающей точкой (это сделано с целью переносимости). Несмотря на то, что NASM генерирует код для х86 процессоров, сам по себе ассемблер может работать на любой системе с ANCI C компилятором. Само собой, ассемблер не может гарантировать присутствия устройства, обрабатывающего числа с плавающей точкой в формате Intel, поэтому стало бы необходимо включить собственный полный набор подпрограмм для работы с такими числами, что неизбежно привело бы к значительному увеличению размера самого ассемблера, хотя польза от этого была бы минимальна.

Код и данные — это упорядоченная последовательность чисел.

Одну из этих директив мы использовали в нашем коде для резервирования памяти и заполнения её текстом:

message db «Hello World!»,0Dh,0Ah,’$’ ; строка для вывода

1 messagedb»Hello World!»,0Dh,0Ah,’$’; строка для вывода

Переходим в режим декодирования (F4->Decode):

00000000: B409 mov ah,009 ;» »
00000002: BA0801 mov dx,00108 ;» »
00000005: CD21 int 021
00000007: C3 retn
00000008: 48 dec ax
00000009: 65 gs:
0000000A: 6C insb
0000000B: 6C insb
0000000C: 6F outsw
0000000D: 20576F and ,dl
00000010: 726C jb 00000007E
00000012: 64210D and fs:,cx
00000015: 0A24 or ah,

1
2
3
4
5
6
7
8
9
10
11
12
13

00000000B409movah,009;» «

00000002BA0801movdx,00108;» «

00000005CD21int021

00000007C3retn

0000000848decax

0000000965gs

0000000A6Cinsb

0000000B6Cinsb

0000000C6Foutsw

0000000D20576Fandbx0006F,dl

00000010726Cjb00000007E

0000001264210Dandfsdi,cx

000000150A24orah,si

Для машины и код и данные — это строго упорядоченная последовательность цифр. Мы видим, что дизассемблер попытался перевести в код и нашу строку «Hello World!»,0Dh,0Ah,’$’, но мы можем различить её по набору байт от 48h до 24h.

Ассемблерный код также представлен в виде цифр. Шестнадцатеричное число B409 декодируется в инструкцию mov ah,009. При этом, очевидно, что B4 — это «mov ah», а 09 — это число «09» (ещё один ноль добавлен HIEW перед числом, чтобы показать его принадлежность именно к числовому значению и мы его не будем учитывать).

Во второй строке кода обратите внимание на последовательность байт: BA 08 01, которая декодируется в инструкцию:

00000002: BA0801 mov dx,00108

1 00000002BA0801movdx,00108

Понятно, что BA — это «mov dx», а 0801 — это число «0108 » (первый ноль добавил HIEW). Однако отображена оно в коде побайтно наоборот — зеркально.

Преимущество C (Си) конвенции по сравнению с PASCAL.

1. Освобождение стека от параметров возлагается на вызывающую процедуру. Это позволяет отимизировать код.

Например, если вызываются несколькл функций подряд, принимающих одни и теже параметры, можно не заполнять стек заново:

….
push b;Второй параметр
push a; Первый параметр — снизу
call myFunc1
call myFunc2
add sp,4; Стек освобождает вызывающая функция
….

1
2
3
4
5
6
7

….

pushb;Второй параметр

pusha; Первый параметр — снизу

callmyFunc1

callmyFunc2

addsp,4; Стек освобождает вызывающая функция

….

2. Более просто создавать функции с изменяемым числом параметров (printf).

7.53 .space РАЗМЕР, FILL

Эта директива выдает РАЗМЕР байт, каждый со значением FILL. И
РАЗМЕР, и FILL — абсолютные выражения. Если запятая и FILL опущены, то
по умолчанию FILL принимается нулем.

Предупреждение: .space имеет совсем другое значение для HPPA;
используйте для замены .block. Смотрите `HP9000 Series 800
Assembly Language Reference Manual’ (HP 92432-90001) для значения
директивы .space. Смотрите раздел 8.5.5 Директивы ассемблера
HPPA.

На AMD 29K эта директива игнорируется, она допускается для
совместимости с другими ассемблерами AMD 29K.

Предупреждение: В большинстве версий GNU-ассемблера директива
.space имеет эффект .block Смотрите главу 8 «Архитектурные
особенности».

Инструкция OR

Инструкция OR выполняет побитовую операцию ИЛИ. Побитовый оператор ИЛИ возвращает , если совпадающие биты одного или обоих операндов равны , и возвращает , если оба бита равны нулю. Например:

Операция OR может быть использована для указания одного или нескольких бит. Например, допустим, что регистр AL содержит , и нам необходимо установить четыре младших бита. Мы можем сделать это, используя оператор OR со значением (т.е. с регистром FH):

OR BL, 0FH ; эта строка устанавливает для BL значение 0011 1111

1 ORBL,0FH; эта строка устанавливает для BL значение 0011 1111

В качестве другого примера давайте сохраним значения и в регистрах AL и BL соответственно. Таким образом, следующая инструкция:

OR AL, BL

1 ORAL,BL

Должна сохранить в регистре AL:

section .text
global _start ; должно быть объявлено для использования gcc

_start: ; сообщаем линкеру входную точку
mov al, 5 ; записываем 5 в регистр AL
mov bl, 3 ; записываем 3 в регистр BL
or al, bl ; выполняем операцию OR с регистрами AL и BL, результатом должно быть число 7
add al, byte ‘0’ ; выполняем конвертацию из десятичной системы в ASCII

mov , al
mov eax, 4
mov ebx, 1
mov ecx, result
mov edx, 1
int 0x80

outprog:
mov eax,1 ; номер системного вызова (sys_exit)
int 0x80 ; вызов ядра

section .bss
result resb 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

section.text

   global_start; должно быть объявлено для использования gcc

_start; сообщаем линкеру входную точку

   moval,5; записываем 5 в регистр AL

   movbl,3; записываем 3 в регистр BL

   oral,bl; выполняем операцию OR с регистрами AL и BL, результатом должно быть число 7

   addal,byte’0′; выполняем конвертацию из десятичной системы в ASCII

   movresult,al

   moveax,4

   movebx,1

   movecx,result

   movedx,1

   int0x80

outprog

   moveax,1; номер системного вызова (sys_exit)

   int0x80; вызов ядра

section.bss

resultresb1

Результат выполнения программы: