Blog:Ovládač pre komunikáciu s PMD 32

Z PMD 85 Infoserver

Ovládač pre komunikáciu s PMD 32

PMD 85-3 obsahuje vo svojej EPROM, na rozdiel od predošlých verzií PMD 85, ovládač pre disketovú jednotku PMD 32. Keďže sa ukázalo, že PMD 32-SD, teda náhradu za PMD 32, by si chceli niektorí pripojiť aj k iným počítačom (SAPI, IQ-151), uvediem tu výpis tohto ovládača, aby si ho záujemci mohli prispôsobiť pre svoj počítač. Ovládač nie je úplnou kópiou z PMD 85-3, ale je naopak optimalizovaný a skrátený. Zároveň ale neobsahuje implementáciu všetkých povelov, ktoré PMD 32, či PMD 32-SD poskytujú, pretože to ani nie je účelné. Verím, že prípadný záujemca si ďalšie povely bez problémov doplní sám.

PMD 32, resp. PMD 32-SD komunikuje s nadradeným počítačom prostredníctvom obojsmerného paraleného kanála, ktorý je na strane počítača tvorený interfejsovým obvodom PPI 8255.

Z 8255 sa využíva iba port PA v móde 2 ako 8 bitová obojsmerná dátová zbernica a 4 riadiace bity hornej polovice portu PC pre handshake. Stav zbernice sa ale testuje 3. bitom portu PC (INTRA), ktorý podľa zvoleného smeru prenosu indikuje, či je na zbernici platný byte (vstup dát), alebo bol byte perifériou prečítaný (výstup dát). Smer prenosu dát sa volí povolením alebo zakázaním prerušenia (signál INTRA) pre vstup alebo výstup dát.

Ovládač potrebuje niekde v RAM 4 premenné:

Drive:  DS     1            ; cislo disku 0 = A:, 1 = B:, 2 = C:, 3 = D:
Sector: DS     1            ; cislo sektora 0 az N (N je max. 63)
Track:  DS     1            ; cislo stopy 0 az N (N je max. 255)
Dma:    DS     2            ; cielova/zdrojova adresa pre citanie/zapis sektora

Základná komunikácia na najnižšej úrovni zahŕňa niekoľko elementárnych rutín:

  • Nastavenie smeru prenosu a test, či je na vstupe platný byte, alebo či bol perifériou byte prečítaný

Tieto rutiny sú volané ostanými rutinami, pre čítanie a zápis bytu atď., takže pri vytváraní rutín vyšších úrovní ich zrejme nebude treba volať.

;------------------------------------------------------------------------------
; Povoli prerusenie pre vstup na porte A
; I: -
; O: povolene prerusenie pre vstup bytu
; M: A
;------------------------------------------------------------------------------
EnaByteIn:
        MVI    A,09h        ; 1->C4 povolenie prerusovacieho signalu INTRA
        OUT    PIO_CW       ; pre vstup na porte A
        MVI    A,0Ch        ; 0->C6 zakazanie prerusovacieho signalu INTRA
        OUT    PIO_CW       ; pre vystup porte A
        RET
 
;------------------------------------------------------------------------------
; Povoli prerusenie pre vystup portu A
; I: -
; O: povolene prerusenie pre vystup bytu
; M: A
;------------------------------------------------------------------------------
EnaByteOut:
        MVI    A,08h        ; 0->C4 zakazanie prerusovacieho signalu INTRA
        OUT    PIO_CW       ; pre vstup na porte A
        MVI    A,0Dh        ; 1->C6 povolenie prerusovacieho signalu INTRA
        OUT    PIO_CW       ; pre vystup na porteA
        RET
 
;------------------------------------------------------------------------------
; Zisti, ci je nastavene INTRA. Na INTRA sa caka zvoleny cas.
; INTRA=1, ak je platny byte na vstupe, alebo bol precitany byte z vystupu.
; I: Pri volani od IsIntraD DE=timeout
; O: CY=1 - timeout
;    CY=0 - OK
; M: CY, DE
;------------------------------------------------------------------------------
IsIntra:
        LXI    D,205        ; timeout asi 5ms
