/* fdd.h: Routines for emulating floppy disk drives
   Copyright (c) 2007-2023 Gergely Szasz, Philip Kendall
   Copyright (c) 2015 Stuart Brady

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

   Author contact information:

   Philip: philip-fuse@shadowmagic.org.uk

*/

#ifndef FUSE_FDD_H
#define FUSE_FDD_H

#include "event.h"
#include "disk.h"

typedef enum fdd_error_t {
  FDD_OK = 0,
  FDD_GEOM,
  FDD_DATA,
  FDD_RDONLY,
  FDD_NONE,		/* FDD not exist (disabled) */

  FDD_LAST_ERROR,
} fdd_error_t;

typedef enum fdd_type_t {
  FDD_TYPE_NONE = 0,	/* FDD not exist/disabled */
  FDD_SHUGART,		/* head load when selected */
  /* 
     .. In a single drive system (program shunt position
        "MX" shorted), with program shunt position "HL"
	shorted, Drive Select when activated to a logical
	zero level, will load the R/W head against the
	diskette enabling contact of the R/W head against
	the media. ...
	
	In a multiple drive system (program shunt position
        "MX" open), the three input lines (Drive Select 1,
	Drive Select 2 and Drive select 3) are provided so
	that the using system may select which drive on
	the interface is to be used. In this mode of opera-
	tion only the drive with its Drive Select line active
	will respond to the input lines and gate the output
	lines. In addition, the selected drive will load its
	R/W head if program shunt position "HL" is
	shorted. ...
  */
  FDD_IBMPC,
} fdd_type_t;

typedef enum fdd_dir_t {
  FDD_STEP_OUT = 0,
  FDD_STEP_IN = 1,
} fdd_dir_t;

typedef struct fdd_t {
  fdd_type_t type;	/* fdd type: Shugart or IBMPC */
  int auto_geom;	/* change geometry according to loading disk */
  int fdd_heads;	/* 1 or 2 */
  int fdd_cylinders;	/* 40/40+/80/80+ */

  int tr00;		/* track 0 mark */
  int index;		/* index hole */
  int wrprot;		/* write protect */
  int data;		/* read/write to data byte 0x00nn or 0xffnn */
  int marks;		/* read/write other marks 0x01 -> FM 0x02 -> WEAK */

  disk_t disk;		/* disk */
  int loaded;		/* disk loaded */
  int upsidedown;	/* flipped disk */
  int selected;		/* Drive Select line active */
  int ready;		/* some disk drive offer a ready signal */
  int dskchg;		/* disk change signal */
  int hdout;		/* High Density signal */

  fdd_error_t status;

/* WD/FD 177X may wait for an index or RDY->/RDY or /RDY->RDY 
   we do not need more, just a subroutine and a pointer to fdc_struct 
*/
  void ( *fdc_index ) ( void *fdc );
  void *fdc;		/* if not NULL FDC wait for an index pulse */

/*--private section, fdc may never use it */
  int unreadable;	/* disk unreadable in this drive */
  int do_read_weak;
  int c_head;		/* current head (side) */
  int c_cylinder;	/* current cylinder number (0 -> TR00) */
  int motoron;		/* motor on */
  int loadhead;		/* head loaded */
  int index_pulse;	/* 'second' index hole, for index status */
} fdd_t;

typedef struct fdd_params_t {
  int enabled;
  int heads;
  int cylinders;
} fdd_params_t;

extern const fdd_params_t fdd_params[];

void fdd_register_startup( void );

const char *fdd_strerror( int error );
/* initialize the fdd_t struct, and set fdd_heads and cylinders (e.g. 2/83 ) */
int fdd_init( fdd_t *d, fdd_type_t type, const fdd_params_t *dt, int reinit );
/* load the given disk into the fdd. if upsidedown = 1, floppy upsidedown in drive :) */
int fdd_load( fdd_t *d, int upsidedown );
/* unload the disk from fdd */
void fdd_unload( fdd_t *d );
/* set fdd head */
void fdd_set_head( fdd_t *d, int head );
/* step one track according to d->direction direction. set d->tr00 if reach track 0 */
void fdd_step( fdd_t *d, fdd_dir_t direction );
/* set floppy position ( upsidedown or not )*/
void fdd_motoron( fdd_t *d, int on );
/* start (1) or stop (0) spindle motor */
void fdd_head_load( fdd_t *d, int load );
/* load (1) or unload (0) head */
void fdd_select( fdd_t *d, int select );
/* select (1) or unselect (0) FDD */
void fdd_flip( fdd_t *d, int upsidedown );
/* Read the next byte from disk. The byte will go into d->data.
   If d->data = 0xffnn then this byte was recorded with different clock 'mark'.
   d->idx is set if we reach the 'index hole'.  0x0100 is read if the disk is
   unreadable, the motor is not on, or the head is not loaded.
*/
int fdd_read_data( fdd_t *d );
/* Write the next byte to disk. The byte is taken from d->data.
   If d->data = 0xffnn then this byte recorded with different clock 'mark'.
   d->idx is set if we reach the 'index hole'.
*/
int fdd_write_data( fdd_t *d );
/* set write protect status on loaded disk */
void fdd_wrprot( fdd_t *d, int wrprot );
/* to reach index hole */
void fdd_wait_index_hole( fdd_t *d );
/* set floppy position ( upsidedown or not )*/
void fdd_flip( fdd_t *d, int upsidedown );

#endif 	/* FUSE_FDD_H */
