Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

arch_fdc.c

/* $Id: arch_fdc.c,v 1.32 2009-01-28 12:59:15 potyra Exp $ 
 *
 * Copyright (C) 2006-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG_CONTROL_FLOW 0

#define FIFO_LEN  16  /* maximum length of FIFO for Data Register */

#define DMA_BUFFER_SIZE (64 * 1024) /* maximum buffer size for DMA channel 2 */

#ifdef STATE
struct {
      unsigned char track[4]; /* Drive should be above this track. */

      unsigned char fifo_in[FIFO_LEN];
      unsigned char fifo_in_count;
      unsigned char fifo_out[FIFO_LEN];
      unsigned char fifo_out_count;

      unsigned char command;
      unsigned int state;
      union {
            struct {          /* Read parameters */
                  unsigned char c;
                  unsigned char h;
                  unsigned char r;
            } read;
            struct {          /* Write parameters */
                  unsigned char c;
                  unsigned char h;
                  unsigned char r;
            } write;
            struct {          /* Relative seek parameters */
                  unsigned short rcn;
            } relative_seek;
      } u;

      unsigned char id_cyl;         /* Last ID read. */
      unsigned char id_sec;
      unsigned int buffer_valid;

      unsigned int dma_size;        /* sector size to read/write */
      unsigned int dma_count;       /* Pointer to data position. */
      unsigned char dma_buffer[DMA_BUFFER_SIZE];
      unsigned int tc;

      unsigned char pending_int[4]; /* set to one for each drive
                                 on which the last interrupt
                                 status has not yet been sensed */

      /* Digital Output Register (DOR) */
      unsigned char motor;          /* Motor running */
      unsigned char dma_gate;       /* DMA Gate */
      unsigned char n_reset;        /* Reset was not pulled? */
      unsigned char drive_sel;      /* Drive Select */

      /* Main status register */
      unsigned char mrq;            /* Data Register is ready */
      unsigned char dio;            /* Data Register input/output needed */
#define FDC_DIR_WRITE   0           /* Guest may write to Data register */
#define FDC_DIR_READ    1           /* Guest must read from Data register */
      unsigned char non_dma;        /* Non-DMA mode flag */
      unsigned char cmd_busy;       /* Controller busy */
      unsigned char drv_busy;       /* Drive busy */

      /* Input signals */
      unsigned char t0;       /* Track 0 */
      unsigned char disk_change;    /* new floppy disk in drive */
      unsigned char wp;       /* Write Protected */
      unsigned char index;          /* Index hole */

      /* Configuration Control Register (CCR) */
      unsigned char drate_sel;      /* Data rate values */

      /* Tape Drive Register (TDR) */
      unsigned char tape_sel;       /* Tape drive register */

      /* Status register 0 */
      /* (one for each drive) */
      struct {
            unsigned char ic;       /* Interrupt Code */
            unsigned char se;       /* Seek End */
            unsigned char ec;       /* Equipment Check */
            unsigned char hds;            /* Head address */
      } st0[4];

      /* Status register 1 */
      unsigned char en;       /* End of Cylinder */
      unsigned char de;       /* Data Error */
      unsigned char or;       /* Overrun/Underrun */
      unsigned char nw;       /* Not Writable */
      unsigned char ma;       /* Missing Address Mark */

      /* Status register 2 */
      unsigned char cm;       /* Control Mark */
      unsigned char dd;       /* Data Error in Data Field */
      unsigned char wc;       /* Wrong Cylinder */
      unsigned char bc;       /* Bad Cylinder */
      unsigned char md;       /* Missing Data Address Mark */

      /* Status register 3 */
      /* Returns states of external signals. */

      /* parameters Intel 82078 */
      unsigned char auto_pd;        /* Auto powerdown control */
      unsigned char cylinder;       /* Cylinder address */
      unsigned char clk48;          /* external 48MHz oscillator */
      unsigned char d0,       /* Drive select */
                  d1,
                  d2,
                  d3;
      unsigned char d;        /* Data pattern */
      unsigned char dir;            /* Direction control */
      unsigned char dtl;            /* Special sector size */
      unsigned char drt0,           /* Data rate table select */
                  drt1;
      unsigned char dt0,            /* Drive density select type */
                  dt1;
      unsigned char efifo;          /* Enable FIFO; 0=FIFO enabled; 1=8272A compatible, FIFO disabled */
      unsigned char eis;            /* Enable implied seek */
      unsigned char eot;            /* End of track */
      unsigned char ereg_en;        /* Enhanced Register Enabled */
      unsigned char fdi_tri;        /* Floppy Drive Interface Tri-state */
      unsigned char fd0,            /* Floppy drive select */
                  fd1;
      unsigned char fifothr;        /* FIFO Thrheshold */
      unsigned char gap;            /* Alters GAP2 length when using Perpendicular Mode */
      unsigned char gpl;            /* Gap langth, GAP3 size */
      unsigned char hlt;            /* Head load time */
      unsigned char hut;            /* Head unload time */
      unsigned char iso;            /* ISO Format used */
      unsigned char lock;           /* lock efifo, fifothr and pretrk while software reset in DSR or DOR register */
      unsigned char mfm;            /* MFM mode */
      unsigned char min_dly;        /* Minimum power update control */
      unsigned char mt;       /* Multi-track selector */
      unsigned char n;        /* Sector size code; 00=128 bytes; 01=256 bytes; ...; 07=16 kbytes */
      unsigned char nrp;            /* No Result phase */
      unsigned char ow;       /* allow overwrite D0, D1, D2, D3 */
      unsigned char pcn;            /* Present cylinder number */
      unsigned char pre_comp;       /* Precompensation values */
      unsigned char pdosc;          /* 1=internal oscillator is turned off */
      unsigned char ps2stat;        /* PS/2 status */
      unsigned char pts;            /* Precompensation table select */
      unsigned char poll;           /* Polling disable */
      unsigned char pretrk;         /* Precompensation start track number */
      unsigned char sector;         /* Sector address */
      unsigned char rcn;            /* Relative cylinder number */
      unsigned char sc;       /* Number of sectors */
      unsigned char sel3v;          /* voltage */
      unsigned char sk;       /* Skip flag */
      unsigned char srt;            /* Step rate interval */
      unsigned char stepping;       /* Steping of 82078 */
      unsigned char wgate;          /* Write gate alters timing of WE */
} NAME;
#endif /* STATE */

#ifdef BEHAVIOR

#define STEP_DEL (TIME_HZ / 8192) /* step delay */
#define WRITE_DEL (TIME_HZ / 1024) /* write delay */

#define FDC_SRA 0 /* Status Register a              (R)         */
#define FDC_SRB 1 /* Status Register b              (R)         */
#define FDC_DOR 2 /* Digital Output Register        (W)         */
#define FDC_TDR 3 /* Tape Drive Register */
#define FDC_MSR 4 /* Main Status Register           (R)         */
#define FDC_DSR 4 /* Data rate select Register      (W)  (PS/2) */
#define FDC_DR  5 /* Data Register                  (RW)        */

