Sadda.ru Ironetcart Андроид Ассемблер MASM32 Linux Все статьи Table of Contents


 

Консоль ввода-вывода

  Макс Петров май 2013

      Наилучшая сфера для применения ассемблера - специализированные небольшие приложения, выполняющие каждое свою конкретную задачу, а по причине конкретности детальный графический интерфейс таким приложениям и не нужен.

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

      Работа с консолью в операционной системе Windows строится следующим образом. В первую очередь приложение должно запросить у Windows создание консоли. Если консоль создана успешно, приложение следующим шагом запрашивает у Windows хэндлы (числовые идентификаторы) своей консоли. После получения хэндлов и с их указанием организуется непосредственно ввод в программу и вывод из нее символьной информации.

Alias - "прозвище". Применительно к API-функциям Windows обозначает псевдонимы функций (запись псевдонима следует непосредственно за словом Alias). Необходимость алиасов вызвана тем, что в каждом языке программирования существует свое соглашение об именовании данных, поэтому алиасы одной и той же API-функции для разных языков программирования тоже запишутся по-разному.

      Для создания консоли приложение вызывает API-функцию Windows AllocConsole:
      Function AllocConsole Lib "kernel32" Alias DWORD "AllocConsole" ().
Из описания этой функции видно, что она содержится в библиотеке Windows kernel32, поэтому в секцию директив нашей ассемблерной программы мы должны включить соответствующие файлы прототипов (kernel32.inc) и библиотек (kernel32.lib). По выполнении функция AllocConsole записывает (возвращает) в регистр EAX значение длиной 4 байта (DWORD). Если консоль создать не удалось, в регистр EAX будет записан ноль.

      Хэндл созданной консоли приложение получает при помощи API-функции GetStdHandle:
      Function GetStdHandle Lib "kernel32" Alias DWORD "GetStdHandle" (DWORD nStdHandle).
В отличие от предыдущей функции, которая не принимала (пустые скобки) от приложения никаких параметров, функции GetStdHandle должно быть передано 4-байтное значение одной из набора констант nStdHandle. После выполнения функции в регистр EAX будет помещено 4-байтное значение хэндла консоли.

      Хэндлы для ввода в консоль и вывода через нее не равны, если мы планируем организовать в нашей программе и ввод, и вывод символьной информации через консоль, функцию GetStdHandle нужно вызывать дважды, передавая ей соответствующие значения констант nStdHandle:
      STD_INPUT_HANDLE (ввод);
      STD_OUTPUT_HANDLE (вывод).
Записи STD_INPUT_HANDLE и STD_OUTPUT_HANDLE обозначают в стандарте обозначений Microsoft всего лишь числа (константы). Замена числовых значений констант принятыми для них символьными именами хороша уже тем, что улучшает читаемость программ, избавляя от необходимости каждый раз вспоминать, что может означать то или иное число. Значения констант STD_INPUT_HANDLE и STD_OUTPUT_HANDLE содержатся в файле windows.inc (входит в комплект поставки MASM32), на который мы укажем в секции директив нашей программы.

      Ввод и вывод через консоль осуществляется посредством двух API-функций Windows, которые определяются следующим образом. Для ввода:
      Function ReadConsole Lib "kernel32" Alias DWORD "ReadConsoleA" (DWORD hConsoleInput, DWORD lpBuffer, DWORD nNumberOfCharsToRead, DWORD lpNumberOfCharsRead, DWORD lpReserved);
для вывода:
      Function WriteConsole Lib "kernel32" Alias DWORD "WriteConsoleA" (DWORD hConsoleOutput, DWORD lpBuffer, DWORD nNumberOfCharsToWrite, DWORD lpNumberOfCharsWritten, DWORD lpReserved).

      Функциям должны быть переданы параметры:
      hConsoleInput (hConsoleOutput) - хэндл для ввода (вывода);
      lpBuffer - адрес памяти, с которого начинается буфер ввода или вывода;
      nNumberOfCharsToRead (nNumberOfCharsToWrite) - устанавливаемое приложением число вводимых (выводимых) символов;
      lpNumberOfCharsRead (lpNumberOfCharsWritten) - адрес ячейки памяти, куда функция запишет число фактически введенных (выведенных) символов;
      lpReserved - адрес ячейки памяти для зарезервированного параметра.

      Об API-функции CharToOem, которая понадобится, если в консоли мы намерены выводить кириллические символы, будет рассказано в следующей статье.

      Ну все, пишем программу:

