Оглавление


§ Чтение и запись

Главные процедуры для чтения и записи на диск.
Требуется реализация переменной irq_timer_counter, которая будет инкрементироваться +1 каждые 1/100 сек. Это нужно для включения и выключения мотора.
  • fdc_read и fdc_write запускают чтение или запись, но ответ не ожидается, можно выполнять программу далее. Статус готовности проверяется по наличию не нулевого значения в fdc.ready
  • fdc_read_sync и fdc_write_sync делают тоже самое, но ответ ожидается
Ниже приведен код.
1; Чтение сектора (AX) в $1000 -> IRQ #6
2fdc_read:
3
4        mov     [fdc.lba], ax           ; Запрошенный LBA
5        call    fdc_prepare
6        call    fdc_dma_read
7        mov     bl, 0
8        call    fdc_rw                  ; BL=0 READ
9        ret
10
11; Запись сектора (AX) из $1000 -> IRQ #6
12fdc_write:
13
14        mov     [fdc.lba], ax
15        call    fdc_prepare
16        call    fdc_dma_write
17        mov     bl, 1
18        call    fdc_rw                  ; BL=1 WRITE
19        ret
20
21; Чтение AX сектора в $1000, ждать ответа
22fdc_read_sync:
23
24        call    fdc_read
25@@:     cmp     [fdc.ready], byte 0
26        je      @b
27        ret
28
29; Запись AX сектора из $1000, ждать ответа
30fdc_write_sync:
31
32        call    fdc_write
33@@:     cmp     [fdc.ready], byte 0
34        je      @b
35        ret

§ Инициализация каналов DMA для FDC

Эта процедура программирует контроллер DMA для работы дискеты.
1fdc_init:
2
3        ; Установка DMA
4        mov     al, $06
5        out     $0A, al ; Маскирование DMA channel 2 и 0
6
7        ; Адрес $1000
8        mov     al, $FF
9        out     $0C, al ; Сброс master flip-flop
10        mov     al, $00
11        out     $04, al ; [7:0]
12        mov     al, $10
13        out     $04, al ; [15:8]
14
15        ; Размер $3FFF
16        mov     al, $FF
17        out     $0C, al ; Сброс master flip-flop
18        mov     al, $FF
19        out     $05, al ; [7:0]
20        mov     al, $3F
21        out     $05, al ; [15:8]
22
23        ; Адрес $00
24        mov     al, $00
25        out     $81, al ; [23:16]
26        mov     al, $02
27        out     $0A, al ; Размаскировать DMA channel 2
28
29        ; Инициализация
30        mov     [fdc.motor], 0
31        mov     [fdc.ready], 0
32        ret
33

§ Подготовить DMA на чтение или запись

1; Подготовить диск на чтение
2fdc_dma_read:
3
4        mov     al, $06 ; mask DMA channel 2 and 0 (assuming 0 is already masked)
5        out     $0A, al
6        mov     al, $56
7        out     $0B, al ; 01010110 single transfer, address increment, autoinit, read, channel2
8        mov     al, $02
9        out     $0A, al ; unmask DMA channel 2
10        ret
11
12; Подготовить диск на запись
13fdc_dma_write:
14
15        mov     al, $06
16        out     $0A, al
17        mov     al, $5A
18        out     $0B, al ; 01011010 single transfer, address increment, autoinit, write, channel2
19        mov     al, $02
20        out     $0A, al
21        ret
22

§ Процедуры

