294 lines
6.3 KiB
C
294 lines
6.3 KiB
C
/////////////////////////////////////
|
|
// 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <conio.h>
|
|
#include <dos.h>
|
|
|
|
#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<<<Seeking>>> \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<<<Reading a sector>>> \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
|
|
}
|