Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Assembler > Перевод целых чисел из десятичной в восьмиричную систему счисления


Автор: Deffi 25.12.08, 15:17
Здраствуйте. Нужно чтоб программа отображала на экране любое введенное целое число в восьмиричной системе счисления.
Возникла проблема - не понимаю как записать, чтоб пользователь мог вводить любое число. Да и исходник не уверена что правильный. Вобщем кто разбирается подкоректируйте его пожалуйта. Буду очень благодарна!
Исходник:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    .MODEL Small
    .STACK 100h
    .DATA
            String          db      16 dup (?),'$'          ; Резервируем 16 байт для строки
            StringEnd               =       $-1                     ; Указывает на символ '$'
            Number          =       1
            Str_digit               db      '5.479$'
            Obrob_digit     db      8 dup (?),'$'
    ;       Drob_chast              db      8 dup (?),'$'
     
            Mes_1           db      0dh, 0ah, "Chislo 6219 v 16-y sisiteme schisleniya - ", "$"  
            Mes_2           db      0dh, 0ah, "Chislo 6219 v 8-y sisiteme schisleniya - ", " $"  
            Mes_3           db      0dh, 0ah, "Chislo 6219 v 2-y sisiteme schisleniya - ", " $"  
            Mes_4           db      0dh, 0ah, "Chislo 0.479 v 16-y sisiteme schisleniya - ", " $"  
     
    .CODE
    ORG     100h
     
     
    Start:
                    mov     ax,@data
                    mov     ds,ax
                    mov     es,ax          
     
                    mov     ch,16          
     
                    lea     dx,Mes_4
                    mov     ah,09h
                    int     21h
     
                    call    Razdel
     
                    mov     ah,09h
                    lea     dx,Obrob_digit
                    int     21h
     
    ;               mov     ah,09h
    ;               lea     dx,Drob_chast
    ;               int     21h
     
    ;               mov     ax,dx
    ;               mov     bx,16
    ;               jmp     Proverka_num
     
     
                    call    Perevod
     
    lea     dx,Mes_2
                    mov     ah,09h
                    int     21h
     
                    mov     ax,Number
                    mov  bx,8
                    lea     di,StringEnd-9
     
                    call    Perevod_num
                    mov     ah,09h
                    lea     dx,String
                    int     21h
    Perevod_num proc
     
                    push    dx
                    push    di
                    push    cx
                    
                    xor     cx,cx
     
    Proverka_num:
     
                    xor     dx,dx
                    div     bx
                    xchg    ax,dx
                    add     al,'0'
                    cmp     al,'9'
                    jbe  Cifra
                    add     al,'A'-('9'+1)
                    
    Cifra:
                    stosb
                    xchg    ax,dx
                    inc     cx
                    test  ax,ax
                    jne     Proverka_num
                    
                    pop     cx      
                    pop     di
                    pop     dx
                    ret
     
    Perevod_num endp
     
    end Start

Автор: Deffi 26.12.08, 14:32
Помогите пожалуйсто!

Автор: Федосеев Павел 27.12.08, 11:10
Лови, Deffi.
Все за тебя делать не стал, но ввод и преобразование строки в число показал.
Компилируй и линкуй:
tasm /zn filename.asm
tlink filename.obj

По ссылке Сложение чисел посмотри. Там немного прокомментирован перевод числа в десятичное представление. Восьмеричное - почти то же самое, только основание системы - 8.

Для ввода строки я использовал функцию DOS 0Ah. Она вводит в буфер следующего формата:
0-й байт - максимально вводимое число символов + 1 (на символ перевода строки - CR)
1-й байт - по возвращении из int 21h здесь будет длина введенной строки (без учета CR)
2-й байт - начало буфера ввода, его длина равна числу в 0-м байте.

