///////////////////////////////////// // Demo of how to access a floppy // // drive without of a BIOS stuff // // // // Program uses DMA data transferr // ///////////////////////////////////// //////////////////////////////// // Compiler Borland C/C++ 3.1 // //////////////////////////////// #ifdef __cplusplus #define __CPPARGS ... #else #define __CPPARGS #endif #include #include #include #include #define CYL 0 typedef struct _DPT_ { unsigned char srt_hut; unsigned char dma_hlt; unsigned char motor_w; unsigned char sec_size; unsigned char eot; unsigned char gap_rw; unsigned char dtl; unsigned char gap_f; unsigned char fill_char; unsigned char hst; unsigned char mot_start; } DPT; DPT far *get_dpt (void); void fdc_out (unsigned char byte); int fdc_inp (void); void int_wait (void); void dma_init (void far *); void tdelay (int cnt); void interrupt IRQ6 (__CPPARGS); void interrupt (*oldIRQ6) (__CPPARGS); char buffer[512]; static int IRQ=0; void main (void) { unsigned i; long l; char status[7], main_status; DPT _far *fdpt; FILE *sect; printf ("\n\nWork with Floppy Drive Controller\n"); oldIRQ6 = _dos_getvect (8+6); _dos_setvect (8+6, IRQ6); // We're opening a file in order to store in it // the very first sector of the diskette sect = fopen ("!sector.dat", "wb+"); // Getting Diskette Parameter Table pointer fdpt = get_dpt(); // Turning on the motor in the "A:" drive // Enabling interrupts before actual turning on _enable(); outp (0x3F2, 0x1C); // Waiting while motor speeds up tdelay (18); // Displaying contents of the controller state register printf ("Motor is on.\t\t"); printf ("State: %02.2X\n", inp(0x3F4)); // recalibrate fdc_out (7); fdc_out (0); int_wait(); // We need to move drive head to the CYL track // "Seek" command fdc_out (0xf); // The "Seek" command needs 2 parameters: // a Head/Drive number and a Track number. // Since we're working with "A:" drive and 0 head, // first parameter is 0, second parameter is CYL fdc_out (0); fdc_out (CYL); // Displaying contents of the controller state register printf ("\n<<>> \t\t"); printf ("State: %02.2X\n", inp(0x3F4)); // Interrupt notifies us about operation end int_wait(); // Delay for head positioning tdelay (5); // In order to check the result of the "Seek" command // we're sending "Read Interrupt State" command // Displaying ST0 register and number of a track after // "Seek" command execution PCN fdc_out (0x8); printf ("Interrupt state:\t"); printf (" ST0: %02.2X, \t", fdc_inp()); printf ("PCN: %02.2X\n", fdc_inp()); // For more detailed info of FDC state // we're sending "Read Media/Drive State" command, // displaying ST3 register fdc_out (4); fdc_out (0); printf ("Media/Drive state:\t ST3: %02.2X\n", fdc_inp()); // Setting speed of data transfer to 500 KB/sec outp (0x3F7, 0); // DMA initialization dma_init ((void far *)buffer); // "Read Data" command fdc_out (0x66); fdc_out (0x0); // drive 0, head 0 fdc_out (CYL); // track CYL fdc_out (0); // head 0 fdc_out (1); // sector no 1 // Sending some technical info to FDC. // This info may be obtained form the Diskette Parameter Table. // Parameters are: // - sector size; // - last sector on a track; // - gap length; // - number of bytes to be read/write fdc_out (fdpt->sec_size); fdc_out (fdpt->eot); fdc_out (fdpt->gap_rw); fdc_out (fdpt->dtl); // Waiting for interrupt (end of operation) int_wait(); // Getting and displaying results of // the "Read Data" command printf ("\n<<>> \n"); printf (" State bytes (ST0,ST1,ST2,C,H,R,N):\n"); for(i=0; i<7; i++) printf("%02.2X\t", (char) fdc_inp()); printf("\n"); // Saving sector to the file for(i=0; i<512; i++) fputc (buffer[i],sect); fclose (sect); // Turning motor off outp (0x3F2, 0xC); lll: _dos_setvect (8+6, oldIRQ6); } // Writes a byte to FDC void fdc_out (unsigned char parm) { asm mov dx,3F4h loop_fdc_out: asm in al,dx asm test al,80h // Is controller ready? asm jz loop_fdc_out // No, waiting... asm inc dx asm mov al, parm // Writing the byte asm out dx, al } // Reads a byte from FDC int fdc_inp (void) { asm mov dx,3F4h loop_fdc_inp: asm in al,dx asm test al,80h // Is controller ready? asm jz loop_fdc_inp // No, waiting... asm inc dx asm in al, dx // Reading a byte return _AL; } // Waits for an interrupt generated by FDC void int_wait (void) { _enable(); while (IRQ==0) {}; IRQ = 0; } void interrupt IRQ6 (__CPPARGS) { IRQ = 1; outportb (0x20, 0x20); } // DMA initialization routine void dma_init (void far *buf) { unsigned long f_adr; unsigned sg, of; // Computing 24-bit address for the data buffer f_adr = ((unsigned long)FP_SEG(buf) << 4) + (unsigned long)FP_OFF(buf); // Splitting the address into a page number // and an offset sg = (f_adr >> 16) & 0xff; of = f_adr & 0xffff; // Disabling ints during DMA programming _disable(); asm mov al,46h // FDC read data command asm out 12,al // We're working with 16-bit ports. // Next byte sent to 16-bit port is less significiant asm out 11,al // DMA mode asm mov ax,of // Buffer offset LSB asm out 4,al asm mov al,ah // Buffer offset MSB asm out 4,al asm mov ax,sg // Page number asm out 81h,al asm mov ax,511 // Data length asm out 5,al asm mov al,ah asm out 5,al asm mov al,2 // channel 2 enabled asm out 10,al // It's now safe to enable ints _enable(); } // This routine returns a Diskette Parameter Table address DPT far *get_dpt(void) { void far * far *ptr; ptr = (void far * far *)MK_FP(0x0, 0x78); return (DPT far*)(*ptr); } // This routine waits for cnt timer ticks. // Timer frequency is 18.2 Hz void tdelay (int cnt) { asm push bx asm push dx asm push si asm mov si, cnt asm mov ah, 0 asm int 1ah asm mov bx, dx asm add bx, si delay_loop: asm int 1ah asm cmp dx, bx asm jne delay_loop asm pop si asm pop dx asm pop bx }