© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
Программируя дисковые операции на уровне портов
В книге А.Ларченко и Н.Родионова «ZX Spectrum и
Я пользовался этим способом, и всё вроде бы было нормально. Но я стал замечать, что в некоторых случаях при обращении к процедуре считывания регистра состояния компьютер зависал или сбрасывался. Пытаясь найти причину, я выяснил следующее:
Алгоритм работает неправильно, если
Действительно, рассмотрим все возможные случаи работы подпрограммы
1) Биты
#3F33: IN A,(#1F)
LD B,A
AND #7F ;Выделяем биты 0-6.
RET Z ;Выход.
2)
#3F33: IN A,(#1F)
LD B,A
AND #7F
RET Z ;Не выходим.
LD HL,#29D8
AND #40 ;Проверяем 4-й бит.
JR NZ,#3F4B ;Условие выполняется.
#3F4B: LD A,#D0
OUT (#1F),A
LD A,B
AND #1 ;Проверяем 0-й бит.
JP NZ,#3EE7 ;Условие не выполняется.
IN A,(#3F) ;Регистр дорожки равен 0.
OR A
JR NZ,#3F5F ;Условие не выполняется.
IN A,(#5F) ;Регистр сектора равен #0A.
CP #0A
RET Z ;Условие выполняется.
3)
#3F33: IN A,(#1F)
LD B,A
AND #7F
RET Z ;Условие не выполняется.
LD HL,#29D8
AND #40 ;Проверяем 4-й бит
JR NZ,#3F4B ;Условие не выполняется.
LD A,B
AND #4
JR Z,#3FA0 ;Условие выполняется.
#3FA0: DEC D ;Получили 0.
JP Z,#3F48 ;Условие выполняется.
#3F48: LD HL,#29E2
LD A,#D0
OUT (#1F),A
LD A,B
AND #1 ;Проверяем 0-й бит.
JP NZ,#3EE7 ;Условие не выполняется.
IN A,(#3F) ;Регистр дорожки равен 0.
OR A
JR NZ,#3F5F ;Условие не выполняется.
IN A,(#5F) ;Регистр сектора равен #0A.
CP #0A
RET Z ;Условие выполняется.
4)
В этом случае, независимо от значения других битов, управление будет
передано по адресу #3EE7. Там расположена процедура, портящая значение
некоторых переменных
........
#01F3: LD SP,(#5D1C)
LD HL,(#5D1A)
LD BC,(#5D0F)
LD B,0
JP (HL)
Так как в ячейках #5D1A#5D1D, вообще говоря, могут содержаться произвольные значения, дальнейшее выполнение программы непредсказуемо. Вдобавок и значение, считанное из порта #1F и хранящееся в регистре B (ради которого всё и затевалось), будет потеряно.
Теперь я приведу написанную мной процедуру, которая правильно (всегда!)
определяет значение регистра состояния. При своей работе она не изменяет
значение ни одной системной переменной
;***************************************
;Процедура STATUS возвращает содержимое
;регистра состояния.
;
;Вход: A - содержимое регистра дорожки,
; B - содержимое регистра сектора,
; которые будут установлены после
; выхода из процедуры.
;
;Выход: A - значение, считанное из порта #1F.
;Прерывания после выхода запрещены!
STATUS DI
LD (RG_D+1),A ;Дорожка.
LD A,B
LD (RG_S+1),A ;Сектор.
;Сохраняем содержимое ячеек, которые
;могут быть испорчены:
LD A,(#5D0E)
LD (ST1+1),A
LD A,(#5D0C)
LD (ST2+1),A
LD A,(#5CB6)
LD (ST3+1),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+1),HL
LD HL,(#5D1C)
LD (ST8+1),HL
LD HL,(#5CF8)
LD (ST9+1),HL
;Устанавливаем содержимое некоторых
;ячеек для правильной работы:
LD A,#FF
LD (#5D0C),A
LD (#5D1F),A
DEC A
LD (#5D0E),A
LD A,#F4
LD (#5CB6),A
LD HL,S_SPEC
LD (#5D1A),HL
LD HL,0
ADD HL,SP
LD DE,-12
ADD HL,DE
LD (#5D1C),HL
LD A,0 ;0 в регистр
LD C,#3F ;дорожки.
CALL TO_WG93
LD A,#0A ;#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,0
LD (#5D0C),A
ST3 LD A,0
LD (#5CB6),A
ST4 LD A,0
LD (#5D1F),A
ST5 LD A,0
LD (#5C3A),A
ST6 LD A,0
LD (#5D17),A
ST7 LD HL,0
LD (#5D1A),HL
ST8 LD HL,0
LD (#5D1C),HL
ST9 LD HL,0
LD (#5CF8),HL
LD A,B
RET
;Сюда будет передано управление, если
;0-й бит регистра состояния равен 1:
S_SPEC POP BC ;Содержимое порта.
LD HL,(#5D1C)
LD DE,12 ;Восстанавливаем
ADD HL,DE ;указатель
LD SP,HL ;стека.
JR RG_D
TO_WG93 LD IX,#2A53
TO_DOS PUSH IX
JP #3D2F
Я думаю, нет лучшего способа объяснить, как работает эта процедура, чем
показать процесс её выполнения, начиная с момента вызова подпрограммы по
адресу #3F33. При этом выберем именно такой случай, когда
#3F33: IN A,(#1F) ;Допустим, считано #FF.
LD B,A
AND #7F
RET Z ;Не выходим.
LD HL,#29D8
AND #40 ;Проверяем 4-й бит.
JR NZ,#3F4B ;Условие выполняется.
#3F4B: LD A,#D0
OUT (#1F),A
LD A,B
AND #1 ;Проверяем 0-й бит.
JP NZ,#3EE7 ;Условие выполняется.
#3EE7: CALL #272B
#272B: LD A,#1A
JR #2731
#2731: LD (#5C3A),A
RET
#3EEA: LD A,#FF
LD (#5D17),A
JP #271B
#271B: LD HL,#27FC
LD A,#06
JP #1C4A
#1C4A: CALL #03C3
#03C3: PUSH AF
LD A,(#5D0E) ;В эту ячейку
;перед запуском
;занесли #FE.
CP #FE
JR NZ,#03CD ;Условие не выполняется.
POP AF
RET
#1C4D: JP #01D3
#01D3: LD HL,0
LD (#5CF8),HL
CALL #20E5
#20E5: PUSH AF
LD A,(#5CF8) ;Там 0.
CP #FF
JR Z,#211C ;Условие не выполняется.
POP AF
CALL #2970
#2970: PUSH HL
PUSH DE
PUSH BC
Как видим, здесь содержимое BC (где находится столь нужный нам регистр состояния) заносится в стек. В моей процедуре этот факт используется для последующего восстановления BC.
PUSH AF
LD HL,#5D0C ;В эту ячейку
;перед запуском
;занесли #FF.
LD A,(HL)
OR A
JR NZ,#2992 ;Условие выполняется.
#2992: POP AF
POP BC
Содержимое BC извлечено из стека, но в памяти, отведённой под стек, оно осталось. Его можно извлечь оттуда, лишь бы оно не было затёрто записываемыми в стек значениями. Процедура обработки прерываний затрёт значение BC, а потому прерывания запрещены.
POP DE
POP HL
RET
#20F1: PUSH AF
LD A,(#5CB6) ;В эту ячейку
;перед запуском
;занесли #F4.
CP #F4
JR Z,#211C ;Условие выполняется.
#211C: POP AF
RET
#01DC: CALL #1D63
#1D63: LD HL,#5D0E ;В эту ячейку
;перед запуском
;занесли #FE.
LD A,(HL)
CP #FF
LD (HL),0
RET NZ ;Условие выполняется.
#01DF: LD HL,#5D17
LD (HL),#AA
LD HL,#5D1F ;В эту ячейку
;перед запуском
;занесли #FF.
LD A,(HL)
OR A
LD (HL),0
JR NZ,#01F3 ;Условие выполняется.
#01F3: LD SP,(#5D1C) ;Значения этих ячеек
LD HL,(#5D1A) ;подготовлены заранее.
LD BC,(#5D0F) ;Теперь BC осталось
;только в стеке...
LD B,0
JP (HL)
При выходе управление передаётся на метку S_SPEC, а SP указывает на то
самое значение регистра состояния, которое
|
Другие мои статьи о
|