Умножение двух значений и вывод их на экран (NASM, Linux)

Я продолжаю читать, что для того, чтобы выполнить деление регистра на целое/с плавающей запятой, выполняемые регистры должны быть на самом деле initialized. Мне любопытно, какая правильная директива ассемблера для этого. Я просто предоставляю адрес примерно так:

mov ecx, 0x65F ;0x65F represents an address for ecx to point to.

А затем быстро (позже в коде) сделайте что-то вроде:

mov byte [ecx], 0xA ;move the value of 0xA into the contents of ecx, using only a byte's worth of data

Это правильный способ выполнить такую ​​​​операцию? Если нет, то что?

Обновить

Итак, что я пытаюсь сделать, так это умножить два значения и вывести их на экран. Код выглядит следующим образом, и по какой-то причине каждый раз, когда я пытаюсь разделить edx, я получаю либо ошибку сегментации, либо арифметическое исключение с плавающей запятой. Может ли кто-нибудь объяснить мне, что я делаю неправильно?

Код

section .data
    counter: db 0xA                         ;store value 10 in 'counter', while allocating only one byte. This will be used for decrementing purposes
section .bss
    valueToPrint: resb 4                    ;alloc 4 bytes of data in 'valueToPrint'

section .text

global _start

_print_char:                    
    add eax, '0'                ;convert to ascii
    mov [valueToPrint], eax     ;store contents of 'eax' in valueToPrint
    mov eax, 4                  ;syswrite
    mov ebx, 1                  ;stdout
    mov ecx, valueToPrint       ;machine will take whatever value exists in 'ecx' and print
    mov edx, 1                  ;print only a single byte's worth of data
    int 0x80                    ;invoke kernel to perfrom instruction
    ret                         

_convert_values:
    mov edx, 0xA                ;dividing eax by 10, which will lower its tens place
    div edx                     ;(**Program crash here**)do division: remainder SHOULD be stored in edx
    mov byte [edx], 0x0         ;zero out edx       
    call _print_char            ;do printing for latest character
    dec byte [counter]          ;decrement counter
    mov dword [eax], counter    ;store counter in eax
    jnz _convert_values         ;while eax > 0 continue process

_endl:
    mov eax, '\n'               ;store newline character in eax to be printed
    call _print_char            ;print value
    ret                 

_mul:
    mov eax, 0x2A ;store 42 in eax
    mov edx, 0x2B ;store 43 in edx
    mul edx       ;multiply [eax] * [edx]
    ret

_safe_exit:
    mov eax, 1  ;initiate 'exit' syscall
    mov ebx, 0  ;exit with error code 0
    int 0x80    ;invoke kernel to do its bidding 

_start:
    nop                             ;used to keep gdb from complaining

    call _mul                       ;multiply the values
    call _convert_values            ;do hex to ascii conversion

    jmp _safe_exit                  ;use jmp as opposed to call since it technically doesn't 'ret'

person zeboidlund    schedule 29.06.2012    source источник
comment
Пожалуйста, объясните, чего именно вы пытаетесь достичь. Да, в памяти/и/или регистрах должны быть установлены допустимые значения, чтобы инструкции могли выполнять что-либо осмысленное — это ничем не отличается от переменных в языке высокого уровня. Ваша первая инструкция просто устанавливает регистр ecx в 0x65f, вторая сохраняет байт 0xa в том, на что указывает ecx.   -  person 500 - Internal Server Error    schedule 30.06.2012
comment
Обновлено; Я ценю помощь.   -  person zeboidlund    schedule 30.06.2012
comment
См.: stackoverflow.com/questions/9793060/ Вам нужно обнулить edx, прежде чем выдавать инструкцию div.   -  person Mysticial    schedule 30.06.2012
comment
А вот еще один с той же проблемой: stackoverflow .com/questions/10343155/   -  person Mysticial    schedule 30.06.2012
comment
Как именно обнулить edx? Если я правильно понимаю, обнуление регистра означает обнуление старшей половины байта. Это правильно? Я пробовал и and, и xor на edx, и ни один из них не работал. Инструкции, приведенные в обоих этих вопросах/ответах, к сожалению, не помогли.   -  person zeboidlund    schedule 01.07.2012
comment
Есть много способов обнулить регистр, если он еще не содержит 0: один способ - выполнить операцию xor сам по себе, другой - переместить в него 0, третий - перезаписать его содержимым ячейки памяти, содержащей 0, или перезаписать его. с содержимым другого регистра, который уже содержит 0.   -  person Olof Forshell    schedule 24.10.2013