#define FDC_DR_READ_TRACK           0x02 /* move to track 0 */
#define FDC_DR_READ_TRACK_OPMASK    0xbf /* Bit-Mask for Op-Code */
#define FDC_DR_SPECIFY              0x03 /* specify HUT etc */
#define FDC_DR_SPECIFY_OPMASK       0xff /* Bit-Mask for Op-Code */
#define FDC_DR_SENSE_DRIVE          0x04 /* Sense Drive Status */
#define FDC_DR_SENSE_DRIVE_OPMASK   0xff /* Bit-Mask for Op-Code */
#define FDC_DR_WRITE                0x05 /* write Sector */
#define FDC_DR_WRITE_OPMASK         0x1f /* Bit-Mask for Op-Code */
#define FDC_DR_READ                 0x06 /* read Sector */
#define FDC_DR_READ_OPMASK          0x1f /* Bit-Mask for Op-Code */
#define FDC_DR_RECALIBRATE          0x07 /* move to track 0 */
#define FDC_DR_RECALIBRATE_OPMASK   0xff /* Bit-Mask for Op-Code */
#define FDC_DR_SENSE_INT            0x08 /* Sense Interrupt Status */
#define FDC_DR_SENSE_INT_OPMASK     0xff /* Bit-Mask for Op-Code */
#define FDC_DR_WRITE_DEL            0x09 /* write Sector */
#define FDC_DR_WRITE_DEL_OPMASK     0x3f /* Bit-Mask for Op-Code */
#define FDC_DR_READ_DEL             0x0c /* read Sector */
#define FDC_DR_READ_DEL_OPMASK      0x1f /* Bit-Mask for Op-Code */
#define FDC_DR_READID               0x0a /* read Sector */
#define FDC_DR_READID_OPMASK        0xbf /* Bit-Mask for Op-Code */
#define FDC_DR_FORMAT               0x0d /* format one track */
#define FDC_DR_FORMAT_OPMASK        0x3f /* Bit-Mask for Op-Code */
#define FDC_DR_DUMPREG              0x0e /* dump the contents of the fdc regs */
#define FDC_DR_DUMPREG_OPMASK       0xff /* Bit-Mask for Op-Code */
#define FDC_DR_SEEK                 0x0f /* seek */
#define FDC_DR_SEEK_OPMASK          0xff /* Bit-Mask for Op-Code */
#define FDC_DR_VERSION              0x10 /* get version code */
#define FDC_DR_VERSION_OPMASK       0xff /* Bit-Mask for Op-Code */
#define FDC_DR_PERPENDICULAR        0x12 /* perpendicular r/w mode */
#define FDC_DR_PERPENDICULAR_OPMASK 0xff /* Bit-Mask for Op-Code */
#define FDC_DR_CONFIGURE            0x13 /* configure FIFO operation */
#define FDC_DR_CONFIGURE_OPMASK     0x7f /* Bit-Mask for Op-Code */
#define FDC_DR_LOCK                 0x14 /* Fifo config lock/unlock */
#define FDC_DR_LOCK_OPMASK          0x7f /* Bit-Mask for Op-Code */
#define FDC_DR_VERIFY               0x16 /* read Sector */
#define FDC_DR_VERIFY_OPMASK        0x1f /* Bit-Mask for Op-Code */
#define FDC_DR_PARTID               0x18 /* partid command */
#define FDC_DR_PARTID_OPMASK        0xff /* Bit-Mask for Op-Code */
#define FDC_DR_DRIVE_SPECIFICATION  0x8e /* drive specification command */
#define FDC_DR_DRIVE_SPECIFICATION_OPMASK 0xff /* Bit-Mask for Op-Code */
#define FDC_DR_RELATIVE_SEEK        0x8f /* seek */
#define FDC_DR_RELATIVE_SEEK_OPMASK 0xef /* Bit-Mask for Op-Code */

/* the following commands are new in the 82078. They are not used in the
 * floppy driver, except the first three. These commands may be useful for apps
 * which use the FDRAWCMD interface. For doc, get the 82078 spec sheets at
 * http://www.intel.com/design/archives/periphrl/
 */

#define FDC_DR_SAVE           0x2e    /* save fdc regs for later restore */
#define FDC_DR_DRIVESPEC      0x8e    /* drive specification: Access to the
                                       * 2 Mbps data transfer rate for tape
                                       * drives */

#define FDC_DR_RESTORE        0x4e    /* later restore */
#define FDC_DR_POWERDOWN      0x27    /* configure FDC's powersave features */
#define FDC_DR_FORMAT_N_WRITE 0xef    /* format and write in one go. */
#define FDC_DR_OPTION         0x33    /* ISO format (which is a clean way to
                                       * pack more sectors on a track) */

#define FDC_DIR 7 /* Digital Input Register         (R)  (AT)   */
#define FDC_CCR 7 /* Configuration Control Register (W)  (AT)   */


#include <assert.h>
#include "fixme.h"
#include <stdio.h>
#include <string.h>

#include "glue-shm.h"
#include "glue-log.h"
#include "system.h" /* FIXME */

/*forward*/
static void
NAME_(step)(void *s);

static void
NAME_(command_phase)(struct cpssp *cpssp)
{
      cpssp->NAME.fifo_in_count = 0;
      cpssp->NAME.mrq = 1;
      cpssp->NAME.dio = FDC_DIR_WRITE;
      cpssp->NAME.cmd_busy = 0;
      cpssp->NAME.drv_busy = 0;
      cpssp->NAME.command = 0;
}

static void
NAME_(reset_cmd)(struct cpssp *cpssp)
{
      int i;

      time_call_delete(NAME_(step), cpssp);

      NAME_(irq_set)(cpssp, 0);

      for (i = 0; i < 4; i++) {
            cpssp->NAME.track[i] = 0;
            cpssp->NAME.st0[i].ic = 3;
            cpssp->NAME.st0[i].se = 0;
            cpssp->NAME.st0[i].ec = 0;
            cpssp->NAME.pending_int[i] = 1;
      }

      cpssp->NAME.fifo_out_count = 0;

      NAME_(command_phase)(cpssp);

      NAME_(irq_set)(cpssp, 1);
}

static unsigned char
NAME_(st0)(struct cpssp *cpssp, unsigned char ds)
{
      unsigned char ret = 0;
      
      ret |= cpssp->NAME.st0[ds].ic << 6;
      ret |= cpssp->NAME.st0[ds].se << 5;
      ret |= cpssp->NAME.st0[ds].ec << 4;
      ret |= 0 << 3;
      ret |= cpssp->NAME.st0[ds].hds << 2;
      ret |= ds << 0;

      cpssp->NAME.st0[ds].se = 0;

      return ret;
}

static unsigned char
NAME_(st1)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= cpssp->NAME.en << 7;
      ret |= 0 << 6;
      ret |= cpssp->NAME.de << 5;
      ret |= cpssp->NAME.or << 4;
      ret |= 0 << 3;
      ret |= cpssp->NAME.non_dma << 2;
      ret |= cpssp->NAME.nw << 1;
      ret |= cpssp->NAME.ma << 0;

      return ret;
}

static unsigned char
NAME_(st2)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= 0 << 7;
      ret |= cpssp->NAME.cm << 6;
      ret |= cpssp->NAME.dd << 5;
      ret |= cpssp->NAME.wc << 4;
      ret |= 0 << 3;
      ret |= 0 << 2;
      ret |= cpssp->NAME.bc << 1;
      ret |= cpssp->NAME.md << 0;

      return ret;
}

static unsigned char
NAME_(st3)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= 0 << 7;
      ret |= cpssp->NAME.wp << 6;
      ret |= 1 << 5;
      ret |= cpssp->NAME.t0 << 4;
      ret |= 1 << 3;
      ret |= cpssp->NAME.st0[cpssp->NAME.drive_sel].hds << 2;
      ret |= cpssp->NAME.drive_sel << 0;

      return ret;
}

/* ----------------------------------------------------------------- */
/* Commands                                                          */
/* ----------------------------------------------------------------- */

