Автоопределение схемы IDE и подключенных устройств — Abzac


PeriodicalsAbzac2009 No. 32 → Автоопределение схемы IDE и подключенных устройств

Автоопределение схемы IDE и подключенных устройств

В этой статье рассматривается авто­матическое определение схемы IDE программным способом. На данный момент широкое распространение получили схемы Nemo (и её модифи­кация Nemo-A8), ATM и SMUC. Наша задача - программно определить тип схемы и подключенные устройства (HDD, CD-ROM, DVD-ROM).

Для начала, составим таблицу портов данных схем:

Назначение порта                         SMUC  ATM   Nemo  NemoA8
команда (in)/состояние (out)             #FFBE #FEEF #FFF0 #FEF0
регистр головки, устройства, режима LBA  #FEBE #FECF #FFD0 #FED0
цилиндр (старшая часть)                  #FDBE #FEAF #FFB0 #FEB0
цилиндр (младшая часть)                  #FCBE #FE8F #FF90 #FE90
сектор                                   #FBBE #FE6F #FF70 #FE70
счётчик                                  #FABE #FE4F #FF50 #FE50
регистр ошибки                           #F9BE #FE2F #FF30 #FE30
данные (старшая часть)                   #D8BE #FF0F #FF11 #FF10
данные (младшая часть)                   #F8BE #FE0F #FF10 #FE10

Стоит заметить, что SMUC и ATM используют для работы теневые порты, и для корректного чтения из портов нам необходимо знать адрес, по которому в TRDOS расположена пара команд:

    IN A,(C)
    RET

Обозначим этот адрес TRD_IN_ADR.

Работа с разными схемами подключения IDE различается лишь номерами портов, поэтому ло­гично использовать одни и те же процедуры с различными входными параметрами. В рассмат­риваемой программе номера портов передаются через таблицу, на которую указывает регистр IX.


Проверка на тип схемы и устройства произ­водится по следующей схеме:

1. Выбираем устройство MASTER, проверяем его на занятость (бит 7 в регистре состояний)

- если он занят - полагается, что его нет. Тогда также проверяем устройство SLAVE.

- если занят и SLAVE - либо оба устройства не подключены, либо тестируемая схема проверку не прошла.

2. Записываем в регистры цилиндра 0 и даём команду #EC - идентификация винчестера

- если команда принята без ошибок, то это вин­честер. Необходимо считать в буфер 512 байт и проверить байт по смещению +256. Это делает­ся, чтобы различить Nemo и Nemo-A8, если этот байт равен 255 - тест провален, иначе - прошёл ус­пешно.

- если команда выдала ошибку и в регистре цилиндра не содержится число #EB14, то тест провален (либо схема не та, либо устройство не опознано).

- если команда выдала ошибку и регистр ци­линдра равен #EB14, мы имеем дело с ATAPI-уст­ройством (это может быть либо CDROM, либо DVDROM). В этом случае даём команду #A1 (ана­лог команды #EC для atapi) и считываем в буфер 2048 байт (?) и аналогично проверяем байт +1024

3. Если тест данной схемы провален, перехо­дим на тест следующей схемы.

(здесь возникли неувязочки, потому как при тесте в Unreal0.32b7 с DVD считалось только 512 байт, остальные были забиты значениями 255).

Далее следует листинг программы.

ide_detect    ld ix,ide_tab    ;IX - указатель на таблицу портов
ide_next    ld a,(ix)    ;проверяем конец таблицы
    or (ix+1)
    jr z,no_ide    ;если конец - подстановка «ложной таблицы»
    ld a,#a0    ;тестируем master
    call test_ide
    ld (master),a
    push af
    ld a,#b0    ;тестируем slave
    call test_ide
    ld (slave),a
    pop bc
    or b
    ret nz    ;если хоть одно устройство 
        ;определилось нормально - выход
    ld de,23
    add ix,de
    jr ide_next
no_ide    ld ix,fake_ide    ;когда все тесты провалены - 
        ;подсовываем «ложный IDE»
    scf
    ret
test_ide    call out_head    ;выбираем устройство (master/slave)
    call in_state    ;устройство должно быть не занято!
    rla
    ld a,0
    ret c
    ld de,0    ;регистр цилиндра = 0
    call out_cylind
    ld a,#EC    ;#EC - команда идентификации hdd
    call ide_command
    jr nc,hdd_detect    ;если ошибки нет - это винчестер
    call in_cylind    ;иначе проверяем регистр цилиндра
    ld hl,#EB14    ;если он не #EB14 - устройство не определено
    and a
    sbc hl,de
    ld a,h
    or l
    jr nz,test_fail
    ld a,#A1    ;#A1 - команда идентификации atapi
    call ide_command
    jr c,test_fail
    ld hl,bufer    ;считываем в буфер 2048 байт
    ld bc,1024
    call ide_read
    ld a,(bufer+1024)    ;и проверяем байт +1024 на равенство 255
    inc a
    jr z,test_fail
    ld a,2
    ret
hdd_detect    ld hl,bufer    ;для винчестера - считываем в буфер 512 байт
    ld bc,256
    call ide_read
    ld a,(bufer+256)    ;и проверяем +256 на равенство 255
    inc a
    jr z,test_fail
    ld a,1
    ret
test_fail    xor a
    ret