Т.к. максимальное число, умещающееся в слове (двух байтах) равно 65535, то длину буфера я выбрал равной 5+1=6 байтам.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    .MODEL  Small
     
    .DATA
     
    KeyBuf  db      6, 0, 6 dup(0)      ;max,len,string,CR(0dh)
    CR_LF   db      0Dh, 0Ah, '$'
     
    Prompt  db      'Введите число: ', '$'
    Res10   db      'Введено число (десятичное  ): ', '$'
    Res08   db      'Введено число (восьмеричное): ', '$'
    Error01 db      'Ошибка ввода числа',0Dh, 0Ah, '$'
     
    Numer   dw      ?
     
    .CODE
     
    ; выводит число в регистре AX на экран
    ; входные данные:
    ; cx - система счисления (не больше 10)
    ; ax - число для отображения
    Show_ax PROC
    ;        mov     cx, 10
            xor     di, di          ; di - кол. цифр в числе
    @@Conv:
            xor     dx, dx
            div     cx              ; dl = num mod 10
            add     dl, '0'         ; перевод в символьный формат
            inc     di
            push    dx              ; складываем в стэк
            or      ax, ax
            jnz     @@conv
            ; выводим из стэка на экран
    @@Show:
            pop     dx              ; dl = очередной символ
            mov     ah, 2           ; ah - функция вывода символа на экран
            int     21h
            dec     di              ; повторяем пока di<>0
            jnz     @@show
            ret
    Show_ax ENDP
     
    ; преобразования строки в число
    ; на входе:
    ; ds:[si] - строка с числом
    ; ds:[di] - адрес числа
    ; на выходе
    ; ds:[di] - число
    ; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
    Str2Num PROC
            push    ax
            push    bx
            push    cx
            push    dx
            push    ds
            push    es
     
            push    ds
            pop     es
     
            mov     cl, ds:[si]
            xor     ch, ch
     
            inc     si
     
            mov     bx, 10
            xor     ax, ax
     
    @@Loop:
            mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
            mov     [di], ax   ; игнорируем старшее слово
            cmp     dx, 0      ; проверяем, результат на переполнение
            jnz     @@Error
     
            mov     al, [si]   ; Преобразуем следующий символ в число
            cmp     al, '0'
            jb      @@Error
            cmp     al, '9'
            ja      @@Error
            sub     al, '0'
            xor     ah, ah
            add     ax, [di]
            inc     si
     
            loop    @@Loop
     
            mov     [di], ax
            clc
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    @@Error:
            xor     ax, ax
            mov     [di], ax
            stc
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    Str2Num ENDP
     
    Main    PROC    FAR
        mov ax, @DATA
            mov ds, ax
            mov     es, ax
     
            ; ввод числа с клавиатуры (строки)
        lea dx, Prompt
            mov     ah,09h
            int     21h
     
            mov     ah, 0Ah
            mov     dx, offset KeyBuf
            int     21h
     
            ; перевод строки (на новую строку)
        lea dx, CR_LF
            mov     ah,09h
            int     21h
     
     
            ; преобразование строки в число
            lea     si, KeyBuf+1
            lea     di, Numer
            call    Str2Num
     
            ; проверка на ошибку
            jnc     @@NoError
     
            ; если есть ошибка ввода - напечатать сообщение об ошибке
        lea dx, Error01
            mov     ah,09h
            int     21h
            jmp     @@Exit
     
            ; если нет ошибки ввода - напечатать число
    @@NoError:
            ; в десятичном представлении
        lea dx, Res10
            mov     ah,09h
            int     21h
     
            mov     ax, Numer
            mov     cx, 10
            call    Show_ax
     
        lea dx, CR_LF
            mov     ah,09h
            int     21h
     
            ; в восьмеричном представлении
        lea dx, Res08
            mov     ah,09h
            int     21h
     
            mov     ax, Numer
            mov     cx, 8
            call    Show_ax
     
        lea dx, CR_LF
            mov     ah,09h
            int     21h
     
            ; выход
    @@Exit:
        mov ax,4c00h
        int 21h
    Main    ENDP
     
    .STACK  100h
     
            END     Main

Автор: Федосеев Павел 27.12.08, 13:19
Заметил у себя ошибку. Если ввести число 65536, то результат будет равен 0. А должно быть сообщение об ошибке.
Нужно добавить строку в процедуру Str2Num
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
           add     ax, [di]
           jc      @@Error       ; Если сумма больше 65535
           inc     si
     
           loop    @@Loop

Автор: Deffi 27.12.08, 15:27
Спасибо огромное Павел. У тебя есть аська, а то есть вопросы, чтоб тут не флудить.

Автор: Федосеев Павел 27.12.08, 15:31
К сожалению, нет ;) . Задавай на форуме. И ведь я не один.

Автор: Deffi 27.12.08, 15:49
Ну ладно, тогда вопрос первый как работает твоя программа? Скомпилила и слинковала. Запускаю ехе файл, черный пустой экран, ввожу любое число программа закрывается и все.

Автор: Федосеев Павел 27.12.08, 17:33
Цитата

Запускаю ехе файл, черный пустой экран

Чуть позже я подкорректировал исходник. Добавил "приглашение" - 'Введите число: '
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            ; ввод числа с клавиатуры (строки)
            lea     dx, Prompt
            mov     ah,09h
            int     21h


Цитата

Скомпилила и слинковала. Запускаю ехе файл .... ввожу любое число программа закрывается и все.


Добавь в конце после метки @@Exit ввод любой клавиши
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    @@Exit:
            ; ожидание нажатия любой клавиши
            mov     ah,01h
            int     21h
     
            mov     ax,4c00h
            int     21h
    Main    ENDP

Вот мой exe-шник

Автор: Deffi 28.12.08, 10:32
Приглашение действительно появилось, а вот когда добавила в конце после метки @@Exit ввод любой клавиши - все равно экзешник закрывается при вводе числа.
Скачала файл Input_D.zip, а чем открыть сам файл, он у тебя там без разширения?