static void
NAME_(clear)(struct cpssp *cpssp, unsigned char ds)
{
      cpssp->NAME.st0[ds].ic = 0;
      cpssp->NAME.st0[ds].se = 0;
      cpssp->NAME.st0[ds].ec = 0;
      cpssp->NAME.en = 0;
      cpssp->NAME.de = 0;
      cpssp->NAME.or = 0;
      cpssp->NAME.nw = 0;
      cpssp->NAME.ma = 0;
      cpssp->NAME.cm = 0;
      cpssp->NAME.dd = 0;
      cpssp->NAME.wc = 0;
      cpssp->NAME.bc = 0;
      cpssp->NAME.md = 0;
}

static void
NAME_(dr_invalid)(struct cpssp *cpssp)
{
      cpssp->NAME.st0[cpssp->NAME.drive_sel].ic = 2; /* Invalid command */
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st0)(cpssp, cpssp->NAME.drive_sel);
      NAME_(irq_set)(cpssp, 1);
}

static void
NAME_(read_track)(struct cpssp *cpssp)
{
      fixme();
}

static void
NAME_(read_step)(struct cpssp *cpssp)
{
      const struct param {
            /* Byte 0: */
            unsigned char command : 5;
            unsigned char sk : 1;
            unsigned char mfm : 1;
            unsigned char mt : 1;
            /* Byte 1: */
            unsigned char ds : 2;
            unsigned char hds : 1;
            unsigned char : 5;
            /* Byte 2: */
            unsigned char c : 8;
            /* Byte 3: */
            unsigned char h : 8;
            /* Byte 4: */
            unsigned char r : 8;
            /* Byte 5: */
            unsigned char n : 8;
            /* Byte 6: */
            unsigned char eot : 8;
            /* Byte 7: */
            unsigned char gpl : 8;
            /* Byte 8: */
            unsigned char dtl : 8;
      } *param = (const struct param *) cpssp->NAME.fifo_in;

      assert(cpssp->NAME.command == FDC_DR_READ);

again:      ;
      switch (cpssp->NAME.state) {
      case 0:
            /*
             * Get parameters from FIFO.
             */
            cpssp->NAME.u.read.c = param->c;
            cpssp->NAME.u.read.h = param->h;
            cpssp->NAME.u.read.r = param->r;
      
            if (param->n == 0) {
                  cpssp->NAME.dma_size = param->dtl;
            } else {
                  cpssp->NAME.dma_size = (128 << param->n);
            }

            NAME_(clear)(cpssp, param->ds);

            cpssp->NAME.state++;
            break;

      case 1:
            /*
             * Step to correct track.
             */
            if (cpssp->NAME.eis) {
                  if (cpssp->NAME.track[param->ds] < cpssp->NAME.u.read.c) {
                        cpssp->NAME.drv_busy |= 1 << param->ds;
                        cpssp->NAME.track[param->ds]++;
                        NAME_(step_in)(cpssp);
                        time_call_after(STEP_DEL, NAME_(step), cpssp);
                        return;

                  } else if (cpssp->NAME.u.read.c < cpssp->NAME.track[param->ds]) {
                        cpssp->NAME.drv_busy |= 1 << param->ds;
                        cpssp->NAME.track[param->ds]--;
                        NAME_(step_out)(cpssp);
                        time_call_after(STEP_DEL, NAME_(step), cpssp);
                        return;

                  } else {
                        /* We're on the right track... */
                        cpssp->NAME.drv_busy &= ~(1 << param->ds);
                        cpssp->NAME.st0[param->ds].se = 1;
                        cpssp->NAME.state++;
                  }

            } else {
                  /* Just continue. */
                  cpssp->NAME.st0[param->ds].se = 0;
                  cpssp->NAME.state++;
            }
            break;

      case 2:
            /*
             * Start read.
             */
            cpssp->NAME.state++;
            cpssp->NAME.buffer_valid = 0;
            NAME_(hds_set)(cpssp, cpssp->NAME.u.read.h);
            NAME_(read_start)(cpssp);
            return;

      case 3:
            /*
             * One read done.
             */
            if (cpssp->NAME.id_sec == 0) {
                  /*
                   * No data.
                   */
                  /* Correct error codes? FIXME */
                  cpssp->NAME.st0[param->ds].ic = 1;  /* Abnormal termination. */
                  cpssp->NAME.ma = 1;                 /* Missing address mark. */
                  cpssp->NAME.tc = 1;

                  cpssp->NAME.state++;
                  break;

            } else if (cpssp->NAME.id_sec != cpssp->NAME.u.read.r) {
                  /*
                   * Just wait for correct sector.
                   */
                  cpssp->NAME.state--;
                  break;

            } else if (! cpssp->NAME.buffer_valid) {
                  /*
                   * Just wait for sector data.
                   */
                  cpssp->NAME.state--;
                  break;

            } else {
                  assert(cpssp->NAME.id_sec == cpssp->NAME.u.read.r
                              && cpssp->NAME.buffer_valid);
                  /*
                   * Sector read.
                   */
                  /* Perform DMA transfer. */
                  cpssp->NAME.dma_count = 0;
                  cpssp->NAME.tc = 0;
                  while (cpssp->NAME.dma_count < cpssp->NAME.dma_size
                      && ! cpssp->NAME.tc) {
                        NAME_(dma_req)(cpssp);
                  }
                  while (cpssp->NAME.dma_count < cpssp->NAME.dma_size) {
                        /* Throw away superfluous bytes. */
                        cpssp->NAME.dma_count++;
                  }

                  if (param->mt) {
                        if (cpssp->NAME.u.read.r == param->eot) {
                              if (cpssp->NAME.u.read.h == 1) {
                                    cpssp->NAME.u.read.c++;
                                    cpssp->NAME.u.read.h = 0;
                                    cpssp->NAME.u.read.r = 1;

                                    /* End reading... */
                                    cpssp->NAME.tc = 1;

                              } else {
                                    /* cpssp->NAME.u.read.c unchanged. */
                                    cpssp->NAME.u.read.h = 1;
                                    cpssp->NAME.u.read.r = 1;

                                    /* Continue reading... */
                              }
                        } else {
                              /* cpssp->NAME.u.read.c unchanged. */
                              /* cpssp->NAME.u.read.h unchanged. */
                              cpssp->NAME.u.read.r++;

                              /* Continue reading... */
                        }
                  } else {
                        if (cpssp->NAME.u.read.r == param->eot) {
                              cpssp->NAME.u.read.c++;
                              /* cpssp->NAME.u.read.h unchanged. */
                              cpssp->NAME.u.read.r = 1;

                              /* End reading... */
                              cpssp->NAME.tc = 1;

                        } else {
                              /* cpssp->NAME.u.read.c unchanged. */
                              /* cpssp->NAME.u.read.h unchanged. */
                              cpssp->NAME.u.read.r++;

                              /* Continue reading... */
                        }
                  }
                  cpssp->NAME.st0[param->ds].ic = 0;

                  if (cpssp->NAME.tc) {
                        /* End reading. */
                        cpssp->NAME.state++;
                  } else {
                        /* Continue reading. */
                        cpssp->NAME.state--;
                  }
            }
            break;

      case 4:
            /*
             * Work done. Set results.
             */
            /* Error codes set above. */
            cpssp->NAME.fifo_out_count = 0;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st0)(cpssp, param->ds);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st1)(cpssp);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st2)(cpssp);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.u.read.c;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.u.read.h;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.u.read.r;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = param->n;
            cpssp->NAME.dio = FDC_DIR_READ;
            cpssp->NAME.mrq = 1;
            NAME_(irq_set)(cpssp, 1);

            cpssp->NAME.state++;
            return;

      default:
            fprintf(stderr, "state=%d\n", cpssp->NAME.state);
            assert(0);  /* Cannot happen. */
      }
      goto again;
}

