Страница Ивана Рощина > Статьи >

© Иван Рощин, Москва

ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru

Особенности отладки программ с помощью монитора STS

(«ZX-Ревю» 5—6/1997)
Дата последнего редактирования: 10.05.2003.

Наверняка у каждого программиста при отладке своего творения возникала мысль: хорошо бы в любой момент работы отлаживаемой программы при нажатии определённой комбинации клавиш выйти в отладчик (например, чтобы посмотреть значения переменных), а затем как ни в чём не бывало вернуться к выполнению программы. В «ZX-Ревю» уже были статьи на эти тему (например, на стр. 244 за 1991 г., на стр. 33 за 1993 г.). Здесь я хочу рассказать о применении для этой цели наиболее популярного монитора-отладчика STS.

В этом отладчике существует возможность поставить в отлаживаемой программе точку останова, при достижении которой происходит выход в STS. Рассмотрим подробнее, что происходит при установке точки останова (с помощью клавиши «W»), и при выходе в STS, когда отлаживаемая программа достигла точки останова.

При установке точки останова:

  1. Из ячеек ADR_W и ADR_W+1 берётся адрес предыдущей точки останова.
  2. По этому адресу заносится содержимое трёх байтов памяти, ранее (при установке предыдущей точки останова) запомненных в ячейках ADR_B—ADR_B+2.
  3. В ячейки ADR_W и ADR_W+1 засылается адрес, отмеченный курсором.
  4. В ячейки ADR_B—ADR_B+2 заносится содержимое трёх байтов памяти, начиная с адреса, отмеченного курсором.
  5. По адресу, отмеченному курсором, заносится команда JP ADR_OST.

Когда выполняемая программа достигает точки останова:

  1. Происходит переход на адрес ADR_OST, и управление передаётся резидентной части STS.
  2. STS запоминает содержимое всех регистров, режима прерываний и т.п.
  3. Из ячеек ADR_W и ADR_W+1 берётся адрес точки останова.
  4. По этому адресу заносится содержимое трёх байтов памяти, ранее (при установке этой точки останова) запомненных в ячейках ADR_B—ADR_B+2.
  5. Вы можете работать в STS.

Приведённой информации вполне достаточно, чтобы написать фрагмент программы, который при нажатии некоторой комбинации клавиш (например, «SS+D» — от слова «Debugger») обеспечивает вход в STS с возможностью дальнейшего продолжения работы отлаживаемой программы. Нужно только знать адреса ADR_W, ADR_B и ADR_OST. Отмечу, что в разных версиях STS эти адреса различаются, а адрес ADR_OST зависит ещё и от расположения резидентной части STS в памяти. Чтобы найти ADR_OST, достаточно посмотреть, какую команду STS поставит при нажатии клавиши «W». Чтобы найти ADR_B, можно, скажем, записать по какому-то адресу памяти байты #AB, #CD, #EF, потом поставить по этому адресу точку останова и найти адрес, по которому STS сохранил эти три байта. Чтобы найти ADR_W, нужно поставить по какому-нибудь адресу (например, #8000) точку останова, а потом посмотреть, где эти два байта (#00, #80) хранятся внутри STS.

