Учимся читать и писать!

«640K ought to be enough for anybody...»

Итак, объём стандартной дискеты — 640Кб. Объём sd card — гораздо выше.
Как использовать SD в своих программах?

Речь пойдёт о драйвере, который повсеместно используется на zx evo — в Wild Commander.
Этот драйвер написан Koshi, и позволяет проводить основные операции с файлами и папками на sd card:
— создавать
— удалять
— переименовывать
— проверять на наличие
— переходить по директориям
— читать сектор
— записывать сектор

В первую очередь я хочу поблагодарить Koshi за реализацию настолько сложной задачи как изучение, кодинг и отладка драйвера;
и во вторых — за предоставление модуля драйвера для использования в составе Zifi.

Итак, базовое руководство по файловым операциям с использованием драйвера sd card.


Для начала, давайте подключим модуль.
Его требования следующие:

1. Фиксированное размещение в памяти:
— драйвер должен лежать в странице #0f;
— включение и вызов драйвера происходит из PAGE0, драйвер работает с адреса #0, эта память должна быть RAM

2. Инициализация драйвера при старте
— необходимо вызвать инит драйвера, инит устройства и проверить выдачу ошибок.

3. Во время работы драйвер использует DMA. Это позволяет производить файловые операции с максимально доступной нам скорость.
Любая DMA транзакция во время работы драйвера:
— при записи может привести к проблемам с данными на карте, и как результат — её инициализация;
— при чтении — прочитает неизвестно что.

Для начала — необходимо перенести определения вызовов драйвера в свой исходный код. По представленной ссылке (в кодировке 866) показан пример работы с драйвером + определения адресов вызовов его функций, которые нам и нужны.

Базовые операции с sd card:

Инициализация драйвера:
sd_driver_page		equ #0f

sd_init		ld a,sd_driver_page
		call set_page0
	
		CALL DOS_SWP ; DEPACK Driver
		CALL DEV_INI ; инициализация карты
		JP NZ,ER0       ; обработка ошибки карты "SD Card NOT ready!"
		CALL HDD
		JP NZ,ER1       ; ошибка FAT32 NOT FOUND
		CALL SETROOT    ; установка и переход в корень диска: SET ROOT DIR
		jp sd_exit      ; возвращаем нужную пагу и другие полезные действия при выходе

set_page0	ld bc,PAGE0
		out (c),a
		ret

после успешного выполнения инициализации вы уже находитесь в корневой папке.

Функции чтения
Переход по папкам
Давайте зайдём в папку wc в корне диска. Для этого нам необходим дескриптор (описатель) папки.
Первый байт — признак папки, далее — имя, заканчивающееся 0.

set_folder	LD HL,DIR
		CALL FENTRY    ; поиск папки
		JP Z,not_found ; папка где ты!?
		CALL SETDIR    ; установка найденного каталога как активного для файловых операций
		ret

DIR		DB #10
		DB "wc",0

Koshi: FENTRY ищет файлы/каталоги и выдает длину оных в ответ, плюс позиционирует на них.
Выход этой функции:
Z — entry not found
NZ — [DE,HL] — file length

Чтение файла
В текущей (активной) папке есть файл с настройками — wc.ini, который охота заполучить в память.
опять нужен дескриптор файла, производим его поиск, и потом — чтение:
Читаем его:
LD HL,FILE_INI
		CALL FENTRY
		JP Z,ini_not_found
		LD C,download_page  ; страница для загрузки
		LD HL,#0000         ; адрес загрузки
		LD B,1              ; размер файла в блоках (1 блок = 512 байт)
		CALL LOAD512

FILE_INI	DB #00; 0 - file, 1 - DIR
		DB "wc.ini",0

Всё, содержимое первых 512 байт файла — находится в паге download_page с адреса 0.

С чтением уже понятнее. Теперь стоит разобраться с записью.

Операции записи
Сюда входит — создание папки, файла, запись в файл.

Создание папки производится следующим образом.
для начала — мы должны находится в активной директории. это может быть корневая, переход в неё производим вызовом SETROOT;
либо же с помошью подпрограммы set_folder из примеров выше — переходим в нужную.

		LD HL,DIR_new
		CALL MKDIR
		ret

DIR_new		DB "zxpron",0

здесь нужно отметить, что для создаваемой папки нужно только её имя с терминатором «0» в конце.

Создание файла так же просто:

		LD HL,FILE
		CALL MKFILE
		JP NZ,error_create_new_file			;File Creation Failed
		ret

FILE		DB #00;        flag
		DW #0000,#0000;length