;Далее следует таблица портов IDE
;+0 comand/state
;+2 head
;+4 cyl.hi
;+6 cyl.low
;+8 sector
;+10 counter
;+12 error
;+14 data.hi
;+16 data.low
;+18 IN prog adr
;+20 OUT prog adr
;+22 sheme ID
ide_tab
    dw #FFBE    ;порты SMUC (см. таблицу выше)
    dw #FEBE
    dw #FDBE
    dw #FCBE 
    dw #FBBE
    dw #FABE
    dw #F9BE
    dw #D8BE
    dw #F8BE
    dw shadow_in    ;адреса чтения и записи в/из портов
    dw shadow_out
    db 1    ;идентификатор (для удобства)
    dw #FEEF    ;порты ATM2+
    dw #FECF 
    dw #FEAF 
    dw #FE8F 
    dw #FE6F 
    dw #FE4F 
    dw #FE2F
    dw #FF0F
    dw #FE0F 
    dw shadow_in
    dw shadow_out
    db 2
    dw #FFF0    ;порты Nemo
    dw #FFD0
    dw #FFB0 
    dw #FF90 
    dw #FF70 
    dw #FF50
    dw #FF30
    dw #FF11
    dw #FF10
    dw open_in
    dw open_out
    db 3
    dw #FEF0    ;порты Nemo-A8
    dw #FED0
    dw #FEB0
    dw #FE90
    dw #FE70
    dw #FE50
    dw #FE30
    dw #FF10
    dw #FE10 
    dw open_in
    dw open_out
    db 4
fake_ide    ds 18,#FF    ;ложная таблица
    dw fake_in
    dw fake_out
    db 0
ide_command    call out_com    ;подать устройству команду
    call ide_busy
ide_error    call in_state
    rra
    ret nc
    call in_error
    scf
    ret
ide_busy    call in_state    ;дождаться освобождения устройства
    rla
    jr c,ide_busy
    ret
ide_read    push bc    ;считать bc слов на hl
    call in_data_l    ;устройство должно быть готово к передаче
    ld (hl),a
    inc hl
    call in_data_h
    ld (hl),a
    inc hl
    pop bc
    dec bc
    ld a,b
    or c
    jr nz,ide_read
    ret
out_cylind    ld a,d    ;вывод de в регистр цилиндра
    call out_cyl_h
    ld a,e
    jr out_cyl_l
in_cylind    call in_cyl_h    ;чтение регистра цилиндра в de
    ld d,a
    call in_cyl_l
    ld e,a
    ret
in_state    ld c,(ix)    ;подпрограммы чтения из регистров IDE
    ld b,(ix+1)
    jr in_bc
in_head    ld c,(ix+2)
    ld b,(ix+3)
    jr in_bc
in_cyl_h    ld c,(ix+4)
    ld b,(ix+5)
    jr in_bc
in_cyl_l    ld c,(ix+6)
    ld b,(ix+7)
    jr in_bc
in_sector    ld c,(ix+8)
    ld b,(ix+9)
    jr in_bc
in_count    ld c,(ix+10)
    ld b,(ix+11)
    jr in_bc
in_error    ld c,(ix+12)
    ld b,(ix+13)
    jr in_bc
in_data_h    ld c,(ix+14)
    ld b,(ix+15)
    jr in_bc
in_data_l    ld c,(ix+16)
    ld b,(ix+17)
in_bc    push hl
    ld l,(ix+18)
    ld h,(ix+19)
    ex (sp),hl
    ret
out_com    ld c,(ix)    ;подпрограммы записи в регистры IDE
    ld b,(ix+1)
    jr out_bc
out_head    ld c,(ix+2)
    ld b,(ix+3)
    jr out_bc
out_cyl_h    ld c,(ix+4)
    ld b,(ix+5)
    jr out_bc
out_cyl_l    ld c,(ix+6)
    ld b,(ix+7)
    jr out_bc
out_sector    ld c,(ix+8)
    ld b,(ix+9)
    jr out_bc
out_count    ld c,(ix+10)
    ld b,(ix+11)
    jr out_bc
out_error    ld c,(ix+12)
    ld b,(ix+13)
    jr out_bc
out_data_h    ld c,(ix+14)
    ld b,(ix+15)
    jr out_bc
out_data_l    ld c,(ix+16)
    ld b,(ix+17)
out_bc    push hl
    ld l,(ix+20)
    ld h,(ix+21)
    ex (sp),hl
    ret
open_in    in a,(c)    ;вывод/ввод для открытых портов
    ret
open_out    out (c),a
    ret
shadow_in    push hl    ;вывод/ввод для закрытых портов
    ld hl,trd_in_adr
    jr to_dos
shadow_out    push hl
    ld hl,10835
to_dos    ex (sp),hl
    jp 15663
fake_in    ld a,#FF
fake_out    ret
master    db 0
slave    db 0
bufer    ds 2048

После вызова ide_detect регистр IX будет ука­зывать на таблицу портов определённой схемы IDE (или на ложную таблицу). В ячейке (IX+22) со­держится идентификатор схемы.

После этого можно использовать подпрограм­мы записи/чтения в/из портов, не заботясь о том, какая схема подключена (и подключена ли). Но перед этим необходимо записывать в IX адрес таблицы, который нам выдала программа.


P.S. Метод обкатан и работает, а вот в самой программе могут быть ошибки - я её писал по памяти, не проверяя. Если кого-то заинтересует, могу найти рабочие исходники.