Оглавление
- Чтение и запись
- Инициализация каналов DMA для FDC
- Подготовить DMA на чтение или запись
- Процедуры
- Вспомогательные процедуры чтения и записи в DMA
- Обработчик прерываний (IRQ #6)
- Переменные для FDC
- Константы
§ Чтение и запись
Главные процедуры для чтения и записи на диск.Требуется реализация переменной
irq_timer_counter
, которая будет инкрементироваться +1 каждые 1/100 сек. Это нужно для включения и выключения мотора.-
fdc_read
иfdc_write
запускают чтение или запись, но ответ не ожидается, можно выполнять программу далее. Статус готовности проверяется по наличию не нулевого значения в fdc.ready -
fdc_read_sync
иfdc_write_sync
делают тоже самое, но ответ ожидается
; Чтение сектора (AX) в $1000 -> IRQ #6 fdc_read: mov [fdc.lba], ax ; Запрошенный LBA call fdc_prepare call fdc_dma_read mov bl, 0 call fdc_rw ; BL=0 READ ret ; Запись сектора (AX) из $1000 -> IRQ #6 fdc_write: mov [fdc.lba], ax call fdc_prepare call fdc_dma_write mov bl, 1 call fdc_rw ; BL=1 WRITE ret ; Чтение AX сектора в $1000, ждать ответа fdc_read_sync: call fdc_read @@: cmp [fdc.ready], byte 0 je @b ret ; Запись AX сектора из $1000, ждать ответа fdc_write_sync: call fdc_write @@: cmp [fdc.ready], byte 0 je @b ret
§ Инициализация каналов DMA для FDC
Эта процедура программирует контроллер DMA для работы дискеты.fdc_init: ; Установка DMA mov al, $06 out $0A, al ; Маскирование DMA channel 2 и 0 ; Адрес $1000 mov al, $FF out $0C, al ; Сброс master flip-flop mov al, $00 out $04, al ; [7:0] mov al, $10 out $04, al ; [15:8] ; Размер $3FFF mov al, $FF out $0C, al ; Сброс master flip-flop mov al, $FF out $05, al ; [7:0] mov al, $3F out $05, al ; [15:8] ; Адрес $00 mov al, $00 out $81, al ; [23:16] mov al, $02 out $0A, al ; Размаскировать DMA channel 2 ; Инициализация mov [fdc.motor], 0 mov [fdc.ready], 0 ret
§ Подготовить DMA на чтение или запись
; Подготовить диск на чтение fdc_dma_read: mov al, $06 ; mask DMA channel 2 and 0 (assuming 0 is already masked) out $0A, al mov al, $56 out $0B, al ; 01010110 single transfer, address increment, autoinit, read, channel2 mov al, $02 out $0A, al ; unmask DMA channel 2 ret ; Подготовить диск на запись fdc_dma_write: mov al, $06 out $0A, al mov al, $5A out $0B, al ; 01011010 single transfer, address increment, autoinit, write, channel2 mov al, $02 out $0A, al ret
§ Процедуры
; Ожидать завершения (проверочный параметр AH) fdc_wait: push eax mov dx, MAIN_STATUS_REGISTER @@: in al, dx and al, ah cmp al, ah jne @b pop eax ret ; Запись данных (al) в FIFO fdc_write_reg: mov ah, $80 call fdc_wait ; Ожидать OK mov dx, FDC_DATA_FIFO out dx, al ; Записать AL в FIFO ret ; Чтение данных (al) из FIFO fdc_read_reg: mov ah, $c0 call fdc_wait ; Ожидать OK mov dx, FDC_DATA_FIFO in al, dx ; Прочесть AL из FIFO ret ; Проверить IRQ-статус после SEEK, CALIBRATE, etc. fdc_sensei: mov al, SENSE_INTERRUPT call fdc_write_reg ; Отправка запроса mov ah, 0xD0 call fdc_wait mov dx, FDC_DATA_FIFO ; Получение результата in al, dx mov [fdc.st0], al mov ah, 0xD0 call fdc_wait mov dx, FDC_DATA_FIFO ; Номер цилиндра in al, dx mov [fdc.cyl], al ret ; Конфигурирование fdc_configure: mov al, SPECIFY call fdc_write_reg mov al, 0 call fdc_write_reg ; steprate_headunload call fdc_write_reg ; headload_ndma ret ; Калибрация драйва fdc_calibrate: call fdc_motor_on mov [fdc.func], FDC_STATUS_SENSEI mov [fdc.ready], 0 mov al, RECALIBRATE call fdc_write_reg ; Команда, Drive = A: mov al, 0 call fdc_write_reg ; Команда, Drive = A: @@: cmp [fdc.ready], 0 ; Ожидать ответа IRQ je @b ret ; Сбросить контроллер перед работой с диском fdc_reset: mov [fdc.func], FDC_STATUS_SENSEI mov [fdc.ready], 0 ; Отключить и включить контроллер mov dx, DIGITAL_OUTPUT_REGISTER mov al, $00 out dx, al mov al, $0C out dx, al @@: cmp [fdc.ready], 0 ; Ожидать ответа IRQ je @b ; Конфигурирование mov dx, CONFIGURATION_CONTROL_REGISTER mov al, 0 out dx, al call fdc_configure call fdc_calibrate ret ; Сбор результирующих данных: если > 0, то ошибка fdc_get_result: call fdc_read_reg mov [fdc.st0], al call fdc_read_reg mov [fdc.st1], al call fdc_read_reg mov [fdc.st2], al call fdc_read_reg mov [fdc.cyl], al call fdc_read_reg mov [fdc.head_end], al call fdc_read_reg mov [fdc.head_start], al call fdc_read_reg and al, $c0 ret ; Преобразовать ax=LBA -> CHS fdc_lba2chs: xor dx, dx mov bx, 18 div bx inc dl mov [fdc.r_sec], dl mov dl, al and dl, 1 mov [fdc.r_hd], dl shr ax, 1 mov [fdc.r_cyl], al ret ; Включить мотор fdc_motor_on: cli mov [fdc.motor], 1 ; Включение sti mov eax, [irq_timer_counter] mov [fdc.motor_time], eax ; Запись таймера mov dx, DIGITAL_OUTPUT_REGISTER ; Регистр DOR = $1C (On) mov al, 0x1C out dx, al ret ; Выключить мотор fdc_motor_off: cli mov [fdc.motor], 0 sti xor eax, eax mov dx, DIGITAL_OUTPUT_REGISTER ; Отключить out dx, al ret
§ Вспомогательные процедуры чтения и записи в DMA
Процедура записи и чтения на диск.; bl = 0 READ; 1 WRITE ; ax = lba ; (byte write, byte head, byte cyl, byte sector) fdc_rw: mov [fdc.ready], 0 mov [fdc.func], FDC_STATUS_RW mov ax, $4546 and bl, bl je @f mov al, ah @@: call fdc_write_reg ; 0 MFM_bit = 0x40 | (W=0x45 | R=0x46) mov al, [fdc.r_hd] shl al, 2 call fdc_write_reg ; 1 mov al, [fdc.r_cyl] call fdc_write_reg mov al, [fdc.r_hd] call fdc_write_reg mov al, [fdc.r_sec] call fdc_write_reg mov al, 2 call fdc_write_reg ; 5 Размер сектора (2 ~> 512 bytes) mov al, 18 call fdc_write_reg ; 6 Последний сектор в цилиндре mov al, $1B call fdc_write_reg ; 7 Длина GAP3 mov al, $FF call fdc_write_reg ; 8 Длина данных, игнорируется ret ; Поиск дорожки => IRQ #6 fdc_seek: mov [fdc.ready], 0 mov [fdc.func], FDC_STATUS_SEEK mov al, SEEK call fdc_write_reg ; Команда mov al, [fdc.r_hd] shl al, 2 call fdc_write_reg ; head<<2 mov al, [fdc.r_cyl] call fdc_write_reg ; Цилиндр ret ; Подготовить диск для чтения/записи (AX = LBA) fdc_prepare: call fdc_lba2chs ; Вычислить LBA mov [fdc.error], 0 ; Отметить, что ошибок пока нет mov eax, [irq_timer_counter] mov [fdc.motor_time], eax cmp [fdc.motor], 0 ; Включить мотор, если нужно jne @f call fdc_reset @@: call fdc_seek @@: cmp [fdc.ready], 0 ; Ожидать ответа IRQ je @b ret
§ Обработчик прерываний (IRQ #6)
fdc_irq: pushad cmp [fdc.func], byte FDC_STATUS_RW je .rw call fdc_sensei ; Выполнить считывание рез-та jmp .exit .rw: call fdc_get_result ; Забрать результат при R/W and al, al jne .exit mov [fdc.error], byte 1 ; Ошибка чтения при al > 0 .exit: mov [fdc.ready], byte 1 ; Завершено mov al, $20 ; EOI out $20, al popad iretd ; Вычисление таймаута и выключение FDC из прерывания IRQ #0 ; ---------------------------------------------------------------------- fdc_timeout: cmp [fdc.motor], 0 ; Мотор включен? je @f mov eax, [irq_timer_counter] ; Если > 5с крутится, выключить sub eax, [fdc.motor_time] cmp eax, 500 jb @f call fdc_motor_off @@: ret
§ Переменные для FDC
Контроллер флоппи-дискаfdc: ; ; результат .st0 db 0 ; Статусный регистр 0 .st1 db 0 .st2 db 0 .cyl db 0 .head_end db 0 .head_start db 0 ; функционирование .motor db 0 ; Включен ли мотор .motor_time dd 0 ; Время включения мотора .func db 0 ; Функция запроса на IRQ .ready db 0 ; IRQ обработан .error db 0 ; Ошибка исполнения ; запрос .lba dw 0 .r_hd db 0 ; * головка .r_cyl db 0 ; * цилиндр .r_sec db 0 ; * сектор
§ Константы
; Floppy Disk Controller ; ---------------------------------------------------------------------- ; Порты STATUS_REGISTER_A equ 0x3F0 ; read-only STATUS_REGISTER_B equ 0x3F1 ; read-only DIGITAL_OUTPUT_REGISTER equ 0x3F2 TAPE_DRIVE_REGISTER equ 0x3F3 MAIN_STATUS_REGISTER equ 0x3F4 ; read-only DATARATE_SELECT_REGISTER equ 0x3F4 ; write-only FDC_DATA_FIFO equ 0x3F5 DIGITAL_INPUT_REGISTER equ 0x3F7 ; read-only CONFIGURATION_CONTROL_REGISTER equ 0x3F7 ; write-only ; Команды READ_TRACK equ 2 ; generates IRQ6 SPECIFY equ 3 ; * set drive parameters SENSE_DRIVE_STATUS equ 4 WRITE_DATA equ 5 ; * write to the disk READ_DATA equ 6 ; * read from the disk RECALIBRATE equ 7 ; * seek to cylinder 0 SENSE_INTERRUPT equ 8 ; * ack IRQ6, get status of last command WRITE_DELETED_DATA equ 9 READ_ID equ 10 ; generates IRQ6 READ_DELETED_DATA equ 12 FORMAT_TRACK equ 13 DUMPREG equ 14 SEEK equ 15 ; * seek both heads to cylinder X VERSION equ 16 ; * used during initialization, once SCAN_EQUAL equ 17 PERPENDICULAR_MODE equ 18 ; * used during initialization, once, maybe CONFIGURE equ 19 ; * set controller parameters LOCK equ 20 ; * protect controller params from a reset VERIFY equ 22 SCAN_LOW_OR_EQUAL equ 25 SCAN_HIGH_OR_EQUAL equ 29 ; Статусы FDC_STATUS_NONE equ 0x0 FDC_STATUS_SEEK equ 0x1 FDC_STATUS_RW equ 0x2 FDC_STATUS_SENSEI equ 0x3