FILE_NAME	ds 16
		db 0

здесь нужно отметить, что дескриптор создаваемого файла гораздо шире чем в предидущих случаях.
Он содержит признак файла, его длину в 32 бита, имя и терминатор. Размер файла может быть больше 640кб ;)
i: HL - flag(1),ln(4),name(1-255),0

пример указания длины файла:
DW #1000,#0000 ;length - 4kb

в данном случае можно заметить, что с начала мы записываем младшие 16 бит длины, далее — последующие 16 бит.

Подпрограмма создания файлов может возвращать следующие ошибки:
Koshi: если файло есть, то NZ получаем, и смотрим в A код ошибке
если 3 — то имя уже занято
o: NZ — ERROR (NO ENOUGHT SPACE)
A: 1 — ln not valid
2 — index fatality
3 — ln already exists
255 — unknown error
Z — SUCCESS

Запись данных производится вызовом подпрограммы SAVE512, которой необходимо передать страницу в С, адрес с данными в HL, и количество блоков по 512 байт для сохранения.
Описание параметров:
i: CHL - Addres
     B - lenght

o: CHL - New Value
     A - EndOfChain (#0F)


Для расчёта количества блоков по текущей длине я использую следующую подпрограмму Коши:
DEL512  ;i:[DE,HL]/512
        LD A,L,L,H,H,E,E,D,D,0
        LD BC,1:OR A:CALL NZ,ADD4B
        LD A,2

DELITX2 ;i:[DE,HL]/A
;                A - Power of Two
;        o:[DE,HL]

        CP 2:RET C
        LD C,0
        SRL A
L33T    SRL D:RR E,H,L,C
        SRL A:JR NC,L33T

        LD A,C:OR A:RET Z
        LD BC,1:CALL ADD4B
        RET

ADD4B   ADD HL,BC:RET NC:INC DE
        RET

Люблю такой код, поиграйте с ним :)
На входе: длина в виде DE,HL
на выходе: длина в блоках, в DE,HL.
Подпрограмма применима в случаях, когда вы собираетесь писать файл больше 128кб.

итак, запись небольшого блока:

;Save data into new file <128kb:
save_64		ld b,len
		ld c,page
		ld hl,adress
		call SAVE512

Запись больше 256 блоков:
Koshi: просто пишешь до посинения пока цепочка не кончиццо
када цепочки конец — что сейв512, что лоад512 выдадут в А=#0F

Удаление файла
Указываем дескриптор, вызываем функцию:

		ld hl,delete_file
		call DELFL

параметры DELFL:
i: HL - flag(1),name(1-255),0
o:  Z - NOT FOUND
   NZ - FILE DELETED


Блин, хорошо, базовый наборчик есть. Можно и попробовать. Но!

Как тестить?

Естественно что функции работы с sd необходимо проверять на sd.
Для этого ваш запускаемый spg-файл необходимо сохранить либо на реальную карту, либо записать в образ диска эмулятора.
Бегать с картой долго, да и — можно запороть своими попытками. Поэтому — пишем в образ эмулятора.
Для тестов и отладки я рекомендую использовать великолепную прогу от Robus, которая позволяет записывать в образ диска эмулятора необходимые вам файлы. К достоинствам robimg.exe следует отнести то, что писать она может даже в открытый сейчас образ, чего не умеет ни один знакомый мне менеджер образов, и работает из командной строки.

Пример использования этого «инжектора» вы можете увидеть здесь. Создаётся папка с нужными именем, в которую копируются все необходимые файлы, и после этого она впрыскивается с заменой в нужную директорию на лету.
Великолепный инструмент, который очень облегчил задачу отладки.

Рекомендую иметь копию образа карты (wc.img) для эмулятора, потому что… ;)

Ссылки:
github.com / ZiFi — драйвер, доки
WildCommander sources
SD driver sources

3 комментария

avatar
The source of «640 kB ought to be enough for anybody» may have actually been «No one will need more than 637 kB of memory for a personal computer», and it may have been said in the early 1970s. // Liftarn
  • VBI
  • +1
avatar
Спасибо за статью!
  • tsl
  • +3
avatar
Без всяких сомнений — Хорошая статья!

Но
Запись больше 256 блоков:
Koshi: просто пишешь до посинения пока цепочка не кончиццо
када цепочки конец — что сейв512, что лоад512 выдадут в А=#0F
всетаки, для пользователей, конкретно в примерах это лучше прописать. имхо.

ps: Пример отображения содержимого SD-карты начиная с корня… как более развернутое изложения примеров..)
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.