.386 ; 32-битный режим .model flat, stdcall ; компиляция в exe-файл с возможностью вызова API option casemap :none ; неразличение прописных и строчных символов ; содержит значения констант include C:\masm32\include\windows.inc ; STD_INPUT_HANDLE, ; STD_OUTPUT_HANDLE include <\masm32\include\kernel32.inc> include <\masm32\include\user32.inc> includelib <\masm32\lib\kernel32.lib> includelib <\masm32\lib\user32.lib> .data ; сегмент данных hConsoleInput DWORD ? ; переменные для хранения хэндлов ввода и вывода, hConsoleOutput DWORD ? ; названия этих переменных могут быть другими ; буфер 1 байт (со значением 0) Buffer byte 1 dup (0) ; для вода с клавиатуры 1 символа, ; название буфера может быть другим NumberOfCharsRead DWORD ? ; переменные для записи числа фактически NumberOfCharsWritten DWORD ? ; введенных и выведенных символов, ; названия этих переменных могут быть другими msg1 byte " Hello, World!" ; строковая переменная ; строковая переменная msg2 byte " Нажмите Enter, чтобы выйти...", 0 ; заканчивается нулем, ; так как она будет передана ; API-функции CharToOem msg1310 byte 13, 10 ; перевод строки .code ; сегмент кода start: invoke AllocConsole ; запрашиваем у Windows консоль invoke GetStdHandle, STD_INPUT_HANDLE ; получаем хэндл консоли для ввода mov hConsoleInput, EAX ; записываем хэндл в переменную invoke GetStdHandle, STD_OUTPUT_HANDLE ; получаем хэндл консоли для вывода mov hConsoleOutput, EAX ; записываем хэндл в переменную invoke WriteConsoleA, ; переводим строку в консоли hConsoleOutput, ; хэндл вывода ADDR msg1310, ; адрес строки msg1310 SIZEOF msg1310, ; размер строки msg1310 ADDR NumberOfCharsWritten, ; сюда функция запишет число символов 0 ; lpReserved передаем, как ноль invoke WriteConsoleA, ; пишем " Hello, World!" hConsoleOutput, ADDR msg1, SIZEOF msg1, ADDR NumberOfCharsWritten, 0 invoke WriteConsoleA, ; переводим строку hConsoleOutput, ADDR msg1310, SIZEOF msg1310, ADDR NumberOfCharsWritten, 0 invoke CharToOem, ADDR msg2, ADDR msg2 ; перекодируем Win1251 -> DOS invoke WriteConsoleA, ; пишем " Нажмите Enter, чтобы выйти..." hConsoleOutput, ADDR msg2, (SIZEOF msg2) - 1, ; уменьшаем размер строки msg2 на 1 (из-за нуля) ADDR NumberOfCharsWritten, 0 invoke ReadConsoleA, ; ожидаем ввода в консоль hConsoleInput, ; хэндл ввода ADDR Buffer, ; адрес буфера 1, ; вводим 1 символ ADDR NumberOfCharsRead, ; сюда функция запишет число символов 0 ; lpReserved передаем, как ноль invoke ExitProcess, 0 ; сообщаем системе, что программа окончена end start ; завершает сегмент кода

      В программе присутствуют незнакомые читателю записи. Первая из них

mov (приемник), (источник)

являет собой команду процессора - пересылку. Мнемоника команды происходит от английского слова moving, что означает перемещение. В нашем случае при помощи этой команды мы дважды предписывали процессору скопировать содержимое регистра EAX, в который функция GetStdHandle записывала хэндл консоли, в соответствующую переменную.

      Оператор ассемблера

ADDR (имя переменной)

подменяется в исполняемом коде адресом переменной в памяти, то есть является указателем на эту переменную.

      Оператор ассемблера

SIZEOF (имя символьной строки)

подменяется в исполняемом коде длиной (в байтах) символьной строки.

      В конце программы ввод через консоль одного символа нужен только для того, чтобы мы смогли на программу (и на консоль) посмотреть. Ввод в консоль осуществляется с клавиатуры, завершает ввод всегда клавиша Enter. Таким образом, пока мы ничего не ввели консоль будет ожидать ввода, после нажатия Enter программа закроется, поскольку ее исполняемый код будет исчерпан.

      Завершает программу вызов API-функции ExitProcess, которая говорит операционной системе, что процесс следует закрыть.

      Создайте текстовый файл, туда скопируйте текст приведенной выше программы, переименуйте файл в "console.asm". После установки MASM32 у вас на рабочем столе Windows присутствует ярлык "MASM32 Editor" - запустите этот редактор. В нем откройте созданный файл "console.asm", затем выберите в меню "Project" редактора опцию "Assemble & Link". В той папке, где находится файл "console.asm", появится результат компиляции - файл "console.exe", который можно запустить. Получится вот это:

консоль ввода-вывода

     


Добавить комментарий

E-mail:
*


Контрольные цифры:



Ассемблер MASM32

      Простейшая программа на ассемблере (beeper)
      Переменные и типы данных ассемблера
      Регистры процессора IA32
      Консоль ввода-вывода
      API-функция CharToOem и строки ассемблера
      API-функция ReadConsoleInput
      API-функция PeekConsoleInput
      События консоли (таблица)
      Системы счисления, тэги ассемблера, перевод чисел
      Отрицательные числа
      Инкремент и декремент
      Деление (DIV, IDIV)
      VKDEBUG
      Макросы ассемблера
      Воспоминание об Альгамбре на системном динамике
      Командная строка
      Пузырьковая сортировка. Эстафета шариков
      Сортировка расческой
      Быстрая сортировка

     


© Max Petrov При использовании материалов ссылка на sadda.ru обязательна