Практически пример. Обработка консольного ввода

Последнее обновление: 03.07.2023

Благодаря взаимодействию с C/C++ мы можем получать в коде ассемблера ввод пользователя и обрабатывать его, используя встроенные функции C/C++. Рассмотрим, как это сделать. Для этого определим файл app.c со следующим кодом на языке C:

#include <stdio.h>

extern void hello(void);

int readLine(char *dest, int maxLen)
{
    // fgets считывает одну строку и возвращает NULL, если произошла ошибка 
    char *result = fgets(dest, maxLen, stdin);
    if(result != NULL)
    {
        // удаляем символ перевода строки в конце строки
        int len = strlen(result);
        if(len > 0)
        {
            dest[len - 1] = 0;
        }
        return len;
    }
    return -1; // если произошла ошибка
}
int main(void)
{
    hello();
}

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

Для считывания данных с консоли определяем функцию readLine, которая принимает буфер, в который считываются данные, и количество считываемых данных. Для непосредственного считывания строки с консоли эта функция использует встроенную функцию fgets. Если при считывании произошла ошибка, то fgets возвращает NULL. Если же считывание прошло удачно, то последний считанный байт будет представлять символ перевода строки, который мы заменяем нулевым байтом.

При успешном считывании функция readLine возвращает количество считанных символов, при неудачном считывании - число -1. Данный результат позволит внешнему коду определить, удачно ли завершилось считывание или нет.

Также определим файл hello.asm со следующим кодом на ассемблере

option casemap:none

maxLen equ 128  ; максимальное количество символов
.data
prompt byte "Enter your name: ", 0      ; приглашение к вводу строки
message byte "Hello %s!", 10, 0         ; выводимое сообщение
username byte maxLen dup (0)            ; буфер для ввода данных

.code
externdef printf:proc       ; подключаем функцию printf
externdef readLine:proc     ; подключаем функцию readLine

public hello
hello proc
    sub rsp, 40     ; устанавливаем стек

    lea rcx, prompt ; загружаем преглашение к вводу
    call printf     ; вызываем функцию printf для отображения приглашения к вводу

    ; считаем строку с консоли
    lea rcx, username   ; в rcx - строка, в которую считываем данные
    mov rdx, maxLen     ; в rdx - максимальное количество считываемых символов
    call readLine       ; вызываем readLine для считывания строки

    ; выводим считанное значение с помощью функции printf
    lea rcx, message    ; в rcx - выводимая строка
    lea rdx, username   ; в rdx - значение для спецификатора %s
    call printf         ; выводим строку

    add rsp, 40         ; восстанавливаем стек
    ret                 ; возвращаемся в программу на С
hello endp
end

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

В секции данных определяем три переменных. Переменная prompt представляет текст-приглашение к вводу имени. Переменная message представляет финальное сообщение, в которое вместо спецификатора %s будет подставлятсья введенное имя. И переменная username представляет строку для ввода имени. Она имеет 128 символов и по умолчанию инициализирована нулями.

В секции кода прежде всего пдключаем функции printf и readLine из кода С:

externdef printf:proc
externdef readLine:proc

Далее определяется процедура hello. В ней вначале выводим приглашение вводу с помощью функции printf

lea rcx, prompt ; загружаем преглашение к вводу
call printf     ; вызываем функцию printf для отображения приглашения к вводу

Далее вызываем функцию readLine и тем самым считываем строку с консоли - имя пользователя:

lea rcx, username   ; в rcx - строка, в которую считываем данные
mov rdx, maxLen     ; в rdx - максимальное количество считываемых символов
call readLine       ; вызываем readLine для считывания строки

После ввода имени выводим его на консоль с помощью функции printf:

lea rcx, message    ; в rcx - выводимая строка
lea rdx, username   ; в rdx - значение для спецификатора %s
call printf         ; выводим строку

Сначала скомпилируем код ассемблера из файла hello.asm с помощью команды:

ml64 /c hello.asm

И скомпилируем файл app.c в файл приложения с помощью команды:

cl app.c hello.obj

После компиляции запустим файл app.exe:

c:\asm>ml64 /c hello.asm
Microsoft (R) Macro Assembler (x64) Version 14.36.32532.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: hello.asm

c:\asm>cl app.c hello.obj
Microsoft (R) C/C++ Optimizing Compiler Version 19.36.32532 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

app.c
Microsoft (R) Incremental Linker Version 14.36.32532.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:app.exe
app.obj
hello.obj

c:\asm>app
Enter your name: Eugene
Hello Eugene!

c:\asm>
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850