Не могу понять функцию printf в сборке x86 и работу с операндами

Я уже нервничаю по поводу публикации этого вопроса, но вот оно. Я пытаюсь разработать программу сборки, которая принимает два целых числа, а затем операнд (*, + или ^). Затем, в зависимости от операнда, программа выполнит арифметическую последовательность на основе выбранного операнда. Вывод должен выглядеть именно так, скажем, пользователь вводит 5, затем пользователь вводит 6 и, наконец, пользователь вводит *, программа должна распечатать

OP 5 * 6 = 30

По какой-то причине я не могу понять концепцию printf, поэтому моя функция печати внизу — это мой нелепый способ попытаться распечатать это. Если бы кто-нибудь мог объяснить мне, как работает printf, я был бы рад это услышать! У меня есть очень базовые знания о стеке. Кроме того, у меня нет экспоненциальной функции, потому что я понятия не имею, как это сделать на ассемблере. И, наконец, у меня есть вопрос, не связанный с программой, для истинных компьютерных богов... Я абсолютно ненавижу язык ассемблера, у меня проблемы с написанием основных операций на этом языке. Мне нравилось работать на C, CPP и Java... Этот язык абсолютно необходим для выживания программиста изо дня в день, или это урок истории о том, как люди добывали огонь до того, как у них появились зажигалки? В любом случае, любые ответы на любые вопросы приветствуются, и заранее спасибо.

;program to add, multiply, or give the power of a number

%include "asm_io.inc"


segment .data 


prompt1 db    "Enter a number: ", 0        ;prompts
prompt2 db    "Enter another number: ", 0
prompt3 db    "Enter an operand", 0
prompt4 db    "OP ", 0    ;idiotic prompts for printing
prompt5 db    " = ", 0    ;another printing prompt

segment .bss

num1  resd 1    ;variable for 1st number
num2  resd 1    ;variable for 2nd number
op    resd 1    ;variable for operand


segment .text

    global  asm_main
asm_main:
    enter   0,0               ; setup routine
    pusha

    restart:
        mov     eax, prompt1      ; print out prompt
        call    print_string
        call    print_nl

        call    read_int          ; read integer
        mov     [num1], eax       ; store into num1
        call    print_nl

        mov     eax, prompt2      ; print out prompt
        call    print_string
        call    print_nl

        call    read_int          ; read integer
        mov     [num2], eax     ; store into num2
        call    print_nl

        mov     eax, promtp3    ;print out prompt
        call    print_string
        call    print_nl

        call    read_char       ;read operand and dispose of null
        call    read_char
        mov     [op], eax       ;store operand in [op]
        call    print_nl

        cmp     [op], '*'       ;operand comparison
        jne     jmp1
        call    mulFun
        jmp     restart

jmp1:   cmp     [op], '+'       ;operand comparison
        jne     jmp2
        call    sumFun
        jmp     restart

jmp2:   cmp     [op], '^'       ;operand comparison
        jne     jmp3
        call    expFun
        jmp     restart

jmp3:   cmp     [op], 'e'       ;exit function
        je      end1

end1:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    call    print_nl          ; pr
    popa
    mov     eax, 0            ; return back to C
    leave
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mulfun:                     ;multiplication function
    mov     ebx, [num1]
    mov     ecx, [num2]
    imul    ebx, ecx
    jmp     printFun

sumFun:                     ;sum function
    mov     ebx, [num1]
    mov     ecx, [num2]
    add     ebx, ecx
    call    printFun


printFun:                   ;printing function
    mov     eax, [prompt4]
    call    print_string
    mov     eax, [num1]
    call    print_string
    mov     eax, [op]
    call    print_string
    mov     eax, [num2]
    call    print_string
    mov     eax, [prompt5]
    call    print_string
    mov     eax, ebx
    call    print_string
    call    print_nl
    ret