Привожу адреса для пяти известных мне версий STS (при условии, что резидент находится по адресу #5B90):

Табл. 1
ADR_W ADR_B ADR_OST
STS 3.2 #DFC3 #DFE4 #5B98
STS 3.3 #DFC3 #DFE4 #5B98
STS 4.1 #E064 #E07C #5B9B
STS 4.3 #E06E #E088 #5B9B
STS 5.1 #E02E #E038 #5B9B

Приведённый ниже фрагмент вы можете вставить в отлаживаемую программу, например, после процедуры опроса клавиатуры или в другом часто выполняемом месте. Хочу заметить, что адрес его расположения в памяти должен быть меньше #C000, т.к. в этом фрагменте переключаются банки ОЗУ.

ADR_W   EQU   #E02E   ;Эти адреса даны для
ADR_B   EQU   #E038   ;STS версии 5.1, если резидент
ADR_OST EQU   #5B9B   ;находится по адресу #5B90.
BANK_S  EQU   #1F     ;Банк STS.
BANK_D  EQU   #18     ;Банк отлаживаемой программы.

   LD      BC,#7FFE   ;Адрес порта клавиатуры.
   IN      A,(C)      ;Проверяем, нажата ли "Symb.Shift".
   BIT     1,A        ;Если да, флаг Z установлен.
   JR      NZ,NEXT_C  ;Если не нажата, выходим...
   LD      B,#FD      ;Изменяем адрес порта.
   IN      A,(C)      ;Аналогично проверяем
   BIT     2,A        ;клавишу "D".
   JR      NZ,NEXT_C  ;Если не нажата, выходим...

   LD      BC,#7FFD   ;Устанавливаем банк, в
   LD      A,BANK_S   ;котором находится STS.
   OUT     (C),A

;Имитируем для STS точку останова:

   LD      HL,ADR_B   ;Здесь хранятся 3 байта,
                      ;запомненные при установке
                      ;предыдущей точки останова.
   LD      DE,(ADR_W) ;Адрес, по которому их надо
                      ;разместить (обязательно ниже #C000)!
   LD      BC,3
   LDIR               ;Восстанавливаем 3 байта.

   LD      HL,NEXT_C  ;Адрес первой команды, которая
   LD      (ADR_W),HL ;будет выполнена после выхода
                      ;из STS, записываем в ячейки
                      ;ADR_W..ADR_W+1 (в них STS хранит
                      ;адрес, по которому была установлена
                      ;точка останова).
   LD      DE,ADR_B   ;В ячейках ADR_B..ADR_B+2 STS хранит
   LD      BC,3       ;3 байта, расположенные по адресу
   LDIR               ;точки останова.

   LD      A,BANK_D   ;Возвращаем банк, который был
   OUT     (C),A      ;до этого.
   JP      ADR_OST    ;Точка останова.

NEXT_C  ...           ;Отсюда начнется выполнение после
                      ;выхода из STS.

После нажатия «SS+D» и выхода в STS все регистры принимают значения, которые были на момент прерывания программы, так что можно будет продолжить её выполнение.

В процессе отладки можно поставить в программе точку останова, лишь бы в момент работы приведённого выше фрагмента (когда удаляется старая точка останова и ставится новая) адрес старой точки останова был ниже #C000.

Вы можете продолжить выполнение отлаживаемой программы с того места, где она была остановлена, например, в пошаговом режиме («SS+Z», «SS+X», «SS+T») или используя окно Trace, или с помощью комбинации клавиш «SS+J» (в версиях STS ниже 4.0 используйте «J», установив адрес запуска равным содержимому PC, или используйте комбинацию «SS+K», но при этом останется экран STS). Если вы хотите вернуться в ассемблер, Бейсик или TR-DOS, используйте клавишу «Q».

Если ваша программа зависает неизвестно почему, STS тоже может вам помочь. Для этого вы должны встроить фрагмент, подобный описанному выше, в процедуру обработки прерываний 2-го рода. Предлагаю вашему вниманию уже готовую процедуру обработки прерываний, которая так же, как и описанная выше, проверяет факт нажатия «SS+D» и выходит в STS. После выхода из STS выполнение отлаживаемой программы будет продолжено с того места, где оно было прервано процедурой обработки прерывания. Поэтому, находясь в STS, вы можете определить, какой фрагмент программы послужил причиной «зависания», по содержимому регистра PC.

Ограничение на адрес расположения процедуры в памяти такое же, как и у ранее описанного фрагмента.

ADR_W   EQU   #E02E   ;Эти адреса даны для STS
ADR_B   EQU   #E038   ;версии 5.1, если резидент
ADR_OST EQU   #5B9B   ;находится по адресу #5B90.
BANK_S  EQU   #1F     ;Банк STS.
BANK_D  EQU   #18     ;Банк отлаживаемой программы.

   EX      (SP),HL   ;Адрес возвращения из прерывания
   LD      (M1+1),HL ;записываем в ячейки M1+1 и M1+2.
   EX      (SP),HL
   PUSH    AF        ;Сохраняем в стеке
   PUSH    BC        ;значения регистров.
   PUSH    DE
   PUSH    HL
   PUSH    IX
   PUSH    IY
   EXX
   EX      AF,AF'
   PUSH    AF
   PUSH    BC
   PUSH    DE
   PUSH    HL

   CALL    USER    ;Здесь может быть опрос
                   ;клавиатуры, вызов музыки,
                   ;в общем, всё что хотите.

   LD      BC,#7FFE   ;Адрес порта клавиатуры.
   IN      A,(C)      ;Проверяем, нажата ли "Symb.Shift".
   BIT     1,A        ;Если да, флаг Z установлен.
   JR      NZ,NEXT_C  ;Если не нажата, выходим...
   LD      B,#FD      ;Изменяем адрес порта.
   IN      A,(C)      ;Аналогично проверяем
   BIT     2,A        ;клавишу "D".
   JR      NZ,NEXT_C  ;Если не нажата, выходим...

   LD      BC,#7FFD   ;Устанавливаем банк, в
   LD      A,BANK_S   ;котором находится STS.
   OUT     (C),A

;Имитируем для STS точку останова:

   LD      HL,ADR_B   ;Здесь хранятся 3 байта,
                      ;запомненные при установке
                      ;предыдущей точки останова.
   LD      DE,(ADR_W) ;Адрес, по которому их надо
                      ;разместить.
   LD      BC,3
   LDIR               ;Восстанавливаем 3 байта.

M1 LD      HL,0       ;Адрес первой команды, которая
   LD      (ADR_W),HL ;будет выполнена после выхода
                      ;из STS, записываем в ячейки
                      ;ADR_W..ADR_W+1 (в них STS хранит
                      ;адрес, по которому была установлена
                      ;точка останова).
   LD      DE,ADR_B   ;В ячейках ADR_B..ADR_B+2 STS хранит
   LD      BC,3       ;3 байта, расположенные по адресу
   LDIR               ;точки останова.

   LD      BC,#7FFD
   LD      A,BANK_D   ;Возвращаем банк, который был
   OUT     (C),A      ;до этого.

   POP     HL       ;Восстанавливаем
   POP     DE       ;значения регистров.
   POP     BC
   POP     AF
   EXX
   EX      AF,AF'
   POP     IY
   POP     IX
   POP     HL
   POP     DE
   POP     BC
   POP     AF
   EX      (SP),HL  ;Убираем из стека
   POP     HL       ;адрес возврата.
   EI
   JP      ADR_OST    ;Точка останова.

;После выхода из STS выполнение программы будет
;продолжено с того места, где оно было прервано
;процедурой обработки прерывания.

NEXT_C   ;Если вызов STS не понадобился:

   POP     HL       ;Восстанавливаем
   POP     DE       ;значения регистров
   POP     BC
   POP     AF
   EXX
   EX      AF,AF'
   POP     IY
   POP     IX
   POP     HL
   POP     DE
   POP     BC
   POP     AF
   EI
   RET              ;и выходим из прерывания.

При отладке программ часто используют совместно STS и ассемблер (TASM, MASM, ZX ASM и т.п.), что очень удобно. Если в вашу программу встроен один из описанных выше фрагментов, вы должны обратить особое внимание на то, как вы запускаете программу на выполнение. Если из STS, всё будет нормально в любом случае. Если же программа запускается из ассемблера или Бейсика, у вас может не получиться выйти в STS при нажатии «SS+D», или вы выйдете в STS, но не сможете продолжить выполнение программы.

* * *

Известно, что пошаговое выполнение команд процессора в STS осуществляется тремя способами: с помощью комбинаций клавиш «SS+Z», «SS+X» и «SS+T». Попробую рассказать об особенностях этих способов и о том, когда лучше использовать каждый из них.

  1. «SS+Z» — происходит пошаговое выполнение команд процессора Z80. Никаких сложностей при использовании «SS+Z» быть не должно, только не трассируйте команду HALT, когда запрещены прерывания, иначе компьютер зависнет.
  2. «SS+X» — то же, что и «SS+Z», но команда CALL будет выполнена непосредственно, без трассировки вызываемой подпрограммы, которая предполагается уже отлаженной. Это же касается и команды RST (кстати, в описании STS упоминается только CALL, а об RST нет ни слова). Выполняемая команда может находиться и в ОЗУ, и в ПЗУ. Это лучший способ для выполнения отлаженных подпрограмм, но у него есть одна особенность: так как вызов подпрограммы происходит из резидента, в стек вместо адреса следующей выполняемой команды вашей программы будет занесён адрес для возврата в резидент. Почти всегда это не имеет никакого значения, но некоторые подпрограммы могут использовать содержимое стека. Например, при отладке своих программ я часто использую подпрограмму, которая при своем вызове печатает на экране содержимое регистров процессора в момент вызова. Чтобы определить значение PC, она анализирует, какой адрес был занесён в стек при её вызове.
  3. «SS+T» — отладчик ставит после выполняемой команды, которая не может находиться в ПЗУ, точку останова и запускает отлаживаемую программу. По этой точке программа возвращается через резидент в STS. «SS+T» удобно применять для трассировки DJNZ, но для трассировки подпрограмм (CALL, RST) лучше этот способ не использовать, т.к. может случиться неприятность. Это видно на следующем примере:

    #8000: CALL #8004
    #8003: RET
    #8004: LD   A,7
    #8006: OUT  254,A
    #8008: RET
    

    Эта программа должна установить белый цвет бордюра. Если выполнить её в пошаговом режиме с помощью «SS+Z», можно убедиться, что именно это она и делает. Однако, если выполнить её, нажав «SS+T», ожидаемого результата вы не получите. Для того, чтобы понять, почему это происходит, подробнее разберём алгоритм работы отладчика при нажатии «SS+T»:

    Всё бы хорошо, но в рассматриваемом случае 3 байта команды JP накладываются на адрес, по которому передаётся управление в команде CALL #8004. В результате этого реально выполняется следующая последовательность команд:

    #8000: CALL  #8004
    #8004: SBC   A,E
    #8005: LD    E,E
    #8006: OUT   (254),A
    #8008: RET
    #8003: JP    #5B9B
    

    Разумеется, всё вышесказанное относится и к команде процессора DJNZ, так что «SS+T» нужно применять с осторожностью. Вот, например, какая может быть ситуация при трассировке DJNZ:

    #8000: LD    B,#00
    #8002: LD    A,(#800A)
    #8005: OUT   (#FE),A
    #8007: DJNZ  #8002
    #8009: RET
    #800A: DB    0  ;Этот байт будет испорчен.
    
* * *

Теперь поговорим о пошаговой трассировке (окно Trace). В этом окне есть параметр Trace Call. Если он включен, отладчик будет заходить «внутрь» процедуры при каждом выполнении команды CALL или RST (эмулируется «SS+Z»), а если выключен — выполнять её с помощью прямого запуска из резидента («SS+X»). Мне кажется, была бы полезной возможность независимо от значения Trace Call отключить трассировку подпрограмм, адреса которых выходят за заранее определённые границы. Эти границы должны указываться в окне Trace. Например, если нижняя граница равна #4000, а верхняя — #FFFF, то отладчик не будет заходить «внутрь» всех подпрограмм ПЗУ. Но так как этой возможности в STS нет, остаётся только надеяться, что она будет в одной из следующих версий.

Другие мои статьи об отладчике STS:

1. 

«Ошибка в STS 6.2 и её исправление». ZX-Pilot #29, Voyager #2, «Радиолюбитель. Ваш компьютер» 5/2000.

2. 

«Как „подружить“ ALASM 4.1 и STS 6.2». Voyager #2.

3. 

«Недокументированная особенность процессора Z80». ZX.SPECTRUM, Deja Vu #8, Voyager #3, «Радиолюбитель. Ваш компьютер» 4/2000 (дополненная версия).

(В этой статье можно найти сведения об исправлении ошибки определения состояния прерываний в STS.)

4. 

«Секреты текстового вывода». «Радиолюбитель. Ваш компьютер» 2—3/2001, под псевдонимом BV_Creator.

(В этой статье описан способ получения шрифта 6x8 на базе шрифта ПЗУ; этот способ можно применить в STS, а освободившееся место, где раньше хранился шрифт, можно использовать для реализации в STS новых функций.)

5. 

«Настройка цветов в STS 6.2». «Радиомир. Ваш компьютер» 9/2002, под псевдонимом BV_Creator.

Страница Ивана Рощина > Статьи >