|
|
1.1 root 1: /*
1.1.1.4 root 2: Hatari - fdc.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.15! root 7: Floppy Disk Controller(FDC) emulation.
! 8: All commands are emulated with good timings estimation, as many programs
! 9: (demo or cracked games) rely on accurate FDC timings and DMA transfer by blocks
! 10: of 16 bytes.
! 11: The behaviour of all FDC's registers matches the official docs and should not
! 12: cause programs to fail when accessing the FDC (especially for Status Register).
! 13: As Hatari only handles ST/MSA disk images that only support 512 bytes sectors as
! 14: well as a fixed number of sectors per track, a few parts of the FDC emulation are
! 15: simplified and would need to be changed to handle more complex disk images (Pasti).
1.1 root 16: */
1.1.1.9 root 17:
1.1.1.11 root 18: const char FDC_fileid[] = "Hatari fdc.c : " __DATE__ " " __TIME__;
1.1 root 19:
20: #include "main.h"
1.1.1.4 root 21: #include "configuration.h"
1.1 root 22: #include "fdc.h"
1.1.1.3 root 23: #include "hdc.h"
1.1 root 24: #include "floppy.h"
1.1.1.6 root 25: #include "ioMem.h"
26: #include "log.h"
1.1 root 27: #include "m68000.h"
28: #include "memorySnapShot.h"
29: #include "mfp.h"
30: #include "psg.h"
31: #include "stMemory.h"
1.1.1.14 root 32: #include "screen.h"
1.1.1.10 root 33: #include "video.h"
1.1.1.15! root 34: #include "clocks_timings.h"
! 35: #include "utils.h"
1.1 root 36:
1.1.1.3 root 37:
1.1 root 38: /*
1.1.1.7 root 39: Floppy Disk Controller
1.1 root 40:
41: Programmable Sound Generator (YM-2149)
42:
43: 0xff8800(even byte) - PSG Register Data (Read, used for parallel port)
44: - PSG Register Select (Write)
45:
46: Write to bits 0-3 to select PSG register to use(then write data to 0xfff8802)
47: Value Register
48:
49: 0000 Channel A Fine Tune
50: 0001 Channel A Coarse Tune
51: 0010 Channel B Fine Tune
52: 0011 Channel B Coarse Tune
53: 0100 Channel C Fine Tune
54: 0101 Channel C Coarse Tune
55: 0110 Noise Generator Control
56: 0111 Mixer Control - I/O enable
57: 1000 Channel A Amplitude
58: 1001 Channel B Amplitude
59: 1010 Channel C Amplitude
60: 1011 Envelope Period Fine Tune
61: 1100 Envelope Peroid Coarse Tune
62: 1101 Envelope Shape
63: 1110 I/O Port A Select (Write only)
64: 1111 I/O Port B Select
65:
66: 0xfff8802(even byte) - Bits according to 0xff8800 Register select
1.1.1.4 root 67:
1.1 root 68: 1110(Register 14) - I/O Port A
69: Bit 0 - Floppy side 0/1
70: Bit 1 - Floppy drive 0 select
71: Bit 2 - Floppy drive 1 select
72: Bit 3 - RS232 Ready to send (RTS)
73: Bit 4 - RS232 Data Terminal Ready (DTR)
74: Bit 5 - Centronics Strobe
75: Bit 6 - General Purpose Output
76: Bit 7 - Reserved
77:
1.1.1.7 root 78: ACSI DMA and Floppy Disk Controller(FDC)
1.1 root 79: 0xff8604 - information from file '1772.info.txt, by David Gahris' (register r0)
1.1.1.15! root 80: Word access only, but only lower byte (ff8605) is used
1.1 root 81: (write) - Disk controller
1.1.1.15! root 82: Set DMA sector count if ff8606 bit 4 == 1
! 83: Set FDC's internal registers depending on bit 1/2 of ff8606 if bit 4 == 0
1.1 root 84: (read) - Disk controller status
85: Bit 0 - Busy. This bit is 1 when the 177x is busy. This bit is 0 when the 177x is free for CPU commands.
86: Bit 1 - Index / Data Request. On Type I commands, this bit is high during the index pulse that occurs once
87: per disk rotation. This bit is low at all times other than the index pulse. For Type II and III commands,
88: Bit 1 high signals the CPU to handle the data register in order to maintain a continuous flow of data.
89: Bit 1 is high when the data register is full during a read or when the data register is empty during a write.
90: "Worst case service time" for Data Request is 23.5 cycles.
91: Bit 2 - Track Zero / Lost Data. After Type I commands, this bit is 0 if the mechanism is at track zero.
92: This bit is 1 if the head is not at track zero. After Type II or III commands, this bit is 1 if the
93: CPU did not respond to Data Request (Status bit 1) in time for the 177x to maintain a continuous data flow.
94: This bit is 0 if the CPU responded promptly to Data Request.
1.1.1.15! root 95: NOTE : on ST, Lost Data is never set because the DMA always handles the data request signal.
1.1 root 96: Bit 3 - CRC Error. This bit is high if a sector CRC on disk does not match the CRC which the 177x
97: computed from the data. The CRC polynomial is x^16+x^12+x^5+1. If the stored CRC matches the newly
98: calculated CRC, the CRC Error bit is low. If this bit and the Record Not Found bit are set, the error
99: was in an ID field. If this bit is set but Record Not Found is clear, the error was in a data field.
100: Bit 4 - Record Not Found. This bit is set if the 177x cannot find the track, sector, or side which
101: the CPU requested. Otherwise, this bit is clear.
102: Bit 5 - Spin-up / Record Type. For Type I commands, this bit is low during the 6-revolution motor
103: spin-up time. This bit is high after spin-up. For Type II and Type III commands, Bit 5 low
104: indicates a normal data mark. Bit 5 high indicates a deleted data mark.
105: Bit 6 - Write Protect. This bit is not used during reads. During writes, this bit is high when the disk is write protected.
1.1.1.15! root 106: After a type I command, this bit is constantly updated an give the current value of the WPT signal.
1.1 root 107: Bit 7 - Motor On. This bit is high when the drive motor is on, and low when the motor is off.
108:
109: 0xff8606 - DMA Status(read), DMA Mode Control(write) - NOTE bits 0,9-15 are not used
110: Bit 1 - FDC Pin A0 (See below)
111: Bit 2 - FDC Pin A1
112: Bit 3 - FDC/HDC Register Select
113: Bit 4 - FDC/Sector count select
114: Bit 5 - Reserved
115: Bit 6 - Enable/Disable DMA
116: Bit 7 - HDC/FDC
117: Bit 8 - Read/Write
118:
119: A1 A0 Read Write(bit 8==1)
120: 0 0 Status Command
121: 0 1 Track Register Track Register
122: 1 0 Sector Register Sector Register
123: 1 1 Data Register Data Register
124:
125:
1.1.1.6 root 126: According to the documentation INTRQ is generated at the completion of each
127: command (causes an interrupt in the MFP). INTRQ is reset by reading the status
128: register OR by loading a new command. So, does this mean the GPIP? Or does it
129: actually CANCEL the interrupt? Can this be done?
1.1.1.15! root 130:
! 131: NOTE [NP] : The DMA is connected to the FDC and its Data Register, each time a DRQ
! 132: is made by the FDC, it's handled by the DMA through its internal 16 bytes buffer.
! 133: This means that in the case of the Atari ST the LOST_DATA bit will never be set
! 134: in the Status Register (but data can be lost if FDC_DMA.SectorCount=0 as there
! 135: will be no transfer between DMA and RAM)
! 136:
! 137:
! 138: Detecting disk changes :
! 139: ------------------------
! 140: 3'1/2 floppy drives include a 'DSKCHG' signal on pin 34 to detect when a disk was changed.
! 141: Unfortunatelly on ST, this signal is not connected. Nevertheless, it's possible to detect
! 142: a disk was inserted or ejected by looking at the 'WPT' signal which tells if a disk is write
! 143: protected or not.
! 144: At the drive level, a light is emitted above the top left corner of the floppy :
! 145: - if the write protection hole on the floppy is opened, the light goes through and the disk
! 146: is considered to be write protected.
! 147: - if the write protection hole on the floppy is closed, the light can't go through and the
! 148: disk is write enabled.
! 149: The point is that when any "solid" part of the floppy obstructs the light signal, the WPT
! 150: signal will change immediatly : it will be considered as if a write enabled disk was present.
! 151: So, when a floppy is ejected or inserted, the body of the floppy will briefly obstruct the light,
! 152: whatever the state of the protection hole could be.
! 153: Similarly, when there's no floppy inside the drive, the light signal can pass through, so it will
! 154: be considered as if a write protected disk was present.
! 155: So, let's call 'C' the state when protection hole is Closed (ie WPT = 0) and 'O' the state
! 156: when protection hole is Opened (ie WPT = 1). We have the following cases :
! 157: - floppy in drive : state can be C or O depending on the protection tab. Let's call it 'X'
! 158: - no floppy in drive : state is equivalent to O (because the light signal is not obstructed)
! 159: - ejecting a floppy : states will go from X to C and finally to O
! 160: - inserting a floppy : states will go from O to C and finally to X
! 161:
! 162: The TOS monitors the changes on the WPT signal to determine if a floppy was ejected or inserted.
! 163: On TOS 1.02fr, the code is located between $fc1bc4 and $fc1ebc. Every 8 VBL, one floppy drive is checked
! 164: to see if the WPT signal changed. When 1 drive is connected, this means a floppy change should keep the
! 165: WPT signal during at least 8 VBLs. When 2 drive are connected, each drive is checked every 16 VBLs, so
! 166: the WPT signal should be kept for at least 16 VBLs.
! 167:
! 168: During these transition phases between "ejected" and "inserted", we force the WPT signal to either 0 or 1,
! 169: depending on which transition we're emulating (see Floppy_DriveTransitionUpdateState()) :
! 170: - Ejecting : WPT will be X, then 0, then 1
! 171: - Inserting : WPT will be 1, then 0, then X
! 172:
1.1 root 173: */
174:
1.1.1.14 root 175: /*-----------------------------------------------------------------------*/
1.1.1.15! root 176:
! 177: #define FDC_STR_BIT_BUSY 0x01
! 178: #define FDC_STR_BIT_INDEX 0x02 /* type I */
! 179: #define FDC_STR_BIT_DRQ 0x02 /* type II and III */
! 180: #define FDC_STR_BIT_TR00 0x04 /* type I */
! 181: #define FDC_STR_BIT_LOST_DATA 0x04 /* type II and III */
! 182: #define FDC_STR_BIT_CRC_ERROR 0x08
! 183: #define FDC_STR_BIT_RNF 0x10
! 184: #define FDC_STR_BIT_SPIN_UP 0x20 /* type I */
! 185: #define FDC_STR_BIT_RECORD_TYPE 0x20 /* type II and III */
! 186: #define FDC_STR_BIT_WPRT 0x40
! 187: #define FDC_STR_BIT_MOTOR_ON 0x80
! 188:
! 189:
! 190: #define FDC_COMMAND_BIT_VERIFY (1<<2) /* 0=verify after type I, 1=no verify after type I */
! 191: #define FDC_COMMAND_BIT_HEAD_LOAD (1<<2) /* for type II/III 0=no extra delay, 1=add 30 ms delay to set the head */
! 192: #define FDC_COMMAND_BIT_MOTOR_ON (1<<3) /* 0=enable motor test, 1=disable motor test */
! 193: #define FDC_COMMAND_BIT_UPDATE_TRACK (1<<4) /* 0=don't update TR after type I, 1=update TR after type I */
! 194: #define FDC_COMMAND_BIT_MULTIPLE_SECTOR (1<<4) /* 0=read/write only 1 sector, 1=read/write many sectors */
! 195:
! 196:
! 197:
! 198: /* FDC Emulation commands used in FDC.Command */
1.1.1.14 root 199: enum
200: {
201: FDCEMU_CMD_NULL = 0,
202: /* Type I */
203: FDCEMU_CMD_RESTORE,
204: FDCEMU_CMD_SEEK,
1.1.1.15! root 205: FDCEMU_CMD_STEP, /* Also used for STEP IN and STEP OUT */
1.1.1.14 root 206: /* Type II */
207: FDCEMU_CMD_READSECTORS,
208: FDCEMU_CMD_WRITESECTORS,
209: /* Type III */
210: FDCEMU_CMD_READADDRESS,
1.1.1.15! root 211: FDCEMU_CMD_READTRACK,
! 212: FDCEMU_CMD_WRITETRACK,
! 213:
! 214: /* Other fake commands used internally */
! 215: FDCEMU_CMD_MOTOR_STOP
1.1.1.14 root 216: };
217:
218:
1.1.1.15! root 219: /* FDC Emulation commands' sub-states used in FDC.CommandState */
1.1.1.14 root 220: enum
221: {
1.1.1.15! root 222: FDCEMU_RUN_NULL = 0,
1.1.1.14 root 223:
1.1.1.15! root 224: /* Restore */
! 225: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO,
! 226: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP,
! 227: FDCEMU_RUN_RESTORE_COMPLETE,
! 228: /* Seek */
1.1.1.14 root 229: FDCEMU_RUN_SEEK_TOTRACK,
1.1.1.15! root 230: FDCEMU_RUN_SEEK_COMPLETE,
! 231: /* Step / Step In / Step Out */
1.1.1.14 root 232: FDCEMU_RUN_STEP_ONCE,
1.1.1.15! root 233: FDCEMU_RUN_STEP_COMPLETE,
! 234: /* Read Sector */
! 235: FDCEMU_RUN_READSECTORS_READDATA,
! 236: FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER,
! 237: FDCEMU_RUN_READSECTORS_READDATA_DMA,
! 238: FDCEMU_RUN_READSECTORS_RNF,
! 239: FDCEMU_RUN_READSECTORS_COMPLETE,
! 240: /* Write Sector */
! 241: FDCEMU_RUN_WRITESECTORS_WRITEDATA,
! 242: FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER,
! 243: FDCEMU_RUN_WRITESECTORS_WRITEDATA_DMA,
! 244: FDCEMU_RUN_WRITESECTORS_RNF,
! 245: FDCEMU_RUN_WRITESECTORS_COMPLETE,
! 246: /* Read Address */
! 247: FDCEMU_RUN_READADDRESS,
! 248: FDCEMU_RUN_READADDRESS_DMA,
! 249: FDCEMU_RUN_READADDRESS_COMPLETE,
! 250: /* Read Track */
! 251: FDCEMU_RUN_READTRACK,
! 252: FDCEMU_RUN_READTRACK_DMA,
! 253: FDCEMU_RUN_READTRACK_COMPLETE,
! 254: /* Write Track */
! 255: FDCEMU_RUN_WRITETRACK,
! 256: FDCEMU_RUN_WRITETRACK_DMA,
! 257: FDCEMU_RUN_WRITETRACK_COMPLETE
1.1.1.14 root 258: };
259:
260:
261:
1.1.1.15! root 262: /* Standard hardware values for the FDC. This should allow to get good timings estimation */
! 263: /* when dealing with non protected disks that require a correct speed (MSA or ST images) */
! 264: /* FIXME [NP] : Those timings could be improved by taking into account the time */
! 265: /* it takes to reach the track index/sector/address field before really reading it, but this level */
! 266: /* of accuracy is not necessary for ST/MSA disk images (it would be required to emulate protections */
! 267: /* in Pasti disk images) */
1.1.1.14 root 268:
1.1.1.15! root 269: #define FDC_BITRATE_STANDARD 250000 /* read/write speed of the WD1772 in bits per sec */
! 270: #define FDC_RPM_STANDARD 300 /* 300 RPM or 5 spins per sec */
! 271: #define FDC_TRACK_BYTES_STANDARD ( ( FDC_BITRATE_STANDARD / 8 ) / ( FDC_RPM_STANDARD / 60 ) ) /* 6250 bytes */
1.1.1.14 root 272:
1.1.1.15! root 273: #define FDC_TRANSFER_BYTES_US( n ) ( ( n ) * 8 * 1000000.L / FDC_BITRATE_STANDARD ) /* micro sec to read/write 'n' bytes in the WD1772 */
1.1.1.14 root 274:
1.1.1.15! root 275: /* Delays are in micro sec */
! 276: #define FDC_DELAY_MOTOR_ON ( 1000000.L * 6 / ( FDC_RPM_STANDARD / 60 ) ) /* 6 spins to reach correct speed */
! 277: #define FDC_DELAY_MOTOR_OFF ( 1000000.L * 9 / ( FDC_RPM_STANDARD / 60 ) ) /* Turn off motor 9 spins after the last command */
1.1.1.14 root 278:
1.1.1.15! root 279: #define FDC_DELAY_HEAD_LOAD ( 30 * 1000 ) /* Additionnal 30 ms delay to load the head in type II/III */
1.1.1.14 root 280:
1.1.1.15! root 281: #define FDC_DELAY_RNF ( 1000000.L * 5 / ( FDC_RPM_STANDARD / 60 ) ) /* 5 spins to set RNF */
1.1 root 282:
1.1.1.15! root 283: #define FDC_DELAY_TYPE_I_PREPARE 100 /* Types I commands take at least 0.1 ms to execute */
! 284: /* (~800 cpu cycles @ 8 Mhz). FIXME [NP] : this was not measured, it's */
! 285: /* to avoid returning immediatly when command has no effect */
! 286: #define FDC_DELAY_TYPE_II_PREPARE 1 /* Start Type II commands immediatly */
! 287: #define FDC_DELAY_TYPE_III_PREPARE 1 /* Start Type III commands immediatly */
! 288: #define FDC_DELAY_TYPE_IV_PREPARE 100 /* FIXME [NP] : this was not measured */
! 289:
! 290: #define FDC_DELAY_TRANSFER_DMA_16 FDC_TRANSFER_BYTES_US( DMA_DISK_TRANSFER_SIZE )
1.1 root 291:
1.1.1.15! root 292: #define FDC_DELAY_COMMAND_COMPLETE 1 /* Number of us before going to the _COMPLETE state (~8 cpu cycles) */
1.1.1.5 root 293:
1.1.1.15! root 294: #define FDC_DELAY_COMMAND_IMMEDIATE 1 /* Number of us to go immediatly to another state */
1.1.1.5 root 295:
296:
1.1.1.15! root 297: #define DMA_DISK_SECTOR_SIZE 512 /* Sector count at $ff8606 is for 512 bytes blocks */
! 298: #define DMA_DISK_TRANSFER_SIZE 16 /* DMA transfers blocks of 16 bytes at a time */
1.1.1.5 root 299:
1.1.1.15! root 300: #define FDC_PHYSICAL_MAX_TRACK 90 /* Head can't go beyond 90 tracks */
1.1 root 301:
1.1.1.2 root 302:
1.1.1.15! root 303: #define FDC_SIDE ( (~PSGRegisters[PSG_REG_IO_PORTA]) & 0x01 ) /* Side 0 or 1 */
! 304: #define FDC_DRIVE FDC_FindFloppyDrive()
1.1.1.14 root 305:
1.1.1.15! root 306: #define FDC_STEP_RATE ( FDC.CR & 0x03 ) /* Bits 0 and 1 of the current type I command */
! 307:
! 308: static int FDC_StepRate_ms[] = { 6 , 12 , 2 , 3 }; /* Controlled by bits 1 and 0 (r1/r0) in type I commands */
! 309:
! 310:
! 311: #define FDC_SECTOR_SIZE_128 0 /* Sector size used in the ID fields */
! 312: #define FDC_SECTOR_SIZE_256 1
! 313: #define FDC_SECTOR_SIZE_512 2
! 314: #define FDC_SECTOR_SIZE_1024 3
! 315:
! 316:
! 317: #define FDC_FAST_FDC_FACTOR 10 /* Divide all delays by this value when --fastfdc is used */
! 318:
! 319:
! 320: typedef struct {
! 321: /* WD1772 internal registers */
! 322: Uint8 DR; /* Data Register */
! 323: Uint8 TR; /* Track Register */
! 324: Uint8 SR; /* Sector Register */
! 325: Uint8 CR; /* Command Register */
! 326: Uint8 STR; /* Status Register */
! 327: int StepDirection; /* +1 (Step In) or -1 (Step Out) */
! 328:
! 329: /* Other variables */
! 330: int Command; /* FDC emulation command currently being exceuted */
! 331: int CommandState; /* Current state for the running command */
! 332: Uint8 CommandType; /* Type of latest FDC command (1,2,3 or 4) */
! 333: bool ReplaceCommandPossible; /* true if the current command can be replaced by another one */
! 334: /* ([NP] FIXME : only possible during prepare+spinup phases ?) */
! 335:
! 336: Uint8 ID_FieldLastSector; /* Last sector number returned by Read Address (to simulate a spinning disk) */
! 337: } FDC_STRUCT;
! 338:
! 339:
! 340: typedef struct {
! 341: /* DMA internal registers */
! 342: Uint16 Status;
! 343: Uint16 Mode;
! 344: Uint16 SectorCount;
! 345: Uint16 BytesInSector;
! 346:
! 347: /* Variables to handle our DMA buffer */
! 348: int PosInBuffer;
! 349: int PosInBufferTransfer;
! 350: int BytesToTransfer;
! 351: } FDC_DMA_STRUCT;
! 352:
! 353:
! 354: static FDC_STRUCT FDC; /* All variables related to the WD1772 emulation */
! 355: static FDC_DMA_STRUCT FDC_DMA; /* All variables related to the DMA transfer */
! 356:
! 357: static Uint8 HeadTrack[ MAX_FLOPPYDRIVES ]; /* A: and B: */
! 358:
! 359: static Uint8 DMADiskWorkSpace[ FDC_TRACK_BYTES_STANDARD+1000 ]; /* Workspace used to transfer bytes between floppy and DMA */
! 360: /* It should be large enough to contain a whole track */
! 361:
! 362:
! 363: /*--------------------------------------------------------------*/
! 364: /* Local functions prototypes */
! 365: /*--------------------------------------------------------------*/
! 366:
! 367: static int FDC_DelayToCpuCycles ( int Delay_micro );
! 368: static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC );
! 369:
! 370: static void FDC_ResetDMA ( void );
! 371: static void FDC_DMA_InitTransfer ( void );
! 372: static bool FDC_DMA_ReadFromFloppy ( void );
! 373: static bool FDC_DMA_WriteToFloppy ( void );
! 374:
! 375: static int FDC_FindFloppyDrive ( void );
! 376: static int FDC_GetSectorsPerTrack ( int Track , int Side );
! 377: static int FDC_GetSidesPerDisk ( int Track );
! 378:
! 379: static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits );
! 380: static int FDC_CmdCompleteCommon ( bool DoInt );
! 381: static void FDC_VerifyTrack ( void );
! 382: static int FDC_UpdateMotorStop ( void );
! 383: static int FDC_UpdateRestoreCmd ( void );
! 384: static int FDC_UpdateSeekCmd ( void );
! 385: static int FDC_UpdateStepCmd ( void );
! 386: static int FDC_UpdateReadSectorsCmd ( void );
! 387: static int FDC_UpdateWriteSectorsCmd ( void );
! 388: static int FDC_UpdateReadAddressCmd ( void );
! 389: static int FDC_UpdateReadTrackCmd ( void );
! 390:
! 391: static int FDC_Check_MotorON ( Uint8 FDC_CR );
! 392: static int FDC_TypeI_Restore ( void );
! 393: static int FDC_TypeI_Seek ( void );
! 394: static int FDC_TypeI_Step ( void );
! 395: static int FDC_TypeI_StepIn ( void );
! 396: static int FDC_TypeI_StepOut ( void );
! 397: static int FDC_TypeII_ReadSector ( void );
! 398: static int FDC_TypeII_WriteSector(void);
! 399: static int FDC_TypeIII_ReadAddress ( void );
! 400: static int FDC_TypeIII_ReadTrack ( void );
! 401: static int FDC_TypeIII_WriteTrack ( void );
! 402: static int FDC_TypeIV_ForceInterrupt ( bool bCauseCPUInterrupt );
! 403:
! 404: static int FDC_ExecuteTypeICommands ( void );
! 405: static int FDC_ExecuteTypeIICommands ( void );
! 406: static int FDC_ExecuteTypeIIICommands ( void );
! 407: static int FDC_ExecuteTypeIVCommands ( void );
! 408: static void FDC_ExecuteCommand ( void );
! 409:
! 410: static void FDC_WriteSectorCountRegister ( void );
! 411: static void FDC_WriteCommandRegister ( void );
! 412: static void FDC_WriteTrackRegister ( void );
! 413: static void FDC_WriteSectorRegister ( void );
! 414: static void FDC_WriteDataRegister ( void );
! 415:
! 416: static bool FDC_ReadSectorFromFloppy ( Uint8 *buf , Uint8 Sector , int *pSectorSize );
! 417: static bool FDC_WriteSectorToFloppy ( int DMASectorsCount , Uint8 Sector , int *pSectorSize );
1.1 root 418:
1.1.1.2 root 419:
420: /*-----------------------------------------------------------------------*/
1.1.1.9 root 421: /**
422: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
423: */
1.1.1.10 root 424: void FDC_MemorySnapShot_Capture(bool bSave)
1.1 root 425: {
1.1.1.15! root 426: MemorySnapShot_Store(&FDC, sizeof(FDC));
! 427: MemorySnapShot_Store(&FDC_DMA, sizeof(FDC_DMA));
! 428: MemorySnapShot_Store(HeadTrack, sizeof(HeadTrack));
! 429:
! 430: MemorySnapShot_Store(DMADiskWorkSpace, sizeof(DMADiskWorkSpace));
1.1 root 431: }
432:
1.1.1.2 root 433:
434: /*-----------------------------------------------------------------------*/
1.1.1.9 root 435: /**
1.1.1.15! root 436: * Convert a delay in micro seconds to its equivalent of cpu cycles
! 437: * (FIXME [NP] : for now we use a fixed 8 MHz clock, because cycInt.c requires it)
1.1.1.9 root 438: */
1.1.1.15! root 439: static int FDC_DelayToCpuCycles ( int Delay_micro )
1.1 root 440: {
1.1.1.15! root 441: int Delay;
! 442:
! 443: Delay = (int) ( ( (Sint64)MachineClocks.FDC_Freq * Delay_micro ) / 1000000 ) & -4;
! 444:
! 445: /* Our conversion expect FDC_Freq to be the same as CPU_Freq (8 Mhz) */
! 446: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
! 447: /* FIXME : as stated above, this should be handled better, without involving 8 MHz CPU_Freq */
! 448: if ( ConfigureParams.System.nMachineType == MACHINE_FALCON )
! 449: Delay /= 2; /* correct delays for a 8 MHz clock instead of 16 */
! 450:
! 451: if ( ( ConfigureParams.DiskImage.FastFloppy ) && ( Delay > FDC_FAST_FDC_FACTOR ) )
! 452: Delay /= FDC_FAST_FDC_FACTOR;
! 453:
! 454: // fprintf ( stderr , "fdc state %d delay %d us %d cycles\n" , FDC.Command , Delay_micro , Delay );
! 455: return Delay;
1.1 root 456: }
457:
1.1.1.2 root 458:
459: /*-----------------------------------------------------------------------*/
1.1.1.9 root 460: /**
1.1.1.15! root 461: * Compute the CRC16 of 'nb' bytes stored in 'buf'.
1.1.1.9 root 462: */
1.1.1.15! root 463: static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC )
1.1 root 464: {
1.1.1.15! root 465: int i;
! 466:
! 467: crc16_reset ( pCRC );
! 468: for ( i=0 ; i<nb ; i++ )
! 469: {
! 470: // fprintf ( stderr , "fdc crc16 %d 0x%x\n" , i , buf[ i ] );
! 471: crc16_add_byte ( pCRC , buf[ i ] );
! 472: }
! 473: // fprintf ( stderr , "fdc crc16 0x%x 0x%x\n" , *pCRC>>8 , *pCRC & 0xff );
1.1 root 474: }
475:
1.1.1.2 root 476:
477: /*-----------------------------------------------------------------------*/
1.1.1.9 root 478: /**
1.1.1.15! root 479: * Reset variables used in FDC and DMA emulation
1.1.1.9 root 480: */
1.1.1.15! root 481: void FDC_Reset ( void )
1.1 root 482: {
1.1.1.15! root 483: int i;
1.1.1.6 root 484:
1.1.1.15! root 485: /* Clear out FDC registers */
! 486:
! 487: FDC.CR = 0;
! 488: FDC.TR = 0;
! 489: FDC.SR = 1;
! 490: FDC.DR = 0;
! 491: FDC.STR = 0;
! 492: FDC.StepDirection = 1;
! 493: FDC.ID_FieldLastSector = 1;
! 494:
! 495: FDC.Command = FDCEMU_CMD_NULL; /* FDC emulation command currently being executed */
! 496: FDC.CommandState = FDCEMU_RUN_NULL;
! 497: FDC.CommandType = 0;
! 498:
! 499: FDC_DMA.Status = 1; /* no DMA error and SectorCount=0 */
! 500: FDC_DMA.Mode = 0;
! 501: FDC_DMA.SectorCount = 0;
! 502: FDC_ResetDMA();
! 503:
! 504: for ( i=0 ; i<MAX_FLOPPYDRIVES ; i++ )
! 505: HeadTrack[ i ] = 0; /* Set all drives to track 0 */
1.1 root 506: }
507:
1.1.1.2 root 508:
509: /*-----------------------------------------------------------------------*/
1.1.1.9 root 510: /**
1.1.1.15! root 511: * Reset DMA (clear internal 16 bytes buffer)
1.1.1.9 root 512: *
513: * This is done by 'toggling' bit 8 of the DMA Mode Control register
514: */
1.1.1.15! root 515: static void FDC_ResetDMA ( void )
1.1 root 516: {
1.1.1.15! root 517: int FrameCycles, HblCounterVideo, LineCycles;
1.1 root 518:
1.1.1.15! root 519: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 520: LOG_TRACE(TRACE_FDC, "fdc reset dma VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 521: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 522:
! 523: /* Reset bytes count for current DMA sector */
! 524: FDC_DMA.BytesInSector = DMA_DISK_SECTOR_SIZE;
! 525:
! 526: /* Reset variables used to handle DMA transfer */
! 527: FDC_DMA.PosInBuffer = 0;
! 528: FDC_DMA.PosInBufferTransfer = 0;
! 529: FDC_DMA.BytesToTransfer = 0;
1.1.1.3 root 530:
1.1.1.10 root 531: /* Reset HDC command status */
1.1.1.15! root 532: HDC_ResetCommandStatus();
1.1 root 533: }
534:
1.1.1.2 root 535:
536: /*-----------------------------------------------------------------------*/
1.1.1.9 root 537: /**
1.1.1.15! root 538: * Set DMA Status at $ff8606
1.1.1.9 root 539: *
1.1.1.15! root 540: * Bit 0 - _Error Status (0=Error 1=No erroe)
1.1.1.9 root 541: * Bit 1 - _Sector Count Zero Status (0=Sector Count Zero)
542: * Bit 2 - _Data Request Inactive Status
1.1.1.15! root 543: *
! 544: * FIXME [NP] : is bit 0 really used on ST ? It seems it's always 1 (no DMA error)
1.1.1.9 root 545: */
1.1.1.15! root 546: void FDC_SetDMAStatus ( bool bError )
1.1 root 547: {
1.1.1.15! root 548: /* Set error bit */
1.1.1.6 root 549: if (!bError)
1.1.1.15! root 550: FDC_DMA.Status |= 0x1; /* No Error, set bit 0 */
1.1.1.6 root 551: else
1.1.1.15! root 552: FDC_DMA.Status &= ~0x1; /* Error, clear bit 0 */
1.1 root 553: }
554:
1.1.1.2 root 555:
556: /*-----------------------------------------------------------------------*/
1.1.1.9 root 557: /**
1.1.1.15! root 558: * Init some variables before starting a new DMA transfer.
! 559: * We must store new data just after the most recent bytes that
! 560: * were not yet transferred by the DMA (16 bytes buffer).
! 561: * To avoid writing above the limit of DMADiskWorkSpace, we move
! 562: * the current 16 bytes buffer at the start of DMADiskWorkSpace
! 563: * if some bytes remain to be transferred, this way we never use
! 564: * more than FDC_TRACK_BYTES_STANDARD in DMADiskWorkSpace.
1.1.1.9 root 565: */
1.1.1.15! root 566: static void FDC_DMA_InitTransfer ( void )
1.1 root 567: {
1.1.1.15! root 568: int i;
! 569:
! 570: /* How many bytes remain in the current 16 bytes DMA buffer ? */
! 571: if ( ( FDC_DMA.BytesToTransfer == 0 ) /* DMA buffer is empty */
! 572: || ( FDC_DMA.BytesToTransfer > DMA_DISK_TRANSFER_SIZE ) ) /* Previous DMA transfer did not finish (FDC errror or Force Int command) */
1.1.1.6 root 573: {
1.1.1.15! root 574: FDC_DMA.PosInBuffer = 0; /* Add new data at the start of DMADiskWorkSpace */
! 575: FDC_DMA.PosInBufferTransfer = 0;
! 576: FDC_DMA.BytesToTransfer = 0; /* No more data to transfer from the previous DMA buffer */
1.1.1.6 root 577: }
1.1.1.15! root 578: else /* 16 bytes buffer partially filled */
! 579: {
! 580: for ( i=0 ; i<FDC_DMA.BytesToTransfer ; i++ ) /* Move these bytes at the start of the buffer */
! 581: DMADiskWorkSpace[ i ] = DMADiskWorkSpace[ FDC_DMA.PosInBufferTransfer + i ];
1.1.1.6 root 582:
1.1.1.15! root 583: FDC_DMA.PosInBuffer = FDC_DMA.BytesToTransfer; /* Add new data after the latest bytes stored in the 16 bytes buffer */
! 584: FDC_DMA.PosInBufferTransfer = 0;
! 585: }
1.1 root 586: }
587:
1.1.1.2 root 588:
589: /*-----------------------------------------------------------------------*/
1.1.1.9 root 590: /**
1.1.1.15! root 591: * Transfer 16 bytes from the DMA workspace to the RAM.
! 592: * Instead of handling a real 16 bytes buffer, this implementation moves
! 593: * a 16 bytes window in DMADiskWorkSpace. The current position of this window
! 594: * is stored in FDC_DMA.PosInBufferTransfer and contains the equivalent of the
! 595: * DMA's internal 16 bytes buffer.
! 596: *
! 597: * Return true if there're no more bytes to transfer or false if some
! 598: * bytes can still be tranfered by the DMA.
! 599: *
! 600: * NOTE [NP] : The DMA is connected to the FDC, each time a DRQ is made by the FDC,
! 601: * it's handled by the DMA and stored in the DMA 16 bytes buffer. This means
! 602: * FDC_STR_BIT_LOST_DATA will never be set (but data can be lost if FDC_DMA.SectorCount==0)
1.1.1.14 root 603: */
1.1.1.15! root 604: static bool FDC_DMA_ReadFromFloppy ( void )
1.1.1.14 root 605: {
1.1.1.15! root 606: Uint32 Address;
! 607: //fprintf ( stderr , "dma transfer read count=%d bytes=%d pos=%d\n" , FDC_DMA.SectorCount, FDC_DMA.BytesToTransfer, FDC_DMA.PosInBufferTransfer );
1.1.1.14 root 608:
1.1.1.15! root 609: if ( FDC_DMA.BytesToTransfer < DMA_DISK_TRANSFER_SIZE )
! 610: return true; /* There should be at least 16 bytes to start a DMA transfer */
1.1.1.14 root 611:
1.1.1.15! root 612: if ( FDC_DMA.SectorCount == 0 )
! 613: {
! 614: //FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* If DMA is OFF, data are lost -> Not on the ST */
! 615: FDC_DMA.PosInBufferTransfer += DMA_DISK_TRANSFER_SIZE;
! 616: FDC_DMA.BytesToTransfer -= DMA_DISK_TRANSFER_SIZE;
! 617: return false; /* FDC DMA is off but we still need to read all bytes from the floppy */
! 618: }
! 619:
! 620: /* Transfer data and update DMA address */
! 621: Address = FDC_GetDMAAddress();
! 622: STMemory_SafeCopy ( Address , DMADiskWorkSpace + FDC_DMA.PosInBufferTransfer , DMA_DISK_TRANSFER_SIZE , "FDC DMA data read" );
! 623: FDC_DMA.PosInBufferTransfer += DMA_DISK_TRANSFER_SIZE;
! 624: FDC_DMA.BytesToTransfer -= DMA_DISK_TRANSFER_SIZE;
! 625: FDC_WriteDMAAddress ( Address + DMA_DISK_TRANSFER_SIZE );
! 626:
! 627: /* Update Sector Count */
! 628: FDC_DMA.BytesInSector -= DMA_DISK_TRANSFER_SIZE;
! 629: if ( FDC_DMA.BytesInSector <= 0 )
! 630: {
! 631: FDC_DMA.SectorCount--;
! 632: FDC_DMA.BytesInSector = DMA_DISK_SECTOR_SIZE;
! 633: }
1.1.1.2 root 634:
1.1.1.15! root 635: return false; /* Transfer is not complete */
1.1 root 636: }
637:
1.1.1.2 root 638:
639: /*-----------------------------------------------------------------------*/
1.1.1.9 root 640: /**
1.1.1.15! root 641: * Transfer 16 bytes from the RAM to disk using DMA.
! 642: * This is used to write data to the disk with correct timings
! 643: * by writing blocks of 16 bytes at a time.
! 644: *
! 645: * Return true if there're no more bytes to transfer or false if some
! 646: * bytes can still be tranfered by the DMA.
! 647: *
! 648: * NOTE [NP] : in the case of the emulation in Hatari, the sector is first written
! 649: * to the disk image and this function is just used to increment
! 650: * DMA address at the correct pace to simulate that bytes are written from
! 651: * blocks of 16 bytes handled by the DMA.
1.1.1.9 root 652: */
1.1.1.15! root 653: static bool FDC_DMA_WriteToFloppy ( void )
1.1 root 654: {
1.1.1.15! root 655: Uint32 Address;
! 656: //fprintf ( stderr , "dma transfer write count=%d bytes=%d pos=%d\n" , FDC_DMA.SectorCount, FDC_DMA.BytesToTransfer, FDC_DMA.PosInBufferTransfer );
! 657:
! 658: if ( FDC_DMA.BytesToTransfer < DMA_DISK_TRANSFER_SIZE )
! 659: return true; /* There should be at least 16 bytes to start a DMA transfer */
! 660:
! 661: if ( FDC_DMA.SectorCount == 0 )
! 662: {
! 663: //FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* If DMA is OFF, data are lost -> Not on the ST */
! 664: FDC_DMA.PosInBufferTransfer += DMA_DISK_TRANSFER_SIZE;
! 665: FDC_DMA.BytesToTransfer -= DMA_DISK_TRANSFER_SIZE;
! 666: return false; /* FDC DMA is off but we still need to process the whole sector */
! 667: }
1.1 root 668:
1.1.1.15! root 669: /* Transfer data and update DMA address */
! 670: Address = FDC_GetDMAAddress();
! 671: //STMemory_SafeCopy ( Address , DMADiskWorkSpace + FDC_DMA.PosInBufferTransfer , DMA_DISK_TRANSFER_SIZE , "FDC DMA data read" );
! 672: FDC_DMA.PosInBufferTransfer += DMA_DISK_TRANSFER_SIZE;
! 673: FDC_DMA.BytesToTransfer -= DMA_DISK_TRANSFER_SIZE;
! 674: FDC_WriteDMAAddress ( Address + DMA_DISK_TRANSFER_SIZE );
1.1 root 675:
1.1.1.15! root 676: /* Update Sector Count */
! 677: FDC_DMA.BytesInSector -= DMA_DISK_TRANSFER_SIZE;
! 678: if ( FDC_DMA.BytesInSector <= 0 )
1.1.1.6 root 679: {
1.1.1.15! root 680: FDC_DMA.SectorCount--;
! 681: FDC_DMA.BytesInSector = DMA_DISK_SECTOR_SIZE;
1.1.1.6 root 682: }
683:
1.1.1.15! root 684: return false; /* Transfer is not complete */
! 685:
1.1 root 686: }
687:
1.1.1.2 root 688:
689: /*-----------------------------------------------------------------------*/
1.1.1.9 root 690: /**
1.1.1.15! root 691: * Return device for FDC, check PORTA bits 1,2 (0=on,1=off)
1.1.1.9 root 692: */
1.1.1.15! root 693: static int FDC_FindFloppyDrive ( void )
1.1 root 694: {
1.1.1.6 root 695: /* Check Drive A first */
696: if ((PSGRegisters[PSG_REG_IO_PORTA]&0x2)==0)
1.1.1.15! root 697: return 0; /* Device 0 (A:) */
1.1.1.6 root 698: /* If off, check Drive B */
699: if ((PSGRegisters[PSG_REG_IO_PORTA]&0x4)==0)
1.1.1.15! root 700: return 1; /* Device 1 (B:) */
1.1 root 701:
1.1.1.6 root 702: /* None appear to be selected so default to Drive A */
1.1.1.15! root 703: return 0; /* Device 0 (A:) */
1.1 root 704: }
705:
1.1.1.2 root 706:
707: /*-----------------------------------------------------------------------*/
1.1.1.9 root 708: /**
1.1.1.15! root 709: * Return number of sectors for track/side of current drive
! 710: * TODO [NP] : this function calls Floppy_FindDiskDetails which handles only ST/MSA
! 711: * disk image so far, so this implies all tracks have in fact the same number
! 712: * of sectors (we don't use Track and Side for now)
1.1.1.9 root 713: */
1.1.1.15! root 714: static int FDC_GetSectorsPerTrack ( int Track , int Side )
1.1 root 715: {
1.1.1.15! root 716: Uint16 SectorsPerTrack;
! 717:
! 718: if (EmulationDrives[ FDC_DRIVE ].bDiskInserted)
! 719: {
! 720: Floppy_FindDiskDetails ( EmulationDrives[ FDC_DRIVE ].pBuffer , EmulationDrives[ FDC_DRIVE ].nImageBytes , &SectorsPerTrack , NULL );
! 721: return SectorsPerTrack;
! 722: }
! 723: else
! 724: return 0;
1.1 root 725: }
726:
1.1.1.2 root 727:
1.1.1.15! root 728: static int FDC_GetSidesPerDisk ( int Track )
1.1 root 729: {
1.1.1.15! root 730: Uint16 SidesPerDisk;
! 731:
! 732: if (EmulationDrives[ FDC_DRIVE ].bDiskInserted)
! 733: {
! 734: Floppy_FindDiskDetails ( EmulationDrives[ FDC_DRIVE ].pBuffer , EmulationDrives[ FDC_DRIVE ].nImageBytes , NULL , &SidesPerDisk );
! 735: return SidesPerDisk; /* 1 or 2 */
! 736: }
! 737: else
! 738: return 0;
1.1 root 739: }
740:
1.1.1.2 root 741:
742: /*-----------------------------------------------------------------------*/
1.1.1.9 root 743: /**
1.1.1.15! root 744: * Acknowledge FDC interrupt
1.1.1.9 root 745: */
1.1.1.15! root 746: void FDC_AcknowledgeInterrupt ( void )
1.1.1.8 root 747: {
1.1.1.15! root 748: /* Acknowledge in MFP circuit, pass bit, enable, pending */
! 749: MFP_InputOnChannel(MFP_FDCHDC_BIT,MFP_IERB,&MFP_IPRB);
! 750: MFP_GPIP &= ~0x20;
1.1.1.8 root 751: }
752:
753:
754: /*-----------------------------------------------------------------------*/
1.1.1.9 root 755: /**
1.1.1.15! root 756: * Handle the current FDC command.
! 757: * We use a timer to go from one state to another to emulate the different
! 758: * phases of an FDC command.
! 759: * When the command completes (success or failure), FDC.Command will be
! 760: * set to FDCEMU_CMD_NULL. Until then, this function will be called to
! 761: * handle each state of the command and the corresponding delay in micro
! 762: * seconds.
! 763: * This handler is called after a first delay corresponding to the prepare
! 764: * delay and the eventual motor on delay.
! 765: * Once we reach this point, the current command can not be replaced by
! 766: * another command (except 'Force Interrupt')
1.1.1.9 root 767: */
1.1.1.15! root 768: void FDC_InterruptHandler_Update ( void )
1.1 root 769: {
1.1.1.15! root 770: int Delay_micro = 0;
1.1.1.6 root 771:
1.1.1.15! root 772: CycInt_AcknowledgeInterrupt();
1.1.1.6 root 773:
774: /* Is FDC active? */
1.1.1.15! root 775: if (FDC.Command!=FDCEMU_CMD_NULL)
1.1.1.6 root 776: {
1.1.1.15! root 777: FDC.ReplaceCommandPossible = false;
! 778:
1.1.1.6 root 779: /* Which command are we running? */
1.1.1.15! root 780: switch(FDC.Command)
1.1.1.6 root 781: {
782: case FDCEMU_CMD_RESTORE:
1.1.1.15! root 783: Delay_micro = FDC_UpdateRestoreCmd();
1.1.1.6 root 784: break;
785: case FDCEMU_CMD_SEEK:
1.1.1.15! root 786: Delay_micro = FDC_UpdateSeekCmd();
1.1.1.6 root 787: break;
788: case FDCEMU_CMD_STEP:
1.1.1.15! root 789: Delay_micro = FDC_UpdateStepCmd();
1.1.1.6 root 790: break;
791:
792: case FDCEMU_CMD_READSECTORS:
1.1.1.15! root 793: Delay_micro = FDC_UpdateReadSectorsCmd();
1.1.1.6 root 794: break;
795: case FDCEMU_CMD_WRITESECTORS:
1.1.1.15! root 796: Delay_micro = FDC_UpdateWriteSectorsCmd();
1.1.1.6 root 797: break;
1.1.1.11 root 798:
799: case FDCEMU_CMD_READADDRESS:
1.1.1.15! root 800: Delay_micro = FDC_UpdateReadAddressCmd();
! 801: break;
! 802:
! 803: case FDCEMU_CMD_READTRACK:
! 804: Delay_micro = FDC_UpdateReadTrackCmd();
! 805: break;
! 806:
! 807: case FDCEMU_CMD_MOTOR_STOP:
! 808: Delay_micro = FDC_UpdateMotorStop();
1.1.1.11 root 809: break;
1.1.1.6 root 810: }
1.1.1.15! root 811: }
1.1.1.6 root 812:
1.1.1.15! root 813: if (FDC.Command != FDCEMU_CMD_NULL)
! 814: {
! 815: CycInt_AddAbsoluteInterrupt ( FDC_DelayToCpuCycles ( Delay_micro ) , INT_CPU_CYCLE , INTERRUPT_FDC );
1.1.1.6 root 816: }
1.1.1.15! root 817: }
! 818:
! 819:
! 820: /*-----------------------------------------------------------------------*/
! 821: /**
! 822: * Update the FDC's Status Register.
! 823: * All bits in DisableBits are cleared in STR, then all bits in EnableBits
! 824: * are set in STR.
! 825: */
! 826: static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits )
! 827: {
! 828: FDC.STR &= (~DisableBits); /* Clear bits in DisableBits */
! 829: FDC.STR |= EnableBits; /* Set bits in EnableBits */
! 830: //fprintf ( stderr , "fdc str 0x%x\n" , FDC.STR );
! 831: }
! 832:
! 833:
! 834: /*-----------------------------------------------------------------------*/
! 835: /**
! 836: * Common to all commands once they're completed :
! 837: * - remove busy bit
! 838: * - acknowledge interrupt if necessary
! 839: * - stop motor after 2 sec
! 840: */
! 841: static int FDC_CmdCompleteCommon ( bool DoInt )
! 842: {
! 843: int FrameCycles, HblCounterVideo, LineCycles;
! 844:
! 845: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 846: LOG_TRACE(TRACE_FDC, "fdc complete command VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 847: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 848:
! 849: FDC_Update_STR ( FDC_STR_BIT_BUSY , 0 ); /* Remove busy bit */
1.1.1.9 root 850:
1.1.1.15! root 851: if ( DoInt )
! 852: FDC_AcknowledgeInterrupt();
! 853:
! 854: FDC.Command = FDCEMU_CMD_MOTOR_STOP; /* Fake command to stop the motor */
! 855: return FDC_DELAY_MOTOR_OFF;
! 856: }
! 857:
! 858:
! 859: /*-----------------------------------------------------------------------*/
! 860: /**
! 861: * Verify track after a type I command.
! 862: * The FDC will read the first ID field of the current track and will
! 863: * compare the track number in this ID field with the current Track Register.
! 864: * If they don't match, an error is set with the RNF bit.
! 865: * NOTE : in the case of Hatari when using ST/MSA images, the track is always the correct one,
! 866: * so the verify will always be good (except if no disk is inserted or the physical head is
! 867: * not on the same track as FDC.TR)
! 868: * This function could be improved to support other images format where logical track
! 869: * could be different from physical track (eg Pasti)
! 870: */
! 871: static void FDC_VerifyTrack ( void )
! 872: {
! 873: int FrameCycles, HblCounterVideo, LineCycles;
! 874:
! 875: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 876:
! 877: if ( ! EmulationDrives[FDC_DRIVE].bDiskInserted ) /* Set RNF bit if no disk is inserted */
1.1.1.9 root 878: {
1.1.1.15! root 879: LOG_TRACE(TRACE_FDC, "fdc type I verify track failed no disk in drive=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 880: FDC_DRIVE , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 881:
! 882: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
! 883: return;
! 884: }
! 885:
! 886: /* In the case of Hatari when using ST/MSA images, the physical track and the track register */
! 887: /* should always be the same. Else, it means TR was not correctly set before running the type I command */
! 888: if ( HeadTrack[ FDC_DRIVE ] != FDC.TR )
! 889: {
! 890: LOG_TRACE(TRACE_FDC, "fdc type I verify track failed TR=0x%x head=0x%x drive=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 891: FDC.TR , HeadTrack[ FDC_DRIVE ] , FDC_DRIVE ,
! 892: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 893:
! 894: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
! 895: return;
1.1.1.9 root 896: }
1.1.1.15! root 897:
! 898: /* In the case of Hatari when using ST/MSA images, the track is always the correct one */
! 899: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* remove RNF bit */
! 900: }
! 901:
! 902:
! 903: /*-----------------------------------------------------------------------*/
! 904: /**
! 905: * When the motor really stops (2 secs after the last command), clear all related bits in SR
! 906: */
! 907: static int FDC_UpdateMotorStop ( void )
! 908: {
! 909: int FrameCycles, HblCounterVideo, LineCycles;
! 910:
! 911: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 912: LOG_TRACE(TRACE_FDC, "fdc motor stopped VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 913: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 914:
! 915: FDC_Update_STR ( FDC_STR_BIT_MOTOR_ON | FDC_STR_BIT_SPIN_UP , 0 ); /* Unset motor and spinup bits */
! 916: /* [NP] FIXME should we clear spin up here or only when the motor is started again ? */
! 917:
! 918: FDC.Command = FDCEMU_CMD_NULL; /* Motor stopped, this is the last state */
! 919: return 0;
1.1 root 920: }
921:
1.1.1.2 root 922:
923: /*-----------------------------------------------------------------------*/
1.1.1.9 root 924: /**
925: * Run 'RESTORE' command
926: */
1.1.1.15! root 927: static int FDC_UpdateRestoreCmd ( void )
1.1 root 928: {
1.1.1.15! root 929: int Delay_micro = 0;
! 930:
! 931: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* at this point, spin up sequence is ok */
! 932:
1.1.1.6 root 933: /* Which command is running? */
1.1.1.15! root 934: switch (FDC.CommandState)
1.1.1.6 root 935: {
936: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO:
1.1.1.15! root 937: /* The FDC will try 255 times to reach track 0 using step out signals */
! 938: /* If track 0 signal is not detected after 255 attempts, the command is interrupted */
! 939: /* and FDC_STR_BIT_RNF is set in the Status Register. */
! 940: /* This will never happen in the case of Hatari, because the physical track can't go */
! 941: /* beyond track FDC_PHYSICAL_MAX_TRACK (=90) */
! 942: /* TR should be set to 255 once the spin-up sequence is made and the command */
! 943: /* can't be interrupted anymore by another command (else TR value will be wrong */
! 944: /* for other type I commands) */
! 945: FDC.TR = 0xff;
! 946: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP; /* continue in the _LOOP state */
! 947: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP:
! 948: if ( FDC.TR == 0 ) /* Track 0 not reached after 255 attempts ? */
! 949: { /* (this should never happen in the case of Hatari) */
! 950: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
! 951: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
! 952: Delay_micro = FDC_CmdCompleteCommon( true );
! 953: }
! 954:
! 955: if ( HeadTrack[ FDC_DRIVE ] != 0 ) /* Are we at track zero ? */
! 956: {
! 957: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
! 958: FDC.TR--; /* One less attempt */
! 959: HeadTrack[ FDC_DRIVE ]--; /* Move physical head */
! 960: Delay_micro = FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000;
! 961: }
1.1.1.6 root 962: else
963: {
1.1.1.15! root 964: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
! 965: FDC.TR = 0; /* Update Track Register to 0 */
! 966: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
! 967: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
1.1.1.6 root 968: }
969: break;
970: case FDCEMU_RUN_RESTORE_COMPLETE:
1.1.1.15! root 971: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
! 972: FDC_VerifyTrack();
! 973: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 974: break;
975: }
1.1.1.15! root 976:
! 977: return Delay_micro;
1.1 root 978: }
979:
1.1.1.2 root 980:
981: /*-----------------------------------------------------------------------*/
1.1.1.9 root 982: /**
983: * Run 'SEEK' command
984: */
1.1.1.15! root 985: static int FDC_UpdateSeekCmd ( void )
1.1 root 986: {
1.1.1.15! root 987: int Delay_micro = 0;
! 988:
! 989: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* at this point, spin up sequence is ok */
! 990:
1.1.1.6 root 991: /* Which command is running? */
1.1.1.15! root 992: switch (FDC.CommandState)
1.1.1.6 root 993: {
994: case FDCEMU_RUN_SEEK_TOTRACK:
1.1.1.15! root 995: if ( FDC.TR == FDC.DR ) /* Are we at the selected track ? */
! 996: {
! 997: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
! 998: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
! 999: }
1.1.1.6 root 1000: else
1001: {
1.1.1.15! root 1002: if ( FDC.DR < FDC.TR ) /* Set StepDirection to the correct value */
! 1003: FDC.StepDirection = -1;
! 1004: else
! 1005: FDC.StepDirection = 1;
! 1006:
! 1007: /* Move head by one track depending on FDC.StepDirection and update Track Register */
! 1008: FDC.TR += FDC.StepDirection;
! 1009:
! 1010: if ( ( HeadTrack[ FDC_DRIVE ] == FDC_PHYSICAL_MAX_TRACK ) && ( FDC.StepDirection == 1 ) )
! 1011: Delay_micro = FDC_DELAY_COMMAND_COMPLETE; /* No delay if trying to go after max track */
! 1012:
! 1013: else if ( ( HeadTrack[ FDC_DRIVE ] == 0 ) && ( FDC.StepDirection == -1 ) )
! 1014: {
! 1015: FDC.TR = 0; /* If we reach track 0, we stop there */
! 1016: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
! 1017: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
! 1018: }
! 1019:
1.1.1.6 root 1020: else
1.1.1.15! root 1021: {
! 1022: HeadTrack[ FDC_DRIVE ] += FDC.StepDirection; /* Move physical head */
! 1023: Delay_micro = FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000;
! 1024: }
1.1.1.6 root 1025: }
1.1.1.15! root 1026:
! 1027: if ( HeadTrack[ FDC_DRIVE ] == 0 )
! 1028: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
! 1029: else
! 1030: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
! 1031:
1.1.1.6 root 1032: break;
1033: case FDCEMU_RUN_SEEK_COMPLETE:
1.1.1.15! root 1034: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
! 1035: FDC_VerifyTrack();
! 1036: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 1037: break;
1038: }
1.1.1.15! root 1039:
! 1040: return Delay_micro;
1.1 root 1041: }
1042:
1.1.1.2 root 1043:
1044: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1045: /**
1046: * Run 'STEP' command
1047: */
1.1.1.15! root 1048: static int FDC_UpdateStepCmd ( void )
1.1 root 1049: {
1.1.1.15! root 1050: int Delay_micro = 0;
! 1051:
! 1052: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* at this point, spin up sequence is ok */
! 1053:
1.1.1.6 root 1054: /* Which command is running? */
1.1.1.15! root 1055: switch (FDC.CommandState)
1.1.1.6 root 1056: {
1057: case FDCEMU_RUN_STEP_ONCE:
1.1.1.15! root 1058: /* Move head by one track depending on FDC.StepDirection */
! 1059: if ( FDC.CR & FDC_COMMAND_BIT_UPDATE_TRACK )
! 1060: FDC.TR += FDC.StepDirection; /* Update Track Register */
! 1061:
! 1062: if ( ( HeadTrack[ FDC_DRIVE ] == FDC_PHYSICAL_MAX_TRACK ) && ( FDC.StepDirection == 1 ) )
! 1063: Delay_micro = FDC_DELAY_COMMAND_COMPLETE; /* No delay if trying to go after max track */
! 1064:
! 1065: else if ( ( HeadTrack[ FDC_DRIVE ] == 0 ) && ( FDC.StepDirection == -1 ) )
! 1066: Delay_micro = FDC_DELAY_COMMAND_COMPLETE; /* No delay if trying to go before track 0 */
1.1.1.6 root 1067:
1.1.1.15! root 1068: else
! 1069: {
! 1070: HeadTrack[ FDC_DRIVE ] += FDC.StepDirection; /* Move physical head */
! 1071: Delay_micro = FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000;
! 1072: }
! 1073:
! 1074: if ( HeadTrack[ FDC_DRIVE ] == 0 )
! 1075: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
! 1076: else
! 1077: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
! 1078:
! 1079: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
1.1.1.6 root 1080: break;
1081: case FDCEMU_RUN_STEP_COMPLETE:
1.1.1.15! root 1082: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
! 1083: FDC_VerifyTrack();
! 1084: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 1085: break;
1086: }
1.1.1.15! root 1087:
! 1088: return Delay_micro;
1.1 root 1089: }
1090:
1.1.1.2 root 1091:
1092: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1093: /**
1.1.1.15! root 1094: * Run 'READ SECTOR/S' command
1.1.1.9 root 1095: */
1.1.1.15! root 1096: static int FDC_UpdateReadSectorsCmd ( void )
1.1 root 1097: {
1.1.1.15! root 1098: int Delay_micro = 0;
! 1099: int FrameCycles, HblCounterVideo, LineCycles;
! 1100: int SectorSize;
! 1101:
! 1102: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 1103:
! 1104:
1.1.1.6 root 1105: /* Which command is running? */
1.1.1.15! root 1106: switch (FDC.CommandState)
1.1.1.6 root 1107: {
1.1.1.15! root 1108: case FDCEMU_RUN_READSECTORS_READDATA:
! 1109: /* TODO : for better FDC's emulation , we should measure the time it takes to spin the disk */
! 1110: /* until we reach the next sector header. In the best case, the head would be just at the start */
! 1111: /* of the sector header, which would mean 6 bytes to be read. */
! 1112: /* So, the delay will be at least 6 bytes; during that time, FDC.SR can be changed (Delirious Demo 4) */
! 1113: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER;
! 1114: Delay_micro = FDC_TRANSFER_BYTES_US ( 6 ); /* Min delay to read 3xA1, FE, TR, SR */
! 1115: break;
! 1116: case FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER:
! 1117: /* TODO : on a real FDC we should compare the sector header at the current */
! 1118: /* spin's position to see if it's the same as FDC.SR. If not, we should wait */
! 1119: /* for the next sector header and check again. After 5 revolutions, set RNF */
! 1120:
! 1121: /* Read a single sector into temporary buffer (512 bytes for ST/MSA) */
! 1122: FDC_DMA_InitTransfer (); /* Update FDC_DMA.PosInBuffer */
! 1123: if ( FDC_ReadSectorFromFloppy ( DMADiskWorkSpace + FDC_DMA.PosInBuffer , FDC.SR , &SectorSize ) )
! 1124: {
! 1125: FDC_DMA.BytesToTransfer += SectorSize; /* 512 bytes per sector for ST/MSA disk images */
! 1126: FDC_DMA.PosInBuffer += SectorSize;
! 1127: FDC.ID_FieldLastSector = FDC.SR;
! 1128:
! 1129: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_DMA;
! 1130: Delay_micro = FDC_DELAY_TRANSFER_DMA_16; /* Transfer blocks of 16 bytes from the sector we just read */
! 1131: }
! 1132: else /* Sector FDC.SR was not found */
! 1133: {
! 1134: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
! 1135: Delay_micro = FDC_DELAY_RNF;
! 1136: }
! 1137: break;
! 1138: case FDCEMU_RUN_READSECTORS_READDATA_DMA:
! 1139: if ( ! FDC_DMA_ReadFromFloppy () )
! 1140: {
! 1141: Delay_micro = FDC_DELAY_TRANSFER_DMA_16; /* Continue transferring blocks of 16 bytes */
! 1142: }
! 1143: else /* Sector completly transferred, check for multi bit */
! 1144: {
! 1145: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
! 1146: {
! 1147: FDC.SR++; /* Try to read next sector and set RNF if not possible */
! 1148: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA;
! 1149: Delay_micro = FDC_DELAY_COMMAND_IMMEDIATE;
! 1150: }
! 1151: else /* Multi=0, stop here with no error */
! 1152: {
! 1153: FDC.CommandState = FDCEMU_RUN_READSECTORS_COMPLETE;
! 1154: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
! 1155: }
! 1156: }
! 1157: break;
! 1158: case FDCEMU_RUN_READSECTORS_RNF:
! 1159: LOG_TRACE(TRACE_FDC, "fdc type II read sector=%d track=%d drive=%d RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1160: FDC.SR , HeadTrack[ FDC_DRIVE ] , FDC_DRIVE , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.6 root 1161:
1.1.1.15! root 1162: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
! 1163: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 1164: break;
1.1.1.15! root 1165: case FDCEMU_RUN_READSECTORS_COMPLETE:
! 1166: Delay_micro = FDC_CmdCompleteCommon( true );
! 1167: break;
! 1168: }
! 1169:
! 1170: return Delay_micro;
! 1171: }
! 1172:
! 1173:
! 1174: /*-----------------------------------------------------------------------*/
! 1175: /**
! 1176: * Run 'WRITE SECTOR/S' command
! 1177: */
! 1178: static int FDC_UpdateWriteSectorsCmd ( void )
! 1179: {
! 1180: int Delay_micro = 0;
! 1181: int FrameCycles, HblCounterVideo, LineCycles;
! 1182: int SectorSize;
! 1183:
! 1184: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 1185:
! 1186: if ( Floppy_IsWriteProtected ( FDC_DRIVE ) )
! 1187: {
! 1188: LOG_TRACE(TRACE_FDC, "fdc type II write sector=%d track=%d drive=%d WPRT VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1189: FDC.SR , HeadTrack[ FDC_DRIVE ] , FDC_DRIVE , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1190:
! 1191: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
! 1192: Delay_micro = FDC_CmdCompleteCommon( true );
! 1193: }
! 1194: else
! 1195: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit */
! 1196:
! 1197:
! 1198: /* Which command is running? */
! 1199: switch (FDC.CommandState)
! 1200: {
! 1201: case FDCEMU_RUN_WRITESECTORS_WRITEDATA:
! 1202: /* TODO : for better FDC's emulation , we should measure the time it takes to spin the disk */
! 1203: /* until we reach the next sector header. In the best case, the head would be just at the start */
! 1204: /* of the sector header, which would mean 6 bytes to be read. */
! 1205: /* So, the delay will be at least 6 bytes; during that time, FDC.SR can be changed (Delirious Demo 4) */
! 1206: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER;
! 1207: Delay_micro = FDC_TRANSFER_BYTES_US ( 6 ); /* Min delay to read 3xA1, FE, TR, SR */
! 1208: break;
! 1209: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER:
! 1210: /* TODO : on a real FDC we should compare the sector header at the current */
! 1211: /* spin's position to see if it's the same as FDC.SR. If not, we should wait */
! 1212: /* for the next sector header and check again. After 5 revolutions, set RNF */
! 1213:
! 1214: /* Write a single sector from RAM (512 bytes for ST/MSA) */
! 1215: FDC_DMA_InitTransfer (); /* Update FDC_DMA.PosInBuffer */
! 1216: if ( FDC_WriteSectorToFloppy ( FDC_DMA.SectorCount , FDC.SR , &SectorSize ) )
! 1217: {
! 1218: FDC_DMA.BytesToTransfer += SectorSize; /* 512 bytes per sector for ST/MSA disk images */
! 1219: FDC_DMA.PosInBuffer += SectorSize;
! 1220: FDC.ID_FieldLastSector = FDC.SR;
! 1221:
! 1222: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_DMA;
! 1223: Delay_micro = FDC_DELAY_TRANSFER_DMA_16; /* Transfer blocks of 16 bytes from the sector we just wrote */
! 1224: }
! 1225: else /* Sector FDC.SR was not found */
! 1226: {
! 1227: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
! 1228: Delay_micro = FDC_DELAY_RNF;
! 1229: }
! 1230: break;
! 1231: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_DMA:
! 1232: if ( ! FDC_DMA_WriteToFloppy () )
! 1233: {
! 1234: Delay_micro = FDC_DELAY_TRANSFER_DMA_16; /* Continue transferring blocks of 16 bytes */
! 1235: }
! 1236: else /* Sector completly transferred, check for multi bit */
! 1237: {
! 1238: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
! 1239: {
! 1240: FDC.SR++; /* Try to write next sector and set RNF if not possible */
! 1241: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA;
! 1242: Delay_micro = FDC_DELAY_COMMAND_IMMEDIATE;
! 1243: }
! 1244: else /* Multi=0, stop here with no error */
! 1245: {
! 1246: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_COMPLETE;
! 1247: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
! 1248: }
! 1249: }
! 1250: break;
! 1251: case FDCEMU_RUN_WRITESECTORS_RNF:
! 1252: LOG_TRACE(TRACE_FDC, "fdc type II write sector=%d track=%d drive=%d RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1253: FDC.SR , HeadTrack[ FDC_DRIVE ] , FDC_DRIVE , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1254:
! 1255: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
! 1256: Delay_micro = FDC_CmdCompleteCommon( true );
! 1257: break;
! 1258: case FDCEMU_RUN_WRITESECTORS_COMPLETE:
! 1259: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 1260: break;
1261: }
1.1.1.15! root 1262:
! 1263: return Delay_micro;
1.1 root 1264: }
1265:
1.1.1.2 root 1266:
1267: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1268: /**
1.1.1.15! root 1269: * Run 'READ ADDRESS' command
1.1.1.9 root 1270: */
1.1.1.15! root 1271: static int FDC_UpdateReadAddressCmd ( void )
1.1 root 1272: {
1.1.1.15! root 1273: int Delay_micro = 0;
! 1274: Uint16 CRC;
! 1275: Uint8 buf[ 4+6 ];
! 1276: Uint8 *p;
! 1277: int Sector;
! 1278:
! 1279: if ( ! EmulationDrives[FDC_DRIVE].bDiskInserted ) /* Set RNF bit if no disk is inserted */
! 1280: {
! 1281: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
! 1282: Delay_micro = FDC_CmdCompleteCommon( true );
! 1283: }
! 1284:
! 1285:
1.1.1.6 root 1286: /* Which command is running? */
1.1.1.15! root 1287: switch (FDC.CommandState)
1.1.1.6 root 1288: {
1.1.1.15! root 1289: case FDCEMU_RUN_READADDRESS:
! 1290: Sector = FDC.ID_FieldLastSector + 1; /* Increase sector from latest ID Field */
! 1291: if ( Sector > FDC_GetSectorsPerTrack ( HeadTrack[ FDC_DRIVE ] , FDC_SIDE ) )
! 1292: Sector = 1;
! 1293:
! 1294: /* In the case of Hatari, only ST/MSA images are supported, so we build */
! 1295: /* a simplified ID field based on current track/sector/side */
! 1296: p = buf;
! 1297: *p++ = 0xa1; /* SYNC bytes and IAM byte are included in the CRC */
! 1298: *p++ = 0xa1;
! 1299: *p++ = 0xa1;
! 1300: *p++ = 0xfe;
! 1301: *p++ = HeadTrack[ FDC_DRIVE ];
! 1302: FDC.SR = HeadTrack[ FDC_DRIVE ]; /* The 1st byte of the ID field is also copied into Sector Register */
! 1303: *p++ = FDC_SIDE;
! 1304: *p++ = Sector;
! 1305: *p++ = FDC_SECTOR_SIZE_512; /* ST/MSA images are 512 bytes per sector */
! 1306:
! 1307: FDC_CRC16 ( buf , 8 , &CRC );
! 1308:
! 1309: *p++ = CRC >> 8;
! 1310: *p++ = CRC & 0xff;
! 1311:
! 1312: FDC_DMA_InitTransfer (); /* Update FDC_DMA.PosInBuffer */
! 1313: memcpy ( DMADiskWorkSpace + FDC_DMA.PosInBuffer , buf + 4 , 6 ); /* Don't return the 3 x $A1 and $FE in the Address Field */
! 1314: FDC_DMA.BytesToTransfer += 6; /* 6 bytes per ID field */
! 1315: FDC_DMA.PosInBuffer += 6;
! 1316:
! 1317: FDC.CommandState = FDCEMU_RUN_READADDRESS_DMA;
! 1318:
! 1319: /* Very simplified method to get correct timings when doing a Read Address just after an index pulse */
! 1320: /* or after a previous Read Address. The right method is to use a timer to count bytes since the */
! 1321: /* latest index pulse, but this would add too much complexity just to handle ST/MSA disk images. */
! 1322: /* So we use a simpler method where ID_FieldLastSector is set to 0 when we simulate an index pulse. */
! 1323: /* The number of bytes to skip should be exactly the same as the GAPs used in ReadTrack */
! 1324: /* (allow Procopy to analyze an ST/MSA disk with the correct timings) */
! 1325: if ( FDC.ID_FieldLastSector == 0 ) /* First Read Address just after an index pulse */
! 1326: Delay_micro = FDC_TRANSFER_BYTES_US ( 72 + 3 + 1 + 6 ); /* Skip 72+3+1 bytes then read 6 bytes */
! 1327: else /* Read Address after a previous sector was just read */
! 1328: Delay_micro = FDC_TRANSFER_BYTES_US ( 614 + 6 ); /* Skip 614 bytes then read 6 bytes */
1.1.1.6 root 1329:
1.1.1.15! root 1330: FDC.ID_FieldLastSector = Sector;
1.1.1.6 root 1331: break;
1.1.1.15! root 1332: case FDCEMU_RUN_READADDRESS_DMA:
! 1333: FDC_DMA_ReadFromFloppy (); /* Transfer bytes if 16 bytes or more are in the DMA buffer */
! 1334:
! 1335: FDC.CommandState = FDCEMU_RUN_READADDRESS_COMPLETE;
! 1336: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
! 1337: break;
! 1338: case FDCEMU_RUN_READADDRESS_COMPLETE:
! 1339: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 1340: break;
1341: }
1.1.1.15! root 1342:
! 1343: return Delay_micro;
1.1 root 1344: }
1345:
1.1.1.2 root 1346:
1347: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1348: /**
1.1.1.15! root 1349: * Run 'READ TRACK' command
1.1.1.9 root 1350: */
1.1.1.15! root 1351: static int FDC_UpdateReadTrackCmd ( void )
1.1 root 1352: {
1.1.1.15! root 1353: int Delay_micro = 0;
! 1354: Uint16 CRC;
! 1355: Uint8 *buf;
! 1356: Uint8 *buf_crc;
! 1357: int Sector;
! 1358: int SectorSize;
! 1359: int i;
! 1360:
! 1361: if ( ! EmulationDrives[FDC_DRIVE].bDiskInserted ) /* Set RNF bit if no disk is inserted */
! 1362: {
! 1363: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* [NP] Should we return random bytes instead ? */
! 1364: Delay_micro = FDC_CmdCompleteCommon( true );
! 1365: }
! 1366:
! 1367:
1.1.1.6 root 1368: /* Which command is running? */
1.1.1.15! root 1369: switch (FDC.CommandState)
1.1.1.6 root 1370: {
1.1.1.15! root 1371: case FDCEMU_RUN_READTRACK:
! 1372:
! 1373: /* Build the track data */
! 1374: FDC_DMA_InitTransfer (); /* Update FDC_DMA.PosInBuffer */
! 1375: buf = DMADiskWorkSpace + FDC_DMA.PosInBuffer;
1.1.1.6 root 1376:
1.1.1.15! root 1377: if ( ( FDC_SIDE == 1 ) /* Try to read side 1 on a disk that doesn't have 2 sides */
! 1378: && ( FDC_GetSidesPerDisk ( HeadTrack[ FDC_DRIVE ] ) != 2 ) )
! 1379: {
! 1380: for ( i=0 ; i<FDC_TRACK_BYTES_STANDARD ; i++ )
! 1381: *buf++ = rand() & 0xff; /* Fill the track buffer with random bytes */
1.1.1.6 root 1382: }
1.1.1.15! root 1383:
! 1384: else /* Track/side available in the disk image */
1.1.1.6 root 1385: {
1.1.1.15! root 1386: for ( i=0 ; i<60 ; i++ ) *buf++ = 0x4e; /* GAP1 */
! 1387:
! 1388: for ( Sector=1 ; Sector <= FDC_GetSectorsPerTrack ( HeadTrack[ FDC_DRIVE ] , FDC_SIDE ) ; Sector++ )
! 1389: {
! 1390: for ( i=0 ; i<12 ; i++ ) *buf++ = 0x00; /* GAP2 */
! 1391:
! 1392: buf_crc = buf;
! 1393: for ( i=0 ; i<3 ; i++ ) *buf++ = 0xa1; /* SYNC (write $F5) */
! 1394: *buf++ = 0xfe; /* Index Address Mark */
! 1395: *buf++ = HeadTrack[ FDC_DRIVE ]; /* Track */
! 1396: *buf++ = FDC_SIDE; /* Side */
! 1397: *buf++ = Sector; /* Sector */
! 1398: FDC.ID_FieldLastSector = Sector;
! 1399: *buf++ = FDC_SECTOR_SIZE_512; /* 512 bytes/sector for ST/MSA */
! 1400: FDC_CRC16 ( buf_crc , buf - buf_crc , &CRC );
! 1401: *buf++ = CRC >> 8; /* CRC1 (write $F7) */
! 1402: *buf++ = CRC & 0xff; /* CRC2 */
! 1403:
! 1404: for ( i=0 ; i<22 ; i++ ) *buf++ = 0x4e; /* GAP3a */
! 1405: for ( i=0 ; i<12 ; i++ ) *buf++ = 0x00; /* GAP3b */
! 1406:
! 1407: buf_crc = buf;
! 1408: for ( i=0 ; i<3 ; i++ ) *buf++ = 0xa1; /* SYNC (write $F5) */
! 1409: *buf++ = 0xfb; /* Data Address Mark */
! 1410:
! 1411: if ( ! FDC_ReadSectorFromFloppy ( buf , Sector , &SectorSize ) ) /* Read a single 512 bytes sector into temporary buffer */
! 1412: {
! 1413: /* Do nothing in case of error, we could put some random bytes, but this case should */
! 1414: /* not happen with ST/MSA disk images, all sectors should be present on each track. */
! 1415: }
! 1416: buf += SectorSize;
! 1417:
! 1418: FDC_CRC16 ( buf_crc , buf - buf_crc , &CRC );
! 1419: *buf++ = CRC >> 8; /* CRC1 (write $F7) */
! 1420: *buf++ = CRC & 0xff; /* CRC2 */
! 1421:
! 1422: for ( i=0 ; i<40 ; i++ ) *buf++ = 0x4e; /* GAP4 */
! 1423: }
! 1424:
! 1425: while ( buf < DMADiskWorkSpace + FDC_DMA.PosInBuffer + FDC_TRACK_BYTES_STANDARD ) /* Complete the track buffer */
! 1426: *buf++ = 0x4e; /* GAP5 */
1.1.1.6 root 1427: }
1.1 root 1428:
1.1.1.15! root 1429: /* Transfer Track data to RAM using DMA */
! 1430: FDC_DMA.BytesToTransfer += FDC_TRACK_BYTES_STANDARD;
! 1431: FDC_DMA.PosInBuffer += FDC_TRACK_BYTES_STANDARD;
1.1.1.2 root 1432:
1.1.1.15! root 1433: FDC.CommandState = FDCEMU_RUN_READTRACK_DMA;
! 1434: Delay_micro = FDC_DELAY_TRANSFER_DMA_16; /* Transfer blocks of 16 bytes from the track we just read */
! 1435: break;
! 1436: case FDCEMU_RUN_READTRACK_DMA:
! 1437: if ( ! FDC_DMA_ReadFromFloppy () )
1.1.1.6 root 1438: {
1.1.1.15! root 1439: Delay_micro = FDC_DELAY_TRANSFER_DMA_16; /* Continue transferring blocks of 16 bytes */
1.1.1.6 root 1440: }
1.1.1.15! root 1441: else /* Track completly transferred */
1.1.1.6 root 1442: {
1.1.1.15! root 1443: FDC.CommandState = FDCEMU_RUN_READTRACK_COMPLETE;
! 1444: Delay_micro = FDC_DELAY_COMMAND_COMPLETE;
1.1.1.6 root 1445: }
1446: break;
1.1.1.15! root 1447: case FDCEMU_RUN_READTRACK_COMPLETE:
! 1448: Delay_micro = FDC_CmdCompleteCommon( true );
1.1.1.6 root 1449: break;
1450: }
1.1.1.15! root 1451:
! 1452: return Delay_micro;
1.1 root 1453: }
1454:
1.1.1.2 root 1455:
1456: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1457: /**
1.1.1.15! root 1458: * Common to types I, II and III
! 1459: *
! 1460: * Start motor / spin up sequence if needed
1.1.1.11 root 1461: */
1.1.1.15! root 1462:
! 1463: static int FDC_Check_MotorON ( Uint8 FDC_CR )
1.1.1.11 root 1464: {
1.1.1.15! root 1465: int FrameCycles, HblCounterVideo, LineCycles;
! 1466:
! 1467: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 1468:
! 1469: if ( ( ( FDC_CR & FDC_COMMAND_BIT_MOTOR_ON ) == 0 ) /* Command wants motor on / spin up */
! 1470: && ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) == 0 ) ) /* Motor on not enabled yet */
1.1.1.11 root 1471: {
1.1.1.15! root 1472: LOG_TRACE(TRACE_FDC, "fdc start motor VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1473: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1474: FDC_Update_STR ( FDC_STR_BIT_SPIN_UP , FDC_STR_BIT_MOTOR_ON ); /* Unset spin up bit and set motor bit */
! 1475: return FDC_DELAY_MOTOR_ON; /* Motor's delay */
1.1.1.11 root 1476: }
1.1.1.15! root 1477:
! 1478: /* Other cases : set bit in STR and don't add delay */
! 1479: LOG_TRACE(TRACE_FDC, "fdc motor already on VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1480: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1481: FDC_Update_STR ( 0 , FDC_STR_BIT_MOTOR_ON );
! 1482: return 0;
1.1.1.11 root 1483: }
1484:
1485:
1486: /*-----------------------------------------------------------------------*/
1487: /**
1.1.1.9 root 1488: * Type I Commands
1489: *
1490: * Restore, Seek, Step, Step-In and Step-Out
1491: */
1.1 root 1492:
1.1.1.2 root 1493:
1494: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1495: static int FDC_TypeI_Restore(void)
1.1 root 1496: {
1.1.1.15! root 1497: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1498:
1499: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1500:
1.1.1.15! root 1501: LOG_TRACE(TRACE_FDC, "fdc type I restore drive=%d current_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1502: FDC_DRIVE , HeadTrack[ FDC_DRIVE ] , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1503:
1.1.1.6 root 1504: /* Set emulation to seek to track zero */
1.1.1.15! root 1505: FDC.Command = FDCEMU_CMD_RESTORE;
! 1506: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO;
! 1507:
! 1508: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 1509:
1.1.1.15! root 1510: return FDC_DELAY_TYPE_I_PREPARE;
1.1 root 1511: }
1512:
1.1.1.2 root 1513:
1514: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1515: static int FDC_TypeI_Seek ( void )
1.1 root 1516: {
1.1.1.15! root 1517: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1518:
1519: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1520:
1.1.1.15! root 1521: LOG_TRACE(TRACE_FDC, "fdc type I seek track=0x%x drive=%d current_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1522: FDC.DR, FDC_DRIVE , HeadTrack[ FDC_DRIVE ] , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1523:
1.1.1.6 root 1524: /* Set emulation to seek to chosen track */
1.1.1.15! root 1525: FDC.Command = FDCEMU_CMD_SEEK;
! 1526: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK;
! 1527:
! 1528: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 1529:
1.1.1.15! root 1530: return FDC_DELAY_TYPE_I_PREPARE;
1.1 root 1531: }
1532:
1.1.1.2 root 1533:
1534: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1535: static int FDC_TypeI_Step ( void )
1.1 root 1536: {
1.1.1.15! root 1537: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1538:
1539: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1540:
1.1.1.15! root 1541: LOG_TRACE(TRACE_FDC, "fdc type I step %d drive=%d current_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1542: FDC.StepDirection, FDC_DRIVE , HeadTrack[ FDC_DRIVE ] , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1543:
! 1544: /* Set emulation to step (using same direction as latest seek executed, ie 'FDC.StepDirection') */
! 1545: FDC.Command = FDCEMU_CMD_STEP;
! 1546: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
1.1.1.10 root 1547:
1.1.1.15! root 1548: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 1549:
1.1.1.15! root 1550: return FDC_DELAY_TYPE_I_PREPARE;
1.1 root 1551: }
1552:
1.1.1.2 root 1553:
1554: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1555: static int FDC_TypeI_StepIn(void)
1.1 root 1556: {
1.1.1.15! root 1557: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1558:
1559: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1560:
1.1.1.15! root 1561: LOG_TRACE(TRACE_FDC, "fdc type I step in drive=%d current_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1562: FDC_DRIVE , HeadTrack[ FDC_DRIVE ] , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1563:
1.1.1.15! root 1564: /* Set emulation to step in (direction = +1) */
! 1565: FDC.Command = FDCEMU_CMD_STEP;
! 1566: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
! 1567: FDC.StepDirection = 1; /* Increment track*/
1.1 root 1568:
1.1.1.15! root 1569: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
! 1570:
! 1571: return FDC_DELAY_TYPE_I_PREPARE;
1.1 root 1572: }
1573:
1.1.1.2 root 1574:
1575: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1576: static int FDC_TypeI_StepOut ( void )
1.1 root 1577: {
1.1.1.15! root 1578: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1579:
1580: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1581:
1.1.1.15! root 1582: LOG_TRACE(TRACE_FDC, "fdc type I step out drive=%d current_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1583: FDC_DRIVE , HeadTrack[ FDC_DRIVE ] , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1584:
! 1585: /* Set emulation to step out (direction = -1) */
! 1586: FDC.Command = FDCEMU_CMD_STEP;
! 1587: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
! 1588: FDC.StepDirection = -1; /* Decrement track */
1.1.1.10 root 1589:
1.1.1.15! root 1590: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 1591:
1.1.1.15! root 1592: return FDC_DELAY_TYPE_I_PREPARE;
1.1 root 1593: }
1594:
1.1.1.2 root 1595:
1596: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1597: /**
1598: * Type II Commands
1599: *
1.1.1.15! root 1600: * Read Sector, Write Sector
1.1.1.9 root 1601: */
1.1 root 1602:
1.1.1.2 root 1603:
1604: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1605: static int FDC_TypeII_ReadSector ( void )
1.1 root 1606: {
1.1.1.15! root 1607: int Delay_micro = 0;
! 1608: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1609:
1610: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1611:
1.1.1.15! root 1612: LOG_TRACE(TRACE_FDC, "fdc type II read sector sector=0x%x multi=%s track=0x%x side=%d drive=%d dmasector=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1613: FDC.SR, ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" , HeadTrack[ FDC_DRIVE ] , FDC_SIDE, FDC_DRIVE , FDC_DMA.SectorCount ,
! 1614: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.12 root 1615:
1.1.1.15! root 1616: /* Set emulation to read sector(s) */
! 1617: FDC.Command = FDCEMU_CMD_READSECTORS;
! 1618: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA;
1.1.1.10 root 1619:
1.1.1.15! root 1620: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
! 1621: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
1.1 root 1622:
1.1.1.15! root 1623: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
! 1624: Delay_micro = FDC_DELAY_HEAD_LOAD;
! 1625:
! 1626: return FDC_DELAY_TYPE_II_PREPARE + Delay_micro;
1.1 root 1627: }
1628:
1.1.1.2 root 1629:
1630: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1631: static int FDC_TypeII_WriteSector ( void )
1.1 root 1632: {
1.1.1.15! root 1633: int Delay_micro = 0;
! 1634: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1635:
1636: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1637:
1.1.1.15! root 1638: LOG_TRACE(TRACE_FDC, "fdc type II write sector sector=0x%x multi=%s track=0x%x side=%d drive=%d dmasector=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1639: FDC.SR, ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" , HeadTrack[ FDC_DRIVE ] , FDC_SIDE, FDC_DRIVE , FDC_DMA.SectorCount,
! 1640: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.2 root 1641:
1.1.1.15! root 1642: /* Set emulation to write a sector(s) */
! 1643: FDC.Command = FDCEMU_CMD_WRITESECTORS;
! 1644: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA;
1.1.1.10 root 1645:
1.1.1.15! root 1646: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
! 1647: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE , FDC_STR_BIT_BUSY );
1.1 root 1648:
1.1.1.15! root 1649: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
! 1650: Delay_micro = FDC_DELAY_HEAD_LOAD;
! 1651:
! 1652: return FDC_DELAY_TYPE_II_PREPARE + Delay_micro;
1.1 root 1653: }
1654:
1.1.1.2 root 1655:
1656: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1657: /**
1658: * Type III Commands
1659: *
1660: * Read Address, Read Track, Write Track
1661: */
1.1 root 1662:
1.1.1.2 root 1663:
1664: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1665: static int FDC_TypeIII_ReadAddress ( void )
1.1 root 1666: {
1.1.1.15! root 1667: int Delay_micro = 0;
! 1668: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1669:
1670: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1671:
1.1.1.15! root 1672: LOG_TRACE(TRACE_FDC, "fdc type III read address track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1673: HeadTrack[ FDC_DRIVE ], FDC_SIDE, FDC_DRIVE , FDC_GetDMAAddress(),
1.1.1.12 root 1674: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1675:
1.1.1.11 root 1676: /* Set emulation to seek to track zero */
1.1.1.15! root 1677: FDC.Command = FDCEMU_CMD_READADDRESS;
! 1678: FDC.CommandState = FDCEMU_RUN_READADDRESS;
1.1.1.11 root 1679:
1.1.1.15! root 1680: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
! 1681: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
! 1682:
! 1683: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
! 1684: Delay_micro = FDC_DELAY_HEAD_LOAD;
! 1685:
! 1686: return FDC_DELAY_TYPE_III_PREPARE + Delay_micro;
1.1 root 1687: }
1688:
1.1.1.2 root 1689:
1690: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1691: static int FDC_TypeIII_ReadTrack ( void )
1.1 root 1692: {
1.1.1.15! root 1693: int Delay_micro = 0;
! 1694: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1695:
1696: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1697:
1.1.1.15! root 1698: LOG_TRACE(TRACE_FDC, "fdc type III read track track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1699: HeadTrack[ FDC_DRIVE ], FDC_SIDE, FDC_DRIVE , FDC_GetDMAAddress(),
! 1700: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1701:
1.1.1.15! root 1702: /* Set emulation to read a single track */
! 1703: FDC.Command = FDCEMU_CMD_READTRACK;
! 1704: FDC.CommandState = FDCEMU_RUN_READTRACK;
1.1.1.5 root 1705:
1.1.1.15! root 1706: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
! 1707: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
1.1.1.5 root 1708:
1.1.1.15! root 1709: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
! 1710: Delay_micro = FDC_DELAY_HEAD_LOAD;
1.1 root 1711:
1.1.1.15! root 1712: return FDC_DELAY_TYPE_III_PREPARE + Delay_micro;
1.1 root 1713: }
1714:
1.1.1.2 root 1715:
1716: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1717: static int FDC_TypeIII_WriteTrack ( void )
1.1 root 1718: {
1.1.1.15! root 1719: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1720:
1721: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1722:
1.1.1.15! root 1723: LOG_TRACE(TRACE_FDC, "fdc type III write track track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1724: HeadTrack[ FDC_DRIVE ], FDC_SIDE, FDC_DRIVE , FDC_GetDMAAddress(),
! 1725: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1726:
1727: Log_Printf(LOG_TODO, "FDC type III command 'write track' does not work yet!\n");
1.1.1.5 root 1728:
1.1.1.15! root 1729: /* FIXME: "Write track" should write all the sectors after extracting them from the track data */
1.1.1.5 root 1730:
1.1.1.6 root 1731: /* Set emulation to write a single track */
1.1.1.15! root 1732: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* FIXME : Not supported yet, set RNF bit */
! 1733: FDC.Command = FDCEMU_CMD_NULL;
! 1734: FDC.CommandState = FDCEMU_RUN_NULL;
1.1 root 1735:
1.1.1.15! root 1736: return FDC_DELAY_TYPE_III_PREPARE;
1.1 root 1737: }
1738:
1.1.1.2 root 1739:
1740: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1741: /**
1742: * Type IV Commands
1743: *
1744: * Force Interrupt
1745: */
1.1 root 1746:
1.1.1.2 root 1747:
1748: /*-----------------------------------------------------------------------*/
1.1.1.15! root 1749: static int FDC_TypeIV_ForceInterrupt ( bool bCauseCPUInterrupt )
1.1 root 1750: {
1.1.1.15! root 1751: int Delay_micro;
! 1752: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 1753:
1754: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1755:
1.1.1.15! root 1756: LOG_TRACE(TRACE_FDC, "fdc type IV force int 0x%x irq=%d index=%d VBL=%d video_cyc=%d %d@%dpc=%x\n",
! 1757: FDC.CR , ( FDC.CR & 0x8 ) >> 3 , ( FDC.CR & 0x4 ) >> 2 ,
1.1.1.12 root 1758: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 1759:
1.1.1.15! root 1760: /* For Type II/III commands, LOST DATA bit is never set (DRQ is always handled by the DMA) */
! 1761: /* (eg Super Monaco GP on Superior 65 : loader fails if LOST DATA is set when there're not enough DMA sectors to transfer bytes) */
! 1762: FDC_Update_STR ( FDC_STR_BIT_LOST_DATA , 0 ); /* Remove LOST DATA / TR00 bit */
! 1763:
! 1764: /* TR00 is updated when a type I command is interrupted or when no command was running */
! 1765: /* MOTOR ON is also set when a type I command is interrupted or when no command was running */
! 1766: /* (eg Knightmare on DBUG 24 : loader fails if motor is off because of the added delay to start it) */
! 1767: if ( ( ( FDC.STR & FDC_STR_BIT_BUSY ) == 0 ) /* No command running */
! 1768: || ( FDC.CommandType == 1 ) ) /* Or busy command is Type I */
! 1769: {
! 1770: if ( HeadTrack[ FDC_DRIVE ] == 0 )
! 1771: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
! 1772:
! 1773: FDC_Update_STR ( 0 , FDC_STR_BIT_MOTOR_ON ); /* Set Motor ON */
! 1774: }
! 1775:
! 1776: /* Remove busy bit, ack int and stop the motor */
! 1777: Delay_micro = FDC_CmdCompleteCommon( bCauseCPUInterrupt );
1.1.1.6 root 1778:
1.1.1.15! root 1779: return FDC_DELAY_TYPE_IV_PREPARE + Delay_micro;
1.1 root 1780: }
1781:
1.1.1.2 root 1782:
1783: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1784: /**
1785: * Execute Type I commands
1786: */
1.1.1.15! root 1787: static int FDC_ExecuteTypeICommands ( void )
1.1 root 1788: {
1.1.1.15! root 1789: int Delay_micro = 0;
! 1790:
! 1791: FDC.CommandType = 1;
1.1.1.6 root 1792: MFP_GPIP |= 0x20;
1.1 root 1793:
1.1.1.6 root 1794: /* Check Type I Command */
1.1.1.15! root 1795: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 1796: {
1797: case 0x00: /* Restore */
1.1.1.15! root 1798: Delay_micro = FDC_TypeI_Restore();
1.1.1.6 root 1799: break;
1800: case 0x10: /* Seek */
1.1.1.15! root 1801: Delay_micro = FDC_TypeI_Seek();
1.1.1.6 root 1802: break;
1803: case 0x20: /* Step */
1804: case 0x30:
1.1.1.15! root 1805: Delay_micro = FDC_TypeI_Step();
1.1.1.6 root 1806: break;
1807: case 0x40: /* Step-In */
1808: case 0x50:
1.1.1.15! root 1809: Delay_micro = FDC_TypeI_StepIn();
1.1.1.6 root 1810: break;
1811: case 0x60: /* Step-Out */
1812: case 0x70:
1.1.1.15! root 1813: Delay_micro = FDC_TypeI_StepOut();
1.1.1.6 root 1814: break;
1815: }
1.1 root 1816:
1.1.1.15! root 1817: /* Check if motor needs to be started and add possible delay */
! 1818: Delay_micro += FDC_Check_MotorON ( FDC.CR );
! 1819:
! 1820: return Delay_micro;
1.1 root 1821: }
1822:
1.1.1.2 root 1823:
1824: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1825: /**
1826: * Execute Type II commands
1827: */
1.1.1.15! root 1828: static int FDC_ExecuteTypeIICommands ( void )
1.1 root 1829: {
1.1.1.15! root 1830: int Delay_micro = 0;
! 1831:
! 1832: FDC.CommandType = 2;
1.1.1.6 root 1833: MFP_GPIP |= 0x20;
1.1 root 1834:
1.1.1.6 root 1835: /* Check Type II Command */
1.1.1.15! root 1836: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 1837: {
1.1.1.15! root 1838: case 0x80: /* Read Sector multi=0*/
! 1839: case 0x90: /* Read Sectors multi=1 */
! 1840: Delay_micro = FDC_TypeII_ReadSector();
1.1.1.6 root 1841: break;
1.1.1.15! root 1842: case 0xa0: /* Write Sector multi=0 */
! 1843: case 0xb0: /* Write Sectors multi=1 */
! 1844: Delay_micro = FDC_TypeII_WriteSector();
1.1.1.6 root 1845: break;
1846: }
1.1 root 1847:
1.1.1.15! root 1848: /* Check if motor needs to be started and add possible delay */
! 1849: Delay_micro += FDC_Check_MotorON ( FDC.CR );
! 1850:
! 1851: return Delay_micro;
1.1 root 1852: }
1853:
1.1.1.2 root 1854:
1855: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1856: /**
1857: * Execute Type III commands
1858: */
1.1.1.15! root 1859: static int FDC_ExecuteTypeIIICommands ( void )
1.1 root 1860: {
1.1.1.15! root 1861: int Delay_micro = 0;
! 1862:
! 1863: FDC.CommandType = 3;
1.1.1.6 root 1864: MFP_GPIP |= 0x20;
1.1 root 1865:
1.1.1.6 root 1866: /* Check Type III Command */
1.1.1.15! root 1867: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 1868: {
1869: case 0xc0: /* Read Address */
1.1.1.15! root 1870: Delay_micro = FDC_TypeIII_ReadAddress();
1.1.1.6 root 1871: break;
1872: case 0xe0: /* Read Track */
1.1.1.15! root 1873: Delay_micro = FDC_TypeIII_ReadTrack();
1.1.1.6 root 1874: break;
1875: case 0xf0: /* Write Track */
1.1.1.15! root 1876: Delay_micro = FDC_TypeIII_WriteTrack();
1.1.1.6 root 1877: break;
1878: }
1.1 root 1879:
1.1.1.15! root 1880: /* Check if motor need to be started and add possible delay */
! 1881: Delay_micro += FDC_Check_MotorON ( FDC.CR );
! 1882:
! 1883: return Delay_micro;
1.1 root 1884: }
1885:
1.1.1.2 root 1886:
1887: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1888: /**
1889: * Execute Type IV commands
1890: */
1.1.1.15! root 1891: static int FDC_ExecuteTypeIVCommands ( void )
1.1 root 1892: {
1.1.1.15! root 1893: int Delay_micro;
1.1 root 1894:
1.1.1.15! root 1895: /* Check Type IV command */
! 1896: /* Most of the time a 0xD8 command is followed by a 0xD0 command to clear the IRQ signal */
! 1897: if ( FDC.CR & 0x8 ) /* I3 set (0xD8) : immediate interrupt with IRQ */
! 1898: Delay_micro = FDC_TypeIV_ForceInterrupt ( true );
! 1899:
! 1900: else if ( FDC.CR & 0x4 ) /* I2 set (0xD4) : IRQ on next index pulse */
! 1901: {
! 1902: /* FIXME [NP] This is not complete, we should report */
! 1903: /* an interrupt each time the FDC sees an index pulse, not just once */
! 1904: FDC.ID_FieldLastSector = 0; /* We simulate an index pulse now */
! 1905: Delay_micro = FDC_TypeIV_ForceInterrupt ( true );
! 1906: }
! 1907:
! 1908: else /* I3-I2 clear (0xD0) : stop command without IRQ */
! 1909: {
! 1910: MFP_GPIP |= 0x20; /* reset IRQ signal */
! 1911: Delay_micro = FDC_TypeIV_ForceInterrupt ( false );
! 1912: }
! 1913:
! 1914: FDC.CommandType = 4; /* Change CommandType after interrupting the current command */
! 1915: return Delay_micro;
1.1 root 1916: }
1917:
1.1.1.2 root 1918:
1919: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1920: /**
1921: * Find FDC command type and execute
1922: */
1.1.1.15! root 1923: static void FDC_ExecuteCommand ( void )
1.1 root 1924: {
1.1.1.15! root 1925: int Delay_micro;
! 1926:
1.1.1.6 root 1927: /* Check type of command and execute */
1.1.1.15! root 1928: if ( ( FDC.CR & 0x80 ) == 0 ) /* Type I - Restore, Seek, Step, Step-In, Step-Out */
! 1929: Delay_micro = FDC_ExecuteTypeICommands();
! 1930: else if ( ( FDC.CR & 0x40 ) == 0 ) /* Type II - Read Sector, Write Sector */
! 1931: Delay_micro = FDC_ExecuteTypeIICommands();
! 1932: else if ( ( FDC.CR & 0xf0 ) != 0xd0 ) /* Type III - Read Address, Read Track, Write Track */
! 1933: Delay_micro = FDC_ExecuteTypeIIICommands();
! 1934: else /* Type IV - Force Interrupt */
! 1935: Delay_micro = FDC_ExecuteTypeIVCommands();
1.1.1.9 root 1936:
1.1.1.15! root 1937: FDC.ReplaceCommandPossible = true; /* This new command can be replaced during the Delay_micro phase */
! 1938: CycInt_AddAbsoluteInterrupt ( FDC_DelayToCpuCycles ( Delay_micro ) , INT_CPU_CYCLE , INTERRUPT_FDC );
1.1 root 1939: }
1940:
1.1.1.2 root 1941:
1942: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1943: /**
1.1.1.15! root 1944: * Write to SectorCount register $ff8604
1.1.1.9 root 1945: */
1.1.1.15! root 1946: static void FDC_WriteSectorCountRegister ( void )
1.1 root 1947: {
1.1.1.12 root 1948: int FrameCycles, HblCounterVideo, LineCycles;
1949:
1950: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1951:
1.1.1.15! root 1952: LOG_TRACE(TRACE_FDC, "fdc write 8604 dma sector count=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1953: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.9 root 1954:
1.1.1.15! root 1955: FDC_DMA.SectorCount = IoMem_ReadByte(0xff8605);
1.1 root 1956: }
1957:
1.1.1.2 root 1958:
1959: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1960: /**
1.1.1.15! root 1961: * Write to Command register $ff8604
1.1.1.9 root 1962: */
1.1.1.15! root 1963: static void FDC_WriteCommandRegister ( void )
1.1 root 1964: {
1.1.1.12 root 1965: int FrameCycles, HblCounterVideo, LineCycles;
1966:
1967: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1968:
1969: LOG_TRACE(TRACE_FDC, "fdc write 8604 command=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.15! root 1970: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1971:
! 1972: /* If fdc is busy, only 'Force Interrupt' is possible */
! 1973: /* [NP] : it's also possible to start a new command just after another command */
! 1974: /* was started and spinup phase was not completed yet (or is this only possible during the 'prepare' delay ?) */
! 1975: /* FIXME : this delay was not measured, it should be at least 880 cycles for Overdrive Demos by Phalanx */
! 1976: /* For now, we allow to cancel the current command if we're in the prepare+spinup delay */
! 1977: if ( FDC.STR & FDC_STR_BIT_BUSY )
! 1978: {
! 1979: if ( ( IoMem_ReadByte(0xff8605) & 0xf0 ) == 0xd0 ) /* 'Force Interrupt' command */
! 1980: {
! 1981: LOG_TRACE(TRACE_FDC, "fdc write 8604 while fdc busy, current command=0x%x interrupted by command=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1982: FDC.CR , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1983: }
! 1984:
! 1985: else if ( FDC.ReplaceCommandPossible )
! 1986: {
! 1987: LOG_TRACE(TRACE_FDC, "fdc write 8604 while fdc busy in prepare+spinup, current command=0x%x replaced by command=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1988: FDC.CR , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1989: }
! 1990:
! 1991: else /* Other cases : new command is ignored */
! 1992: {
! 1993: LOG_TRACE(TRACE_FDC, "fdc write 8604 fdc busy, command=0x%x ignored VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1994: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 1995: return;
! 1996: }
! 1997: }
1.1.1.9 root 1998:
1.1.1.15! root 1999: FDC.CR = IoMem_ReadByte(0xff8605);
1.1.1.6 root 2000: FDC_ExecuteCommand();
1.1 root 2001: }
2002:
1.1.1.2 root 2003:
2004: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2005: /**
1.1.1.15! root 2006: * Write to Track register $ff8604
1.1.1.9 root 2007: */
1.1.1.15! root 2008: static void FDC_WriteTrackRegister ( void )
1.1 root 2009: {
1.1.1.12 root 2010: int FrameCycles, HblCounterVideo, LineCycles;
2011:
2012: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2013:
2014: LOG_TRACE(TRACE_FDC, "fdc write 8604 track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15! root 2015: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 2016:
1.1.1.15! root 2017: /* [NP] Contrary to what is written in the WD1772 doc, Track Register can be changed */
! 2018: /* while the fdc is busy */
! 2019: if ( FDC.STR & FDC_STR_BIT_BUSY )
! 2020: {
! 2021: LOG_TRACE(TRACE_FDC, "fdc write 8604 fdc busy, track=0x%x may be ignored VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 2022: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 2023: }
! 2024:
! 2025: FDC.TR = IoMem_ReadByte(0xff8605);
1.1 root 2026: }
2027:
1.1.1.2 root 2028:
2029: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2030: /**
1.1.1.15! root 2031: * Write to Sector register $ff8604
1.1.1.9 root 2032: */
1.1.1.15! root 2033: static void FDC_WriteSectorRegister ( void )
1.1 root 2034: {
1.1.1.12 root 2035: int FrameCycles, HblCounterVideo, LineCycles;
2036:
2037: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2038:
2039: LOG_TRACE(TRACE_FDC, "fdc write 8604 sector=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15! root 2040: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 2041:
! 2042: /* [NP] Contrary to what is written in the WD1772 doc, Sector Register can be changed */
! 2043: /* while the fdc is busy (but it will have no effect once the sector's header is found) */
! 2044: /* (fix Delirious Demo IV's loader, which is bugged and set SR after starting the Read Sector command) */
! 2045: if ( FDC.STR & FDC_STR_BIT_BUSY )
! 2046: {
! 2047: LOG_TRACE(TRACE_FDC, "fdc write 8604 fdc busy, sector=0x%x may be ignored VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 2048: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 2049: }
1.1.1.9 root 2050:
1.1.1.15! root 2051: FDC.SR = IoMem_ReadByte(0xff8605);
1.1 root 2052: }
2053:
1.1.1.2 root 2054:
2055: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2056: /**
1.1.1.15! root 2057: * Write to Data register $ff8604
1.1.1.9 root 2058: */
1.1.1.15! root 2059: static void FDC_WriteDataRegister ( void )
1.1 root 2060: {
1.1.1.12 root 2061: int FrameCycles, HblCounterVideo, LineCycles;
2062:
2063: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2064:
2065: LOG_TRACE(TRACE_FDC, "fdc write 8604 data=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15! root 2066: IoMem_ReadByte(0xff8605), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 2067:
1.1.1.15! root 2068: FDC.DR = IoMem_ReadByte(0xff8605);
1.1 root 2069: }
2070:
1.1.1.2 root 2071:
2072: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2073: /**
1.1.1.15! root 2074: * Store byte in FDC registers or DMA sector count, when writing to $ff8604
1.1.1.9 root 2075: */
1.1.1.15! root 2076: void FDC_DiskController_WriteWord ( void )
1.1 root 2077: {
1.1.1.12 root 2078: int FrameCycles, HblCounterVideo, LineCycles;
2079:
1.1.1.15! root 2080: if ( nIoMemAccessSize == SIZE_BYTE )
1.1.1.6 root 2081: {
2082: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.8 root 2083: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_WRITE);
1.1.1.6 root 2084: return;
2085: }
2086:
1.1.1.8 root 2087: M68000_WaitState(4);
2088:
1.1.1.12 root 2089: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.9 root 2090:
1.1.1.12 root 2091: LOG_TRACE(TRACE_FDC, "fdc write 8604 data=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15! root 2092: IoMem_ReadWord(0xff8604), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.6 root 2093:
1.1.1.12 root 2094: /* Is it an ASCII HD command? */
1.1.1.15! root 2095: if ( ( FDC_DMA.Mode & 0x0018 ) == 8 )
1.1.1.12 root 2096: {
2097: /* Handle HDC functions */
2098: HDC_WriteCommandPacket();
1.1.1.10 root 2099: return;
1.1.1.12 root 2100: }
1.1.1.6 root 2101:
1.1.1.15! root 2102: /* Are we trying to set the SectorCount ? */
! 2103: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.6 root 2104: FDC_WriteSectorCountRegister();
2105: else
1.1.1.8 root 2106: {
2107: /* Write to FDC registers */
1.1.1.15! root 2108: switch ( FDC_DMA.Mode & 0x6 )
1.1.1.6 root 2109: { /* Bits 1,2 (A1,A0) */
1.1.1.15! root 2110: case 0x0: /* 0 0 - Command register */
1.1.1.6 root 2111: FDC_WriteCommandRegister();
2112: break;
1.1.1.15! root 2113: case 0x2: /* 0 1 - Track register */
1.1.1.6 root 2114: FDC_WriteTrackRegister();
2115: break;
1.1.1.15! root 2116: case 0x4: /* 1 0 - Sector register */
1.1.1.6 root 2117: FDC_WriteSectorRegister();
2118: break;
1.1.1.15! root 2119: case 0x6: /* 1 1 - Data register */
1.1.1.6 root 2120: FDC_WriteDataRegister();
2121: break;
2122: }
2123: }
1.1 root 2124: }
2125:
1.1.1.2 root 2126:
2127: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2128: /**
1.1.1.15! root 2129: * Return Status/FDC register when reading from $ff8604
1.1.1.9 root 2130: */
1.1.1.15! root 2131: void FDC_DiskControllerStatus_ReadWord ( void )
1.1 root 2132: {
1.1.1.15! root 2133: Uint16 DiskControllerByte = 0; /* Used to pass back the parameter */
1.1.1.12 root 2134: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15! root 2135: int ForceWPRT;
1.1.1.6 root 2136:
2137: if (nIoMemAccessSize == SIZE_BYTE)
2138: {
2139: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.8 root 2140: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_READ);
1.1.1.6 root 2141: return;
2142: }
2143:
1.1.1.8 root 2144: M68000_WaitState(4);
2145:
1.1.1.15! root 2146: if ((FDC_DMA.Mode & 0x18) == 0x08) /* HDC status reg selected? */
1.1.1.6 root 2147: {
2148: /* return the HDC status reg */
1.1.1.15! root 2149: DiskControllerByte = HDC_GetCommandStatus();
1.1.1.6 root 2150: }
1.1.1.15! root 2151: else if ((FDC_DMA.Mode & 0x18) == 0x18) /* HDC sector counter??? */
1.1.1.8 root 2152: {
2153: Log_Printf(LOG_DEBUG, "*** Read HDC sector counter???\n");
1.1.1.15! root 2154: DiskControllerByte = HDC_GetSectorCount();
1.1.1.8 root 2155: }
1.1.1.6 root 2156: else
2157: {
1.1.1.15! root 2158: /* FDC code */
! 2159: switch (FDC_DMA.Mode & 0x6) /* Bits 1,2 (A1,A0) */
1.1.1.6 root 2160: {
1.1.1.15! root 2161: case 0x0: /* 0 0 - Status register */
! 2162: /* [NP] Contrary to what is written in the WD1772 doc, the WPRT bit */
! 2163: /* is updated after a Type I command */
! 2164: /* (eg : Procopy or Terminators Copy 1.68 do a Restore/Seek to test WPRT) */
! 2165: if ( FDC.CommandType == 1 )
1.1.1.6 root 2166: {
1.1.1.15! root 2167: if ( Floppy_IsWriteProtected ( FDC_DRIVE ) )
! 2168: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
! 2169: else
! 2170: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit */
1.1.1.6 root 2171: }
2172:
1.1.1.15! root 2173: /* When there's no disk in drive, the floppy drive hardware can't see */
! 2174: /* the difference with an inserted disk that would be write protected */
! 2175: if ( ! EmulationDrives[ FDC_DRIVE ].bDiskInserted )
! 2176: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
! 2177:
! 2178: DiskControllerByte = FDC.STR;
! 2179:
! 2180: /* Temporarily change the WPRT bit if we're in a transition phase */
! 2181: /* regarding the disk in the drive (inserting or ejecting) */
! 2182: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC_DRIVE );
! 2183: if ( ForceWPRT == 1 )
! 2184: DiskControllerByte |= FDC_STR_BIT_WPRT; /* Force setting WPRT */
! 2185: if ( ForceWPRT == -1 )
! 2186: DiskControllerByte &= ~FDC_STR_BIT_WPRT; /* Force clearing WPRT */
! 2187:
! 2188: if ( ForceWPRT != 0 )
! 2189: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n", ForceWPRT==1?1:0, nVBLs, FDC_DRIVE, DiskControllerByte );
! 2190:
! 2191: /* When Status Register is read, FDC's INTRQ is reset */
1.1.1.6 root 2192: MFP_GPIP |= 0x20;
2193: break;
1.1.1.15! root 2194: case 0x2: /* 0 1 - Track register */
! 2195: DiskControllerByte = FDC.TR;
1.1.1.6 root 2196: break;
1.1.1.15! root 2197: case 0x4: /* 1 0 - Sector register */
! 2198: DiskControllerByte = FDC.SR;
1.1.1.6 root 2199: break;
1.1.1.15! root 2200: case 0x6: /* 1 1 - Data register */
! 2201: DiskControllerByte = FDC.DR;
1.1.1.6 root 2202: break;
2203: }
2204: }
2205:
1.1.1.7 root 2206: IoMem_WriteWord(0xff8604, DiskControllerByte);
1.1.1.9 root 2207:
1.1.1.12 root 2208: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2209:
2210: LOG_TRACE(TRACE_FDC, "fdc read 8604 ctrl status=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
2211: DiskControllerByte , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1 root 2212: }
2213:
1.1.1.2 root 2214:
2215: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2216: /**
1.1.1.15! root 2217: * Write word to $ff8606 (DMA Mode Control)
! 2218: *
! 2219: * Eg.
! 2220: * $80 - Selects command/status register
! 2221: * $82 - Selects track register
! 2222: * $84 - Selects sector register
! 2223: * $86 - Selects data regsiter
! 2224: * NOTE - OR above values with $100 is transfer from memory to floppy
! 2225: * Also if bit 4 is set, write to DMA sector count register
! 2226: */
! 2227: void FDC_DmaModeControl_WriteWord ( void )
! 2228: {
! 2229: Uint16 Mode_prev; /* Store previous write to 0xff8606 for 'toggle' checks */
! 2230: int FrameCycles, HblCounterVideo, LineCycles;
! 2231:
! 2232:
! 2233: if (nIoMemAccessSize == SIZE_BYTE)
! 2234: {
! 2235: /* This register does not like to be accessed in byte mode on a normal ST */
! 2236: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_WRITE);
! 2237: return;
! 2238: }
! 2239:
! 2240: Mode_prev = FDC_DMA.Mode; /* Store previous to check for _read/_write toggle (DMA reset) */
! 2241: FDC_DMA.Mode = IoMem_ReadWord(0xff8606); /* Store to DMA Mode control */
! 2242:
! 2243: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 2244:
! 2245: LOG_TRACE(TRACE_FDC, "fdc write 8606 ctrl=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
! 2246: FDC_DMA.Mode , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 2247:
! 2248: /* When write to 0xff8606, check bit '8' toggle. This causes DMA status reset */
! 2249: if ((Mode_prev ^ FDC_DMA.Mode) & 0x0100)
! 2250: FDC_ResetDMA();
! 2251: }
! 2252:
! 2253:
! 2254: /*-----------------------------------------------------------------------*/
! 2255: /**
! 2256: * Read DMA Status at $ff8606
! 2257: *
! 2258: * Bit 0 - Error Status (0=Error)
! 2259: * Bit 1 - Sector Count Zero Status (0=Sector Count Zero)
! 2260: * Bit 2 - Data Request Inactive Status
! 2261: */
! 2262: void FDC_DmaStatus_ReadWord ( void )
! 2263: {
! 2264: if (nIoMemAccessSize == SIZE_BYTE)
! 2265: {
! 2266: /* This register does not like to be accessed in byte mode on a normal ST */
! 2267: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_READ);
! 2268: return;
! 2269: }
! 2270:
! 2271: /* Set zero sector count */
! 2272: FDC_DMA.Status &= ~0x2; /* Clear bit 1 */
! 2273: if ( FDC_DMA.Mode & 0x08 ) /* Get which sector count ? */
! 2274: FDC_DMA.Status |= (HDC_GetSectorCount())?0x2:0; /* HDC */
! 2275: else
! 2276: FDC_DMA.Status |= (FDC_DMA.SectorCount)?0x2:0; /* FDC */
! 2277:
! 2278: /* In the case of the ST, DRQ is always 0 because it's handled by the DMA and its 16 bytes buffer */
! 2279:
! 2280: IoMem_WriteWord(0xff8606, FDC_DMA.Status);
! 2281: }
! 2282:
! 2283:
! 2284: /*-----------------------------------------------------------------------*/
! 2285: /**
! 2286: * Read hi/med/low DMA address byte at $ff8609/0b/0d
! 2287: */
! 2288: void FDC_DmaAddress_ReadByte ( void )
! 2289: {
! 2290: int FrameCycles, HblCounterVideo, LineCycles;
! 2291:
! 2292: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 2293:
! 2294: LOG_TRACE(TRACE_FDC, "fdc read dma address %x val=0x%02x address=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
! 2295: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] , FDC_GetDMAAddress() ,
! 2296: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 2297: }
! 2298:
! 2299:
! 2300: /*-----------------------------------------------------------------------*/
! 2301: /**
! 2302: * Write hi/med/low DMA address byte at $ff8609/0b/0d
! 2303: */
! 2304: void FDC_DmaAddress_WriteByte ( void )
! 2305: {
! 2306: int FrameCycles, HblCounterVideo, LineCycles;
! 2307:
! 2308: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 2309:
! 2310: LOG_TRACE(TRACE_FDC, "fdc write dma address %x val=0x%02x address=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
! 2311: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] , FDC_GetDMAAddress() ,
! 2312: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 2313: }
! 2314:
! 2315:
! 2316: /*-----------------------------------------------------------------------*/
! 2317: /**
! 2318: * Get DMA address used to transfer data between FDC and RAM
1.1.1.9 root 2319: */
1.1.1.15! root 2320: Uint32 FDC_GetDMAAddress(void)
1.1 root 2321: {
1.1.1.6 root 2322: Uint32 Address;
1.1 root 2323:
1.1.1.6 root 2324: /* Build up 24-bit address from hardware registers */
2325: Address = ((Uint32)STMemory_ReadByte(0xff8609)<<16) | ((Uint32)STMemory_ReadByte(0xff860b)<<8) | (Uint32)STMemory_ReadByte(0xff860d);
1.1 root 2326:
1.1.1.6 root 2327: return Address;
1.1 root 2328: }
2329:
1.1.1.2 root 2330:
2331: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2332: /**
1.1.1.15! root 2333: * Write a new address to the FDC DMA address registers at $ff8909/0b/0d
1.1.1.9 root 2334: */
1.1.1.15! root 2335: void FDC_WriteDMAAddress ( Uint32 Address )
1.1 root 2336: {
1.1.1.12 root 2337: int FrameCycles, HblCounterVideo, LineCycles;
2338:
2339: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2340:
2341: LOG_TRACE(TRACE_FDC, "fdc write 0x%x to dma address VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
2342: Address , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 2343:
1.1.1.6 root 2344: /* Store as 24-bit address */
2345: STMemory_WriteByte(0xff8609, Address>>16);
2346: STMemory_WriteByte(0xff860b, Address>>8);
2347: STMemory_WriteByte(0xff860d, Address);
1.1 root 2348: }
2349:
1.1.1.2 root 2350:
2351: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2352: /**
2353: * Read sector from floppy drive into workspace
2354: * We copy the bytes in chunks to simulate reading of the floppy using DMA
2355: */
1.1.1.15! root 2356: static bool FDC_ReadSectorFromFloppy ( Uint8 *buf , Uint8 Sector , int *pSectorSize )
1.1 root 2357: {
1.1.1.12 root 2358: int FrameCycles, HblCounterVideo, LineCycles;
2359:
2360: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2361:
2362: LOG_TRACE(TRACE_FDC, "fdc read sector addr=0x%x dev=%d sect=%d track=%d side=%d VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15! root 2363: FDC_GetDMAAddress(), FDC_DRIVE, Sector, HeadTrack[ FDC_DRIVE ], FDC_SIDE,
1.1.1.12 root 2364: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 2365:
1.1.1.15! root 2366: /* Copy 1 sector to our workspace */
! 2367: if ( Floppy_ReadSectors ( FDC_DRIVE, buf, Sector, HeadTrack[ FDC_DRIVE ], FDC_SIDE, 1, NULL, pSectorSize ) )
1.1.1.12 root 2368: return true;
1.1 root 2369:
1.1.1.6 root 2370: /* Failed */
1.1.1.12 root 2371: LOG_TRACE(TRACE_FDC, "fdc read sector failed\n" );
2372: return false;
1.1 root 2373: }
2374:
1.1.1.2 root 2375:
2376: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2377: /**
1.1.1.15! root 2378: * Write sector from RAM to floppy drive
1.1.1.9 root 2379: * We copy the bytes in chunks to simulate writing of the floppy using DMA
1.1.1.15! root 2380: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
! 2381: * and some '0' bytes will be written to the disk.
1.1.1.9 root 2382: */
1.1.1.15! root 2383: static bool FDC_WriteSectorToFloppy ( int DMASectorsCount , Uint8 Sector , int *pSectorSize )
1.1 root 2384: {
1.1.1.15! root 2385: Uint8 *pBuffer;
1.1.1.12 root 2386: int FrameCycles, HblCounterVideo, LineCycles;
2387:
2388: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2389:
2390: LOG_TRACE(TRACE_FDC, "fdc write sector addr=0x%x dev=%d sect=%d track=%d side=%d VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15! root 2391: FDC_GetDMAAddress(), FDC_DRIVE, Sector, HeadTrack[ FDC_DRIVE ], FDC_SIDE,
1.1.1.12 root 2392: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 2393:
1.1.1.15! root 2394: if ( DMASectorsCount > 0 )
! 2395: pBuffer = &STRam[ FDC_GetDMAAddress() ];
! 2396: else
1.1.1.6 root 2397: {
1.1.1.15! root 2398: pBuffer = DMADiskWorkSpace; /* If DMA can't transfer data, we write '0' bytes */
! 2399: memset ( pBuffer , 0 , DMA_DISK_SECTOR_SIZE );
1.1.1.6 root 2400: }
1.1.1.15! root 2401:
! 2402: /* Write 1 sector from our workspace */
! 2403: if ( Floppy_WriteSectors ( FDC_DRIVE, pBuffer, Sector, HeadTrack[ FDC_DRIVE ], FDC_SIDE, 1, NULL, pSectorSize ) )
! 2404: return true;
1.1 root 2405:
1.1.1.6 root 2406: /* Failed */
1.1.1.12 root 2407: LOG_TRACE(TRACE_FDC, "fdc write sector failed\n" );
2408: return false;
1.1 root 2409: }
2410:
2411:
1.1.1.2 root 2412: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2413: /**
2414: * Write to floppy mode/control (?) register (0xff860F).
2415: * Used on Falcon only!
2416: * FIXME: I've found hardly any documentation about this register, only
2417: * the following description of the bits:
2418: *
2419: * __________54__10 Floppy Controll-Register
2420: * || ||
2421: * || |+- Prescaler 1
2422: * || +-- Media detect 1
2423: * |+----- Prescaler 2
2424: * +------ Media detect 2
2425: *
2426: * For DD - disks: 0x00
2427: * For HD - disks: 0x03
2428: * for ED - disks: 0x30 (not supported by TOS)
2429: */
1.1.1.15! root 2430: void FDC_FloppyMode_WriteByte ( void )
1.1.1.9 root 2431: {
2432: // printf("Write to floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
2433: }
2434:
2435:
2436: /*-----------------------------------------------------------------------*/
2437: /**
2438: * Read from floppy mode/control (?) register (0xff860F).
2439: * Used on Falcon only!
2440: * FIXME: I've found hardly any documentation about this register, only
2441: * the following description of the bits:
2442: *
2443: * ________76543210 Floppy Controll-Register
2444: * ||||||||
2445: * |||||||+- Prescaler 1
2446: * ||||||+-- Mode select 1
2447: * |||||+--- Media detect 1
2448: * ||||+---- accessed during DMA transfers (?)
2449: * |||+----- Prescaler 2
2450: * ||+------ Mode select 2
2451: * |+------- Media detect 2
2452: * +-------- Disk changed
2453: */
1.1.1.15! root 2454: void FDC_FloppyMode_ReadByte ( void )
1.1.1.9 root 2455: {
2456: IoMem_WriteByte(0xff860f, 0x80); // FIXME: Is this ok?
2457: // printf("Read from floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
2458: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.