IsIntraD:
        PUSH   PSW          ; uloz AF
IsIntra2:
        IN     PIO_C        ; zisti stav INTRA
        ANI    8
        JNZ    IsIntra3     ; ak je INTRA=1, skoc dopredu
        DCX    D
        MOV    A,D
        ORA    E
        JNZ    IsIntra2     ; a ak nie je nulove, vrat sa do slucky
        POP    PSW          ; doslo k timeoutu, obnov AF
        STC                 ; nastav CY ako priznak chyby
        RET
IsIntra3:
        POP    PSW
        ORA    A            ; vynuluj CY
        RET
  • Čítanie bytu

Pred volaním tejto rutiny musí byť povolený vstup bytu volaním rutiny EnaByteIn. Rutina priebežne modifikuje CRC, ktoré sa prenáša v registri B.

;------------------------------------------------------------------------------
; Precita byte z PMD 32 a zaroven modifikuje CRC.
; I: B=priebežné CRC
; O: CY=1 - chyba
;    CY=0 - A=C=precitany byte, B=modifikovane CRC
; M: AF, BC
;------------------------------------------------------------------------------
ReadByte:
        PUSH   D            ; DE na zasobnik
        CALL   IsIntra      ; je platny byte na vstupe?
        POP    D            ; obnov DE
        RC                  ; nie, vrat sa s chybou
        IN     PIO_A        ; precitaj byte
        MOV    C,A          ; odloz si ho do C
        XRA    B            ; modifikuj CRC
        MOV    B,A          ; a uloz ho naspat do B
        MOV    A,C          ; vrat nacitany byte do A
        RET
  • Odoslanie bytu a povelu

Pred volaním rutiny SendByte musí byť povolený výstup bytu volaním rutiny EnaByteOut. Rutina priebežne modifikuje CRC, ktoré sa prenáša v registri B.

Rutina SendCommand slúži pre odoslanie povelu. Táto rutina najprv volaním rutiny IsPresent preveruje, či náhodou nie je na zbernici prezentačný byte, ktorý posiela PMD 32, keď je "v kľude". Pokiaľ na zbernici prezentačný byte nie je, povolí sa výstup bytu a povel sa odošle. Inak sa najprv napsäť odošle prezentačný byte a následne samotný povel. Rutina pred odoslaním povelu inicializuje CRC.

;------------------------------------------------------------------------------
; Posle povel do PMD 32. Pred poslanim povelu sa najprv preveri, ci na zbernici
; nahodou nie je prezentacny byte.
; I: A=povel
; O: CY=1 - chyba
;    CY=0 - OK, B=A=modifikovane CRC
; M: AF, B
;------------------------------------------------------------------------------
SendCommand:
       MOV     B,A          ; uloz posielany byte do B
       PUSH    B            ; BC, DE na zasobnik
       PUSH    D
       CALL    IsPresent    ; zisti, ci je na zbernici prezentacny byte 0AAh
                            ; ak ano, odosli naspat prezentacny byte
       POP     D
       POP     B            ; obnov DE, BC
       RC                   ; vrat sa, ak doslo ku chybe
       MOV     A,B          ; vrat posielany byte do A
       MVI     B,0          ; vynuluj CRC
; pokracuj dalej
 
;------------------------------------------------------------------------------
; Posle byte do PMD 32 a modifikuje priebezne CRC.
; I: A=posielany byte, B=priebezne CRC
; O: CY=1 - chyba
;    CY=0 - OK, B=A=modifikovane CRC
; M: AF, B
;------------------------------------------------------------------------------
SendByte:
       PUSH    D            ; DE na zasobnik
       CALL    IsIntra      ; zisti, ci je volny vystup
       POP     D            ; obnov DE
       RC                   ; vrat sa, ak doslo ku timeoutu
       OUT     PIO_A        ; posli byte na vystup
       XRA     B            ; modofikuj CRC
       MOV     B,A          ; a uloz ho naspat do B
       RET
 