Автор: Федосеев Павел 28.12.08, 19:29
Сейчас еще раз проверил - в архиве два файла Input_D.asm и Input_D.exe. Input_D.asm в кодировке DOS. Input_D.exe запускается и в конце ждет нажатия. Т.е. работает. Покажи еще раз свою программу. Может быть вместо int 21h выполняется int 21 ?
А какой файл в архиве без расширения? Там один exe-шник, другой asm. Сам архив открывается WinZip'ом или WinRar'ом.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    .MODEL  Small
     
    .DATA
     
    KeyBuf  db      6, 0, 6 dup(0)      ;max,len,string,CR(0dh)
    CR_LF   db      0Dh, 0Ah, '$'
     
    Prompt  db      'Введите число: ', '$'
    Res10   db      'Введено число (десятичное  ): ', '$'
    Res08   db      'Введено число (восьмеричное): ', '$'
    Error01 db      'Ошибка ввода числа',0Dh, 0Ah, '$'
     
    Numer   dw      ?
     
    .CODE
     
    ; выводит число в регистре AX на экран
    ; входные данные:
    ; cx - система счисления (не больше 10)
    ; ax - число для отображения
    Show_ax PROC
    ;        mov     cx, 10
            xor     di, di          ; di - кол. цифр в числе
    @@Conv:
            xor     dx, dx
            div     cx              ; dl = num mod 10
            add     dl, '0'         ; перевод в символьный формат
            inc     di
            push    dx              ; складываем в стэк
            or      ax, ax
            jnz     @@conv
            ; выводим из стэка на экран
    @@Show:
            pop     dx              ; dl = очередной символ
            mov     ah, 2           ; ah - функция вывода символа на экран
            int     21h
            dec     di              ; повторяем пока di<>0
            jnz     @@show
            ret
    Show_ax ENDP
     
    ; преобразования строки в число
    ; на входе:
    ; ds:[si] - строка с числом
    ; ds:[di] - адрес числа
    ; на выходе
    ; ds:[di] - число
    ; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
    Str2Num PROC
            push    ax
            push    bx
            push    cx
            push    dx
            push    ds
            push    es
     
            push    ds
            pop     es
     
            mov     cl, ds:[si]
            xor     ch, ch
     
            inc     si
     
            mov     bx, 10
            xor     ax, ax
     
    @@Loop:
            mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
            mov     [di], ax   ; игнорируем старшее слово
            cmp     dx, 0      ; проверяем, результат на переполнение
            jnz     @@Error
     
            mov     al, [si]   ; Преобразуем следующий символ в число
            cmp     al, '0'
            jb      @@Error
            cmp     al, '9'
            ja      @@Error
            sub     al, '0'
            xor     ah, ah
            add     ax, [di]
            jc      @@Error    ; Если сумма больше 65535
            inc     si
     
            loop    @@Loop
     
            mov     [di], ax
            clc
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    @@Error:
            xor     ax, ax
            mov     [di], ax
            stc
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    Str2Num ENDP
     
    Main    PROC    FAR
            mov     ax, @DATA
            mov     ds, ax
            mov     es, ax
     
            ; ввод числа с клавиатуры (строки)
            lea     dx, Prompt
            mov     ah,09h
            int     21h
     
            mov     ah, 0Ah
            mov     dx, offset KeyBuf
            int     21h
     
            ; перевод строки (на новую строку)
            lea     dx, CR_LF
            mov     ah,09h
            int     21h
     
     
            ; преобразование строки в число
            lea     si, KeyBuf+1
            lea     di, Numer
            call    Str2Num
     
            ; проверка на ошибку
            jnc     @@NoError
     
            ; если есть ошибка ввода - напечатать сообщение об ошибке
            lea     dx, Error01
            mov     ah,09h
            int     21h
            jmp     @@Exit
     
            ; если нет ошибки ввода - напечатать число
    @@NoError:
            ; в десятичном представлении
            lea     dx, Res10
            mov     ah,09h
            int     21h
     
            mov     ax, Numer
            mov     cx, 10
            call    Show_ax
     
            lea     dx, CR_LF
            mov     ah,09h
            int     21h
     
            ; в восьмеричном представлении
            lea     dx, Res08
            mov     ah,09h
            int     21h
     
            mov     ax, Numer
            mov     cx, 8
            call    Show_ax
     
            lea     dx, CR_LF
            mov     ah,09h
            int     21h
     
            ; выход
    @@Exit:
            ; ожидание нажатия любой клавиши
            mov     ah,01h
            int     21h
     
            mov     ax,4c00h
            int     21h
    Main    ENDP
     
    .STACK  100h
     
            END     Main

Автор: Deffi 29.12.08, 17:11
Я скачала вот этот архив, Input_D.zip (1.83 кб) открыла WinRar и у меня там один файл Input_D без разширения и все больше ничего нет.

В Tasm можно как-то сделать поддержку русского языка, а то у меня текст крякозяблами отображается?

И по поводу экзешника, открываю его появляется приглашения ввода числа, ввожу любое число (экзешник уже не закрывается после ввода) но результаты странные, ввожу число например 64, результат в восьмеричной системе этого числа 8 (правильно я понимаю?) , а появляется в результате 100. Я скриншот прикрепляю, посмотри пожалуйсто.

Автор: AndNot 29.12.08, 19:27
Цитата Deffi @
Я скачала вот этот архив, Input_D.zip (1.83 кб) открыла WinRar и у меня там один файл Input_D без разширения и все больше ничего нет.
Сама добавь расширение распакованному файлу Input_D, например ZIP, или RAR. И распакуй еще раз :)
Цитата Deffi @
ввожу число например 64, результат в восьмеричной системе этого числа 8 (правильно я понимаю?) , а появляется в результате 100
Ну так это и есть правильный результат.
Цитата Deffi @
В Tasm можно как-то сделать поддержку русского языка, а то у меня текст крякозяблами отображается?
В тасме нельзя :) Тебе нужно русификатор установить, для DOS-сессии.

Автор: Федосеев Павел 29.12.08, 21:15
Для избавления от кракозябр, нужно текст программы на ассемблере сконвертировать из кодировки Win(1251) в кодировку DOS(866), а затем скомпилировать. Полученный exe-шник будет "говорить по-русски".
Перекодировку можно сделать текстовым редактором AkelPad (скачать можно по ссылке AkelPad объем примерно 25 кБайт) из меню "Кодировки"-"Сохранить в DOS-866".
А результат в восьмеричной системе можно проверить в Windows-ком калькуляторе. Пункт меню "Вид" - Инженерный.
Набрать число в десятичной системе - Dec, а потом переключиться в восьмеричную систему - Oct. Очень наглядно видно, что 6410=1008.

Добавлено
Сейчас немного "погулял" по форуму, встретил несколько ссылок по системам счисления:
Системы счисления.
перевода чисел в другую систему счислени