1; Ожидать завершения (проверочный параметр AH)
2fdc_wait:
3
4        push    eax
5        mov     dx, MAIN_STATUS_REGISTER
6@@:     in      al, dx
7        and     al, ah
8        cmp     al, ah
9        jne     @b
10        pop     eax
11        ret
12
13; Запись данных (al) в FIFO
14fdc_write_reg:
15
16        mov     ah, $80
17        call    fdc_wait            ; Ожидать OK
18        mov     dx, FDC_DATA_FIFO
19        out     dx, al              ; Записать AL в FIFO
20        ret
21
22; Чтение данных (al) из FIFO
23fdc_read_reg:
24
25        mov     ah, $c0
26        call    fdc_wait            ; Ожидать OK
27        mov     dx, FDC_DATA_FIFO
28        in      al, dx              ; Прочесть AL из FIFO
29        ret
30
31; Проверить IRQ-статус после SEEK, CALIBRATE, etc.
32fdc_sensei:
33
34        mov     al, SENSE_INTERRUPT
35        call    fdc_write_reg       ; Отправка запроса
36        mov     ah, 0xD0
37        call    fdc_wait
38        mov     dx, FDC_DATA_FIFO   ; Получение результата
39        in      al, dx
40        mov     [fdc.st0], al
41        mov     ah, 0xD0
42        call    fdc_wait
43        mov     dx, FDC_DATA_FIFO   ; Номер цилиндра
44        in      al, dx
45        mov     [fdc.cyl], al
46        ret
47
48; Конфигурирование
49fdc_configure:
50
51        mov     al, SPECIFY
52        call    fdc_write_reg
53        mov     al, 0
54        call    fdc_write_reg       ; steprate_headunload
55        call    fdc_write_reg       ; headload_ndma
56        ret
57
58; Калибрация драйва
59fdc_calibrate:
60
61        call    fdc_motor_on
62        mov     [fdc.func],  FDC_STATUS_SENSEI
63        mov     [fdc.ready], 0
64        mov     al, RECALIBRATE
65        call    fdc_write_reg       ; Команда, Drive = A:
66        mov     al, 0
67        call    fdc_write_reg       ; Команда, Drive = A:
68@@:     cmp     [fdc.ready], 0      ; Ожидать ответа IRQ
69        je      @b
70        ret
71
72; Сбросить контроллер перед работой с диском
73fdc_reset:
74
75        mov     [fdc.func],  FDC_STATUS_SENSEI
76        mov     [fdc.ready], 0
77
78        ; Отключить и включить контроллер
79        mov     dx, DIGITAL_OUTPUT_REGISTER
80        mov     al, $00
81        out     dx, al
82        mov     al, $0C
83        out     dx, al
84@@:     cmp     [fdc.ready], 0      ; Ожидать ответа IRQ
85        je      @b
86
87        ; Конфигурирование
88        mov     dx, CONFIGURATION_CONTROL_REGISTER
89        mov     al, 0
90        out     dx, al
91        call    fdc_configure
92        call    fdc_calibrate
93        ret
94
95; Сбор результирующих данных: если > 0, то ошибка
96fdc_get_result:
97
98        call    fdc_read_reg
99        mov     [fdc.st0], al
100        call    fdc_read_reg
101        mov     [fdc.st1], al
102        call    fdc_read_reg
103        mov     [fdc.st2], al
104        call    fdc_read_reg
105        mov     [fdc.cyl], al
106        call    fdc_read_reg
107        mov     [fdc.head_end], al
108        call    fdc_read_reg
109        mov     [fdc.head_start], al
110        call    fdc_read_reg
111        and     al, $c0
112        ret
113
114; Преобразовать ax=LBA -> CHS
115fdc_lba2chs:
116
117        xor     dx, dx
118        mov     bx, 18
119        div     bx
120        inc     dl
121        mov     [fdc.r_sec], dl
122        mov     dl, al
123        and     dl, 1
124        mov     [fdc.r_hd], dl
125        shr     ax, 1
126        mov     [fdc.r_cyl], al
127        ret
128
129; Включить мотор
130fdc_motor_on:
131
132        cli
133        mov     [fdc.motor], 1              ; Включение
134        sti
135        mov     eax, [irq_timer_counter]
136        mov     [fdc.motor_time], eax       ; Запись таймера
137        mov     dx, DIGITAL_OUTPUT_REGISTER ; Регистр DOR = $1C (On)
138        mov     al, 0x1C
139        out     dx, al
140        ret
141
142; Выключить мотор
143fdc_motor_off:
144
145        cli
146        mov     [fdc.motor], 0
147        sti
148        xor     eax, eax
149        mov     dx, DIGITAL_OUTPUT_REGISTER ; Отключить
150        out     dx, al
151        ret

§ Вспомогательные процедуры чтения и записи в DMA

Процедура записи и чтения на диск.
1; bl = 0 READ; 1 WRITE
2; ax = lba
3; (byte write, byte head, byte cyl, byte sector)
4
5fdc_rw:
6
7        mov     [fdc.ready], 0
8        mov     [fdc.func],  FDC_STATUS_RW
9        mov     ax, $4546
10        and     bl, bl
11        je      @f
12        mov     al, ah
13@@:     call    fdc_write_reg       ; 0 MFM_bit = 0x40 | (W=0x45 | R=0x46)
14        mov     al, [fdc.r_hd]
15        shl     al, 2
16        call    fdc_write_reg       ; 1
17
18        mov     al, [fdc.r_cyl]
19        call    fdc_write_reg
20        mov     al, [fdc.r_hd]
21        call    fdc_write_reg
22        mov     al, [fdc.r_sec]
23        call    fdc_write_reg
24        mov     al, 2
25        call    fdc_write_reg       ; 5 Размер сектора (2 ~> 512 bytes)
26        mov     al, 18
27        call    fdc_write_reg       ; 6 Последний сектор в цилиндре
28        mov     al, $1B
29        call    fdc_write_reg       ; 7 Длина GAP3
30        mov     al, $FF
31        call    fdc_write_reg       ; 8 Длина данных, игнорируется
32        ret
33
34; Поиск дорожки => IRQ #6
35fdc_seek:
36
37        mov     [fdc.ready], 0
38        mov     [fdc.func],  FDC_STATUS_SEEK
39        mov     al, SEEK
40        call    fdc_write_reg           ; Команда
41        mov     al, [fdc.r_hd]
42        shl     al, 2
43        call    fdc_write_reg           ; head<<2
44        mov     al, [fdc.r_cyl]
45        call    fdc_write_reg           ; Цилиндр
46        ret
47
48; Подготовить диск для чтения/записи (AX = LBA)
49fdc_prepare:
50
51        call    fdc_lba2chs             ; Вычислить LBA
52        mov     [fdc.error], 0          ; Отметить, что ошибок пока нет
53        mov     eax, [irq_timer_counter]
54        mov     [fdc.motor_time], eax
55        cmp     [fdc.motor], 0          ; Включить мотор, если нужно
56        jne     @f
57        call    fdc_reset
58@@:     call    fdc_seek
59@@:     cmp     [fdc.ready], 0      ; Ожидать ответа IRQ
60        je      @b
61        ret

