Files
2024-02-19 00:25:23 -05:00

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
}