Вопрос по assembly, nasm – Как использовать scanf в NASM?

14

Я пытаюсь понять, как использоватьscanf чтобы получить пользовательский ввод. Я знаю, чтобы использоватьprintf: все, что мне нужно сделать, это поместить данные, которые я хочу записать на экран, в стек следующим образом:

global _main
extern _printf
extern _scanf

section .data
msg db "Hi", 0

section .text
_main:
  push ebp
  mov ebp, esp  

  push msg
  call _printf

  mov esp, ebp
  pop ebp
ret

Но я не могу понять, как использоватьscanf, Может кто-нибудь, пожалуйста, просто дайте мне максимально простой исходный код, который вы можетеscanf? Я действительно хочу добавить то, что вводит пользователь.

Я не привык к 32-битной сборке. Я когда-либо использовал только 16 бит, и я знаю, что в 16 бит (DOS) вы можете просто сделать это:

mov ah, 3fh
mov dx, input
int 21h

input rb 100d

И все, что вы введете, будет помещено по адресу & quot; ввода. & Quot;

Ваш Ответ

4   ответа
3

Приет. Я сделал простой пример на основе вашего кода, чтобы проиллюстрировать использование scanf.

Программа, которая запрашивает целое число и выводит его на экран:

section .text
  global main
  extern printf
  extern scanf

section .data
  message: db "The result is = %d", 10, 0
  request: db "Enter the number: ", 0
  integer1: times 4 db 0 ; 32-bits integer = 4 bytes
  formatin: db "%d", 0

main:
  ;  Ask for an integer
  push request
  call printf
  add esp, 4    ; remove the parameter

  push integer1 ; address of integer1, where the input is going to be stored (second parameter)
  push formatin ; arguments are right to left (first  parameter)
  call scanf
  add esp, 8    ; remove the parameters

  ; Move the value under the address integer1 to EAX
  mov eax, [integer1]

  ; Print out the content of eax register
  push eax
  push message
  call printf
  add esp, 8

  ;  Linux terminate the app
  MOV AL, 1
  MOV EBX, 0 
  INT 80h 
0

scanf("%d %d", &var1, &var2);

Это принимает два значения и сохраняет их в переменных.

В сборе вы быpush адреса переменных в стек (в обратном порядке), а затемcall scanf.

Что-то вроде у вас есть две переменные

var1 resd 1
var2 resd 1

... а потом

push var2
push var1
call scanf

* Обратите внимание, что я толкнул их в обратном порядке, первое значение будет сохранено в var1.

После выполнения введенные вами значения будут сохранены в переменных.

Если вы хотите только одно значение, просто нажмите одну переменную.

Не забудьте выгрузить стек после того, как scanf вернется. (add esp, 8 для 32-битного кода). 64-битный код передает первые до 6 аргументов в регистрах, поэтому у вас не будет стековых аргументов. Кроме того, текущая версия i386 System V ABI в Linux требует выравнивания стека 16 байт передcallТаким образом, вы не можете просто нажать произвольное количество вещей.
1

Это первое сообщение, которое появляется при поискеscanf в сборке, так что, даже если это 4 года пост, я думаю, что это должно быть правильно.

Оукеи, так, чтобыcall scanf в сборке NASM нужно:

Extern scanf Prepare a formatation for you scanf Prepare the variables or single variable to store the values expected Push the parameters in backward order Call scanf Restore stack

Итак, допустим, вы перешли на

scanf ("%d %d", &val1, &val2);

и после списка:
... 1.

section .text
extern scanf

... 2. This is the first parameter you pass to your C scanfговорит что получишь. Одно целое число, два, число с плавающей точкой, строка, символ ... В этом случае два целых числа разделены пробелом (также работает с Enter)

section .data
fmt: db "%d %d",0

... 3.

section .bss
val1: resd 1
val2: resd 1

... 4 5 6. Note that you push the address of the variables, not its content (i.e. [var])

push val2
push val1
push fmt
call scanf
add esp, 12

Также обратите внимание, что вам нужно добавить 12 к указателю стека, потому что вы выдвинули 3 параметра двойного слова. Таким образом, вы добавляете 12 байтов (3 * 4 байта) в стек, чтобы "перейти" параметры.

* Я объявил dword для переменных, потому что%d использует меч, так же, какprintf.
**,0 в конце строки форматирования находится символ стража.

Я ответил как гость с именем PunditPoe, не могу понять, смогу ли я "ввести" этот гостевой аккаунт и удалите пост. Но, во всяком случае, я исправил этот, чтобы стоять самостоятельно. Благодарю.
Вы можете объединить свои учетные записи (поиск наMeta Stack Overflow чтобы узнать как). Или вы можете удалить свой предыдущий ответ из другой учетной записи, поскольку в любом случае он не имеет никаких голосов. И пожалуйстаedit этот стоит сам по себе, вместо ссылки на другой пост.
15

«Программирование в NASM.PDF»;

; add1.asm
SECTION .data
    message1: db "Enter the first number: ", 0
    message2: db "Enter the second number: ", 0
    formatin: db "%d", 0
    formatout: db "%d", 10, 0 ; newline, nul terminator
    integer1: times 4 db 0 ; 32-bits integer = 4 bytes
    integer2: times 4 db 0 ;
SECTION .text
   global _main 
   extern _scanf 
   extern _printf     

_main:

   push ebx ; save registers
   push ecx
   push message1
   call printf

   add esp, 4 ; remove parameters
   push integer1 ; address of integer1 (second parameter)
   push formatin ; arguments are right to left (first parameter)
   call scanf

   add esp, 8 ; remove parameters
   push message2
   call printf

   add esp, 4 ; remove parameters
   push integer2 ; address of integer2
   push formatin ; arguments are right to left
   call scanf

   add esp, 8 ; remove parameters

   mov ebx, dword [integer1]
   mov ecx, dword [integer2]
   add ebx, ecx ; add the values          ; the addition
   push ebx
   push formatout
   call printf                            ; call printf to display the sum
   add esp, 8                             ; remove parameters
   pop ecx
   pop ebx ; restore registers in reverse order
   mov eax, 0 ; no error
   ret

Какая версия asm этой функции C:

#include <stdio.h>
int main(int argc, char *argv[])
{
    int integer1, integer2;
    printf("Enter the first number: ");
    scanf("%d", &integer1);
    printf("Enter the second number: ");
    scanf("%d", &integer2);
    printf("%d\n", integer1+integer2);
    return 0;
}
Текущая версия i386 System V ABI (используется в Linux) требует 16-байтового выравнивания стека передcall, Вашprintf вызовы выполняются с правильно выровненным стеком (обратный адрес + 3 нажатия), но вызовы scanf смещены. glibc scanf будет разрешен segfault (как в 64-битном режиме), но 32-битная версия, вероятно, не будет. Также,ecx не является сохраняемым вызовом регистром. Нет смысла его спасать; printf / scanf clobber ECX и EDX, и ваш собеседник ожидает, что вы тоже их затрете. (Используйте EDX вместо EBX, и вы можете избежать любого сохранения / восстановления).

Похожие вопросы