Автор: Федосеев Павел 30.12.08, 20:49
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ; преобразования строки в число
    ; на входе:
    ; ds:[si] - строка с числом
    ; ds:[di] - адрес числа
    ; на выходе
    ; ds:[di] - число
    ; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
    Str2Num PROC
            push    ax            ; сохранение используемых регистров при входе в подпрограмму
            push    bx
            push    cx
            push    dx
            push    ds
            push    es
     
            push    ds            ; регистр es=ds - случайно остались две строки :) - можно удалить
            pop     es
     
            mov     cl, ds:[si]   ; для цикла LOOP устанавливаем счетчик - т.е. сколько
            xor     ch, ch        ; символов в строке нужно обработать
     
            inc     si            ; переходим к первому символу в строке (ds:si - содержит адрес)
     
            mov     bx, 10        ; множитель для каждой цифры
            xor     ax, ax        ; начальное значение результата приравниваем к нулю
     
    @@Loop:
            mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
            mov     [di], ax   ; сохраняем результат, игнорируя старшее слово
            cmp     dx, 0      ; проверяем, результат на переполнение
            jnz     @@Error
     
            mov     al, [si]   ; Преобразуем следующий символ в число
            cmp     al, '0'    ; проверяем на то, что в al находится символ цифры от '0' до '9'
            jb      @@Error    ; если не символ цифры - тогда ошибка
            cmp     al, '9'
            ja      @@Error
            sub     al, '0'
            xor     ah, ah     ; в ax сейчас число соответствующее цифре
            add     ax, [di]   ; сложить цифру (ax) с текущим результатом
            jc      @@Error    ; Если сумма больше 65535
            inc     si         ; перейти к адресу следующего символа цифры
     
            loop    @@Loop
     
            mov     [di], ax   ; сохранить результат
            clc                ; сбросить флаг CY - ошибки нет
            pop     es         ; восстановить регистры
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    @@Error:
            xor     ax, ax     ; в случае ошибки записать результат равным нулю
            mov     [di], ax
            stc                ; и установить флаг переноса CY - ошибка при преобразовании строки
            pop     es         ; восстановить регистры
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    Str2Num ENDP


Идея примерно следующая:
На входе имеется
1) адрес строки в формате: длина_строки (1 байт), строка (несколько байт);
2) адрес, по которому будет находиться результат (2 байта).

После завершения подпрограммы
1) если сброшен бит CY (переноса), то по второму адресу будет записано число, соответсвующее строке;
2) если флаг CY установлен, то значит во время преобразования произошла ошибка (или результат должен был быть больше 65535, или в строке присутствовали не только цифры но и лишние символы).

Давай посмотрим по шагам для строки '987'.
При входе в п/п
ds:si указывает на строку db 3, '987' (si=адресу ячейки памяти с числом 3)
Перед входом в цикл LOOP после всех присвоений имеем
cx=3, ax=0, ds:si - равен адресу символа '9', bx=10.
Вошли в цикл.
dx:ax=ax*bx=0*10=0 (т.е. dx=0 и ax=0)
Записали в результат значение ax - ds:[di]=ax=0
Проверяем на переполнение (результат не превышает 65535 - т.е. dx должен быть равен 0) - все в порядке.
По адресу ds:[si] считываем в al символ цифры (mov al, [si]) - al='9'.
Проверяем, чтобы '0' <= al <= '9' - да, условие выполняется.
Из al вычитаем число, соответствующее коду символа '0' - теперь al=9 (и ax=9).
Складываем с результатом (по адресу ds:[di]) - ax=0+9=9.
Проверяем, не было ли переполнения при сложении (число больше 65535) - нет не было.
Адрес указателя ds:si увеличиваем на 1 - теперь в ds:[si] адрес символа '8' - это для следующего прохода.
Оператор loop вычитает из cx единицу и проверяет cx=0? Нет cx=cx-1=3-1=2 <> 0. Переход к метке @@Loop.

Дальше для ускорения описания пропущу проверки.
Снова проходим цикл с метки @@Loop.
dx:ax=ax*bx=9*10=90.
Результат (ds:[di]) приравниваем к ax=90.
Считываем в al символ из адреса ds:[si] - т.е. al='8'
Вычитаем из al код символа '0' - al=8 (ax=8).
Складываем с результатом (по адресу ds:di) - ax=90+8=98.
Адрес указателя ds:si увеличиваем на 1 - теперь в ds:[si] адрес символа '7' - это для следующего прохода.
Оператор loop вычитает из cx единицу и проверяет cx=0? Нет, cx=cx-1=2-1=1 <> 0. Переход к метке @@Loop.

Снова проходим цикл с метки @@Loop.
dx:ax=ax*bx=98*10=980.
Результат (ds:[di]) приравниваем к ax=980.
Считываем в al символ из адреса ds:[si] - т.е. al='7'
Вычитаем из al код символа '0' - al=7 (ax=7).
Складываем с результатом (по адресу ds:di) - ax=980+7=987.
Адрес указателя ds:si увеличиваем на 1 - теперь в ds:[si] адрес за символом '7' - хоть и некорректный, но это ведь последний проход цикла.
Оператор loop вычитает из cx единицу и проверяет cx=0? Да, cx=cx-1=1-1=0 == 0. Переход к следующему за Loop оператору.

По адресу ds:[di] записывается содержимое ax=987.
Сбрасывается флаг переноса CY - нет ошибки преобразования.
Восстанавливаются регистры (их содержимое теперь равно тому, что было перед входом в подпрограмму) - это можно делать, т.к. результат возвращается не в регистре, а в памяти по адресу ds:[di].

Если на вход подпрограммы подать некорректные данные, то во время одной из проверок произойдет переход на метку @@Error. Тогда там обнуляется регистр ax (xor ax, ax) и значение ax (т.е. ноль) записывается в ds:[di]. А также устанавливается признак ошибки - флаг переноса CY. Потом восстанавливаются регистры.

В общем, весь алгоритм напоминает схему Горнера - ( 9*10 + 8 )*10 + 7 = 987.

"Несколько" коряво, но вроде бы понятно (надеюсь)...

У-ух. Набрал... :)