static void
NAME_(write_step)(struct cpssp *cpssp)
{
      const struct param {
            /* Byte 0: */
            unsigned char command : 5;
            unsigned char : 1;
            unsigned char mfm : 1;
            unsigned char mt : 1;
            /* Byte 1: */
            unsigned char : 5;
            unsigned char ds : 2;
            unsigned char hds : 1;
            /* Byte 2: */
            unsigned char c : 8;
            /* Byte 3: */
            unsigned char h : 8;
            /* Byte 4: */
            unsigned char r : 8;
            /* Byte 5: */
            unsigned char n : 8;
            /* Byte 6: */
            unsigned char eot : 8;
            /* Byte 7: */
            unsigned char gpl : 8;
            /* Byte 8: */
            unsigned char dtl : 8;
      } *param = (const struct param *) cpssp->NAME.fifo_in;

      assert(cpssp->NAME.command = FDC_DR_WRITE);

again:      ;
      switch (cpssp->NAME.state) {
      case 0:
            /*
             * Get parameters from FIFO.
             */
            cpssp->NAME.u.write.c = param->c;
            cpssp->NAME.u.write.h = param->h;
            cpssp->NAME.u.write.r = param->r;

            if (param->n == 0) {
                  cpssp->NAME.dma_size = param->dtl;
            } else {
                  cpssp->NAME.dma_size = (128 << param->n);
            }

            NAME_(clear)(cpssp, param->ds);

            cpssp->NAME.state++;
            break;

      case 1:
            /*
             * Step to correct track.
             */
            if (cpssp->NAME.eis) {
                  if (cpssp->NAME.track[param->ds] < cpssp->NAME.u.write.c) {
                        cpssp->NAME.drv_busy |= 1 << param->ds;
                        cpssp->NAME.track[param->ds]++;
                        NAME_(step_in)(cpssp);
                        time_call_after(STEP_DEL, NAME_(step), cpssp);
                        return;

                  } else if (cpssp->NAME.u.write.c < cpssp->NAME.track[param->ds]) {
                        cpssp->NAME.drv_busy |= 1 << param->ds;
                        cpssp->NAME.track[param->ds]--;
                        NAME_(step_out)(cpssp);
                        time_call_after(STEP_DEL, NAME_(step), cpssp);
                        return;

                  } else {
                        /* We're on the right track... */
                        cpssp->NAME.drv_busy &= ~(1 << param->ds);
                        cpssp->NAME.st0[param->ds].se = 1;
                        cpssp->NAME.state++;
                  }
            } else {
                  /* Just continue. */
                  cpssp->NAME.st0[param->ds].se = 0;
                  cpssp->NAME.state++;
            }
            break;

      case 2:
            /*
             * Wait for correct sector.
             */
            cpssp->NAME.state++;
            cpssp->NAME.buffer_valid = 0;
            NAME_(hds_set)(cpssp, cpssp->NAME.u.write.h);
            NAME_(read_start)(cpssp);
            return;

      case 3:
            if (cpssp->NAME.id_sec == 0) {
                  /*
                   * No sector.
                   */
                  /* Correct error codes? FIXME VOSSI */
                  cpssp->NAME.st0[param->ds].ic = 1;  /* Abnormal termination. */
                  cpssp->NAME.ma = 1;                 /* Missing address mark. */
                  cpssp->NAME.tc = 1;

                  cpssp->NAME.state++;

            } else if (cpssp->NAME.id_sec != cpssp->NAME.u.write.r) {
                  /*
                   * Just wait for correct sector.
                   */
                  cpssp->NAME.state--;
                  break;

            } else { assert(cpssp->NAME.id_sec == cpssp->NAME.u.write.r);
                  /*
                   * Correct sector header found.
                   * Re-write sector data.
                   */
                  /* Perform DMA transfer. */
                  cpssp->NAME.dma_count = 0;
                  cpssp->NAME.tc = 0;
                  while (cpssp->NAME.dma_count < cpssp->NAME.dma_size
                      && ! cpssp->NAME.tc) {
                        NAME_(dma_req)(cpssp);
                  }
                  while (cpssp->NAME.dma_count < cpssp->NAME.dma_size) {
                        /* Fill missing bytes with zeros. */
                        cpssp->NAME.dma_buffer[cpssp->NAME.dma_count++] = 0;
                  }

                  /* call IO process to transfer data */
                  cpssp->NAME.state++;
                  cpssp->NAME.buffer_valid = 0;
                  time_call_after(WRITE_DEL, NAME_(step), cpssp);
                  NAME_(writedata)(cpssp,
                              cpssp->NAME.dma_buffer, 128 << param->n);
                  return;
            }
            break;

      case 4:
            /*
             * Write done.
             */
            if (param->mt) {
                  if (cpssp->NAME.u.write.r == param->eot) {
                        if (cpssp->NAME.u.write.h == 1) {
                              cpssp->NAME.u.write.c++;
                              cpssp->NAME.u.write.h = 0;
                              cpssp->NAME.u.write.r = 1;

                              /* End writing... */
                              cpssp->NAME.tc = 1;

                        } else {
                              /* cpssp->NAME.u.write.c unchanged. */
                              cpssp->NAME.u.write.h = 1;
                              cpssp->NAME.u.write.r = 1;

                              /* Continue writing... */
                        }
                  } else {
                        /* cpssp->NAME.u.write.c unchanged. */
                        /* cpssp->NAME.u.write.h unchanged. */
                        cpssp->NAME.u.write.r++;

                        /* Continue writing... */
                  }
            } else {
                  if (cpssp->NAME.u.write.r == param->eot) {
                        cpssp->NAME.u.write.c++;
                        /* cpssp->NAME.u.write.h unchanged. */
                        cpssp->NAME.u.write.r = 1;

                        /* End writing... */
                        cpssp->NAME.tc = 1;

                  } else {
                        /* cpssp->NAME.u.write.c unchanged. */
                        /* cpssp->NAME.u.write.h unchanged. */
                        cpssp->NAME.u.write.r++;

                        /* Continue writing... */
                  }
            }
            cpssp->NAME.st0[param->ds].ic = 0;

            if (cpssp->NAME.tc) {
                  /* End writing. */
                  cpssp->NAME.state = 5;
            } else {
                  /* Continue writing. */
                  cpssp->NAME.state = 2;
            }
            break;

      case 5:
            /*
             * Work done. Set results.
             */
            cpssp->NAME.nw = cpssp->NAME.wp;

            cpssp->NAME.fifo_out_count = 0;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st0)(cpssp, param->ds);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st1)(cpssp);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st2)(cpssp);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.u.write.c;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.u.write.h;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.u.write.r;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = param->n;
            cpssp->NAME.dio = FDC_DIR_READ;
            cpssp->NAME.mrq = 1;
            NAME_(irq_set)(cpssp, 1);
            return;

      default:
            fprintf(stderr, "state=%d\n", cpssp->NAME.state);
            assert(0);  /* Cannot happen. */
      }
      goto again;
}

static void
NAME_(verify)(struct cpssp *cpssp)
{
      fixme();
}

static void
NAME_(read_deleted_sector)(struct cpssp *cpssp)
{
      fixme();
}

static void
NAME_(write_deleted_sector)(struct cpssp *cpssp)
{
      fixme();
}

static void
NAME_(format_track)(struct cpssp *cpssp)
{
      fixme();
}

