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