person Jamie Jackson    schedule 03.11.2015    source источник
comment
Кроме того, я хотел бы улучшить использование этого веб-сайта, поэтому любые ошибки форматирования, общие правила публикации или советы, которые у вас есть для меня, будут приняты и оценены с благодарностью.   -  person Jamie Jackson    schedule 04.11.2015
comment
Во-первых, вам нужно объяснить, что именно делает ваш код сейчас, где он дает сбой и что вы ожидали от него. Во-вторых, кодирование на ассемблере намного утомительнее, чем использование любого из упомянутых вами языков более высокого уровня - никто в здравом уме не будет кодировать что-то сложное полностью на ассемблере в настоящее время - но это то, во что все в конечном итоге компилируется, так что полезно знать это хотя бы внешне.   -  person 500 - Internal Server Error    schedule 04.11.2015
comment
Этот сайт для ботаников, поэтому говорить нам, что вы нервничаете или благодарны, излишне. Вы можете пропустить это. :-) Способность читать язык ассемблера полезна при оценке того, что компилятор сделал с вашим кодом. Написание ассемблера стоит дорого и поэтому редко используется в настоящее время, за исключением очень специализированных сред.   -  person Bo Persson    schedule 04.11.2015
comment
Ну, я думаю, моя основная проблема заключается в печати «OP 5 * 6 = 30». Если бы это был C, он просто использовал бы «printf(OP %d %c %d = %d,num1, op1, num2, product;» не знаю как напечатать это на ассемблере   -  person Jamie Jackson    schedule 04.11.2015
comment
По-видимому, вы используете asm_io.inc, который предоставляет вам основные помощники ввода-вывода. Поэтому используйте все, что он предоставляет, чтобы печатать части одну за другой, например. call write_char и write_int и write_string (используйте любые функции, которые у вас действительно есть, с соответствующим аргументом).   -  person Jester    schedule 04.11.2015
comment
Он помечен как MASM, но выглядит как NASM   -  person Michael Petch    schedule 04.11.2015


Ответы (1)


Везде, где я находил проблему, я добавлял *** в комментариях. Несколько замечаний:

  • У вас были опечатки в некоторых ярлыках.
  • NASM чувствителен к регистру, поэтому mulfun — это не то же самое, что mulFun.
  • Если у вас есть инструкция, принимающая один или несколько операндов, которая не использует регистр (для источника или адресата), но ссылается на память, вам необходимо указать размер операнда памяти. Например, cmp [op], '+' размер данных, на которые ссылается [op], должен быть указан, поэтому вы добавляете к нему префикс размера. Размер может быть byte, word, dword. Вы сравниваете один символ, поэтому размер будет byte . Код должен был выглядеть так cmp byte [op], '+'
  • Если у вас есть переменная, и вы делаете mov eax, varname, это перемещает адрес varname в EAX. Если вы хотите переместить содержимое varname, вы должны заключить его в квадратные скобки, например mov eax, [varname] . При работе со строковыми адресами и подпрограмме print_string вам нужно передать адрес строки в EAX, а не ее содержимое. Так что опусти скобки.
  • Если вы используете call для вызова функции, ваша функция должна заканчиваться на ret.
  • Не jmp к функции, используйте call
  • Если вы хотите напечатать символ, не используйте print_string, используйте print_char. Поместите персонажа в EAX
  • Если вы хотите напечатать целое число, не используйте print_string, используйте print_int. Поместите целое число в EAX

Пересмотренный код выглядит следующим образом:

;program to add, multiply, or give the power of a number

%include "asm_io.inc"

segment .data

prompt1 db    "Enter a number: ", 0        ;prompts
prompt2 db    "Enter another number: ", 0
prompt3 db    "Enter an operand", 0
prompt4 db    "OP ", 0    ;idiotic prompts for printing
prompt5 db    " = ", 0    ;another printing prompt

segment .bss

num1  resd 1    ;variable for 1st number
num2  resd 1    ;variable for 2nd number
op    resd 1    ;variable for operand


segment .text

    global  asm_main
asm_main:
    enter   0,0                 ; setup routine
    pusha

    restart:
        mov     eax, prompt1    ; print out prompt
        call    print_string
        call    print_nl

        call    read_int        ; read integer
        mov     [num1], eax     ; store into num1
        call    print_nl

        mov     eax, prompt2    ; print out prompt
        call    print_string
        call    print_nl

        call    read_int        ; read integer
        mov     [num2], eax     ; store into num2
        call    print_nl

        mov     eax, prompt3    ;print out prompt
                                ;*** Typo - promtp3 changed to prompt3
        call    print_string
        call    print_nl

        call    read_char       ;read operand and dispose of null
        call    read_char
        mov     [op], eax       ;store operand in [op]
        call    print_nl

        cmp     byte [op], '*'  ;operand comparison
                                ;*** We must specify the size of data we are
                                ;*** comparing, so we tell the assembler that
                                ;*** with 'byte' in front of the variable
        jne     jmp1
        call    mulFun
        jmp     restart

jmp1:   cmp     byte [op], '+'  ;operand comparison
                                ;*** We must specify the size of data we are
                                ;*** comparing, so we tell the assembler that
                                ;*** with 'byte' in front of the variable
        jne     jmp2
        call    sumFun
        jmp     restart

jmp2:   cmp     byte [op], '^'  ;operand comparison
                                ;*** We must specify the size of data we are
                                ;*** comparing, so we tell the assembler that
                                ;*** with 'byte' in front of the variable
        jne     jmp3
;        call    expFun         ;*** This expFun function doesn't exist so
                                ;*** don't call it so we can compile and link
        jmp     restart

jmp3:   cmp     byte [op], 'e'  ;exit function
                                ;*** We must specify the size of data we are
                                ;*** comparing, so we tell the assembler that
                                ;*** with 'byte' in front of the variable
        je      end1

end1:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    call    print_nl          ; pr
    popa
    mov     eax, 0            ; return back to C
    leave
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mulFun:                     ;multiplication function
                            ;*** NASM is case sensitive - changed mulfun to mulFun
    mov     ebx, [num1]
    mov     ecx, [num2]
    imul    ebx, ecx
    call    printFun        ;*** Call printFun, don't 'jmp' to it
    ret                     ;*** Since mulfun was called, we use 'ret' to return

sumFun:                     ;sum function
    mov     ebx, [num1]
    mov     ecx, [num2]
    add     ebx, ecx
    call    printFun
    ret

printFun:                   ;printing function
    mov     eax, prompt4    ;*** We want the address of prompt4, not what is at prompt4
                            ;*** Remove the brackets from [prompt4]
    call    print_string
    mov     eax, [num1]
    call    print_int       ;*** Use print_int to print an integer, not print_string
    mov     eax, [op]
    call    print_char      ;*** Use print_char to print a char, not print_string
    mov     eax, [num2]
    call    print_int       ;*** Use print_int to print an integer, not print_string
    mov     eax, prompt5    ;*** We want the address of prompt5, not what is at prompt5
                            ;*** Remove the brackets from [prompt5]
    call    print_string
    mov     eax, ebx
    call    print_int       ;*** Use print_int to print an integer, not print_string;
    call    print_nl
    ret

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

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

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

person Michael Petch    schedule 04.11.2015
comment
Спасибо, Майкл, я решил свою проблему, и вы были правы в том, что вопрос был неправильно отмечен. Я провожу занятия онлайн, поэтому, когда в видео нет подробностей о чем-то вроде printf, это может создать барьеры между мной и программой. Я не очень четко задал вопрос, что, очевидно, уменьшило мои шансы получить правильный ответ, но ваш ответ указал мне правильное направление. - person Jamie Jackson; 06.11.2015