static void
NAME_(seek_step)(struct cpssp *cpssp)
{
      const struct param {
            /* Byte 0: */
            unsigned char command : 8;
            /* Byte 1: */
            unsigned char ds : 2;
            unsigned char hds : 1;
            unsigned char : 5;
            /* Byte 2: */
            unsigned char ncn : 8;
      } *param = (struct param *) cpssp->NAME.fifo_in;

      assert(cpssp->NAME.command == FDC_DR_SEEK);

again:      ;
      switch (cpssp->NAME.state) {
      case 0:
            /*
             * Get parameter.
             */
            NAME_(clear)(cpssp, param->ds);
            cpssp->NAME.cmd_busy = 0;

            cpssp->NAME.state++;
            break;

      case 1:
            /*
             * Start seek.
             */
            if (cpssp->NAME.track[param->ds] < param->ncn) {
                  cpssp->NAME.drv_busy |= 1 << param->ds;
                  cpssp->NAME.track[param->ds]++;
                  NAME_(step_in)(cpssp);
                  time_call_after(STEP_DEL, NAME_(step), cpssp);
                  return;
            } else if (param->ncn < cpssp->NAME.track[param->ds]) {
                  cpssp->NAME.drv_busy |= 1 << param->ds;
                  cpssp->NAME.track[param->ds]--;
                  NAME_(step_out)(cpssp);
                  time_call_after(STEP_DEL, NAME_(step), cpssp);
                  return;
            } else {
                  /* We're on the right track... */
                  cpssp->NAME.drv_busy &= ~(1 << param->ds);
                  cpssp->NAME.st0[param->ds].se = 1;
                  cpssp->NAME.state++;
            }
            break;

      case 2:
            /*
             * Work done. Set results.
             */
            cpssp->NAME.st0[param->ds].ic = 0;
            cpssp->NAME.fifo_out_count = 0;
            cpssp->NAME.fifo_in_count = 0;
            cpssp->NAME.dio = FDC_DIR_WRITE;
            cpssp->NAME.mrq = 1;
            cpssp->NAME.pending_int[param->ds] = 1;
            NAME_(irq_set)(cpssp, 1);
            return;

      default:
            fprintf(stderr, "state=%d\n", cpssp->NAME.state);
            assert(0);  /* Cannot happen. */
      }
      goto again;
}

static void
NAME_(relative_seek_step)(struct cpssp *cpssp)
{
      const struct param {
            /* Byte 0: */
            unsigned char command : 5;
            unsigned char : 1;
            unsigned char dir : 1;
            unsigned char : 1;
            /* Byte 1: */
            unsigned char ds : 2;
            unsigned char hds : 1;
            unsigned char : 5;
            /* Byte 2: */
            unsigned char rcn;
      } *param = (const struct param *) cpssp->NAME.fifo_in;

      assert(cpssp->NAME.command == FDC_DR_RELATIVE_SEEK);

again:      ;
      switch (cpssp->NAME.state) {
      case 0:
            /*
             * Get parameters.
             */
            if (param->rcn == 0) {
                  cpssp->NAME.u.relative_seek.rcn = 0x100;
            } else {
                  cpssp->NAME.u.relative_seek.rcn = param->rcn;
            }

            NAME_(clear)(cpssp, param->ds);
            cpssp->NAME.cmd_busy = 0;

            cpssp->NAME.state++;
            break;

      case 1:
            /*
             * Start relative seek.
             */
            if (cpssp->NAME.u.relative_seek.rcn != 0) {
                  cpssp->NAME.u.relative_seek.rcn--;
                  if (param->dir) {
                        /* Step in. */
                        cpssp->NAME.drv_busy |= 1 << param->ds;
                        cpssp->NAME.track[param->ds]++;
                        NAME_(step_in)(cpssp);
                        time_call_after(STEP_DEL, NAME_(step), cpssp);
                        return;

                  } else {
                        /* Step out. */
                        cpssp->NAME.drv_busy |= 1 << param->ds;
                        cpssp->NAME.track[param->ds]--;
                        NAME_(step_out)(cpssp);
                        time_call_after(STEP_DEL, NAME_(step), cpssp);
                        return;
                  }
            } else {
                  /* We're on the right track. */
                  cpssp->NAME.drv_busy &= ~(1 << param->ds);
                  cpssp->NAME.st0[param->ds].ic = 0;
                  cpssp->NAME.st0[param->ds].se = 1;
                  cpssp->NAME.state++;
            }
            break;

      case 2:
            /*
             * Work done. Set results.
             */
            cpssp->NAME.fifo_out_count = 0;
            cpssp->NAME.fifo_in_count = 0;
            cpssp->NAME.dio = FDC_DIR_WRITE;
            cpssp->NAME.mrq = 1;
            cpssp->NAME.pending_int[param->ds] = 1;
            NAME_(irq_set)(cpssp, 1);
            return;

      default:
            fprintf(stderr, "state=%d\n", cpssp->NAME.state);
            assert(0);  /* Cannot happen. */
      }
      goto again;
}

static void
NAME_(recalibrate_step)(struct cpssp *cpssp)
{
      const struct param {
            /* Byte 0: */
            unsigned char command : 8;
            /* Byte 1: */
            unsigned char ds : 2;
            unsigned char : 6;
      } *param = (const struct param *) cpssp->NAME.fifo_in;

      assert(cpssp->NAME.command == FDC_DR_RECALIBRATE);

again:      ;
      switch (cpssp->NAME.state) {
      case 0:
            /*
             * Get parameter.
             */
            NAME_(clear)(cpssp, param->ds);
            cpssp->NAME.cmd_busy = 0;
            cpssp->NAME.track[param->ds] = 79;

            cpssp->NAME.state++;
            break;

      case 1:
            /*
             * Step out until "track0" is signaled.
             */
            if (cpssp->NAME.t0) {
                  /* We've reached track 0 => continue. */
                  cpssp->NAME.drv_busy &= ~(1 << param->ds);
                  cpssp->NAME.track[param->ds] = 0;

                  cpssp->NAME.st0[param->ds].ic = 0;
                  cpssp->NAME.st0[param->ds].se = 1;

                  cpssp->NAME.state++;

            } else if (0 < cpssp->NAME.track[param->ds]) {
                  /* Track 0 not reached so far => step out. */
                  cpssp->NAME.drv_busy |= 1 << param->ds;
                  cpssp->NAME.track[param->ds]--;
                  NAME_(step_out)(cpssp);
                  time_call_after(STEP_DEL, NAME_(step), cpssp);
                  return;

            } else {
                  /* Track 0 not reached after 79 step pulses. */
                  assert(cpssp->NAME.track[param->ds] == 0);
                  cpssp->NAME.drv_busy &= ~(1 << param->ds);

                  cpssp->NAME.st0[param->ds].ic = 1;
                  cpssp->NAME.st0[param->ds].se = 1;
                  cpssp->NAME.st0[param->ds].ec = 1;

                  cpssp->NAME.state++;
            }
            break;

      case 2:
            /*
             * Set result.
             */
            /* Error codes set above. */
            cpssp->NAME.fifo_out_count = 0;
            cpssp->NAME.fifo_in_count = 0;
            cpssp->NAME.dio = FDC_DIR_WRITE;
            cpssp->NAME.mrq = 1;
            cpssp->NAME.pending_int[param->ds] = 1;
            NAME_(irq_set)(cpssp, 1);
            return;

      default:
            fprintf(stderr, "state=%d\n", cpssp->NAME.state);
            assert(0);  /* Cannot happen. */
      }
      goto again;
}

