Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[13.58.40.4] |
|
Страницы: (3) 1 [2] 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
Сообщ.
#17
,
|
|
|
Кстати, а как переводятся отрицательные числа ?
|
Сообщ.
#18
,
|
|
|
Для вывода отрицательных чисел нужно добавить строки в процедуру Show_ax, проверяющие знак числа. В случае отрицательного числа нужно вывести знак минус "-", и преобразовать число в регистре ax.
; если число в ax отрицательное, то ;1) напечатать '-' ;2) сделать ax положительным or ax, ax jns @@Conv push ax mov dx, '-' mov ah, 2 ; ah - функция вывода символа на экран int 21h pop ax neg ax В итоге это будет выглядеть ; выводит число в регистре 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). |
Сообщ.
#19
,
|
|
|
Немного помудрил...
Нужно увеличить буфер ввода на один символ ("-32768" длиной 6 символов) KeyBuf db 7, 0, 7 dup(0) ;max,len,string,CR(0dh) Str2Num получилась такая ; преобразования строки в число ; на входе: ; 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) перед циклом преобразования ;если в строке первый символ '-' ; - перейти к следующему ; - уменьшить количество рассматриваемых символов cmp byte ptr [si], '-' jne @@Loop inc si dec cx 2) после цикла преобразования pop si ;проверка на знак push si inc si cmp byte ptr [si], '-' jne @@StoreRes neg ax @@StoreRes: 3) внутри цикла преобразования js @@Error ; Если результат стал отрицательным 4) Чтобы была возможность просмотреть первый символ, нужно в начале процедуры сохранить si, а в конце процедуры извлечь из стека (в обоих случаях завершения - правильном и ошибочном). Т.е. идея следующая. 1) Если в строке символ "-", то его поначалу пропускаем и получаем положительный результат. 2) Потом опять проверяем исходную строку на наличие "-". Если он есть, то делаем результат отрицательным (neg ax). 3) Таким образом, во время выполнения преобразования временный результат все время должен быть положительным (флаг знака S - сброшен) - это и есть проверка внутри цикла. |
Сообщ.
#20
,
|
|
|
Сейчас еще раз потестировал - нашел ошибку. Не вводиться число -32768.
Нужно заменить строку js @@Error ; Если результат стал отрицательным на строки cmp ax, 8000h ja @@Error И еще добавить проверку для случая 32768. В целом Str2Num 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 Прикреплённый файлINPUT_2.ZIP (1.74 Кбайт, скачиваний: 150) |
Сообщ.
#21
,
|
|
|
Павел, тогда получается что 100 (Dec) = 144 (Oct) и -100 (Dec) = -144 (Oct) хотя если проверить на инженерном калькуляторе в Excel -100 (Dec) = 7777777634 (Oct) Так вот
Прикреплённый файлСкриншот.png (7.23 Кбайт, скачиваний: 290) |
Сообщ.
#22
,
|
|
|
Deffi
Думаю, что программа считает правильно. Ведь она не не изменяет само значение числа (переменную Number). А только в двух форматах (десятичном и восьмеричном) выводит на экран. И если это число -1448 сложить с числом 1448 должен получиться 0. А "калькулятор" производит преобразование из десятичной в восьмеричную без учета знака. Можешь даже проверить (если не лень). Измени программу - чтобы Str2Num была новая, а Show_ax - старая-беззнаковая. И увидишь такой же результат. |
Сообщ.
#23
,
|
|
|
Немного не поняла твоего пояснения, извини.
Добавлено Программа просто добавляет знак минуса на экран и все, и считает как положительные. |
Сообщ.
#24
,
|
|
|
Нет, 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 Вот такой хулиганский файл прилагаю Прикреплённый файлINPUT3.ZIP (1.69 Кбайт, скачиваний: 165) |
Сообщ.
#25
,
|
|
|
Ты так подробно все объясняешь, что и wikipedia нет надобности читать.
У нас даже преподаватели так подробно не объясняют. |
Сообщ.
#26
,
|
|
|
Павел, чесно сказать уже и совесть не позволяет что-либо у тебя еще спрашивать, но может напишешь как сделать, чтоб после ввода пользователем числа экзешник не закрывался автоматически, а только когда я нажму 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) - нет не было.." Почему умножаешь? Еще раз извини за глупые вопросы, просто хочу разобратся в алгоритме, а не тупо сдать программу написанную не мной... |
Сообщ.
#27
,
|
|
|
1. Закрываться на кнопку Esc.
.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). Вот вызов для восьмеричной: 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. Прикреплённый файлINPUT_5.ZIP (1.07 Кбайт, скачиваний: 139) |
Сообщ.
#28
,
|
|
|
Я к твоему файу Input_2.asm добавила вот такое:
@@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 с умножением разобралась, спасибо !!! |
Сообщ.
#29
,
|
|
|
Зацикли. Если не Esc, то переход не на метку @@WaitForEsc, a к приглашению ввести число - к новой метке @@Input.
Main PROC FAR mov ax, @DATA mov ds, ax mov es, ax @@Input: ; ввод числа с клавиатуры (строки) lea dx, Prompt mov ah,09h int 21h Прикреплённый файлINPUT_6.ZIP (1.85 Кбайт, скачиваний: 133) |
Сообщ.
#30
,
|
|
|
Ясненько
Павел, вопрос немного не по теме, может знаешь где можна проконсультироватся по UML (унифицированный язык моделирования)? Я спрашивала на нескольких форумах, но именно в разделе UML людей совсем нет, и мой вопрос остался без ответа. |