;------------------------------------------------------------------------------
; Zisti, ci na zbernici nie je nahodou prezentacny byte 0AAh, ktory poslala
; disketova jednotka po navrate do "idle" stavu, kedy cakala na povel.
; I: -
; O: CY=1 - chyba
;    CY=0 - OK
; M: AF, DE
;------------------------------------------------------------------------------
IsPresent:
       CALL    EnaByteIn    ; povol prerusenie pre vstup
       CALL    WaitQ        ; male zdrzanie asi 90 us
       IN      PIO_C
       ANI     8            ; je platny byte na vstupe?
       JZ      EnaByteOut   ; ak nie, vrat sa cez povolenie prerusenia pre vystup
IsPresent5:
       CALL    EnaByteIn    ; povol prerusenie pre vstup
IsPresent4:
       IN      PIO_A        ; precitaj byte zo vstupu
       CPI     0AAh         ; ak je to prezentacny byte 0AAh
       JZ      IsPresent2   ; skoc poslat prezentacny byte 0AAh na vystup
       LXI     D,5000       ; timeout asi 125 ms
       CALL    IsIntraD     ; cakaj na byte
       RC                   ; timeout, vrat sa
       JMP     IsPresent4   ; ano, skoc precitat byte zo vstupu
 
IsPresent2:
       CALL    WaitQ        ; male zdrzanie asi 90 us
       CALL    EnaByteOut   ; povol prerusenie pre vystup
       MVI     A,0AAh       ; posli na vystup prezentacny byte 0AAh
       OUT     PIO_A
       LXI     D,5000       ; timeout asi 125 ms
       CALL    IsIntraD     ; cakaj na byte
       JC      IsPresent5   ; ak nebol prezentacny byte prijaty, skoc otestovat vstup
       RET                  ; bol prijaty prezentacny byte, vrat sa s vynulovanym CY
 
;------------------------------------------------------------------------------
; Kratke zdrzanie
; I: -
; O: kratke zdrzanie asi 90 us
; M: AF
;------------------------------------------------------------------------------
WaitQ:
       MVI     A,20
WaitQ1:
       DCR     A
       JNZ     WaitQ1
       RET
  • Zistenie, či je PMD 32 pripojená
  • Táto rutina musí byť volaná ako prvá, pretože inicializuje 8255 a preveruje, či je PMD 32 pripojená.
;------------------------------------------------------------------------------
; Zisti, ci je pripojena PMD 32 a ci odpoveda prezentacnym bytom.
; I: -
; O: CY=1 - chyba, timeout, PMD 32 neodpoveda prezentacnym bytom
; M: AF, DE, B
;------------------------------------------------------------------------------
CheckDevice:
       MVI     A,0C4h       ; inicializuj GPIO 8255
       OUT     PIO_CW       ; SK A: MOD 2, A-IN/OUT,  SK B: MOD 1, B-OUT
       CALL    EnaByteIn    ; povol prerusenie pre vstup
       LXI     D,10000      ; timeout asi 250 ms
       CALL    IsIntraD     ; cakaj na byte
       RC                   ; timeout, vrat sa
       IN      PIO_A        ; precitaj prijaty byte
       CPI     0AAh         ; je to prezentacny byte 0AAh ?
       STC                  ; nastav CY ako priznak chyby
       RNZ                  ; nie je to prezentacny byte 0AAh, vrat sa s CY
       CALL    EnaByteOut   ; povol prerusenie pre vystup
       MVI     A,0AAh       ; posli ako odpoved prezentacny byte 0AAh
       JMP     SendByte
  • Rutiny pre ukončenie povelu

Rutina SndCrcRdAckErr slúži pre odoslanie CRC, ktoré sa priebežne počíta v registri B. Po odoslaní sa počká na potvrdzovací byte ACK a na chybový kód.

Rutina SndCrcRdAck je rovnaká ako predošlá, ale chybový kód sa testuje na nulu.

Rutina ReadCheckCrc precita CRC z PMD 32 a porovná ho s priebežne počítaným CRC v registri B.

;------------------------------------------------------------------------------
; Posle CRC na vystup a pocka na ACK a vysledok povelu.
; I: B=aktualne CRC
; O: CY=1 - chyba
;    CY=0 - OK, A=vysledok povelu
; M: AF, BC, DE
;------------------------------------------------------------------------------
SndCrcRdAckErr:
       MOV     A,B          ; posielane CRC do A
       CALL    SendByte     ; posli na vystup
       RC                   ; vrat sa, ak doslo ku chybe
