Программа печати целого числа на NASM
Данная программа имеет простую логику: считывает целое число и выводит его. Но в коде вместо описания функцииscan_int
стоит "заглушка",
которая всегда возвращает 133.
Задача. Напишите правильную реализацию функции
scan_int
.
Логика этой функции должна быть такой, как в приведённой ниже программе на Си.
Задача. Добавьте в функции NASM
print_int
, scan_int
возможность считывания и печати отрицательных целых чисел.
Задача "a*b". Напишите программу на NASM, которая считывает
два целых числа и выводит результат их сложения и умножения.
Используйте результаты предыдущей задачи.
Задача. Напишите реализации функции
scan_hex
и print_hex
на языках NASM и Cи. В реализациях на языке Си используйте
функции write
и read
(см. man 2 read
и man 2 write
).
Постарайтесь сделать логику программ на Си и на NASM максимально близкой.
section .bss buffer resb 20 section .text global _start _start: call scan_int ; читать int из stdin в регистр eax push eax ; поместить eax в стек это будет ; аргументом для следующей функции call print_int ; напечатать int, который находится в вершине стека ; в стандартный поток вывода ; "call func" эквивалентно ; push <адрес_следующей_инструкции>; ; jmp func mov eax, 1 ; Эти три строчки эквиваленты exit(0) mov ebx, 0 ; int 0x80 ; scan_int: ; эта функция должна читать int из stdin, mov eax, 133 ; но пока она не реализована и просто ret ; возвращает число 133 print_int: ; функция печати целого числа в stdout ; аргумент (4-байтовое целое число) ; находится в вершине стека ; ebp содержит адрес начала stack frame ; esp содержит адрес вершины стека ; esp < ebp, то есть вершина имеет меньший адрес ; в начале по адресам (ebp-4, ebp-3, ebp -2, ebp -1) лежат ; четыре байта целого числа, которое нам передали ; в качестве аргумента push ebp ; поместим в стек адрес начала стека ; этот push автоматически делает esp -= 4 mov ebp, esp ; теперь ebp равно esp ; аргумеенты находятся по адресу ebp + 8 mov ecx, [ebp+8] ; значение переданного нам целого числа поместим в ecx xor edx, edx ; обнулим edx mov esi, 10 ; на 10 мы будем делить. mov edi, 18 ; символы-цифры нашего числа мы будем помещать ; по адресам buffer + 17, buffer+16, buffer+15, ... mov byte [buffer + 18], 0xA ; 19-й и 20-й символы это перенос строчки mov byte [buffer + 19], 0 ; и символ конца строки .loop: mov eax, ecx ; xor edx, edx ; данные четыре строки дают div esi ; ecx = ecx / 10 mov ecx, eax ; add edx, '0' ; '0' ассемблером интерпретируется как ASCII код символа '0' dec edi mov byte [buffer+edi], dl cmp ecx, 0 jne .loop mov eax, 4 ; эквивалентно write( 1, buffer + edi, 19 - edi ) mov ebx, 1 mov ecx, buffer ; можно короче lea ecx, [buffer+edi] add ecx, edi mov edx, 19 sub edx, edi int 0x80 leave ; эквивалентно mov esp, ebp ; pop ebp ret ; эквивалентно pop IP ;
Считывание целого положительного числа на Си без использования scanf
#include <unistd.h> char buffer[20]; int scan_int() { int a = 0; do { int c; int ret = read( 2, buffer, 1 ); c = buffer[0]; if( ret > 0 && c != '\n' ) { a *= 10; a += c - '0'; } else { break; } } while(1); return a; } int print_int(int a) { int c; int i = 20; buffer[--i] = 0; buffer[--i] = '\n'; do { c = a % 10; a /= 10; c += '0'; buffer[--i] = c; } while( a ); write( 1, buffer + i, 20 - i ); return a; } int main() { int a = scan_int(); print_int( a ); }