§ Обработчик прерываний (IRQ #6)

1fdc_irq:
2
3        pushad
4        cmp     [fdc.func], byte FDC_STATUS_RW
5        je      .rw
6        call    fdc_sensei              ; Выполнить считывание рез-та
7        jmp     .exit
8.rw:    call    fdc_get_result          ; Забрать результат при R/W
9        and     al, al
10        jne     .exit
11        mov     [fdc.error], byte 1     ; Ошибка чтения при al > 0
12.exit:  mov     [fdc.ready], byte 1     ; Завершено
13        mov     al, $20                 ; EOI
14        out     $20, al
15        popad
16        iretd
17
18; Вычисление таймаута и выключение FDC из прерывания IRQ #0
19; ----------------------------------------------------------------------
20
21fdc_timeout:
22
23        cmp     [fdc.motor], 0           ; Мотор включен?
24        je      @f
25        mov     eax, [irq_timer_counter] ; Если > 5с крутится, выключить
26        sub     eax, [fdc.motor_time]
27        cmp     eax, 500
28        jb      @f
29        call    fdc_motor_off
30@@:     ret

§ Переменные для FDC

Контроллер флоппи-диска
1fdc:                            ;
2
3    ; результат
4    .st0            db 0        ; Статусный регистр 0
5    .st1            db 0
6    .st2            db 0
7    .cyl            db 0
8    .head_end       db 0
9    .head_start     db 0
10
11    ; функционирование
12    .motor          db 0        ; Включен ли мотор
13    .motor_time     dd 0        ; Время включения мотора
14    .func           db 0        ; Функция запроса на IRQ
15    .ready          db 0        ; IRQ обработан
16    .error          db 0        ; Ошибка исполнения
17
18    ; запрос
19    .lba            dw 0
20    .r_hd           db 0        ; * головка
21    .r_cyl          db 0        ; * цилиндр
22    .r_sec          db 0        ; * сектор

§ Константы

1
2; Floppy Disk Controller
3; ----------------------------------------------------------------------
4
5; Порты
6STATUS_REGISTER_A               equ 0x3F0  ; read-only
7STATUS_REGISTER_B               equ 0x3F1  ; read-only
8DIGITAL_OUTPUT_REGISTER         equ 0x3F2
9TAPE_DRIVE_REGISTER             equ 0x3F3
10MAIN_STATUS_REGISTER            equ 0x3F4  ; read-only
11DATARATE_SELECT_REGISTER        equ 0x3F4  ; write-only
12FDC_DATA_FIFO                   equ 0x3F5
13DIGITAL_INPUT_REGISTER          equ 0x3F7  ; read-only
14CONFIGURATION_CONTROL_REGISTER  equ 0x3F7  ; write-only
15
16; Команды
17READ_TRACK                      equ 2  ; generates IRQ6
18SPECIFY                         equ 3  ; * set drive parameters
19SENSE_DRIVE_STATUS              equ 4
20WRITE_DATA                      equ 5  ; * write to the disk
21READ_DATA                       equ 6  ; * read from the disk
22RECALIBRATE                     equ 7  ; * seek to cylinder 0
23SENSE_INTERRUPT                 equ 8  ; * ack IRQ6, get status of last command
24WRITE_DELETED_DATA              equ 9
25READ_ID                         equ 10 ; generates IRQ6
26READ_DELETED_DATA               equ 12
27FORMAT_TRACK                    equ 13
28DUMPREG                         equ 14
29SEEK                            equ 15 ; * seek both heads to cylinder X
30VERSION                         equ 16 ; * used during initialization, once
31SCAN_EQUAL                      equ 17
32PERPENDICULAR_MODE              equ 18 ; * used during initialization, once, maybe
33CONFIGURE                       equ 19 ; * set controller parameters
34LOCK                            equ 20 ; * protect controller params from a reset
35VERIFY                          equ 22
36SCAN_LOW_OR_EQUAL               equ 25
37SCAN_HIGH_OR_EQUAL              equ 29
38
39; Статусы
40FDC_STATUS_NONE                 equ 0x0
41FDC_STATUS_SEEK                 equ 0x1
42FDC_STATUS_RW                   equ 0x2
43FDC_STATUS_SENSEI               equ 0x3