ReadAckErr:
       CALL    EnaByteIn    ; povol prerusenie na vstupe
       CALL    ReadByte     ; precitaj byte zo vstupu
       RC                   ; vrat sa, ak doslo ku chybe
       CPI     33h          ; bolo prijate byte ACK?
       STC                  ; nastav chybovy priznak
       RNZ                  ; vrat sa s chybou, ak to nebolo ACK
WaitErrT15:
       LXI     D,3000       ; timeout asi 15,5 sec
WaitErrT:
       PUSH    D            ; DE na zasobnik
       CALL    IsIntra      ; je platny byte na vstupe?
       POP     D            ; obnov DE
       JNC     ReadByte     ; ak ano, skoc ho precitat
       DCX     D            ; zniz pocitadlo timeoutu
       MOV     A,D          ; a testuj ho na nulu
       ORA     E
       JNZ     WaitErrT     ; ak nie je nulovy, skoc do slucky
       STC                  ; nastav priznak chyby
       RET
 
;------------------------------------------------------------------------------
; Odosle CRC a caka na potvrdenie. Otestuje chybovy kod na nulu.
; I: B=posielane CRC
; O: CY=1 - chyba
;    CY=0 - OK
; M: AF, BC, DE
;------------------------------------------------------------------------------
SndCrcRdAck:
       CALL    SndCrcRdAckErr ; posli CRC a cakaj ACK a vysledok povelu
       RC                     ; vrat sa, ak doslo ku chybe
       JMP     ReadCheckCrc2  ; ak bol druhy byte skutocne 0, je to OK
 
;------------------------------------------------------------------------------
; Precita byte zo vstupu, predstavujuci CRC
; I: -
; O: CY=1 - chyba CRC alebo timeout
;    CY=0 - CRC OK
; M: AF, BC
;------------------------------------------------------------------------------
ReadCheckCrc:
       CALL    ReadByte     ; precitaj CRC zo vstupu
       RC                   ; vrat sa, ak doslo ku chybe
       MOV     A,B          ; skontroluj CRC
ReadCheckCrc2:
       ORA     A
       RZ                   ; OK
       STC                  ; nastav priznak chyby
       RET
  • Rutiny pre čítanie a zápis sektora

Jedná sa o povely B, Q a T. Povely Q a T používajú rutinu SendSectorTrack, ktorá odošle sektor a disk ako prvý byte a následne stopu ako druhý byte. Aj keď má zvyčajne prvý sektor na disku fyzické číslo 1, PMD 32 vo svojich poveloch čísluje sektory od 0. Číslo disku od 0 do 3 sa posiela v horných dvoch bitoch prvého bytu. Aby sa zachovala spätná kompatibilita s pôvodnou PMD 32, kde sú iba dva disky a disk B je označený siedmym bitom, bity označujúce disk sú zamenené. Keďže žiadna komunikácia nie je bez chýb, (tieto) povely je vhodné vložiť do slučky niekoľkých pokusov pri chybe. BIOS PMD 85 napr. používa 4 pokusy.

;------------------------------------------------------------------------------
; Posle do PMD 32 cislo sektoru vratane cisla disku a cislo stopy.
; vstup: cislo sektoru, disku a stopy na adresach Drive, Sector, Track
;        B=priebezne CRC
; vystup: CY = 1 - chyba
;         CY = 0 - OK, B = A = modifikovane CRC
; meni: AF, BC
;------------------------------------------------------------------------------
SendSectorTrack:
       LDA     Drive        ; cislo disku transformuj do 7. a 6. bitu
       ORA     A            ; disk A: ?
       JZ      SendSectorTrack2 ; ano, skoc
       MVI     C,0C0H       ; transformovana hodnota pre disk D:
       XRI     3            ; invertuj (prehod) bity cisla disku
       JZ      SendSectorTrack3 ; je to disk D: ? ano, skoc 
       RRC                  ; pre disky B: a C: posun cislo do hornych bitov
       RRC
