|  | @@ -28,8 +28,22 @@ enum drive_flags {
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  enum pending {
 | 
	
		
			
				|  |  | -    PEND_IO    = 1,
 | 
	
		
			
				|  |  | -    PEND_RESET = 2
 | 
	
		
			
				|  |  | +    PEND_IO       = 1,		/* I/O transfer complete */
 | 
	
		
			
				|  |  | +    PEND_STARTCMD = 2,		/* C1# = go to command state */
 | 
	
		
			
				|  |  | +    PEND_RESET    = 4		/* C3# or RST# = local/global reset */
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + * Status bits in INP(1); bits 5 & 2 are unknown, bit 4 is apparently
 | 
	
		
			
				|  |  | + * always 0 on at least card 66 81046-02 (55 21046-x1)...
 | 
	
		
			
				|  |  | + * "fast DMA controller".
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +enum abc_status {
 | 
	
		
			
				|  |  | +    AS_CMD     = 0x80,		/* Start of command */
 | 
	
		
			
				|  |  | +    AS_WRITE   = 0x40,		/* Data direction OUT(0) (to controller) */
 | 
	
		
			
				|  |  | +    AS_ERROR   = 0x08,		/* INP(0) contains error info */
 | 
	
		
			
				|  |  | +    AS_READING = 0x04,		/* Waiting for sector load */
 | 
	
		
			
				|  |  | +    AS_READY   = 0x01		/* Ready for input */
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* Number of fragments (extents) we can memoize */
 | 
	
	
		
			
				|  | @@ -71,6 +85,7 @@ struct ctl_state {
 | 
	
		
			
				|  |  |      const struct ctl_params *params;
 | 
	
		
			
				|  |  |      uint8_t k[4];		/* Command bytes */
 | 
	
		
			
				|  |  |      uint8_t drives;		/* Total drives present */
 | 
	
		
			
				|  |  | +    uint8_t error;		/* Error status */
 | 
	
		
			
				|  |  |      bool initialized;	        /* Controller initialized */
 | 
	
		
			
				|  |  |      volatile enum pending pending;	/* Need to do I/O */
 | 
	
		
			
				|  |  |      struct drive_state drv[8];	/* Per-drive state */
 | 
	
	
		
			
				|  | @@ -78,12 +93,13 @@ struct ctl_state {
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /*
 | 
	
		
			
				|  |  | - * DOSGEN depends on this value... and different DOSGEN
 | 
	
		
			
				|  |  | - * expect different values. If this value is wrong, DOSGEN
 | 
	
		
			
				|  |  | - * will spin forever on "testing sector..."
 | 
	
		
			
				|  |  | + * Error codes for INP(0), derived from FD1791 status codes
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * DOSGEN depends on OUT_OF_RANGE having this value... and different
 | 
	
		
			
				|  |  | + * DOSGEN expect different values. If this value is wrong, DOSGEN will
 | 
	
		
			
				|  |  | + * spin forever on "testing sector..."
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | -#define OUT_OF_RANGE 0x21	/* Status code for an invalid sector */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +#define OUT_OF_RANGE   0x21	/* Status code for an invalid sector */
 | 
	
		
			
				|  |  |  #define DISK_NOT_READY 0x80
 | 
	
		
			
				|  |  |  #define WRITE_PROTECT  0x40
 | 
	
		
			
				|  |  |  #define WRITE_FAULT    0x20
 | 
	
	
		
			
				|  | @@ -226,9 +242,12 @@ static void disk_start_command(struct ctl_state *state)
 | 
	
		
			
				|  |  |  #if 0
 | 
	
		
			
				|  |  |      con_printf("%-2s:  start command\n", state->params->name);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      memset(&state->k, 0xee, sizeof state->k);
 | 
	
		
			
				|  |  | -    abc_setup_out_queue(&state->iodev, state->k, 4, 0x81);
 | 
	
		
			
				|  |  | +    state->iodev.callback_mask |= 1 << 0;
 | 
	
		
			
				|  |  | +    abc_setup_out_queue(&state->iodev, state->k, 4,
 | 
	
		
			
				|  |  | +			AS_CMD|AS_READY|AS_WRITE|
 | 
	
		
			
				|  |  | +			(state->error ? AS_ERROR : 0));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void sync_drives(struct ctl_state *state)
 | 
	
	
		
			
				|  | @@ -244,16 +263,14 @@ static void sync_drives(struct ctl_state *state)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void disk_set_error(struct ctl_state *state, unsigned int error)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -    abc_set_inp_default(&state->iodev, error);
 | 
	
		
			
				|  |  | +    abc_set_inp_default(&state->iodev, state->error = error);
 | 
	
		
			
				|  |  | +    state->k[0] = 0;		/* Abort remaining command sequence */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void disk_reset_state(struct ctl_state *state)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      abc_set_inp_status(&state->iodev, 0);
 | 
	
		
			
				|  |  |      disk_set_error(state, 0);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    sync_drives(state);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      disk_start_command(state);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -372,31 +389,39 @@ static void unmount_drives(struct ctl_state *state)
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#define IDLE_CALLBACK_MASK	(1 << 4) /* C3 = reset */
 | 
	
		
			
				|  |  | +/* RST# = global reset, C3# = local reset, C1# = goto command start */
 | 
	
		
			
				|  |  | +#define IDLE_CALLBACK_MASK	((1 << 7)|(1 << 4)|(1 << 2))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void abcdisk_callback_out(struct abc_dev *dev, uint8_t data)
 | 
	
		
			
				|  |  | +static ABC_CALLBACK(abcdisk_callback_out)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      dev->callback_mask = IDLE_CALLBACK_MASK;
 | 
	
		
			
				|  |  |      ABC_INP1_DATA = dev->inp_data[1] = 0;
 | 
	
		
			
				|  |  | +    state->error = 0;
 | 
	
		
			
				|  |  |      state->pending |= PEND_IO;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void abcdisk_callback_inp(struct abc_dev *dev)
 | 
	
		
			
				|  |  | +static ABC_CALLBACK(abcdisk_callback_inp)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      dev->callback_mask = IDLE_CALLBACK_MASK;
 | 
	
		
			
				|  |  |      ABC_INP1_DATA = dev->inp_data[1] = 0;
 | 
	
		
			
				|  |  | +    state->error = 0;
 | 
	
		
			
				|  |  |      state->pending |= PEND_IO;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void abcdisk_callback_cmd(struct abc_dev *dev, uint8_t data, uint8_t addr)
 | 
	
		
			
				|  |  | +static ABC_CALLBACK(abcdisk_callback_restart)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
 | 
	
		
			
				|  |  | -    if (addr == 4)
 | 
	
		
			
				|  |  | -	state->pending |= PEND_RESET;
 | 
	
		
			
				|  |  | +    state->pending |= PEND_STARTCMD;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static ABC_CALLBACK(abcdisk_callback_rst)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    struct ctl_state *state = container_of(dev, struct ctl_state, iodev);
 | 
	
		
			
				|  |  | +    state->pending |= PEND_RESET;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static char abcdisk_80_800[] = "/abcdisk.800/";
 | 
	
	
		
			
				|  | @@ -448,21 +473,20 @@ static void do_next_command(struct ctl_state *state)
 | 
	
		
			
				|  |  |  	       drv->name, state->k[0], state->k[1],
 | 
	
		
			
				|  |  |  	       state->k[2], state->k[3]);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      if (state->k[0] & 0x01) {
 | 
	
		
			
				|  |  |  	/* READ SECTOR */
 | 
	
		
			
				|  |  |  	if (!(drv->flags & DF_MOUNTED)) {
 | 
	
		
			
				|  |  |  	    disk_set_error(state, DISK_NOT_READY);
 | 
	
		
			
				|  |  |  	} else if (!cur_sector_valid(state)) {
 | 
	
		
			
				|  |  |  	    disk_set_error(state, OUT_OF_RANGE);
 | 
	
		
			
				|  |  | -	} else if (!file_pos_valid(state)) {
 | 
	
		
			
				|  |  | -	    disk_set_error(state, CRC_ERROR);
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  |  	    UINT rlen = 0;
 | 
	
		
			
				|  |  |  	    FRESULT rv;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	    set_led(LED_DISKIO, true);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	    abc_set_inp_status(&state->iodev, AS_READING|AS_READY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	    rv = f_lseek(&drv->file, file_pos(state));
 | 
	
		
			
				|  |  |  	    if (rv == FR_OK)
 | 
	
		
			
				|  |  |  		rv = f_read(&drv->file, buf, 256, &rlen);
 | 
	
	
		
			
				|  | @@ -475,13 +499,15 @@ static void do_next_command(struct ctl_state *state)
 | 
	
		
			
				|  |  |      if (state->k[0] & 0x02) {
 | 
	
		
			
				|  |  |  	/* SECTOR TO HOST */
 | 
	
		
			
				|  |  |  	state->k[0] &= ~0x02;   /* Command done */
 | 
	
		
			
				|  |  | -	abc_setup_inp_queue(&state->iodev, buf, 256, 0x01);
 | 
	
		
			
				|  |  | +	state->iodev.callback_mask |= 1 << 8;
 | 
	
		
			
				|  |  | +	abc_setup_inp_queue(&state->iodev, buf, 256, AS_READY);
 | 
	
		
			
				|  |  |  	return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (state->k[0] & 0x04) {
 | 
	
		
			
				|  |  |  	/* SECTOR FROM HOST */
 | 
	
		
			
				|  |  |  	state->k[0] &= ~0x04;   /* Command done */
 | 
	
		
			
				|  |  | -	abc_setup_out_queue(&state->iodev, buf, 256, 0x01);
 | 
	
		
			
				|  |  | +	state->iodev.callback_mask |= 1 << 0;
 | 
	
		
			
				|  |  | +	abc_setup_out_queue(&state->iodev, buf, 256, AS_WRITE|AS_READY);
 | 
	
		
			
				|  |  |  	return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (state->k[0] & 0x08) {
 | 
	
	
		
			
				|  | @@ -492,8 +518,6 @@ static void do_next_command(struct ctl_state *state)
 | 
	
		
			
				|  |  |  	    disk_set_error(state, WRITE_PROTECT);
 | 
	
		
			
				|  |  |  	} else if (!cur_sector_valid(state)) {
 | 
	
		
			
				|  |  |  	    disk_set_error(state, OUT_OF_RANGE);
 | 
	
		
			
				|  |  | -	} else if (!file_pos_valid(state)) {
 | 
	
		
			
				|  |  | -	    disk_set_error(state, CRC_ERROR);
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  |  	    UINT wlen = 0;
 | 
	
		
			
				|  |  |  	    FRESULT rv;
 | 
	
	
		
			
				|  | @@ -708,7 +732,7 @@ void abcdisk_io_poll(void)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      for (int i = 0; i < CONTROLLER_TYPES; i++) {
 | 
	
		
			
				|  |  |  	struct ctl_state *state = &controllers[i];
 | 
	
		
			
				|  |  | -	enum pending pending;
 | 
	
		
			
				|  |  | +	enum pending pending = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (unmount_all && state->initialized) {
 | 
	
		
			
				|  |  |  	    unmount_drives(state);
 | 
	
	
		
			
				|  | @@ -726,19 +750,18 @@ void abcdisk_io_poll(void)
 | 
	
		
			
				|  |  |  	    state->pending = 0;
 | 
	
		
			
				|  |  |  	    unmask_irq(ABC_IRQ);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	    if (pending & PEND_RESET)
 | 
	
		
			
				|  |  | +	    if (pending & (PEND_RESET|PEND_STARTCMD))
 | 
	
		
			
				|  |  |  		disk_reset_state(state);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	    if (pending & PEND_IO)
 | 
	
		
			
				|  |  | +	    else if (pending & PEND_IO)
 | 
	
		
			
				|  |  |  		do_next_command(state);
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if (state->initialized && need_sync)
 | 
	
		
			
				|  |  | +	if (state->initialized && (need_sync || (pending & PEND_RESET)))
 | 
	
		
			
				|  |  |  	    sync_drives(state);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (need_sync) {
 | 
	
		
			
				|  |  | -	/* Did we sync drives? */
 | 
	
		
			
				|  |  | +	/* Did we sync drives? Clear LED. */
 | 
	
		
			
				|  |  |  	set_led(LED_DISKIO, false);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -751,11 +774,14 @@ void abcdisk_init(void)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      static const struct abc_dev iodev_template = {
 | 
	
		
			
				|  |  |  	.callback_mask = IDLE_CALLBACK_MASK,
 | 
	
		
			
				|  |  | +	.inp_en = 3,
 | 
	
		
			
				|  |  |  	.status_first_out_mask = (uint8_t)~0x80,
 | 
	
		
			
				|  |  |  	.status_first_inp_mask = (uint8_t)~0x80,
 | 
	
		
			
				|  |  | -	.callback_out = abcdisk_callback_out,
 | 
	
		
			
				|  |  | -	.callback_inp = abcdisk_callback_inp,
 | 
	
		
			
				|  |  | -	.callback_cmd = abcdisk_callback_cmd
 | 
	
		
			
				|  |  | +	.callback_out[0] = abcdisk_callback_out,
 | 
	
		
			
				|  |  | +	.callback_inp[0] = abcdisk_callback_inp,
 | 
	
		
			
				|  |  | +	.callback_out[2] = abcdisk_callback_restart,
 | 
	
		
			
				|  |  | +	.callback_out[4] = abcdisk_callback_rst, /* C3# = local reset */
 | 
	
		
			
				|  |  | +	.callback_rst    = abcdisk_callback_rst
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      for (int i = 0; i < CONTROLLER_TYPES; i++) {
 | 
	
	
		
			
				|  | @@ -764,7 +790,7 @@ void abcdisk_init(void)
 | 
	
		
			
				|  |  |  	state->params = ¶meters[i];
 | 
	
		
			
				|  |  |  	state->iodev  = iodev_template;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	abc_register(&state->iodev, state->params->devsel);
 | 
	
		
			
				|  |  |  	disk_reset_state(state);
 | 
	
		
			
				|  |  | +	abc_register(&state->iodev, state->params->devsel);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 |