Пример программы в FASM: вывод на консоль текстового файла в Win32

FASM - Flat assembler - это бесплатный ассемблер, которым можно транслировать программы для DOS, Windows, Linux и Unix. Он в себе содержит также компоновщик, поэтому получение исполняемого файла очень простое: fasm.exe <filename.asm>. FASM постоянно развивается и наряду с архитектурой x86-64 поддерживает последние наборы инструкций SSE и AVX. По этой ссылке вы можете скачать новейшую версию FASM.

Учебника FASM - по крайней мере, на русском языке - не существует, есть документация на сайте этого продукта. Поэтому начинающие программисты ищут информацию и примеры программ на FASM в Интернете.

В этом примере мы рассмотрим консольную программу в FASM, которая получает имя текстового файла из командной строки и выводит его содержимое на консоль. В данном случае мы будем использовать функции из библиотеки msvcrt.dll. Она поставляется вместе с Windows и называется, как можно понять из имени dll, Microsoft Visual C Runtime Library. Список функций из этой dll и примеры их использования вы можете увидеть на англоязычном сайте, посвящённом C++. Здесь есть справочник на русском языке по библиотеке msvcrt времени выполнения.

В начале программы вы видите строку include 'win32ax.inc'. Чтобы не писать весь путь к подключаемому файлу с макоопределениями win32ax.inc, надо зайти в "свойства моего компьютера" во вкладку "дополнительно" и выбрать "переменные среды". Там надо создать переменную среды с именем include и дать ей значение в виде пути к папке include каталога с FASM, например: c:\fasm\include.

Далее в тексте программы вы видите несколько констант для работы с файлами. Текст программы должен быть в кодировке OEM (code page 866), т.к. Windows в консоли использует её. Если вы хотите использовать кодировку Windows (code page 1251), то раскомментируйте вызовы
invoke SetConsoleOutputCP,1251
invoke SetConsoleCP,1251
При этом заметьте, что шрифт в консоли должен быть не Consolas и не точечным, а Lucida Console, иначе в некоторых случаях вы будете видеть закорючки вместо русских букв. Шрифт можно менять в свойствах Far (правой кнопкой по иконке) или свойствах программы cmd.exe (правой кнопкой в верхнем левом углу окна программы). Для перекодировки строки из кодировки Windows в OEM перед выводом на экран также можно использовать CharToOemA. В закомментированных строках перекодируются параметры программы (полный путь с именем файла данной программы и заданный ей параметр).

Если эта программа будет запущена из консольной программы (напр., из Far manager), то она унаследует консоль Far. Если раскомментировать вызовы
invoke FreeConsole
invoke AllocConsole
то программа создаст свою консоль.

В программе делается проверка на ошибки после вызовов функций из msvcrt.dll макросом cinvoke. Для разнообразия в одном случае используется макрос .if, который наряду с макросами .while, .repeat облегчает восприятие текста программы и уменьшает число меток ценой некоторого "загрязнения" результирующего кода. В этих макросах можно использовать выражения и скобки, например:
.if eax <> 0 & [mem1] > ebx & (ecx > [mem2])
Предусмотрена возможность делать сравнения для переменных со знаком. Также показано, как можно задавать строки в виде литералов прямо в вызовах invoke и cinvoke.

В конце программы в секции .idata перечисляются используемые файлы dll и импортируемые из них функции. В дальнейшем мы увидим, что явно создавать эту секцию и выписывать всё это не всегда обязательно, это может сделать макрос.

Далее идёт текст вышеописанной программы.

; Пример Win32 консольной программы для FASM, которая получает в строке вызова
; имя текстового файла и выводит его содержимое и размер на консоль.
; Сделано IQFun.ru, см. www.iqfun.ru/articles/fasm-primer-console-output.shtml
; Создавайте этот файл в кодировке DOS (OEM, code page 866).

format PE CONSOLE
include 'win32ax.inc'

SEEK_SET equ 0  ; Относительно начала файла, начало файла - позиция 0
SEEK_CUR equ 1  ; Относительно текущей позиции, > 0 - вперед, < 0 - назад
SEEK_END equ 2  ; Относительно конца файла (значение pos - отрицательное)
NULL     equ 0
EOF      equ -1

section '.data' data readable writeable

argc    dd ?
argv    dd ?
env     dd ?
fp      dd ?
flength dd ?
fbuf    dd ?
msg     db  '%s %s%s',0
errmsg  db "Ошибка! Нужно задать имя текстового файла для вывода.",0

