mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1175 lines
29 KiB
1175 lines
29 KiB
#include "insignia.h"
|
|
#include "host_def.h"
|
|
/*
|
|
* SoftPC Version 2.0
|
|
*
|
|
* Title : Floppy Disk Adaptor Emulator
|
|
*
|
|
* Description : The following functions are defined in this module:
|
|
*
|
|
* fla_init() Initialise the fla
|
|
* fla_inb() Read byte from port
|
|
* fla_outb() Write byte to port
|
|
*
|
|
* The actual interface to the device that is acting as
|
|
* a floppy diskette (ie virtual file, slave PC or device
|
|
* driver for real diskette) is handled by the GFI layer.
|
|
* Hence the job of the FLA is to package up the command
|
|
* and pass it to GFI and simulate the result phase once
|
|
* GFI has executed the command.
|
|
*
|
|
* This module provides the Bios and CPU with an emulation
|
|
* of the entire Diskette Adaptor Card including the
|
|
* Intel 8272A FDC and the Digital Ouput Register.
|
|
*
|
|
* Author : Henry Nash / Jim Hatfield
|
|
*
|
|
* Notes : For a detailed description of the IBM Floppy Disk Adaptor
|
|
* and the INTEL Controller chip refer to the following
|
|
* documents:
|
|
*
|
|
* - IBM PC/XT Technical Reference Manual
|
|
* (Section 1-109 Diskette Adaptor)
|
|
* - INTEL Microsystems Components Handbook
|
|
* (Section 6-478 FDC 8272A)
|
|
*
|
|
* The interaction of the Sense Interrupt Status command with
|
|
* the Recalibrate and Seek commands and with chip reset is
|
|
* very complex. The FDC chip does NOT behave as its spec sheets
|
|
* say in some situations. We do the best we can here. If all
|
|
* Seek and Recalibrate commands on a drive are followed by
|
|
* Sense Interrupt Status before any other command to that
|
|
* drive then all should be OK.
|
|
*
|
|
*/
|
|
|
|
#ifdef SCCSID
|
|
static char SccsID[]="@(#)fla.c 1.18 07/06/94 Copyright Insignia Solutions Ltd.";
|
|
#endif
|
|
|
|
#ifdef SEGMENTATION
|
|
/*
|
|
* The following #include specifies the code segment into which this
|
|
* module will by placed by the MPW C compiler on the Mac II running
|
|
* MultiFinder.
|
|
*/
|
|
#include "SOFTPC_FLOPPY.seg"
|
|
#endif
|
|
|
|
|
|
/*
|
|
* O/S include files.
|
|
*/
|
|
#include <stdio.h>
|
|
#include TypesH
|
|
|
|
/*
|
|
* SoftPC include files
|
|
*/
|
|
#include "xt.h"
|
|
#include CpuH
|
|
#include "ica.h"
|
|
#include "ios.h"
|
|
#include "fla.h"
|
|
#include "config.h"
|
|
#include "gfi.h"
|
|
#include "trace.h"
|
|
#include "debug.h"
|
|
#include "fdisk.h"
|
|
#include "quick_ev.h"
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Global data
|
|
* ============================================================================
|
|
*/
|
|
|
|
/*
|
|
* The flag indicating that the FLA is busy and cannot accept asynchronous
|
|
* commands (eg motor off).
|
|
*/
|
|
|
|
boolean fla_busy = TRUE; /* busy until initialised */
|
|
boolean fla_ndma = FALSE;
|
|
|
|
/*
|
|
* ============================================================================
|
|
* Local static data and defines
|
|
* ============================================================================
|
|
*/
|
|
|
|
/*
|
|
* Static forward declarations.
|
|
*/
|
|
static void fdc_ndma_bufmgr_wt IPT1(half_word, value);
|
|
static void fla_atomicxqt IPT0();
|
|
static void fla_ndmaxqt IPT0();
|
|
static void fla_ndma_bump_sectid IPT0();
|
|
|
|
/*
|
|
* The command and result blocks that are used to communicate to the GFI
|
|
* layer. The result block is filled in by gfi during the execution phase.
|
|
*/
|
|
|
|
static FDC_CMD_BLOCK fdc_command_block[MAX_COMMAND_LEN];
|
|
static FDC_RESULT_BLOCK fdc_result_block[MAX_RESULT_LEN];
|
|
|
|
#define FDC_INVALID_CMD 0x80 /* Status after bad command */
|
|
#define FDC_NORMAL_TERMINATION 0
|
|
|
|
/*
|
|
* The FLA supports the FDC status register.
|
|
*/
|
|
|
|
static half_word fdc_status;
|
|
|
|
/*
|
|
* The FLA holds the current active (if any) command in the following byte.
|
|
* This is used as an index into the FDC command data structure.
|
|
*/
|
|
|
|
static half_word fdc_current_command;
|
|
|
|
/*
|
|
* The FLA has an output interrupt line which is gated by a bit in the DOR.
|
|
*/
|
|
|
|
static half_word fdc_int_line;
|
|
|
|
/*
|
|
* The FLA knows when a Sense Interrupt Status is permitted.
|
|
*/
|
|
|
|
static struct {
|
|
half_word full; /* Slot occupied */
|
|
half_word res[2]; /* Result phase */
|
|
} fdc_sis_slot[4]; /* One for each drive */
|
|
|
|
/*
|
|
* The FLA is responsible for maintaining the command and result phases
|
|
* of the FDC. Two variables hold the current pointer into the stack
|
|
* of command and result registers.
|
|
*/
|
|
|
|
static half_word fdc_command_count;
|
|
static half_word fdc_result_count;
|
|
|
|
/*
|
|
* The FLA will emulate non-DMA 8088 <==> FDC data transfers, but only allow DMA mode
|
|
* transfers to actually be sent to the GFI (potentially the back end of the SCSI might
|
|
* re-map this again).
|
|
* The following variable reflects whether the FDC has been put into non-DMA mode
|
|
* from the 8088 program's point of view
|
|
*/
|
|
|
|
|
|
/* The following sector buffer is used for non_dma transfers */
|
|
|
|
#ifdef macintosh
|
|
char *fla_ndma_buffer; /* so that host_init can 'see' it to malloc() it. */
|
|
#else
|
|
static char fla_ndma_buffer[8192];
|
|
#endif
|
|
static int fla_ndma_buffer_count;
|
|
static int fla_ndma_sector_size;
|
|
|
|
/*
|
|
* The FLA stores the IBM Digital Output Register internally
|
|
*/
|
|
|
|
#ifdef BIT_ORDER1
|
|
typedef union {
|
|
half_word all;
|
|
struct {
|
|
HALF_WORD_BIT_FIELD motor_3_on:1;
|
|
HALF_WORD_BIT_FIELD motor_2_on:1;
|
|
HALF_WORD_BIT_FIELD motor_1_on:1;
|
|
HALF_WORD_BIT_FIELD motor_0_on:1;
|
|
HALF_WORD_BIT_FIELD interrupts_enabled:1;
|
|
HALF_WORD_BIT_FIELD not_reset:1;
|
|
HALF_WORD_BIT_FIELD drive_select:2;
|
|
} bits;
|
|
} DOR;
|
|
#endif
|
|
|
|
#ifdef BIT_ORDER2
|
|
typedef union {
|
|
half_word all;
|
|
struct {
|
|
HALF_WORD_BIT_FIELD drive_select:2;
|
|
HALF_WORD_BIT_FIELD not_reset:1;
|
|
HALF_WORD_BIT_FIELD interrupts_enabled:1;
|
|
HALF_WORD_BIT_FIELD motor_0_on:1;
|
|
HALF_WORD_BIT_FIELD motor_1_on:1;
|
|
HALF_WORD_BIT_FIELD motor_2_on:1;
|
|
HALF_WORD_BIT_FIELD motor_3_on:1;
|
|
} bits;
|
|
} DOR;
|
|
#endif
|
|
|
|
DOR dor;
|
|
static IU8 drive_selected = 0; /* Device last used. */
|
|
|
|
/* Centralised handling of connection to ICA, so that very slow
|
|
* CPUs can find out if there is a floppy interrupt pending (see
|
|
* wait_int() in floppy.c). There is some confusion in the code anyway,
|
|
* because the fdc_int_line would appear to be the wire from the
|
|
* fdc to the ica, but the code doesn't quite manage to use it in
|
|
* this way.
|
|
*/
|
|
|
|
GLOBAL IBOOL fdc_interrupt_pending = FALSE;
|
|
|
|
LOCAL void fla_clear_int IFN0()
|
|
{
|
|
/* if (fdc_int_line && dor.bits.interrupts_enabled) */
|
|
|
|
if (fdc_interrupt_pending) {
|
|
ica_clear_int(0, CPU_DISKETTE_INT);
|
|
}
|
|
|
|
fdc_int_line = 0;
|
|
fdc_interrupt_pending = FALSE;
|
|
}
|
|
|
|
LOCAL void fla_hw_interrupt IFN0()
|
|
{
|
|
ica_hw_interrupt(0, CPU_DISKETTE_INT, 1);
|
|
fdc_interrupt_pending = TRUE;
|
|
|
|
/* We would like to set fdc_int_line in this routine,
|
|
* but this wouldn't match the existing code. In particular
|
|
* there are calls which don't seem to include setting the
|
|
* fdc_int_line, and a call which sets up a quick_event to
|
|
* interrupt the ICA but which sets the fdc_int_line
|
|
* immediately.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* ============================================================================
|
|
* External functions
|
|
* ============================================================================
|
|
*/
|
|
|
|
/* This procedure is called from the host specific rfloppy routines
|
|
* as an equivalent to dma_enquire, when the intel program has selected
|
|
* non-dma mode for the FDC (via the SPECIFY command)
|
|
*/
|
|
|
|
void fla_ndma_enquire IFN1(int *,transfer_count)
|
|
{
|
|
*transfer_count = fla_ndma_buffer_count;
|
|
}
|
|
|
|
/* This procedure is called from the host specific rfloppy routines
|
|
* when it wants to transfer diskette data read from the diskette to
|
|
* the 'non-dma buffer' (equivalent of dma_request). The Intel program will be fed from this non-dma buffer.
|
|
*/
|
|
|
|
void fla_ndma_req_wt IFN2(char *,buf,int,n)
|
|
{
|
|
char *p = fla_ndma_buffer;
|
|
|
|
fla_ndma_buffer_count = n;
|
|
while(n--)
|
|
*p++ = *buf++;
|
|
}
|
|
|
|
/* This procedure is called from the host specific rfloppy routines
|
|
* when it wants data destined for the diskette
|
|
*/
|
|
|
|
void fla_ndma_req_rd IFN2(char *,buf,int,n)
|
|
{
|
|
char *p = fla_ndma_buffer;
|
|
|
|
while (n--)
|
|
*buf++ = *p++;
|
|
}
|
|
|
|
|
|
void fla_inb IFN2(io_addr, port, half_word *,value)
|
|
{
|
|
|
|
|
|
note_trace0_no_nl(FLA_VERBOSE, "fla_inb() ");
|
|
fla_busy = TRUE;
|
|
|
|
if (port == DISKETTE_STATUS_REG)
|
|
{
|
|
*value = fdc_status;
|
|
|
|
/*
|
|
* After a read of this register assert the RQM bit,
|
|
* unless the 'not_reset' line is held low!
|
|
*/
|
|
|
|
if (dor.bits.not_reset)
|
|
fdc_status |= FDC_RQM;
|
|
}
|
|
else
|
|
if (port == DISKETTE_DATA_REG)
|
|
{
|
|
/*
|
|
* Make sure the 'not reset' line in the DOR is high
|
|
*/
|
|
|
|
if (!dor.bits.not_reset)
|
|
{
|
|
note_trace0_no_nl(FLA_VERBOSE, "<chip frozen!>");
|
|
*value = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure that the RQM bit is up
|
|
*/
|
|
|
|
if (!(fdc_status & FDC_RQM))
|
|
{
|
|
note_trace0_no_nl(FLA_VERBOSE, "<no RQM!>");
|
|
*value = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure that the DIO bit is up
|
|
*/
|
|
|
|
if (!(fdc_status & FDC_DIO))
|
|
{
|
|
note_trace0_no_nl(FLA_VERBOSE, "<no DIO!>");
|
|
*value = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The first byte of the result phase will clear the INT line
|
|
*/
|
|
|
|
if (fdc_result_count == 0)
|
|
fla_clear_int();
|
|
|
|
/*
|
|
* Read the result bytes out of the result block one at a time.
|
|
*/
|
|
|
|
*value = fdc_result_block[fdc_result_count++];
|
|
|
|
if (fdc_result_count >= gfi_fdc_description[fdc_current_command].result_bytes)
|
|
{
|
|
/*
|
|
* End of result phase - clear BUSY and DIO bits of status reg
|
|
*/
|
|
|
|
fdc_status &= ~FDC_BUSY;
|
|
fdc_status &= ~FDC_DIO;
|
|
fdc_result_count = 0;
|
|
}
|
|
|
|
/*
|
|
* After a read of the data register de-assert the RQM bit
|
|
*/
|
|
|
|
fdc_status &= ~FDC_RQM;
|
|
}
|
|
|
|
else if (port == DISKETTE_DIR_REG)
|
|
{
|
|
/*
|
|
* On the DUAL card, the bottom 7 bits of this register are
|
|
* supplied by the fixed disk adapter ...
|
|
*/
|
|
fdisk_read_dir(port, value);
|
|
|
|
/*
|
|
* ... the top bit comes from the floppy disk adapter
|
|
*/
|
|
if (gfi_change(drive_selected))
|
|
*value |= DIR_DISKETTE_CHANGE;
|
|
else
|
|
*value &= ~DIR_DISKETTE_CHANGE;
|
|
}
|
|
else if (port == DISKETTE_ID_REG)
|
|
{
|
|
/*
|
|
** Do we have a Dual Card ?
|
|
** This is an important question for the floppy BIOS.
|
|
** If a Dual Card exists then the BIOS will do data rate changes
|
|
** supporting hi and lo density media, without a Dual Card the BIOS
|
|
** assumes that lo density media is always present.
|
|
** I imagine this is because a "real" PC has limited floppy device
|
|
** options and a hi density 3.5 inch unit will only exist with a
|
|
** dual card.
|
|
** This is not the case for SoftPC, any combination of floppy devices
|
|
** seems to be quite OK.
|
|
** I will try pretending we have a Dual Card whenever a high denisty
|
|
** unit is present on A or B.
|
|
*/
|
|
switch( gfi_drive_type(0) ){
|
|
case GFI_DRIVE_TYPE_12:
|
|
case GFI_DRIVE_TYPE_144:
|
|
case GFI_DRIVE_TYPE_288:
|
|
*value = DUAL_CARD_ID; break;
|
|
case GFI_DRIVE_TYPE_360:
|
|
case GFI_DRIVE_TYPE_720:
|
|
case GFI_DRIVE_TYPE_NULL:
|
|
switch( gfi_drive_type(1) ){
|
|
case GFI_DRIVE_TYPE_12:
|
|
case GFI_DRIVE_TYPE_144:
|
|
case GFI_DRIVE_TYPE_288:
|
|
*value = DUAL_CARD_ID; break;
|
|
case GFI_DRIVE_TYPE_360:
|
|
case GFI_DRIVE_TYPE_720:
|
|
case GFI_DRIVE_TYPE_NULL:
|
|
*value = 0; break;
|
|
default:
|
|
always_trace0("ERROR: bad drive type");
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
always_trace0( "ERROR: Bad drive type." );
|
|
break;
|
|
}
|
|
#ifndef PROD
|
|
if( *value==DUAL_CARD_ID ){
|
|
note_trace0( FLA_VERBOSE, "Dual Card\n" );
|
|
}else{
|
|
note_trace0( FLA_VERBOSE, "No Dual Card\n" );
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
*value = 0;
|
|
note_trace0_no_nl(FLA_VERBOSE, "<unknown port>");
|
|
}
|
|
|
|
note_trace2(FLA_VERBOSE, " port %x, returning %x", port, *value);
|
|
fla_busy = FALSE;
|
|
|
|
|
|
}
|
|
|
|
|
|
void fla_outb IFN2(io_addr, port, half_word, value)
|
|
{
|
|
int i;
|
|
DOR new_dor;
|
|
|
|
|
|
note_trace2_no_nl(FLA_VERBOSE, "fla_outb(): port %x, value %x ", port, value);
|
|
fla_busy = TRUE;
|
|
|
|
|
|
|
|
if (port == DISKETTE_STATUS_REG)
|
|
{
|
|
note_trace0(FLA_VERBOSE, "<write on status reg>");
|
|
}
|
|
|
|
else if (port == DISKETTE_DCR_REG)
|
|
{
|
|
/*
|
|
** Send the specified data rate to the floppy using the new
|
|
** style gfi_high() function that now has a data rate parameter.
|
|
*/
|
|
note_trace2( FLA_VERBOSE,
|
|
"fla_outb:DCR:port=%x value=%x set data rate",
|
|
port, value );
|
|
gfi_high(dor.bits.drive_select, value);
|
|
}
|
|
else if (port == DISKETTE_DATA_REG)
|
|
{
|
|
/*
|
|
* Make sure the 'not reset' line in the DOR is high
|
|
*/
|
|
|
|
if (!dor.bits.not_reset)
|
|
{
|
|
note_trace0_no_nl(FLA_VERBOSE, "<chip frozen!>");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure that the RQM bit is up
|
|
*/
|
|
|
|
if (!(fdc_status & FDC_RQM))
|
|
{
|
|
note_trace0_no_nl(FLA_VERBOSE, "<no RQM!>");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure that the DIO bit is down
|
|
*/
|
|
|
|
if (fdc_status & FDC_DIO)
|
|
{
|
|
note_trace0_no_nl(FLA_VERBOSE, "<DIO set!>");
|
|
return;
|
|
}
|
|
|
|
note_trace0(FLA_VERBOSE, "");
|
|
|
|
/*
|
|
* Output to the data register: must be programming up a command or issuing
|
|
* a data byte for a non-dma disk write.
|
|
* If the BUSY flag isn't set then it's the first byte.
|
|
*/
|
|
|
|
if (!(fdc_status & FDC_BUSY))
|
|
{
|
|
fdc_current_command = value & FDC_COMMAND_MASK;
|
|
fdc_command_count = 0;
|
|
fdc_status |= FDC_BUSY;
|
|
}
|
|
|
|
if (!(fdc_status & FDC_NDMA))
|
|
/* programming up a command
|
|
*/
|
|
{
|
|
if (gfi_fdc_description[fdc_current_command].cmd_bytes == 0)
|
|
{
|
|
/*
|
|
* Invalid command or Sense Int Status.
|
|
* If Sense Int Status, try to find some result phase data,
|
|
* else treat as an invalid command. In either case go
|
|
* straight into the result phase.
|
|
* Sense Int Status also clears Drive Busy bits and the INT line.
|
|
*/
|
|
|
|
if (fdc_current_command == FDC_SENSE_INT_STATUS)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
if (fdc_sis_slot[i].full)
|
|
break;
|
|
|
|
if (i < 4) /* Found one! */
|
|
{
|
|
fdc_sis_slot[i].full = 0;
|
|
fdc_result_block[0] = fdc_sis_slot[i].res[0];
|
|
fdc_result_block[1] = fdc_sis_slot[i].res[1];
|
|
|
|
fla_clear_int(); /* Clear INT line */
|
|
|
|
fdc_status &= ~(1 << i); /* Clear Drive Busy */
|
|
}
|
|
else
|
|
{
|
|
fdc_command_block[0] = 0;
|
|
fdc_result_block[0] = FDC_INVALID_CMD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fdc_command_block[0] = 0;
|
|
fdc_result_block[0] = FDC_INVALID_CMD;
|
|
}
|
|
|
|
fdc_status |= FDC_DIO;
|
|
}
|
|
else
|
|
{
|
|
fdc_command_block[fdc_command_count++] = value;
|
|
if (fdc_command_count >= gfi_fdc_description[fdc_current_command].cmd_bytes)
|
|
{
|
|
/* n.b; the field 'dma_required' is a misnomer ... it
|
|
* strictly should be 'data_required'
|
|
*/
|
|
if (!(gfi_fdc_description[fdc_current_command].dma_required))
|
|
fla_atomicxqt();
|
|
else
|
|
{
|
|
if (!fla_ndma)
|
|
fla_atomicxqt();
|
|
else
|
|
fla_ndmaxqt();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
/* receiving a non-dma data byte
|
|
*/
|
|
{
|
|
/* pass written byte to buffer manager
|
|
*/
|
|
fdc_ndma_bufmgr_wt (value);
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled)
|
|
fla_hw_interrupt();
|
|
}
|
|
|
|
|
|
/*
|
|
* On write of the data register de-assert the RQM bit
|
|
*/
|
|
|
|
fdc_status &= ~FDC_RQM;
|
|
}
|
|
|
|
else
|
|
if (port == DISKETTE_DOR_REG)
|
|
{
|
|
note_trace0(FLA_VERBOSE, "");
|
|
|
|
new_dor.all = value;
|
|
if (!new_dor.bits.not_reset)
|
|
{
|
|
dor.all = new_dor.all;
|
|
dor.bits.motor_0_on = 0;
|
|
dor.bits.motor_1_on = 0;
|
|
dor.bits.motor_2_on = 0;
|
|
dor.bits.motor_3_on = 0;
|
|
fdc_status &= ~FDC_RQM;
|
|
fdc_int_line = 0;
|
|
}
|
|
else
|
|
{
|
|
if (!dor.bits.not_reset && new_dor.bits.not_reset)
|
|
{
|
|
/*
|
|
* Reset the FLA and GFI (and hence the real device).
|
|
* It is assumed that GFI reset will stop all drive motors.
|
|
* After the reset check to see if we need to turn any drive on.
|
|
*
|
|
* Note that reset effectively has a result phase since GFI
|
|
* will execute a Sense Interrupt Status command after it.
|
|
*/
|
|
|
|
gfi_reset(fdc_result_block, new_dor.bits.drive_select);
|
|
|
|
fdc_status = FDC_RQM;
|
|
fdc_command_count = 0;
|
|
fdc_result_count = 0;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
fdc_sis_slot[i].full = 1;
|
|
fdc_sis_slot[i].res[0] = 0xC0 + i; /* Empirically */
|
|
fdc_sis_slot[i].res[1] = 0;
|
|
}
|
|
|
|
fdc_int_line = 1;
|
|
}
|
|
|
|
/*
|
|
* There are three ways in which an interrupt may be generated:
|
|
*
|
|
* 1) The not_reset line goes low to high when the enable_ints
|
|
* line is high.
|
|
*
|
|
* 2) The enable_ints line goes low to high when the INT line
|
|
* is high.
|
|
*
|
|
* 3) Both of the above!
|
|
*/
|
|
|
|
if ((!dor.bits.not_reset && new_dor.bits.not_reset && new_dor.bits.interrupts_enabled)
|
|
||(fdc_int_line && !dor.bits.interrupts_enabled && new_dor.bits.interrupts_enabled))
|
|
fla_hw_interrupt();
|
|
|
|
/*
|
|
* If any drive motor bits have changed then issue GFI calls
|
|
*/
|
|
|
|
if (!dor.bits.motor_0_on && new_dor.bits.motor_0_on)
|
|
gfi_drive_on(0);
|
|
else
|
|
if (dor.bits.motor_0_on && !new_dor.bits.motor_0_on)
|
|
gfi_drive_off(0);
|
|
|
|
if (!dor.bits.motor_1_on && new_dor.bits.motor_1_on)
|
|
gfi_drive_on(1);
|
|
else
|
|
if (dor.bits.motor_1_on && !new_dor.bits.motor_1_on)
|
|
gfi_drive_off(1);
|
|
|
|
if (!dor.bits.motor_2_on && new_dor.bits.motor_2_on)
|
|
gfi_drive_on(2);
|
|
else
|
|
if (dor.bits.motor_2_on && !new_dor.bits.motor_2_on)
|
|
gfi_drive_off(2);
|
|
|
|
if (!dor.bits.motor_3_on && new_dor.bits.motor_3_on)
|
|
gfi_drive_on(3);
|
|
else
|
|
if (dor.bits.motor_3_on && !new_dor.bits.motor_3_on)
|
|
gfi_drive_off(3);
|
|
|
|
/* Only store drive_select if actively used. */
|
|
if (new_dor.bits.motor_0_on || new_dor.bits.motor_1_on)
|
|
{
|
|
drive_selected = new_dor.bits.drive_select;
|
|
}
|
|
dor.all = new_dor.all;
|
|
}
|
|
}
|
|
|
|
#ifndef PROD
|
|
else
|
|
note_trace0(FLA_VERBOSE, "<unknown port>");
|
|
#endif
|
|
|
|
fla_busy = FALSE;
|
|
|
|
|
|
}
|
|
|
|
|
|
void trap_ndma IFN0()
|
|
{
|
|
if (get_type_cmd(fdc_command_block) == FDC_SPECIFY)
|
|
{
|
|
if (get_c6_ND(fdc_command_block))
|
|
{
|
|
|
|
fla_ndma = TRUE;
|
|
put_c6_ND(fdc_command_block, 0);
|
|
note_trace0(FLA_VERBOSE, "DISABLING NON_DMA FDC REQ>");
|
|
}
|
|
else
|
|
fla_ndma = FALSE;
|
|
}
|
|
}
|
|
|
|
LOCAL void fla_int_call_back IFN1(long,junk)
|
|
{
|
|
UNUSED(junk);
|
|
fla_hw_interrupt();
|
|
}
|
|
|
|
#ifdef NTVDM
|
|
|
|
void fdc_command_completed (UTINY drive, half_word fdc_command)
|
|
{
|
|
|
|
if (gfi_fdc_description[fdc_command].int_required) {
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled)
|
|
add_q_event_i(fla_int_call_back, HOST_FLA_DELAY, 0);
|
|
fdc_int_line = 1;
|
|
}
|
|
|
|
/*
|
|
* If the command issued was Seek or Recalibrate, save
|
|
* the GFI result phase ready for Sense Int Status.
|
|
* Set the Drive Busy line (cleared by SIS).
|
|
* Any other command clears the SIS slot.
|
|
*/
|
|
|
|
if (fdc_command == FDC_SEEK || fdc_command == FDC_RECALIBRATE) {
|
|
fdc_sis_slot[drive].full = 1;
|
|
fdc_sis_slot[drive].res[0] = fdc_result_block[0];
|
|
fdc_sis_slot[drive].res[1] = fdc_result_block[1];
|
|
fdc_status |= (1 << drive);
|
|
}
|
|
else
|
|
fdc_sis_slot[drive].full = 0;
|
|
|
|
/*
|
|
* If there is no result phase then go back to READY.
|
|
*/
|
|
|
|
if (gfi_fdc_description[fdc_command].result_bytes == 0)
|
|
fdc_status &= ~FDC_BUSY;
|
|
else
|
|
fdc_status |= FDC_DIO;
|
|
}
|
|
|
|
/*
|
|
* This routine will 'automatically' execute the current FDC command.
|
|
* The GFI layer will actually perform the command/execution/result phases
|
|
* and return any result block. The Intel program.
|
|
*/
|
|
|
|
static void fla_atomicxqt IFN0()
|
|
{
|
|
int ret_stat;
|
|
UTINY drive;
|
|
|
|
/*
|
|
* Call GFI to execute the command
|
|
*/
|
|
drive = get_type_drive(fdc_command_block);
|
|
trap_ndma();
|
|
|
|
ret_stat = gfi_fdc_command(fdc_command_block, fdc_result_block);
|
|
if (ret_stat != SUCCESS)
|
|
{
|
|
/*
|
|
* GFI failed due to timeout or protocol error - so we will
|
|
* fake up a real timeout by not generating an interrupt.
|
|
*/
|
|
/* we created a new thread to simulate the fdc while something is wrong.
|
|
* here we don't want to turn the busy signal off until we have a reset
|
|
* fdc_status &= ~FDC_BUSY;
|
|
* fdc_status &= ~FDC_DIO;
|
|
*/
|
|
note_trace1(FLA_VERBOSE, "fla_outb(): <gfi returns error %x>",
|
|
ret_stat);
|
|
}
|
|
else
|
|
fdc_command_completed(drive, fdc_current_command);
|
|
}
|
|
|
|
#else /* NTVDM */
|
|
|
|
/*
|
|
* This routine will 'automatically' execute the current FDC command.
|
|
* The GFI layer will actually perform the command/execution/result phases
|
|
* and return any result block. The Intel program.
|
|
*/
|
|
|
|
static void fla_atomicxqt IFN0()
|
|
{
|
|
int ret_stat;
|
|
int drive;
|
|
|
|
/*
|
|
* Call GFI to execute the command
|
|
*/
|
|
|
|
trap_ndma();
|
|
|
|
ret_stat = gfi_fdc_command(fdc_command_block, fdc_result_block);
|
|
if (ret_stat != SUCCESS)
|
|
{
|
|
/*
|
|
* GFI failed due to timeout or protocol error - so we will
|
|
* fake up a real timeout by not generating an interrupt.
|
|
*/
|
|
|
|
fdc_status &= ~FDC_BUSY;
|
|
fdc_status &= ~FDC_DIO;
|
|
|
|
note_trace1(FLA_VERBOSE, "fla_outb(): <gfi returns error %x>",
|
|
ret_stat);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Command was successful, generate an interrupt if enabled.
|
|
*/
|
|
|
|
if (gfi_fdc_description[fdc_current_command].int_required)
|
|
{
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled) {
|
|
add_q_event_i(fla_int_call_back, HOST_FLA_DELAY, 0);
|
|
}
|
|
fdc_int_line = 1;
|
|
}
|
|
|
|
/*
|
|
* If the command issued was Seek or Recalibrate, save
|
|
* the GFI result phase ready for Sense Int Status.
|
|
* Set the Drive Busy line (cleared by SIS).
|
|
* Any other command clears the SIS slot.
|
|
*/
|
|
|
|
drive = get_type_drive(fdc_command_block);
|
|
|
|
if (fdc_current_command == FDC_SEEK || fdc_current_command == FDC_RECALIBRATE)
|
|
{
|
|
fdc_sis_slot[drive].full = 1;
|
|
fdc_sis_slot[drive].res[0] = fdc_result_block[0];
|
|
fdc_sis_slot[drive].res[1] = fdc_result_block[1];
|
|
fdc_status |= (1 << drive);
|
|
}
|
|
else
|
|
fdc_sis_slot[drive].full = 0;
|
|
|
|
/*
|
|
* If there is no result phase then go back to READY.
|
|
*/
|
|
|
|
if (gfi_fdc_description[fdc_current_command].result_bytes == 0)
|
|
fdc_status &= ~FDC_BUSY;
|
|
else
|
|
fdc_status |= FDC_DIO;
|
|
}
|
|
}
|
|
#endif /* NTVDM */
|
|
|
|
|
|
static void fdc_request_write_data_to_cpu IFN0()
|
|
{
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled)
|
|
fla_hw_interrupt();
|
|
}
|
|
|
|
|
|
static void fdc_request_read_data_from_cpu IFN0()
|
|
{
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled)
|
|
fla_hw_interrupt();
|
|
}
|
|
|
|
|
|
/* Prepare for processor data requests.
|
|
* i.e; based upon the current command, establish the minimum number of bytes
|
|
* likely to be involved in a transfer, based upon the N parameter.
|
|
* Set the fla_ndma_byte_count global appropriately. Set the ndma buffer
|
|
* count to zero, forcing a real command read to occur the first time
|
|
* the Intel program tries to read/write data to the FDC during its
|
|
* non-dma execution phase.
|
|
* Issue the first interrupt, and set FDC status register to mirror this
|
|
* and set non-dma bit in status register
|
|
*/
|
|
|
|
static void fla_ndmaxqt IFN0()
|
|
{
|
|
int n;
|
|
static int fla_ndma_sectsize[] = {128,256,512,1024,2048,4096,8192};
|
|
|
|
note_trace0(FLA_VERBOSE, "DOING FLA_NDMAXQT");
|
|
|
|
/* set the non-dma bit in the status register ...
|
|
* this clears at the end of the execution phase
|
|
*/
|
|
|
|
fdc_status |= FDC_NDMA;
|
|
|
|
fla_ndma_buffer_count = 0;
|
|
|
|
switch (gfi_fdc_description[fdc_current_command].cmd_class)
|
|
{
|
|
case 0: /* sector read(s) */
|
|
|
|
n = get_c0_N(fdc_command_block);
|
|
if (n)
|
|
fla_ndma_sector_size = fla_ndma_sectsize[n];
|
|
else
|
|
fla_ndma_sector_size = get_c0_DTL(fdc_command_block);
|
|
|
|
/* kick of the execution phase by issuing
|
|
* an interrupt
|
|
*/
|
|
|
|
fdc_request_write_data_to_cpu();
|
|
|
|
break;
|
|
|
|
case 1: /* sector write(s) */
|
|
|
|
n = get_c0_N(fdc_command_block);
|
|
if (n)
|
|
fla_ndma_sector_size = fla_ndma_sectsize[n];
|
|
else
|
|
fla_ndma_sector_size = get_c0_DTL(fdc_command_block);
|
|
|
|
|
|
/* kick of the execution phase by issuing
|
|
* an interrupt
|
|
*/
|
|
|
|
fdc_request_read_data_from_cpu();
|
|
|
|
break;
|
|
|
|
case 2: /* track read */
|
|
always_trace0("\n FLA ... non-dma read track unimplemented");
|
|
break;
|
|
|
|
case 3: /* format track */
|
|
always_trace0("\n FLA ... non-dma format unimplemented");
|
|
break;
|
|
|
|
default:
|
|
always_trace0("\n FLA ... unexpected command for non-dma");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* peek a quick look at the 'first' sector involved in this current
|
|
* FDC command, to establish whether abnormal termination would have occurred * with the non-DMA transfer, and flag accordingly
|
|
*/
|
|
|
|
void fla_ndma_sector_peep IFN1(int *,all_clear)
|
|
/* all_clear -----> = 0 --> time out
|
|
* = 1 --> sector good
|
|
* = 2 --> abnormal termination
|
|
*/
|
|
{
|
|
int true_command, status;
|
|
|
|
/* build a 'read data' command using all current command
|
|
* parameters.
|
|
*/
|
|
|
|
true_command = get_type_cmd(fdc_command_block);
|
|
put_type_cmd(fdc_command_block, FDC_READ_DATA);
|
|
|
|
status = gfi_fdc_command(fdc_command_block, fdc_result_block);
|
|
|
|
fla_ndma_buffer_count = 0;
|
|
|
|
/* repair the command block
|
|
*/
|
|
|
|
put_type_cmd(fdc_command_block, true_command);
|
|
|
|
*all_clear = 0;
|
|
|
|
if (status == SUCCESS)
|
|
{
|
|
if (get_r1_ST0_int_code(fdc_result_block) == FDC_NORMAL_TERMINATION)
|
|
*all_clear = 1;
|
|
else
|
|
*all_clear = 2;
|
|
}
|
|
|
|
}
|
|
|
|
/* This routine emulates the execution phase for sector writes
|
|
* ... Here we buffer up data destined for the diskette on a
|
|
* 'per sector' basis (the 'sector' size being determined by the
|
|
* 'N' parameter (or possibly the 'DTL' parameter (if N=0)) specified
|
|
* within the FDC command block. If the buffer is empty, the equivalent
|
|
* read command is issued to the GFI layer, mainly to determine whether
|
|
* the sector is kosher.
|
|
*/
|
|
|
|
static void fdc_ndma_bufmgr_wt IFN1(half_word, value)
|
|
{
|
|
int status;
|
|
int all_clear;
|
|
|
|
note_trace1(FLA_VERBOSE,
|
|
"FDC_NDMA_BUFMGR_WT called .. buffered byte = %x",
|
|
(unsigned int) value);
|
|
|
|
/*
|
|
* empty buffer!! if so, read the sector first to see if it exists, etc.
|
|
*/
|
|
|
|
if (fla_ndma_buffer_count == 0)
|
|
{
|
|
fla_ndma_sector_peep(&all_clear);
|
|
switch (all_clear)
|
|
{
|
|
case 0: /* FDC dead */
|
|
fdc_status &= ~FDC_BUSY;
|
|
fdc_status &= ~FDC_DIO;
|
|
return;
|
|
case 1: /* FDC cooking */
|
|
/* ... increment the sector id (as the controller
|
|
* would do given half a chance!!
|
|
*/
|
|
fla_ndma_bump_sectid();
|
|
break;
|
|
case 2: /* FDC does not like command parameters
|
|
* if it doesn't ... neither do i !!
|
|
*/
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled)
|
|
fla_hw_interrupt();
|
|
fdc_status &= ~FDC_NDMA;
|
|
fdc_status |= FDC_DIO;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* is there room in the buffer ? ... flush out if not
|
|
* and recurse.
|
|
*/
|
|
|
|
if (fla_ndma_buffer_count == fla_ndma_sector_size)
|
|
{
|
|
/* do the command ... the GFI layler will call 'fla_ndma_req_rd'
|
|
* to get the data in this full buffer
|
|
*/
|
|
|
|
status = gfi_fdc_command(fdc_command_block, fdc_result_block);
|
|
|
|
/* reset the buffer */
|
|
|
|
fla_ndma_buffer_count = 0;
|
|
|
|
if (status != SUCCESS)
|
|
{
|
|
fdc_status &= ~FDC_BUSY;
|
|
fdc_status &= ~FDC_DIO;
|
|
}
|
|
else
|
|
{
|
|
if (get_r1_ST0_int_code(fdc_result_block) == FDC_NORMAL_TERMINATION)
|
|
fdc_ndma_bufmgr_wt(value);
|
|
else
|
|
{
|
|
if (!fdc_int_line && dor.bits.interrupts_enabled)
|
|
fla_hw_interrupt();
|
|
fdc_status &= ~FDC_NDMA;
|
|
fdc_status |= FDC_DIO;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
fla_ndma_buffer[fla_ndma_buffer_count++] = value;
|
|
|
|
}
|
|
|
|
|
|
static void fla_ndma_bump_sectid IFN0()
|
|
{
|
|
int i;
|
|
|
|
i = get_c0_sector(fdc_command_block) + 1;
|
|
put_c0_sector(fdc_command_block, i);
|
|
}
|
|
|
|
static void fla_ndma_unbump_sectid IFN0()
|
|
{
|
|
int i;
|
|
|
|
i = get_c0_sector(fdc_command_block) - 1;
|
|
put_c0_sector(fdc_command_block, i);
|
|
}
|
|
|
|
#ifdef SEGMENTATION
|
|
/*
|
|
* The following #include specifies the code segment into which this
|
|
* function will by placed by the MPW C compiler on the Mac II running
|
|
* MultiFinder.
|
|
*/
|
|
#include "SOFTPC_INIT.seg"
|
|
#endif
|
|
|
|
void fla_init IFN0()
|
|
{
|
|
io_addr i;
|
|
|
|
note_trace0(FLA_VERBOSE, "fla_init() called");
|
|
|
|
/*
|
|
* Set up the IO chip select logic for this adaptor
|
|
* Assume that the DOR comes up with all bits zero.
|
|
*/
|
|
|
|
io_define_inb(FLA_ADAPTOR, fla_inb);
|
|
io_define_outb(FLA_ADAPTOR, fla_outb);
|
|
|
|
/*
|
|
* For the DUAL card, one of the registers must be left
|
|
* for the hard disk adapter to connect to
|
|
*/
|
|
|
|
for(i = DISKETTE_PORT_START; i <= DISKETTE_PORT_END; i++)
|
|
{
|
|
if (i != DISKETTE_FDISK_REG)
|
|
io_connect_port(i, FLA_ADAPTOR, IO_READ_WRITE);
|
|
}
|
|
|
|
fla_busy = TRUE;
|
|
|
|
fdc_status = 0;
|
|
fdc_command_count = 0;
|
|
fdc_result_count = 0;
|
|
fdc_int_line = 0;
|
|
dor.all = 0;
|
|
|
|
fla_ndma = FALSE;
|
|
fla_busy = FALSE;
|
|
}
|