Assembler для начинающих






    Программа DEBUG (отладчик) дает средство обнаружения ошибок при
    работе с программой, транслированной в машинный язык.  Программа
    DEBUG обеспечивает возможность пошагово выполнять программу и
    следить за тем, что при этом происходит.  Программа DEBUG - это еще
    одно программное средство, поставляемое как часть DOS.  Вы
    загружаете ее так же, как и любую другую программу, и работаете в
    диалоге, используя клавиатуру и экран.  Когда программа DEBUG
    ожидает каких-либо действий со стороны пользователя, то свой запрос
    она обозначает символом "-".
 
      Вместо перечисления всех команд, которые входят в программу
    DEBUG, используем данный отладчик для проверки работы только что
    составленной программы, приведенной на Фиг. 5.13 и П5.14. На
    Фиг. 5.17 приведен соответствующий листинг.
      В данном примере сначала вызывается программа DEBUG и
    указывается та программа, которую предполагается отлаживать - в
    нашем случае программа FIG5=13.EXE. После того, как программа DEBUG
    загружена, она производит загрузку отлаживаемой программы.
    Управление теперь принадлежит отладчику, и он с помощью символа "-"
    показывает, что ожидает ввода. До тех пор, пока вы не введете для
    него указаний, с программой ничего происходить не будет.
 
      Команда R выводит содержимое всех регистров в момент,
    соответствующий загрузке программы FIG5=13 и передаче ей управления.
    Содержимое регистров не требует пояснений, за исключением, быть
    может, значений флагов.  Флаг NV указывает на отсутствие
    переполнения, флаг UP - флаг направления и т.д.  При выводе
    содержимого регистров в последней строке приводится следующая
    выполняемая команда.  В ячейке 04C5:0000 записана команда PUSH DS.
                  B>A:DEBUG  FIG5_13.EXE
                  -R
 
                  AX=0000  BX=0000  CX=0120  DX=0000  SP=FFF0  BP=0000  SI=0000  DI=0000
                  DS=2C26  ES=2C26  SS=2C26  CS=2C26  IP=0000   NV UP DI PL NZ NA PO NC
                  2C26:0000  1E             PUSH      DS
 
                  -U
 
                  2C26:0000  1E             PUSH      DS
                  2C26:0001  B80000   MOV AX,0000
                  2C26:0004  50             PUSH      AX
                  2C26:0005  FC             CLD
                  2C26:0006  8CC8     MOV AX,CS
                  2C26:0008  8ED8     MOV DS,AX
                  2C26:000A  8D361D00       LEA SI,[001D]
                  2C26:000E  AC             LODSB
                  2C26:000F  A24000   MOV [0040],AL
                  2C26:0012  E82C00   CALL      0041
                  2C26:0015  803E40000A     CMP [0040],0A
                  2C26:001A  75F2     JNZ 000E
                  2C26:001C  CB             RET Far
                  2C26:001D  9D             POPF
                  2C26:001E  E2A0     LOOP      FFC0
                  2C26:0020  20AFE0AE       AND [BX+AEE0],CH
                  2C26:0024  A3E0A0   MOV [A0E0],AX
 
                  -D2C26:0
 
                  2C0E:0000   CD 20 00 A0 00 9A EE FE 1D F0 ED 04 04 1C 3C 01   . ............<.
                  2C0E:0010   22 1B EB 04 22 1B 04 1C 01 01 01 00 02 06 FF FF   "..."...........
                  2C0E:0020   FF FF FF FF FF FF FF FF FF FF FF FF 08 2C D0 FF   .............,..
                  2C0E:0030   04 1C 14 00 18 00 0E 2C FF FF FF FF 00 00 00 00   .......,........
                  2C0E:0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
                  2C0E:0050   CD 21 CB 00 00 00 00 00 00 00 00 00 00 20 20 20   .!...........
                  2C0E:0060   20 20 20 20 20 20 20 20 00 00 00 00 00 20 20 20       .....
                  2C0E:0070   20 20 20 20 20 20 20 20 00 00 00 00 00 00 00 00       ........
 
                  -RAX
                  AX 0000
                  :1234
 
                  -E 2C0E:21
                  2C0E:0021   69.   73.   20.   61.20 20.
 
                  -G 3C
 
                  AX=0E54  BX=0000  CX=003F  DX=0000  SP=FFEA  BP=0000  SI=001D  DI=0000
                  DS=2C26  ES=2C26  SS=2C26  CS=2C26  IP=003C   NV UP DI PL NZ NA PO NC
                  2C26:003C CD10          INT      10
                  -T
 
                  AX=0E54  BX=0000  CX=003F  DX=0000  SP=FFEA  BP=0000  SI=001D  DI=0000
                  DS=2C26  ES=2C26  SS=2C26  CS=F000  IP=0165   NV UP DI PL NZ NA PO NC
                  F000:F065 FB            STI
                  -T   
 
          Фиг. 5.17 Листинг отладчика для Фиг 5.13 и 4.14 (начало)
                  AX=0E54  BX=0000  CX=003F  DX=0000  SP=FFEA  BP=0000  SI=001D  DI=0000
                  DS=2C26  ES=2C26  SS=2C26  CS=F000  IP=0166   NV UP DI PL NZ NA PE NC
                  F000:F066 FC            CLD
                  -G 2C26:3E
 
                  T
                  AX=0E54  BX=0000  CX=003F  DX=0000  SP=FFEA  BP=0000  SI=001D  DI=0000
                  DS=2C26  ES=2C26  SS=2C26  CS=2C26  IP=013E   NV UP DI PL NZ NA PO NC
                  2C26:003E         RET
                  -G
 
                  Эта программа - тест
 
                  Program terminated normally
                  -R
 
                  AX=0754  BX=0000  CX=003F  DX=0000  SP=FFEA  BP=0000  SI=001D  DI=0000
                  DS=2C26  ES=2C26  SS=2C26  CS=2C26  IP=003E   NV UP DI PL NZ NA PO NC
                  2C26:003E C3            RET
                  -Q
 
                  B>
 
      Фиг. 5.17 Листинг отладчика для Фиг 5.13 и 4.14 (продолжение)
 
 
      Здесь следует немного задержаться и проанализировать
    информацию, которая записывается в регистры. Содержимое регистров
    соответствует моменту, когда программа FIG5=13 получает управление
    от командного процессора. Обратите внимание, что пара регистров
    CS:IP указывает на первую команду, определяемую оператором END
    программы. Регистры DS и ES указывают на префикс программного
    сегмента. И наконец, пара регистров SS:SP указывает на сегмент
    STACK. Описанное состояние регистров будет сравниваться позднее с
    аналогичным состоянием регистров для файла типа .COM.
 
      Чтобы просмотреть большее число команд, надо ввести символ "U"
    (дизассемблировать), и на дисплей выводится около двадцати
    следующих команд. Это удобно при обладке программы, для которой нет
    листинга. Дизассемблирование программы позволяет просмотреть ее
    команды. Это может сэкономить вам бумагу и время в случае, когда в
    программу внесены небольшие изменения. Так как ваш листинг больше
    не соответствует той программе, которая находится в памяти, то ее
    дизассемблирование позволяет вам узнать правильные адреса для
    каждой команды.
 
      Однако у дизассемблирования с помощью программы DEBUG имеется
    ряд недостатков по сравнению с использованием листинга. Отсутствуют
    комментарии (может быть очень важные для понимания программы), и
    ячейки памяти идентифицируются только по адресу, а не по имени
    переменной. Например, хранящаяся в ячейке 04C5:000E команда имеет,
    как показано на Фиг.5.13, следующий вид:
 
      MOV   OUTPUT_CHARACTER,AL
 
      а в дизассемблированном виде
 
      MOV   [0030],AL
 
      Это одна и та же команда. Для программиста, выполняющего
    отладку, имя переменной OUTPUT_CHARACTER говорит больше, чем адрес
    ячейки [0030]. Однако программе DEBUG не известны имена переменных,
    и она вынуждена оперировать фактическими адресами.
 
      Кроме того, программа, DEBUG не обеспечивает той же самой
    ассемблерной мнемоники, которую воспринимает ассемблер. Это значит,
    что некоторые команды будут выглядеть по-разному. Команда из ячейки
    04C5:0014 будет при дмзассемблировании иметь вид
 
      CMP   B,[3000],0A
 
      но та же саммая команда на Фиг.5.13 представлена в виде:
 
      CMP   OUTPUT_CHARACTER,10
 
      Программа дизассемблирования как на входе, так и на выходе
    работает только с шестнадцатеричными значениями. Этим объясняется
    появление значения 0A. Мы уже выяснили, почему получается значение
    [0030], вместо имени OUTPUT_CHARACTER. Что же такое символ "B,"?
 
      Ассемблер оперирует переменными вполне определенного типа. Это
    значит, что во время ассемблирования тип переменных может иметь
    значение: байт, слово или какой-нибудь другой. Таким образом, когда
    программист вводит команду, содержащую ссылку на область памяти, то
    ассемблеру известен размер этой области. На программа DEBUG не
    имеет представления о длине переменной, записанной по адресу
    [0030]. Однако программе дизассемблирования точно известно, что
    данная команда пересылает ровно один байт данных, указанных
    непосредственно в команде, по адресу [0030]. Таким образом, символ
    "B," указывает на то, что непосредственная операция состоит в
    пересылке одного байта. Для получения того же самого результата с
    помощью ассемблера соответствующая команда должна иметь вид:
 
      CMP   BYTE PTR [0030],10
 
      Вы можете рассматривать символ "B," как сокращение BYTE PTR.
    Аналогично символ W используется для WORD PTR,L - для длинного
    (far) возврата и т.д.
 
      Вместе с командой в дизассемблированном виде вводится и ее
    объектный код. Как вы можете заметить, по адресу 04C5:001C записаны
    какие-то команды, которых нет на Фиг. 5.13. Эта область данных,
    содержащая строку "Это тест". Однако команде, осуществляющей
    дизассемблирование, не известно, где в программе кончаются команды
    и начинаются данные. Таким образом она все интерпретирует как
    команды. (Кстати, именно эта последовательность команд выполнялась
    бы, если в вашей программе был сделан переход в рассматриваемую
    область данных.)
 
      Команда вывода на экран D позволяет просмотреть на дисплее
    области данных. Отображение на экране состоит из двух частей.
    Вместе с листингом содержимого ячеек памяти в шестнадцатеричном
    представлении приводятся символы в коде ASCII, которые
    соответствуют этим значениям. Если отображение команд подобным
    образом не имеет смысла, то область данных представляется очень
    ясно. Когда вам будет нечем особенно заняться, вы можете
    попробовать написать такую программу, команды которой в коде ASCII
    соответствуют инициалам вашего имени. С помощью отладчика можно
    изменять содержимое регистров и ячеек памяти. Если ввести символ R
    (регистр) и затем тип регистра, то на дисплей будет выведено
    содержимое этого регистра с возможностью его коррекции. Если нажать
    клавишу "возврат", то содержимое регистра останется прежним. Оно
    будет изменено, если ввести новое значение.
 
      Можно также модифицировать содержимое ячеек памяти. Ввод
    символа E (редактирование) позволяет это сделать. При этом
    программа DEBUG выводит на дисплей значения отдельных ячеек памяти,
    после которых следует символ . Вы можете изменить содержимое
    ячейки, вводя новое значение, либо нажать клавишу пробела, чтобы
    перейти к следующей ячейке, или - клавишу "возврат", чтобы
    вернуться к режиму запроса следующей команды отладчика. В
    рассматриваемом примере значения в первых трех ячейках остаются
    прежними. Содержимое ячейки 04C5:0024 изменено со значения 61H на
    значенее 20H. Так как эта ячейка входит в область данных, то
    выводимое сообщение будет отличаться от тог, которое было в
    транслированной программе.
 
      В любой команде, обращающейся к ячейкам памяти, предполагается,
    что адрес является частью команды. Команда E, как и команда
    отображения, выводит на дисплей содержимое ячейки по указанному в
    ней адресу. Точно так же можно было использовать адрес в команде
    дизассемблирования. Можно ввести адрес в виде сегмента и смещения,
    или только смещения. Если вы указали только смещение, то
    соответствующий сегментный регистр будет выбран программой DEBUG. В
    случае команды U используется регистр CS, а для команд D и E по
    умолчанию сегмент будет определяться регистром DS.
 
      Теперь попытаемся выполнить эту программу. Ее можно просто
    запустить и посмотреть, что будет происходить. На для этого не
    нужна программа DEBUG. Программа DEBUG позволяет задать точки
    останова, называемые "точками прерывания" программы. Благодаря
    введению в программу таких точек можно возвращать управление
    программе DEBUG. Это дает еще одну возможность проверки состояния
    памяти и регистров для того, чтобы контролировать ход выполнения
    программы.
 
      Команда G, (выполнять) передает управление от программы DEBUG
    пользовательской программе. Выполнение команд начинается с ячейки,
    задаваемой парой регистров CS:IP (так же, как в реальном
    микропроцессоре). Тестируемая программа продолжает выполняться до
    тех пор, пока она не пройдет точку прерывания. В нашем примере мы
    задали точку прерывания по адресу 3CH. Так как мы указали только
    смещение, то для определения сегмента программа DEBUG использует
    содержимое регистра CS. Из листинга, приведенного на Фиг. 5.14,
    видно, что смещение 3CH соответствует команде INT 10H. В
    рассматриваемом примере программы было выбрано именно это место,
    потому что это - та точка, где управление передается подпрограмме
    BIOS, вызываемой из ПЗУ. Проверка программы в этой точке
    гарантирует, что мя установили регистры в нужное состояние перед
    выполнением подпрограммы BIOS.
 
      Как только встречается точка прерывания, управление
    возвращается программе DEBUG. При этом, так же как и в случае
    команды R, на дисплей выводятся содержимое регистров и следующая
    выполняемая команда. Так как управление опять передано программе
    DEBUG, вы можете использовать любую из команд отладки.
 
      Имеются ограничения на использование точек прерывания.
    Фактически точка прерывания реализуется кодом операции 0CCH.
    Соответствующая этому коду команда вызывает прерывание INT 3.
    Данное математическое прерывание возвращает управление программе
    DEBUG. Если какая-то команда возвращает управление отладчику, то
    точка прерывания должна находиться в начале этой команды. Если же
    точка прерывания выбрана где-то в другом месте, то управление не
    будет возвращено отладчику и будет выполняться не та команда
    программы, которая предполагалась. Например, если бы было задано
    "=G 3D", то по адресу 3CH была бы команда INT 0CCH, и дальнейшее
    выполнение программы предсказать трудно.
 
      Если точка прерывания выбрана осмотрительно, то никаких
    осложнений не будет. Командв "G" позволяет задать до десяти точек
    прерывания. После прохождения любой из них происходит
    восстановление исходных значений точек прерывания. Выполнение
    команды отладки "G" без указания точек прерывания никогда не выйдет
    ни на какую из ранее заданных точек прерывания, потому что все они
    были удалены. Если вы запустили программу, и произошел ее останов и
    она зациклилась, то возможно, что вернуть управление, которое
    передано программе, удастся только с помощью клавиши системного
    сброса Ctrl=Alt=Del, т.е. вам придется начать все с начала.
    Запуская незнакомую программу, следует быть осторожным.
 
      Если вы захотите ввести пермаментную точку прерывания,
    воспользуйтесь командой E для изменения значения первого байта
    команды на значение 0CCH. Эта точка прерывания будет там постоянно
    или, по крайней мере, до тех пор, пока вы ее не измените. Возможно
    вы захотите использовать это прерывание в точке входа в
    подпрограмму обработки ошибки и проанализируете возникающие ошибки
    более тщательно, чем при передаче их обработки программе.
 
      Существует еще одно обстоятельство, связанное с точками
    прерывания, о котором следует помнить. Если вы попытаетесь задать
    точку прерывания в области, относящейся к ПЗУ, то ничего не
    получится. Так как вы не можете менять содержимое ПЗУ, то команда
    0CCH никогда туда не запишется.
 
      Рассмотрим следующую команду отладчика - команда трассировки T.
    Данная команда инициирует выполнение одной команды отлаженной
    программы. В нашем примере команда T выполнена несколько раз. Как
    вы можете убедиться, выполняется несколько первых команд BIOS,
    вызванной по прерыванию INT 10H. Подпрограмма BIOS, естественно,
    находится в ПЗУ. Команда трассировки позволяет "приостановить"
    программу при ее выполнении в ПЗУ.
 
      Перед передачей управления пользовательской программе команда
    трассировки выставляет в регистре флагов соответствующий бит
    трассировки. Этот бит инициирует прерывание INT 1 после выполнения
    каждой команды. Вектор прерывания INT 1 возвращает управление
    программе DEBUG. Выполнение прерывания INT 1 автоматически
    сбрасывает бит трассировки в исходное состояние. Это значит, что
    программа DEBUG не прерывается после выполнения каждой ее команды.
    Команда трассировки служит прекрасным средством "пробиться" через
    трудный участок программы. При этом программа DEBUG выводит на
    экран каждую команду вместе с содержимым регистров как раз в
    момент, предшествующий выполнению этой команды. Так как в данном
    режиме используются не точки прерывания, а собственно прерывания,
    то можно выполнять трассировку даже программ ПЗУ.
 
      Вернемся к нашему примеру. Команда =G 4C5:3E обеспечивает
    полное выполнение подпрограммы BIOS. Обратите внимание, что
    программа вывела на дисплей символ "Э". Вызванная по прерыванию 10H
    подпрограмма BIOS выводит символы на дисплей. В данном случае это
    первый символ выводимого сообщения. Так как теперь можно быть
    уверенными, что наша программа выполняется правильно, то, введя
    символ "G", мы обеспечим выполнение программы до конца без точек
    прерывания.
 
      В данном примере рассматривался файл типа .EXE, и потому для
    возврата управления системе DOS мы не могли использовать прерывание
    INT 20H. Вместо этого программа записала в стек состояние регистра
    DS и значение 0. Управление передается обратно системе DOS в конце
    основной программы с помощью команды возврата типа FAR. Программа
    DEBUG распознает это и фиксирует состояние машины в конце
    тестируемой программы. Если бы это был файл типа .EXE, то
    прерывание INT 20H аналогичным образом вернуло бы управление
    программе DEBUG. Теперь, уделив достаточно времени этому примеру,
    мы можем выйти из программы DEBUG и вернуться в систему DOS с
    помощью команды завершения Q.