Автор: Deffi 01.01.09, 15:17
Спасибо, Павел! Столько набрал, я даже не ожидала :)
Если возникнут еще вопросы я напишу сюда, хотя врядли после такого подробного коментария.

P.S С НОВЫМ ГОДОМ! Желаю тебе крепкого здоровья, удачи, финансового благополучия, ну и чтоб все то добро, которое ты делаешь другим людям вернулось тебе в трехкратном размере :wub: !

Автор: miksayer 03.01.09, 23:15
кстати
http://flatassembler.net/examples/basecnv.zip

Автор: Deffi 12.01.09, 09:41
Кстати, а как переводятся отрицательные числа ?

Автор: Федосеев Павел 12.01.09, 11:12
Для вывода отрицательных чисел нужно добавить строки в процедуру Show_ax, проверяющие знак числа. В случае отрицательного числа нужно вывести знак минус "-", и преобразовать число в регистре ax.

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            ; если число в ax отрицательное, то
            ;1) напечатать '-'
            ;2) сделать ax положительным
            or      ax, ax
            jns     @@Conv
            push    ax
            mov     dx, '-'
            mov     ah, 2           ; ah - функция вывода символа на экран
            int     21h
            pop     ax
     
            neg     ax


В итоге это будет выглядеть

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ; выводит число в регистре AX на экран
    ; входные данные:
    ; cx - система счисления (не больше 10)
    ; ax - число для отображения
    Show_ax PROC
    ;        mov     cx, 10
            xor     di, di          ; di - кол. цифр в числе
     
            ; если число в ax отрицательное, то
            ;1) напечатать '-'
            ;2) сделать ax положительным
            or      ax, ax
            jns     @@Conv
            push    ax
            mov     dx, '-'
            mov     ah, 2           ; ah - функция вывода символа на экран
            int     21h
            pop     ax
     
            neg     ax
     
    @@Conv:
            xor     dx, dx
            div     cx              ; dl = num mod 10
            add     dl, '0'         ; перевод в символьный формат
            inc     di
            push    dx              ; складываем в стэк
            or      ax, ax
            jnz     @@conv
            ; выводим из стэка на экран
    @@Show:
            pop     dx              ; dl = очередной символ
            mov     ah, 2           ; ah - функция вывода символа на экран
            int     21h
            dec     di              ; повторяем пока di<>0
            jnz     @@show
            ret
    Show_ax ENDP


Для ввода, нужно добавить строки в процедуре Str2Num
1) перед началом цикла преобразования проверить - первый символ равен '-'. Если равен, то перейти к следующему символу (inc si)
2) после завершения преобразования (а пока что преобразовалось число без учета знака), ещё раз проверить самый первый символ на равенство '-'. Если равен, то нужно выполнить преобразование (neg ax), перед сохранением результата в памяти (mov [di], ax). Еще, желательно, перед сохранением результата выполнить проверку - если результат не соответствует знаку, то это указывает на ошибку (в два байта помещаются числа из диапазона -32768...32767 и если преобразовывать, например, число +32768, то результат будет -32768).

Автор: Федосеев Павел 12.01.09, 12:18
Немного помудрил...
Нужно увеличить буфер ввода на один символ ("-32768" длиной 6 символов)

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    KeyBuf  db      7, 0, 7 dup(0)      ;max,len,string,CR(0dh)


Str2Num получилась такая

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ; преобразования строки в число
    ; на входе:
    ; ds:[si] - строка с числом
    ; ds:[di] - адрес числа
    ; на выходе
    ; ds:[di] - число
    ; CY - флаг переноса (при ошибке - установлен, иначе - сброшен)
    Str2Num PROC
            push    ax
            push    bx
            push    cx
            push    dx
            push    ds
            push    es
            push    si
     
            push    ds
            pop     es
     
            mov     cl, ds:[si]
            xor     ch, ch
     
            inc     si
     
     
            mov     bx, 10
            xor     ax, ax
     
            ;если в строке первый символ '-'
            ; - перейти к следующему
            ; - уменьшить количество рассматриваемых символов
            cmp     byte ptr [si], '-'
            jne     @@Loop
            inc     si
            dec     cx
    @@Loop:
            mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
            mov     [di], ax   ; игнорируем старшее слово
            cmp     dx, 0      ; проверяем, результат на переполнение
            jnz     @@Error
     
            mov     al, [si]   ; Преобразуем следующий символ в число
            cmp     al, '0'
            jb      @@Error
            cmp     al, '9'
            ja      @@Error
            sub     al, '0'
            xor     ah, ah
            add     ax, [di]
            jc      @@Error    ; Если сумма больше 65535
            js      @@Error    ; Если результат стал отрицательным
            inc     si
     
            loop    @@Loop
     
            pop     si         ;проверка на знак
            push    si
            inc     si
            cmp     byte ptr [si], '-'
            jne     @@StoreRes
            neg     ax
    @@StoreRes:
            mov     [di], ax
            clc
            pop     si
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    @@Error:
            xor     ax, ax
            mov     [di], ax
            stc
            pop     si
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    Str2Num ENDP


Здесь добавились строки
1) перед циклом преобразования
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            ;если в строке первый символ '-'
            ; - перейти к следующему
            ; - уменьшить количество рассматриваемых символов
            cmp     byte ptr [si], '-'
            jne     @@Loop
            inc     si
            dec     cx

2) после цикла преобразования
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            pop     si         ;проверка на знак
            push    si
            inc     si
            cmp     byte ptr [si], '-'
            jne     @@StoreRes
            neg     ax
    @@StoreRes:

3) внутри цикла преобразования
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            js      @@Error    ; Если результат стал отрицательным

4) Чтобы была возможность просмотреть первый символ, нужно в начале процедуры сохранить si, а в конце процедуры извлечь из стека (в обоих случаях завершения - правильном и ошибочном).