section '.code' code readable executable

entry start

start:
; invoke  FreeConsole
; invoke  AllocConsole
; cinvoke printf,'%s%s',,<13,10,0>
; invoke SetConsoleOutputCP,1251
; invoke SetConsoleCP,1251

 cinvoke printf,'%s',<'Привет',13,10,0>

 ; Получаем полный путь к программе и заданный ей параметр аналогично программе на C
 cinvoke __getmainargs,argc,argv,env,0
 cmp [argc],2
 jne .err
 mov esi,[argv]

; invoke CharToOemA,dword [esi],dword [esi]
; invoke CharToOemA,dword [esi+4],dword [esi+4]

 ; Выводим имя программы с путём и параметр
 cinvoke printf,msg,dword [esi],dword [esi+4],<13,10,0>

 ; Открываем заданный текстовый файл в режиме "только чтение" и "двоичный"
 cinvoke fopen,dword [esi+4],'rb'
 test eax,eax
 jz .err
 mov [fp],eax

 ; Устанавливаем указатель чтения-записи в конец файла
 cinvoke fseek,[fp],0,SEEK_END
 .if eax <> 0
  jmp .err
 .endif

 ; Узнаём значение этого указателя, который равен размеру файла
 cinvoke ftell,[fp]
 test eax,eax
 jz .err
 mov [flength],eax

 ; Запрашиваем динамическую память размером длина файла + 1 байт
 inc eax
 cinvoke malloc,eax
 test eax,eax
 jz .err
 mov [fbuf],eax

 ; Возвращаем указатель чтения-записи в начало файла
 cinvoke fseek,[fp],0,SEEK_SET
 test eax,eax
 jnz .err

 ; Читаем весь файл в динамическую память
 cinvoke fread,[fbuf],1,[flength],[fp]
 cmp eax,[flength]
 jne .err

 ; Записываем 0 за концом данных для обозначения конца строки
 mov eax,[fbuf]
 add eax,[flength]
 mov byte [eax],0

 ; Выводим файл на консоль
 cinvoke puts,[fbuf]
 cinvoke printf,'Длина файла = %u%s',[flength],<13,10,0>

 ; Закрываем файл
 cinvoke fclose,[fp]
 test eax,eax
 jnz .err

 ; Освобождаем динамическую память
 cinvoke free,[fbuf]

.finish:
 cinvoke puts,'Press any key...'
 cinvoke _getch
 invoke ExitProcess,0

.err:
 cinvoke puts,errmsg
 jmp .finish

section '.idata' import data readable writeable

library kernel,'kernel32.dll',\
msvcrt,'msvcrt.dll',\
user32,'user32.dll'

import kernel,\
ExitProcess,'ExitProcess',\
GetCommandLineA,'GetCommandLineA',\
AllocConsole,'AllocConsole',\
FreeConsole,'FreeConsole',\
SetConsoleOutputCP,'SetConsoleOutputCP',\
SetConsoleCP,'SetConsoleCP'

import msvcrt,\
__getmainargs,'__getmainargs',\
fopen,'fopen',\
fseek,'fseek',\
ftell,'ftell',\
malloc,'malloc',\
free,'free',\
fread,'fread',\
fclose,'fclose',\
printf,'printf',\
_getch,'_getch',\
puts,'puts'

import user32,\
CharToOemA,'CharToOemA'

© IQFun.ru, 2016 г.

Новости игр и головоломок

Пройдите лабиринты на флеш

24.09.2016 Лабиринты разных размеров.

Слова, слова, слова...

09.09.2016 Словесно-интеллектуальный конкурс № 73.

Вот тебе бог, а вот тебе и пророк!

02.09.2016 Литературная викторина № 72.

Новые стихи-каламбуры.

07.07.2016 Не хуже "королей рифмы".

Слова, в словах:

07.07.2016 добавлены слова, разбивающиеся на слова.

Добавлены загадки с метаграммами.

05.07.2016 Шарадные загадки и пародии на них.

Слова, в словах:

04.07.2016 примеры, список слов, в которых прячутся другие слова.

Пополнение вопросов ребром, а также

30.06.2016 вопросы учёного, возникшие утром 1-го января.

Статья о метаграммах.

17.06.2016 Метаграмма - примеры, список метаграмм и добавлялок-вставлялок.

Бессистемное программирование.

09.04.2016 Пример программы для ассемблера FASM.