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 paralelné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 ; číslo disku 0 = A:, 1 = B:, 2 = C:, 3 = D: Sector: DS 1 ; číslo sektora 0 až N (N je max. 63) Track: DS 1 ; číslo stopy 0 až N (N je max. 255) Dma: DS 2 ; cieľová/zdrojová adresa pre čítanie/zápis 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é ostatnými rutinami, pre čítanie a zápis bytu atď., takže pri vytváraní rutín vyšších úrovní ich zrejme nebude treba volať.
;------------------------------------------------------------------------------ ; Povolí prerušenie pre vstup bytu na porte A ; I: - ; O: povolené prerušenie pre vstup bytu ; M: A ;------------------------------------------------------------------------------ EnaByteIn: MVI A,09h ; 1->C4 povolenie prerušovacieho signálu INTRA OUT PIO_CW ; pre vstup bytu na porte A MVI A,0Ch ; 0->C6 zakázanie prerušovacieho signálu INTRA OUT PIO_CW ; pre výstup bytu na port A RET ;------------------------------------------------------------------------------ ; Povolí prerušenie pre výstup bytu na porte A ; I: - ; O: povolené prerušenie pre výstup bytu ; M: A ;------------------------------------------------------------------------------ EnaByteOut: MVI A,08h ; 0->C4 zakázanie prerušovacieho signálu INTRA OUT PIO_CW ; pre vstup bytu na porte A MVI A,0Dh ; 1->C6 povolenie prerušovacieho signálu INTRA OUT PIO_CW ; pre výstup bytu na port A RET ;------------------------------------------------------------------------------ ; Zistí, či je nastavené INTRA. Na INTRA sa čaká zvolený čas. ; INTRA=1, ak je platný byte na vstupe, alebo bol prečítaný byte z výstupu. ; I: Pri volaní od IsIntraD DE=timeout ; O: CY=1 - timeout ; CY=0 - OK ; M: CY, DE ;------------------------------------------------------------------------------ IsIntra: LXI D,205 ; timeout asi 5ms IsIntraD: PUSH PSW ; ulož AF IsIntra2: IN PIO_C ; zisti stav INTRA ANI 8 JNZ IsIntra3 ; ak je INTRA=1, skoč dopredu DCX D MOV A,D ORA E JNZ IsIntra2 ; a ak nie je nulové, vráť sa do slučky POP PSW ; došlo k timeoutu, obnov AF STC ; nastav CY ako príznak chyby RET IsIntra3: POP PSW ; obnov AF 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.
;------------------------------------------------------------------------------ ; Prečíta byte z PMD 32 a zároveň modifikuje CRC. ; I: B=priebežné CRC ; O: CY=1 - chyba ; CY=0 - A=C=prečítaný byte, B=modifikované CRC ; M: AF, BC ;------------------------------------------------------------------------------ ReadByte: PUSH D ; DE na zásobník CALL IsIntra ; je platný byte na vstupe? POP D ; obnov DE RC ; nie, vráť sa s chybou IN PIO_A ; prečítaj byte MOV C,A ; odlož si ho do C XRA B ; modifikuj CRC MOV B,A ; a ulož ho naspäť do B MOV A,C ; vráť načítaný 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 naspäť odošle prezentačný byte a následne samotný povel. Rutina pred odoslaním povelu inicializuje CRC.
;------------------------------------------------------------------------------ ; Pošle povel do PMD 32. Pred poslaním povelu sa najprv preverí, či na zbernici ; náhodou nie je prezentačný byte. ; I: A=povel ; O: CY=1 - chyba ; CY=0 - OK, B=A=modifikované CRC ; M: AF, B ;------------------------------------------------------------------------------ SendCommand: MOV B,A ; ulož posielaný povelový byte do B PUSH B ; BC, DE na zásobník PUSH D CALL IsPresent ; zisti, či je na zbernici prezentačný byte 0AAh ; ak áno, odošli naspäť prezentačný byte POP D POP B ; obnov DE, BC RC ; vráť sa, ak došlo ku chybe MOV A,B ; vráť posielaný povelový byte do A MVI B,0 ; vynuluj CRC ; pokračuj ďalej ;------------------------------------------------------------------------------ ; Pošle byte do PMD 32 a modifikuje priebežné CRC. ; I: A=posielaný byte, B=priebežné CRC ; O: CY=1 - chyba ; CY=0 - OK, B=A=modifikované CRC ; M: AF, B ;------------------------------------------------------------------------------ SendByte: PUSH D ; DE na zásobník CALL IsIntra ; zisti, či je voľný výstup POP D ; obnov DE RC ; vráť sa, ak došlo ku timeoutu OUT PIO_A ; pošli byte na výstup XRA B ; modifikuj CRC MOV B,A ; a ulož ho naspäť do B RET ;------------------------------------------------------------------------------ ; Zisti, či na zbernici nie je náhodou prezentačný byte 0AAh, ktorý poslala ; disketová jednotka po návrate do "idle" stavu, kedy čakala na povel. ; I: - ; O: CY=1 - chyba ; CY=0 - OK ; M: AF, DE ;------------------------------------------------------------------------------ IsPresent: CALL EnaByteIn ; povoľ prerušenie pre vstup CALL WaitQ ; malé zdržanie asi 90 µs IN PIO_C ANI 8 ; je platný byte na vstupe? JZ EnaByteOut ; ak nie, vráť sa cez povolenie prerušenia pre výstup IsPresent5: CALL EnaByteIn ; povoľ prerušenie pre vstup IsPresent4: IN PIO_A ; prečítaj byte zo vstupu CPI 0AAh ; ak je to prezentačný byte 0AAh JZ IsPresent2 ; skoč poslať prezentačný byte 0AAh na výstup LXI D,5000 ; timeout asi 125 ms CALL IsIntraD ; čakaj na byte RC ; vráť sa s chybou, ak čas vypršal JMP IsPresent4 ; inak, skoč prečítať byte zo vstupu IsPresent2: CALL WaitQ ; malé zdržanie asi 90 µs CALL EnaByteOut ; povoľ prerušenie pre výstup MVI A,0AAh ; pošli na výstup prezentačný byte 0AAh OUT PIO_A LXI D,5000 ; timeout asi 125 ms CALL IsIntraD ; čakaj na byte JC IsPresent5 ; ak nebol prezentačný byte prijatý, skoč otestovať vstup RET ; bol prijatý prezentačný byte, vráť sa s vynulovaným CY ;------------------------------------------------------------------------------ ; Krátke zdržanie ; I: - ; O: krátke zdržanie asi 90 µs ; 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á.
;------------------------------------------------------------------------------ ; Zistí, či je pripojená PMD 32 a či odpovedá prezentačným bytom. ; I: - ; O: CY=1 - chyba, timeout, PMD 32 neodpovedá prezentačným bytom ; CY=0 - OK ; 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 ; povoľ prerušenie pre vstup LXI D,10000 ; timeout asi 250 ms CALL IsIntraD ; čakaj na byte RC ; timeout, vráť sa IN PIO_A ; prečítaj prijatý byte CPI 0AAh ; je to prezentačný byte 0AAh ? STC ; nastav CY ako príznak chyby RNZ ; nie je to prezentačný byte 0AAh, vráť sa s CY CALL EnaByteOut ; povoľ prerušenie pre výstup MVI A,0AAh ; pošli ako odpoveď prezentačný 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 ihneď testuje na nulu.
Rutina ReadCheckCrc prečíta CRC z PMD 32 a porovná ho s priebežne počítaným CRC v registri B.
;------------------------------------------------------------------------------ ; Pošle CRC na výstup a počká na ACK a výsledok povelu. ; I: B=aktuálne CRC ; O: CY=1 - chyba ; CY=0 - OK, A=výsledok povelu ; M: AF, BC, DE ;------------------------------------------------------------------------------ SndCrcRdAckErr: MOV A,B ; posielané CRC do A CALL SendByte ; pošli na výstup RC ; vráť sa, ak došlo ku chybe ReadAckErr: CALL EnaByteIn ; povoľ prerušenie na vstupe CALL ReadByte ; prečítaj byte zo vstupu RC ; vráť sa, ak došlo ku chybe CPI 33h ; bolo prijate byte ACK? STC ; nastav chybový príznak RNZ ; vráť sa s chybou, ak to nebolo ACK WaitErrT15: LXI D,3000 ; timeout asi 15,5 sec WaitErrT: PUSH D ; DE na zásobník CALL IsIntra ; je platný byte na vstupe? POP D ; obnov DE JNC ReadByte ; ak áno, skoč ho prečítať DCX D ; zníž počítadlo timeoutu MOV A,D ; a testuj ho na nulu ORA E JNZ WaitErrT ; ak nie je nulové, skoč do slučky STC ; nastav príznak chyby RET ;------------------------------------------------------------------------------ ; Odošle CRC a čaká na potvrdenie. Otestuje chybový kód na nulu. ; I: B=posielané CRC ; O: CY=1 - chyba ; CY=0 - OK ; M: AF, BC, DE ;------------------------------------------------------------------------------ SndCrcRdAck: CALL SndCrcRdAckErr ; pošli CRC a čakaj ACK a výsledok povelu RC ; vráť sa, ak došlo ku chybe JMP ReadCheckCrc2 ; ak bol druhy byte skutočne 0, je to OK ;------------------------------------------------------------------------------ ; Prečíta byte zo vstupu, predstavujúci CRC ; I: - ; O: CY=1 - chyba CRC alebo timeout ; CY=0 - CRC OK ; M: AF, BC ;------------------------------------------------------------------------------ ReadCheckCrc: CALL ReadByte ; prečítaj CRC zo vstupu RC ; vráť sa, ak došlo ku chybe MOV A,B ; skontroluj CRC ReadCheckCrc2: ORA A RZ ; OK STC ; nastav príznak 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.
;------------------------------------------------------------------------------ ; Pošle do PMD 32 číslo sektora vrátane čísla disku a čísla stopy. ; I: číslo sektora, disku a stopy na adresách Drive, Sector a Track ; B=priebežné CRC ; O: CY=1 - chyba ; CY=0 - OK, B=A=modifikovane CRC ; M: AF, BC ;------------------------------------------------------------------------------ SendSectorTrack: LDA Drive ; číslo disku transformuj do 7. a 6. bitu ORA A ; disk A: ? JZ SendSectorTrack2 ; áno, skoč MVI C,0C0H ; transformovaná hodnota pre disk D: XRI 3 ; invertuj (prehoď) bity čísla disku JZ SendSectorTrack3 ; je to disk D: ? áno, skoč RRC ; pre disky B: a C: posuň číslo do horných bitov RRC SendSectorTrack2: ; C bude obsahovať transformovanú hodnotu čísla disku MOV C,A ; C -> 00h=A: 80h=B: 40h=C: 0C0h=D: SendSectorTrack3: LDA Sector ; vezmi číslo sektora 0 až N (N je max. 63) ANI 03FH ; -> nemusí byť, ak sa zabezpečí povolený rozsah ORA C ; do horných bitov pridaj číslo disku CALL SendByte ; pošli číslo disku a sektora RC ; návrat s CY=1 pri chybe LDA Track ; číslo stopy 0 až N (N je max. 255) JMP SendByte ; pošli stopu ;------------------------------------------------------------------------------ ; Pošle povel "B" a prečíta BOOT - sektor 0 na 0. stope. ; Vždy je to z jednotky A: ; I: cieľová adresa na adrese Dma ; O: CY=1 - chyba timeout, CRC ; CY=0 - OK ; M: všetky ;------------------------------------------------------------------------------ ReadBoot: MVI A,'B' ; povel "B" - prečítaj BOOT CALL SendCommand ; odošli na výstup JMP ReadSectorB ;------------------------------------------------------------------------------ ; Povel "Q" - prečítanie sektora z disku. ; I: číslo sektora, disku a stopy na adresách Drive, Sector, Track ; cieľová adresa na adrese Dma ; O: CY=1 - chyba timeout, CRC ; CY=0 - OK ; M: všetky ;------------------------------------------------------------------------------ ReadSector: MVI A,'Q' ; povel "Q" - prečítaj sektor CALL SendCommand ; odošli na výstup RC ; vráť sa, ak došlo ku chybe CALL SendSectorTrack ; pošli stopu a sektor ReadSectorB: RC ; vráť sa, ak došlo ku chybe CALL SndCrcRdAck ; pošli CRC a počkaj na potvrdenie RC ; vráť sa, ak došlo ku chybe MVI B,0 ; vynuluj CRC MVI D,128 ; nastav počítadlo LHLD Dma ; do HL cieľovú adresu ReadSector2: CALL ReadByte ; prečítaj byte RC ; vráť sa, ak došlo ku chybe MOV M,A ; ulož byte do pamäte INX H ; posuň ukazateľ DCR D ; zníž počítadlo prijímaných bytov JNZ ReadSector2 ; ak neboli prijaté všetky, vráť sa do slučky JMP ReadCheckCrc ; skoč prečítať CRC ;------------------------------------------------------------------------------ ; Povel "T" - zapíše sektor na disk. ; I: číslo sektora, disku a stopy na adresách Drive, Sector, Track ; zdrojová adresa na adrese Dma ; O: CY=1 - chyba timeout, CRC ; CY=0 - OK ; M: vsetky ;------------------------------------------------------------------------------ WriteSector: MVI A,'T' ; povel "T" - zapíš sektor CALL SendCommand ; odošli na výstup RC ; vráť sa, ak došlo ku chybe CALL SendSectorTrack ; pošli stopu a sektor RC ; vráť sa, ak došlo ku chybe MVI D,128 ; nastav počítadlo LHLD Dma ; do HL zdrojová adresa WriteSector2: MOV A,M ; byte z pamäte CALL SendByte ; odošli RC ; vráť sa, ak došlo ku chybe INX H ; posuň ukazateľ DCR D ; zníž počítadlo bytov JNZ WriteSector2 ; opakuj pre všetkých 128 bytov sektora JMP SndCrcRdAck ; odošli CRC a čakaj 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".
- zdrojový súbor tohto ovládača
- komunikačný protokol PMD 32/PMD 32-SD