static void
NAME_(dr_readid)(struct cpssp *cpssp)
{
      cpssp->NAME.drive_sel = (cpssp->NAME.fifo_in[1] & 0x03) >> 0;
      cpssp->NAME.st0[cpssp->NAME.drive_sel].hds = (cpssp->NAME.fifo_in[1] & 0x04) >> 2;

      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st0)(cpssp, cpssp->NAME.drive_sel);
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st1)(cpssp);
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st2)(cpssp);

      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.track[cpssp->NAME.drive_sel];  /* cylinder */
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.st0[cpssp->NAME.drive_sel].hds;/* head */
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = 1;                             /* sector */
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = 2;                             /* means 512 */

      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
      NAME_(irq_set)(cpssp, 1);
}

static void
NAME_(dr_version)(struct cpssp *cpssp)
{
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = 0x90; /* floppy drive is enhanced */
      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
}

static void
NAME_(dr_partid)(struct cpssp *cpssp)
{
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] = 0x01; /* 64 Pin */
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] |= (cpssp->NAME.stepping & 0x7) << 1;
      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
}

static void
NAME_(dr_sense_drive_status)(struct cpssp *cpssp)
{
      cpssp->NAME.drive_sel = (cpssp->NAME.fifo_in[1] & 0x03) >> 0;
      cpssp->NAME.st0[cpssp->NAME.drive_sel].hds = (cpssp->NAME.fifo_in[1] & 0x04) >> 2;

      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st3)(cpssp);
      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
}

static void
NAME_(dr_sense_interrupt_status)(struct cpssp *cpssp)
{
      unsigned char drive;

      cpssp->NAME.fifo_out_count = 0;

      for(drive=0; drive < 4 && !cpssp->NAME.pending_int[drive]; drive++); 

      if(drive < 4) {
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = NAME_(st0)(cpssp, drive);
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.track[drive];
            cpssp->NAME.st0[drive].se = 0;
            cpssp->NAME.st0[drive].ic = 0;
            cpssp->NAME.pending_int[drive] = 0;
      } else {
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = 0x80;
            cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = 0;
      }

      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;

      NAME_(irq_set)(cpssp, 0);
}

static void
NAME_(dr_lock)(struct cpssp *cpssp)
{
      cpssp->NAME.lock = (cpssp->NAME.fifo_in[0] & 0x80) >> 7;
      
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] = 0x00;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] |= cpssp->NAME.lock << 4;
      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
}

static void
NAME_(dr_specify)(struct cpssp *cpssp)
{
      cpssp->NAME.srt = (cpssp->NAME.fifo_in[1] & 0xf0) >> 4;
      cpssp->NAME.hut = (cpssp->NAME.fifo_in[1] & 0x0f) >> 0;

      cpssp->NAME.hlt = (cpssp->NAME.fifo_in[2] & 0xfe) >> 1;
      cpssp->NAME.non_dma =  (cpssp->NAME.fifo_in[2] & 0x01) >> 0;
      
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_in_count = 0;
      cpssp->NAME.dio = FDC_DIR_WRITE;
      cpssp->NAME.mrq = 1;
      cpssp->NAME.cmd_busy = 0;
}

static void
NAME_(dr_perpendicular)(struct cpssp *cpssp)
{
      cpssp->NAME.ow    = (cpssp->NAME.fifo_in[1] & 0x80) >> 7;
      cpssp->NAME.d3    = (cpssp->NAME.fifo_in[1] & 0x20) >> 5;
      cpssp->NAME.d2    = (cpssp->NAME.fifo_in[1] & 0x10) >> 4;
      cpssp->NAME.d1    = (cpssp->NAME.fifo_in[1] & 0x08) >> 3;
      cpssp->NAME.d0    = (cpssp->NAME.fifo_in[1] & 0x04) >> 2;
      cpssp->NAME.gap   = (cpssp->NAME.fifo_in[1] & 0x02) >> 1;
      cpssp->NAME.wgate = (cpssp->NAME.fifo_in[1] & 0x01) >> 0;
      
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_in_count = 0;
      cpssp->NAME.dio = FDC_DIR_WRITE;
      cpssp->NAME.mrq = 1;
      cpssp->NAME.cmd_busy = 0;
}

static void
NAME_(dr_configure)(struct cpssp *cpssp)
{
      cpssp->NAME.clk48 = (cpssp->NAME.fifo_in[0] & 0x80) >> 7;
      
      cpssp->NAME.eis     = (cpssp->NAME.fifo_in[2] & 0x40) >> 6;
      cpssp->NAME.efifo   = (cpssp->NAME.fifo_in[2] & 0x20) >> 5;
      cpssp->NAME.poll    = (cpssp->NAME.fifo_in[2] & 0x10) >> 4;
      cpssp->NAME.fifothr = (cpssp->NAME.fifo_in[2] & 0x0f) >> 0;
      
      cpssp->NAME.pretrk = cpssp->NAME.fifo_in[3];

      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_in_count = 0;
      cpssp->NAME.dio = FDC_DIR_WRITE;
      cpssp->NAME.mrq = 1;
      cpssp->NAME.cmd_busy = 0;
}

static void
NAME_(dr_dumpreg)(struct cpssp *cpssp)
{
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.track[0];
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.track[1];
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.track[2];
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.track[3];
      
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] = 0x00;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.srt << 4;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] |= cpssp->NAME.hut;
      
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] = 0x00;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.hlt << 7;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] |= cpssp->NAME.non_dma;
      
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.sc;
      
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] = 0x00;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.lock << 7;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.d3 << 5;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.d2 << 4;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.d1 << 3;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.d0 << 2;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.gap << 1;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] |= cpssp->NAME.wgate << 0;
      
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] = 0x00;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.eis << 6;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.efifo << 5;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count] |= cpssp->NAME.poll << 4;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] |= cpssp->NAME.fifothr << 0;
      
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++] = cpssp->NAME.pretrk;
      
      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
}

static void
NAME_(dr_drive_specification)(struct cpssp *cpssp)
{
      /* FIXME VOSSI */
      cpssp->NAME.fifo_out_count = 0;
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++]
            = (floppydrive[0] ? cpssp->NAME.fifo_in[1] : 0);
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++]
            = (floppydrive[1] ? cpssp->NAME.fifo_in[2] : 0);
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++]
            = (floppydrive[2] ? cpssp->NAME.fifo_in[3] : 0);
      cpssp->NAME.fifo_out[cpssp->NAME.fifo_out_count++]
            = (floppydrive[3] ? cpssp->NAME.fifo_in[4] : 0);
      cpssp->NAME.dio = FDC_DIR_READ;
      cpssp->NAME.mrq = 1;
}

static void
NAME_(step)(void *_css)
{
      struct cpssp *cpssp = (struct cpssp *) _css;

      switch (cpssp->NAME.command) {
      case FDC_DR_READ:
            NAME_(read_step)(cpssp);
            break;
      case FDC_DR_WRITE:
            NAME_(write_step)(cpssp);
            break;
      case FDC_DR_SEEK:
            NAME_(seek_step)(cpssp);
            break;
      case FDC_DR_RELATIVE_SEEK:
            NAME_(relative_seek_step)(cpssp);
            break;
      case FDC_DR_RECALIBRATE:
            NAME_(recalibrate_step)(cpssp);
            break;
      default:
            /* FIXME */
            fprintf(stderr, "WARNING: %s command=0x%02x (line %d)\n",
                        __FUNCTION__, cpssp->NAME.command, __LINE__);
            break;
      }
}

/* ----------------------------------------------------------------- */
/* Callback functions                                                */
/* ----------------------------------------------------------------- */

static void
NAME_(trk0_set)(void *s, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) s;

      cpssp->NAME.t0 = val;
}

static void
NAME_(index_set)(void *s, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) s;

      cpssp->NAME.index = val;
}

static void
NAME_(wp_set)(void *s, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *)s;

      cpssp->NAME.wp = val;
}

