|
ZX Club
#06
31 декабря 1997 |
|
Soft group - Драйвер ввода в режимах последовательного и прямого доступа из файлов системы TR-DOS. Как использовать драйвер.

Борис Курицын,
ассистент кафедры вычислительной техники
Черкасского инженерно-технологического
института, аспирант
────────────────────────────────────────
257010 Черкассы, а/я 1529
ДРАЙВЕР ВВОДА В РЕЖИМАХ
ПОСЛЕДОВАТЕЛЬНОГО И ПРЯМОГО ДОСТУПА
ИЗ ФАЙЛОВ СИСТЕМЫ TR-DOS
СОДЕРЖАНИЕ
Введение..................................
Вызов системных функций TR-DOS............
Обработка исключений......................
Драйвер потокового ввода/вывода
в/из файлов TR-DOS........................
Использование драйвера из Assembler.......
Интерфейс Basic...........................
Литература................................
Автор написал этот драйвер (вернее,
его первую часть) как базовый элемент ком-
пилятора языка высокого уровня. Но он яв-
ляется совершенно автономной программой,
которую напрямую могут использовать те,
кто пишет на ассемблере, а через разрабо-
танный расширитель Бейсика -- и те, кто
программирует на Бейсике.
Кроме того, анализ ее познакомит чи-
тателя с программированием драйверов, ра-
ботой каналов и потоков, принципами напи-
сания расширителей Бейсика, основами син-
таксического анализа, с методами расшире-
ния множества ошибок Бейсика.
В чем цель написания этого драйвера?
Есть класс программ, которые одновремено
должны работать с большим количеством фай-
лов (и по вводу, и по выводу) и сами при
этом занимают достаточный объем памяти,
так что об одновременной загрузке всех
требуемых файлов не может быть и речи.
Открывая системный поток на каждый файл,
они могут работать через этот драйвер даже
с 10 файлами длиной, скажем, по 60 КБ од-
новременно, при расходе памяти около 290
байт на файл. По функциям драйвер близок к
операторам TR-DOS для работы с файлами
прямого/последовательного доступа, но ра-
ботает с CODE-файлам и с аналогичными фай-
лами с нестандартными типами.
Кроме того, как показал "отец всех
программистов" Никлаус Вирт (автор языков
Паскаль, Модула-2), программа тогда прос-
та, корректна и надежна, когда СТРУКТУРА
ПРОГРАММЫ СООТВЕТСТВУЕТ СТРУКТУРЕ ДАННЫХ.
Чтобы реализовать этот принцип, нужны
соответствующие средства. Одним из них
есть этот драйвер, поскольку он позволяет
программе работать только с "абстрактным"
аспектом данных, т. е. в терминах "сим-
вол", "позиция считывания".
Сначала о технических моментах. Автор
предпочитает ассемблер ZX Turbo Assembler
v2.4 и листинг приведен в его формате.
Надеюсь, у пользователей других ассембле-
ров не возникнет проблем с переносом тек-
стов программ.
Автор, при написании программ, ис-
пользует два вспомогательных модуля:
модуль доступа к TR-DOS
и
модуль перехвата системных ошибок.
Модуль TR-DOS очень прост. Это аналог
системного вызова #3D13, который ошибки
TR-DOS преобразует в исключения (вызовы
RST 8) с кодами 100-112, что избавляет от
необходимости каждый раз проверять код
ошибки системы. Кроме того, сам вызов де-
лается через вектор (dosVector), что поз-
воляет "навешивать" на вызов TR-DOS допол-
нительные функции прямо во время работы
программы заменой значения dosVector на
адрес своего дополнительного обработчика.
; ┌────────────────────────────────────┐
; │Вызов системных функций TR-DOS v5.0x│
; │ │
; │ (c) Б. Курицын, июнь 1996 │
; │ │
; │ Файл: dosapi.a │
; │ Формат файла: ZX ТASM │
; └────────────────────────────────────┘
; исключение "ошибка TR-DOS"
excDosError equ 99
; ошибки TR-DOS
dNoFile equ 1
dFileExists equ 2
dNoSpace equ 3
dDirFull equ 4
dRecOF equ 5
dNoDisk equ 6
dStrmOpened equ 10
dNotDskFile equ 11
dVfyError equ 13
dos db #c3 ; это jp (dosVector)
dosVector
dw dosIntr
; вызов интерпретатора функций DOS
dosIntr push af
ld a,c
ld (_fncNo),a
xor a
ld (23823),a
ld (23824),a
pop af
call #3d13
push af
ld a,(_fncNo)
сp #a ;если исполнялась функция 10
;(поиск файла),
jr z,dosOk ; код возврата - это не
; код ошибки
ld a,(23823)
or a
jr z,dosOk ;если была ошибка, ини-
;циировать исключение
; инициация исключения DOS
dosError
add a,excDosError
ld (_errNo),a
rst 8
_errNo db 0
_fncNo db 0
dosOk pop af
ret
Интересующиеся обработкой исключений
могут обратиться к статье [1]. Здесь же
скажу кратко: исключения - это чрезвычайно
удобная технология программирования, кото-
рая позволяет осуществлять неявные перехо-
ды к обработке ошибок или исключительных
ситуаций без необходимости постоянных про-
верок флагов, кодов возврата и т.п. При
этом обеспечивается сохранение состояния
некоторых элементов системы и программы
(как минимум, стека). Приведенный ниже мо-
дуль поддерживает многоуровненую обработку
исключений с возможностью динамического
изменения множества сохраняемых элементов
и обработчика исключений. Интересующиеся
могут просто проанализировать текст прог-
раммы. Кратко же можно сказать так: если в
начале какой-либо программы вызвать try
(см. листинг) с указанием адреса Вашей
подпрограммы обработки ошибок, то, в слу-
чае вызова RST 8 где-либо внутри програм-
мы, управление передастся на Ваш обработ-
чик. Обработчик должен первым делом выз-
вать endTry для восстановления стека и
снятия себя же. Тот же вызов должен де-
латься перед командой RET программы.
; ┌────────────────────────────┐
; │ Exceptions handle │
; │ Обработка исключений│
; │(c) 1994-96 Б. Курицын │
; │версия: 2.0 от 18/06/96 │
; │файл: except.a │
; └────────────────────────────┘
err_SP equ 23613
; Внешние обращения
try db #C3 ;это JP (tryAd)
tryAd dw mark
endTry db #C3
eTryAd dw unmark
;Продолжить текущее исключение
;(вызов только из обработчика исключения
;командой JP)
;новое исключение, код в A
putErr ld (iy),a
;то же исключение
contErr ld sp,(err_SP)
ret
;Изменить маркировку стека
;вход: @HL-новый обработчик Try, @DE-новый
;обработчик EndTry
;выход:@HL-старый обработчик Try, @DE-ста-
;рый обработчик EndTry
chMarking ld bc,(tryAd)
ld (tryAd),hl
ld h,b
ld l,c
ld bc,(eTryAd)
ld (eTryAd),de
ld d,b
ld e,c
ret
; РЕАЛИЗАЦИЯ
;Маркировать стек, вход: @DE-обработчик
;исключений
;разрушаются: HL,IX
mark POP IX
LD HL,(err_SP)
PUSH HL
PUSH DE
LD (err_SP),SP
JP (IX)
;Демаркировать стек, разрушаются: HL,IX
unmark POP IX
LD SP,(err_SP)
POP HL
POP HL
LD (err_SP),HL
JP (IX)
Необходимо оговориться, что те, кто
не знает, что такое "канал" и "поток", пе-
ред продолжением чтения этой статьи должны
почитать главу по каналам и потокам в зна-
менитом трехтомнике Инфоркома по програм-
мированию для ZX Spectrum или приложение к
инструкции пользователя по текстовому ре-
дактору BK Write, распостраняемому авто-
ром.
А теперь о самом драйвере. Какой на-
бор функций он должен выполнять?
Во-первых, это открытие файла: созда-
ние канала, связанного с файлом и подклю-
чение какого-либо потока к этому каналу.
Во-вторых, драйвер дожен позволять
считывать байты файла не только последова-
тельно, но и переставлять указатель считы-
вания сразу на любую позицию в файле.
В-третьих, это закрытие файла: разру-
шение канала, закрытие потока.
А как же, собственно, ввод информа-
ции, спросите Вы?
А очень просто - системной процедурой
WaitKey (адрес #15D4).
Далее мы рассмотрим комментированный
текст драйвера (примечание: в драйвере
есть моменты, связанные с выводом в файл -
эта часть драйвера еще не закончена, кроме
того, включить ее в статью не представ-
ляется возможным из-за большого объема).
Сразу же обратимся к основной идее
драйвера: нужно выдать байт файла, смеще-
ние которого в файле определяется текущей
позицией. Позиция может быть произвольно
изменена в любой момент. При считывании
байта указатель позиции перемещается на
следующий байт. При попытке считать
что-либо за концом файла генерируется
соответствующее исключение.
Для реализации этого в ракорде канала
предусмотрено место для одного сектора
файла. В любой момент в этот буфер загру-
жен сектор, соответствующий текущей пози-
ции в файле. При любом изменении позиции
номер сектора контролируется, и, если на-
до, сектор перегружается.
; ****************************************
; * *
; * DISK STREAMABLE I/O DRIVER v1.05 *
; * *
; * Драйвер потокового ввода/вывода *
; * в/из файлов TR-DOS. *
; * *
; * (c) Б. Курицын, июнь 1996 *
; * *
; * Файл: stream.a *
; * Формат файла: ZX Тurbo Assembler *
; ****************************************
include "dosapi.a"
; длина сектора
sectLen equ 256
; системные переменные
chans equ 23631 ; указатель на область
; каналов
curChl equ 23633 ; указатель на текущий
; канал
prog equ 23635 ; указатель на прог-
;рамму (за конец области каналов)
; системные переменные TR-DOS
dfDrive equ #5d19 ;дисковод по умолчанию
searchCh equ 23814 ;кол-во символов для
;поиска имени
; системные прoцедуры,
; подробности относительно процедур
;смотри в [2]
makeRoom equ #1655
reclaim2 equ #19E8
strData equ #171E+3
chOpen equ #1601
callJp equ #162C
; Канал ввода и вывода имеет имя "F",
;чтобы их можно было различать,
;устанавливаются сигнатуры ракордов:
readSign equ #5678
writeSign equ #6789
; генерация исключений
invStream
rst 8
db 23 ; "неверно используется
; поток"
invFileName
rst 8
db 14 ; "неправильное имя файла"
invDevice
rst 8
db 18 ; "неправильное устройство"
endOfFile
rst 8
db 7 ; "конец файла"
; смещения в ракорде; относительно
; аналогов структуры ракорда смотри [3]
oPrint equ 0 ; адрес процедуры вывода
oInput equ 2 ; адрес процедуры ввода
oName equ 4 ; имя канала
oSign equ 5 ; сигнатура канала
oClose equ 7 ; адрес процедуры закрытия
; файла
oLength equ 9 ; длина ракорда
oIntern equ 11 ; дополнительная информация
; образ ракорда канала для чтения
readRecord
DW invStream
DW inputIn
DB "F"
DW readSign, noOp
DW 30+sectLen; длина ракорда
DB 0; устройство
; (смещение oIntern+0)
DB " C"
DS 7; элемент каталога (...+1)
DW #FFFF; позиция в файле (...+17)
;здесь: DS sectLen - буфер сектора
;файла (...+19)
rdRecLen equ $-readRecord+sectLen
; Процедура ввода символа из файла;
; очередной байт файла возвращается в A.
; Байт возвращается всегда, поэтому
; состояния INKEY$#5 = "" не бывает.
inputIn ld ix,(curChl) ; адрес ракорда
ld l,(ix+oIntern+17) ; текущая
; позиция в файле
ld h,(ix+oIntern+18)
ld c,l
ld b,0
add ix,bc
ld a,(ix+oIntern+19) ; выборка
; байта из текущего сектора
scf ; флаг: байт получен
; с устройства
push af
inc hl
call _seekRead ; переход на
; следующую позицию
pop af
noOp ret
;Позиционирование в текущем файле
;для чтения
;вход: HL-позиция
;---внешняя версия; проверяет, что текущий
; канал -- F и он открыт для чтения, если
; нет - неверный поток)
seekRead
push hl
call testChl
ld l,(ix+oSign)
ld h,(ix+oSign+1)
ld bc,readSign
and a
sbc hl,bc
jp nz,invStream
pop hl
;---внутренняя версия
_seekRead
ld ix,(curChl)
;проверить, не достигнут ли конец файла
ld c,l
ld b,h
ld e,(ix+oIntern+12) ;сравнить с
;длиной файла
ld d,(ix+oIntern+13)
and a
sbc hl,de
;позиция точно за последним байтом файла,
;буфер не перегружается
jr z,atEOF
jp nc,endOfFile
;установить новую позицию
ld a,b
cp (ix+oIntern+18) ; в этом же
; секторе?
atEOF ld (ix+oIntern+17),c
ld (ix+oIntern+18),b
ret z ; да - заканчиваем
;нет - новая позиция соответствует байту
;из другого сектора файла, загрузить новый
;сектор в буфер
ld l,(ix+oIntern+15)
ld h,(ix+oIntern+16) ; позиция
; начала файла на диске
call makeLog ; преобразование
; в логический сектор
ld e,b
ld d,0
add hl,de ;это логический номер
;сектора,
;который нужно загрузить
call makePhis ;обратное
;преобразование
ex de,hl
push ix
pop hl
ld bc,oIntern+19
add hl,bc
push hl
push de
ld a,(ix+oIntern)
ld c,1
call dos ; выбор дисковода
pop de
pop hl
ld bc,#105 ; загрузка сектора
jp dos
; Преобразовать параметры
; "сектор L - дорожка H"
; в "логический сектор HL"
; ЗАМЕЧАНИЕ: преобразование
; в "логический сектор" позволяет легко
; рассчитывать положение сектора на диске,
; так как логические сектора можно
; складывать и отнимать как обычные числа.
; Формула преобразования:
; лог. сектор = дорожка * 16 + сектор
makeLog xor a
srl h
rr a
srl h
rr a
srl h
rr a
srl h
rr a
or l
ld l,a
ret
; Преобразовать параметры
; "логический сектор HL"
; в "сектор L - дорожка H"
makePhis
ld a,l
sla a
rl h
sla a
rl h
sla a
rl h
sla a
rl h
ld a,#F
and l
ld l,a
ret
; Тип файла для открытия; изменяя это
; значение перед вызовом openRead
; можно открывать нестандартные файлы
fileType db "C"
; Открытие файла для чтения;
; вход:
; A-номер потока, @DE/BC- строка имени
; (как обычно: адрес DE, длина BC)
; Указанный поток будет открыт на файл.
openRead
exx
ld (stream),a ; проверяем, что
; указанный поток закрыт
call strData
ld a,b
or c
ld a,dStrmOpened ; если нет -
; ошибка DOS
; "поток уже открыт"
jp nz,dosError
call newRdRecord ; создаем ракорд
; канала
ld (record),hl
push hl
exx
pop hl
call parseName ; синтаксический
; разбор имени
ld hl,(record)
inc hl
ld bc,(chans) ; находим смещение
; ракорда в (Chans)...
and a
sbc hl,bc
push hl
ld a,(stream)
call strData
pop bc
ld (hl),c ; ...и открываем
; затребованный поток на канал
inc hl
ld (hl),b
ld ix,(record)
ld a,(ix+oIntern)
ld c,1
call dos ; выбор дисковода
ld c,#18
call dos ; настройка на дискету
ld hl,(record)
ld bc,oIntern+1
add hl,bc
push hl
ld c,#13
call dos
ld hl,searchCh
ld (hl),9
ld c,#a
call dos ; поиск файла, который
; открываем
bit 7,c
jr nz,noFile ; если файла нет -
; все вернуть в исходное состояние
ld a,c
ld c,8
call dos ; если файл есть,
; получаем его элемент каталога
pop hl
ld c,#14
call dos ; и копируем его в ракорд
; канала
ld a,(stream)
call chOpen
ld hl,0
jp _seekRead ; устанавливаем
; позицию на начало файла
; при отсутствии файла все возвращается в
; начальное состояние
noFile ld ix,(record)
call freeRecord ; удаляем ракорд
; канала
ld a,(stream)
call strData
ld (hl),0 ; закрываем поток
inc hl
ld (hl),0
ld a,dNoFile ; выдаем ошибку DOS
; "нет такого файла"
jp dosError
stream db 0
record dw 0
;Синтаксический анализ имени файла
;в виде "[дисковод:]имя". Если дисковод
;не указан, берется дисковод по умолчанию.
;Имя и дисковод переносятся в область
;oIntern ракорда.
;вход: HL-адрес ракорда открываемого
;канала, @DE/BC- строка имени
parseName
push hl
pop ix
ld a,b ; пустая строка имени -
; ошибка
or c
jp z,invFileName
ld a,(dfDrive) ; пока что дисковод
; по умолчанию
ld (ix+oIntern),a
ld a,(fileType) ; устанавливаем
; тип файла
ld (ix+oIntern+9),a
ld hl,-3 ; имя длинее
; трех символов?
and a
adc hl,bc
jp m,nameOnly ; нет - значит,
; дисковод в имени точно не указан
inc de ; проверяем относительно
; дисковода...
ld a,(de)
dec de
cp ":" ; если второй символ -
; двоеточие...
jr nz,nameOnly
ld a,(de) ;...то первый - дисковод
res 5,a ; приводим к верхнему
; регистру
sub "A" ; отделяем правильные
; имена: A,B,C,D.
jp c,invDevice
cp 4
jp nc,invDevice
ld (ix+oIntern),a ; дисковод
; найден и установлен в ракорде
inc de ; смещаемся
; к имени... (+2 символа)
inc de
dec bc
dec bc
nameOnly
ld a,b
or a
jp nz,invFileName
ld a,c ; если имя длинее
; 8 символов - ошибка
cp 9
jp nc,invFileName
ld b,c ; если все в порядке -
; копируем имя в ракорд
copyName
ld a,(de)
inc de
ld (ix+oIntern+1),a
inc ix
djnz copyName
ret
; Создание ракорда канала чтения
; выход: HL-адрес созданного ракорда
newRdRecord
ld hl,(prog) ; в конце области
; Chans...
dec hl
ld bc,rdRecLen ; ...выделяем память
; для ракорда...
call makeRoom
inc hl
push hl
ex de,hl
ld hl,readRecord ;...и копируем
;туда образ ракорда
ld bc,rdRecLen-sectLen
ldir
pop hl
ret
; Удаление ракорда канала чтения/записи
; вход: IX-адрес ракорда
freeRecord
ld c,(ix+oLength) ; из ракорда
; получаем его длину...
ld b,(ix+oLength+1)
ld (recLen),bc
push ix
pop hl
jp reclaim2 ;...и освобождаем
;область, им занимаемую
recLen dw 0
; Проверить, что текущий канал -
; это канал "F"
; выход: IX=(curChl)
testChl
ld ix,(curChl) ; если имя текущего
; канала...
ld a,"F"
cp (ix+oName) ; ...не "F"...
ld a,dNotDskFile
jp nz,dosError ; ...то ошибка DOS
; "не дисковый файл"
ret
; Закрыть файл чтения/записи потока A
close push af
call chOpen ; делаем канал текущим
call testChl ; проверим, что этот
; поток связан с нашим каналом
; выполнить процедуру закрытия канала
ld l,(ix+oClose)
ld h,(ix+oClose+1)
call callJp ; переход к исполнению
;освободить память
ld ix,(curChl)
call freeRecord ; удаляем текущий
; канал из памяти
;закрыть поток
pop af
call strData
ld (hl),0 ; значение 0 - поток закрыт
inc hl
ld (hl),0
; Скорректировать переменные Streams для
; потоков 0-15.
; Дело в том, что после открытия этого ка-
; нала могли быть открыты и другие. Их ра-
; корды расположены в памяти после нашего
; ракорда.
; С удалением нашего ракорда смещения в
; области Streams на указанные каналы ста-
; ли недействительными, и их нужно скор-
; ректировать на значение длины удаленного
; ракорда.
ld d,b ; смещение на уже удаленный
; ракорд
ld e,c
ld b,16 ; просматриваем 16 потоков
strmLoop
push bc
ld a,b
dec a
call strData ; выбираем значение для
; потока
push hl
pop ix
ld h,d
ld l,e
and a
sbc hl,bc ; если его смещение
; не больше нашего...
jr nc,noCorrt ; ...корректировка
; не требуется
ld h,b
ld l,c
ld bc,(recLen) ; иначе уменьшаем
; смещение на длину ракорда
and a
sbc hl,bc
ld (ix),l ; и сохраняем
ld (ix+1),h
noCorrt
pop bc
djnz strmLoop ;цикл для всех потоков
ret
Итак, драйвер написан. А как его ис-
пользовать? Элементарно. Сначала какой-ли-
бо свободный поток (скажем, #5) открываем
на файл:
org 60000
jp start
include "stream.a" ; включение
; драйвера...
include "except.a" ; ...и обра-
; ботки исключений
name db "textfile" ;определение имени
;файла...
nameLen equ $-name ; ...и его длины
start ld de, name
ld bc, nameLen
ld a, 5
call openRead
Файл открыт. Теперь, например, напе-
чатаем его на экране. Будем посимвольно
считывать файл и передавать байты каналу
"S". Поскольку процесс закончится исключе-
нием "конец файла", перехватываем исключе-
ния:
ld de, endOfPrint ; адрес
; обработчика
call try
Теперь можно выводить файл в "беско-
нечном" цикле.
waitKey equ #15D4
process ld a, 5 ; поток файла
call chOpen ; chOpen = #1601
call waitKey ; вводим символ
push af
ld a,2 ; поток экрана
call chOpen
pop af
rst 16 ; выводим символ
jr process
По окончании файла или по другому ис-
ключению, управление будет передано сюда:
endOfPrint
call endTry ; снимаем обработчик
ld a, 7
cp (iy) ; текущая ошибка -
; это "конец файла"?
jp nz, contErr ; если нет -
; продолжаем ее распостранение, если да -
; все в порядке, вывод закончен
ld a, 5
call close ; закрываем
; дисковый файл
ret
А как же с Бейсиком? Хотелось бы иметь
возможность открывать файлы прямо операто-
рами Бейсика.
Нет ничего проще. Приведенный ниже
интерфейс Бейсика к драйверу реализует это
и еще кое-что: он преобразует новые ДОСов-
ские исключения в новые ошибки Бейсика,
которые выводятся так же, как и обычные.
Кроме того, он выводит статус потоков сис-
темы на экран или в любой другой поток.
Новые операторы Бейсика выглядят так:
LET d=64000
REM открытие <потока> на файл <имя_файла>
RANDOMIZE USR d: OPEN #поток, имя_файла$
REM закрытие дискового файла, связанного
с <потоком>
RANDOMIZE USR d: CLOSE #поток
REM переход к <позиции> в файле,
связанном с <потоком>
PRINT #поток; : RANDOMIZE USR d
GO TO позиция
REM вывод информации
о состоянии потоков в <поток>
REM для вывода на экран поток = 2
RANDOMIZE USR d: LIST #поток
Небольшое замечание относительно опе-
ратора GO TO: оператор PRINT, который Вы
видите впереди, только делает поток теку-
щим, так как GO TO работает с текущим по-
током. Обратите внимание на ";" в конце
оператора PRINT.
Оператор LIST выводит список всех от-
крытых потоков с указанием каналов, на ко-
торые они открыты.
Непосредственно ввод из файла осущест-
вляется обычным образом: оператором
INPUT #поток; симв_переменная$
для ввода строки символов, или оператором
LET симв_переменная$ = INKEY$ #поток
для ввода одного символа.
Второй способ предпочтительнее, так
как в системе ZX Spectrum есть такая осо-
бенность: предполагается, что оператором
INPUT данные вводятся только из потоков,
связанных с клавиатурой, и звук клавиатуры
обрабатывается в операторе INPUT. Поэтому
при вводе из файла Вы будете слышать пос-
ледовательности писков "нажатия на клави-
шу" при вводе каждого символа. Кроме того,
ввод строки завершится только по получении
символа с кодом клавиши <Enter> (#0D) и
сам этот символ в строку включен не будет.
В тексте программы хорошо видно, как
расширитель Бейсика не только выполняет
действия, но и анализирует синтасис прог-
раммы.
;*****************************************
;* *
;* BASIC INTERFACE TO *
;* DISK STREAMABLE I/O DRIVER, v1.05 *
;* *
;* Интерфейс Бейсика к *
;* Драйверу потокового ввода/вывода *
;* в/из файлов TR-DOS. *
;* *
;* (c) Б. Курицын, июнь 1996 *
;* *
;* Файл: basint.a *
;* Формат файла: ZX Тurbo Assembler *
;*****************************************
; коды токенов
t_open equ #d3
t_close equ #d4
t_goto equ #ec
t_list equ #f0
; системные переменные (или смещения)
oFlags equ 1
oTVFlag equ 2
oFlags2 equ 48
oFlagX equ 55
oXPtrHi equ 37
defAdd equ 23563
strms_6 equ 23574
; Используемые п/п ПЗУ.
; Подробнее смотрите в [2].
class06 equ #1C82 ; синтакс. анализатор
; - должно быть
; числовое выражение
class0A equ #1C8C ; синтакс. анализатор
; - должна быть
; строка символов
separator equ #1B6F ; синтакс. анализатор
; - должна быть
; лексема
stkToA equ #1E94 ; стек калькулятора -
; в A
stkToBC equ #1E99 ; стек калькулятора -
; в BC
stkFetch equ #2BF1 ; стек калькулятора -
; в A/BC/DE
clsLower equ #0D6E
setMin equ #16B0
copyBuff equ #0ECD
stackA equ #2D28 ; A - на стек
; калькулятора
printFP equ #2DE3 ; напечатать число со
; стека калькулятора
po_mess equ #0C0A ; вывести сообщение A
; из таблицы (DE)
org 64000
; точка входа
startUp ld de,newErrors ; новый
; обработчик иссключений
call try
ld c,":" ; это разделитель
; после USR 64000
; Separator проверяет, что текущий интер-
; претируенмый символ именно таков.
; Если нет - ошибка "нонсенс в Бейсике"
call separator
ld c,a
rst #20 ;берем имя оператора...
ld hl,statms ; ...и ищем его
; в таблице Statms
jr compare
nextSearch inc hl
inc hl
compare ld a,(hl)
inc hl
or a
jr nz,cont_comp ; если таблица
; кончилась - ошибка
rst 8
db #b; Nonsense in Basic
cont_comp cp c
jr nz,nextSearch
ld a,(hl) ; найдено
; выбираем адрес обработчика...
inc hl
ld h,(hl)
ld l,a
call callJp ;...и запускаем его
call endTry ;снимаем обработчик
; исключений
ret
; Таблица описания операторов.
; Формат: токен, адрес обработчика,
; токен, адрес обработчика,...,0.
statms db t_open
dw _open
db t_close
dw _close
db t_goto
dw _goto
db t_list
dw _list
db 0
listStrm db 0
; Тексты для опреатора LIST
listMess db #80,"Streams status:",13+#80
db ": opened to ",34+#80
db 34,13+#80
; Оператор LIST #stream
_list ld c,"#" ; проверка символа
; потока
call separator
call class06 ; должно следовать
; числовое выражение
call stkToA ; его -
; в аккумулятор
call chOpen ; открываем канал
; для вывода
ld de,listMess
sub a
ld (listStrm),a
call po_mess ; вывод заголовка
listNext ld a,(listStrm) ; для потока...
call strData ;... выбираются
;данные из Streams
ld a,b
or c
jr z,listCont ; если закрыт -
; следующий поток
ld a,"#" ; печать символа
; потока...
push bc
rst 16
ld a,(listStrm)
call stackA
call printFP ; ... и его номера
pop bc
ld hl,(chans)
add hl,bc
inc hl
inc hl
inc hl ;определение адреса
;смещения oName в канале
push hl
ld a,1
ld de,listMess
call po_mess ;сообщение
;"открыт на...
pop hl
ld a,(hl)
rst 16 ; ...такой-то канал"
ld a,2
ld de,listMess
call po_mess
listCont ld hl,listStrm
inc (hl) ; следующий поток
ld a,16
cp (hl)
jr nz,listNext ; переход к
; следующему потоку
ret
;Оператор OPEN #stream, name$ -
;без комментариев
_open exx
push hl
call openParams
call openRead
pop hl
exx
ret
; синтаксический анализ оператора OPEN
openParams call class06 ;должен быть номер
;потока - на стек
; калькулятора
ld c,","
call separator ; потом запятая
call class0A ; затем символьное
; выражение -
; имя файла
rst #28 ; обменять имя и поток
; местами на стеке
db 1,#38; exchange
call stkToA ; поток -
; в аккумулятор
push af
call stkFetch ; имя - в DE/BC
pop af
ret
; Оператор CLOSE #stream
_close call class06 ;должен быть номер
;потока - на стек
; калькулятора
call stkToA ; поток -
; в аккумулятор
jp close
; Оператор GO TO pos
_goto call class06 ;число - позиция
;в файле
call stkToBC ;со стека
;калькулятора в BC
ld h,b
ld l,c
jp seekRead
; Обработчик ошибок:
; визуализирует новые ошибки
newErrors call endTry ; снять обработчик
halt
ld a,(iy) ; код ошибки
cp excDosError+dNoFile
jp m,contErr ; обычные ошибки
; распостраняются дальше
;Эта часть программы практически повторяет
;действия стандартного обработчика ошибок
;из ПЗУ до вывода сообщения об ошибке.
;Смотрите [3] с адреса #1303
res 5,(iy+oFlags)
bit 1,(iy+oFlags2)
call nz,copyBuff
ld hl,0
ld (iy+oFlagX),h
ld (iy+oXPtrHi),h
ld (defAdd),hl
ld hl,1
ld (strms_6),hl
call setMin
res 5,(iy+oFlagX)
call clsLower
set 5,(iy+oTVFlag)
ld a,(iy)
sub excDosError+dNoFile
ld b,a
add a,"a" ;новые коды сообщения
rst 16
ld a," " ; пробел
rst 16
ld a,b
ld de,messages ;новые сообщения
;в этой таблице
ld (iy),#FF
jp #1346 ; дальнейшая обработка
; стандартная - в ПЗУ
;Таблица текстов сообщений о новых ошибках
;(в формате процедуры PO_MESS)
messages db #80,"No fil","e"+#80
db "File exist","s"+#80
db "Disk ful","l"+#80
db "Dir ful","l"+#80
db "RecNo overflo","w"+#80
db "No dis","k"+#80
db "(7) DOS erro","r"+#80
db "(8) DOS erro","r"+#80
db "(9) DOS erro","r"+#80
db "Stream opene","d"+#80
db "Not disk fil","e"+#80
db "(12) DOS erro","r"+#80
db "Verify erro","r"+#80
;Включение драйвера и обработки исключений
include "stream.a"
include "except.a"
; *** end of basint.a ***
Надеюсь, что мои краткие (из-за боль-
шого объема ассемблерного текста) коммен-
тарии дали хотя бы общее представление о
работе программы, а сама программа будет
полезна Вам.
При возникновении проблем или спе-
циальных вопросов, Вы можете написать ав-
тору по адресу, указанному выше.
ЛИТЕРАТУРА
1. Б. Курицын. Перехват системных ошибок
при программировании на ассемблере для
компьютеров "ZX Spectrum".: Радиолюбитель,
10'1994; стр. 12 - Минск, 1994.
2. Логан, о'Хара. Полное описание ПЗУ ZX
Spectrum.: "Программ-Асс" - Харьков, 1992.
3. К.Курылович, Д.Мадей, К.Марасек. Путе-
водитель по ZX Spectrum.: "Программ-Асс" -
Харьков, 1992.
Другие статьи номера:
Похожие статьи:
В этот день... 1 ноября
Dnieprobite #03,
ACNews #13,
ZX Time #10,
Echo #07,
Funeral #1.5,
Info Guide #02,
ZX Guide #02,
Plutonium #14,
Crossroads #07,
ZX Club #09,
Black Crow #02,
Spectrum Expert #01,
C-Net Week #03,
Maximum #46,
Review #01,
Anigdot #46,
Nicron #05,
Spectrum Land #02,
Crysral Dream #01,
Platinum #02,
Oberon #02,
Echo #01,
Emulate #03,
ZX Format #01,
Speccy #02,
ZX Panorama #01