esxDOS для самых маленьких. Чтение и запись файлов
Введение
Официальной информации по API esxDOS в принципе не существует, а русскоязычной информации и неофициальной я не нашел.
Система же уже достаточно активно заняла свои позиции(как в виде фирмвари для divMMC/divIDE), так и как часть многих компьютеров(Карабас, ZXUno, Reverse, Spectrum Next и многие другие).
API хорошо работает и с IM2(можно подгружать файлы под музыку), работает почти на всем, что можно и достаточно легко в освоении.
В этой статье я опишу минимальное необходимое для жизни API, которое позволит создавать вам приложения под эту систему.
Что ж начнем!
Вся работа с esxDOS на уровне API очень похожа на работу с POSIX API и идет через RST 8.
Вызов выглядит примерное так:
<устанавливаем значения регистров>
RST 8
db <Номер вызова>
Если же при выполнении нашей операции произошла ошибка — будет установлен флаг C(а код ошибки будет записан в системные переменные бейсика, но в 90% случаем нам достаточно просто знать, что случилась ошибка).
Попробуем считать файл
Для этого нам нужны будут следующие методы API: fopen, fread, fclose и getsetdrv(получение либо установка текущего устройства).
Пока что выглядит знакомо для любого человека, что писал под современные компьютеры.
Константы для вызова этих методов будут следующие:
ESX_GETSETDRV = #89
ESX_FOPEN = #9A
ESX_FCLOSE = #9B
ESX_FREAD = #9D
А так же для метода fopen необходимо будет указать режим работы с файлом(режимы могут объединяться через ИЛИ):
FMODE_READ = #01 ; Чтение
FMODE_WRITE = #06 ; Запись, будет ошибка, если файла нет
FMODE_CREATE = #0E ; Создание файла, если уже существовал - будет перезаписан
Метод API fopen принимает следующие параметры:
- A — текущее устройство
- B — Режим(чтение/запись/создание)
- IX — указатель на строчку именем файла(заканчивается 0), можно указывать как просто имя файла, так и с путем(в т.ч. относительным)
Номер открытого файла будет в регистре А — его нужно будет запомнить для дальнейшей работы.
Для получения текущего устройства необходимо вызвать метод getsetdrv, указав ему в качестве параметра в регистре А — нуль.
Я не люблю передавать параметры функциям через IX, поэтому в своей обертке я принимаю имя файла в HL.
И так файл мы можем открыть следующим кодом:
; Returns:
; A - current drive
getDefaultDrive:
ld a, 0 : rst #8
defb ESX_GETSETDRV
ret
; Opens file on default drive
; B - File mode
; HL - File name
; Returns:
; A - file stream id
fopen:
push bc : push hl
call getDefaultDrive
pop ix : pop bc
rst #8
defb ESX_FOPEN
ret
После открытия нам необходимо высвободить ресурсы(у divMMC и divIDE своя память и если бросить файл открытым — то он будет жрать внутренний дескриптор, поможет только выключение/включение компьютера).
Закрыть файл еще проще — нужно просто передать в регистр A его номер и вызвать ESX_FCLOSE:
; A - file stream id
fclose:
rst #8
defb ESX_FCLOSE
ret
Отлично, файл открыли, файл закрыли, но хотелось бы его все таки считать :-) или хотя бы его часть.
И в чем счастье? А в том, что esxDOS позволяет считать хоть один байт, хоть десять, хоть… #ffff — было бы куда читать.
Указываем сколько мы хотим считать в байтах и передаем это значение в регистр BC, в IX указатель на память, а в А — файловый дескриптор, но в моем случае это будет выглядеть вот так:
; A - file stream id
; BC - length
; HL - buffer
; Returns
; BC - length(how much was actually read)
fread:
push hl : pop ix
rst #8
defb ESX_FREAD
ret
В BC мы получаем реальное количество байт, считанных с диска — т.е. если файл кончился раньше — то мы можем узнать это по BC.
И что ж? Небольшой практикум — считаем образ экрана с флэшки:
start:
ld c, #fe : ld a, 0 : out (c), a
ld hl, filename : ld b, FMODE_READ : call fopen : jr c, onError
push af
ld hl, #4000 : ld bc, #ffff : call fread
pop af
call fclose
ret
onError:
ld c, #fe
ld a, 3
out (c), a
jr $
filename db "/out.scr", 0
Если файла нет — вывалимся в onError, если есть — получим картинку на экране.
Что ж, сохраним на диск что-нибудь
Для этого нам понадобятся следующие методы API:
ESX_FSYNC = #9C
ESX_FWRITE = #9E
fsync нужен для того, чтобы «уж точно записать на диск»(он принудительно сливает буфер на диск).
У fsync есть только один параметр — номер файла(регистр А).
А интерфейс fwrite — аналогичен fread, только работает наоборот.
Я использую такие обертки:
; A - file stream id
; BC - length
; HL - buffer
; Returns:
; BC - actually written bytes
fwrite:
push hl : pop ix
rst #8
defb ESX_FWRITE
ret
; A - file stream id
fsync:
rst #8
defb ESX_FSYNC
ret
Для примера сделаем дампалку нижних 16к(там будет esxDOS, возможно, со страницами ОЗУ).
ld c, #fe : ld a, 0 : out (c), a
ld hl, filename : ld b, FMODE_CREATE : call fopen : jr c, onError
push af
ld hl, #0000 : ld bc, #4000 : call fwrite
pop af : push af
call fsync
pop af
call fclose
ret
onError:
ld c, #fe
ld a, 3
out (c), a
jr $
filename db "l16.bin", 0
Что ж самый минимум описан, если будут желающие — продолжу статьи по esxDOS(например, получение каталога или атрибутов файлов, работа со смещениями в файлах)
UPD
Есть несколько мелочей, которые стоит прояснить.
Читать и писать можно много много раз — поточная работа — это основа работы с esxDOS(т.е. можно читать в область экрана из файла каждый раз по целому экрану, могу к следующей статье написать проигрывалку видео для спектрума).
Номер файла — это номер записи во внутренней таблице файловых дескрипторах esxDOS. Точное количество одновременно открытых файлов я не знаю — но оно достаточное, чтобы не особо париться по этому поводу, однако, если постоянно файлы открывать и не закрывать, то они кончатся(таблица хранится во внутренней памяти divMMC/divIDE), поможет выключение/включение компьютера(или полный перезапуск divIDE/divMMC).
И еще раз напомню — чтение и запись считаются побайтово(т.е. если вам нужно записать 3 байта — вы пишите 3 байта, если 100 — то 100).
И да, раз эта тема интересна — то будет еще одна статья. В коментариях предложите — получение каталога или работа со смещениями в файле.
5 комментариев
Например, в разделе про esxDOS там есть очень ценное замечание, что не обязательно всякий раз вызывать функцию GetSetDrv чтобы получить номер текущего диска когда нужно вызвать функцию которой нужно передать номер диска. Оказывается в аккумуляторе можно передать символьные значения:
A='*' для текущего диска и
A='$' для системного диска (где установлена NextOS или, видимо, сама esxDOS)
Так же там есть важное замечание, что к любым вызовам надо относится так, что они уничтожают AF,BC,DE,HL, но сохраняют все остальные регистры (например тот же IX).
В общем полезно почитать…
Поэтому я рекомендую все таки вызывать функцию, можно один раз и сохранять результат. Этот вариант проверено работает везде.
MODE_OPEN_CREAT = $08; Open existing or create new
Вот всё остальное работает:
Может открыть существующий файл и выдать ошибку если его нет.
Может создать файл если его нет или стереть его содержимое если он есть и открыть.
И даже может создать новый файл если такого еще нет, но выдать ошибку если он есть.
Но блин создать новый или открыть на запись старый (не стирая предыдущего содержимого) — нифига, даёт ошибку.
Хотя технически это можно реализовать двумя попытками открытия, но как то странно. Надеюсь детские болезни.