static void
NAME_(dskchg_set)(void *s, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) s;

      assert(/* 0 <= val && */ val <= 1);
#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: val=%u\n", __FUNCTION__, val);
#endif
      cpssp->NAME.disk_change = val;
}

static void
NAME_(readid)(void *s, unsigned char cyl, unsigned char sec)
{
      struct cpssp *cpssp = (struct cpssp *) s;

      cpssp->NAME.id_cyl = cyl;
      cpssp->NAME.id_sec = sec;

      cpssp->NAME.buffer_valid = 0; /* cyl/sec info doesn't match data */

      NAME_(step)(cpssp);
}

static void
NAME_(readdata)(
      void *s,
      unsigned char *buf,
      unsigned int bufsize
)
{
      struct cpssp *cpssp = (struct cpssp *) s;

      memcpy(cpssp->NAME.dma_buffer, buf, bufsize);
      cpssp->NAME.buffer_valid = 1;

      NAME_(step)(cpssp);
}

/* ----------------------------------------------------------------- */
/* Register access                                                   */
/* ----------------------------------------------------------------- */

static unsigned char
NAME_(read_sra)(struct cpssp *cpssp)
{
      /* FIXME VOSSI */
      fprintf(stderr, "WARNING: fdc: reading SRA. Returning 0.\n");

      return 0;
}

/* write_sra is not defined. */

static unsigned char
NAME_(read_srb)(struct cpssp *cpssp)
{
      /* FIXME VOSSI */
      fprintf(stderr, "WARNING: fdc: reading SRB. Returning 0.\n");

      return 0;
}

/* write_srb is not defined. */

static unsigned char
NAME_(read_dor)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= cpssp->NAME.motor << 4;
      ret |= cpssp->NAME.dma_gate << 3;
      ret |= cpssp->NAME.n_reset << 2;
      ret |= cpssp->NAME.drive_sel;

#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: ret=0x%02x\n", __FUNCTION__, ret);
#endif
      return ret;
}

static void
NAME_(write_dor)(struct cpssp *cpssp, unsigned char value)
{
      unsigned int drive;

#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: value=0x%02x\n",
                  __FUNCTION__, value);
#endif

      /* Bit 7-4: motor enable bits */
      cpssp->NAME.motor = (value >> 4) & 0xf;
      for (drive = 0; drive < 4; drive++) {
            NAME_(motor_set)(cpssp, drive, (cpssp->NAME.motor >> drive) & 1);
      }

      /* Bit 3: dma_gate bit */
      cpssp->NAME.dma_gate = (value >> 3) & 1;

      /* Bit 2: reset bit          */
      /*        (low means active) */
      if (! ((value >> 2) & 1)) {
            if (cpssp->NAME.n_reset) {
                  cpssp->NAME.n_reset = 0;
            }
      } else {
            if (! cpssp->NAME.n_reset) {
                  cpssp->NAME.n_reset = 1;
                  NAME_(reset_cmd)(cpssp);
            }
      }

      /* Bit 1-0: drive_sel bits */
      cpssp->NAME.drive_sel = (value >> 0) & 3;
      for (drive = 0; drive < 4; drive++) {
            NAME_(select_set)(cpssp, drive, drive == cpssp->NAME.drive_sel);
      }
}

static unsigned char
NAME_(read_tdr)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= cpssp->NAME.tape_sel << 0;

#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: ret=0x%02x\n", __FUNCTION__, ret);
#endif
      return ret;
}

static void
NAME_(write_tdr)(struct cpssp *cpssp, unsigned char value)
{
#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: value=0x%02x\n",
                  __FUNCTION__, value);
#endif
      cpssp->NAME.tape_sel = (value >> 0) & 3;

      if (cpssp->NAME.tape_sel != 0) {
            fixme();
      }
}

static unsigned char
NAME_(read_msr)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= cpssp->NAME.mrq << 7;
      ret |= cpssp->NAME.dio << 6;
      ret |= cpssp->NAME.non_dma << 5;
      ret |= cpssp->NAME.cmd_busy << 4;
      ret |= cpssp->NAME.drv_busy;
      /* FIXME VOSSI */

#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: ret=0x%02x\n", __FUNCTION__, ret);
#endif
      return ret;
}

static void
NAME_(write_dsr)(struct cpssp *cpssp, unsigned char value)
{
#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: value=0x%02x\n",
                  __FUNCTION__, value);
#endif

      /* Bit 7: s/w reset */
      if ((value >> 7) & 1) {
            NAME_(reset_cmd)(cpssp);
      }

      /* Bit 6: power down */
      /* FIXME */

      /* Bit 5: 0 */

      /* Bit 4-2: precompensation value select */
      cpssp->NAME.pre_comp = (value >> 2) & 0x7;

      /* Bit 1-0: data rate select */
      cpssp->NAME.drate_sel = (value >> 0) & 0x3;
}

static unsigned char
NAME_(read_dr)(struct cpssp *cpssp)
{
      unsigned char ret;
      
      if (! cpssp->NAME.n_reset) {
            return 0;
      }

      assert(/* 0 <= cpssp->NAME.fifo_out_count
          && */ cpssp->NAME.fifo_out_count <= FIFO_LEN);
      
      if (cpssp->NAME.dio != FDC_DIR_READ) {
            /* Don't read from FIFO if dio isn't FDC_DIR_READ! */
            assert(cpssp->NAME.fifo_out_count == 0);
            ret = 0x00;

      } else {
            /* Get one byte from FIFO. */
            assert(0 < cpssp->NAME.fifo_out_count);
            ret = cpssp->NAME.fifo_out[0];
            cpssp->NAME.fifo_out_count--;
            if (cpssp->NAME.fifo_out_count == 0) {
                  /* Disable IRQ when FIFO has been read. */
                  NAME_(irq_set)(cpssp, 0);

                  /* Enter command phase again. */
                  NAME_(command_phase)(cpssp);

            } else {
                  memmove(&cpssp->NAME.fifo_out[0], &cpssp->NAME.fifo_out[1],
                              cpssp->NAME.fifo_out_count);
            }
      }

      return ret;
}