Ответы (1)


Мы говорили отдельно в чате....

Вот рабочая версия, с которой можно поиграть.

У него есть тонкая проблема. Сможете найти? Можете ли вы объяснить, ПОЧЕМУ он делает то, что делает?

; Multiply two numbers, display in ascii/decimal
;
; (because I have a 64bit system, this forces 32bit code)
        bits    32
;
        section .text
;
; _start is the ONLY label you MUST prepend _
; others might be library functions (ex: _printf,_exit)
; pure assembly only needs _start, if linked with glibc
; typically need _main INSTEAD of _start
;
        global _start
;
;
_start:
        nop                             ;placeholder for gdb's debug interrupt
;
        call    mul                     ;multiply the values
        call    convert_values          ;do hex to ascii conversion
;
        jmp     safe_exit               ;use jmp as opposed to call since it technically doesn't 'ret'
;
;
; subroutines / functions follow
;
mul:
        mov     eax, 0x2A               ;store 42 in eax
        mov     edx, 0x2B               ;store 43 in edx (42*43=1806)
        mul     edx                     ;multiply eax*edx, result in edx:eax
        ret
;
; this routine doesn't handle BIG values from 'mul' which extend into edx
; we're learning, don't make things multiply out to more than 4.2 billion-ish
convert_values:
        mov     edx,0                   ;value actually edx:eax, zero edx
        mov     ecx,0x0A                ;divide edx:eax by 10
        idiv    ecx                     ;result in eax, remainder in edx
        push    eax                     ;save value on stack
        mov     eax,edx                 ;put remainder (0-9) in eax
        add     eax,'0'                 ;convert value to ascii character
        call    print_char              ;print the latest character
        pop     eax                     ;restore value
        or      eax,eax                 ;set flags based on eax value
        jnz     convert_values          ;while eax != 0 continue process
;
; nasm doesn't convert \n into LF... just use 10, equivalent
endl:
        mov     eax, 10                 ;store newline character in eax to be printed
        call    print_char              ;print value
        ret
;
print_char:
        mov     [valueToPrint], eax     ;store contents of 'eax' in [valueToPrint]
        mov     eax, 4                  ;syswrite
        mov     ebx, 1                  ;stdout
        mov     ecx, valueToPrint       ;machine will take whatever value exists in [ecx] and print
        mov     edx, 1                  ;print only a single byte's worth of data
        int     0x80                    ;invoke kernel to perfrom instruction
        ret
;
safe_exit:
        mov     eax,1                   ;initiate 'exit' syscall
        mov     ebx,0                   ;exit with error code 0
        int     0x80                    ;invoke kernel to do its bidding
;
; =====================================
        section .bss
; this section is not allocated, just reserved.
; Automatically set to zero when program starts
;
; alloc 4 bytes of data in 'valueToPrint'
valueToPrint:
        resd    1               ; 1 resd=4 resb (Dword/Byte)
;
;

Осторожно, спойлеры...

Он печатает результат НАЗАД!
Чтобы исправить это, нам придется изменить способ получения и сохранения цифр перед печатью.

Я отправляю это по электронной почте непосредственно вам вместе с некоторыми дополнительными примечаниями.

person lornix    schedule 01.07.2012