#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;
}