(С) Иван Рощин, Москва, 1996г. ДОСТУП К ПОРТУ #iF В TR-DOS 5.03.
Программируя дисковые операции на уровне портов ввода-вывода, Вы неизбежно столкнетесь с необходимостью получить значение регистра состояния из порта #1F. Как же это сделать? Известо, что адреса регистров микроконтроллера появляются в адресном пространстве только в момент работы дисковой системы. Из бей-сик-системы регистры недоступны. Не сделано, также, никаких попыток обеспечить прямой доступ к ним из дисковой системы. Несмотря на это, "достать" микроконтроллер все-таки возможно, для этого используются "обрывки" программ ОС. По другому, к сожалению, не получится.
ДОС
ВВЕРХ
ВНИЗ
ВЛЕВО
ВПРАВО
ОГОНЬ
ОЧИСТКА
ЦВЕТ
ОЧИСТКА ЭКРАНА
МЛАДШИЙ
СТАРШИЙ
МАКСИМАЛЬНЫЙ
МИНИМАЛЬНЫЙ
УСТАНОВИТЬ
ВЫКЛЮЧИТЬ
ПЕРЕМЕННАЯ
ЧИСЛО
ЗНАК
МУЗЫКА
ЗВУК
ШРИФТ
КОМПРЕССОР
ДЕКОМПРЕССОР
КОДИРОВЩИК
ДЕКОДЕР
ДЕКОМПРЕССОР
ДЛЯ
К, ДО
ПРОСТРАНСТВО
МЕСТО
АРХИВ
РАЗАРХИВАТОР ФОРМАТ КАДР СКАНЕР
ДЕМОНСТРАЦИЯ ВЫХОД РАСШИРЕНИЕ ИНИЦИАЛИЗАЦИЯ РАБОТА ТЕСТ ЦИФРА
Опубликованная в "ZX-РЕВЮ" 96/4-5 информация о том, что в ПЗУ TR-DOS 5.03 по адресу #09BF стоит последовательность команд IN A,(#1F) и RET, не подтвердилась. В фирменной версии ПЗУ по этому адресу Вы можете найти лишь коды #FF. Еще один экзотический способ доступа к портам (передаем управление в ПЗУ TR-DOS за несколько тактов до прерывания 2-го рода, а процедура обработки прерывания получает считанное из порта значение) теоретически неплох, но практически вряд ли осуществим (а если промежуток между двумя прерываниями непостоянен, этот метод вообще неприменим).
В книге А.Ларченко и Н.Родионова "ZX Spectrum & TR-DOS для пользователей и программистов" приводится следующий алгоритм считывания регистра состояний микроконтроллера:
• -записать 0 в регистр дорожки;
• -записать #0А в регистр сектора;
• -записать 1 в регистр D;
• -вызвать подпрограмму по адресу #3F33;
• -взять из регистра В значение регистра состояний;
• -Заслепи» »У/1
• -восстановить в регистрах дорожки и сектора исходные значения.
Я пользовался этим способом, и все вроде бы было нормально. Но я стал замечать, что в некоторых случаях при обращении к процедуре считывания регистра состояния компьютер зависал или сбрасывался. Пытаясь найти причину, я выяснил следующее:
АЛГОРИТМ РАБОТАЕТ НЕПРАВИЛЬНО, ЕСЛИ 0-Й БИТ РЕГИСТРА СОСТОЯНИЙ УСТАНОВЛЕН В 1 (КОНТРОЛЛЕР ЗАНЯТ ВЫПОЛНЕНИЕМ КАКОЙ-ЛИБО КОМАНДЫ).
Действительно, рассмотрим все возможные случаи работы подпрограммы TR-DOS по адресу #3F33 в зависимости от считанного из порта #1F числа, а заодно разберемся, почему перед ее вызовом необходимо устанавливать
описанные выше параметры. |
проверяем 4-й бит |
1) Биты 0-6 |
равны нулю |
JR NZ,#3F4B |
(Х0000000). |
|
;условие не выполняется |
#3F33: IN |
A,(#1F) |
LD А,В |
LD |
В,А |
AND #4 |
AND |
#7F |
JR Z,#3FA0 |
; выделяем биты 0-6 |
;условие выполняется |
RET |
Z |
|
; выход |
|
#3FA0: DEC D |
2) б-й бит равен 1, 0-й бит равен 0 |
; получили 0 |
(Х1ХХХХХ0). |
|
JP Z,#3F48 |
#3F33: IN |
А,(# 1F) |
;условие выполняется |
LD |
В,А |
|
AND |
# 7F |
#3F48: LD HL,#29E2 |
RET |
Z |
LD A,#D0 |
;не выходим |
|
OUT (#1F),A |
LD |
HL,#29D8 |
LD А, В |
AND |
#40 |
AND #1 |
проверяем 4-й бит |
{проверяем 0-й бит |
JR |
NZ,#3F4B |
JP NZ,#3EE7 |
;условие выполняется
;условие не выполняется
IN A,(#3F)
#3F4B: LD A.#D0 OUT (#1F),A LD А, В AND #1 проверяем 0-й бит
JP NZ,#3EE7 ;условие не выполняется
IN A,(#3F) ; регистр дорожки равен 0 OR А JR NZ,#3F5F ;условие не выполняется
IN A,(#5F) ; регистр сектора равен #0А СР #0А RET Z ;условие выполняется 3) 6-й бит равен 0, 2-й бит равен 0-й бит равен 0, какой-то из бит 1,3,4,5 равен 1. (X0XXX0X0). #3F33: IN A,(#1F) LD В,A AND #7F RET Z ;условие не выполняется
LD HL,#29D8 AND #40
;регистр дорожки равен О OR А JR NZ,#3F5F ;условие не выполняется
IN A,(#5F) регистр сектора равен #0А CP #0А RET Z ;условие выполняется 4) 0-й бит равен 1 (ХХХХХХХ1).
В этом случае, независимо от значения других битов, управление будет передано по адресу #ЗЕЕ7. Там расположена процедура, портящая значение некоторых переменных TR-DOS и завершающая свою работу так:
#01F3:
SP,(#5D1C) HL,(#5D1A) BC,(#5D0F) В,0 (HL)
Так как в ячейках #5D1A-#5D1D, вообще говоря, могут содержаться произвольные значения, дальнейшее выполнение программы непредсказуемо. Вдобавок, значение, считанное из порта #1F и хранящееся в регистре В (ради которого все и затевалось), будет потеряно.
Теперь я приведу написанную мной процедуру, которая правильно (всегда!) определяет значение регистра состояния. При своей работе она не изменяет значение ни одной системной переменной. TR-DOS.
•ПРОЦЕДУРА STATUS ВОЗВРАЩАЕТ СОДЕРЖИМОЕ РЕГИСТРА СОСТОЯНИЯ. ;ВХОД: А -СОДЕРЖИМОЕ РЕГИСТРА ДОРОЖКИ, ; В -СОДЕРЖИМОЕ РЕГИСТРА СЕКТОРА, ; КОТОРЫЕ БУДУТ УСТАНОВЛЕНЫ ПОСЛЕ ; ВЫХОДА ИЗ ПРОЦЕДУРЫ. ;ВЫХОД: А -ЗНАЧЕНИЕ, СЧИТАННОЕ ИЗ ПОРТА #1F. ПРЕРЫВАНИЯ ПОСЛЕ ВЫХОДА ЗАПРЕЩЕНЫ!
STATUS DI LD
; ДО РОЖКА LD LD
; СЕКТОР
СОХРАНЯЕМ СОДЕРЖИМОЕ ЯЧЕЕК, КОТОРЫЕ ;МОГУТ БЫТЬ ИСПОРЧЕНЫ:
LD |
A,(#5D0E) |
LD |
(ST1 + 1 ),A |
LD |
A,(#5D0C) |
LD |
(ST2+1 ),A |
LD |
A,(#5CB6) |
LD |
(ST3t1),A |
LD |
A,(#5D1F) |
LD |
(ST4+1),A |
LD |
A,(#5C3A) |
LD |
(ST5+1),A |
LD |
A,(#5D17) |
LD |
(ST6+1),A |
LD |
HL,(#5D1A) |
LD |
(ST7+D.HL |
LD |
HL,(#5D1C) |
LD |
(ST8+D.HL |
LD |
HL,(#5CF8) |
LD |
(ST9+1),HL |
ОСТАНАВЛИВАЕМ СОДЕРЖИМОЕ НЕКОТОРЫХ ;ЯЧЕЕК ДЛЯ ПРАВИЛЬНОЙ РАБОТЫ:
A,#FF (#5D0C),A (#5D1F),A A
(#5D0E),A
(RG_D+1),A А,В
(RG_S+1 ),А
CALL TO_WG93 LD A,#OA ;#A В РЕГИСТР LD C,#5F ; СЕКТОРА CALL TO_WG93 LD D, 1 LD IX,16179 CALL TO.DOS ОПРЕДЕЛИЛИ #1F
ТЕПЕРЬ ВОССТАНАВЛИВАЕМ СОДЕРЖИМОЕ РЕГИСТРОВ ДОРОЖКИ И
СЕКТОРА: |
|
RG D LD |
A,0 |
LD |
C,#3F |
CALL |
TO WG93 |
RG S LD |
A,0 |
LD |
C,#5F |
CALL |
TO WG93 |
{ВОССТАНАВЛИВАЕМ РАНЕЕ ЗАПОМНЕННОЕ {СОДЕРЖИМОЕ ЯЧЕЕК:
ST1 |
LD |
A,0 |
|
LD |
(#5D0E),A |
ST2 |
LD |
A.O |
|
LD |
(#5D0C),A |
ST3 |
LD |
A,0 |
|
LD |
(#5CB6),A |
;СЮДА БУДЕТ ПЕРЕДАНО УПРАВЛЕНИЕ, ЕСЛИ ;0-Й БИТ РЕГИСТРА СОСТОЯНИЙ РАВЕН 1:
S_SPEC POP ВС ; СОДЕРЖИ МОЕ ПОРТА
LD HL,(#5D1C) LD DE.12 ВОССТАНАВЛИВАЕМ
ADD HL.DE УКАЗАТЕЛЬ
LD SP.HL
СТЕКА
JR
RG_D
TO_WG93 TO DOS
LD IX,#2A53
PUSH IX JP #3D2F
Я думаю, нет лучшего способа объяснить, как работает эта процедура, чем показать процесс ее выполнения, начиная с момента вызова подпрограммы по адресу #3F33. При этом выберем именно такой случай, когда 0-й бит регистра состояния равен 1: #3F33: IN A,(#1F) {допустим, считано #FF LD В, А AND #7F RET Z ;не выходим
|
|
форум |
|
|
LD |
A,#F4 |
ST4 |
LD |
A.O |
LD |
(#5CB6),A |
|
LD |
(#5D1F),A |
|
|
ST5 |
LD |
A,0 |
LD |
HL.S SPEC |
|
LD |
(#5C3A),A |
LD |
(#5D1A),HL |
ST6 |
LD |
A,0 |
LD |
HL.O |
|
LD |
(#5D17),A |
ADD |
HL.SP |
ST7 |
LD |
HL.O |
LD |
DE.-12 |
|
LD |
(#5D1A),HL |
ADD |
HL.DE |
ST8 |
LD |
HL,0 |
LD |
(#5D1C),HL |
|
LD |
(#5D1C),HL |
|
|
ST9 |
LD |
HL,0 |
LD |
A,0 |
|
LD |
(#5CF8),HL |
;0 В РЕГИСТР |
|
|
|
LD |
C,#3F |
|
LD |
А,В |
{ДОРОЖКИ |
|
RET |
|
LD HL,#29D8 AND #40 проверяем 4-й бит
JR NZ,#3F4B ;условие выполняется
#3F4B: LD A,#D0 OUT (#1F),A LD А, В AND #1 проверяем 0-й бит
,JP NZ,#3EE7 •.условие выполняется
#ЗЕЕ7: |
CALL |
#272B |
#272В: |
LD |
A,# 1A |
|
JR |
#2731 |
#2731: |
LD |
(#5C3A),A |
|
RET |
|
#ЗЕЕА: |
LD |
A,#FF |
|
LD |
(#5D17),A |
|
JP |
#271B |
#271 В: |
LD |
HL,#27FC |
|
LD |
A, #06 |
|
JP |
# 1C4A |
#1С4А: |
CALL |
#03C3 |
#ОЗСЗ: |
PUSH |
AF |
|
LD |
A,(#5D0E |
;в эту ячейку ;перед запуском ;занесли #FE CP JR
;условие не выполняется
POP RET
AF
A,(#5CF8) #FF
Z,#211С
;условие не выполняется POP AF CALL #2970
#2970: PUSH HL PUSH DE PUSH ВС Как видим, здесь содержимое ВС (где находится столь нужный нам регистр состояния) заносится в стек. В моей процедуре этот факт используется для последующего восстановления ВС. PUSH AF LD HL,#5D0C ;в эту ячейку ; перед запуском ;занесли #FF LD A,(HL) OR A JR NZ,#2992 ;условие выполняется
#2992: POP AF POP ВС Содержимое ВС извлечено из стека, но в памяти, отведенной под стек, оно осталось. Его можно извлечь оттуда, лишь бы оно не было затерто записываемыми в стек значениями. Процедура обработки прерываний затрет значение ВС, а потому прерывания запрещены.
POP DE POP HL RET
# 1C4D: JP #01D3
HL,0
(#5CF8),HL #20E5
#20F1: PUSH LD
;в эту ячейку ;перед запуском
AF
А,(#5СВ6)
.занесли #F4 CP JR
#211С:
#01 DC: CALL
# 1D63:
;в эту ячейку ;перед запуском ;занесли #FE LD CP LD RET
#01DF: LD LD LD
;в эту ячейку {перед запуском {занесли #FF LD OR LD JR
HL,#5D0E
A,(HL) #FF (HL).O NZ
HL,#5D17 (HL),#AA HL,#5D1 F
A,(HL) A
(HL),0 NZ,#01F3
{условие выполняется
#01F3: LD SP,(#5D1C) {значения этих ячеек
LD HL,(#5D1A) {подготовлены заранее
LD BC,(#5D0F) {теперь ВС осталось {только в стеке...
LD В,0 JP (HL)
При выходе управление передается на метку SSPEC, а SP указывает на то самое значение системного регистра, которое когда-то было сохранено в стеке.