static void
NAME_(write_dr)(struct cpssp *cpssp, unsigned char value)
{
      const static struct {
            unsigned char opcode;
            unsigned char mask;
            unsigned char count;
            void (*func)(struct cpssp *);
      } table[] = {
            { FDC_DR_READ_TRACK, FDC_DR_READ_TRACK_OPMASK,
                  9, NAME_(read_track) },
            { FDC_DR_SPECIFY, FDC_DR_SPECIFY_OPMASK,
                  3, NAME_(dr_specify) },
            { FDC_DR_WRITE, FDC_DR_WRITE_OPMASK,
                  9, NAME_(write_step) },
            { FDC_DR_READ, FDC_DR_READ_OPMASK,
                  9, NAME_(read_step) },
            { FDC_DR_RECALIBRATE, FDC_DR_RECALIBRATE_OPMASK,
                  2, NAME_(recalibrate_step) },
            { FDC_DR_SENSE_DRIVE, FDC_DR_SENSE_DRIVE_OPMASK,
                  2, NAME_(dr_sense_drive_status) },
            { FDC_DR_SENSE_INT, FDC_DR_SENSE_INT_OPMASK,
                  1, NAME_(dr_sense_interrupt_status) },
            { FDC_DR_WRITE_DEL, FDC_DR_WRITE_DEL_OPMASK,
                  9, NAME_(write_deleted_sector) },
            { FDC_DR_READ_DEL, FDC_DR_READ_DEL_OPMASK,
                  9, NAME_(read_deleted_sector) },
            { FDC_DR_READID, FDC_DR_READID_OPMASK,
                  2, NAME_(dr_readid) },
            { FDC_DR_FORMAT, FDC_DR_FORMAT_OPMASK,
                  6, NAME_(format_track) },
            { FDC_DR_DUMPREG, FDC_DR_DUMPREG_OPMASK,
                  1, NAME_(dr_dumpreg) },
            { FDC_DR_SEEK, FDC_DR_SEEK_OPMASK,
                  3, NAME_(seek_step) },
            { FDC_DR_VERSION, FDC_DR_VERSION_OPMASK,
                  1, NAME_(dr_version) },
            { FDC_DR_PERPENDICULAR, FDC_DR_PERPENDICULAR_OPMASK,
                  2, NAME_(dr_perpendicular) },
            { FDC_DR_CONFIGURE, FDC_DR_CONFIGURE_OPMASK,
                  4, NAME_(dr_configure) },
            { FDC_DR_LOCK, FDC_DR_LOCK_OPMASK,
                  1, NAME_(dr_lock) },
            { FDC_DR_VERIFY, FDC_DR_VERIFY_OPMASK,
                  9, NAME_(verify) },
            { FDC_DR_PARTID, FDC_DR_PARTID_OPMASK,
                  1, NAME_(dr_partid) },
            { FDC_DR_DRIVE_SPECIFICATION, FDC_DR_DRIVE_SPECIFICATION_OPMASK,
                  6, NAME_(dr_drive_specification) },
            { FDC_DR_RELATIVE_SEEK, FDC_DR_RELATIVE_SEEK_OPMASK,
                  3, NAME_(relative_seek_step) },
            { 0, 0,
                  1, NAME_(dr_invalid) }
      };
      const unsigned int table_count = sizeof(table) / sizeof(table[0]);
      unsigned int i;

      cpssp->NAME.fifo_in[cpssp->NAME.fifo_in_count++] = value;

      for (i = 0; ; i++) {
            assert(i < table_count);
            if ((cpssp->NAME.fifo_in[0] & table[i].mask) == table[i].opcode) {
                  if (cpssp->NAME.fifo_in_count == table[i].count) {
                        cpssp->NAME.command = table[i].opcode;
                        cpssp->NAME.cmd_busy = 1;
                        cpssp->NAME.mrq = 0;
                        cpssp->NAME.state = 0;
#if DEBUG_CONTROL_FLOW
                        int j;
                        fprintf(stderr, "%s: command is ", __FUNCTION__);
                        for(j=0; j < table[i].count; j++) {
                              fprintf(stderr, "0x%02x ",
                                          cpssp->NAME.fifo_in[j]);
                        }
                        fprintf(stderr, "\n");
#endif
                        table[i].func(cpssp);
#if DEBUG_CONTROL_FLOW
                        if(cpssp->NAME.fifo_out_count > 0) {
                              fprintf(stderr, "%s: result is ", __FUNCTION__);
                              for(j=0; j < cpssp->NAME.fifo_out_count; j++) {
                                    fprintf(stderr, "0x%02x ",
                                                cpssp->NAME.fifo_out[j]);
                              }
                              fprintf(stderr, "\n");
                        }
#endif
                  }
                  break;
            }
      }
}

static unsigned char
NAME_(read_dir)(struct cpssp *cpssp)
{
      unsigned char ret = 0;

      ret |= cpssp->NAME.disk_change << 7;
#if DEBUG_CONTROL_FLOW  
      fprintf(stderr, "%s: ret=0x%02x\n",
                  __FUNCTION__, ret);
#endif
      return ret;
}

static void
NAME_(write_ccr)(struct cpssp *cpssp, unsigned char value)
{
#if DEBUG_CONTROL_FLOW
      fprintf(stderr, "%s: value=0x%02x\n",
                  __FUNCTION__, value);
#endif

      /* Bit 1-0: data rate select */
      cpssp->NAME.drate_sel = (value >> 0) & 0x3;
}

static uint8_t
NAME_(inb)(struct cpssp *cpssp, unsigned int port)
{
      uint8_t retval;

      switch (port) {
      case FDC_SRA:
            retval = NAME_(read_sra)(cpssp);
            break;

      case FDC_SRB:
            retval = NAME_(read_srb)(cpssp);
            break;

      case FDC_DOR:
            retval = NAME_(read_dor)(cpssp);
            break;

      case FDC_TDR:
            retval = NAME_(read_tdr)(cpssp);
            break;

      case FDC_MSR:
            retval = NAME_(read_msr)(cpssp);
            break;

      case FDC_DR:
            retval = NAME_(read_dr)(cpssp);
            break;

      case FDC_DIR:
            retval = NAME_(read_dir)(cpssp);
            break;
      
      default:
            assert(0); /* Cannot happen. */
      }

//#if DEBUG_CONTROL_FLOW
#if 0
            faum_log(FAUM_LOG_DEBUG, "fdc", "", "inb(0x%04x)->0x%02x\n",
                        port, retval);
#endif

      return retval;
}

static void
NAME_(outb)(struct cpssp *cpssp, uint8_t value, unsigned int port)
{
//#if DEBUG_CONTROL_FLOW
#if 0
            faum_log(FAUM_LOG_DEBUG, "fdc", "", "outb(0x%02x, 0x%04x)\n",
                        value, port);
#endif

      switch (port) {
      case FDC_SRA:
            /* Write on SRA is not specified in documentation. */
            break;

      case FDC_SRB:
            /* Write on SRB is not specified in documentation. */
            break;

      case FDC_DOR:
            NAME_(write_dor)(cpssp, value);
            break;

      case FDC_TDR:
            NAME_(write_tdr)(cpssp, value);
            break;

      case FDC_DSR:
            NAME_(write_dsr)(cpssp, value);
            break;

      case FDC_DR:
            NAME_(write_dr)(cpssp, value);
            break;

      case FDC_CCR:
            NAME_(write_ccr)(cpssp, value);
            break;

      default:
            assert(0); /* Cannot happen. */
      }
}

static void
NAME_(ack_inb)(void *_css, unsigned int tc, unsigned char *val)
{
      struct cpssp *cpssp = (struct cpssp *) _css;

      *val = cpssp->NAME.dma_buffer[cpssp->NAME.dma_count++];
      cpssp->NAME.tc = tc;
}

static void
NAME_(ack_outb)(void *_css, unsigned int tc, unsigned char val)
{
      struct cpssp *cpssp = (struct cpssp *) _css;

      cpssp->NAME.dma_buffer[cpssp->NAME.dma_count++] = val;
      cpssp->NAME.tc = tc;
}

static void
NAME_(ack_verifyb)(void *_css, unsigned int tc)
{
      struct cpssp *cpssp = (struct cpssp *) _css;
      cpssp->NAME.tc = tc;
}

static void
NAME_(reset)(struct cpssp *cpssp)
{
      unsigned char i;

      cpssp->NAME.n_reset = 1;
      for(i=0; i < 4; i++) {
            cpssp->NAME.pending_int[i] = 1;
      }
      cpssp->NAME.fifo_out_count = 0;
      NAME_(command_phase)(cpssp);

      cpssp->NAME.eis = 0;
      cpssp->NAME.efifo = 0;
      cpssp->NAME.poll = 0;
      cpssp->NAME.fifothr = 1;
      cpssp->NAME.pretrk = 0;
}

void
NAME_(init)(struct cpssp *cpssp)
{
      /* Nothing to do... */
}

#endif /* BEHAVIOR */

#undef DEBUG_CONTROL_FLOW

Generated by  Doxygen 1.6.0   Back to index