Т.е. идея следующая.
1) Если в строке символ "-", то его поначалу пропускаем и получаем положительный результат.
2) Потом опять проверяем исходную строку на наличие "-". Если он есть, то делаем результат отрицательным (neg ax).
3) Таким образом, во время выполнения преобразования временный результат все время должен быть положительным (флаг знака S - сброшен) - это и есть проверка внутри цикла.

Автор: Федосеев Павел 12.01.09, 13:45
Сейчас еще раз потестировал - нашел ошибку. Не вводиться число -32768.
Нужно заменить строку
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            js      @@Error    ; Если результат стал отрицательным

на строки
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            cmp     ax, 8000h
            ja      @@Error

И еще добавить проверку для случая 32768. В целом Str2Num
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Str2Num PROC
            push    ax
            push    bx
            push    cx
            push    dx
            push    ds
            push    es
            push    si
     
            push    ds
            pop     es
     
            mov     cl, ds:[si]
            xor     ch, ch
     
            inc     si
     
     
            mov     bx, 10
            xor     ax, ax
     
            ;если в строке первый символ '-'
            ; - перейти к следующему
            ; - уменьшить количество рассматриваемых символов
            cmp     byte ptr [si], '-'
            jne     @@Loop
            inc     si
            dec     cx
    @@Loop:
            mul     bx         ; умножаем ax на 10 ( dx:ax=ax*bx )
            mov     [di], ax   ; игнорируем старшее слово
            cmp     dx, 0      ; проверяем, результат на переполнение
            jnz     @@Error
     
            mov     al, [si]   ; Преобразуем следующий символ в число
            cmp     al, '0'
            jb      @@Error
            cmp     al, '9'
            ja      @@Error
            sub     al, '0'
            xor     ah, ah
            add     ax, [di]
            jc      @@Error    ; Если сумма больше 65535
            cmp     ax, 8000h
            ja      @@Error
            inc     si
     
            loop    @@Loop
     
            pop     si         ;проверка на знак
            push    si
            inc     si
            cmp     byte ptr [si], '-'
            jne     @@Check    ;если должно быть положительным
            neg     ax         ;если должно быть отрицательным
            jmp     @@StoreRes
    @@Check:                   ;дополнительная проверка
           or       ax, ax     ;
           js       @@Error
    @@StoreRes:                ;сохранить результат
            mov     [di], ax
            clc
            pop     si
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    @@Error:
            xor     ax, ax
            mov     [di], ax
            stc
            pop     si
            pop     es
            pop     ds
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    Str2Num ENDP

