#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 #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, ""); *value = 0; return; } /* * Make sure that the RQM bit is up */ if (!(fdc_status & FDC_RQM)) { note_trace0_no_nl(FLA_VERBOSE, ""); *value = 0; return; } /* * Make sure that the DIO bit is up */ if (!(fdc_status & FDC_DIO)) { note_trace0_no_nl(FLA_VERBOSE, ""); *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, ""); } 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, ""); } 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, ""); return; } /* * Make sure that the RQM bit is up */ if (!(fdc_status & FDC_RQM)) { note_trace0_no_nl(FLA_VERBOSE, ""); return; } /* * Make sure that the DIO bit is down */ if (fdc_status & FDC_DIO) { note_trace0_no_nl(FLA_VERBOSE, ""); 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, ""); #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(): ", 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(): ", 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; }