Сложение двух чисел для получения двузначного числа

Я хочу добавить два предопределенных значения вместе и получить результат. Что мой код делает в данный момент, так это складывает 16 и 6 вместе, что должно распечатать 22. Однако он распечатывает 2... Я не совсем уверен, как это исправить...

Вот код:

data    segment                         ; data segment. Keyword db means define byte. You can also define word (dw)
        numA    db  16                  ;Define first variable
        numB    db  06                  ;Define second variable
        StrMsg  db 'The answer is $'    ;return message to the user
        leng    db 1                    ;length of the charachters - this value will be overwritten
data    ends

                                        ; stack segment
stack1  segment stack       
        db  100 dup(?)                  ; This is the stack of 100 bytes
stack1  ends


code    segment
        assume  cs:code, ds:data, ss:stack1

start: 
                                        ;Perform initialization 
        mov ax, data                    ;Put the starting address of the data segment into the ax register (must do this first)
        mov ds, ax                      ;Put the starting address of the data segment into the ds register (where it belongs)

        mov ax, stack1                  ;Put the starting address of the stack into the ax register (must do this first)
        mov ss, ax                      ;Put the starting address of the stack segment into the ss register (where it belongs)

mov al, numA        ;move numA to ax
    add al, numB        ;ax contains numa + numb
    mov dl, al          ;move result to dl for display                      

    lea dx, StrMsg      ;load message to display the result to the user
    mov ah, 9h          ;display string subroutine 
    int 21h             ;interrupt for MS-DOS routine   

    add dl, 30h         ;Add 30h for ASCII table offset 
    mov ah, 2h          ;Store interrupt code in ah to display results in dl
    int 21h             ;display character in dl as translated by ascii code

mov ah, 4ch                     ;Set up code to specify return to dos
        int 21h                         ;Interpt number 21 (Return control to dos prompt)

code    ends

end     start

person user2961971    schedule 31.10.2014    source источник
comment
Поскольку вы печатаете только 1 символ, не удивляйтесь. Чтобы обрабатывать двузначные числа, вы можете печатать десятки и единицы друг за другом. Согласно математике начальной школы, это будет частное и остаток от деления на 10.   -  person Jester    schedule 31.10.2014
comment
Пожалуйста, добавьте тег для используемого вами ассемблера ([tasm] или [masm])   -  person rkhb    schedule 31.10.2014


Ответы (1)


Если вы действительно хотите получить только двузначное десятичное число (как следует из названия), вы можете использовать AAM:

data    segment                         ; data segment. Keyword db means define byte. You can also define word (dw)
        numA    db  16                  ;Define first variable
        numB    db  06                  ;Define second variable
        StrMsg  db 'The answer is $'    ;return message to the user
        leng    db 1                    ;length of the charachters - this value will be overwritten
data    ends

                                        ; stack segment
stack1  segment stack
        db  100 dup(?)                  ; This is the stack of 100 bytes
stack1  ends


code    segment
        assume  cs:code, ds:data, ss:stack1

