Доброго времени суток. Вопрос к знатокам ассемблера. Моя функция вызывает мое же программное прерывание 65h. Переход осуществляется успешно. В обработчик прерывания через стек я передаю 2 слова(по 16 бит каждое). Я беру эти слова, подняв указатель стека вверх на 6 байт (6 байт - вроде FLAGS+CS:IP). mov bp, sp ;сохранить указатель на стек add sp, 6 pop di pop cx так вот при {mov bp,sp, add sp,6} почему-то содержимое стека вокруг SS (в файле выделено) меняется, в результате, в конце фунция iret передает управление на непонятное место. Если поможете разобраться, буду очень благодарен, поскольку проблема для меня интересная=) Обновление, опустим пока прерывания. В зипе два скриншота программы в турбодебаггере. 1ый - программа до прибавления к SP, 6. Второй - после. Внизу видно, что память меняется (выделено красным) Спасибо
круто а к есп, если я правильно понял твою идею, надо прибавлять не 6, а 8, т.к. в стеке кроме адреса возврата должен лежать еще и регистр флагов (этим iret и отличается от ret). Дос я не помню, поэтому посмотри сам, где передается в стек (и передается ли вообще) регистр с флагами
Перепутал, слово машинное в 16 бит, а не байт))). 6 байт - вроде FLAGS(2) + CS:IP(2+2) Проблема не в этом, а том что почему-то стек меняется, если писать в BP или SP. Может я чего-то не догоняю, но мне непонятно почему так происходит)))
MSoft Насколько я помню, все же 6, т.к. в 16битном режиме нет выравнивания на четыре. 4DA А ты отнимаешь 6 от sp перед iret? При выполнении iret значения flags/cs/ip берутся с вершины стека, т.е. оттуда, куда указывает sp. Если прибавить к нему шесть и не вернуть на место, то естественно будет проблема. Я думаю, что лучше либо передавать параметры в регистрах, либо выполнять обработчик прерывания в отдельном стеке, либо сохранять flags/cs/ip где-то в памяти и потом восстанавливать их в стеке.
я не про выравнивание говорил, а про лишний параметр в стеке (или его отсутствие). Вообще, я б советовал автору не извращаться так и делать прямое обращение к параметрам через mov cx,[bp+xxx], чтобы не было путаницы
Нет, я как раз из BP записывал в SP перед выходом. Проблема в том, что у меня стек портится непонятно почему
Ух-ты не знал. Это всегда надо делать? Проверил - не помогает. Просто тупо в обычной функции добавляю к SP 6. Байт 10 сразу перезаписываются в стеке. Может я что-то не правильно понимаю в работе МП?
4DA я не совсем четко выразился, я имел ввиду смену ss и sp надо производить при отключенных прерываниях, а то может получится так, что некоторое внешнее прерывание (напр. таймер) сработает после смены ss, но до смены sp, и ss:sp будет указывать в небо. Я не внимательно посмотрел на твой код, у тебя действительно ошибка не в этом. Ты изменяешь только sp. А вообще, лучше в прерывание аргументы передавать через регистры, а не через стек. Ты наверное что-то не так делаешь. После add sp,6 изменится только sp.
meduza При изменении ss происходит автоматическая блокировка аппаратных прерываний на время выполнения следующей инструкции. Может когда-то давно это было не так, но уже начиная с 486 (как минимум) cli/sti не обязательны. В защищенном режиме чуть сложнее -- там переключение стека надо производить с помощью lss, т.к. страничное прерывание не относится к аппаратному и может "проскочить" за счет предвыборки кода. Правда, делать так надо только в нулевом кольце т.к. переключение стека при смене уровня привилегий происходит актоматиечски (берется из TSS). 4DA Покажи хотя бы код обработчика прерываний. М.б. изменения в памяти -- результат работы отладчика, а ошибка есть в коде?
Mika0x65, почему страничное прерывание может проскочить за счет предвыборки? Где про это почитать? Что именно не так? Вот именно только SP должен измениться, а у меня еще меняется стек=).
4DA Я читал об этом у Григорьева и в Intel Manual. Точно страницу не помню сейчас. Суть в том, что процессор выбирает код "забегая вперед", т.е. не только по текущему значению EIP. Значит, может создаться такая ситуация: 1. Инструкция 'mov ss, ax', например, находится в конце страницы. 2. Следующая страница отсутствует в памяти. 3. Мы находимся в нулевом кольце. Процессор выполняет 'mov ss, ax' попутно выбирая из памяти следующую команду (подробнее об этом лучше спросить у leo ). Происходит #PF, несмотря на то, что прерывания запрещены. Т.к. смены привилегий не происходит, то обработчик прерываний выполняется с некорректным стеком -- ss изменен, esp -- нет. Это, конечно, маловероятно и редко бывает, и если сегменты плоские, то ничего страшного, но лучше уберечься . P.S. А код ты так и не привел.
В древние времена реального режима и DOSа, к которым и относится вопрос топика, стек был только один и использовался и самой программой, и операционной системой, и обработчиками прерываний от устройств, и отладчиками. Соответственно всё, что в стеке находится ниже sp (по меньшим адресам), может быть в любой момент перезаписано без всякого участия со стороны программы просто из-за того, что не вовремя пришло аппаратное прерывание. Здесь они, впрочем, ни при чём, поскольку стек перезаписывает отладчик, под которым весь этот код проверяется в пошаговом режиме.
Сорри. К первому топику приложил зип, там 2 скриншота Turbo Debugger'a. Красным выделил что меняется. Забьем пока на прерывания. Там просто обычный код, который прибавляет к SP 6. Что там может быть не так?
diamond, вполне возможно, что отладчик перезаписывает стек по нижним адресам, но почему перезаписаывается и что выше SP?? Кстати, почему отладчик перезаписывает стек?
То, что выше sp, не перезаписывается (на скриншотах видно, что начиная с ss:0x206 память не изменяется, а sp=0x206 на втором). Но после команды "add sp,6" адрес возврата оказывается ниже sp и тут же затирается. Перезапись происходит по следующей причине. Отобрать управление у программы можно только по какому-нибудь прерыванию. В пошаговом режиме (неэмулирующий) отладчик отбирает управление себе после каждой инструкции программы. Таким образом, происходит прерывание. В реальном режиме процессор при прерывании кладёт в стек (ss:sp, который один на всех) cs:ip:flags, которые затирают то, что находилось в стеке раньше, и передаёт управление обработчику прерывания. Обработчик может тут же установить собственный стек, но дело уже сделано - 6 байт ниже sp затёрты.
Ага, супер, теперь все понятно, но почему в перезаписанном стеке не фигурируют 000A (флаги) и 18F2 (CS) и 0008(адрес следующей инструкции)? Они же сохраняются при int 3, которое отладчик, вставляет вместе каждой команды? Как тогода обработчик прерывания сможет вернуть управление? Или по-другому все устроено?
Хороший вопрос. Проверил поведение TD на тестовой программе. Старые версии действительно помещают в стек ниже sp значения флагов и cs:ip. В версии поновее Borland, видимо, многое изменила, и теперь TD выходит в защищённый режим (использует DPMI для работы), а там всё по-другому и есть разделённые стеки, так что cs:ip:flags помещаются в стек не самой программы, а гипервизора и можно организовать отладчик так, что он вообще не будет трогать стек отлаживаемой программы (по такому принципу организован, например, DG aka DeGlucker). Почему TD стек всё-таки затирает... наиболее вероятный вариант объяснения (не гарантирую, что всё происходит именно так, но похоже на то) следующий: старые версии TD использовали стек программы не только из-за особенностей процессора, но ещё и для каких-то своих нужд, затирая больше 6 байт; когда модифицировали старую версию, решили как можно меньше трогать старый работающий код, так что гипервизор, обнаружив отладочное прерывание, просто передаёт управление основному коду отладчика, который по-прежнему исполняется в той же среде, что и программа, и по-прежнему частично использует её стек - отсюда кое-что всё-таки затирается, но уже на 6 байт меньше, потому что cs:ip:flags лежат в стеке гипервизора, который не видно.