Автор: Deffi 12.01.09, 16:37
Павел, тогда получается что 100 (Dec) = 144 (Oct) и -100 (Dec) = -144 (Oct) хотя если проверить на инженерном калькуляторе в Excel -100 (Dec) = 7777777634 (Oct) Так вот :(

Автор: Федосеев Павел 12.01.09, 17:11
Deffi
Думаю, что программа считает правильно. Ведь она не не изменяет само значение числа (переменную Number). А только в двух форматах (десятичном и восьмеричном) выводит на экран. И если это число -1448 сложить с числом 1448 должен получиться 0. А "калькулятор" производит преобразование из десятичной в восьмеричную без учета знака.
Можешь даже проверить (если не лень). Измени программу - чтобы Str2Num была новая, а Show_ax - старая-беззнаковая. И увидишь такой же результат.

Автор: Deffi 13.01.09, 11:46
Немного не поняла твоего пояснения, извини.

Добавлено
Программа просто добавляет знак минуса на экран и все, и считает как положительные.

Автор: Федосеев Павел 13.01.09, 15:53
Нет, Deffi, не просто добавляет.
Обе подпрограммы работают уже с другим форматом данных. При отображении на экран действительно кажется только, что добавляется символ "-". Но так и должно быть.
Например, 10010=1448 и -10010=-1448. Все вроде правильно. Но внутреннее представление этих чисел разное.
Посмотрим пример:
10010=1448=6416=0000 0000 0110 01002 - вроде понятно
-10010=-1448=-6416=-0000 0000 0110 01002 - тоже понятно.
Но как в пределах 16-бит найти место для обозначения отрицательных чисел?
Разработчики микросхем пошли по следующему пути. Решили, что старший бит будет означать знак минус. Так для чисел осталось 15 бит. Положительные от 0000 0000 0000 00002 до 0111 1111 1111 11112 (от 0 до 32767) и отрицательные от 1111 1111 1111 11112 до 1000 0000 0000 00002 (от -1 до -32368). Отрицательные числа решили сделать в машинном представлении такими, чтобы без дополнительных материальных (в смысле транзисторов внутри процессора) затрат организовать вычисления - т.е. чтобы -1+1=0, как при обычном сложении. Возьмем число 1=0000 0000 0000 00012. Что нужно к нему прибавить, чтобы получился 0=0000 0000 0000 00002? Правильно, 1111 1111 1111 11112=-1. Заметь, что число -1=1111 1111 1111 11112, а не 1000 0000 0000 0001. Такой формат называется "дополнительный код".
В дополнительном коде
010=0000 0000 0000 00002
110=0000 0000 0000 00012
210=0000 0000 0000 00102
310=0000 0000 0000 00112
...........................................
3276710=0111 1111 1111 11112
-110=1111 1111 1111 11112 - в дополнительном коде
-210=1111 1111 1111 11102 - в дополнительном коде
-310=1111 1111 1111 11012 - в дополнительном коде
.............................................
-3276810=1000 0000 0000 00002 - в дополнительном коде

Первый вариант программы работал с положительными числами от 0 до 65535 (никаких отрицательных). Поэтому там символ "-" рассматривался как ошибочный. При вводе строки "65535" в переменную Number процедура Str2Num записывала число 1111 1111 1111 11112. Подпрограмма Show_ax преобразовывала число в Number в строку "65535" и выводиоа на экран.

Второй вариант программы работает уже форматом чисел в дополнительном коде. Поэтому пришлось изменить процедуры ввода числа и вывода числа.

Проще изменения в Show_ax - проверяется знаковый бит, и если он установлен, то сразу же выводится на экран символ "-", а потом инвертируется число (neg ax), т.е. было -10010=-1448=-0000 0000 0110 01002=1111 1111 1001 11002, а стало +10010=+1448=0000 0000 0110 01002. А потом уже это число преобразуется в строку. Все это есть в комментариях.

В Str2Num изменений побольше. Еще до начала цикла проверяется строка на сивол "-". Если его нет, то преобразование почти без изменений по сравнению с первым (беззнаковым вариантом) - добавилось лишь ограничение диапазона чисел до 32767. Если же первым в строке стоит символ "-", тогда перед циклом переходим к следующему символу (см. в комментариях). Потом происходит такое же преобразование строки, получаем положительное число. После преобразования еще раз проверяется первый символ в строке, если он "-", тогда число приводится к дополнительному коду (neg ax).

В общем, первый вариант программы работал с беззнаковыми числами, а второй со знаковыми в дополнительном коде.
Если я не очень хорошо объяснил, почитай в книжках по асму про дополнительный код или в Wiki http://ru.wikipedia.org/wiki/Дополнительный_код_(представление_числа).

Из баловства, можешь поэкспериментировать с разными сочетаниями подпрограмм ввода/вывода. При знаковом вводе и беззнаковом выводе будут получаться необычные результаты - как на "калькуляторе":
вводим: -100
получаем: 6543610 и 1776348
Вот такой хулиганский файл прилагаю

Автор: Deffi 14.01.09, 11:02
Ты так подробно все объясняешь, что и wikipedia нет надобности читать.
У нас даже преподаватели так подробно не объясняют.

Автор: Deffi 14.01.09, 16:53
Павел, чесно сказать уже и совесть не позволяет что-либо у тебя еще спрашивать, но может напишешь как сделать, чтоб после ввода пользователем числа экзешник не закрывался автоматически, а только когда я нажму Esc.

Добавлено
И еще такой вопрос, немного неясен твой алгоритм, ведь например в математике для перевода десятичного числа в восьмиричное, его нужно последовательно делить на 8 до тех пор, пока не останется остаток, меньше или равен 7, сдесь же ты умножаешь ax*bx
"...Вошли в цикл.
dx:ax=ax*bx=0*10=0 (т.е. dx=0 и ax=0)
Записали в результат значение ax - ds:[di]=ax=0
Проверяем на переполнение (результат не превышает 65535 - т.е. dx должен быть равен 0) - все в порядке.
По адресу ds:[si] считываем в al символ цифры (mov al, [si]) - al='9'.
Проверяем, чтобы '0' <= al <= '9' - да, условие выполняется.
Из al вычитаем число, соответствующее коду символа '0' - теперь al=9 (и ax=9).
Складываем с результатом (по адресу ds:[di]) - ax=0+9=9.
Проверяем, не было ли переполнения при сложении (число больше 65535) - нет не было.."

Почему умножаешь?

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

Автор: Федосеев Павел 14.01.09, 21:06
1. Закрываться на кнопку Esc.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    .DATA
    ........................
    ExitMsg db      'Для завершения нажмите Esc', '$'
    ..........................
    .CODE
    ..........................
    @@Exit:
            lea     dx, ExitMsg
            mov     ah,09h
            int     21h
     
            ; ожидание нажатия клавиши Esc
    @@WaitForEsc:
            mov     ah,07h        ; ожидаем нажатие любой клавиши
            int     21h
     
            cmp     al, 1Bh       ; 1Bh - код клавиши Esc
            jnz     @@WaitForEsc  ; если это не Esc, то повторить
    .............................


2. Откуда умножение.
Show_ax - подпрограмма для вывода на экран. Именно в ней и происходит преобразование в десятичный или в восьмеричный вид. Поэтому она вызывается в теле основной программы (Main) два раза - один раз для десятичного отображения введенного числа, другой раз для восьмеричного отображения того же числа. Там и происходит деление исходного числа на основание системы счисления (основание системы передается в п/п в регистре cx). Вот вызов для восьмеричной:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
            mov     ax, Numer
            mov     cx, 8
            call    Show_ax

Обрати внимание на закомментированную строку в начале Show_ax (mov cx, 10). Эта процедура изначально предназначалась лишь для десятичного отображения, но теперь в регистре cx можно передавать любое значение для системы счисления не превосходящее 10 (если нужно больше 10 - тогда нужно усложнять процедуру).
Как ты справедливо отмечаешь, исходное число (ax) нужно делить на основание системы - так и происходит (div cx). Оператор div cx - содержимое регистровой пары dx:ax делится на cx (поэтому перед делением обнуляется dx - xor dx,dx), частное от деления помещается в ax, а остаток в dx. Т.к. делим мы на небольшие числа (в пределах 10), то dh=0, а в dl - нужный нам остаток. Чтобы не повторяться дам ссылку, где кратко объясняется работа процедуры Show_ax, только в пояснении замени число 10 на "основание системы счисления" Сложение чисел .

Str2Num - это процедура преобразования строки в число - поэтому там и нет деления, зато есть умножение.

Давай посмотрим что делает программа (с метки Main):
1. Вводится строка, содержащая символы ('-', '0',... ,'9') - это еще не число.
2. Вызывается процедура преобразования строки в число Str2Num. Она из строки получает двухбайтное число Numer.
3. Число Numer выводится в десятичной системе - процедура Show_ax.
4. Число Numer выводится в восьмеричной системе - процедура Show_ax.
5. Ожидается нажатие Esc.

Если не критично (в смысле условия задания), то чтобы не путаться, можешь удалить всю процедуру Str2Num и её вызовы из программы. Само число набери в строке (Numer dw -123). Вызывая два раза Show_ax увидишь как десятичное, так и восьмеричное представление числа Numer.

Автор: Deffi 15.01.09, 09:53
Я к твоему файу Input_2.asm добавила вот такое:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    @@Exit:
            ; ожидание нажатия любой клавиши
            mov     ah,01h
            int     21h
     
     
            cmp     al, 1Bh       ; 1Bh - код клавиши Esc
            jnz     @@Exit  ; если это не Esc, то повторить
     
     
     
            mov     ax,4c00h
            int     21h
    Main    ENDP

Теперь прога закрывается после нажатия кнопки ESC, а если я хочу ввести несколько чисел, вот например ввела первое число, программа его перева и напечатала результат, а потом чтоб опять вывелось приглашение 'Введите число (-32768..+32767): , как такое сделать?

P.S с умножением разобралась, спасибо !!!

Автор: Федосеев Павел 15.01.09, 10:24
Зацикли. Если не Esc, то переход не на метку @@WaitForEsc, a к приглашению ввести число - к новой метке @@Input.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Main    PROC    FAR
            mov     ax, @DATA
            mov     ds, ax
            mov     es, ax
    @@Input:
            ; ввод числа с клавиатуры (строки)
            lea     dx, Prompt
            mov     ah,09h
            int     21h

Автор: Deffi 15.01.09, 11:42
Ясненько :)
Павел, вопрос немного не по теме, может знаешь где можна проконсультироватся по UML (унифицированный язык моделирования)? Я спрашивала на нескольких форумах, но именно в разделе UML людей совсем нет, и мой вопрос остался без ответа.