SendSectorTrack2:           ; C bude obsahovat transformovanu hodnotu cisla disku
       MOV     C,A          ; C -> 00h=A: 80h=B: 40h=C: 0C0h=D:
SendSectorTrack3:
       LDA     Sector       ; vezmi cislo sektora 0 az N (N je max. 63)
       ANI     03FH         ;  -> nemusi byt, ak sa zabezpeci povoleny rozsah
       ORA     C            ; do hornych bitov pridaj cislo disku
       CALL    SendByte     ; posli cislo disku a sektora
       RC                   ; navrat s CY=1 pri chybe
       LDA     Track        ; cislo stopy 0 az N (N je max. 255)
       JMP     SendByte     ; posli stopu
 
;------------------------------------------------------------------------------
; Posle povel "B" a precita BOOT - sektor 0 a 0. stope.
; Vzdy je to z jednotky A:
; vstup: cielova adresa na adrese Dma
; vystup: CY = 1 - chyba timeout, CRC
;         CY = 0 - OK
; meni: vsetky
;------------------------------------------------------------------------------
ReadBoot:
       MVI     A,'B'        ; povel "B" - precitaj BOOT
       CALL    SendCommand  ; odosli na vystup
       JMP     ReadSectorB
 
;------------------------------------------------------------------------------
; Povel "Q" - precitanie sektora z disku.
; vstup: cislo sektoru, disku a stopy na adresach Drive, Sector, Track
;        cielova adresa na adrese Dma
; vystup: CY = 1 - chyba timeout, CRC
;         CY = 0 - OK
; meni: vsetky
;------------------------------------------------------------------------------
ReadSector:
       MVI     A,'Q'        ; povel "Q" - precitaj sektor
       CALL    SendCommand  ; odosli na vystup
       RC                   ; vrat sa, ak doslo ku chybe
       CALL    SendSectorTrack ; posli stopu a sektor
ReadSectorB:
       RC                   ; vrat sa, ak doslo ku chybe
       CALL    SndCrcRdAck  ; posli CRC a pockaj na potvrdenie
       RC                   ; vrat sa, ak doslo ku chybe
       MVI     B,0          ; vynuluj CRC
       MVI     D,128        ; nastav pocitadlo
       LHLD    Dma          ; do HL adresa pamati
ReadSector2:
       CALL    ReadByte     ; precitaj byte
       RC                   ; vrat sa, ak doslo ku chybe
       MOV     M,A          ; uloz byte do pamati
       INX     H            ; posun ukazatel
       DCR     D            ; zniz pocitadlo prijimanych bytov
       JNZ     ReadSector2  ; ak neboli prijate vsetky, vrat sa do slucky
       JMP     ReadCheckCrc ; skoc precitat CRC
 
;------------------------------------------------------------------------------
; Povel "T" - zapise sektor na disk.
; vstup: cislo sektoru, disku a stopy na adresach Drive, Sector, Track
;        zdrojova adresa na adrese Dma
; vystup: CY = 1 - chyba timeout, CRC
;         CY = 0 - OK
; meni: vsetky
;------------------------------------------------------------------------------
WriteSector:
       MVI     A,'T'        ; povel "T" - zapis sektor
       CALL    SendCommand  ; odosli na vystup
       RC                   ; vrat sa, ak doslo ku chybe
       CALL    SendSectorTrack ; posli stopu a sektor
       RC                   ; vrat sa, ak doslo ku chybe
       MVI     D,128        ; nastav pocitadlo
       LHLD    Dma          ; do HL adresa pamati
WriteSector2:
       MOV     A,M          ; byte z pamati
       CALL    SendByte     ; odosli
       RC                   ; vrat sa, ak doslo ku chybe
       INX     H            ; posun ukazatel
       DCR     D            ; zniz pocitadlo bytov
       JNZ     WriteSector2 ; opakuj pre vsetkych 128 bytov
       JMP     SndCrcRdAck  ; odosli CRC a cakaj na potvrdenie

Download

Zdroják je pripravený na kompiláciu v ASe. Ale pochopiteľne je ho možné upraviť a kompilovať "v čomkoľvek".