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 комментариев

avatar
Откопал крайне любопытный документ по NextOS + esxDOS для Next: raw.githubusercontent.com/z88dk/techdocs/master/targets/zx-next/nextos/nextzxos_api.pdf
Например, в разделе про esxDOS там есть очень ценное замечание, что не обязательно всякий раз вызывать функцию GetSetDrv чтобы получить номер текущего диска когда нужно вызвать функцию которой нужно передать номер диска. Оказывается в аккумуляторе можно передать символьные значения:
A='*' для текущего диска и
A='$' для системного диска (где установлена NextOS или, видимо, сама esxDOS)
Так же там есть важное замечание, что к любым вызовам надо относится так, что они уничтожают AF,BC,DE,HL, но сохраняют все остальные регистры (например тот же IX).
В общем полезно почитать…
avatar
Тут стоит все таки отметить, что конкретно это поведение есть только в NextZXOS.

Поэтому я рекомендую все таки вызывать функцию, можно один раз и сохранять результат. Этот вариант проверено работает везде.
avatar
Жаль. Было бы неплохо если бы это стало стандартом и в самом esxDOS. Как я понял документация по работе в «командной строке/бейсике» esxDOS сама говорит, что вместо имени текущего диска можно подставлять символ *, т.е. тут есть некая монолитность.
avatar
С флагами открытия в F_OPEN прям какая то «бида». Крутил-вертел, но так и не смог заставить работать по документации из ссылки выше флаг
MODE_OPEN_CREAT = $08; Open existing or create new
Вот всё остальное работает:
Может открыть существующий файл и выдать ошибку если его нет.
Может создать файл если его нет или стереть его содержимое если он есть и открыть.
И даже может создать новый файл если такого еще нет, но выдать ошибку если он есть.
Но блин создать новый или открыть на запись старый (не стирая предыдущего содержимого) — нифига, даёт ошибку.
Хотя технически это можно реализовать двумя попытками открытия, но как то странно. Надеюсь детские болезни.
avatar
У меня есть просьба к зубрам уже хорошо соприкоснувшимся с ZX Spectrum Next проверить черновик моей обзорной статьи про архитектуру Next OS: gamedev.ru/flame/forum/?id=231961&page=17&m=5253430#m241 на предмет актуальности, неверных сведений и так далее. Надеюсь она и тут станет полезной в итоге.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.