start:
                        ;Perform initialization
    mov ax, data        ;Put the starting address of the data segment into the ax register (must do this first)
    mov ds, ax          ;Put the starting address of the data segment into the ds register (where it belongs)

    mov ax, stack1      ;Put the starting address of the stack into the ax register (must do this first)
    mov ss, ax          ;Put the starting address of the stack segment into the ss register (where it belongs)

    lea dx, StrMsg      ;load message to display the result to the user
    mov ah, 9h          ;display string subroutine
    int 21h             ;interrupt for MS-DOS routine

    mov al, numA        ;move numA to al
    add al, numB        ;al contains numa + numb

    lea di, StrMsg      ; Place for target string (The old value of StrMsg isn't used anymore)
    aam                 ; AL => AH (first dec. digit) AL (second dec. digit) (unpacked BCD)
    or ax, 3030h                 ; Convert both digits to ASCII
    mov BYTE PTR [di], ah        ; Store first digit
    mov BYTE PTR [di+1], al      ; Store second digit
    mov BYTE PTR [di+2], '$'     ; Store termination character for 'int 21h fn 09h'

    lea dx, StrMsg      ;load message to display the result to the user
    mov ah, 9h          ;display string subroutine
    int 21h             ;interrupt for MS-DOS routine

    mov ah, 4ch         ;Set up code to specify return to dos
    int 21h             ;Interpt number 21 (Return control to dos prompt)

code    ends

end     start

Если вы также хотите получить трехзначное десятичное число, вы можете изолировать цифры на два деления. Сначала разделите на 100, и в результате вы получите первую цифру. Затем разделите остаток на 10, и вы получите вторую цифру, а третья цифра находится в остатке:

data    segment                         ; data segment. Keyword db means define byte. You can also define word (dw)
        numA    db  16                  ;Define first variable
        numB    db  06                  ;Define second variable
        StrMsg  db 'The answer is $'    ;return message to the user
        leng    db 1                    ;length of the charachters - this value will be overwritten
data    ends

                                        ; stack segment
stack1  segment stack
        db  100 dup(?)                  ; This is the stack of 100 bytes
stack1  ends


code    segment
        assume  cs:code, ds:data, ss:stack1

start:
                        ;Perform initialization
    mov ax, data        ;Put the starting address of the data segment into the ax register (must do this first)
    mov ds, ax          ;Put the starting address of the data segment into the ds register (where it belongs)

    mov ax, stack1      ;Put the starting address of the stack into the ax register (must do this first)
    mov ss, ax          ;Put the starting address of the stack segment into the ss register (where it belongs)

    lea dx, StrMsg      ;load message to display the result to the user
    mov ah, 9h          ;display string subroutine
    int 21h             ;interrupt for MS-DOS routine

    mov al, numA        ;move numA to ax
    add al, numB        ;ax contains numa + numb
    mov dl, al          ;move result to dl for display

    lea di, StrMsg      ; Place for target string (The old value of StrMsg isn't used anymore)
    call al2dec

    lea dx, StrMsg      ;load message to display the result to the user
    mov ah, 9h          ;display string subroutine
    int 21h             ;interrupt for MS-DOS routine

    mov ah, 4ch         ;Set up code to specify return to dos
    int 21h             ;Interpt number 21 (Return control to dos prompt)

al2dec PROC             ; Args: AL register to convert, DS:DI pointer to target string
    mov bl, 100
    xor ah, ah          ; Clear AH for division
    div bl              ; AL = AX / BL remainder AH
    or al, 30h          ; Convert result to ASCII
    mov BYTE PTR [di], al        ; Store as first digit

    shr ax, 8           ; Shift remainder into AL, clear AH
    mov bl, 10
    div bl              ; AL = AX / BL remainder AH
    or al, 30h          ; Convert result to ASCII
    mov BYTE PTR [di+1], al      ; Store as second digit

    or ah, 30h          ; Convert remainder to ASCII
    mov BYTE PTR [di+2], ah      ; Store as third digit

    mov BYTE PTR [di+3], '$'     ; Store at last termination character for 'int 21h fn 09h'
    ret
al2dec ENDP             ; DS:DI contains string with decimal digits

code    ends

end     start

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

al2dec PROC                         ; Args: AL register to convert, DS:DI pointer to target string
    mov bl, 10                      ; Base 10 -> divisor
    xor cx, cx                      ; CX=0 (number of digits)

  al2dec_loop_1:                    ; 1st loop
    xor ah, ah                      ; Clear AH for division (don't forget it!)
    div bl                          ; AL = AX / BL   Remainder AH
    push ax                         ; Push remainder for LIFO in Loop_2
    add cl, 1                       ; Equivalent to 'inc cl'
    test al, al                     ; AL = 0?
    jnz al2dec_loop_1               ; No: once more
  al2dec_loop_2:                    ; 2nd loop
    pop ax                          ; Get back pushed digits
    or ah, 00110000b                ; Conversion to ASCII
    mov BYTE PTR [di], ah                    ; Store only AH to [DS:DI] (DI is a pointer to a string)
    add di, 1
    loop al2dec_loop_2              ; Until there are no digits left

    mov BYTE PTR [di], '$'                   ; Store termination character for 'int 21h fn 09h'
    ret                             ; Ret: DS:DI contains decimal '$'-terminated ASCII-String
al2dec ENDP
person rkhb    schedule 31.10.2014
comment
Нет причин использовать add cl, 1. Если вы оптимизируете размер кода с помощью таких инструкций, как loop, вы также должны использовать однобайтовый inc cx. Особенно потому, что вы обнуляете CX и используете его в качестве счетчика циклов, поэтому для удобочитаемости работы с CX везде было бы понятнее. Но пока это выглядит как разумный канонический дубликат для двузначных/многозначных вопросов DOS. (Мне не нравится 2 цикла push/pop, хотя он раздут по сравнению с простым сохранением в памяти от последнего к первому, начиная с конца буфера, особенно когда вы просто хотите печатать одним вызовом) - person Peter Cordes; 09.04.2019