Автор: Федосеев Павел 15.01.09, 12:28
Нет, про UML ничего не знаю. Попробуй погуглить, пояндексить или порамблерить. Ещё существует "студенческий коллективный разум" внутри собственной группы или потока ;) .

Автор: Deffi 16.01.09, 15:35
Я обучаюсь заочно, так что студенческий коллективный разум внутри собственной группы или потока - это исключено ;)

Автор: Deffi 07.02.09, 11:17
Привет, Павел! У меня возникли сложности при написании лабораторной работы. У меня задание: найти произведение элементов массива, и максимальное значение.
Работаю я в TASM, а в учебнике нашла похожие примеры, но для MASM. Вобщем кое-как слепила из-этих примеров себе лабораторную. Но при компилировании возникали ошибки, так как Tasm не понимал команды Masm. Пыталась изменять команды, чтоб исходник компилировался в TASM, но все время возникают ошибки.
Павел, помоги мне подправить этот исходник. Очень тебе прошу :rolleyes: !
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    ; Подготовка программы:
    ; MASM /Z /ZI /N P, P, P;
    ; LINK /CO P,P;
    ; Програмные мтроки
    text    segment 'code'
            assume  cs:text, ds:data
    ; Определения
    count = 80;
    KEYBOARD STRUCT
        maxInput    BYTE    count   ; Максималтный размер буфера
        inputCount  BYTE    ?       ; Количество введеных символов
        buffer      BYTE    count DUP(?)    ; Введенные символы
    KEYBOARD ENDS
     
    myproc  proc
        mov     AX,data        
        mov     DS,AX
    ; Очистка экрана
        mov     ax,0003h
        int     10h
    ; Читает строку символов из стандартного устройства ввода
        mov     ah,0Ah
        mov     dx,OFFSET kybdData
        int     21h
    ; Перемещение курсора в начало следующец строки
        mov     ah,02h
        mov     dl,0Ah
        int     21h
        mov     bl,kybdData.inputCount
        mov     dx,OFFSET kybdData
        add     dx,2
        add     dx,bx
        mov     di,dx
        mov     byte ptr [di],'$'
    ; Выводит строку, оканчивающуюся символом "$", на стандартное устройство вывода
        mov     ah,09h
        mov     dx,OFFSET kybdData
        add     dx,2
        int     21h
    ; Добуток елеиентів масиву
        mov     si,OFFSET arrayB
        mov     cx,(LENGTHOF arrayB)-1
        mov     ax,[si]
    L1:
        add     si,2        ; Поскольку массив arrayB типа WORD
        mov     bx,[si]
        mul     bx          ; В ax будет гаходится результат умножения
        loop    L1
    ; максимальне значення масиву
        mov     si,OFFSET arrayB
        mov     cx,(LENGTHOF arrayB)-1
        mov     ax,[si]
        .WHILE  cx > 0
        add     si,2        ; Поскольку массив arrayB типа WORD
        dec     cx
        .IF     ax < [si]
        mov     ax,[si]
    ;   .ELSE  
    ;   mov     bx,ax
        .ENDIF
        .ENDW
    ; Завершим программу
        mov     AX,4C00h
        int     21h
    myproc  endp
    text    ends
     
    ; Поля данных
        data    segment
        kybdData KEYBOARD <>
        arrayB  WORD        2, 1, 1, 1, 1, 1, 3, 1, 1, 5, 1
        data    ends
        stack   segment     para stack 'stak'
                db          128 dup (?)
        stack   ends
                end         myproc

Автор: Федосеев Павел 07.02.09, 21:55
Привет, Deffi

Вообще-то, это уже вопрос для отдельной темы.
Поэтому отвечу в приват.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)