ну, похоже, задача предназначена для использования стека, и ваш комментарий:
не понимает предмет
кажется точным.Но затем вы просите людей написать короткую главу книги в простой ответ SO ... хм, обычно я не люблю этого делать, потому что полная книга - это полная книга, а в коротком ответе нужно будет опустить некоторые детали, но давайтепопробуйте, если что-то вырубленное может удержаться как ответ:
Память стека в реальном режиме 16b является обычной компьютерной памятью, но пара регистров ss:sp
указывает на "вершину стека", и есть несколько неявных инструкцийиспользуя эти два регистра для доступа к памяти, например, pop dx
«читает значение слова по адресу памяти ss:sp
в регистр dx
, затем добавляет 2 к sp
(чтобы оно указывало на следующее слово в памяти)» (словов смысле «16 бит информации», а не «текстовое» слово).
Обратите внимание на описания инструкций push / pop (см. руководство по эксплуатации, официальные документы от Intel или, например, основы *)1017 *), push
вычитает из sp
, т. Е. Стек «увеличивается» от старших адресов памяти к низким адресам памяти, а когда вы pop
,sp
возвращается к старшим адресам памяти.«Вершина стека» - это последний вставленный в него элемент, который находится по текущему адресу ss:sp
.Предыдущий элемент, помещенный в стек, находится в ss:sp+2
(в режиме 16b) и т. Д., Поэтому, если вы хотите использовать bp
для адресации, и вы копируете значение sp
в bp
после последнего push
сохраняя элементы в стеке, ваши элементы доступны с помощью адресации [bp+0], [bp+2], [bp+4], ...
(примечание: bp
по умолчанию ассоциируется с сегментом ss
, поэтому mov ax,[bp]
неявно mov ax,ss:[bp]
(загружает значение из сегмента стека),в то время как mov ax,[bx]
по умолчанию неявно mov ax,ds:[bx]
(загружается из сегмента данных) .. если вы не указали явное переопределение сегмента в исходном коде).
Обратите также внимание на инструкции call
и ret
они также неявно используют стек, так что если вы начнете использовать подпрограммы с помощью инструкций call
/ ret
, структура данных стека будет также содержать return-адрес в подпрограмме (в верхней части стека при входеподпрограмма).
В своем коде в emu8086 вы резервируете область памяти для стека, определяя сегмент стека в начале вашего кода, резервируя256 байтов (128 слов), что является минимальным стеком для такой задачи, требующей большого количества стеков, поскольку ваш код будет потреблять стек со скоростью «одно слово на один входной символ», вы можете ввести не более 128 символов ... это может показаться разумнымДо тех пор, пока вы не узнаете, что фактический пользовательский стек используется также прерываниями DOS, что имеет два основных последствия:
- некоторые из этих 128 слов используются прерываниями (не уверен, сколько, скажем, 30 слов), и если вы введете 100 символов, прерывания будут уже ниже вашей зарезервированной памяти, начиная перезаписывать части памяти, которые вы не зарезервировали для нее (проблема «переполнения стека»)
- память ниже current
ss:sp
периодически перезаписывается обработчиками прерываний DOS
Ваша задача звучит так, как будто вы можете:
- читать ввод пользователя в цикле до z/ Z вводится, помещая каждый элемент в стек (и считая = у вас теперь есть что-то подобное, как только вы установите
pop
только правильное количество раз), или указатель конца напустой стек) - просмотреть все введенные значения и вывести только маленькие символы
- вывести новую строку
- просмотреть все введенные значения и вывести только заглавные буквы
- output newline
- пройти через все введенные значения и вывести только цифры
- output newline
- exit
Теперь у вас может возникнуть желание сделать pop
в самом первом «просмотре всех введенных значений», но это означает, что вы бы переместили ss:sp
вверх к исходному «пустому стеку» ... и если бы вызатем вычтите 2 * предметов из sp
, вы можете подумать, что теперь вы можете pop
снова использовать те же символы из памяти, изменяя модификацией sp
.Но если какое-то прерывание все же произошло, исходные символы ниже ss:sp
в этот момент уничтожаются, поэтому НЕ pop
для первых двух циклов.
используйте скорее bp
для доступа к данным, то есть mov bp,sp
перед каждым выходным циклом, а затем зацикливание элементов, много раз делая mov dl,[bp]
add bp,2
, но оставляя ss:sp
неизменным (и где-то хранится количество копий элементов, так что вы получите его для второго и третьего цикла ..)или скопируйте "empty" sp
где-нибудь, например, mov di,sp
в начале, чтобы сделать cmp bp,di
для проверки выходных циклов, если вы прочитали все элементы.
Если вы в сборке еще меньшепредмет программирования, и вы чувствуете себя потерянным из-за большей части моего текста, сначала прочитайте какую-нибудь книгу или учебное пособие ... основы из регистров / памяти / и т. д. часто можно выбрать из этого резюме http://www.cs.virginia.edu/~evans/cs216/guides/x86.html, которое предназначено для 32-битного режима, ноЯ не знаю о таком коротком введении для 16b (я думаю, что ваши лекторы дали вам некоторые рекомендации, что изучать), или используйте google ... имейте в виду, что режим 16b более сложный и сложный, чем режим 32b, bпотому что вам также нужно узнать о сегментах памяти, и адресация памяти более ограничена (mov al,[cx]
не существует в режиме 16b, в то время как mov al,[ecx]
допустимо в режиме 32b).
Если у вас есть проблемытолько с какой-то определенной частью, спросите в комментариях.
Затем взгляните в своем коде, научитесь использовать отладчик (это абсолютно необходимо, использование веб-стека overoverflow, поскольку сервис отладки менее эффективен и рассматривается многими (включая меня)как грубое и плохое поведение ... если вы можете показать, что вы отладили код, и вы можете хорошо описать удивительное поведение (что происходит и чего вы хотели / ожидаете вместо этого), будет гораздо больше людей, желающих написать вамответьте, почему это происходит и где ваш мыслительный процесс неправильный), и попробуйте использовать приведенную выше информацию, чтобы переписать ее.