Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Задача  (Прочитано 24495 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Vanya1529
Интересующийся

by
Offline Offline

« : 10-04-2014 16:49 » 

Привет. Помогите с задачей:

Условия задачи: Задано целое беззнаковое число Х<65536. Определить, есть ли в записи числа цифра, равная среднему арифметическому остальных цифр? Полученное значение вывести на экран.

Дела такие, введенное число н. п. 50433 я разбиваю на числа 5, 0, 4, 3, 3 делением на 10 и записываю в стэк, как дальше делать - никак не пойму. Вот код:
Код:
...
mov ax,62002
mov number,ax
mov bx,10
mov cx,0

a:
div bx
push dx
mov dx,0
inc cx
cmp ax,0
jne a

mov bx,cx
...
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 10-04-2014 16:58 » 

Vanya1529, а если среднее - не целое, то что будешь делать?

Цитата: Vanya1529
введенное число н. п. 50433 я разбиваю на числа 5, 0, 4, 3, 3 делением на 10 и записываю в стэк, как дальше делать - никак не пойму.
Ну как что? ты ж сам написал: посчитать среднее, затем поискать число.

Как считается среднее?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #2 : 10-04-2014 17:11 » 

Vanya1529, а если среднее - не целое, то что будешь делать?
Об этом не думал как-то
Ну как что? ты ж сам написал: посчитать среднее, затем поискать число.

Как считается среднее?
Складываются все и делятся на количество, но я все-равно не понимаю, стопр какой-то
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 10-04-2014 17:23 » 

Vanya1529, чего не понимаешь? Ты ж написал цикл получения цифр. Ну напиши цикл сложения цифр, а потом подели. Подсказка: складывать можно прям в том же цикле, где и цифры "добываешь".
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #4 : 10-04-2014 17:38 » 

Vanya1529, чего не понимаешь? Ты ж написал цикл получения цифр. Ну напиши цикл сложения цифр, а потом подели. Подсказка: складывать можно прям в том же цикле, где и цифры "добываешь".
Всмысле прямо в стеке складывать?Это как?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 10-04-2014 19:18 » 

Vanya1529, что значит прямо в стеке? Ты же число сперва получаешь в регистре, прежде чем в стек положить.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #6 : 10-04-2014 19:21 » 

Вот что-то получилось, только там где вопросы я не знаю как на экран вывести число если оно есть и если нет.
Код:
.MODEL small
.STACK 100h
.DATA
mas dw 5 dup(0)
.CODE
begin:
xor ax,ax
xor bx,bx
xor cx,cx
xor dx,dx
xor si,si
xor di,di

mov ax,62002
mov bx,10
mov cx,0
mov si,4

a:
div bx  
sub si, 1
mov mas[si],dx
add di,dx
mov dx,0
inc cx
cmp ax,0
jne a

mov bx,cx
xor ax,ax
mov ax,di
div bx
xor cx,cx
mov cx,5

b:
cmp ax, mas[si]
je c
inc si
LOOP b
????????

c:
?????????????
mov ax, 4c00h
int 21h
end start
 

Добавлено через 20 секунд:
Правильно так?
« Последнее редактирование: 10-04-2014 19:21 от Vanya1529 » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 10-04-2014 22:40 » 

Vanya1529, а ты словами каждую строчку опиши.

Цитата
mov mas[si],dx
Это как-то странно выглядит. Кругом регистры, а тут бац и массив. Вообще для работы с массивом сперва его адрес нужно загрузить в регистр базы (BX), делается это инструкцией LEA.

В общем-то никто тебе не мешал работать и со стеком, как в предыдущих вариантах. Но массив так массив.

Ещё обрати внимание, что массив у тебя содержит левые нули - в тех случаях, когда числа короче 5 цифр. И ты эти нули считаешь полноправными цифрами, хотя это по условию задачи не задано. В итоге у тебя и среднее получается с влиянием этих нулей, и поиск ты делаешь тоже с учётом нулей. Что-то мне кажется, что так не должно быть.

И для поиска в массиве слов в есть специальные инструкции SCASW REPE и т.п. Т.е. цикл для задачи поиска писать не обязательно - процессор умеет её делать аппаратно. Но это для 286 и выше.
« Последнее редактирование: 10-04-2014 22:42 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
darkelf
Молодой специалист

no
Offline Offline

« Ответ #8 : 11-04-2014 05:30 » 

Vanya1529, а ты словами каждую строчку опиши.

Цитата
mov mas[si],dx
Это как-то странно выглядит. Кругом регистры, а тут бац и массив. Вообще для работы с массивом сперва его адрес нужно загрузить в регистр базы (BX), делается это инструкцией LEA.
В данном случае это не обязательно.

И для поиска в массиве слов в есть специальные инструкции SCASW REPE и т.п. Т.е. цикл для задачи поиска писать не обязательно - процессор умеет её делать аппаратно. Но это для 286 и выше.
По-моему эта команда есть начиная с 86, да и в любом случае - на всех современных процессорах она уже есть.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 11-04-2014 06:20 » 

Цитата: darkelf
По-моему эта команда есть начиная с 86, да и в любом случае - на всех современных процессорах она уже есть.
Ну человек пишет код для TASM под DOS 16 бит. Тут дело не в том, что процессоры поддерживают, а в том, как сам ассемблер обрабатывает исходный код. Для 286 и других нужно указать соответствующую директиву. Так что по умолчанию код 8086.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #10 : 06-05-2014 16:22 » 

Код:
.MODEL small
.STACK 100h
.DATA
stroka1 db "Chisla ravnogo srednemu arifmeticheskomu ostalnih cifr net: $"
stroka2 db "Nashlos chislo ravnoe srednemu arifmeticheskomu ostalnih cifr: $"
mas dw 5 dup(0)
.CODE
begin:
xor dx,dx
xor di,di                                               
         
mov ah,0ah
int 21h
         
mov ax,dx
xor dx,dx                       
mov bx,10                                   
mov cx,0
mov si,4

a:
div bx   
mov mas[si],dx
dec si
add di,dx
mov dx,0
inc cx
cmp ax,0
jne a

mov bx,cx
xor ax,ax
mov ax,di
div bx
xor cx,cx
mov cx,5

b:
cmp ax,mas[si]
je c
inc si
LOOP b
Вот подскажите где ошибка, программа всегда выдает Stroka1 на экран, даже если в записи числа есть нужная цифра. я запутался

Добавлено через 1 минуту и 8 секунд:
Код:
.MODEL small
.STACK 100h
.DATA
stroka1 db "Chisla ravnogo srednemu arifmeticheskomu ostalnih cifr net: $"
stroka2 db "Nashlos chislo ravnoe srednemu arifmeticheskomu ostalnih cifr: $"
mas dw 5 dup(0)
.CODE
begin:
xor dx,dx
xor di,di                                               
         
mov ah,0ah
int 21h
         
mov ax,dx
xor dx,dx                       
mov bx,10                                   
mov cx,0
mov si,4

a:
div bx   
mov mas[si],dx
dec si
add di,dx
mov dx,0
inc cx
cmp ax,0
jne a

mov bx,cx
xor ax,ax
mov ax,di
div bx
xor cx,cx
mov cx,5

b:
cmp ax,mas[si]
je c
inc si
LOOP b

lea dx,stroka1
mov ah,09h
int 21h
jmp d

c:
lea dx,stroka2
mov ah,09h
int 21h

d:
mov ax, 4c00h
int 21h
end
 
Упс, не весь код кинул
« Последнее редактирование: 06-05-2014 16:23 от Vanya1529 » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 06-05-2014 17:44 » 

Vanya1529, а что ты хочешь? Ты выводишь строку и всё. А где превращение числа в строку из цифр? Где вывод этой строки или печать в цикле по одной цифре?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #12 : 06-05-2014 18:16 » 

Я имею ввиду то что даже если в записи числа есть цифра, равная среднему арифметическому всех остальных цифр то программа все равно выдает мне строку 1. А нужна строка 2.
Записан
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #13 : 06-05-2014 18:17 » 

Вот например число 44444 среднее арифм. 4. В числе есть такая цифра, а оно мне вот ... Да еще и тттттт какие-то
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 06-05-2014 18:51 » 

Vanya1529, так ты не инициализировал правильным образом сегментный регистр DS, и при вызове функции происходит ерунда.

Код: (ASM)
.model small

.data

s       db      "Test", 0Dh, 0Ah, "$"

.code

.8086

entry:

        ; Вот эта инициализация регистра DS обязательна для модели памяти small
        mov     ax, @data
        mov     ds, ax

        lea     dx, s
        mov     ah, 09h
        int     21h

        mov     ah, 4Ch
        int     21h

end entry
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 06-05-2014 19:02 » 

Другой вариант: вовсе не использовать разные сегменты.

Код: (ASM)
.model tiny

.code

        ; Задаёт смещение начала кода - нужно для DOS.
        org     100h

.8086

entry:

        ; Перепрыгиваем область данных
        jmp     main

s       db      "Test", 0Dh, 0Ah, "$"

        ; Область кода
main:

        lea     dx, s
        mov     ah, 09h
        int     21h

        ; Выход в DOS другим способом - для COM-программы.
        int     20h

end entry
Собирать нужно, вызывая tlink с ключом /t. Получится com-файл.

Модель памяти tiny (крошечная) - это когда данные, код и стек находятся в одном общем сегменте размером 64 Кб. Модель памяти small (маленькая) - это когда код и стек находятся в одном общем сегменте 64 Кб, а данные в другом отдельном сегменте тоже размером 64 Кб. Итого может использоваться до 128 Кб.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #16 : 09-05-2014 12:52 » 

Вот сделал, все работает. Но программа дает правильный результат только с 5-значными числами. Например ввести 44444, оно выдаст что число такое есть, но стоит ввести 234 например и все.. хотя (2+3+4):3 = 3
Вроде эта строчка(mas dw 5 dup(0)) где я  задаю массив не влияет  на это, так в чем проблема тогда?
Код:
.MODEL small
.STACK 100h
.DATA
stroka1 db 10,13,"Chisla ravnogo srednemu arifmeticheskomu ostalnih cifr net: $"
stroka2 db 10,13,"Nashlos chislo ravnoe srednemu arifmeticheskomu ostalnih cifr: $"
s db "Vvedite chislo: $"
mas dw 5 dup(0)
.CODE
include 'emu8086.inc'
begin:       ;
mov     ax, @data
mov     ds, ax 

xor dx,dx
xor di,di

lea dx, s
mov ah,09h
int 21h         
CALL   scan_num                                               
                                                               
mov ax,cx                                                   
xor dx,dx                                                     
mov bx,10                                   
mov cx,0                                                 
xor si,si

a:
div bx   
mov mas[si],dx
inc si
add di,dx
mov dx,0
cmp ax,0                               
jne a

mov bx,si
xor ax,ax
mov ax,di
div bx
mov cx,si


b:
cmp ax,mas[si]
je c
dec si
LOOP b

lea dx,stroka1
mov ah,09h
int 21h
jmp d

c:
lea dx,stroka2
mov ah,09h
int 21h

d:
mov ax, 4c00h
int 21h
DEFINE_SCAN_NUM
end begin
 

Добавлено через 1 минуту и 18 секунд:
И еще как мне вывести "это" число, если оно есть. У меня выводит просто сообщение, а нужно чтоб еще и его выводило на экран.
« Последнее редактирование: 09-05-2014 12:53 от Vanya1529 » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #17 : 09-05-2014 13:19 » 

Vanya1529, я не знаю, что ты там за макросы нагородил, и структура кода вообще отсутствует. Поэтому разбираться за тебя, почему эта каша не работает, как-то не весело.

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

А то так фактически предлагаешь, чтобы за тебя кто-то эту программу запустил (что невозможно из-за отсутствия используемых inc-файлов), прошёлся по ней отладчиком и нашёл баг. Это работа программиста, причём не какая-то там интеллектуальная, а черновая. С какой радости её за тебя делать?


Что касается вывода числа. Число на экране изображается символами-цифрами. Десятичными. Соответственно, тебе нужно число разложить на десятичные цифры, каждую цифру вывести на экран (или записать в строчку, которую потом вывести на экран). Причём делать это в правильном порядке следования цифр. Для этого опиши алгоритм. В твоём случае цифра всё равно одна, так что это будет алгоритм вывода цифры. Если задача расширится до многознаковых чисел, то вывод одной цифры будет вспомогательным алгоритмом для вывода длинных чисел. Вывод отдельного символа в телетайпном режиме обеспечивает int 10h, функция AH=0eh, ASCII-код символ нужно записать в регистр AL, в регистре BL может быть указан цвет символа.

Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #18 : 12-05-2014 17:22 » 

Цитата
.MODEL small
.STACK 100h
.DATA
stroka1   db 10,13,"Chisla ravnogo srednemu arifmeticheskomu ostalnih cifr net: $"               ;заносим в переменную строку
stroka2   db 10,13,"Nashlos chislo ravnoe srednemu arifmeticheskomu ostalnih cifr: $"             ;заносим в переменную строку
s db "Vvedite chislo: $"                ;заносим в переменную строку
mas dw 5 dup(0)                             ;создаем массив из 5-ти элементов, заполняем нулями
.CODE
include 'emu8086.inc'                          ;подключаем файлы библиотек
                          
begin:                                                 ;начало программы
mov     ax, @data
mov     ds, ax  

xor dx,dx                                         ;обнуляем регистры dx и di
xor di,di

lea dx, s                                               ;Выводим на экран строку, содержащуюся в переменной s
mov ah,09h
int 21h          
CALL   scan_num                                   ;с клавиатуры вводится число ( Х<65536 ) (заносится в регистр сх)          
                                                              
mov ax,cx                                                   ;переносим число из регистра сх в ах (для дальнейшего деления)
xor dx,dx                                                     ;обнуляем регистр dx
mov bx,10                                                    ;заносим в регистр bx число 10 ( в дальнейшем делением на 10 будем заносить цифры числа в массив)
mov cx,0                                                       ;обнуляем регистр сх
xor si,si                                                         ;обнуляем регистр si


                              БЛОК 1 (занос цифр числа в массив)

a:                                                    
div bx                                         ; Делим содержимое регистра ах на содержимое регистра bx (т.е на 10) и тем самым получаем 1 цифру числа в регистре dx
mov mas[si],dx                              ;затем эту первую цифру заносим в 1 элемент массива (т.е в нулевой). затем это будет 2,3 и т.д
inc si                                              ;инкрементрируем регистр si, что бы сл. цифру занести уже в сл. элемент массива
add di,dx                                       ;В регистре di мы накапливаем сумму всех этих цифр ( для дальнейшего деления их суммы на количества)
mov dx,0                                       ;обнуляем регистр dx
cmp ax,0                                         ;сравниваем регистр ах( в котором изначально находится число) с 0
jne a                       ;если он равен нулю то все цифры числа перенесены в массив и можно продолжать работу, иначе, переход снов ана метку а


                              БЛОК 2 (Нахождение среднего арифметического цифр числа)

                                    
xor ax,ax                                         ;обнуляем регистр ах
mov cx,si ;заносим содержимое регистра si(в si сейчас кол-во проходов цикла из блока 1, т.е количество цифр в числе) в регистр сх(нужно для цикла в блоке 3
mov ax,di                                          ;заносим содержимое регистра di (сумма отдельных цифр числа) в регистр ах
div bx                                                 ;делим содержимое регистра ах на содержимое регистра si ( результат в ах)


                      БЛОК 3 (поиск цифры в числе, равной среднему арифметическому остальных цифр)


b:
cmp ax,mas[si]                               ;сравниваем то самое среднее арифметическое со всеми цифрами по очереди
je c                                                 ;если равная цифра нашлась то переход на метку с
dec si                                                 ;уменьшение регистра si на единицу ( чтобы с начала цикла уже сравнивалось число с другим элементов массива)
LOOP b                                               ;переход на следующую команду по завершению цикла

lea dx,stroka1                                     ;вывод на экран содержимого переменной stroka1 ( т.е сообщения о том что таких цифр нет)
mov ah,09h
int 21h
jmp d                              ;безусловный переход на конец программы

c:
lea dx,stroka2                                   ;вывод на экран содержимого переменной stroka2 ( т.е сообщения о том что нашлась такая цифра)
mov ah,09h
int 21h

d:
mov ax, 4c00h
   int 21h
DEFINE_SCAN_NUM    
end begin                                              ;конец программы

« Последнее редактирование: 12-05-2014 17:29 от Vanya1529 » Записан
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #19 : 12-05-2014 17:30 » 

Ну вот. Все равно не понимаю из-за чего она только 5-ти значные берет. Было одно лишнее действие, я его убрал.
Записан
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #20 : 12-05-2014 20:56 » 

Число как вывести я уже разобрался - через PRINT_NUM_UNS. Но все равно почему программа правильно работает только с 5-ти значными числами??
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #21 : 12-05-2014 22:09 » 

Ну давай разбираться.

Я лично не знаю, что вот эти строки делают и как
Цитата: Vanya1529
include 'emu8086.inc'                          ;подключаем файлы библиотек

CALL   scan_num                                   ;с клавиатуры вводится число ( Х<65536 ) (заносится в регистр сх)    

DEFINE_SCAN_NUM    
У меня нет файла emu8086.inc, процедуры scan_num и макроса DEFINE_SCAN_NUM. Они нестандартные.

И не очень-то понятно, зачем они тебе. Потому что если б ты сам написал чтение цифр в массив, ты бы понял, как записывать число на экран. Это одинаковые задачи, просто в разном направлении работают: одна читает символы, вторая печатает.

Цитата: Vanya1529
;заносим в переменную строку
Это не совсем так. Это не операция присваивания переменной. Это метка на адрес куска памяти, где записана строка. Ближе всего будет понятие "константа" в языках высокого уровня. Но всё же строчки по адресу можно перезаписывать - нужно лишь помнить об их размерах.

Цитата: Vanya1529
xor dx,dx                                         ;обнуляем регистры dx и di
xor di,di
Прекрасно. Только непонятно, зачем? Ниже ты регистр DX благополучно перезаписываешь в инструкции LEA. Регистр DI вовсе не используется для ввода. Лишние бесполезные строчки - мусор в программе. Больше мусора, меньше понятно, что к чему.

Цитата: Vanya1529
mov ax,cx                                                   ;переносим число из регистра сх в ах (для дальнейшего деления)
xor dx,dx                                                     ;обнуляем регистр dx
mov bx,10                                                    ;заносим в регистр bx число 10 ( в дальнейшем делением на 10 будем заносить цифры числа в массив)
mov cx,0                                                       ;обнуляем регистр сх
xor si,si                                                         ;обнуляем регистр si


                              БЛОК 1 (занос цифр числа в массив)

a:                                                    
div bx                                         ; Делим содержимое регистра ах на содержимое регистра bx (т.е на 10) и тем самым получаем 1 цифру числа в регистре dx
mov mas[si],dx                              ;затем эту первую цифру заносим в 1 элемент массива (т.е в нулевой). затем это будет 2,3 и т.д
inc si                                              ;инкрементрируем регистр si, что бы сл. цифру занести уже в сл. элемент массива
add di,dx                                       ;В регистре di мы накапливаем сумму всех этих цифр ( для дальнейшего деления их суммы на количества)
mov dx,0                                       ;обнуляем регистр dx
cmp ax,0                                         ;сравниваем регистр ах( в котором изначально находится число) с 0
jne a
Итак, ты используешь алгоритм циклического деления на 10 и размещение остатков внутри массива (заодно подумай, так ли уж сильно это отличается от вывода числа на экран). Здесь полезно заранее планировать, какие регистры для чего используются.

Инструкции деления DIV/IDIV использует регистры AX,DX таким образом, что делимое 32 бита располагается в двух 16-битных регистрах DX (старшие разряды) и AX (младшие разряды). Это делимое делится (с учётом бита отрицательного числа в IDIV и без учёта в DIV) на аргумент инструкции. Результат оказывается тоже в регистрах: AX - целая часть результата, DX - остаток. AX - это аккумуляторный регистр, DX - регистр данных.

Эта инструкция ключевая, и по определению видно, что регистр AX должен получить входное значение, а затем никак не использоваться до конца алгоритма. Алгоритм заканчивается, когда AX обращается в ноль.

Для работы с массивами используются специальные индексные регистры: BX - регистр адреса базы, BP - регистр указателя относительно базы, SI, DI - регистры индекса данных. Достаточно лишь использовать один регистр указателя, который последовательно будет указывать на элементы массива.

Аргументом деления может быть либо другой регистр, либо значение в ячейке памяти, но, к сожалению, не константа. Поэтому воспользуемся каким-либо регистром. У нас из основных регистров остаётся свободным CX - регистр счётчика.

В итоге получаем такой код:
Код: (ASM)
        mov     ax, <1>           ; подготовительная операция: в аккумуляторный регистр AX записывается исходное число
        lea     bx, mas           ; подготовительная операция: в регистр базы BX записывается указатель на начало массива
        mov     cx, 10            ; подготовительная операция: в регистр счётчика записывается делитель
r1:                               ; начало цикла
        cwd                       ; обнуляется DX, который содержит старшие разряды делимого
        div     cx                ; деление, в результате в AX оказывается частное, а в DX остаток от деления
        mov     byte ptr [bx], dl ; запись остатка от деления в массив: записывается 1 байт,
                                  ; и поэтому используется младшая половина регистра DX - регистр DL размером 8 бит
        inc     bx                ; указатель сдвигается к следующему элементу массива
        cmp     ax, 0             ; частное сравнивается с нулём для определения конца алгоритма
        jnz     r1                ; условный переход: если частное не равно 0, переход на метку r1    
Здесь вместо <1> нужно использовать то место, где лежит исходное число. У тебя это регистр CX. То, что потом CX "испортится", не имеет значения.

Посмотрим на этот алгоритм. Во-первых, он "стрёмный" с точки зрения размера массива: если массив окажется короче количества цифр в исходном числе, произойдёт выход за пределы массива, и значения соседних переменных могут быть испорчены. Более безопасный вариант - делать цикл по длине массива. Во-вторых, массив заполняется цифрами задом наперёд. Т.е. число "12345" в массиве будет записано как "5 4 3 2 1".

Чтобы исправить оба этих "косяка", будет разумно использовать не цикл while ax>0, а цикл for cx с уменьшением регистра счётчика - инструкцию LOOP. Она уменьшает CX на единицу, и если CX ещё не 0, переходит на метку.

Но для этой схемы нужно переместить делитель в другой регистр. Из свободных у нас остались лишь разные счётчики и указатели: DI, SI, BP. Ну, допустим, тот же DI.

Получается:
Код: (ASM)
        ; подготовительные к циклу операции
        mov     ax, <1>           ; в аккумуляторный регистр AX записывается исходное число
        mov     cx, 5             ; в регистр счётчика CX записывает размер массива
        lea     bx, mas           ; в регистр базы BX записывается указатель на начало массива
        add     bx, cx            ; указатель сдвигается за край массива
        dec     bx                ; указатель сдвигается на последний элемент массива (на 1 меньше размер массива)
        mov     di, 10            ; в регистр индекса DI записывается делитель
r1:     ; тело цикла
        cwd                       ; обнуляется DX, который содержит старшие разряды делимого
        div     di                ; деление, в результате в AX оказывается частное, а в DX остаток от деления
        mov     byte ptr [bx], dl ; запись остатка от деления в массив: записывается 1 байт,
                                  ; и поэтому используется младшая половина регистра DX - регистр DL размером 8 бит
        dec     bx                ; указатель сдвигается к предыдущем элементу массива
        loop    r1                ; циклический переход по регистру счётчика: если CX>0, переход на метку r1
Этот цикл работает до начала массива, даже если последние цифры и регистр AX давно уже равны нулю. Но он работает лишь до начала массива, даже если не все цифры исходного числа разобраны.

Теперь к этому хозяйству нужно приделать подсчёт среднего арифметического: т.е. нужны регистр для накопления суммы и счётчик цифр (их ведь может быть меньше, чем размер массива). Выбор тут небогатый: остались два свободные регистра. Пусть счётчик будет в SI, а сумма в BP. Их нужно в начале обнулить, потом внутри цикла правильно обрабатывать. Кроме того, внутри цикла нужно сделать break на тот случай, если количество цифр в числе окажется меньше размера массива - и чтобы счётчик цифр дальше не увеличивался, и чтобы "лишние" итерации не крутились.
Код: (ASM)
        ; подготовительные к циклу операции
        mov     ax, <1>           ; в аккумуляторный регистр AX откуда-то вводится исходное число
        mov     cx, 5             ; в регистр счётчика CX записывает размер массива
        lea     bx, mas           ; в регистр базы BX записывается указатель на начало массива
        add     bx, cx            ; указатель сдвигается за край массива
        dec     bx                ; указатель сдвигается на последний элемент массива (на 1 меньше размер массива)
        mov     di, 10            ; в регистр индекса DI записывается делитель
        mov     si, 0             ; в регистр индекса SI записывается начальное значение счётчика цифр
        mov     bp, 0             ; в регистр указателя BP записывается начальное значение сумм цифр
r1:     ; тело цикла
        cwd                       ; обнуляется DX, который содержит старшие разряды делимого
        div     di                ; деление, в результате в AX оказывается частное, а в DX остаток от деления
        mov     byte ptr [bx], dl ; запись остатка от деления - цифры числа - в массив: записывается 1 байт,
                                  ; и поэтому используется младшая половина регистра DX - регистр DL размером 8 бит
        add     bp, dx            ; добавление цифры числа к сумме цифр (тут DX вместо DL потому,
                                  ; что инструкция ADD в данном случае требует аргумент 16 бит)
        inc     si                ; увеличение счётчика цифр
        dec     bx                ; указатель сдвигается к предыдущем элементу массива (важно сделать до выхода из цикла)
        cmp     ax, 0             ; сравнение частного с 0
        loopne  r1                ; циклический переход по регистру счётчика: если CX>0 и результат предыдущего сравнения не равен 0, переход на метку r1 - начало цикла
a:      ; вычисление среднего
        mov     ax, bp            ; в аккумуляторный регистр записывается делимое - сумма цифр
        cwd                       ; обнуляется DX, который содержит старшие разряды делимого
        div     si                ; деление, в результате в AX оказывается частное
        mov     <2>, ax           ; из аккумуляторного регистра AX куда-то выводится результат - округлённое до целого среднее всех цифр

Дальше требуется найти в массиве такую цифру, которая получилась как значение среднего. Исходными данными для этой задачи являются:
- аккумуляторный регистр AX, в котором содержится среднее;
- массив, в котором содержатся цифры, причём они "прижаты" к правому краю массива - массив хранит число в десятичной записи с левыми незначащими нулями, например, 00123;
- регистр индекса SI, в котором содержится количество цифр в массиве.
- регистр базы BX, который указывает на элемент массива левее последней цифры числа (или за левый край массива, если весь массив заполнен)
Код: (ASM)
        ; поиск среднего среди цифр
        mov     cx, si            ; регистр счётчика CX становится равен количеству цифр.
        add     bx, si            ; регистр базы BX, остановившийся левее последней цифры числа, сдвигается опять на последний элемент массива
r2:     ; тело цикла
        cmp     byte ptr [bx], al ; сравниваем цифру в массиве с цифрой среднего в младшей половине регистра AX - регистре AL.
        je      f                 ; условный переход: если результат сравнения - равенство, выход за пределы цикла - на метку f
        dec     bx                ; указатель сдвигается к предыдущем элементу массива
        loop    r2                ; циклический переход по регистру счётчика: если CX>0, переход на метку r1
        ; если не найдена цифра в числе, равная среднему
        nop                       ; холостой ход процессора, тут нужны другие некие действия
        jmp     e                 ; перепрыгивание через другую ветвь условия - безусловный переход на метку e.
f:      ; если найдена цифра в числе, равная среднему
        nop                       ; холостой ход процессора, тут нужны другие некие действия
e:      ; завершение программы
        nop                       ; холостой ход процессора, тут нужны другие некие действия      

Ну вот как-то так, и это работает правильно c количеством цифр от 1 до 5. Сверяй со своим.

Примечание: у меня описана работа с массивом по байтам (тип массива db dup(5)), а не по словам, как у тебя (тип массива dw dup(5)). Чтобы мой код работал с dw, нужно изменить арифметику указателей: все операции с bx умножать на 2, и вместо byte ptr использовать word ptr.
« Последнее редактирование: 12-05-2014 22:14 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #22 : 12-05-2014 22:30 » 

Наконец, вариант, в котором ограничения на размер массива не играют роли - это работа со стеком. Массив вообще не используется. В этом случае количество цифр не ограничивается даже 5, а теоретически может быть равно свободному месту в стеке - это многие тысячи цифр, но в этом случае нужно смотреть за регистром, накапливающим сумму, чтобы он не переполнился.

Код: (ASM)
        ; подготовительные к циклу операции
        mov     ax, <1>           ; в аккумуляторный регистр AX откуда-то вводится исходное число
        mov     di, 10            ; в регистр индекса DI записывается делитель
        mov     cx, 0             ; в регистр счётчика CX записывается начальное значение счётчика цифр
        mov     si, 0             ; в регистр индекса SI записывается начальное значение сумм цифр
r1:     ; тело цикла
        cwd                       ; расширяем значение AX до пары DX,AX, помещая в DX старшие разряды делимого
        div     di                ; деление, в результате в AX оказывается частное, а в DX остаток от деления
        push    dx                ; полученная как остаток деления цифра числа сохраняется в стеке
        add     si, dx            ; добавление цифры числа к сумме цифр
        inc     cx                ; увеличение счётчика цифр
        cmp     ax, 0             ; сравнение частного с 0
        jnz     r1                ; условный переход: если частное ещё не равно нулю, переход на метку r1 - начало цикла
a:      ; вычисление среднего
        mov     ax, si            ; в аккумуляторный регистр записывается делимое - сумма цифр
        cwd                       ; расширяем значение AX до пары DX,AX, помещая в DX старшие разряды делимого
        div     cx                ; деление на количество цифр, в результате в AX оказывается частное - среднее цифр
        ; поиск среднего среди цифр
r2:     ; тело цикла
        pop     dx                ; из стека извлекается очередная цифра
        cmp     dx, ax            ; сравниваем цифру из стека с цифрой среднего.
        je      f                 ; условный переход: если результат сравнения - равенство, выход за пределы цикла - на метку f
        loop    r2                ; циклический переход по регистру счётчика: если CX>0, переход на метку r2
        ; если не найдена цифра в числе, равная среднему
        nop                       ; холостой ход процессора, тут нужны другие некие действия
        jmp     e                 ; перепрыгивание через другую ветвь условия - безусловный переход на метку e.
f:      ; если найдена цифра в числе, равная среднему
        nop                       ; холостой ход процессора, тут нужны другие некие действия
e:      ; завершение программы
        nop                       ; холостой ход процессора, тут нужны другие некие действия      
« Последнее редактирование: 13-05-2014 15:46 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #23 : 13-05-2014 20:24 » 

Спасибо большое за помощь. Улыбаюсь Вот я составил 2 финальные программы: с помощью массива и с помощью стека:
Способ 1 (с помощью массива):
Цитата
.MODEL small
.STACK 100h
.DATA
stroka1   db 10,13,"Chisla ravnogo srednemu arifmeticheskomu ostalnih cifr net: $"                   ;заносим в переменную константу
stroka2   db 10,13,"Nashlos chislo ravnoe srednemu arifmeticheskomu ostalnih cifr: $"               ;заносим в переменную константу
s db "Vvedite chislo: $"                         ;заносим в переменную константу
mas db 5 dup(0)                                      ;создаем массив из 5-ти элементов, заполняем нулями
.CODE
include 'emu8086.inc'                                 ;подключаем файлы библиотек
begin:       ;                                                   ;начало программы
mov     ax, @data
mov     ds, ax  

lea dx, s                                                         ;Выводим на экран константу, содержащуюся в переменной s
mov ah,09h
int 21h          
CALL   scan_num                                               ;с клавиатуры вводится число ( Х<65536 ) (заносится в регистр сх)                                          
 
       ; подготовительные к циклу операции
    mov     ax, cx                                                   ; в аккумуляторный регистр AX из регистра СХ вводится исходное число
    mov     cx, 5                                                     ; в регистр счётчика CX записывает размер массива
    lea     bx, mas                                                  ; в регистр базы BX записывается указатель на начало массива
    add     bx, cx                                                    ; указатель сдвигается за край массива
    dec     bx                                                           ; указатель сдвигается на последний элемент массива (на 1 меньше размер массива)
    mov     di, 10                                                     ; в регистр индекса DI записывается делитель
    mov     si, 0                                                       ; в регистр индекса SI записывается начальное значение счётчика цифр
    mov     bp, 0                                                     ; в регистр указателя BP записывается начальное значение сумм цифр

r1:     ; тело цикла
    xor dx, dx                                                          ; обнуляется DX, который содержит старшие разряды делимого
    div     di                                                             ; деление, в результате в AX оказывается частное, а в DX остаток от деления
    mov     byte ptr [bx], dl                                     ; запись остатка от деления - цифры числа - в массив: записывается 1 байт,
                                                                             ; и поэтому используется младшая половина регистра DX - регистр DL размером 8 бит
    add     bp, dx                                                   ; добавление цифры числа к сумме цифр (тут DX вместо DL потому,
                                                                             ; что инструкция ADD в данном случае требует аргумент 16 бит)
    inc     si                                                             ; увеличение счётчика цифр
    dec     bx                                                           ; указатель сдвигается к предыдущем элементу массива (важно сделать до выхода из цикла)
    cmp     ax, 0                                                       ; сравнение частного с 0
    loopne  r1                                                          ; циклический переход по регистру счётчика: если CX>0 и результат предыдущего сравнения не равен 0, переход на метку r1 - начало цикла

a:     ; вычисление среднего
    mov     ax, bp                                                   ; в аккумуляторный регистр записывается делимое - сумма цифр
    xor dx, dx                                                         ; обнуляется DX, который содержит старшие разряды делимого
    div     si                                                             ; деление, в результате в AX оказывается частное
    cmp dx, 0                                                         ;сравнение регистра dx (в котором находится остаток от деления) с нулем. Если регистр dx не равен нулю, значит число получилось дробное и его не будет среди цифр массива. Значит переход на метку с
    jne  c
          
     ; поиск среднего среди цифр
    mov     cx, si                                                       ; регистр счётчика CX становится равен количеству цифр.
    add     bx, si                                                       ; регистр базы BX, остановившийся левее последней цифры числа, сдвигается опять на последний элемент массива

r2:     ; тело цикла
    cmp     byte ptr [bx], al                                       ; сравниваем цифру в массиве с цифрой среднего в младшей половине регистра AX - регистре AL.
    je      f                                                                 ; условный переход: если результат сравнения - равенство, выход за пределы цикла - на метку f
    dec     bx                                                             ; указатель сдвигается к предыдущем элементу массива
    loop    r2                                                             ; циклический переход по регистру счётчика: если CX>0, переход на метку r2
   ; если не найдена цифра в числе, равная среднему
c:
    lea dx,stroka1                                                     ;вывод на экран содержимого переменной stroka1 ( т.е сообщения о том что таких цифр нет)
    mov ah,09h
    int 21h                      
    jmp e                                                                   ;безусловный переход на конец программы
                  
f:      ;если найдена цифра в числе, равная среднему
    mov bp, ax
    lea dx, stroka2                                                    ;вывод на экран содержимого переменной stroka2 ( т.е сообщения о том что нашлась такая цифра)
    mov ah, 09h
    int 21h  
    mov ax, bp
    CALL PRINT_NUM_UNS
                              
e:     ; завершение программы
    mov ax, 4c00h
   int 21h
DEFINE_SCAN_NUM
DEFINE_PRINT_NUM_UNS    
end begin
Оставил я уже работу с массивом по байтам, только изменил mas dw 5 dup(0)  на  mas db 5 dup(0) и заменил команды "cwd" на xor dx, dx. Иначе выдавало ошибку деления - переполнение если вводилось число больше 32768. Теперь число больше 65536 просто не вводится, как и надо по условию

Способ 2 (с помощью стека):
Цитата
.MODEL small
.STACK 100h
.DATA
stroka1   db 10,13,"Chisla ravnogo srednemu arifmeticheskomu ostalnih cifr net: $"                   ;заносим в переменную константу
stroka2   db 10,13,"Nashlos chislo ravnoe srednemu arifmeticheskomu ostalnih cifr: $"              ;заносим в переменную константу
s db "Vvedite chislo: $"                         ;заносим в переменную константу
mas db 5 dup(0)                                       ;создаем массив из 5-ти элементов, заполняем нулями
.CODE
include 'emu8086.inc'                                 ;подключаем файлы библиотек
begin:       ;                                                    ;начало программы
mov     ax, @data
mov     ds, ax  

lea dx, s                                                          ;Выводим на экран константу, содержащуюся в переменной s
mov ah,09h
int 21h          
CALL   scan_num                                               ;с клавиатуры вводится число ( Х<65536 ) (заносится в регистр сх)                                          
  
    ; подготовительные к циклу операции
    mov     ax, cx                                                  ; в аккумуляторный регистр AX откуда-то вводится исходное число
    mov     di, 10                                                 ; в регистр индекса DI записывается делитель
    mov     cx, 0                                                   ; в регистр счётчика CX записывается начальное значение счётчика цифр
    mov     si, 0                                                    ; в регистр индекса SI записывается начальное значение сумм цифр
r1:     ; тело цикла
    xor dx, dx                                                       ; расширяем значение AX до пары DX,AX, помещая в DX старшие разряды делимого
    div     di                                                           ; деление, в результате в AX оказывается частное, а в DX остаток от деления
    push    dx                                                       ; полученная как остаток деления цифра числа сохраняется в стеке
    add     si, dx                                                   ; добавление цифры числа к сумме цифр
    inc     cx                                                           ; увеличение счётчика цифр
    cmp     ax, 0                                                   ; сравнение частного с 0
    jnz     r1                                                         ; условный переход: если частное ещё не равно нулю, переход на метку r1 - начало цикла
a:      ; вычисление среднего
    mov     ax, si                                                    ; в аккумуляторный регистр записывается делимое - сумма цифр
    xor dx, dx                                                       ; расширяем значение AX до пары DX,AX, помещая в DX старшие разряды делимого
    div     cx                                                         ; деление на количество цифр, в результате в AX оказывается частное - среднее цифр
    cmp dx, 0                                                        ;сравнение регистра dx (в котором находится остаток от деления) с нулем. Если регистр dx не равен нулю, значит число получилось дробное и его не будет среди цифр массива. Значит переход на метку с
    jne  c
       ; поиск среднего среди цифр
r2:     ; тело цикла
    pop     dx                                                       ; из стека извлекается очередная цифра
    cmp     dx, ax                                                   ; сравниваем цифру из стека с цифрой среднего.
    je      f                                                             ; условный переход: если результат сравнения - равенство, выход за пределы цикла - на метку f
    loop    r2                                                        ; циклический переход по регистру счётчика: если CX>0, переход на метку r2
   ; если не найдена цифра в числе, равная среднему
c:
     lea dx,stroka1                                                     ;вывод на экран содержимого переменной stroka1 ( т.е сообщения о том что таких цифр нет)
    mov ah,09h
    int 21h                      
    jmp e                                                                   ;безусловный переход на конец программы
                  
f:     ;если найдена цифра в числе, равная среднему
    mov bp, ax
    lea dx, stroka2                                                    ;вывод на экран содержимого переменной stroka2 ( т.е сообщения о том что нашлась такая цифра)
    mov ah, 09h
    int 21h  
    mov ax, bp
    CALL PRINT_NUM_UNS
                              
e:     ; завершение программы
    mov ax, 4c00h
   int 21h
DEFINE_SCAN_NUM
DEFINE_PRINT_NUM_UNS    
end begin
Тоже самое, пришлось поменять команды "cwd" на xor dx, dx. Иначе выдавало ту же ошибку деления - переполнение. Также в обоих программах я добавил в метку "а" строки кода:
cmp dx, 0
jne  c
чтобы избежать принятия программой дробного среднего как целого.

Как я понял файл с библиотеками находится у меня в папке с программой по пути emu8086/inc. Вот я прикрепил архив, в нем эта самая папка и русский хелп, в котором я и на нашел эти макросы по пути уроки/Библиотека общих функций - emu8086.inc

* архив.rar (259.98 Кб - загружено 777 раз.)
« Последнее редактирование: 13-05-2014 21:03 от Vanya1529 » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 13-05-2014 21:40 » 

Цитата: Vanya1529
и заменил команды "cwd" на xor dx, dx. Иначе выдавало ошибку деления - переполнение если вводилось число больше 32768.
Ну инструкция CWD расширяет число с учётом знака. Т.е. если в AX старший бит 1 (знак минус), то DX заполняется единицами, а если 0 (знак плюс), то нулями.

Цитата: Vanya1529
Вот я составил 2 финальные программы
Увы, ты не "составил", а "передрал" с косметическими изменениями. Это грустно. Лучше б разобрался и сам нашёл, где у тебя были ошибки.

Например, стек ты вставил, а всё остальное не поменял: и массив в программе остался ненужный, и размер стека какой-то куцый - 256 байт.

Халтура.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Vanya1529
Интересующийся

by
Offline Offline

« Ответ #25 : 13-05-2014 21:57 » new

Ошибки я свои видел, и основная заключалась именно в этом
Цитата
Кроме того, внутри цикла нужно сделать break на тот случай, если количество цифр в числе окажется меньше размера массива - и чтобы счётчик цифр дальше не увеличивался, и чтобы "лишние" итерации не крутились.
А на счет стека. Да забыл как-то. Делал на этой же программе и забыл убрать
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines