#include "insignia.h" #include "host_def.h" /* * SoftPC Revision 3.0 * * * Title : Secondary SFD BIOS floppy diskette functions * * * Description : This module defines the functions that the DISKETTE_IO * operating system call switches to on the value of AH: * * (AH=00H) reset the floppy diskette system * * (AH=01H) return the status of the floppy diskette system * * (AH=02H) read sectors from a floppy diskette * * (AH=03H) write sectors to a floppy diskette * * (AH=04H) verify sectors on a floppy diskette * * (AH=05H) format a track on a floppy diskette * * (AH=06H) * to invalid * (AH=07H) * * (AH=08H) return floppy diskette system parameters * * (AH=09H) * to invalid * (AH=14H) * * (AH=15H) return floppy diskette drive parameters * * (AH=16H) return floppy diskette drive change line status * * (AH=17H) set media density for a format operation * * (AH=18H) set media type for a format operation * * * Author : Ross Beresford * * * Notes : For a detailed description of the IBM Floppy Disk Adaptor, * and the INTEL Controller chips refer to the following * documents: * * - IBM PC/XT Technical Reference Manual * (Section 1-109 Diskette Adaptor) * - INTEL Microsystems Components Handbook * (Section 6-52 DMA Controller 8237A) * - INTEL Microsystems Components Handbook * (Section 6-478 FDC 8272A) * * Mods: * Tim September 1991. nec_term() changed two error code returns. * Helps Dos give correct error messages when no floppy in drive. */ /* * * # # ## # ##### #### * # # # # # # # * # # # # # # #### * # ## # ###### # # # * ## ## # # # # # # * # # # # # # #### * * READ THIS: IMPORTANT NOTICE ABOUT WAITS * * The motor and head settle time waits etc used to be done * using a busy wait loop in a sub-CPU: this was what the * waitf() call was for, and this accurately emulated what * the real BIOS does. * * It was certainly a bad thing, however, as most * floppies we support are "soft" in the sense that * their underlying driver automatically waits for motor * start-up etc (examples are the slave, virtual, and * empty drive, and the real drive on the VAX ports). * * How should we deal with the few drives where we must * actually wait the correct time for motor start-up etc * before doing reads, writes, formats etc? The low * density BIOS relies on the GFI real diskette server * waiting for motor start-up in the driver itself (see * for example sun3_wang.c and ip32_flop.c). For the * moment the high density BIOS will do the same: it * might be better, however, for new GFI level functions * to be added to explicitly wait for driver events. */ /* * static char SccsID[]="@(#)floppy.c 1.22 09/19/94 Copyright Insignia Solutions Ltd."; */ #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 #include #include TypesH #include "xt.h" #include CpuH #include "sas.h" #include "ios.h" #include "bios.h" #include "dma.h" #include "config.h" #include "fla.h" #include "gfi.h" #include "equip.h" #include "floppy.h" #include "trace.h" #include "debug.h" #include "tape_io.h" #include "cmos.h" #include "cmosbios.h" #include "rtc_bios.h" /* * Definition of the diskette operation function jump table */ void ((*(fl_fnc_tab[FL_JUMP_TABLE_SIZE])) IPT1(int, drive)) = { fl_disk_reset, fl_disk_status, fl_disk_read, fl_disk_write, fl_disk_verify, fl_disk_format, fl_fnc_err, fl_fnc_err, fl_disk_parms, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_fnc_err, fl_disk_type, fl_disk_change, fl_format_set, fl_set_media, }; #ifdef NTVDM extern UTINY number_of_floppy; #endif /* NTVDM */ /* * Functions defined later */ LOCAL half_word get_parm IPT1(int, index); LOCAL cmos_type IPT2(int, drive, half_word *, type); LOCAL wait_int IPT0(); LOCAL void nec_output IPT1(half_word, byte_value); LOCAL results IPT0(); LOCAL void send_spec IPT0(); LOCAL void setup_end IPT1(int, sectors_transferred); LOCAL void rd_wr_vf IPT3(int, drive, FDC_CMD_BLOCK *, fcbp, half_word, dma_type); LOCAL void translate_new IPT1(int, drive); LOCAL void fmt_init IPT1(int, drive); LOCAL med_change IPT1(int, drive); LOCAL chk_lastrate IPT1(int, drive); LOCAL void send_rate IPT1(int, drive); LOCAL fmtdma_set IPT0(); LOCAL void nec_init IPT2(int, drive, FDC_CMD_BLOCK *, fcbp); LOCAL nec_term IPT0(); LOCAL dr_type_check IPT3(half_word, drive_type, word *, seg_ptr, word *, off_ptr); LOCAL read_dskchng IPT1(int, drive); LOCAL void setup_state IPT1(int, drive); LOCAL setup_dbl IPT1(int, drive); LOCAL dma_setup IPT1(half_word, dma_mode); LOCAL void rwv_com IPT2(word, md_segment, word, md_offset); LOCAL retry IPT1(int, drive); LOCAL void dstate IPT1(int, drive); LOCAL num_trans IPT0(); LOCAL void motor_on IPT1(int, drive); LOCAL seek IPT2(int, drive, int, track); LOCAL read_id IPT2(int, drive, int, head); LOCAL turn_on IPT1(int, drive); LOCAL void waitf IPT1(long, time); LOCAL recal IPT1(int, drive); LOCAL chk_stat_2 IPT0(); /* * This macro defines the normal behaviour of the FDC after a reset. * Sending a series of sense interrupt status commands following a * reset, for each drive in the correct order, should elicit the * expected result in ST0. */ #define expected_st0(drive) (ST0_INTERRUPT_CODE_0 | ST0_INTERRUPT_CODE_1 | drive) LOCAL UTINY fl_nec_status[8]; #define LOAD_RESULT_BLOCK sas_loads(BIOS_FDC_STATUS_BLOCK, fl_nec_status,\ sizeof(fl_nec_status)) LOCAL BOOL rate_unitialised = TRUE; /* * Definition of the external functions */ /* reports whether drive is high density, replaces old test for dual card which assumed high density a or b implied high density a, which it doesn't now we can have two drives of any 3.5 / 5.25 combination */ LOCAL BOOL high_density IFN1(int, drive) { half_word drive_type; if (cmos_type(drive, &drive_type) == FAILURE) return(FALSE); switch (drive_type) { case GFI_DRIVE_TYPE_12: case GFI_DRIVE_TYPE_144: case GFI_DRIVE_TYPE_288: return(TRUE); default: return(FALSE); } } void fl_disk_reset IFN1(int, drive) { /* * Reset the FDC and all drives. "drive" is not significant * * Register inputs: * none * Register outputs: * AH diskette status * CF status flag */ half_word motor_status, diskette_dor_reg, diskette_status; /* * Switch on interrupt enable and clear the reset bit in the * DOR to do the reset, then restore the reset bit */ sas_load(MOTOR_STATUS, &motor_status); diskette_dor_reg = (motor_status << 4) | (motor_status >> 4); diskette_dor_reg &= ~DOR_RESET; diskette_dor_reg |= DOR_INTERRUPTS; outb(DISKETTE_DOR_REG, diskette_dor_reg); diskette_dor_reg |= DOR_RESET; outb(DISKETTE_DOR_REG, diskette_dor_reg); /* * Set SEEK_STATUS up to force a recalibrate on all drives */ sas_store(SEEK_STATUS, 0); /* * Check FDC responds as expected, viz: a drive ready * transition for each drive potentially installed; if * not, then there is an error in the FDC. */ if (wait_int() == FAILURE) { /* * Problem with the FDC * * The reset implied by the outb(DISKETTE_DOR_REG) above * should trigger a hardware interrupt, and the wait_int * should have detected and processed it. */ always_trace0("FDC failed to interrupt after a reset - HW interrupts broken?"); sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_FDC_ERROR; sas_store(FLOPPY_STATUS, diskette_status); } else { for(drive = 0; drive < MAX_DISKETTES; drive++) { nec_output(FDC_SENSE_INT_STATUS); if ( (results() == FAILURE) || (get_r3_ST0(fl_nec_status) != expected_st0(drive))) { /* * Problem with the FDC */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_FDC_ERROR; sas_store(FLOPPY_STATUS, diskette_status); always_trace1("diskette_io: FDC error - drive %d moribund after reset", drive); break; } } /* * If all drives OK, send the specify command to the * FDC */ if (drive == MAX_DISKETTES) send_spec(); } /* * Return, without setting sectors transferred */ setup_end(IGNORE_SECTORS_TRANSFERRED); } void fl_disk_status IFN1(int, drive) { /* * Set the diskette status, and return without setting * sectors transferred. "drive" is not significant * * Register inputs: * AH diskette status * Register outputs: * AH diskette status * CF status flag */ UNUSED(drive); sas_store(FLOPPY_STATUS, getAH()); setup_end(IGNORE_SECTORS_TRANSFERRED); } void fl_disk_read IFN1(int, drive) { /* * Read sectors from the diskette in "drive" * * Register inputs: * DH head number * CH track number * CL sector number * AL number of sectors * ES:BX buffer address * Register outputs: * AL number of sectors read * AH diskette status * CF status flag */ half_word motor_status; FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; /* * Not a write operation */ sas_load(MOTOR_STATUS, &motor_status); motor_status &= ~MS_WRITE_OP; sas_store(MOTOR_STATUS, motor_status); /* * Fill in skeleton FDC command block and use generic * diskette transfer function to do the read */ put_c0_cmd(fdc_cmd_block, FDC_READ_DATA); put_c0_skip(fdc_cmd_block, 1); put_c0_MFM(fdc_cmd_block, 1); put_c0_MT(fdc_cmd_block, 1); rd_wr_vf(drive, fdc_cmd_block, BIOS_DMA_READ); } void fl_disk_write IFN1(int, drive) { /* * Write sectors to the diskette in "drive" * * Register inputs: * DH head number * CH track number * CL sector number * AL number of sectors * ES:BX buffer address * Register outputs: * AL number of sectors written * AH diskette status * CF status flag */ half_word motor_status; FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; /* * A write operation */ sas_load(MOTOR_STATUS, &motor_status); motor_status |= MS_WRITE_OP; sas_store(MOTOR_STATUS, motor_status); /* * Fill in skeleton FDC command block and use generic * diskette transfer function to do the write */ put_c1_cmd(fdc_cmd_block, FDC_WRITE_DATA); put_c1_pad(fdc_cmd_block, 0); put_c1_MFM(fdc_cmd_block, 1); put_c1_MT(fdc_cmd_block, 1); rd_wr_vf(drive, fdc_cmd_block, BIOS_DMA_WRITE); } void fl_disk_verify IFN1(int, drive) { /* * Verify sectors in the diskette in "drive" * * Register inputs: * DH head number * CH track number * CL sector number * AL number of sectors * Register outputs: * AL number of sectors verified * AH diskette status * CF status flag */ half_word motor_status; FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; /* * Not a write operation */ sas_load(MOTOR_STATUS, &motor_status); motor_status &= ~MS_WRITE_OP; sas_store(MOTOR_STATUS, motor_status); /* * Fill in skeleton FDC command block and use generic * diskette transfer function to do the verify */ put_c0_cmd(fdc_cmd_block, FDC_READ_DATA); put_c0_skip(fdc_cmd_block, 1); put_c0_MFM(fdc_cmd_block, 1); put_c0_MT(fdc_cmd_block, 1); rd_wr_vf(drive, fdc_cmd_block, BIOS_DMA_VERIFY); } /* ** The low level 3.5 inch floppy format wants to know these params. ** For the funny format function. ** Have a look at hp_flop3.c FDC_FORMAT_TRACK bit. */ LOCAL int f_cyl, f_head, f_sector, f_N; void GetFormatParams IFN4(int *, c, int *, h, int *, s, int *, n) { *c = f_cyl; *h = f_head; *s = f_sector; *n = f_N; } void fl_disk_format IFN1(int, drive) { /* * Format the diskette in "drive" * * Register inputs: * DH head number * CH track number * CL sector number * AL number of sectors * ES:BX address fields for the track * Register outputs: * AH diskette status * CF status flag */ FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; half_word motor_status; /* ** Set up format params so hp_flop3.c can find out what they are when ** the format is about to happen. ** cylinder, head, sector and Number of sectors. */ f_cyl = getCH(); f_head = getDH(); f_sector = getCL(); f_N = getAL(); /* * Establish the default format for the size of drive, unless * this has already been set up via previous calls to * diskette_io() */ translate_new(drive); fmt_init(drive); /* * A write operation */ sas_load(MOTOR_STATUS, &motor_status); motor_status |= MS_WRITE_OP; sas_store(MOTOR_STATUS, motor_status); /* * Don't proceed with the format if a DUAL card is installed * and the media has been changed */ if ((! high_density(drive)) || (med_change(drive) == SUCCESS)) { /* * Send the specify command to the FDC, and establish * the data rate if necessary */ send_spec(); if (chk_lastrate(drive) != FAILURE) send_rate(drive); /* * Prepare for DMA transfer that will do the format */ if (fmtdma_set() != FAILURE) { /* * Seek to the required track, and initialise * the FDC for the format */ put_c3_cmd(fdc_cmd_block, FDC_FORMAT_TRACK); put_c3_pad1(fdc_cmd_block, 0); put_c3_MFM(fdc_cmd_block, 1); put_c3_pad(fdc_cmd_block, 0); nec_init(drive, fdc_cmd_block); /* * Send the remainder of the format * parameters to the FDC */ nec_output(get_parm(DT_N_FORMAT)); nec_output(get_parm(DT_LAST_SECTOR)); nec_output(get_parm(DT_FORMAT_GAP_LENGTH)); nec_output(get_parm(DT_FORMAT_FILL_BYTE)); /* * Complete the FDC command */ (void )nec_term(); } } /* * Return without setting sectors transferred */ translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); } void fl_fnc_err IFN1(int, drive) { /* * This routine sets the diskette status when an illegal * function number or drive number is passed to diskette_io(); * "drive" is not significant * * Register inputs: * none * Register outputs: * AH diskette status * CF status flag */ UNUSED(drive); setAH(FS_BAD_COMMAND); sas_store(FLOPPY_STATUS, FS_BAD_COMMAND); setCF(1); } void fl_disk_parms IFN1(int, drive) { /* * Return the drive parameters * * Register inputs: * none * Register outputs: * CL sectors/track * CH maximum track number * BL drive type * BH 0 * DL number of diskette drives * DH maximum head number * ES:DI parameter table address * AX 0 * CF 0 */ half_word disk_state, drive_type; half_word parameter; word segment, offset; EQUIPMENT_WORD equip_flag; /* * Set up number of diskette drives attached */ translate_new(drive); setBX(0); sas_loadw(EQUIP_FLAG, &equip_flag.all); if (equip_flag.bits.diskette_present == 0) setDL(0); else setDL(equip_flag.bits.max_diskette + 1); /* * Set up drive dependent parameters */ #ifdef NTVDM if ( (equip_flag.bits.diskette_present == 1) && (drive < number_of_floppy)) #else if ( (equip_flag.bits.diskette_present == 1) && (drive < MAX_FLOPPY)) #endif /* NTVDM */ { if (! high_density(drive)) { /* * Set up sectors/track, drive type and * maximum track number */ setCL(9); sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & DC_80_TRACK) == 0) { drive_type = GFI_DRIVE_TYPE_360; setCH(MAXIMUM_TRACK_ON_360); } else { drive_type = GFI_DRIVE_TYPE_720; setCH(MAXIMUM_TRACK_ON_720); } setBX(drive_type); /* * Set up maximum head and parameter table * address, return OK */ setDH(1); (void )dr_type_check(drive_type, &segment, &offset); setDI(offset); setES(segment); translate_old(drive); setAX(0); setCF(0); return; } /* * Dual card present: set maximum head number and * try to establish a parameter table entry for * the drive */ setDH(1); if ( cmos_type(drive, &drive_type) != FAILURE && drive_type != GFI_DRIVE_TYPE_NULL && dr_type_check(drive_type, &segment, &offset) != FAILURE) { /* * Set parameters from parameter table */ setBL(drive_type); sas_load(effective_addr(segment,offset) + DT_LAST_SECTOR, ¶meter); setCL(parameter); sas_load(effective_addr(segment,offset) + DT_MAXIMUM_TRACK, ¶meter); setCH(parameter); setDI(offset); setES(segment); translate_old(drive); setAX(0); setCF(0); return; } /* * Establish drive type from status */ sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & FS_MEDIA_DET) != 0) { switch(disk_state & RS_MASK) { case RS_250: if ((disk_state & DC_80_TRACK) == 0) drive_type = GFI_DRIVE_TYPE_360; else drive_type = GFI_DRIVE_TYPE_144; break; case RS_300: drive_type = GFI_DRIVE_TYPE_12; break; case RS_1000: drive_type = GFI_DRIVE_TYPE_288; break; default: drive_type = GFI_DRIVE_TYPE_144; break; } (void )dr_type_check(drive_type, &segment, &offset); /* * Set parameters from parameter table */ setBL(drive_type); sas_load(effective_addr(segment,offset) + DT_LAST_SECTOR, ¶meter); setCL(parameter); sas_load(effective_addr(segment,offset) + DT_MAXIMUM_TRACK, ¶meter); setCH(parameter); setDI(offset); setES(segment); translate_old(drive); setAX(0); setCF(0); return; } } /* * Arrive here if "drive" is invalid or if its type * could not be determined */ setCX(0); setDH(0); setDI(0); setES(0); translate_old(drive); setAX(0); setCF(0); return; } void fl_disk_type IFN1(int, drive) { /* * Return the diskette drive type for "drive" * * Register inputs: * none * Register outputs: * AH drive type * CF 0 */ half_word disk_state; EQUIPMENT_WORD equip_flag; note_trace1( GFI_VERBOSE, "floppy:fl_disk_type():drive=%x:", drive ); if (high_density(drive)) { /* * Dual card present: set type if "drive" valid */ note_trace0( GFI_VERBOSE, "floppy:fl_disk_type():DUAL CARD" ); translate_new(drive); sas_load(FDD_STATUS+drive, &disk_state); if (disk_state == 0) setAH(DRIVE_IQ_UNKNOWN); else if ((disk_state & DC_80_TRACK) != 0) setAH(DRIVE_IQ_CHANGE_LINE); else setAH(DRIVE_IQ_NO_CHANGE_LINE); translate_old(drive); } else { note_trace0( GFI_VERBOSE,"floppy:fl_disk_type():NO DUAL CARD" ); /* * Set no change line support if "drive" valid */ sas_loadw(EQUIP_FLAG, &equip_flag.all); if (equip_flag.bits.diskette_present) setAH(DRIVE_IQ_NO_CHANGE_LINE); else setAH(DRIVE_IQ_UNKNOWN); } setCF(0); #ifndef PROD switch( getAH() ){ case DRIVE_IQ_UNKNOWN: note_trace0( GFI_VERBOSE, "unknown drive\n" ); break; case DRIVE_IQ_CHANGE_LINE: note_trace0( GFI_VERBOSE, "change line\n" ); break; case DRIVE_IQ_NO_CHANGE_LINE: note_trace0( GFI_VERBOSE, "no change line\n" ); break; default: note_trace0( GFI_VERBOSE, "bad AH return value\n" ); break; } #endif } void fl_disk_change IFN1(int, drive) { /* * Return the state of the disk change line for "drive" * * Register inputs: * none * Register outputs: * AH diskette status * CF status flag */ half_word disk_state, diskette_status; note_trace1( GFI_VERBOSE, "floppy:fl_disk_change(%d)", drive); if (! high_density(drive)) { /* * Only dual card supports change line, so call * the error function */ fl_fnc_err(drive); } else { translate_new(drive); sas_load(FDD_STATUS+drive, &disk_state); if (disk_state != 0) { /* * If "drive" is high density, check for * a disk change */ if ( ((disk_state & DC_80_TRACK) == 0) || (read_dskchng(drive) != SUCCESS)) { sas_load(FLOPPY_STATUS, &diskette_status); diskette_status = FS_MEDIA_CHANGE; sas_store(FLOPPY_STATUS, diskette_status); } } else { /* * "drive" is invalid */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_TIME_OUT; sas_store(FLOPPY_STATUS, diskette_status); } /* * Return without setting sectors transferred */ translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); } } void fl_format_set IFN1(int, drive) { /* * Establish type of media to be used for subsequent format * operation * * Register inputs: * AL media type * Register outputs: * AH diskette status * CF status flag */ half_word media_type = getAL(), disk_state, diskette_status; translate_new(drive); sas_load(FDD_STATUS+drive, &disk_state); disk_state &= ~(FS_MEDIA_DET | FS_DOUBLE_STEP | RS_MASK); sas_store(FDD_STATUS+drive, disk_state); if (media_type == MEDIA_TYPE_360_IN_360) { /* * Need to set low data rate */ disk_state |= (FS_MEDIA_DET | RS_250); sas_store(FDD_STATUS+drive, disk_state); } else { if (high_density(drive)) { /* * Need to check for media change */ (void )med_change(drive); sas_load(FLOPPY_STATUS, &diskette_status); if (diskette_status == FS_TIME_OUT) { /* * Return without setting sectors * transferred */ translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); return; } } switch(media_type) { case MEDIA_TYPE_360_IN_12: /* * Need to set low density and double step */ disk_state |= (FS_MEDIA_DET | FS_DOUBLE_STEP | RS_300); sas_store(FDD_STATUS+drive, disk_state); break; case MEDIA_TYPE_12_IN_12: /* * Need to set high density */ disk_state |= (FS_MEDIA_DET | RS_500); sas_store(FDD_STATUS+drive, disk_state); break; case MEDIA_TYPE_720_IN_720: /* * Set 300kbs data rate if multi-format * supported on drive, otherwise 250kbs */ if ( ((disk_state & DC_DETERMINED) != 0) && ((disk_state & DC_MULTI_RATE) != 0)) disk_state |= (FS_MEDIA_DET | RS_300); else disk_state |= (FS_MEDIA_DET | RS_250); sas_store(FDD_STATUS+drive, disk_state); break; default: /* * Unsupported media type */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status = FS_BAD_COMMAND; sas_store(FLOPPY_STATUS, diskette_status); break; } } /* * Return without setting sectors transferred */ translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); } void fl_set_media IFN1(int, drive) { /* * Set the type of media and data rate to be used in the * subsequent format operation * * Register inputs: * CH maximum track number * CL sectors/track * Register outputs: * ES:DI parameter table address * AH diskette status * CF status flag */ half_word max_track = getCH(), sectors = getCL(); half_word dt_max_track, dt_sectors; half_word drive_type, diskette_status, disk_state, data_rate; half_word dt_drive_type; half_word lastrate; word md_segment, md_offset; #ifdef NTVDM sys_addr dt_start = dr_type_addr; sys_addr dt_end = dr_type_addr + DR_CNT * DR_SIZE_OF_ENTRY; #else sys_addr dt_start = DR_TYPE_ADDR; sys_addr dt_end = DR_TYPE_ADDR + DR_CNT * DR_SIZE_OF_ENTRY; #endif sys_addr md_table; translate_new(drive); /* * Check for a media change on drives with a change line */ sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & DC_80_TRACK) != 0) { (void )med_change(drive); sas_load(FLOPPY_STATUS, &diskette_status); if (diskette_status == FS_TIME_OUT) { /* * Return without setting sectors * transferred */ translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); return; } sas_store(FLOPPY_STATUS, FS_OK); } /* * Search the parameter table for the correct entry */ if (cmos_type(drive, &drive_type) == FAILURE) { sas_store(FLOPPY_STATUS, FS_MEDIA_NOT_FOUND); } else if (drive_type != 0) { if (dr_type_check(drive_type, &md_segment, &md_offset) == FAILURE) { sas_store(FLOPPY_STATUS, FS_MEDIA_NOT_FOUND); } else { /* * Try to find the parameter table entry which * has both the right drive type and matches * the max sector and max track numbers */ while (dt_start < dt_end) { sas_load(dt_start, &dt_drive_type); if ((dt_drive_type & ~DR_WRONG_MEDIA) == drive_type) { sas_loadw(dt_start+sizeof(half_word), &md_offset); md_table = effective_addr(md_segment, md_offset); sas_load(md_table + DT_LAST_SECTOR, &dt_sectors); sas_load(md_table + DT_MAXIMUM_TRACK, &dt_max_track); if (dt_sectors == sectors && dt_max_track == max_track) break; } dt_start += DR_SIZE_OF_ENTRY; } if (dt_start >= dt_end) { /* * Failed to find an entry */ sas_store(FLOPPY_STATUS, FS_MEDIA_NOT_FOUND); } else { /* * Update disk state and store * parameter table address */ sas_load(md_table+DT_DATA_TRANS_RATE, &data_rate); if (data_rate == RS_300) data_rate |= FS_DOUBLE_STEP; data_rate |= FS_MEDIA_DET; sas_load(FDD_STATUS+drive, &disk_state); /* CHECK - IN CASE OF 2 DRIVES * check last rate against the new data rate set * in the status byte. If they differ * set BIOS RATE STATUS byte to reflect old rate status * for this drive as it may have been altered by an * access to the other drive. This may result in a call * to send_rate not being performed because the old * rate status (possibly for the other drive) matching the * new data rate for this drive, when actually the last rate * attempted for this drive was different. Thus the * controller for this drive is at an old rate (for low * density say) and we are assuming it has been previously * set to the updated (high) state when it has not! * In all this will ensure the updated data rate being sent * for the drive concerned ! */ if ((disk_state & RS_MASK) != (data_rate & RS_MASK)) { sas_load(RATE_STATUS, &lastrate); /*LINTIGNORE*/ lastrate &= ~RS_MASK; lastrate |= disk_state & RS_MASK; sas_store(RATE_STATUS, lastrate); } disk_state &= ~(FS_MEDIA_DET | FS_DOUBLE_STEP | RS_MASK); disk_state |= data_rate; sas_store(FDD_STATUS+drive, disk_state); setES(md_segment); setDI(md_offset); } } } /* * Return without setting sectors transferred */ translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); } LOCAL dr_type_check IFN3(half_word, drive_type, word *, seg_ptr, word *, off_ptr) { /* * Return the address of the first parameter table entry * that matches "drive_type" */ half_word dt_drive_type; sys_addr dt_start, dt_end; #ifdef NTVDM *seg_ptr = dr_type_seg; dt_start = dr_type_addr; dt_end = dr_type_addr + DR_CNT * DR_SIZE_OF_ENTRY; #else *seg_ptr = DISKETTE_IO_1_SEGMENT; dt_start = DR_TYPE_ADDR; dt_end = DR_TYPE_ADDR + DR_CNT * DR_SIZE_OF_ENTRY; #endif /* NTVDM */ while (dt_start < dt_end) { sas_load(dt_start, &dt_drive_type); if (dt_drive_type == drive_type) { sas_loadw(dt_start+sizeof(half_word), off_ptr); return(SUCCESS); } dt_start += DR_SIZE_OF_ENTRY; } return(FAILURE); } LOCAL void send_spec IFN0() { /* * Send a specify command to the FDC using data from the * parameter table pointed to by @DISK_POINTER */ nec_output(FDC_SPECIFY); nec_output(get_parm(DT_SPECIFY1)); nec_output(get_parm(DT_SPECIFY2)); } LOCAL void send_spec_md IFN2(word, segment, word, offset) { /* * Send a specify command to the FDC using data from the * parameter table pointed to by "segment" and "offset" */ half_word parameter; nec_output(FDC_SPECIFY); sas_load(effective_addr(segment, offset+DT_SPECIFY1), ¶meter); nec_output(parameter); sas_load(effective_addr(segment, offset+DT_SPECIFY2), ¶meter); nec_output(parameter); } LOCAL void translate_new IFN1(int, drive) { /* * Translates diskette state locations from compatible * mode to new architecture */ half_word hf_cntrl, disk_state; sas_load(DRIVE_CAPABILITY, &hf_cntrl); #ifdef NTVDM if (high_density(drive) && (drive < number_of_floppy)) #else if (high_density(drive) && (drive < MAX_FLOPPY)) #endif /* NTVDM */ { sas_load(FDD_STATUS+drive, &disk_state); if (disk_state == 0) { /* * Try to establish drive capability */ drive_detect(drive); } else { /* * Copy drive capability bits */ hf_cntrl >>= (drive << 2); hf_cntrl &= DC_MASK; disk_state &= ~DC_MASK; disk_state |= hf_cntrl; sas_store(FDD_STATUS+drive, disk_state); } } } void translate_old IFN1(int, drive) { /* * Translates diskette state locations from new * architecture to compatible mode */ half_word hf_cntrl, disk_state, mode, drive_type; int shift_count = drive << 2; sas_load(DRIVE_CAPABILITY, &hf_cntrl); sas_load(FDD_STATUS+drive, &disk_state); #ifdef NTVDM if (high_density(drive) && (drive < number_of_floppy) && (disk_state != 0)) #else if (high_density(drive) && (drive < MAX_FLOPPY) && (disk_state != 0)) #endif /* NTVDM */ { /* * Copy drive capability bits */ if ((hf_cntrl & (DC_MULTI_RATE << shift_count)) == 0) { hf_cntrl &= ~(DC_MASK << shift_count); hf_cntrl |= (disk_state & DC_MASK) << shift_count; sas_store(DRIVE_CAPABILITY, hf_cntrl); } /* * Copy media type bits */ switch (disk_state & RS_MASK) { case RS_500: /* * Drive should be a 1.2M */ if ( (cmos_type(drive, &drive_type) != FAILURE) && (drive_type == GFI_DRIVE_TYPE_12)) { mode = FS_12_IN_12; if ((disk_state & FS_MEDIA_DET) != 0) mode = media_determined(mode); } else { mode = FS_DRIVE_SICK; } break; case RS_300: /* * Should be double-stepping for 360K floppy * in 1.2M drive */ mode = FS_360_IN_12; if ((disk_state & FS_DOUBLE_STEP) != 0) { if ((disk_state & FS_MEDIA_DET) != 0) mode = media_determined(mode); } else { mode = FS_DRIVE_SICK; } break; case RS_250: /* * Should be 360K floppy in 360K drive, * ie 250kbs and 40 track */ if ((disk_state & DC_80_TRACK) == 0) { mode = FS_360_IN_360; if ((disk_state & FS_MEDIA_DET) != 0) mode = media_determined(mode); } else { mode = FS_DRIVE_SICK; } break; case RS_1000: /* * Drive should be a 2.88M */ if ( (cmos_type(drive, &drive_type) != FAILURE) && (drive_type == GFI_DRIVE_TYPE_288)) { mode = FS_288_IN_288; if ((disk_state & FS_MEDIA_DET) != 0) mode = media_determined(mode); } else { mode = FS_DRIVE_SICK; } break; default: /* * Weird data rate */ mode = FS_DRIVE_SICK; break; } disk_state &= ~DC_MASK; disk_state |= mode; sas_store(FDD_STATUS+drive, disk_state); } } LOCAL void rd_wr_vf IFN3(int, drive, FDC_CMD_BLOCK *, fcbp, half_word, dma_type) { /* * Common read, write and verify; main loop for data rate * retries */ half_word data_rate, dt_data_rate, drive_type, dt_drive_type; half_word disk_state; sys_addr dt_start, dt_end; int sectors_transferred; word md_segment, md_offset; /* * Establish initial data rate, then loop through each * possible data rate */ translate_new(drive); setup_state(drive); while ((! high_density(drive)) || med_change(drive) == SUCCESS) { sas_load(FDD_STATUS+drive, &disk_state); data_rate = disk_state & RS_MASK; if (cmos_type(drive, &drive_type) != FAILURE) { /* * Check CMOS value against what is really * known about the drive */ /* * The original code here had a very bad case of "Bad-C" * if-if-else troubles, but replacing the code with * the switch statement originally intended breaks * 5.25" floppies. I have removed the redundant bits * of the code, but BEWARE - there is a another * fault somewhere to cancel out this one! * William Roberts - 9/2/93 */ if (drive_type == GFI_DRIVE_TYPE_360) { if ((disk_state & DC_80_TRACK) != 0) { drive_type = GFI_DRIVE_TYPE_12; } /* else if (drive_type == GFI_DRIVE_TYPE_12) ... */ } /* ** dr_type_check() looks for the first matching drive ** value in the small table and returns a ** pointer to the coresponding entry in the big ** parameter table. ** The segment is used later on but the offset is ** determined by a subsequent search of the table below. ** These table live in ROM (see bios2.rom) fe00:c80 */ if ( (drive_type != GFI_DRIVE_TYPE_NULL) && (dr_type_check(drive_type, &md_segment, &md_offset) != FAILURE)) { /* * Try to find parameter table entry with * right drive type and current data rate */ #ifdef NTVDM dt_start = dr_type_addr; dt_end = dr_type_addr + DR_CNT * DR_SIZE_OF_ENTRY; #else dt_start = DR_TYPE_ADDR; dt_end = DR_TYPE_ADDR + DR_CNT * DR_SIZE_OF_ENTRY; #endif /* NTVDM */ while (dt_start < dt_end) { /* ** get drive type from table */ sas_load(dt_start, &dt_drive_type); if ((dt_drive_type & ~DR_WRONG_MEDIA) == drive_type) { /* ** get data rate from table */ sas_loadw(dt_start+sizeof(half_word), &md_offset); sas_load(effective_addr(md_segment, md_offset) + DT_DATA_TRANS_RATE, &dt_data_rate); /* ** if table rate matches that ** selected by setup_state() ** then try current table entry ** parameters. */ if (data_rate == dt_data_rate) break; } dt_start += DR_SIZE_OF_ENTRY; } if (dt_start >= dt_end) { /* * Assume media matches drive */ #ifdef NTVDM md_segment = dr_type_seg; md_offset = dr_type_off; if ((disk_state & DC_80_TRACK) == 0) md_offset += MD_TBL1_OFFSET; else md_offset += MD_TBL3_OFFSET; #else md_segment = DISKETTE_IO_1_SEGMENT; if ((disk_state & DC_80_TRACK) == 0) md_offset = MD_TBL1_OFFSET; else md_offset = MD_TBL3_OFFSET; #endif /* NTVDM */ } } else { /* * Assume media matches drive */ #ifdef NTVDM md_segment = dr_type_seg; md_offset = dr_type_off; if ((disk_state & DC_80_TRACK) == 0) md_offset += MD_TBL1_OFFSET; else md_offset += MD_TBL3_OFFSET; #else md_segment = DISKETTE_IO_1_SEGMENT; if ((disk_state & DC_80_TRACK) == 0) md_offset = MD_TBL1_OFFSET; else md_offset = MD_TBL3_OFFSET; #endif /* NTVDM */ } } else { /* * Assume media matches drive */ #ifdef NTVDM md_segment = dr_type_seg; md_offset = dr_type_off; if ((disk_state & DC_80_TRACK) == 0) md_offset += MD_TBL1_OFFSET; else md_offset += MD_TBL3_OFFSET; #else md_segment = DISKETTE_IO_1_SEGMENT; if ((disk_state & DC_80_TRACK) == 0) md_offset = MD_TBL1_OFFSET; else md_offset = MD_TBL3_OFFSET; #endif /* NTVDM */ } /* * Send a specify command to the FDC; change the * rate if it has been updated */ send_spec_md(md_segment, md_offset); if (chk_lastrate(drive) != FAILURE) send_rate(drive); /* * Decide whether double stepping is required for * the data rate currently being tried */ if (setup_dbl(drive) != FAILURE) { if (dma_setup(dma_type) == FAILURE) { translate_old(drive); setup_end(IGNORE_SECTORS_TRANSFERRED); return; } /* * Attempt the transfer */ nec_init(drive, fcbp); rwv_com(md_segment, md_offset); (void )nec_term(); } /* ** Will select next data rate in range specified by ** setup_state() and try again. ** When there are no more rates give up. */ if (retry(drive) == SUCCESS) break; } /* * Determine the current drive state and return, setting * the number of sectors actually transferred */ dstate(drive); sectors_transferred = num_trans(); translate_old(drive); setup_end(sectors_transferred); } LOCAL void setup_state IFN1(int, drive) { half_word drive_type; /* Floppy unit type specified by CMOS */ /* * Initialises start and end data rates */ half_word disk_state, start_rate, end_rate, lastrate; if (high_density(drive)) { sas_load(FDD_STATUS+drive, &disk_state); #ifndef NTVDM if ((disk_state & FS_MEDIA_DET) == 0) { /* * Set up first and last data rates to * try */ if ( ((disk_state & DC_DETERMINED) != 0) && ((disk_state & DC_MULTI_RATE) == 0) ) { /* not a multi-rate drive */ start_rate = end_rate = RS_250; } else { /* multi-rate drive */ /* * The real BIOS always sets up start_rate=500 and end_rate=300 * If we attempt this then some bug (not yet found) will cause the following * sequence to fail (5.25") low density read followed by high density read. * This gives rate transitions 500 -> 250 -> 300 -> 500 ... * Read the drive type from CMOS and adjust the start and end rates to match. * The CMOS drive type is set up during cmos_post() by calling config_inquire(). */ if( cmos_type( drive, &drive_type ) != FAILURE ){ switch( drive_type ){ case GFI_DRIVE_TYPE_360: case GFI_DRIVE_TYPE_12: start_rate = RS_300; /* different to Real BIOS */ end_rate = RS_500; break; case GFI_DRIVE_TYPE_720: case GFI_DRIVE_TYPE_144: start_rate = RS_500; /* same as Real BIOS */ end_rate = RS_300; break; /* * We don't know what the real BIOS does here. These values work * fine. Any code in rd_wr_vf that gets confused will drop out to * default high density values if neither of the following two * rates work. */ case GFI_DRIVE_TYPE_288: start_rate = RS_1000; end_rate = RS_300; break; default: always_trace1( "setup_state(): Bad Drive from CMOS:%x", drive_type ); break; } }else{ always_trace0( "setup_state(): CMOS read failure: Drive Type" ); } } #else /* NTVDM */ if ((disk_state & FS_MEDIA_DET) == 0) { if( cmos_type( drive, &drive_type ) != FAILURE ){ switch( drive_type ){ case GFI_DRIVE_TYPE_360: case GFI_DRIVE_TYPE_720: start_rate = end_rate = RS_250; break; case GFI_DRIVE_TYPE_12: start_rate = RS_300; /* different to Real BIOS */ end_rate = RS_500; break; case GFI_DRIVE_TYPE_144: start_rate = RS_500; /* same as Real BIOS */ end_rate = RS_250; break; /* * We don't know what the real BIOS does here. These values work * fine. Any code in rd_wr_vf that gets confused will drop out to * default high density values if neither of the following two * rates work. */ case GFI_DRIVE_TYPE_288: start_rate = RS_1000; end_rate = RS_300; break; default: always_trace1( "setup_state(): Bad Drive from CMOS:%x", drive_type ); break; } }else{ always_trace0( "setup_state(): CMOS read failure: Drive Type" ); } #endif /* NTVDM */ /* * Set up disk state with current data * rate; clear double stepping, which * may be re-established by a call to * setup_dbl() */ disk_state &= ~(RS_MASK | FS_DOUBLE_STEP); disk_state |= start_rate; sas_store(FDD_STATUS+drive, disk_state); /* * Store final rate to try in rate data */ sas_load(RATE_STATUS, &lastrate); lastrate &= ~(RS_MASK >> 4); lastrate |= (end_rate >> 4); sas_store(RATE_STATUS, lastrate); } } } LOCAL void fmt_init IFN1(int, drive) { /* * If the media type has not already been set up, establish * the default media type for the drive type */ half_word disk_state, drive_type; if (high_density(drive)) { sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & FS_MEDIA_DET) == 0) { if ( (cmos_type(drive, &drive_type) != FAILURE) && (drive_type != 0)) { disk_state &= ~(FS_MEDIA_DET | FS_DOUBLE_STEP | RS_MASK); switch(drive_type) { case GFI_DRIVE_TYPE_360: disk_state |= (FS_MEDIA_DET | RS_250); break; case GFI_DRIVE_TYPE_12: case GFI_DRIVE_TYPE_144: disk_state |= (FS_MEDIA_DET | RS_500); break; case GFI_DRIVE_TYPE_288: disk_state |= (FS_MEDIA_DET | RS_1000); break; case GFI_DRIVE_TYPE_720: if ((disk_state & (DC_DETERMINED|DC_MULTI_RATE)) == (DC_DETERMINED|DC_MULTI_RATE)) disk_state |= (FS_MEDIA_DET | RS_300); else disk_state |= (FS_MEDIA_DET | RS_250); break; default: disk_state = 0; break; } } else { disk_state = 0; } sas_store(FDD_STATUS+drive, disk_state); } } } LOCAL med_change IFN1(int, drive) { /* * Checks for media change, resets media change, * checks media change again */ half_word disk_state, motor_status; if (high_density(drive)) { if (read_dskchng(drive) == SUCCESS) return(SUCCESS); /* * Media has been changed - set media state to * undetermined */ sas_load(FDD_STATUS+drive, &disk_state); disk_state &= ~FS_MEDIA_DET; sas_store(FDD_STATUS+drive, disk_state); /* * Start up the motor, since opening the * door may have turned the motor off */ sas_load(MOTOR_STATUS, &motor_status); motor_status &= ~(1 << drive); sas_store(MOTOR_STATUS, motor_status); motor_on(drive); /* * This sequence of seeks should reset the * disk change line, if the door is left * alone */ fl_disk_reset(drive); (void )seek(drive, 1); (void )seek(drive, 0); /* * If disk change line still active, assume drive * is empty or door has been left open */ if (read_dskchng(drive) == SUCCESS) sas_store(FLOPPY_STATUS, FS_MEDIA_CHANGE); else sas_store(FLOPPY_STATUS, FS_TIME_OUT); } return(FAILURE); } LOCAL void send_rate IFN1(int, drive) { /* * Update the data rate for "drive" */ half_word lastrate, disk_state; if (high_density(drive)) { /* * Update the adapter data rate */ sas_load(RATE_STATUS, &lastrate); lastrate &= ~RS_MASK; sas_load(FDD_STATUS+drive, &disk_state); disk_state &= RS_MASK; lastrate |= disk_state; sas_store(RATE_STATUS, lastrate); /* * Establish the new data rate for the drive via * the floppy adapter */ outb(DISKETTE_DCR_REG, (disk_state >> 6)); } } LOCAL chk_lastrate IFN1(int, drive) { /* * Reply whether the adapter data rate is different to * the disk state data rate */ half_word lastrate, disk_state; if (rate_unitialised) { rate_unitialised = FALSE; return(SUCCESS); } sas_load(RATE_STATUS, &lastrate); sas_load(FDD_STATUS+drive, &disk_state); return((lastrate & RS_MASK) != (disk_state & RS_MASK) ? SUCCESS : FAILURE); } LOCAL dma_setup IFN1(half_word, dma_mode) { /* * This routine sets up the DMA for read/write/verify * operations */ DMA_ADDRESS dma_address; reg byte_count; /* * Disable interrupts */ setIF(0); /* * Set up the DMA adapter's internal state and mode */ outb(DMA_CLEAR_FLIP_FLOP, dma_mode); outb(DMA_WRITE_MODE_REG, dma_mode); /* * Output the address to the DMA adapter as a page address * and 16 bit offset */ if (dma_mode == BIOS_DMA_VERIFY) dma_address.all = 0; else dma_address.all = effective_addr(getES(), getBX()); outb(DMA_CH2_ADDRESS, dma_address.parts.low); outb(DMA_CH2_ADDRESS, dma_address.parts.high); outb(DMA_FLA_PAGE_REG, dma_address.parts.page); /* * Calculate the number of bytes to be transferred from the * number of sectors, and the sector size. Subtract one * because the DMA count must wrap to 0xFFFF before it * stops */ byte_count.X = ((unsigned int)getAL() << (7 + get_parm(DT_N_FORMAT))) - 1; outb(DMA_CH2_COUNT, byte_count.byte.low); outb(DMA_CH2_COUNT, byte_count.byte.high); /* * Enable interrupts */ setIF(1); /* * Set up diskette channel for the operation, checking * for wrapping of the bottom 16 bits of the address */ outb(DMA_WRITE_ONE_MASK_BIT, DMA_DISKETTE_CHANNEL); if (((long)dma_address.words.low + (long)byte_count.X) > 0xffff) { sas_store(FLOPPY_STATUS, FS_DMA_BOUNDARY); return(FAILURE); } return(SUCCESS); } LOCAL fmtdma_set IFN0() { /* * This routine sets up the DMA for format operations */ DMA_ADDRESS dma_address; reg byte_count; /* * Disable interrupts */ setIF(0); /* * Set up the DMA adapter's internal state and mode */ outb(DMA_CLEAR_FLIP_FLOP, BIOS_DMA_WRITE); outb(DMA_WRITE_MODE_REG, BIOS_DMA_WRITE); /* * Output the address to the DMA adapter as a page address * and 16 bit offset */ dma_address.all = effective_addr(getES(), getBX()); outb(DMA_CH2_ADDRESS, dma_address.parts.low); outb(DMA_CH2_ADDRESS, dma_address.parts.high); outb(DMA_FLA_PAGE_REG, dma_address.parts.page); /* * Calculate the number of bytes to be transferred from the * number of sectors per track, given that 4 bytes (C,H,R,N) * are needed to define each sector's address mark. Subtract * one because the DMA count must wrap to 0xFFFF before it * stops */ byte_count.X = ((unsigned int)get_parm(DT_LAST_SECTOR) << 2) - 1; outb(DMA_CH2_COUNT, byte_count.byte.low); outb(DMA_CH2_COUNT, byte_count.byte.high); /* * Enable interrupts */ setIF(1); /* * Set up diskette channel for the operation, checking * for wrapping of the bottom 16 bits of the address */ #ifndef NTVDM /* we don't have to worry about this on NT */ outb(DMA_WRITE_ONE_MASK_BIT, DMA_DISKETTE_CHANNEL); if (((long)dma_address.words.low + (long)byte_count.X) > 0xffff) { sas_store(FLOPPY_STATUS, FS_DMA_BOUNDARY); return(FAILURE); } #endif return(SUCCESS); } LOCAL void nec_init IFN2(int, drive, FDC_CMD_BLOCK *, fcbp) { /* * This routine seeks to the requested track and * initialises the FDC for the read/write/verify * operation. */ motor_on(drive); if (seek(drive, (int)getCH()) != FAILURE) { nec_output(fcbp[0]); put_c2_head(fcbp, getDH()); put_c2_drive(fcbp, drive); put_c2_pad1(fcbp, 0); nec_output(fcbp[1]); } } LOCAL void rwv_com IFN2(word, md_segment, word, md_offset) { /* * This routine send read/write/verify parameters to the * FDC */ half_word md_gap; /* * Output track number, head number and sector number */ nec_output(getCH()); nec_output(getDH()); nec_output(getCL()); /* * Output bytes/sector and sectors/track */ nec_output(get_parm(DT_N_FORMAT)); nec_output(get_parm(DT_LAST_SECTOR)); /* * Output gap length */ sas_load(effective_addr(md_segment, md_offset)+DT_GAP_LENGTH, &md_gap); nec_output(md_gap); /* * Output data length */ nec_output(get_parm(DT_DTL)); } LOCAL nec_term IFN0() { /* * This routine waits for the operation then interprets * the results from the FDC */ half_word diskette_status; int wait_int_result; wait_int_result = wait_int(); if (results() != FAILURE && wait_int_result != FAILURE) { /* * Result phase completed */ if ((get_r0_ST0(fl_nec_status) & (ST0_INTERRUPT_CODE_0 | ST0_INTERRUPT_CODE_1)) != 0) { /* * Command did not terminate normally */ sas_load(FLOPPY_STATUS, &diskette_status); if ((get_r0_ST0(fl_nec_status) & ST0_INTERRUPT_CODE_0) == 0) { /* * Problem with the FDC */ diskette_status |= FS_FDC_ERROR; always_trace0("diskette_io: FDC error - emetic command"); } else { /* * Abnormal termination - set * diskette status up accordingly */ if (get_r0_ST1(fl_nec_status) & ST1_END_OF_CYLINDER) { diskette_status |= FS_SECTOR_NOT_FOUND; } else if (get_r0_ST1(fl_nec_status) & ST1_DATA_ERROR) { diskette_status |= FS_CRC_ERROR; } else if (get_r0_ST1(fl_nec_status) & ST1_OVERRUN) { diskette_status |= FS_DMA_ERROR; } else if (get_r0_ST1(fl_nec_status) & ST1_NO_DATA) { diskette_status |= FS_FDC_ERROR; /* Tim Sept 91, was FS_SECTOR_NOT_FOUND */ } else if (get_r0_ST1(fl_nec_status) & ST1_NOT_WRITEABLE) { diskette_status |= FS_WRITE_PROTECTED; } else if (get_r0_ST1(fl_nec_status) & ST1_MISSING_ADDRESS_MARK) { diskette_status |= FS_BAD_ADDRESS_MARK; } else { /* * Problem with the FDC */ diskette_status |= FS_TIME_OUT; /* Tim Sept 91, was FS_FDC_ERROR */ always_trace0("diskette_io: FDC error - perverted result"); } } sas_store(FLOPPY_STATUS, diskette_status); } } sas_load(FLOPPY_STATUS, &diskette_status); return((diskette_status == FS_OK) ? SUCCESS : FAILURE); } LOCAL void dstate IFN1(int, drive) { /* * Determine the drive state after a successful operation */ half_word diskette_status, disk_state, drive_type; if (high_density(drive)) { sas_load(FLOPPY_STATUS, &diskette_status); if (diskette_status == 0) { /* * Command successful, both media and drive * are now determined */ sas_load(FDD_STATUS+drive, &disk_state); disk_state |= FS_MEDIA_DET; if ((disk_state & DC_DETERMINED) == 0) { if ( ((disk_state & RS_MASK) == RS_250) && (cmos_type(drive, &drive_type) != FAILURE) && (drive_type != GFI_DRIVE_TYPE_144) && (drive_type != GFI_DRIVE_TYPE_288) ) { /* * No multi-format capability */ disk_state &= ~DC_MULTI_RATE; disk_state |= DC_DETERMINED; } else { /* * Multi-format capability */ disk_state |= (DC_DETERMINED | DC_MULTI_RATE); } } sas_store(FDD_STATUS+drive, disk_state); } } } LOCAL retry IFN1(int, drive) { /* * Determines whether a retry is necessary. If retry is * required then state information is updated for retry */ half_word diskette_status, disk_state, data_rate, lastrate; sas_load(FLOPPY_STATUS, &diskette_status); if (diskette_status != FS_OK && diskette_status != FS_TIME_OUT) { sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & FS_MEDIA_DET) == 0) { sas_load(RATE_STATUS, &lastrate); if ((data_rate = (disk_state & RS_MASK)) != ((lastrate << 4) & RS_MASK)) { /* * Last command failed, the media * is still unknown, and there are * more data rates to check, so set * up next data rate */ data_rate = next_rate(data_rate); /* * Reset state and go for retry */ disk_state &= ~(RS_MASK | FS_DOUBLE_STEP); disk_state |= data_rate; sas_store(FDD_STATUS+drive, disk_state); sas_store(FLOPPY_STATUS, FS_OK); return(FAILURE); } } } /* * Retry not worthwhile */ return(SUCCESS); } LOCAL num_trans IFN0() { /* * This routine calculates the number of sectors that * were actually transferred to/from the diskette */ half_word diskette_status; int sectors_per_track, sectors_transferred = 0; sas_load(FLOPPY_STATUS, &diskette_status); if (diskette_status == 0) { /* * Number of sectors = final sector - initial sector */ LOAD_RESULT_BLOCK; sectors_transferred = get_r0_sector(fl_nec_status) - getCL(); /* * Adjustments for spanning heads or tracks */ sectors_per_track = (int)get_parm(DT_LAST_SECTOR); LOAD_RESULT_BLOCK; if (get_r0_head(fl_nec_status) != getDH()) sectors_transferred += sectors_per_track; else if (get_r0_cyl(fl_nec_status) != getCH()) sectors_transferred += (sectors_per_track * 2); } return(sectors_transferred); } LOCAL void setup_end IFN1(int, sectors_transferred) { /* * Restore MOTOR_COUNT to parameter provided in table; * set return status values and sectors transferred, * where applicable */ half_word diskette_status; sas_store(MOTOR_COUNT, get_parm(DT_MOTOR_WAIT)); sas_load(FLOPPY_STATUS, &diskette_status); setAH(diskette_status); if (diskette_status != 0) { /* * Operation failed */ if (sectors_transferred != IGNORE_SECTORS_TRANSFERRED) setAL(0); setCF(1); } else { /* * Operation succeeded */ if (sectors_transferred != IGNORE_SECTORS_TRANSFERRED) setAL(sectors_transferred); setCF(0); } } LOCAL setup_dbl IFN1(int, drive) { /* * Check whether media requires to be double-stepped to * be read at the current data rate */ half_word disk_state; int track, max_track; if (high_density(drive)) { sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & FS_MEDIA_DET) == 0) { /* * First check track 0 to get out quickly if * the media is unformatted */ sas_store(SEEK_STATUS, 0); motor_on(drive); (void )seek(drive, 0); if (read_id(drive, 0) != FAILURE) { /* * Try reading ids from cylinder 2 to * the last cylinder on both heads. If * the putative track number disagrees * with what is on the disk, then * double stepping is required */ if ((disk_state & DC_80_TRACK) == 0) max_track = 0x50; else max_track = 0xa0; for (track = 4; track < max_track; track++) { /* ensure motor stays on */ sas_store(MOTOR_COUNT, MC_MAXIMUM); sas_store(FLOPPY_STATUS, FS_OK); (void )seek(drive, track/2); if (read_id(drive, track%2) == SUCCESS) { LOAD_RESULT_BLOCK; sas_store(FDD_TRACK+drive, get_r0_cyl(fl_nec_status)); if ((track/2) != get_r0_cyl(fl_nec_status)) { disk_state |= FS_DOUBLE_STEP; sas_store(FDD_STATUS+drive, disk_state); } return(SUCCESS); } } } return(FAILURE); } } return(SUCCESS); } LOCAL read_id IFN2(int, drive, int, head) { /* * Perform the read id function */ FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; put_c4_cmd(fdc_cmd_block, FDC_READ_ID); put_c4_pad1(fdc_cmd_block, 0); put_c4_MFM(fdc_cmd_block, 1); put_c4_pad(fdc_cmd_block, 0); nec_output(fdc_cmd_block[0]); put_c4_drive(fdc_cmd_block, drive); put_c4_head(fdc_cmd_block, head); put_c4_pad2(fdc_cmd_block, 0); nec_output(fdc_cmd_block[1]); return(nec_term()); } LOCAL cmos_type IFN2(int, drive, half_word *, type) { /* * Returns diskette type from the soft CMOS */ half_word cmos_byte; /* * Check the CMOS battery and checksum */ cmos_byte = cmos_read(CMOS_DIAG); if ((cmos_byte & (BAD_CKSUM|BAD_BAT)) != 0) return(FAILURE); /* * Read the CMOS diskette drive type byte and return * the nibble for the drive requested. The types for * drive 0 and 1 are given in the high and low nibbles * respectively. */ cmos_byte = cmos_read(CMOS_DISKETTE); if (drive == 0) cmos_byte >>= 4; *type = cmos_byte & 0xf; return(SUCCESS); } LOCAL half_word get_parm IFN1(int, index) { /* * Return the byte in the current diskette parameter table * offset by "index" */ half_word value; word segment, offset; sas_loadw(DISK_POINTER_ADDR, &offset); sas_loadw(DISK_POINTER_ADDR + 2, &segment); sas_load(effective_addr(segment, offset+index), &value); #ifndef PROD { char *parm_name = "Unknown???"; #define DT_PARM_NAME(x,y) case x: parm_name = y; break; switch (index) { DT_PARM_NAME(DT_SPECIFY1,"SPECIFY1"); DT_PARM_NAME(DT_SPECIFY2,"SPECIFY2"); DT_PARM_NAME(DT_MOTOR_WAIT,"MOTOR_WAIT"); DT_PARM_NAME(DT_N_FORMAT,"N_FORMAT"); DT_PARM_NAME(DT_LAST_SECTOR,"LAST_SECTOR"); DT_PARM_NAME(DT_GAP_LENGTH,"GAP_LENGTH"); DT_PARM_NAME(DT_DTL,"DTL"); DT_PARM_NAME(DT_FORMAT_GAP_LENGTH,"FORMAT_GAP_LENGTH"); DT_PARM_NAME(DT_FORMAT_FILL_BYTE,"FORMAT_FILL_BYTE"); DT_PARM_NAME(DT_HEAD_SETTLE,"HEAD_SETTLE"); DT_PARM_NAME(DT_MOTOR_START,"MOTOR_START"); DT_PARM_NAME(DT_MAXIMUM_TRACK,"MAXIMUM_TRACK"); DT_PARM_NAME(DT_DATA_TRANS_RATE,"DATA_TRANS_RATE"); } note_trace5(FLOPBIOS_VERBOSE, "diskette_io:get_parm(%04x:%04x+%02x) %s=%02x)", segment, offset, index, parm_name, value); } #endif /* PROD */ return(value); } LOCAL void motor_on IFN1(int, drive) { /* * Turn motor on and wait for motor start up time */ double_word time_to_wait; /* * If motor was previously off - wait for the start-up time */ if (turn_on(drive) != FAILURE) { /* * Notify OS that BIOS is about to wait for motor * start up */ #ifndef JOKER word savedAX, savedCX, savedDX, savedCS, savedIP; translate_old(drive); savedAX = getAX(); savedCS = getCS(); savedIP = getIP(); setAH(INT15_DEVICE_BUSY); setAL(INT15_DEVICE_FLOPPY_MOTOR); #ifdef NTVDM setCS(int15_seg); setIP(int15_off); #else setCS(RCPU_INT15_SEGMENT); setIP(RCPU_INT15_OFFSET); #endif /* NTVDM */ host_simulate(); setAX(savedAX); setCS(savedCS); setIP(savedIP); translate_new(drive); /* * Quit if operating system handled wait and motor * is still on */ if (getCF() && turn_on(drive) == FAILURE) return; #endif /* JOKER */ /* * Get time to wait in 1/8 second units - minimum * wait time 1 second */ if ((time_to_wait = get_parm(DT_MOTOR_START)) < WAIT_A_SECOND) time_to_wait = WAIT_A_SECOND; /* * Convert time to wait into microseconds */ time_to_wait *= 125000L; /* at this point the real BIOS sets CX,DX to time_to_wait; we don't actually need to wait at all, so request the minimum length wait */ #ifndef JOKER /* * Ask OS to do wait */ savedAX = getAX(); savedCX = getCX(); savedDX = getDX(); savedCS = getCS(); savedIP = getIP(); setAH(INT15_WAIT); setCX(0); setDX(1); #ifdef NTVDM setCS(int15_seg); setIP(int15_off); #else setCS(RCPU_INT15_SEGMENT); setIP(RCPU_INT15_OFFSET); #endif /* NTVDM */ host_simulate(); setAX(savedAX); setCX(savedCX); setDX(savedDX); setCS(savedCS); setIP(savedIP); /* * Quit if wait succeeded */ if (!getCF()) return; #endif /* JOKER */ /* * Need to do fixed wait locally */ waitf(time_to_wait); } } LOCAL turn_on IFN1(int, drive) { /* * Turn motor on and return wait state */ half_word motor_status, drive_select_desired, motor_on_desired; half_word drive_select, status_desired, old_motor_on, new_motor_on; half_word diskette_dor_reg; /* * Disable interrupts */ setIF(0); /* * Make sure the motor stays on as long as possible */ sas_store(MOTOR_COUNT, MC_MAXIMUM); /* * Get existing and desired drive select and motor on */ sas_load(MOTOR_STATUS, &motor_status); drive_select = motor_status & MS_DRIVE_SELECT_MASK; drive_select_desired = (drive << 4); motor_on_desired = (1 << drive); if ( (drive_select != drive_select_desired) || ((motor_on_desired & motor_status) == 0)) { /* * Store desired motor status */ status_desired = motor_on_desired | drive_select_desired; old_motor_on = motor_status & MS_MOTOR_ON_MASK; motor_status &= ~MS_DRIVE_SELECT_MASK; motor_status |= status_desired; sas_store(MOTOR_STATUS, motor_status); /* * Switch on motor of selected drive via a write * to the floppy adapter's Digital Output Register */ new_motor_on = motor_status & MS_MOTOR_ON_MASK; setIF(1); diskette_dor_reg = motor_status << 4; diskette_dor_reg |= (motor_status & MS_DRIVE_SELECT_MASK) >> 4; diskette_dor_reg |= (DOR_INTERRUPTS | DOR_RESET); outb(DISKETTE_DOR_REG, diskette_dor_reg); /* * Flag success only if the motor was switched on, * and not just reselected */ if (new_motor_on != old_motor_on) return(SUCCESS); } /* * Enable interrupts */ setIF(1); return(FAILURE); } LOCAL void hd_wait IFN1(int, drive) { /* * Wait for head settle time */ half_word motor_status, disk_state; word time_to_wait; #ifndef JOKER word savedAX, savedCX, savedDX, savedCS, savedIP; #endif /* * Get head settle time; for write operations, the minimum * head settle times may need to be enforced */ time_to_wait = get_parm(DT_HEAD_SETTLE); sas_load(MOTOR_STATUS, &motor_status); if ((motor_status & MS_WRITE_OP) != 0) { if (time_to_wait == 0) { /* * Use minimum wait times according to the * media type */ sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & RS_MASK) == RS_250) time_to_wait = HEAD_SETTLE_360; else time_to_wait = HEAD_SETTLE_12; } } else if (time_to_wait == 0) return; /* * Convert time to wait into microseconds */ time_to_wait *= 1000; /* at this point the real BIOS sets CX,DX to time_to_wait; we don't actually need to wait at all, so request a zero length wait */ #ifndef JOKER /* * Ask OS to do wait */ savedAX = getAX(); savedCX = getCX(); savedDX = getDX(); savedCS = getCS(); savedIP = getIP(); setAH(INT15_WAIT); setCX(0); setDX(1); #ifdef NTVDM setCS(int15_seg); setIP(int15_off); #else setCS(RCPU_INT15_SEGMENT); setIP(RCPU_INT15_OFFSET); #endif /* NTVDM */ host_simulate(); setAX(savedAX); setCX(savedCX); setDX(savedDX); setCS(savedCS); setIP(savedIP); /* * Quit if wait succeeded */ if (!getCF()) return; #endif /* JOKER */ /* * Need to do fixed wait locally */ waitf(time_to_wait); } LOCAL void nec_output IFN1(half_word, byte_value) { /* * This routine sends a byte to the FDC after testing for * correct direction and controller ready. If the FDC does * not respond after a few tries, it is assumed that there * is a bug in our FDC emulation */ half_word diskette_status_reg; int count; /* * Wait for ready and correct direction */ count = 0; do { if (count++ >= FDC_TIME_OUT) { always_trace0("diskette_io: FDC error - input repletion"); return; } inb(DISKETTE_STATUS_REG, &diskette_status_reg); } while ((diskette_status_reg & (DSR_RQM | DSR_DIO)) != DSR_RQM); /* * Output the byte */ outb(DISKETTE_DATA_REG, byte_value); /* * Do fixed wait for FDC update cycle time */ waitf(FDC_SETTLE); } LOCAL seek IFN2(int, drive, int, track) { /* * This routine will move the head on the named drive * to the named track. If the drive has not been accessed * since the drive reset command was issued, the drive * will be recalibrated */ half_word seek_status, disk_track, disk_state; FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; int status; note_trace2(FLOPBIOS_VERBOSE, "diskette_io:seek(drive=%d,track=%d)", drive, track); /* * Check if recalibration required before seek */ sas_load(SEEK_STATUS, &seek_status); if ((seek_status & (1 << drive)) == 0) { /* * Update the seek status and recalibrate */ sas_store(SEEK_STATUS, seek_status | (1 << drive)); if (recal(drive) != SUCCESS) { sas_store(FLOPPY_STATUS, FS_OK); if (recal(drive) == FAILURE) return(FAILURE); } /* * Drive will now be at track 0 */ sas_store(FDD_TRACK+drive, 0); if (track == 0) { /* * No need to seek */ hd_wait(drive); return(SUCCESS); } } /* * Allow for double stepping */ sas_load(FDD_STATUS+drive, &disk_state); if ((disk_state & FS_DOUBLE_STEP) != 0) track *= 2; /* * Update current track number */ sas_load(FDD_TRACK+drive, &disk_track); if (disk_track == track) { /* * No need to seek */ return(SUCCESS); } sas_store(FDD_TRACK+drive, track); /* * Do the seek and check the results */ put_c8_cmd(fdc_cmd_block, FDC_SEEK); put_c8_pad(fdc_cmd_block, 0); nec_output(fdc_cmd_block[0]); put_c8_drive(fdc_cmd_block, drive); put_c8_head(fdc_cmd_block, 0); put_c8_pad1(fdc_cmd_block, 0); nec_output(fdc_cmd_block[1]); put_c8_new_cyl(fdc_cmd_block, track); nec_output(fdc_cmd_block[2]); status = chk_stat_2(); /* * Wait for head settle time */ hd_wait(drive); return(status); } LOCAL recal IFN1(int, drive) { /* * Send recalibrate drive command to the FDC and check the * results */ FDC_CMD_BLOCK fdc_cmd_block[MAX_COMMAND_LEN]; note_trace1(FLOPBIOS_VERBOSE, "diskette_io:recal(drive=%d)", drive); put_c5_cmd(fdc_cmd_block, FDC_RECALIBRATE); put_c5_pad(fdc_cmd_block, 0); nec_output(fdc_cmd_block[0]); put_c5_drive(fdc_cmd_block, drive); put_c5_pad1(fdc_cmd_block, 0); nec_output(fdc_cmd_block[1]); return(chk_stat_2()); } LOCAL chk_stat_2 IFN0() { /* * This routine handles the interrupt received after * recalibrate, seek or reset to the adapter. The * interrupt is waited for, the interrupt status * sensed, and the result returned to the caller */ half_word diskette_status; /* * Check for interrupt */ if (wait_int() != FAILURE) { /* * Sense the interrupt and check the results */ nec_output(FDC_SENSE_INT_STATUS); if (results() != FAILURE) { if ((get_r3_ST0(fl_nec_status) & (ST0_SEEK_END | ST0_INTERRUPT_CODE_0)) != (ST0_SEEK_END | ST0_INTERRUPT_CODE_0)) { return(SUCCESS); } /* * Abnormal termination of command */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_SEEK_ERROR; sas_store(FLOPPY_STATUS, diskette_status); } } return(FAILURE); } LOCAL wait_int IFN0() { /* * Check whether an interrupt occurred; if it did, return * SUCCESS; if there was a time out return FAILURE */ half_word seek_status, diskette_status; #ifndef JOKER word savedAX, savedCS, savedIP; /* * Enable interrupts */ setIF(1); /* * Notify OS that BIOS is about to "wait" for a * diskette interrupt. Any pending diskette * interrupt will be serviced here, so there's * no need for a subsequent sub-cpu call to * wait for the interrupt * * [[WTR - is this true, we do do 2 host_simulates...? ]] */ savedAX = getAX(); savedCS = getCS(); savedIP = getIP(); setAH(INT15_DEVICE_BUSY); setAL(INT15_DEVICE_FLOPPY); #ifdef NTVDM setCS(int15_seg); setIP(int15_off); #else setCS(RCPU_INT15_SEGMENT); setIP(RCPU_INT15_OFFSET); #endif /* NTVDM */ host_simulate(); setAX(savedAX); setCS(savedCS); setIP(savedIP); /* * Call sub-cpu to do the "wait" for interrupt, saving * registers that would otherwise be corrupted */ try_again: savedCS = getCS(); savedIP = getIP(); #ifdef NTVDM setCS(wait_int_seg); setIP(wait_int_off); #else setCS(RCPU_WAIT_INT_SEGMENT); setIP(RCPU_WAIT_INT_OFFSET); #endif /* NTVDM */ host_simulate(); setCS(savedCS); setIP(savedIP); #else /* JOKER */ /* Since we can't have a recursive CPU call, we'd be ** well stuffed but for the fact that the default diskette ** interrupt on SoftPC is actually a BOP which calls the "C" ** function diskette_int() in "floppy_io.c". So most, if not ** all, of the action takes place on the host side anyway. ** ** FieldFloppyInterrupts() simply checks if an interrupt ** was generated, and does what the diskette_int() does, ** but without the recursive CPU call. */ FieldFloppyInterrupts(); #endif /* JOKER */ /* * Check for success, or time out */ sas_load(SEEK_STATUS, &seek_status); if ((seek_status & SS_INT_OCCURRED) == 0) { #ifdef FLOPPIES_KEEP_TRYING extern IBOOL fdc_interrupt_pending; /* If the CPU is very slow, or interrupt emulation * has changed for the worst, then the low-priority * floppy interrupt may not get through in the execution * of the instructions allotted. This code looks at a * global variable maintained by fla.c, which says whether * or not the ICA has an un-processed diskette interrupt * pending. */ if (fdc_interrupt_pending) { always_trace0("fdc_interrupt_pending, so try again"); goto try_again; } #endif /* FLOPPIES_KEEP_TRYING */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_TIME_OUT; sas_store(FLOPPY_STATUS, diskette_status); return(FAILURE); } else { seek_status &= ~SS_INT_OCCURRED; sas_store(SEEK_STATUS, seek_status); return(SUCCESS); } } LOCAL results IFN0() { /* * This routine will read anything that the FDC controller * returns following an interrupt */ half_word diskette_status_reg, diskette_status; int count; UTINY val; /* * Wait for ready and direction */ count = 0; do { if (count++ >= FDC_TIME_OUT) { /* * Expect to return here when there is a * time out (not an FDC error) */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_TIME_OUT; sas_store(FLOPPY_STATUS, diskette_status); LOAD_RESULT_BLOCK; return(FAILURE); } inb(DISKETTE_STATUS_REG, &diskette_status_reg); } while ((diskette_status_reg & (DSR_RQM | DSR_DIO)) != (DSR_RQM | DSR_DIO)); /* * Extract the results from the FDC */ count = 0; do { /* * Read a byte of result data */ inb(DISKETTE_DATA_REG, &val); sas_store( BIOS_FDC_STATUS_BLOCK + count, val ); count++; /* * Do fixed wait for FDC update cycle time */ waitf(FDC_SETTLE); /* * Check for further result bytes */ inb(DISKETTE_STATUS_REG, &diskette_status_reg); } while ((diskette_status_reg & FDC_BUSY) && (count < MAX_RESULT_LEN)); LOAD_RESULT_BLOCK; if ((diskette_status_reg & FDC_BUSY) && (count == MAX_RESULT_LEN)) { /* * Problem with the FDC */ sas_load(FLOPPY_STATUS, &diskette_status); diskette_status |= FS_FDC_ERROR; sas_store(FLOPPY_STATUS, diskette_status); always_trace0("diskette_io: FDC error - output overdose"); return(FAILURE); } return(SUCCESS); } LOCAL read_dskchng IFN1(int, drive) { /* * Reads the state of the disk change line for "drive" */ half_word diskette_dir_reg; /* * Switch to the required drive */ motor_on(drive); /* * Read the diskette changed bit from the Digital Input * register */ inb(DISKETTE_DIR_REG, &diskette_dir_reg); return(((diskette_dir_reg & DIR_DISKETTE_CHANGE) != 0) ? FAILURE : SUCCESS); } void drive_detect IFN1(int, drive) { /* * Determines whether drive is 80 or 40 tracks * and updates state information accordingly */ half_word disk_state; int track; /* * This method of determining the number of tracks on the * drive depends on seeking to a track that lies beyond the * last track of a 40 track drive, but is valid on an 80 * track drive. * * At this point the real track number on a 40 track drive * will be out of step with what the FDC thinks it is. * * By seeking downwards to track 0, and observing when a * sense drive status reports that the drive is really at * track 0, a 40 and 80 track drive can be distinguished. */ note_trace1( GFI_VERBOSE, "drive_detect():start: DRIVE %x", drive ); motor_on(drive); if ( (recal(drive) == SUCCESS) && (seek(drive, FDD_CLONK_TRACK) == SUCCESS)) { track = FDD_JUDDER_TRACK + 1; do { if (--track < 0) { /* * 40 track drive */ note_trace0( GFI_VERBOSE, "drive_detect(): 40 TRACK" ); sas_load(FDD_STATUS+drive, &disk_state); disk_state |= (DC_DETERMINED | FS_MEDIA_DET | RS_250); sas_store(FDD_STATUS+drive, disk_state); return; } if (seek(drive, track) != SUCCESS) { always_trace0( "drive_detect(): FAILURE" ); return; } nec_output(FDC_SENSE_DRIVE_STATUS); nec_output((half_word)drive); (void )results(); } while (get_r2_ST3_track_0(fl_nec_status) != 1); /* * Drive reports that it is at track 0; what does * the FDC think? */ if (track != 0) { note_trace0( GFI_VERBOSE, "drive_detect(): 40 TRACK" ); /* * Must be a 40 track drive */ sas_load(FDD_STATUS+drive, &disk_state); disk_state |= (DC_DETERMINED | FS_MEDIA_DET | RS_250); sas_store(FDD_STATUS+drive, disk_state); return; } else { /* * Must be an 80 track drive */ note_trace0( GFI_VERBOSE, "drive_detect(): 80 TRACK" ); sas_load(FDD_STATUS+drive, &disk_state); disk_state |= DC_80_TRACK; sas_store(FDD_STATUS+drive, disk_state); return; } } } LOCAL void waitf IFN1(long, time) { UNUSED(time); /* * Fixed wait of "time" microseconds */ } #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_INIT.seg" #endif void fl_diskette_setup IFN0() { /* * Identify what types of drives are installed in the system * and initialise the diskette BIOS state variables to known * values. Do not change declaration of rtc_wait to rtc_wait_flag * as there is a macro (yes MACRO!!) of the same name declared in * rtc_bios.h. */ half_word rtc_wait, lastrate; int drive; /* * Disable RTC wait function */ sas_load(RTC_WAIT_FLAG_ADDR, &rtc_wait); rtc_wait |= 1; sas_store(RTC_WAIT_FLAG_ADDR, rtc_wait); /* * Initialise other variables in the diskette data * area */ sas_storew(FDD_STATUS, 0); sas_storew(FDD_STATUS+1, 0); /* drive b as well */ sas_load(RATE_STATUS, &lastrate); rate_unitialised = TRUE; lastrate &= ~(RS_MASK | (RS_MASK >> 4)); lastrate |= RS_MASK; sas_store(RATE_STATUS, lastrate); sas_store(SEEK_STATUS, 0); sas_store(MOTOR_COUNT, 0); sas_store(MOTOR_STATUS, 0); sas_store(FLOPPY_STATUS, 0); /* * Try to determine the type of each drive */ for (drive = 0; drive < MAX_FLOPPY; drive++) { drive_detect(drive); translate_old(drive); } /* * Force an immediate recalibrate */ sas_store(SEEK_STATUS, 0); /* * Enable RTC wait function */ sas_load(RTC_WAIT_FLAG_ADDR, &rtc_wait); rtc_wait &= ~1; sas_store(RTC_WAIT_FLAG_ADDR, rtc_wait); /* * Return without setting sectors transferred */ setup_end(IGNORE_SECTORS_TRANSFERRED); }