|
|
1.1 root 1: /*
1.1.1.4 root 2: Hatari - fdc.c
3:
1.1.1.17 root 4: This file is distributed under the GNU General Public License, version 2
5: or at 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:
1.1.1.19 root 20: #include <inttypes.h>
21:
1.1 root 22: #include "main.h"
1.1.1.4 root 23: #include "configuration.h"
1.1 root 24: #include "fdc.h"
1.1.1.3 root 25: #include "hdc.h"
1.1 root 26: #include "floppy.h"
1.1.1.18 root 27: #include "floppy_ipf.h"
28: #include "floppy_stx.h"
1.1.1.6 root 29: #include "ioMem.h"
30: #include "log.h"
1.1 root 31: #include "m68000.h"
32: #include "memorySnapShot.h"
33: #include "mfp.h"
34: #include "psg.h"
35: #include "stMemory.h"
1.1.1.14 root 36: #include "screen.h"
1.1.1.10 root 37: #include "video.h"
1.1.1.15 root 38: #include "clocks_timings.h"
39: #include "utils.h"
1.1.1.17 root 40: #include "statusbar.h"
1.1 root 41:
1.1.1.3 root 42:
1.1 root 43: /*
1.1.1.7 root 44: Floppy Disk Controller
1.1 root 45:
46: Programmable Sound Generator (YM-2149)
47:
48: 0xff8800(even byte) - PSG Register Data (Read, used for parallel port)
49: - PSG Register Select (Write)
50:
51: Write to bits 0-3 to select PSG register to use(then write data to 0xfff8802)
52: Value Register
53:
54: 0000 Channel A Fine Tune
55: 0001 Channel A Coarse Tune
56: 0010 Channel B Fine Tune
57: 0011 Channel B Coarse Tune
58: 0100 Channel C Fine Tune
59: 0101 Channel C Coarse Tune
60: 0110 Noise Generator Control
61: 0111 Mixer Control - I/O enable
62: 1000 Channel A Amplitude
63: 1001 Channel B Amplitude
64: 1010 Channel C Amplitude
65: 1011 Envelope Period Fine Tune
66: 1100 Envelope Peroid Coarse Tune
67: 1101 Envelope Shape
68: 1110 I/O Port A Select (Write only)
69: 1111 I/O Port B Select
70:
71: 0xfff8802(even byte) - Bits according to 0xff8800 Register select
1.1.1.4 root 72:
1.1 root 73: 1110(Register 14) - I/O Port A
74: Bit 0 - Floppy side 0/1
75: Bit 1 - Floppy drive 0 select
76: Bit 2 - Floppy drive 1 select
77: Bit 3 - RS232 Ready to send (RTS)
78: Bit 4 - RS232 Data Terminal Ready (DTR)
79: Bit 5 - Centronics Strobe
80: Bit 6 - General Purpose Output
81: Bit 7 - Reserved
82:
1.1.1.7 root 83: ACSI DMA and Floppy Disk Controller(FDC)
1.1 root 84: 0xff8604 - information from file '1772.info.txt, by David Gahris' (register r0)
1.1.1.15 root 85: Word access only, but only lower byte (ff8605) is used
1.1 root 86: (write) - Disk controller
1.1.1.15 root 87: Set DMA sector count if ff8606 bit 4 == 1
88: Set FDC's internal registers depending on bit 1/2 of ff8606 if bit 4 == 0
1.1 root 89: (read) - Disk controller status
90: Bit 0 - Busy. This bit is 1 when the 177x is busy. This bit is 0 when the 177x is free for CPU commands.
91: Bit 1 - Index / Data Request. On Type I commands, this bit is high during the index pulse that occurs once
92: per disk rotation. This bit is low at all times other than the index pulse. For Type II and III commands,
93: Bit 1 high signals the CPU to handle the data register in order to maintain a continuous flow of data.
94: Bit 1 is high when the data register is full during a read or when the data register is empty during a write.
95: "Worst case service time" for Data Request is 23.5 cycles.
96: Bit 2 - Track Zero / Lost Data. After Type I commands, this bit is 0 if the mechanism is at track zero.
97: This bit is 1 if the head is not at track zero. After Type II or III commands, this bit is 1 if the
98: CPU did not respond to Data Request (Status bit 1) in time for the 177x to maintain a continuous data flow.
99: This bit is 0 if the CPU responded promptly to Data Request.
1.1.1.15 root 100: NOTE : on ST, Lost Data is never set because the DMA always handles the data request signal.
1.1 root 101: Bit 3 - CRC Error. This bit is high if a sector CRC on disk does not match the CRC which the 177x
102: computed from the data. The CRC polynomial is x^16+x^12+x^5+1. If the stored CRC matches the newly
103: calculated CRC, the CRC Error bit is low. If this bit and the Record Not Found bit are set, the error
104: was in an ID field. If this bit is set but Record Not Found is clear, the error was in a data field.
105: Bit 4 - Record Not Found. This bit is set if the 177x cannot find the track, sector, or side which
106: the CPU requested. Otherwise, this bit is clear.
107: Bit 5 - Spin-up / Record Type. For Type I commands, this bit is low during the 6-revolution motor
108: spin-up time. This bit is high after spin-up. For Type II and Type III commands, Bit 5 low
109: indicates a normal data mark. Bit 5 high indicates a deleted data mark.
110: 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 111: After a type I command, this bit is constantly updated an give the current value of the WPT signal.
1.1 root 112: Bit 7 - Motor On. This bit is high when the drive motor is on, and low when the motor is off.
113:
114: 0xff8606 - DMA Status(read), DMA Mode Control(write) - NOTE bits 0,9-15 are not used
115: Bit 1 - FDC Pin A0 (See below)
116: Bit 2 - FDC Pin A1
117: Bit 3 - FDC/HDC Register Select
118: Bit 4 - FDC/Sector count select
119: Bit 5 - Reserved
120: Bit 6 - Enable/Disable DMA
121: Bit 7 - HDC/FDC
122: Bit 8 - Read/Write
123:
124: A1 A0 Read Write(bit 8==1)
125: 0 0 Status Command
126: 0 1 Track Register Track Register
127: 1 0 Sector Register Sector Register
128: 1 1 Data Register Data Register
129:
130:
1.1.1.15 root 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
1.1.1.18 root 135: will be no transfer between DMA and RAM).
136: So, "read sector" and "write sector" type II command will never set LOST_DATA, but
137: strangely on a real STF the "read track" type III command will set LOST_DATA when
138: it succeeds (maybe it depends on the size of the track ?)
139: (eg Super Monaco GP on Superior 65 : loader fails if LOST_DATA is set when
140: there're not enough DMA sectors to transfer bytes with read sectors)
141:
142: NOTE [NP] : All commands that require to read data from the floppy (verify sequence,
143: searching next sector id, ...) will not fail if the drive is OFF or empty. They will
144: wait forever until the drive is enabled again or a floppy is inserted ; at this point
145: the command will complete as usual, even after several seconds/minutes of delay.
146:
147: NOTE [NP] : Although the doc says a new type I/II/III command can't be started while
148: the busy bit is set, it's in fact possible to do it under certain conditions. As seen
149: in the loader of 'The Overdrive Demos' by Phalanx, the 'restore' command should be
150: replaced by a 'seek' command when it occurs in less than 900 cycles.
151: A possible explanation from this could be seen in the WD1772's documentation, where
152: the specific type I command is in fact checked after the 'prepare + spinup' sequence
153: in the state machine diagram.
154: Similarly, we can guess that a type II command can be replaced by another type II as long
155: as the 'prepare + spinup + head settle' sequence is not over (this was not tested on real HW)
156:
157: NOTE [NP] : As verified on a real STF, when reading DMA status at $ff8606 or DMA sector
158: count at $ff8604, the unused bits are not set to 0, but they contain the value from the latest
159: read/write made at $ff8604 when accessing FDC or HDC registers. Moreover, it's not possible to
160: read DMA sector count, so we return the lowest 8 bits from the latest access at $ff8604.
161:
162:
163: Cycles and wait states :
164: ------------------------
165: As verified on a real STF, reading or writing to $ff8604 or $ff8606 can add some 4 cycles
166: wait state.
167: lea $ffff8604,a3
168: lea $ffff8606,a4
169:
170: move.w (a4),d0 ; dma status motorola=8 stf=8
171:
172: move.w #$90,(a4) ; dma ctrl sector count motorola=12 stf=12+4
173: move.w (a3),d0 ; read sector count / fdc reg motorola=8 stf=8
174: move.w #1,(a3) ; write sector count motorola=12 stf=12+4
175:
176: move.w #$80,(a4) ; dma ctrl status/cmd reg motorola=12 stf=12+4
177: move.w (a3),d0 ; read fdc reg motorola=8 stf=8+4
178: move.w #$d0,(a3) ; write fdc reg motorola=12 stf=12+4
179:
180: -> All accesses take 4 extra cycles, except reading DMA status and reading DMA sector count
181: (which can't really be read, see note above)
1.1.1.15 root 182:
183:
184: Detecting disk changes :
185: ------------------------
186: 3'1/2 floppy drives include a 'DSKCHG' signal on pin 34 to detect when a disk was changed.
187: Unfortunatelly on ST, this signal is not connected. Nevertheless, it's possible to detect
188: a disk was inserted or ejected by looking at the 'WPT' signal which tells if a disk is write
1.1.1.20 root 189: protected or not (but this method has some limitations and doesn't work in all cases).
190:
191: The following values of the WPT signal were measured with a custom program when ejecting/inserting
192: a floppy (tested on a 520 STF with a single sided drive and with a double sided drive) :
193: - floppy with write protection OFF (write possible), WPT=0 :
194: eject start=0 -> end=1
195: insert start=1 -> end=0
196: - floppy with write protection ON (write not possible), WPT=1 :
197: eject start=1 -> end=1
198: insert start=1 -> end=1
199:
200: As can be seen, when a disk is write protected (WPT=1), it is not possible to detect the
201: transition between inserting and ejecting, WPT will always be 1.
1.1.1.15 root 202:
203: The TOS monitors the changes on the WPT signal to determine if a floppy was ejected or inserted.
204: On TOS 1.02fr, the code is located between $fc1bc4 and $fc1ebc. Every 8 VBL, one floppy drive is checked
205: to see if the WPT signal changed. When 1 drive is connected, this means a floppy change should keep the
206: WPT signal during at least 8 VBLs. When 2 drive are connected, each drive is checked every 16 VBLs, so
207: the WPT signal should be kept for at least 16 VBLs.
208:
1.1.1.20 root 209: During these transition phases between "ejected" and "inserted", we force the WPT signal to 1,
1.1.1.15 root 210: depending on which transition we're emulating (see Floppy_DriveTransitionUpdateState()) :
1.1.1.20 root 211: - Ejecting : WPT will be X, then 1
212: - Inserting : WPT will be 1, then X
1.1.1.15 root 213:
1.1 root 214: */
215:
1.1.1.14 root 216: /*-----------------------------------------------------------------------*/
1.1.1.15 root 217:
1.1.1.21! root 218: /* Status register */
1.1.1.15 root 219: #define FDC_STR_BIT_BUSY 0x01
220: #define FDC_STR_BIT_INDEX 0x02 /* type I */
221: #define FDC_STR_BIT_DRQ 0x02 /* type II and III */
222: #define FDC_STR_BIT_TR00 0x04 /* type I */
223: #define FDC_STR_BIT_LOST_DATA 0x04 /* type II and III */
224: #define FDC_STR_BIT_CRC_ERROR 0x08
225: #define FDC_STR_BIT_RNF 0x10
226: #define FDC_STR_BIT_SPIN_UP 0x20 /* type I */
227: #define FDC_STR_BIT_RECORD_TYPE 0x20 /* type II and III */
228: #define FDC_STR_BIT_WPRT 0x40
229: #define FDC_STR_BIT_MOTOR_ON 0x80
230:
231:
232:
233: /* FDC Emulation commands used in FDC.Command */
1.1.1.14 root 234: enum
235: {
236: FDCEMU_CMD_NULL = 0,
237: /* Type I */
238: FDCEMU_CMD_RESTORE,
239: FDCEMU_CMD_SEEK,
1.1.1.15 root 240: FDCEMU_CMD_STEP, /* Also used for STEP IN and STEP OUT */
1.1.1.14 root 241: /* Type II */
242: FDCEMU_CMD_READSECTORS,
243: FDCEMU_CMD_WRITESECTORS,
244: /* Type III */
245: FDCEMU_CMD_READADDRESS,
1.1.1.15 root 246: FDCEMU_CMD_READTRACK,
247: FDCEMU_CMD_WRITETRACK,
248:
249: /* Other fake commands used internally */
250: FDCEMU_CMD_MOTOR_STOP
1.1.1.14 root 251: };
252:
253:
1.1.1.15 root 254: /* FDC Emulation commands' sub-states used in FDC.CommandState */
1.1.1.14 root 255: enum
256: {
1.1.1.15 root 257: FDCEMU_RUN_NULL = 0,
1.1.1.14 root 258:
1.1.1.15 root 259: /* Restore */
260: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO,
1.1.1.18 root 261: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP,
262: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON,
1.1.1.15 root 263: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP,
1.1.1.17 root 264: FDCEMU_RUN_RESTORE_VERIFY,
1.1.1.18 root 265: FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK,
266: FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER,
267: FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER,
1.1.1.15 root 268: FDCEMU_RUN_RESTORE_COMPLETE,
269: /* Seek */
1.1.1.14 root 270: FDCEMU_RUN_SEEK_TOTRACK,
1.1.1.18 root 271: FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP,
272: FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON,
1.1.1.17 root 273: FDCEMU_RUN_SEEK_VERIFY,
1.1.1.18 root 274: FDCEMU_RUN_SEEK_VERIFY_HEAD_OK,
275: FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER,
276: FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER,
1.1.1.15 root 277: FDCEMU_RUN_SEEK_COMPLETE,
278: /* Step / Step In / Step Out */
1.1.1.14 root 279: FDCEMU_RUN_STEP_ONCE,
1.1.1.18 root 280: FDCEMU_RUN_STEP_ONCE_SPIN_UP,
281: FDCEMU_RUN_STEP_ONCE_MOTOR_ON,
1.1.1.17 root 282: FDCEMU_RUN_STEP_VERIFY,
1.1.1.18 root 283: FDCEMU_RUN_STEP_VERIFY_HEAD_OK,
284: FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER,
285: FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER,
1.1.1.15 root 286: FDCEMU_RUN_STEP_COMPLETE,
287: /* Read Sector */
288: FDCEMU_RUN_READSECTORS_READDATA,
1.1.1.18 root 289: FDCEMU_RUN_READSECTORS_READDATA_SPIN_UP,
290: FDCEMU_RUN_READSECTORS_READDATA_HEAD_LOAD,
291: FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON,
292: FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER,
1.1.1.15 root 293: FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER,
1.1.1.17 root 294: FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START,
295: FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP,
296: FDCEMU_RUN_READSECTORS_CRC,
1.1.1.18 root 297: FDCEMU_RUN_READSECTORS_MULTI,
1.1.1.15 root 298: FDCEMU_RUN_READSECTORS_RNF,
299: FDCEMU_RUN_READSECTORS_COMPLETE,
300: /* Write Sector */
301: FDCEMU_RUN_WRITESECTORS_WRITEDATA,
1.1.1.18 root 302: FDCEMU_RUN_WRITESECTORS_WRITEDATA_SPIN_UP,
303: FDCEMU_RUN_WRITESECTORS_WRITEDATA_HEAD_LOAD,
304: FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON,
305: FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER,
1.1.1.15 root 306: FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER,
1.1.1.17 root 307: FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START,
308: FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP,
309: FDCEMU_RUN_WRITESECTORS_CRC,
1.1.1.18 root 310: FDCEMU_RUN_WRITESECTORS_MULTI,
1.1.1.15 root 311: FDCEMU_RUN_WRITESECTORS_RNF,
312: FDCEMU_RUN_WRITESECTORS_COMPLETE,
313: /* Read Address */
314: FDCEMU_RUN_READADDRESS,
1.1.1.18 root 315: FDCEMU_RUN_READADDRESS_SPIN_UP,
316: FDCEMU_RUN_READADDRESS_HEAD_LOAD,
317: FDCEMU_RUN_READADDRESS_MOTOR_ON,
318: FDCEMU_RUN_READADDRESS_NEXT_SECTOR_HEADER,
319: FDCEMU_RUN_READADDRESS_TRANSFER_START,
320: FDCEMU_RUN_READADDRESS_TRANSFER_LOOP,
321: FDCEMU_RUN_READADDRESS_RNF,
1.1.1.15 root 322: FDCEMU_RUN_READADDRESS_COMPLETE,
323: /* Read Track */
324: FDCEMU_RUN_READTRACK,
1.1.1.18 root 325: FDCEMU_RUN_READTRACK_SPIN_UP,
326: FDCEMU_RUN_READTRACK_HEAD_LOAD,
327: FDCEMU_RUN_READTRACK_MOTOR_ON,
1.1.1.17 root 328: FDCEMU_RUN_READTRACK_INDEX,
1.1.1.18 root 329: FDCEMU_RUN_READTRACK_TRANSFER_LOOP,
1.1.1.15 root 330: FDCEMU_RUN_READTRACK_COMPLETE,
331: /* Write Track */
332: FDCEMU_RUN_WRITETRACK,
1.1.1.18 root 333: FDCEMU_RUN_WRITETRACK_SPIN_UP,
334: FDCEMU_RUN_WRITETRACK_HEAD_LOAD,
335: FDCEMU_RUN_WRITETRACK_MOTOR_ON,
1.1.1.17 root 336: FDCEMU_RUN_WRITETRACK_INDEX,
1.1.1.18 root 337: FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP,
338: FDCEMU_RUN_WRITETRACK_COMPLETE,
339:
340: /* Motor Stop */
341: FDCEMU_RUN_MOTOR_STOP,
342: FDCEMU_RUN_MOTOR_STOP_WAIT,
343: FDCEMU_RUN_MOTOR_STOP_COMPLETE
1.1.1.14 root 344: };
345:
346:
347:
1.1.1.18 root 348: /*
349: * Standard hardware values for the FDC. This should allow to get very good timings' emulation
350: * when dealing with non protected disks that still require a correct speed (MSA or ST images)
351: *
352: * - WD1772's datasheet is based on a reference clock of 8 MHz, so delays expressed in milli-seconds
353: * will be slightly different for the Atari ST, whose FDC's clock is around 8.021247 MHz (but this is
354: * not really noticeable in practice, less than 0.3 %)
355: * - DD MFM encoding defines a standard signal of 4 micro sec per bit (a possible variation of +/- 10 %
356: * should still be possible). This means the WD1772 will read/write at 250 kbits/sec.
357: * Taking 4 us per bit means 32 us for a full byte, and with a 8 MHz clock, 256 cycles per byte.
358: * - The floppy drives used in the Atari ST are spinning at 300 RPM. Variations are possible, as long
359: * as it keeps the duration of an MFM bit in the required 4 us +/- 10 % (in practice, ST drives are often
360: * at 299-301 RPM)
361: * - When FDC runs at 8 MHz, the 250 kbits/s and 300 RPM give 6250 bytes for a standard track
362: * - When FDC runs at 8.021247 MHz (Atari ST), the 250.664 kbit/s and 300 RPM give 6267 bytes per track
363: *
364: * Notes on timings required for precise emulation :
365: * For a standard floppy recorded with a constant speed, the FDC will take 32 microsec
366: * to read/write 1 byte on the floppy. On STF with a 8 MHz CPU clock, this means one byte can be
367: * transferred every 256 cpu cycles. So, to get some correct timings as required by some games' protection
368: * we must update the emulated FDC's state every 256 cycles (it could be less frequently and still work,
369: * due to the 16 bytes DMA FIFO that will transfer data only 16 bytes at a time, every 256*16=4096 cycles)
370: */
371:
1.1.1.14 root 372:
1.1.1.18 root 373: #define FDC_CLOCK_STANDARD (8000000.L) /* In the WD1772's datasheet, all timings are related to a reference clock of 8 MHz */
374: #define FDC_DELAY_CYCLE_MFM_BYTE ( 4 * 8 * 8 ) /* 4 us per bit, 8 bits per byte, 8 MHz clock -> 256 cycles */
1.1.1.15 root 375: #define FDC_BITRATE_STANDARD 250000 /* read/write speed of the WD1772 in bits per sec */
376: #define FDC_RPM_STANDARD 300 /* 300 RPM or 5 spins per sec */
1.1.1.18 root 377: //#define FDC_TRACK_BYTES_STANDARD ( ( FDC_BITRATE_STANDARD / 8 ) / ( FDC_RPM_STANDARD / 60 ) ) /* 6250 bytes */
378: #define FDC_TRACK_BYTES_STANDARD 6268
1.1.1.14 root 379:
1.1.1.15 root 380: #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 381:
1.1.1.18 root 382: #define FDC_DELAY_IP_SPIN_UP 6 /* 6 index pulses to reach correct speed during spin up */
383: #define FDC_DELAY_IP_MOTOR_OFF 9 /* Turn off motor 9 index pulses after the last command */
384: #define FDC_DELAY_IP_ADDRESS_ID 5 /* 5 index pulses max when looking for next sector's address id field */
385:
1.1.1.14 root 386:
1.1.1.18 root 387: /* Delays are in micro sec */
388: #define FDC_DELAY_US_HEAD_LOAD ( 15 * 1000 ) /* Additionnal 15 ms delay to load the head in type II/III */
1.1.1.14 root 389:
1.1.1.18 root 390: /* Index pulse signal remains high during 3.71 ms on each rotation ([NP] tested on my STF, can vary between 1.5 and 4 ms depending on the drive) */
391: #define FDC_DELAY_US_INDEX_PULSE_LENGTH ( 3.71 * 1000 )
1.1 root 392:
1.1.1.17 root 393:
1.1.1.18 root 394: /* Internal delays to process commands are in fdc cycles for a 8 MHz clock */
395: #define FDC_DELAY_CYCLE_TYPE_I_PREPARE (90*8) /* Types I commands take at least 0.09 ms to execute */
1.1.1.17 root 396: /* (~740 cpu cycles @ 8 Mhz). [NP] : this was measured on a 520 STF */
397: /* and avoid returning immediately when command has no effect */
1.1.1.18 root 398: #define FDC_DELAY_CYCLE_TYPE_II_PREPARE (1*8) // 65 /* Start Type II commands immediately */
399: #define FDC_DELAY_CYCLE_TYPE_III_PREPARE (1*8) /* Start Type III commands immediately */
400: #define FDC_DELAY_CYCLE_TYPE_IV_PREPARE (100*8) /* FIXME [NP] : this was not measured */
401: #define FDC_DELAY_CYCLE_COMMAND_COMPLETE (1*8) /* Number of cycles before going to the _COMPLETE state (~8 cpu cycles) */
402: #define FDC_DELAY_CYCLE_COMMAND_IMMEDIATE (0) /* Number of cycles to go immediately to another state */
403:
404: /* When the drive is switched off or if there's no floppy, some commands will wait forever */
405: /* as they can't find the next index pulse. Instead of continuously testing if a valid drive */
406: /* or floppy becomes available (which would slow down emulation), we only test every 50000 FDC cycles, */
407: /* which shouldn't give any noticeable emulation error */
408: #define FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY 50000
1.1.1.5 root 409:
1.1.1.18 root 410: /* Update the floppy's angular position on a regular basis to detect the index pulses */
411: #define FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE 500
1.1.1.5 root 412:
413:
1.1.1.18 root 414: #define FDC_DMA_SECTOR_SIZE 512 /* Sector count at $ff8606 is for 512 bytes blocks */
415: #define FDC_DMA_FIFO_SIZE 16 /* DMA transfers blocks of 16 bytes at a time */
1.1.1.5 root 416:
1.1.1.15 root 417: #define FDC_PHYSICAL_MAX_TRACK 90 /* Head can't go beyond 90 tracks */
1.1 root 418:
1.1.1.2 root 419:
1.1.1.15 root 420: #define FDC_STEP_RATE ( FDC.CR & 0x03 ) /* Bits 0 and 1 of the current type I command */
421:
1.1.1.21! root 422: int FDC_StepRate_ms[] = { 6 , 12 , 2 , 3 }; /* Controlled by bits 1 and 0 (r1/r0) in type I commands */
1.1.1.15 root 423:
424:
1.1.1.17 root 425:
426:
1.1.1.15 root 427: #define FDC_FAST_FDC_FACTOR 10 /* Divide all delays by this value when --fastfdc is used */
428:
1.1.1.18 root 429: /* Standard ST floppies are double density ; to simulate HD or ED floppies, we use */
430: /* a density factor to have x2 or x4 bytes more during 1 FDC cycle */
431: #define FDC_DENSITY_FACTOR_DD 1
432: #define FDC_DENSITY_FACTOR_HD 2 /* For a HD disk, we get x2 bytes than DD */
433: #define FDC_DENSITY_FACTOR_ED 4 /* For a ED disk, we get x4 bytes than DD */
434:
435:
436: #define FDC_EMULATION_MODE_INTERNAL 1 /* Use fdc.c to handle emulation (ST, MSA, DIM and STX images) */
437: #define FDC_EMULATION_MODE_IPF 2 /* Use floppy_ipf.c to handle emulation (IPF, CTR images) */
438:
1.1.1.15 root 439:
440: typedef struct {
441: /* WD1772 internal registers */
442: Uint8 DR; /* Data Register */
443: Uint8 TR; /* Track Register */
444: Uint8 SR; /* Sector Register */
445: Uint8 CR; /* Command Register */
446: Uint8 STR; /* Status Register */
447: int StepDirection; /* +1 (Step In) or -1 (Step Out) */
448:
1.1.1.18 root 449: Uint8 SideSignal; /* Side 0 or 1 */
450: int DriveSelSignal; /* 0 or 1 for drive A or B ; or -1 if no drive selected */
451: Uint8 IRQ_Signal; /* 0 if IRQ is not set, else value depends on the source of the IRQ */
452:
1.1.1.15 root 453: /* Other variables */
1.1.1.18 root 454: int Command; /* FDC emulation command currently being executed */
1.1.1.15 root 455: int CommandState; /* Current state for the running command */
456: Uint8 CommandType; /* Type of latest FDC command (1,2,3 or 4) */
1.1.1.18 root 457: bool ReplaceCommandPossible; /* true if the current command can be replaced by another one (see notes) */
458:
459: Uint8 Status_Temp; /* Temporary content of the status register */
460: bool StatusTypeI; /* When true, STR will report the status of a type I command */
461: int IndexPulse_Counter; /* To count the number of rotations when motor is ON */
462: Uint8 NextSector_ID_Field_TR; /* Track value in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
463: Uint8 NextSector_ID_Field_SR; /* Sector value in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
464: Uint8 NextSector_ID_Field_LEN; /* Sector's length in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
465: Uint8 NextSector_ID_Field_CRC_OK; /* CRC OK or not in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
466: Uint8 InterruptCond; /* For a type IV force interrupt, contains the condition on the lower 4 bits */
1.1.1.15 root 467:
1.1.1.18 root 468: int EmulationMode; /* FDC_EMULATION_MODE_INTERNAL or FDC_EMULATION_MODE_IPF */
1.1.1.15 root 469: } FDC_STRUCT;
470:
471:
472: typedef struct {
473: /* DMA internal registers */
474: Uint16 Status;
475: Uint16 Mode;
476: Uint16 SectorCount;
1.1.1.18 root 477: Sint16 BytesInSector;
478:
479: Uint8 FIFO[ FDC_DMA_FIFO_SIZE ];
480: int FIFO_Size; /* Between 0 and FDC_DMA_FIFO_SIZE */
481:
482: Uint16 ff8604_recent_val; /* Most recent value read/written at $ff8604 in fdc/hdc mode (bit4=0 in $ff8606) */
1.1.1.15 root 483:
484: /* Variables to handle our DMA buffer */
485: int PosInBuffer;
1.1.1.18 root 486: int PosInBufferTransfer; /* FIXME REMOVE */
1.1.1.15 root 487: int BytesToTransfer;
488: } FDC_DMA_STRUCT;
489:
1.1.1.21! root 490: Uint32 FDC_DMA_Address; // TODO : move into FDC_DMA_STRUCT (which will change memory snapshot)
! 491:
1.1.1.15 root 492:
1.1.1.18 root 493: typedef struct {
494: bool Enabled;
495: bool DiskInserted;
496: int RPM; /* Rotation Per Minutes * 1000 */
497: int Density; /* 1 for DD (720 kB), 2 for HD (1.4 MB), 4 for ED (2.8 MB) */
498: Uint8 HeadTrack; /* Current position of the head */
499: // Uint8 Motor; /* State of the drive's motor : 0=OFF 1=ON */
500: Uint8 NumberOfHeads; /* 1 for single sided drive, 2 for double sided */
501:
502: Uint64 IndexPulse_Time; /* CyclesGlobalClockCounter value last time we had an index pulse with motor ON */
503: } FDC_DRIVE_STRUCT;
504:
505:
506: /**
507: * Bytes to transfer with type II/III commands are stored in this buffer
508: * which associates a specific delay to each byte. This allows to
509: * have a common method to transfer data from ST/MSA disk images (with fixed
510: * timing), as well as data from STX disk images (with possible timing variations)
511: */
512: typedef struct {
513: int Size;
514: int PosRead;
515:
516: struct {
517: Uint8 Byte;
518: Uint16 Timing;
519: } Data [ FDC_TRACK_BYTES_STANDARD*4+1000 ];
520: } FDC_BUFFER_STRUCT;
521:
522:
1.1.1.15 root 523: static FDC_STRUCT FDC; /* All variables related to the WD1772 emulation */
524: static FDC_DMA_STRUCT FDC_DMA; /* All variables related to the DMA transfer */
1.1.1.18 root 525: static FDC_DRIVE_STRUCT FDC_DRIVES[ MAX_FLOPPYDRIVES ]; /* A: and B: */
526: static FDC_BUFFER_STRUCT FDC_BUFFER; /* Buffer of Timing/Byte to transfer with the FDC */
1.1.1.15 root 527:
1.1.1.18 root 528: static Uint8 DMADiskWorkSpace[ FDC_TRACK_BYTES_STANDARD*4+1000 ];/* Workspace used to transfer bytes between floppy and DMA */
1.1.1.15 root 529: /* It should be large enough to contain a whole track */
1.1.1.18 root 530: /* We use a x4 factor when we need to simulate HD and ED too */
531:
1.1.1.15 root 532:
533:
534: /*--------------------------------------------------------------*/
535: /* Local functions prototypes */
536: /*--------------------------------------------------------------*/
537:
1.1.1.18 root 538: static Uint32 FDC_DelayToFdcCycles ( Uint32 Delay_micro );
539: static Uint32 FDC_FdcCyclesToCpuCycles ( Uint32 FdcCycles );
540: static Uint32 FDC_CpuCyclesToFdcCycles ( Uint32 CpuCycles );
541: static void FDC_StartTimer_FdcCycles ( int FdcCycles , int InternalCycleOffset );
542: static int FDC_TransferByte_FdcCycles ( int NbBytes );
1.1.1.15 root 543: static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC );
544:
545: static void FDC_ResetDMA ( void );
1.1.1.18 root 546:
547: static int FDC_GetEmulationMode ( void );
548: static int FDC_GetSectorsPerTrack ( int Drive , int Track , int Side );
549: static int FDC_GetSidesPerDisk ( int Drive , int Track );
1.1.1.19 root 550: static int FDC_GetTracksPerDisk ( int Drive );
1.1.1.18 root 551: static int FDC_GetDensity ( int Drive );
552:
553: static Uint32 FDC_GetCyclesPerRev_FdcCycles ( int Drive );
554: static void FDC_IndexPulse_Update ( void );
555: static void FDC_IndexPulse_Init ( int Drive );
1.1.1.17 root 556:
1.1.1.15 root 557: static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits );
558: static int FDC_CmdCompleteCommon ( bool DoInt );
1.1.1.18 root 559: static bool FDC_VerifyTrack ( void );
1.1.1.15 root 560: static int FDC_UpdateMotorStop ( void );
561: static int FDC_UpdateRestoreCmd ( void );
562: static int FDC_UpdateSeekCmd ( void );
563: static int FDC_UpdateStepCmd ( void );
564: static int FDC_UpdateReadSectorsCmd ( void );
565: static int FDC_UpdateWriteSectorsCmd ( void );
566: static int FDC_UpdateReadAddressCmd ( void );
567: static int FDC_UpdateReadTrackCmd ( void );
1.1.1.18 root 568: static int FDC_UpdateWriteTrackCmd ( void );
1.1.1.15 root 569:
1.1.1.18 root 570: static bool FDC_Set_MotorON ( Uint8 FDC_CR );
1.1.1.15 root 571: static int FDC_TypeI_Restore ( void );
572: static int FDC_TypeI_Seek ( void );
573: static int FDC_TypeI_Step ( void );
574: static int FDC_TypeI_StepIn ( void );
575: static int FDC_TypeI_StepOut ( void );
576: static int FDC_TypeII_ReadSector ( void );
577: static int FDC_TypeII_WriteSector(void);
578: static int FDC_TypeIII_ReadAddress ( void );
579: static int FDC_TypeIII_ReadTrack ( void );
580: static int FDC_TypeIII_WriteTrack ( void );
1.1.1.18 root 581: static int FDC_TypeIV_ForceInterrupt ( void );
1.1.1.15 root 582:
583: static int FDC_ExecuteTypeICommands ( void );
584: static int FDC_ExecuteTypeIICommands ( void );
585: static int FDC_ExecuteTypeIIICommands ( void );
586: static int FDC_ExecuteTypeIVCommands ( void );
587: static void FDC_ExecuteCommand ( void );
588:
589: static void FDC_WriteSectorCountRegister ( void );
590: static void FDC_WriteCommandRegister ( void );
591: static void FDC_WriteTrackRegister ( void );
592: static void FDC_WriteSectorRegister ( void );
593: static void FDC_WriteDataRegister ( void );
594:
1.1.1.18 root 595: static int FDC_NextSectorID_FdcCycles_ST ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side );
596: static Uint8 FDC_NextSectorID_TR_ST ( void );
597: static Uint8 FDC_NextSectorID_SR_ST ( void );
598: static Uint8 FDC_NextSectorID_LEN_ST ( void );
599: static Uint8 FDC_NextSectorID_CRC_OK_ST ( void );
600: static Uint8 FDC_ReadSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int *pSectorSize );
601: static Uint8 FDC_WriteSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize );
602: static Uint8 FDC_ReadAddress_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side );
603: static Uint8 FDC_ReadTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side );
604: static Uint8 FDC_WriteTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side , int TrackSize );
1.1 root 605:
1.1.1.2 root 606:
607: /*-----------------------------------------------------------------------*/
1.1.1.9 root 608: /**
609: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
610: */
1.1.1.18 root 611: void FDC_MemorySnapShot_Capture(bool bSave)
1.1 root 612: {
1.1.1.15 root 613: MemorySnapShot_Store(&FDC, sizeof(FDC));
614: MemorySnapShot_Store(&FDC_DMA, sizeof(FDC_DMA));
1.1.1.18 root 615: MemorySnapShot_Store(&FDC_DRIVES, sizeof(FDC_DRIVES));
616: MemorySnapShot_Store(&FDC_BUFFER, sizeof(FDC_BUFFER_STRUCT));
1.1.1.15 root 617:
618: MemorySnapShot_Store(DMADiskWorkSpace, sizeof(DMADiskWorkSpace));
1.1 root 619: }
620:
1.1.1.2 root 621:
622: /*-----------------------------------------------------------------------*/
1.1.1.9 root 623: /**
1.1.1.17 root 624: * Change the color of the drive's led color in the statusbar, depending
1.1.1.18 root 625: * on the state of the busy bit in STR
1.1.1.17 root 626: */
1.1.1.18 root 627: void FDC_Drive_Set_BusyLed ( Uint8 STR )
1.1.1.17 root 628: {
1.1.1.18 root 629: //fprintf ( stderr ,"fdc led %d %x\n" , FDC.DriveSelSignal , STR );
630: if ( FDC.DriveSelSignal < 0 )
631: return; /* no drive selected */
1.1.1.17 root 632:
1.1.1.18 root 633: if ( STR & FDC_STR_BIT_BUSY )
634: Statusbar_SetFloppyLed ( FDC.DriveSelSignal , LED_STATE_ON_BUSY );
1.1.1.17 root 635: else
1.1.1.18 root 636: Statusbar_SetFloppyLed ( FDC.DriveSelSignal , LED_STATE_ON );
637: }
638:
639:
640: /*-----------------------------------------------------------------------*/
641: /**
642: * Return a small text + length with the current values of the FDC's registers
643: * This text is displayed in the statusbar and it looks like :
644: * CC:xx HH:TT:SS:s
645: * CC=command in 2 letters
646: * xx=command in hexa
647: * HH=physical head's position
648: * TT=track register
649: * SS=sector register
650: * s=side
651: */
652: int FDC_Get_Statusbar_Text ( char *text, size_t maxlen )
653: {
654: Uint8 Command , Head , Track , Sector , Side;
655: char CommandText[ 3 ];
656: size_t written;
657: int Drive;
658:
659: Drive = FDC.DriveSelSignal;
660: if ( Drive < 0 ) /* If no drive enabled, use drive O for Head */
661: Drive = 0;
662:
663: if ( FDC_GetEmulationMode() == FDC_EMULATION_MODE_INTERNAL )
664: {
665: Command = FDC.CR;
666: Head = FDC_DRIVES[ Drive ].HeadTrack;
667: Track = FDC.TR;
668: Sector = FDC.SR;
669: Side = FDC.SideSignal;
670: }
671: else /* FDC_EMULATION_MODE_IPF */
672: {
673: IPF_FDC_StatusBar ( &Command , &Head , &Track , &Sector , &Side );
674: }
675:
676: if ( ( Command & 0xf0 ) == 0x00 ) strcpy ( CommandText , "RE" ); /* Restore */
677: else if ( ( Command & 0xf0 ) == 0x10 ) strcpy ( CommandText , "SE" ); /* Seek */
678: else if ( ( Command & 0xe0 ) == 0x20 ) strcpy ( CommandText , "ST" ); /* Step */
679: else if ( ( Command & 0xe0 ) == 0x40 ) strcpy ( CommandText , "SI" ); /* Step In */
1.1.1.19 root 680: else if ( ( Command & 0xe0 ) == 0x60 ) strcpy ( CommandText , "SO" ); /* Step Out */
1.1.1.18 root 681: else if ( ( Command & 0xe0 ) == 0x80 ) strcpy ( CommandText , "RS" ); /* Read Sector */
682: else if ( ( Command & 0xe0 ) == 0xa0 ) strcpy ( CommandText , "WS" ); /* Write Sector */
683: else if ( ( Command & 0xf0 ) == 0xc0 ) strcpy ( CommandText , "RA" ); /* Read Address */
684: else if ( ( Command & 0xf0 ) == 0xe0 ) strcpy ( CommandText , "RT" ); /* Read Track */
685: else if ( ( Command & 0xf0 ) == 0xf0 ) strcpy ( CommandText , "WT" ); /* Write Track */
686: else strcpy ( CommandText , "FI" ); /* Force Int */
687:
688: written = snprintf ( text, maxlen, "%s:%02X %02X:%02X:%02X:%d" , CommandText , Command , Head , Track , Sector , Side );
689: assert(written < maxlen); /* more space needs to be allocated */
690: return written;
1.1.1.17 root 691: }
692:
693:
694: /*-----------------------------------------------------------------------*/
695: /**
1.1.1.18 root 696: * Convert a delay in micro seconds to its equivalent of fdc cycles
697: * (delays in the WD1772 specs are relative to a 8 MHz reference clock)
1.1.1.9 root 698: */
1.1.1.18 root 699: static Uint32 FDC_DelayToFdcCycles ( Uint32 Delay_micro )
1.1 root 700: {
1.1.1.18 root 701: Uint32 FdcCycles;
702:
703: FdcCycles = (Uint32) ( ( (Uint64) FDC_CLOCK_STANDARD * Delay_micro ) / 1000000 );
1.1.1.15 root 704:
1.1.1.18 root 705: //fprintf ( stderr , "fdc state %d delay %d us %d fdc cycles\n" , FDC.Command , Delay_micro , FdcCycles );
706: return FdcCycles;
707: }
708:
709:
710: /*-----------------------------------------------------------------------*/
711: /**
712: * Convert a number of fdc cycles at freq MachineClocks.FDC_Freq to a number
713: * of cpu cycles at freq MachineClocks.CPU_Freq
1.1.1.20 root 714: * TODO : we use a fixed 8 MHz clock to convert cycles for our internal timers
715: * in cycInt.c. This should be replaced some days by using MachineClocks.CPU_Freq.
1.1.1.18 root 716: * (for Falcon, we multiply cycles by 2 to simulate a freq in the 8 MHz range)
717: */
718: static Uint32 FDC_FdcCyclesToCpuCycles ( Uint32 FdcCycles )
719: {
720: Uint32 CpuCycles;
1.1.1.15 root 721:
1.1.1.18 root 722: /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
1.1.1.15 root 723: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
724: /* FIXME : as stated above, this should be handled better, without involving 8 MHz CPU_Freq */
1.1.1.20 root 725: if (Config_IsMachineFalcon())
1.1.1.18 root 726: FdcCycles *= 2; /* correct delays for a 8 MHz FDC_Freq clock instead of 16 */
727:
728: // CpuCycles = rint ( ( (Uint64)FdcCycles * MachineClocks.CPU_Freq ) / MachineClocks.FDC_Freq );
729: CpuCycles = rint ( ( (Uint64)FdcCycles * 8021247.L ) / MachineClocks.FDC_Freq );
1.1.1.20 root 730: CpuCycles <<= nCpuFreqShift; /* Convert to x1 or x2 or x4 cpu speed */
1.1.1.15 root 731:
1.1.1.18 root 732: //fprintf ( stderr , "fdc command %d machine %d fdc cycles %d cpu cycles %d\n" , FDC.Command , ConfigureParams.System.nMachineType , FdcCycles , CpuCycles );
1.1.1.17 root 733: //if ( Delay==4104) Delay=4166; // 4166 : decade demo
1.1.1.18 root 734: return CpuCycles;
735: }
736:
737:
738: /*-----------------------------------------------------------------------*/
739: /**
740: * Convert a number of cpu cycles at freq MachineClocks.CPU_Freq to a number
741: * of fdc cycles at freq MachineClocks.FDC_Freq (this is the opposite
742: * of FDC_FdcCyclesToCpuCycles)
1.1.1.20 root 743: * TODO : we use a fixed 8 MHz clock to convert cycles for our internal timers
744: * in cycInt.c. This should be replaced some days by using MachineClocks.CPU_Freq.
1.1.1.18 root 745: */
746: static Uint32 FDC_CpuCyclesToFdcCycles ( Uint32 CpuCycles )
747: {
748: int FdcCycles;
749:
750:
1.1.1.20 root 751: CpuCycles >>= nCpuFreqShift; /* Compensate for x2 or x4 cpu speed */
1.1.1.21! root 752:
1.1.1.18 root 753: // FdcCycles = rint ( ( (Uint64)CpuCycles * MachineClocks.FDC_Freq ) / MachineClocks.CPU_Freq );
754: FdcCycles = rint ( ( (Uint64)CpuCycles * MachineClocks.FDC_Freq ) / 8021247.L );
755:
756: /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
757: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
758: /* FIXME : as stated above, this should be handled better, without involving 8 MHz CPU_Freq */
1.1.1.20 root 759: if (Config_IsMachineFalcon())
1.1.1.18 root 760: FdcCycles /= 2; /* correct delays for a 8 MHz FDC_Freq clock instead of 16 */
761:
762: //fprintf ( stderr , "fdc state %d delay %d cpu cycles %d fdc cycles\n" , FDC.Command , CpuCycles , FdcCycles );
763: return FdcCycles;
1.1 root 764: }
765:
1.1.1.2 root 766:
767: /*-----------------------------------------------------------------------*/
1.1.1.9 root 768: /**
1.1.1.17 root 769: * Start an internal timer to handle the FDC's events.
770: * If "fast floppy" mode is used, we speed up the timer by dividing
771: * the number of cycles by a fixed number.
772: */
1.1.1.18 root 773: static void FDC_StartTimer_FdcCycles ( int FdcCycles , int InternalCycleOffset )
1.1.1.17 root 774: {
1.1.1.18 root 775: //fprintf ( stderr , "fdc start timer %d cycles\n" , FdcCycles );
776:
777: if ( ( ConfigureParams.DiskImage.FastFloppy ) && ( FdcCycles > FDC_FAST_FDC_FACTOR ) )
778: FdcCycles /= FDC_FAST_FDC_FACTOR;
779:
1.1.1.20 root 780: CycInt_AddRelativeInterruptWithOffset ( FDC_FdcCyclesToCpuCycles ( FdcCycles ) , INT_CPU_CYCLE , INTERRUPT_FDC , InternalCycleOffset );
1.1.1.18 root 781: }
1.1.1.17 root 782:
783:
1.1.1.18 root 784: /*-----------------------------------------------------------------------*/
785: /**
786: * Return the number of FDC cycles required to read/write 'nb' bytes
787: * This function will always be called when FDC.DriveSelSignal >= 0, as
788: * there's no case where we transfer bytes if no drive is enabled. This
789: * means we can safely call FDC_GetDensity() here to simulate HD/ED floppies.
1.1.1.20 root 790: *
791: * 2015/10/23 [NP] As seen in the 'Bird Mad Girl Show' demo, it's possible to get
792: * FDC.DriveSelSignal < 0 once a transfer was started (for example, read sector
793: * will complete successfully). So we use DD by default in that case.
1.1.1.18 root 794: */
795: static int FDC_TransferByte_FdcCycles ( int NbBytes )
796: {
797: //fprintf ( stderr , "fdc state %d transfer %d bytes\n" , FDC.Command , NbBytes );
1.1.1.20 root 798: if ( FDC.DriveSelSignal < 0 )
799: {
800: /* Drive was unselected during the transfer : assume DD for the rest of the bytes */
801: return ( NbBytes * FDC_DELAY_CYCLE_MFM_BYTE ) / FDC_DENSITY_FACTOR_DD;
802: }
803:
1.1.1.18 root 804: return ( NbBytes * FDC_DELAY_CYCLE_MFM_BYTE ) / FDC_DRIVES[ FDC.DriveSelSignal ].Density;
1.1.1.17 root 805: }
806:
807:
808: /*-----------------------------------------------------------------------*/
809: /**
1.1.1.15 root 810: * Compute the CRC16 of 'nb' bytes stored in 'buf'.
1.1.1.9 root 811: */
1.1.1.15 root 812: static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC )
1.1 root 813: {
1.1.1.15 root 814: int i;
815:
816: crc16_reset ( pCRC );
817: for ( i=0 ; i<nb ; i++ )
818: {
819: // fprintf ( stderr , "fdc crc16 %d 0x%x\n" , i , buf[ i ] );
820: crc16_add_byte ( pCRC , buf[ i ] );
821: }
822: // fprintf ( stderr , "fdc crc16 0x%x 0x%x\n" , *pCRC>>8 , *pCRC & 0xff );
1.1 root 823: }
824:
1.1.1.2 root 825:
826: /*-----------------------------------------------------------------------*/
1.1.1.9 root 827: /**
1.1.1.18 root 828: * Init variables used in FDC and DMA emulation
829: */
830: void FDC_Init ( void )
831: {
832: int i;
833:
834: LOG_TRACE ( TRACE_FDC , "fdc init\n" );
835:
836: for ( i=0 ; i<MAX_FLOPPYDRIVES ; i++ )
837: {
838: FDC_DRIVES[ i ].Enabled = true;
839: FDC_DRIVES[ i ].DiskInserted = false;
840: FDC_DRIVES[ i ].RPM = FDC_RPM_STANDARD * 1000;
841: FDC_DRIVES[ i ].Density = FDC_DENSITY_FACTOR_DD;
842: FDC_DRIVES[ i ].HeadTrack = 0; /* Set all drives to track 0 */
843: FDC_DRIVES[ i ].NumberOfHeads = 2; /* Double sided drive */
844: FDC_DRIVES[ i ].IndexPulse_Time = 0;
845: }
846:
847: FDC_Buffer_Reset();
848:
849: FDC.EmulationMode = FDC_EMULATION_MODE_INTERNAL;
850: }
851:
852:
853: /*-----------------------------------------------------------------------*/
854: /**
1.1.1.15 root 855: * Reset variables used in FDC and DMA emulation
1.1.1.9 root 856: */
1.1.1.18 root 857:
858: /* This function is called after a hardware reset of the FDC.
859: * Cold reset is when the computer is turned off/on.
860: * Warm reset is when the reset button is pressed or the 68000
861: * RESET instruction is used.
862: * On warm reset, TR and DR should not be reset.
863: * STR is set to 0 and SR is set to 1 (verified on a real STF)
864: */
865: void FDC_Reset ( bool bCold )
1.1 root 866: {
1.1.1.15 root 867: int i;
1.1.1.6 root 868:
1.1.1.18 root 869: LOG_TRACE ( TRACE_FDC , "fdc reset mode=%s\n" , bCold?"cold":"warm" );
1.1.1.15 root 870:
1.1.1.18 root 871: /* Clear out FDC registers */
1.1.1.15 root 872: FDC.CR = 0;
873: FDC.STR = 0;
1.1.1.18 root 874: FDC.SR = 1;
875: FDC.StatusTypeI = false;
876:
877: /* On cold reset, TR and DR should be reset */
878: /* On warm reset, TR and DR value should be kept */
879: if ( bCold )
880: {
881: FDC.TR = 0;
882: FDC.DR = 0;
883: FDC_DMA.ff8604_recent_val = 0; /* Only set to 0 on cold reset */
884: }
1.1.1.15 root 885: FDC.StepDirection = 1;
886:
887: FDC.Command = FDCEMU_CMD_NULL; /* FDC emulation command currently being executed */
888: FDC.CommandState = FDCEMU_RUN_NULL;
889: FDC.CommandType = 0;
1.1.1.18 root 890: FDC.InterruptCond = 0;
891: FDC.IRQ_Signal = 0;
1.1.1.21! root 892: FDC_ClearIRQ(); /* Propagate IRQ signal to MFP GPIP5 */
1.1.1.18 root 893:
894: FDC.IndexPulse_Counter = 0;
895: for ( i=0 ; i<MAX_FLOPPYDRIVES ; i++ )
896: {
897: FDC_DRIVES[ i ].IndexPulse_Time = 0; /* Current IP's locations are lost after a reset (motor is now OFF) */
898: }
1.1.1.15 root 899:
900: FDC_DMA.Status = 1; /* no DMA error and SectorCount=0 */
901: FDC_DMA.Mode = 0;
1.1.1.18 root 902:
1.1.1.15 root 903: FDC_ResetDMA();
904:
1.1.1.18 root 905: FDC_Buffer_Reset();
906:
907: /* Also reset IPF emulation */
908: IPF_Reset();
1.1 root 909: }
910:
1.1.1.2 root 911:
912: /*-----------------------------------------------------------------------*/
1.1.1.9 root 913: /**
1.1.1.15 root 914: * Reset DMA (clear internal 16 bytes buffer)
1.1.1.9 root 915: *
916: * This is done by 'toggling' bit 8 of the DMA Mode Control register
1.1.1.18 root 917: * This will empty the FIFOs and reset Sector Count to 0
1.1.1.9 root 918: */
1.1.1.15 root 919: static void FDC_ResetDMA ( void )
1.1 root 920: {
1.1.1.15 root 921: int FrameCycles, HblCounterVideo, LineCycles;
1.1 root 922:
1.1.1.15 root 923: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
924: LOG_TRACE(TRACE_FDC, "fdc reset dma VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 925: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
926:
927: /* Empty FIFO */
928: FDC_DMA.FIFO_Size = 0;
1.1.1.15 root 929:
930: /* Reset bytes count for current DMA sector */
1.1.1.18 root 931: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
932: FDC_DMA.SectorCount = 0; /* After a reset, sector count is 0 (verified on a real STF) */
1.1.1.15 root 933:
1.1.1.18 root 934: /* Reset internal variables used to handle DMA transfer */
1.1.1.15 root 935: FDC_DMA.PosInBuffer = 0;
936: FDC_DMA.PosInBufferTransfer = 0;
937: FDC_DMA.BytesToTransfer = 0;
1.1.1.3 root 938:
1.1.1.10 root 939: /* Reset HDC command status */
1.1.1.15 root 940: HDC_ResetCommandStatus();
1.1 root 941: }
942:
1.1.1.2 root 943:
944: /*-----------------------------------------------------------------------*/
1.1.1.9 root 945: /**
1.1.1.15 root 946: * Set DMA Status at $ff8606
1.1.1.9 root 947: *
1.1.1.18 root 948: * Bit 0 - Error Status (0=Error 1=No error)
949: * Bit 1 - Sector Count Zero Status (0=Sector Count Zero)
950: * Bit 2 - Data Request signal from the FDC
1.1.1.9 root 951: */
1.1.1.15 root 952: void FDC_SetDMAStatus ( bool bError )
1.1 root 953: {
1.1.1.15 root 954: /* Set error bit */
1.1.1.6 root 955: if (!bError)
1.1.1.15 root 956: FDC_DMA.Status |= 0x1; /* No Error, set bit 0 */
1.1.1.6 root 957: else
1.1.1.15 root 958: FDC_DMA.Status &= ~0x1; /* Error, clear bit 0 */
1.1 root 959: }
960:
1.1.1.2 root 961:
962: /*-----------------------------------------------------------------------*/
1.1.1.9 root 963: /**
1.1.1.18 root 964: * Return the value of bit 8 in the FDC's DMA mode control register.
965: * 0=dma read 0x100=dma write
1.1.1.9 root 966: */
1.1.1.18 root 967: int FDC_DMA_GetModeControl_R_WR ( void )
1.1 root 968: {
1.1.1.18 root 969: return FDC_DMA.Mode & 0x100;
1.1 root 970: }
971:
1.1.1.2 root 972:
973: /*-----------------------------------------------------------------------*/
1.1.1.9 root 974: /**
1.1.1.18 root 975: * Add a byte to the DMA's FIFO buffer (read from disk).
976: * If the buffer is full and DMA is ON, write the FIFO's 16 bytes to DMA's address.
1.1.1.15 root 977: *
1.1.1.18 root 978: * NOTE [NP] : the DMA is connected to the FDC, each time a DRQ is made by the FDC,
979: * it's handled by the DMA and stored in the DMA's 16 bytes buffer. This means
1.1.1.15 root 980: * FDC_STR_BIT_LOST_DATA will never be set (but data can be lost if FDC_DMA.SectorCount==0)
1.1.1.18 root 981: *
982: * NOTE [NP] : as seen on a real STF, the unused bits when reading DMA Status at $ff8606
983: * are also changed by the DMA operations (this might not be complete, but seems quite reproducible) :
984: * - reading a byte from the FDC to the DMA will change unused bits in the lowest byte at $ff8604
985: * - transferring the 16 byte DMA buffer to RAM will change the unused bits in the 2 bytes at $ff8604
986: *
987: * In all cases, the byte read from the FDC is transferred to the DMA, even if DMA sector count is 0, so
988: * we must always update lowest byte of ff8604_recent_val.
989: * DMA FIFO is transferred only when DMA sector count is >0, so high byte of ff8604_recent_val will be
990: * updated only in that case.
1.1.1.14 root 991: */
1.1.1.18 root 992: void FDC_DMA_FIFO_Push ( Uint8 Byte )
1.1.1.14 root 993: {
1.1.1.15 root 994: Uint32 Address;
1.1.1.14 root 995:
1.1.1.18 root 996: //fprintf ( stderr , "dma push pos=%d byte=%x %lld\n" , FDC_DMA.FIFO_Size , Byte , CyclesGlobalClockCounter );
997:
998: /* Store the byte that was just read from FDC's Data Register */
999: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | Byte;
1.1.1.14 root 1000:
1.1.1.15 root 1001: if ( FDC_DMA.SectorCount == 0 )
1002: {
1003: //FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* If DMA is OFF, data are lost -> Not on the ST */
1.1.1.18 root 1004: FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
1005: return;
1.1.1.15 root 1006: }
1007:
1.1.1.18 root 1008: FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
1009:
1010: FDC_DMA.FIFO [ FDC_DMA.FIFO_Size++ ] = Byte;
1011:
1012: if ( FDC_DMA.FIFO_Size < FDC_DMA_FIFO_SIZE ) /* FIFO is not full yet */
1013: return;
1014:
1015: /* FIFO full : transfer data from FIFO to RAM and update DMA address */
1.1.1.15 root 1016: Address = FDC_GetDMAAddress();
1.1.1.18 root 1017: STMemory_SafeCopy ( Address , FDC_DMA.FIFO , FDC_DMA_FIFO_SIZE , "FDC DMA push to fifo" );
1018: FDC_WriteDMAAddress ( Address + FDC_DMA_FIFO_SIZE );
1019: FDC_DMA.FIFO_Size = 0; /* FIFO is now empty again */
1020:
1021: /* Store the last word that was just transferred by the DMA */
1022: FDC_DMA.ff8604_recent_val = ( FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-2 ] << 8 ) | FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-1 ];
1.1.1.15 root 1023:
1024: /* Update Sector Count */
1.1.1.18 root 1025: FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
1.1.1.15 root 1026: if ( FDC_DMA.BytesInSector <= 0 )
1027: {
1028: FDC_DMA.SectorCount--;
1.1.1.18 root 1029: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
1.1.1.15 root 1030: }
1.1 root 1031: }
1032:
1.1.1.2 root 1033:
1034: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1035: /**
1.1.1.18 root 1036: * Get a byte from the DMA's FIFO buffer (write to disk).
1037: * If the buffer is empty and DMA is ON, load 16 bytes in the FIFO from DMA's address.
1.1.1.15 root 1038: *
1.1.1.18 root 1039: * NOTE [NP] : on a real ST, there're two 16 byte DMA FIFO, this allows to refill one FIFO
1040: * while the other FIFO is used to transfer data to the FDC. We don't emulate this at the
1041: * moment, as it doesn't cause any problem (when a DMA is set to write mode, we would need
1042: * to prefill 32 bytes instead of 16 bytes as we do now)
1.1.1.15 root 1043: *
1.1.1.18 root 1044: * NOTE [NP] : as with FDC_DMA_FIFO_Push, this also changes the unused bits at $ff8606
1.1.1.9 root 1045: */
1.1.1.18 root 1046: Uint8 FDC_DMA_FIFO_Pull ( void )
1.1 root 1047: {
1.1.1.15 root 1048: Uint32 Address;
1.1.1.18 root 1049: Uint8 Byte;
1.1.1.15 root 1050:
1.1.1.18 root 1051: //fprintf ( stderr , "fifo pull %d %d %d\n" , FDC_DMA.BytesToTransfer , FDC_DMA.BytesInSector , FDC_DMA.SectorCount );
1.1.1.15 root 1052: if ( FDC_DMA.SectorCount == 0 )
1053: {
1054: //FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* If DMA is OFF, data are lost -> Not on the ST */
1.1.1.18 root 1055: FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
1056: return 0; /* Write a '0' byte when dma is off */
1.1.1.15 root 1057: }
1.1 root 1058:
1.1.1.18 root 1059: FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
1.1 root 1060:
1.1.1.18 root 1061: if ( FDC_DMA.FIFO_Size > 0 ) /* FIFO is not empty yet */
1062: Byte = FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE - ( FDC_DMA.FIFO_Size-- ) ]; /* return byte at pos 0, 1, .., 15 */
1063:
1064: else
1.1.1.6 root 1065: {
1.1.1.18 root 1066: /* FIFO empty : transfer data from RAM to FIFO and update DMA address */
1067: Address = FDC_GetDMAAddress();
1068: memcpy ( FDC_DMA.FIFO , &STRam[ Address ] , FDC_DMA_FIFO_SIZE );/* TODO : check we read from a valid RAM location ? */
1069: FDC_WriteDMAAddress ( Address + FDC_DMA_FIFO_SIZE );
1070: FDC_DMA.FIFO_Size = FDC_DMA_FIFO_SIZE - 1; /* FIFO is now full again (minus the byte we will return below) */
1071:
1072: /* Store the last word that was just transferred by the DMA */
1073: FDC_DMA.ff8604_recent_val = ( FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-2 ] << 8 ) | FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-1 ];
1074:
1075: /* Update Sector Count */
1076: FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
1077: if ( FDC_DMA.BytesInSector < 0 )
1078: {
1079: FDC_DMA.SectorCount--;
1080: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
1081: }
1082:
1.1.1.20 root 1083: Byte = FDC_DMA.FIFO [ 0 ]; /* return the 1st byte we just transferred in the FIFO */
1.1.1.6 root 1084: }
1085:
1.1.1.18 root 1086: /* Store the byte that will be written to FDC's Data Register */
1087: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | Byte;
1088:
1089: return Byte;
1.1 root 1090: }
1091:
1.1.1.2 root 1092:
1093: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1094: /**
1.1.1.21! root 1095: * Return the value of FDC_DMA.SectorCount (used in floppy_ipf.c)
! 1096: */
! 1097: int FDC_DMA_GetSectorCount ( void )
! 1098: {
! 1099: return FDC_DMA.SectorCount;
! 1100: }
! 1101:
! 1102:
! 1103: /*-----------------------------------------------------------------------*/
! 1104: /**
1.1.1.18 root 1105: * Reset the buffer used to transfer data between the FDC and the DMA
1.1.1.16 root 1106: */
1.1.1.18 root 1107: void FDC_Buffer_Reset ( void )
1.1.1.16 root 1108: {
1.1.1.18 root 1109: FDC_BUFFER.Size = 0;
1110: FDC_BUFFER.PosRead = 0;
1.1.1.16 root 1111: }
1112:
1113:
1114: /*-----------------------------------------------------------------------*/
1115: /**
1.1.1.18 root 1116: * Add a byte to the FDC transfer buffer, using a specific timing
1.1.1.9 root 1117: */
1.1.1.18 root 1118: void FDC_Buffer_Add_Timing ( Uint8 Byte , Uint16 Timing )
1.1 root 1119: {
1.1.1.18 root 1120: FDC_BUFFER.Data[ FDC_BUFFER.Size ].Byte = Byte;
1121: FDC_BUFFER.Data[ FDC_BUFFER.Size ].Timing = Timing;
1122: FDC_BUFFER.Size++;
1.1 root 1123: }
1124:
1.1.1.2 root 1125:
1126: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1127: /**
1.1.1.18 root 1128: * Add a byte to the FDC transfer buffer, using a default timing
1.1.1.9 root 1129: */
1.1.1.18 root 1130: void FDC_Buffer_Add ( Uint8 Byte )
1.1 root 1131: {
1.1.1.18 root 1132: FDC_Buffer_Add_Timing ( Byte , FDC_TransferByte_FdcCycles ( 1 ) );
1.1 root 1133: }
1134:
1.1.1.2 root 1135:
1.1.1.17 root 1136: /*-----------------------------------------------------------------------*/
1137: /**
1.1.1.18 root 1138: * Return the timing needed to transfer the Byte at the current position
1.1.1.17 root 1139: */
1.1.1.18 root 1140: Uint16 FDC_Buffer_Read_Timing ( void )
1.1.1.17 root 1141: {
1.1.1.18 root 1142: //fprintf ( stderr , "read timing %d %x\n" , FDC_BUFFER.PosRead , FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Timing );
1143: return FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Timing;
1.1.1.17 root 1144: }
1145:
1146:
1147: /*-----------------------------------------------------------------------*/
1148: /**
1.1.1.18 root 1149: * Return the Byte at the current position and increment position
1.1.1.17 root 1150: */
1.1.1.18 root 1151: Uint8 FDC_Buffer_Read_Byte ( void )
1.1.1.17 root 1152: {
1.1.1.18 root 1153: //fprintf ( stderr , "read byte %d %x\n" , FDC_BUFFER.PosRead , FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Byte );
1154: return FDC_BUFFER.Data[ FDC_BUFFER.PosRead++ ].Byte;
1.1.1.17 root 1155: }
1156:
1157:
1158: /*-----------------------------------------------------------------------*/
1159: /**
1.1.1.18 root 1160: * Return the Byte at a given position
1.1.1.17 root 1161: */
1.1.1.18 root 1162: Uint8 FDC_Buffer_Read_Byte_pos ( int pos )
1.1.1.17 root 1163: {
1.1.1.18 root 1164: //fprintf ( stderr , "read byte pos %d %x\n" , pos , FDC_BUFFER.Data[ pos ].Byte );
1165: return FDC_BUFFER.Data[ pos ].Byte;
1.1.1.17 root 1166: }
1167:
1168:
1169: /*-----------------------------------------------------------------------*/
1170: /**
1.1.1.18 root 1171: * Return the number of bytes stored in FDC_BUFFER
1.1.1.17 root 1172: */
1.1.1.18 root 1173: int FDC_Buffer_Get_Size ( void )
1.1.1.17 root 1174: {
1.1.1.18 root 1175: return FDC_BUFFER.Size;
1.1.1.17 root 1176: }
1177:
1178:
1179: /*-----------------------------------------------------------------------*/
1180: /**
1.1.1.18 root 1181: * Return the mode to handle a read/write in $ff86xx
1182: * Depending on the images inserted in each floppy drive and on the
1183: * selected drive, we must choose which fdc emulation should be used.
1184: * Possible emulation methods are 'internal' or 'ipf'.
1185: * To avoid mixing emulation methods on both drives when possible (which
1186: * could lead to inconstancies when precise timings are required), we also
1187: * use the IPF mode for an empty drive if the other drive contains an IPF
1188: * image.
1189: * If no drive is selected, we must use the previous mode (before drives were
1190: * unselected), not the internal one : in case some commands are sent when
1191: * drives are deselected and the drive was in IPF mode, we must send
1192: * the command to IPF to ensure no command are lost if the drive is selected again
1193: * (eg : D0 command in "Saint Dragon" IPF)
1.1.1.17 root 1194: */
1.1.1.18 root 1195: static int FDC_GetEmulationMode ( void )
1.1.1.17 root 1196: {
1.1.1.18 root 1197: int Mode;
1.1.1.17 root 1198:
1.1.1.18 root 1199: Mode = FDC.EmulationMode; /* Default to previous mode if no drive is selected */
1.1.1.17 root 1200:
1.1.1.18 root 1201: /* Check drive 1 first */
1202: if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x04 ) == 0 )
1.1.1.17 root 1203: {
1.1.1.18 root 1204: if ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_IPF )
1205: Mode = FDC_EMULATION_MODE_IPF;
1206: else if ( ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_NONE ) /* Drive 1 is empty */
1207: && ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_IPF ) ) /* Drive 0 is an IPF image */
1208: Mode = FDC_EMULATION_MODE_IPF; /* Use IPF for drive 1 too */
1.1.1.17 root 1209: else
1.1.1.18 root 1210: Mode = FDC_EMULATION_MODE_INTERNAL;
1.1.1.17 root 1211: }
1212:
1.1.1.18 root 1213: /* If both drive 0 and drive 1 are enabled, we keep only drive 0 to choose emulation's mode */
1214: if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x02 ) == 0 )
1.1.1.17 root 1215: {
1.1.1.18 root 1216: if ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_IPF )
1217: Mode = FDC_EMULATION_MODE_IPF;
1218: else if ( ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_NONE ) /* Drive 0 is empty */
1219: && ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_IPF ) ) /* Drive 1 is an IPF image */
1220: Mode = FDC_EMULATION_MODE_IPF; /* Use IPF for drive 0 too */
1221: else
1222: Mode = FDC_EMULATION_MODE_INTERNAL;
1.1.1.17 root 1223: }
1224:
1.1.1.18 root 1225: FDC.EmulationMode = Mode;
1226: //fprintf ( stderr , "emul mode %x %d\n" , PSGRegisters[PSG_REG_IO_PORTA] & 0x06 , Mode );
1227: // return FDC_EMULATION_MODE_INTERNAL;
1228: // return FDC_EMULATION_MODE_IPF;
1229: return Mode;
1.1.1.17 root 1230: }
1231:
1232:
1.1.1.2 root 1233: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1234: /**
1.1.1.18 root 1235: * Update the FDC's internal variables on a regular basis.
1236: * To get correct accuracy, this should be called every 200-500 FDC cycles
1237: * So far, we only need to update the index position for the valid
1238: * drive/floppy ; updating every 500 cycles is enough for this case.
1.1.1.9 root 1239: */
1.1.1.19 root 1240: static void FDC_UpdateAll(void)
1.1.1.8 root 1241: {
1.1.1.18 root 1242: FDC_IndexPulse_Update ();
1.1.1.8 root 1243: }
1244:
1245:
1246: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1247: /**
1.1.1.18 root 1248: * This function is used to enable/disable a drive when
1249: * using the UI or command line parameters
1.1.1.9 root 1250: */
1.1.1.18 root 1251: void FDC_Drive_Set_Enable ( int Drive , bool value )
1.1 root 1252: {
1.1.1.19 root 1253: LOG_TRACE ( TRACE_FDC , "fdc enable drive=%d %s\n" , Drive , value?"on":"off" );
1.1.1.17 root 1254:
1.1.1.18 root 1255: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1256: FDC_DRIVES[ Drive ].Enabled = value;
1.1.1.6 root 1257:
1.1.1.18 root 1258: /* Also forward change to IPF emulation */
1259: IPF_Drive_Set_Enable ( Drive , value );
1260: }
1.1.1.6 root 1261:
1.1.1.17 root 1262:
1.1.1.18 root 1263: /*-----------------------------------------------------------------------*/
1264: /**
1265: * This function is used to choose single sided or double sided for a drive
1266: * when using the UI or command line parameters
1267: */
1268: void FDC_Drive_Set_NumberOfHeads ( int Drive , int NbrHeads )
1269: {
1.1.1.19 root 1270: LOG_TRACE ( TRACE_FDC , "fdc set nbr heads drive=%d %d\n" , Drive , NbrHeads );
1.1.1.17 root 1271:
1.1.1.18 root 1272: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1273: FDC_DRIVES[ Drive ].NumberOfHeads = NbrHeads;
1.1.1.6 root 1274:
1.1.1.18 root 1275: /* Also forward change to IPF emulation */
1276: IPF_Drive_Set_DoubleSided ( Drive , NbrHeads==2 ? true : false );
1277: }
1.1.1.11 root 1278:
1.1.1.15 root 1279:
1.1.1.18 root 1280: /*-----------------------------------------------------------------------*/
1281: /**
1282: * This function is called when a floppy is inserted in a drive
1283: * using the UI or command line parameters
1284: */
1285: void FDC_InsertFloppy ( int Drive )
1286: {
1287: LOG_TRACE ( TRACE_FDC , "fdc insert drive=%d\n" , Drive );
1.1.1.15 root 1288:
1.1.1.18 root 1289: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1290: {
1291: FDC_DRIVES[ Drive ].DiskInserted = true;
1292: if ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) != 0 ) /* If we insert a floppy while motor is already on, we must */
1293: FDC_IndexPulse_Init ( Drive ); /* init the index pulse's position */
1294: else
1295: FDC_DRIVES[ Drive ].IndexPulse_Time = 0; /* Index pulse's position not known yet */
1296: FDC_DRIVES[ Drive ].Density = FDC_GetDensity ( Drive );
1297: }
1298: }
1299:
1300:
1301: /*-----------------------------------------------------------------------*/
1302: /**
1303: * This function is called when a floppy is ejected from a drive
1304: * using the UI or command line parameters
1305: */
1306: void FDC_EjectFloppy ( int Drive )
1307: {
1308: LOG_TRACE ( TRACE_FDC , "fdc eject drive=%d\n" , Drive );
1309:
1310: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1311: {
1312: FDC_DRIVES[ Drive ].DiskInserted = false;
1313: FDC_DRIVES[ Drive ].IndexPulse_Time = 0; /* Stop counting index pulses on an empty drive */
1314: }
1315: }
1316:
1317:
1318: /*-----------------------------------------------------------------------*/
1319: /**
1320: * Handle a write in the IO_PORTA register $E through $ff8802. Only bits
1321: * 0-2 are available here, others are masked to 0.
1322: * bit 0 : side select
1323: * bit 1-2 : drive select
1324: *
1325: * - For internal FDC emulation, we init index pulse if the active drive
1326: * changed
1327: * - We also forward the change to IPF emulation, as it doesn't have direct access
1328: * to this IO_PORTA register.
1329: *
1330: * If both drives are selected, we keep only drive 0
1331: */
1332: void FDC_SetDriveSide ( Uint8 io_porta_old , Uint8 io_porta_new )
1333: {
1334: int Side;
1335: int Drive;
1336:
1337: if ( io_porta_old == io_porta_new )
1338: return; /* No change */
1339:
1340: Side = ( (~io_porta_new) & 0x01 ); /* Side 0 or 1 */
1341:
1342: Drive = -1; /* By default, don't select any drive */
1343:
1344: /* Check drive 1 first */
1345: if ( ( io_porta_new & 0x04 ) == 0 )
1346: Drive = 1; /* Select drive 1 */
1347:
1348: /* If both drive 0 and drive 1 are enabled, we keep only drive 0 as newdrive */
1349: if ( ( io_porta_new & 0x02 ) == 0 )
1350: Drive = 0; /* Select drive 0 (and un-select drive 1 if set above) */
1351:
1352: LOG_TRACE(TRACE_FDC, "fdc change drive/side io_porta_old=0x%x io_porta_new=0x%x side %d->%d drive %d->%d VBL=%d HBL=%d\n" ,
1353: io_porta_old , io_porta_new , FDC.SideSignal , Side , FDC.DriveSelSignal , Drive , nVBLs , nHBL );
1354:
1355: if ( FDC.DriveSelSignal != Drive )
1356: {
1357: if ( FDC.DriveSelSignal >= 0 ) /* A drive was previously enabled */
1358: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time = 0; /* Stop counting index pulses on the previous drive */
1359:
1360: if ( Drive >= 0 ) /* A new drive is enabled */
1361: {
1362: if ( ( FDC_DRIVES[ Drive ].DiskInserted ) /* If there's a disk in the new drive and motor is already */
1363: && ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) != 0 ) ) /* on, we must init the index pulse's position */
1364: FDC_IndexPulse_Init ( Drive );
1365: else
1366: FDC_DRIVES[ Drive ].IndexPulse_Time = 0; /* Index pulse's position not known yet */
1.1.1.6 root 1367: }
1.1.1.15 root 1368: }
1.1.1.6 root 1369:
1.1.1.18 root 1370: FDC.SideSignal = Side;
1371: FDC.DriveSelSignal = Drive;
1372:
1373:
1374: /* Also forward change to IPF emulation */
1375: IPF_SetDriveSide ( io_porta_old , io_porta_new );
1376: }
1377:
1378:
1379: /*-----------------------------------------------------------------------*/
1380: /**
1381: * Return the number of sectors for track/side for the current floppy in a drive
1382: * TODO [NP] : this function calls Floppy_FindDiskDetails which handles only ST/MSA
1383: * disk images so far, so this implies all tracks have in fact the same number
1384: * of sectors (we don't use Track and Side for now)
1385: * Drive should be a valid drive (0 or 1)
1386: */
1387: static int FDC_GetSectorsPerTrack ( int Drive , int Track , int Side )
1388: {
1389: Uint16 SectorsPerTrack;
1390:
1391: if (EmulationDrives[ Drive ].bDiskInserted)
1392: {
1393: Floppy_FindDiskDetails ( EmulationDrives[ Drive ].pBuffer , EmulationDrives[ Drive ].nImageBytes , &SectorsPerTrack , NULL );
1394: return SectorsPerTrack;
1395: }
1396: else
1397: return 0;
1398: }
1399:
1400:
1401: /*-----------------------------------------------------------------------*/
1402: /**
1403: * Return the number of sides for a track for the current floppy in a drive
1404: * Drive should be a valid drive (0 or 1)
1405: */
1406: static int FDC_GetSidesPerDisk ( int Drive , int Track )
1407: {
1408: Uint16 SidesPerDisk;
1409:
1410: if (EmulationDrives[ Drive ].bDiskInserted)
1411: {
1412: Floppy_FindDiskDetails ( EmulationDrives[ Drive ].pBuffer , EmulationDrives[ Drive ].nImageBytes , NULL , &SidesPerDisk );
1413: return SidesPerDisk; /* 1 or 2 */
1414: }
1415: else
1416: return 0;
1417: }
1418:
1419:
1420: /*-----------------------------------------------------------------------*/
1421: /**
1.1.1.19 root 1422: * Return the number of tracks for the current floppy in a drive
1423: * For ST/MSA, this assumes both sides have the same number of tracks
1424: * Drive should be a valid drive (0 or 1)
1425: */
1426: static int FDC_GetTracksPerDisk ( int Drive )
1427: {
1428: Uint16 SectorsPerTrack;
1429: Uint16 SidesPerDisk;
1430:
1431: if (EmulationDrives[ Drive ].bDiskInserted)
1432: {
1433: Floppy_FindDiskDetails ( EmulationDrives[ Drive ].pBuffer , EmulationDrives[ Drive ].nImageBytes , &SectorsPerTrack , &SidesPerDisk );
1434: return ( ( EmulationDrives[Drive].nImageBytes / NUMBYTESPERSECTOR ) / SectorsPerTrack ) / SidesPerDisk;
1435: }
1436: else
1437: return 0;
1438: }
1439:
1440:
1441: /*-----------------------------------------------------------------------*/
1442: /**
1.1.1.18 root 1443: * Return a density factor for the current floppy in a drive
1444: * A DD track is usually 9 or 10 sectors and has a x1 factor,
1445: * but to handle HD or ED ST/MSA disk images, we check if we
1446: * have more than 18 or 36 sectors.
1447: * In that case, we use a x2 or x4 factor for theses disks,
1448: * to simulate more bytes per FDC cycles.
1449: * Drive should be a valid drive (0 or 1)
1450: */
1451: static int FDC_GetDensity ( int Drive )
1452: {
1453: Uint16 SectorsPerTrack;
1454:
1455: if ( EmulationDrives[ Drive ].bDiskInserted )
1.1.1.15 root 1456: {
1.1.1.18 root 1457: SectorsPerTrack = FDC_GetSectorsPerTrack ( Drive , FDC_DRIVES[ Drive ].HeadTrack , FDC.SideSignal );
1458: if ( SectorsPerTrack >= 36 )
1459: return FDC_DENSITY_FACTOR_ED; /* Simulate a ED disk, 36 sectors or more */
1460: else if ( SectorsPerTrack >= 18 )
1461: return FDC_DENSITY_FACTOR_HD; /* Simulate a HD disk, between 18 and 36 sectors */
1462: else
1463: return FDC_DENSITY_FACTOR_DD; /* Normal DD disk */
1.1.1.6 root 1464: }
1.1.1.18 root 1465: else
1466: return FDC_DENSITY_FACTOR_DD; /* No disk, default to Double Density */
1467: }
1468:
1469:
1470: /*-----------------------------------------------------------------------*/
1471: /**
1472: * Return the number of bytes in a raw track
1473: * For ST/MSA disk images, we consider all the tracks have the same size.
1474: * To simulate HD/ED floppies, we multiply the size by a density factor.
1475: * Drive should be a valid drive (0 or 1)
1476: */
1477: int FDC_GetBytesPerTrack ( int Drive )
1478: {
1479: int TrackSize;
1480:
1481: TrackSize = FDC_TRACK_BYTES_STANDARD; /* For a standard DD disk */
1482: return TrackSize * FDC_DRIVES[ Drive ].Density; /* Take density into account for HD/ED floppies */
1483: }
1484:
1485:
1486: /*-----------------------------------------------------------------------*/
1487: /**
1488: * Get the number of FDC cycles for one revolution of the floppy
1489: * RPM is already multiplied by 1000 to simulate non-integer values
1490: * (for Falcon, we divide cycles by 2 to simulate a FDC freq in the 8 MHz range)
1491: * For STX image, the number of cycles depends on drive/track/side.
1492: * Drive should be a valid drive (0 or 1)
1493: */
1494: static Uint32 FDC_GetCyclesPerRev_FdcCycles ( int Drive )
1495: {
1496: Uint32 FdcCyclesPerRev;
1497:
1498: /* If the inserted disk is an STX, we use the supplied track length to compute cycles per rev */
1499: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
1500: return FDC_GetCyclesPerRev_FdcCycles_STX ( Drive , FDC_DRIVES[ Drive ].HeadTrack , FDC.SideSignal );
1501:
1502: /* Assume a standard length for all tracks for ST/MSA images */
1503: FdcCyclesPerRev = (Uint64)(MachineClocks.FDC_Freq * 1000.L) / ( FDC_DRIVES[ Drive ].RPM / 60 );
1504:
1505: /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
1506: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
1507: /* FIXME : this should be handled better, without involving 8 MHz CPU_Freq */
1.1.1.20 root 1508: if (Config_IsMachineFalcon())
1.1.1.18 root 1509: FdcCyclesPerRev /= 2; /* correct delays for a 8 MHz FDC_Freq clock instead of 16 */
1510:
1511: return FdcCyclesPerRev;
1512: }
1513:
1514:
1515:
1516: /*-----------------------------------------------------------------------*/
1517: /**
1518: * If some valid drive/floppy are available and the motor signal is on,
1519: * update the current angular position for the drive and check if
1520: * a new index pulse was reached. Increase Index Pulse counter in that case.
1521: *
1522: * This function should be called at least every 500 FDC cycles when motor
1523: * is ON to get good accuracy.
1524: *
1525: * [NP] TODO : should we have 2 different Index Pulses for each side or do they
1526: * happen at the same time ?
1527: */
1.1.1.19 root 1528: static void FDC_IndexPulse_Update(void)
1.1.1.18 root 1529: {
1530: Uint32 FdcCyclesPerRev;
1531: int FrameCycles, HblCounterVideo, LineCycles;
1532:
1533: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1534:
1535: //fprintf ( stderr , "update index drive=%d side=%d counter=%d VBL=%d HBL=%d\n" , FDC.DriveSelSignal , FDC.SideSignal , FDC.IndexPulse_Counter , nVBLs , nHBL );
1536:
1537: if ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) == 0 )
1538: return; /* Motor is OFF, nothing to update */
1539:
1540: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
1541: || ( !FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted ) )
1542: return; /* No valid drive/floppy, nothing to update */
1543:
1544: if ( FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time == 0 ) /* No reference Index Pulse for this drive */
1545: FDC_IndexPulse_Init ( FDC.DriveSelSignal ); /* (could be the case after a 'reset') */
1546:
1547: FdcCyclesPerRev = FDC_GetCyclesPerRev_FdcCycles ( FDC.DriveSelSignal );
1548:
1549: if ( CyclesGlobalClockCounter - FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time >= FDC_FdcCyclesToCpuCycles ( FdcCyclesPerRev ) )
1550: {
1551: /* Store new position of the most recent Index Pulse */
1552: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time += FDC_FdcCyclesToCpuCycles ( FdcCyclesPerRev );
1553: FDC.IndexPulse_Counter++;
1554: LOG_TRACE(TRACE_FDC, "fdc update index drive=%d side=%d counter=%d ip_time=%"PRIu64" VBL=%d HBL=%d\n" ,
1555: FDC.DriveSelSignal , FDC.SideSignal , FDC.IndexPulse_Counter ,
1556: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time , nVBLs , nHBL );
1557:
1558: if ( FDC.InterruptCond & FDC_INTERRUPT_COND_IP ) /* Do we have a "force int on index pulse" command running ? */
1559: {
1560: LOG_TRACE(TRACE_FDC, "fdc type IV force int on index, set irq VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1561: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1562: FDC_SetIRQ ( FDC_IRQ_SOURCE_INDEX );
1563: }
1564: }
1565: }
1566:
1567:
1568: /*-----------------------------------------------------------------------*/
1569: /**
1570: * When motor is started, the position of the next index pulse will be random,
1571: * as we don't know how much the floppy rotated when the motor was stopped or
1572: * the floppy was inserted.
1573: * We compute a random position in the "past" (less than one revolution)
1574: * and use it as a reference to detect the next index pulse.
1575: *
1576: */
1577: static void FDC_IndexPulse_Init ( int Drive )
1578: {
1579: Uint32 FdcCyclesPerRev;
1580: Uint64 IndexPulse_Time;
1581:
1582: FdcCyclesPerRev = FDC_GetCyclesPerRev_FdcCycles ( Drive );
1583: IndexPulse_Time = CyclesGlobalClockCounter - rand () % FDC_FdcCyclesToCpuCycles ( FdcCyclesPerRev );
1584: if ( IndexPulse_Time <= 0 ) /* Should not happen (only if FDC_IndexPulse_Init is */
1585: IndexPulse_Time = 1; /* called just after emulation starts) */
1586: FDC_DRIVES[ Drive ].IndexPulse_Time = IndexPulse_Time;
1587:
1588: LOG_TRACE(TRACE_FDC, "fdc init index drive=%d side=%d counter=%d ip_time=%"PRIu64" VBL=%d HBL=%d\n" ,
1589: FDC.DriveSelSignal , FDC.SideSignal , FDC.IndexPulse_Counter ,
1590: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time , nVBLs , nHBL );
1591: }
1592:
1593:
1594: /*-----------------------------------------------------------------------*/
1595: /**
1596: * Return the number of FDC cycles since the previous index pulse signal
1597: * for the current drive.
1598: * If there's no available drive/floppy (i.e. no index), we return -1
1599: */
1600: int FDC_IndexPulse_GetCurrentPos_FdcCycles ( Uint32 *pFdcCyclesPerRev )
1601: {
1602: Uint32 FdcCyclesPerRev;
1603: Uint32 CpuCyclesSinceIndex;
1604:
1605: if ( ( FDC.DriveSelSignal < 0 ) || ( FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time == 0 ) )
1606: return -1;
1607:
1608: FdcCyclesPerRev = FDC_GetCyclesPerRev_FdcCycles ( FDC.DriveSelSignal );
1609: CpuCyclesSinceIndex = CyclesGlobalClockCounter - FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time;
1610:
1611: if ( pFdcCyclesPerRev )
1612: *pFdcCyclesPerRev = FdcCyclesPerRev;
1613:
1614: //fprintf ( stderr , "current pos %d %lld %d %lld\n" , FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time ,
1615: // FdcCyclesPerRev , CyclesGlobalClockCounter - FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time );
1616:
1617: return FDC_CpuCyclesToFdcCycles ( CpuCyclesSinceIndex );
1618: }
1619:
1620:
1621: /*-----------------------------------------------------------------------*/
1622: /**
1623: * Return the current position in the track relative to the index pulse.
1624: * For standard floppy, this is a number of bytes in the range [0,6250[
1625: * If there's no available drive/floppy and no index, we return -1
1626: * To simulate HD/ED floppies, we multiply the number of bytes by a density factor.
1627: */
1628: int FDC_IndexPulse_GetCurrentPos_NbBytes ( void )
1629: {
1630: int FdcCyclesSinceIndex;
1631:
1632: FdcCyclesSinceIndex = FDC_IndexPulse_GetCurrentPos_FdcCycles ( NULL );
1633: if ( FdcCyclesSinceIndex < 0 ) /* No drive/floppy available at the moment */
1634: return -1;
1635: //fprintf ( stderr , "fdc index current pos new=%d\n" , FdcCyclesSinceIndex / FDC_DELAY_CYCLE_MFM_BYTE );
1636:
1637: return FdcCyclesSinceIndex * FDC_DRIVES[ FDC.DriveSelSignal ].Density / FDC_DELAY_CYCLE_MFM_BYTE;
1638: }
1639:
1640:
1641:
1642: /*-----------------------------------------------------------------------*/
1643: /**
1644: * Return the current state of the index pulse signal.
1645: * The signal goes to 1 when reaching the index pulse location and remain
1646: * to 1 during 1.5 ms (approx 46 bytes).
1647: * During the rest of the track, the signal will be 0 (it will also be 0
1648: * if the drive if OFF or empty)
1649: */
1650: int FDC_IndexPulse_GetState ( void )
1651: {
1652: int state;
1653: int FdcCyclesSinceIndex;
1654:
1655: FdcCyclesSinceIndex = FDC_IndexPulse_GetCurrentPos_FdcCycles ( NULL );
1656:
1657: state = 0;
1658: if ( ( FdcCyclesSinceIndex >= 0 ) /* We have a valid drive/floppy */
1659: && ( (Uint32)FdcCyclesSinceIndex < FDC_DelayToFdcCycles ( FDC_DELAY_US_INDEX_PULSE_LENGTH ) ) )
1660: state = 1;
1661:
1662: //fprintf ( stderr , "fdc index state 2 pos pos=%d state=%d\n" , FdcCyclesSinceIndex , state );
1663: return state;
1664: }
1665:
1666:
1667: /*-----------------------------------------------------------------------*/
1668: /**
1669: * Return the number of FDC cycles before reaching the next index pulse signal.
1670: * If there's no available drive/floppy and no index, we return -1
1671: */
1672: int FDC_NextIndexPulse_FdcCycles ( void )
1673: {
1674: Uint32 FdcCyclesPerRev;
1675: int FdcCyclesSinceIndex;
1676: int res;
1677:
1678: FdcCyclesSinceIndex = FDC_IndexPulse_GetCurrentPos_FdcCycles ( &FdcCyclesPerRev );
1679: if ( FdcCyclesSinceIndex < 0 ) /* No drive/floppy available at the moment */
1680: return -1;
1681:
1682: res = FdcCyclesPerRev - FdcCyclesSinceIndex;
1683:
1684: /* If the next IP is in 0 or 1 cycle, we consider this is a rounding error */
1685: /* and we wait for one full revolution (this can happen in Force Int on Index Pulse */
1686: /* when we call FDC_NextIndexPulse_FdcCycles in a loop) */
1687: if ( res <= 1 )
1688: res = FdcCyclesPerRev; // TODO : 0 should be allowed
1689:
1690: //fprintf ( stderr , "fdc next index current pos new=%d\n" , res );
1691: return res;
1692: }
1693:
1694:
1695: /*-----------------------------------------------------------------------*/
1696: /**
1697: * Set the IRQ signal
1698: * This is called either on command completion, or when an index pulse
1699: * is received or when the "force interrupt immediate" command is used.
1700: * This function can also be called from the HDC emulation or from another
1701: * FDC emulation module (IPF for example)
1.1.1.19 root 1702: *
1703: * NOTE : although high/1 on the IRQ pin of the FDC means an interrupt is
1704: * requested, this signal is inverted before going into MFP's GPIP5.
1705: * So, we must set the line to low/0 to request an interrupt.
1.1.1.18 root 1706: */
1707: void FDC_SetIRQ ( Uint8 IRQ_Source )
1708: {
1709: if ( FDC.IRQ_Signal != 0 ) /* Don't set MFP's IRQ again if already set */
1710: {
1711: LOG_TRACE(TRACE_FDC, "fdc set irq, irq 0x%x already set VBL=%d HBL=%d\n" , FDC.IRQ_Signal , nVBLs , nHBL );
1712: }
1713:
1714: else
1715: {
1716: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1.1.1.19 root 1717: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_LOW );
1.1.1.18 root 1718: LOG_TRACE(TRACE_FDC, "fdc set irq 0x%x source 0x%x VBL=%d HBL=%d\n" , FDC.IRQ_Signal , IRQ_Source , nVBLs , nHBL );
1719: }
1720:
1721: /* If IRQ comes from HDC or other sources (IPF), we don't need */
1722: /* to handle the forced IRQ case used in FDC */
1723: if ( IRQ_Source == FDC_IRQ_SOURCE_HDC )
1724: FDC.IRQ_Signal = FDC_IRQ_SOURCE_HDC;
1725:
1726: else if ( IRQ_Source == FDC_IRQ_SOURCE_OTHER )
1727: FDC.IRQ_Signal = FDC_IRQ_SOURCE_OTHER;
1728:
1729: else /* IRQ comes from FDC */
1730: {
1731: FDC.IRQ_Signal &= ~( FDC_IRQ_SOURCE_HDC | FDC_IRQ_SOURCE_OTHER );
1732: FDC.IRQ_Signal |= IRQ_Source;
1733: }
1734: }
1735:
1736:
1737: /*-----------------------------------------------------------------------*/
1738: /**
1739: * Reset the IRQ signal ; in case the source of the interrupt is also
1740: * a "force interrupt immediate" command, the IRQ signal should not be cleared
1741: * (only command 0xD0 or any new command followed by a read of status reg
1742: * can clear the forced IRQ)
1.1.1.19 root 1743: *
1744: * NOTE : although low/0 on the IRQ pin of the FDC means interrupt is
1745: * cleared, this signal is inverted before going into MFP's GPIP5.
1746: * So, we must set the line to high/1 to clear interrupt request.
1.1.1.18 root 1747: */
1748: void FDC_ClearIRQ ( void )
1749: {
1750: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED ) == 0 )
1751: {
1752: FDC.IRQ_Signal = 0;
1.1.1.19 root 1753: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_HIGH );
1.1.1.18 root 1754: LOG_TRACE(TRACE_FDC, "fdc clear irq VBL=%d HBL=%d\n" , nVBLs , nHBL );
1755: }
1756:
1757: else
1758: {
1759: FDC.IRQ_Signal &= FDC_IRQ_SOURCE_FORCED; /* Clear all sources except 'forced irq' and keep IRQ set in MFP */
1760: LOG_TRACE(TRACE_FDC, "fdc clear irq not done, irq forced VBL=%d HBL=%d\n" , nVBLs , nHBL );
1761: }
1762: }
1763:
1.1.1.19 root 1764: void FDC_ClearHdcIRQ(void)
1765: {
1766: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_HDC;
1767: if (FDC.IRQ_Signal == 0)
1768: {
1769: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_HIGH );
1770: }
1771: }
1.1.1.18 root 1772:
1773: /*-----------------------------------------------------------------------*/
1774: /**
1775: * Handle the current FDC command.
1776: * We use a timer to go from one state to another to emulate the different
1777: * phases of an FDC command.
1778: * When the command completes (success or failure), FDC.Command will be
1779: * set to FDCEMU_CMD_NULL. Until then, this function will be called to
1780: * handle each state of the command and the corresponding delay in micro
1781: * seconds.
1782: * This handler is called after a first delay corresponding to the prepare
1783: * delay and the eventual motor on delay.
1784: * Once we reach this point, the current command can not be replaced by
1785: * another command (except 'Force Interrupt')
1786: */
1787: void FDC_InterruptHandler_Update ( void )
1788: {
1789: int FdcCycles = 0;
1790: int PendingCyclesOver;
1791:
1792: /* Number of internal cycles we went over for this timer ( <= 0 ) */
1793: /* Used to restart the next timer and keep a constant rate (important for DMA transfers) */
1794: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1795:
1796: //fprintf ( stderr , "fdc int handler %lld delay %d\n" , CyclesGlobalClockCounter, PendingCyclesOver );
1797:
1798: CycInt_AcknowledgeInterrupt();
1799:
1800: do /* We loop as long as FdcCycles == 0 (immediate change of state) */
1801: {
1802: /* Update FDC's internal variables */
1803: FDC_UpdateAll ();
1804:
1805: /* Is FDC active? */
1806: if (FDC.Command!=FDCEMU_CMD_NULL)
1807: {
1808: /* Which command are we running ? */
1809: switch(FDC.Command)
1810: {
1811: case FDCEMU_CMD_RESTORE:
1812: FdcCycles = FDC_UpdateRestoreCmd();
1813: break;
1814: case FDCEMU_CMD_SEEK:
1815: FdcCycles = FDC_UpdateSeekCmd();
1816: break;
1817: case FDCEMU_CMD_STEP:
1818: FdcCycles = FDC_UpdateStepCmd();
1819: break;
1820:
1821: case FDCEMU_CMD_READSECTORS:
1822: FdcCycles = FDC_UpdateReadSectorsCmd();
1823: break;
1824: case FDCEMU_CMD_WRITESECTORS:
1825: FdcCycles = FDC_UpdateWriteSectorsCmd();
1826: break;
1827:
1828: case FDCEMU_CMD_READADDRESS:
1829: FdcCycles = FDC_UpdateReadAddressCmd();
1830: break;
1831:
1832: case FDCEMU_CMD_READTRACK:
1833: FdcCycles = FDC_UpdateReadTrackCmd();
1834: break;
1835:
1836: case FDCEMU_CMD_WRITETRACK:
1837: FdcCycles = FDC_UpdateWriteTrackCmd();
1838: break;
1839:
1840: case FDCEMU_CMD_MOTOR_STOP:
1841: FdcCycles = FDC_UpdateMotorStop();
1842: break;
1843: }
1844: }
1845: }
1846: while ( ( FDC.Command != FDCEMU_CMD_NULL ) && ( FdcCycles == 0 ) );
1847:
1848: if ( FDC.Command != FDCEMU_CMD_NULL )
1849: {
1850: FDC_StartTimer_FdcCycles ( FdcCycles , -PendingCyclesOver );
1851: }
1852: }
1853:
1854:
1855: /*-----------------------------------------------------------------------*/
1856: /**
1857: * Return the type of a command, based on the upper bits of CR
1858: */
1859: Uint8 FDC_GetCmdType ( Uint8 CR )
1860: {
1861: if ( ( CR & 0x80 ) == 0 ) /* Type I - Restore, Seek, Step, Step-In, Step-Out */
1862: return 1;
1863: else if ( ( CR & 0x40 ) == 0 ) /* Type II - Read Sector, Write Sector */
1864: return 2;
1865: else if ( ( CR & 0xf0 ) != 0xd0 ) /* Type III - Read Address, Read Track, Write Track */
1866: return 3;
1867: else /* Type IV - Force Interrupt */
1868: return 4;
1.1.1.15 root 1869: }
1870:
1871:
1872: /*-----------------------------------------------------------------------*/
1873: /**
1874: * Update the FDC's Status Register.
1875: * All bits in DisableBits are cleared in STR, then all bits in EnableBits
1876: * are set in STR.
1877: */
1878: static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits )
1879: {
1880: FDC.STR &= (~DisableBits); /* Clear bits in DisableBits */
1881: FDC.STR |= EnableBits; /* Set bits in EnableBits */
1.1.1.17 root 1882:
1.1.1.18 root 1883: FDC_Drive_Set_BusyLed ( FDC.STR );
1.1.1.15 root 1884: //fprintf ( stderr , "fdc str 0x%x\n" , FDC.STR );
1885: }
1886:
1887:
1888: /*-----------------------------------------------------------------------*/
1889: /**
1890: * Common to all commands once they're completed :
1891: * - remove busy bit
1.1.1.18 root 1892: * - set interrupt if necessary
1.1.1.15 root 1893: * - stop motor after 2 sec
1894: */
1895: static int FDC_CmdCompleteCommon ( bool DoInt )
1896: {
1897: int FrameCycles, HblCounterVideo, LineCycles;
1898:
1899: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1900: LOG_TRACE(TRACE_FDC, "fdc complete command VBL=%d video_cyc=%d %d@%d pc=%x\n",
1901: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1902:
1903: FDC_Update_STR ( FDC_STR_BIT_BUSY , 0 ); /* Remove busy bit */
1.1.1.9 root 1904:
1.1.1.15 root 1905: if ( DoInt )
1.1.1.18 root 1906: FDC_SetIRQ ( FDC_IRQ_SOURCE_COMPLETE );
1.1.1.15 root 1907:
1908: FDC.Command = FDCEMU_CMD_MOTOR_STOP; /* Fake command to stop the motor */
1.1.1.18 root 1909: FDC.CommandState = FDCEMU_RUN_MOTOR_STOP;
1910: return FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 1911: }
1912:
1913:
1914: /*-----------------------------------------------------------------------*/
1915: /**
1916: * Verify track after a type I command.
1917: * The FDC will read the first ID field of the current track and will
1918: * compare the track number in this ID field with the current Track Register.
1.1.1.18 root 1919: * If they don't match, we try again with the next ID field until we
1920: * reach 5 revolutions, in which case we set RNF.
1921: *
1922: * NOTE [NP] : in the case of Hatari when using ST/MSA images, the track is always the correct one,
1.1.1.15 root 1923: * so the verify will always be good (except if no disk is inserted or the physical head is
1924: * not on the same track as FDC.TR)
1.1.1.21! root 1925: * For STX images, verify track might fail on purpose with some protection
1.1.1.15 root 1926: */
1.1.1.18 root 1927: static bool FDC_VerifyTrack ( void )
1.1.1.15 root 1928: {
1929: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.21! root 1930: Uint8 Next_TR;
! 1931: Uint8 Next_CRC_OK;
1.1.1.15 root 1932:
1933: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1934:
1.1.1.18 root 1935: /* Return false if no drive selected, or drive not enabled, or no disk in drive */
1936: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
1937: || ( !FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted ) )
1938: {
1939: LOG_TRACE(TRACE_FDC, "fdc type I verify track failed disabled/empty drive=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
1940: FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1941: return false;
1942: }
1943:
1.1.1.21! root 1944: /* Check if the current ID Field is the one we're looking for (same track and correct CRC) */
! 1945: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
1.1.1.9 root 1946: {
1.1.1.21! root 1947: Next_TR = FDC_NextSectorID_TR_STX ();
! 1948: Next_CRC_OK = FDC_NextSectorID_CRC_OK_STX ();
! 1949: }
! 1950: else
! 1951: {
! 1952: Next_TR = FDC_NextSectorID_TR_ST ();
! 1953: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
! 1954: }
! 1955:
! 1956: /* ST/MSA image will always be correct, only STX can fail depending on some protections */
! 1957: if ( ( Next_TR != FDC.TR ) || ( Next_CRC_OK == 0 ) )
! 1958: {
! 1959: LOG_TRACE(TRACE_FDC, "fdc type I verify track failed ID_TR=0x%x TR=0x%x crc_ok=%d head=0x%x drive=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
! 1960: Next_TR , FDC.TR , Next_CRC_OK , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal ,
1.1.1.18 root 1961: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 1962:
1.1.1.18 root 1963: return false;
1.1.1.15 root 1964: }
1965:
1.1.1.18 root 1966: /* If disk image has only one side or drive is single sided and we're trying to verify on 2nd side, then return false */
1967: if ( ( FDC.SideSignal == 1 )
1968: && ( ( FDC_GetSidesPerDisk ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ) != 2 )
1969: || ( FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads == 1 ) ) )
1.1.1.15 root 1970: {
1.1.1.18 root 1971: LOG_TRACE(TRACE_FDC, "fdc type I verify track failed TR=0x%x head=0x%x side=1 doesn't exist drive=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
1972: FDC.TR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal ,
1.1.1.15 root 1973: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1974:
1.1.1.18 root 1975: return false;
1.1.1.9 root 1976: }
1.1.1.15 root 1977:
1.1.1.18 root 1978: /* The track is the correct one */
1979: return true;
1.1.1.15 root 1980: }
1981:
1982:
1983: /*-----------------------------------------------------------------------*/
1984: /**
1.1.1.18 root 1985: * Run the 'motor stop' sequence : wait for 9 revolutions (1.8 sec)
1986: * and stop the motor.
1987: * We clear motor bit, but spinup bit remains to 1 (verified on a real STF)
1.1.1.15 root 1988: */
1989: static int FDC_UpdateMotorStop ( void )
1990: {
1.1.1.18 root 1991: int FdcCycles = 0;
1.1.1.15 root 1992: int FrameCycles, HblCounterVideo, LineCycles;
1993:
1.1.1.18 root 1994: /* Which command is running? */
1995: switch (FDC.CommandState)
1996: {
1997: case FDCEMU_RUN_MOTOR_STOP:
1998: FDC.IndexPulse_Counter = 0;
1999: FDC.CommandState = FDCEMU_RUN_MOTOR_STOP_WAIT;
2000: case FDCEMU_RUN_MOTOR_STOP_WAIT:
2001: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_MOTOR_OFF )
2002: {
2003: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2004: break;
2005: }
2006: /* If IndexPulse_Counter reached, we go directly to the _COMPLETE state */
2007: case FDCEMU_RUN_MOTOR_STOP_COMPLETE:
2008: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2009: LOG_TRACE(TRACE_FDC, "fdc motor stopped VBL=%d video_cyc=%d %d@%d pc=%x\n",
2010: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2011:
1.1.1.18 root 2012: FDC.IndexPulse_Counter = 0;
2013: if ( FDC.DriveSelSignal >= 0 ) /* A drive was previously enabled */
2014: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time = 0; /* Stop counting index pulses on the drive */
1.1.1.15 root 2015:
1.1.1.18 root 2016: FDC_Update_STR ( FDC_STR_BIT_MOTOR_ON , 0 ); /* Unset motor bit and keep spin up bit */
2017: FDC.Command = FDCEMU_CMD_NULL; /* Motor stopped, this is the last state */
2018: FdcCycles = 0;
2019: break;
2020: }
2021: return FdcCycles;
1.1 root 2022: }
2023:
1.1.1.2 root 2024:
2025: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2026: /**
2027: * Run 'RESTORE' command
2028: */
1.1.1.15 root 2029: static int FDC_UpdateRestoreCmd ( void )
1.1 root 2030: {
1.1.1.18 root 2031: int FdcCycles = 0;
2032: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2033:
1.1.1.18 root 2034: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2035:
1.1.1.6 root 2036: /* Which command is running? */
1.1.1.15 root 2037: switch (FDC.CommandState)
1.1.1.6 root 2038: {
2039: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO:
1.1.1.18 root 2040: if ( FDC_Set_MotorON ( FDC.CR ) )
2041: {
2042: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP;
2043: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2044: }
2045: else
2046: {
2047: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON;
2048: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2049: }
2050: break;
2051: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP:
2052: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2053: {
2054: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2055: break;
2056: }
2057: /* If IndexPulse_Counter reached, we go directly to the _MOTOR_ON state */
2058: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON:
2059: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* At this point, spin up sequence is ok */
2060: FDC.ReplaceCommandPossible = false;
2061:
1.1.1.15 root 2062: /* The FDC will try 255 times to reach track 0 using step out signals */
2063: /* If track 0 signal is not detected after 255 attempts, the command is interrupted */
2064: /* and FDC_STR_BIT_RNF is set in the Status Register. */
1.1.1.18 root 2065: /* This can happen if no drive is selected or if the selected drive is disabled */
1.1.1.15 root 2066: /* TR should be set to 255 once the spin-up sequence is made and the command */
2067: /* can't be interrupted anymore by another command (else TR value will be wrong */
2068: /* for other type I commands) */
2069: FDC.TR = 0xff;
2070: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP; /* continue in the _LOOP state */
2071: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP:
2072: if ( FDC.TR == 0 ) /* Track 0 not reached after 255 attempts ? */
1.1.1.18 root 2073: { /* (this can happen if the drive is disabled) */
1.1.1.15 root 2074: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2075: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
1.1.1.18 root 2076: FdcCycles = FDC_CmdCompleteCommon( true );
2077: break;
1.1.1.15 root 2078: }
2079:
1.1.1.18 root 2080: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
2081: || ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack != 0 ) ) /* Are we at track zero ? */
1.1.1.15 root 2082: {
2083: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
2084: FDC.TR--; /* One less attempt */
1.1.1.18 root 2085: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
2086: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack--; /* Move physical head only if an enabled drive is selected */
2087: FdcCycles = FDC_DelayToFdcCycles ( FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000 );
1.1.1.15 root 2088: }
1.1.1.18 root 2089: else /* Drive is enabled and head is at track 0 */
1.1.1.6 root 2090: {
1.1.1.15 root 2091: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
2092: FDC.TR = 0; /* Update Track Register to 0 */
1.1.1.17 root 2093: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY;
1.1.1.18 root 2094: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2095: }
2096: break;
2097: case FDCEMU_RUN_RESTORE_VERIFY:
2098: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2099: {
1.1.1.18 root 2100: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK;
2101: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
1.1.1.17 root 2102: }
2103: else
2104: {
1.1.1.15 root 2105: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
1.1.1.18 root 2106: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2107: }
2108: break;
2109: case FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK:
2110: FDC.IndexPulse_Counter = 0;
2111: case FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER:
2112: /* If 'verify' doesn't succeed after 5 revolutions, we abort with RNF */
2113: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2114: {
2115: LOG_TRACE(TRACE_FDC, "fdc type I restore track=%d drive=%d verify RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2116: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
2117:
2118: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
2119: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
2120: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2121: break;
2122: }
2123:
2124: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2125: FdcCycles = -1;
2126: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2127: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2128: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2129: else
2130: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2131: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2132: if ( FdcCycles < 0 )
2133: {
1.1.1.19 root 2134: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER;
1.1.1.18 root 2135: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2136: }
2137: else
2138: {
2139: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2140: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, ID field */
2141: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER;
1.1.1.6 root 2142: }
2143: break;
1.1.1.18 root 2144: case FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER:
2145: /* Check if the current ID Field matches the track number */
2146: if ( FDC_VerifyTrack () )
2147: {
2148: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* Track is correct, remove RNF bit */
2149: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
2150: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2151: }
2152: else
2153: {
2154: /* Verify failed with this ID field ; check the next one */
2155: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER;
2156: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2157: }
1.1.1.17 root 2158: break;
1.1.1.6 root 2159: case FDCEMU_RUN_RESTORE_COMPLETE:
1.1.1.18 root 2160: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2161: break;
2162: }
1.1.1.15 root 2163:
1.1.1.18 root 2164: return FdcCycles;
1.1 root 2165: }
2166:
1.1.1.2 root 2167:
2168: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2169: /**
2170: * Run 'SEEK' command
2171: */
1.1.1.15 root 2172: static int FDC_UpdateSeekCmd ( void )
1.1 root 2173: {
1.1.1.18 root 2174: int FdcCycles = 0;
2175: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2176:
1.1.1.18 root 2177: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2178:
1.1.1.6 root 2179: /* Which command is running? */
1.1.1.15 root 2180: switch (FDC.CommandState)
1.1.1.6 root 2181: {
2182: case FDCEMU_RUN_SEEK_TOTRACK:
1.1.1.18 root 2183: if ( FDC_Set_MotorON ( FDC.CR ) )
2184: {
2185: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP;
2186: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2187: }
2188: else
2189: {
2190: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON;
2191: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2192: }
2193: break;
2194: case FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP:
2195: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2196: {
2197: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2198: break;
2199: }
2200: /* If IndexPulse_Counter reached, we go directly to the _MOTOR_ON state */
2201: case FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON:
2202: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* At this point, spin up sequence is ok */
2203: FDC.ReplaceCommandPossible = false;
2204:
1.1.1.15 root 2205: if ( FDC.TR == FDC.DR ) /* Are we at the selected track ? */
2206: {
1.1.1.17 root 2207: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
1.1.1.18 root 2208: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 2209: }
1.1.1.6 root 2210: else
2211: {
1.1.1.15 root 2212: if ( FDC.DR < FDC.TR ) /* Set StepDirection to the correct value */
2213: FDC.StepDirection = -1;
2214: else
2215: FDC.StepDirection = 1;
2216:
2217: /* Move head by one track depending on FDC.StepDirection and update Track Register */
2218: FDC.TR += FDC.StepDirection;
2219:
1.1.1.18 root 2220: FdcCycles = FDC_DelayToFdcCycles ( FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000 );
2221: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* By default, unset bit TR00 */
1.1.1.15 root 2222:
1.1.1.18 root 2223: /* Check / move physical head only if an enabled drive is selected */
2224: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
1.1.1.15 root 2225: {
1.1.1.18 root 2226: if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == FDC_PHYSICAL_MAX_TRACK ) && ( FDC.StepDirection == 1 ) )
2227: {
2228: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
2229: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No delay if trying to go after max track */
2230: }
1.1.1.15 root 2231:
1.1.1.18 root 2232: else if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 ) && ( FDC.StepDirection == -1 ) )
2233: {
2234: FDC.TR = 0; /* If we reach track 0, we stop there */
2235: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
2236: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2237: }
2238:
2239: else
2240: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack += FDC.StepDirection; /* Move physical head */
2241:
2242: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
2243: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
1.1.1.15 root 2244: }
1.1.1.6 root 2245: }
1.1.1.15 root 2246:
1.1.1.6 root 2247: break;
1.1.1.17 root 2248: case FDCEMU_RUN_SEEK_VERIFY:
1.1.1.15 root 2249: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
1.1.1.17 root 2250: {
1.1.1.18 root 2251: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_HEAD_OK;
2252: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
1.1.1.17 root 2253: }
2254: else
2255: {
2256: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
1.1.1.18 root 2257: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2258: }
2259: break;
2260: case FDCEMU_RUN_SEEK_VERIFY_HEAD_OK:
2261: FDC.IndexPulse_Counter = 0;
2262: case FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER:
2263: /* If 'verify' doesn't succeed after 5 revolutions, we abort with RNF */
2264: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2265: {
2266: LOG_TRACE(TRACE_FDC, "fdc type I seek track=%d drive=%d verify RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2267: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
2268:
2269: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
2270: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
2271: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2272: break;
2273: }
2274:
2275: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2276: FdcCycles = -1;
2277: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2278: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2279: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2280: else
2281: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2282: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2283: if ( FdcCycles < 0 )
2284: {
1.1.1.19 root 2285: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER;
1.1.1.18 root 2286: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2287: }
2288: else
2289: {
2290: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2291: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, ID field */
2292: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER;
1.1.1.17 root 2293: }
2294: break;
1.1.1.18 root 2295: case FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER:
2296: /* Check if the current ID Field matches the track number */
2297: if ( FDC_VerifyTrack () )
2298: {
2299: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* Track is correct, remove RNF bit */
2300: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
2301: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2302: }
2303: else
2304: {
2305: /* Verify failed with this ID field ; check the next one */
2306: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER;
2307: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2308: }
1.1.1.17 root 2309: break;
2310: case FDCEMU_RUN_SEEK_COMPLETE:
1.1.1.18 root 2311: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2312: break;
2313: }
1.1.1.15 root 2314:
1.1.1.18 root 2315: return FdcCycles;
1.1 root 2316: }
2317:
1.1.1.2 root 2318:
2319: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2320: /**
2321: * Run 'STEP' command
2322: */
1.1.1.15 root 2323: static int FDC_UpdateStepCmd ( void )
1.1 root 2324: {
1.1.1.18 root 2325: int FdcCycles = 0;
2326: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2327:
1.1.1.18 root 2328: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2329:
1.1.1.6 root 2330: /* Which command is running? */
1.1.1.15 root 2331: switch (FDC.CommandState)
1.1.1.6 root 2332: {
2333: case FDCEMU_RUN_STEP_ONCE:
1.1.1.18 root 2334: if ( FDC_Set_MotorON ( FDC.CR ) )
2335: {
2336: FDC.CommandState = FDCEMU_RUN_STEP_ONCE_SPIN_UP;
2337: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2338: }
2339: else
2340: {
2341: FDC.CommandState = FDCEMU_RUN_STEP_ONCE_MOTOR_ON;
2342: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2343: }
2344: break;
2345: case FDCEMU_RUN_STEP_ONCE_SPIN_UP:
2346: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2347: {
2348: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2349: break;
2350: }
2351: /* If IndexPulse_Counter reached, we go directly to the _MOTOR_ON state */
2352: case FDCEMU_RUN_STEP_ONCE_MOTOR_ON:
2353: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* At this point, spin up sequence is ok */
2354: FDC.ReplaceCommandPossible = false;
2355:
1.1.1.15 root 2356: /* Move head by one track depending on FDC.StepDirection */
2357: if ( FDC.CR & FDC_COMMAND_BIT_UPDATE_TRACK )
2358: FDC.TR += FDC.StepDirection; /* Update Track Register */
2359:
1.1.1.18 root 2360: FdcCycles = FDC_DelayToFdcCycles ( FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000 );
2361: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* By default, unset bit TR00 */
2362:
2363: /* Check / move physical head only if an enabled drive is selected */
2364: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
2365: {
2366: if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == FDC_PHYSICAL_MAX_TRACK ) && ( FDC.StepDirection == 1 ) )
2367: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No delay if trying to go after max track */
1.1.1.15 root 2368:
1.1.1.18 root 2369: else if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 ) && ( FDC.StepDirection == -1 ) )
2370: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No delay if trying to go before track 0 */
1.1.1.6 root 2371:
1.1.1.18 root 2372: else
2373: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack += FDC.StepDirection; /* Move physical head */
2374:
2375: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
2376: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
2377: }
2378:
2379: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY;
2380: break;
2381: case FDCEMU_RUN_STEP_VERIFY:
2382: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2383: {
2384: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_HEAD_OK;
2385: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2386: }
1.1.1.15 root 2387: else
2388: {
1.1.1.18 root 2389: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2390: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2391: }
1.1.1.18 root 2392: break;
2393: case FDCEMU_RUN_STEP_VERIFY_HEAD_OK:
2394: FDC.IndexPulse_Counter = 0;
2395: case FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER:
2396: /* If 'verify' doesn't succeed after 5 revolutions, we abort with RNF */
2397: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2398: {
2399: LOG_TRACE(TRACE_FDC, "fdc type I step track=%d drive=%d verify RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2400: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2401:
1.1.1.18 root 2402: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
2403: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2404: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2405: break;
2406: }
2407:
2408: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2409: FdcCycles = -1;
2410: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2411: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2412: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2413: else
2414: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2415: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2416: if ( FdcCycles < 0 )
2417: {
1.1.1.19 root 2418: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER;
1.1.1.18 root 2419: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2420: }
1.1.1.15 root 2421: else
1.1.1.18 root 2422: {
2423: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2424: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, ID field */
2425: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER;
2426: }
1.1.1.17 root 2427: break;
1.1.1.18 root 2428: case FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER:
2429: /* Check if the current ID Field matches the track number */
2430: if ( FDC_VerifyTrack () )
1.1.1.17 root 2431: {
1.1.1.18 root 2432: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* Track is correct, remove RNF bit */
2433: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2434: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.17 root 2435: }
2436: else
2437: {
1.1.1.18 root 2438: /* Verify failed with this ID field ; check the next one */
2439: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER;
2440: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2441: }
2442: break;
1.1.1.6 root 2443: case FDCEMU_RUN_STEP_COMPLETE:
1.1.1.18 root 2444: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2445: break;
2446: }
1.1.1.15 root 2447:
1.1.1.18 root 2448: return FdcCycles;
1.1 root 2449: }
2450:
1.1.1.2 root 2451:
2452: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2453: /**
1.1.1.15 root 2454: * Run 'READ SECTOR/S' command
1.1.1.9 root 2455: */
1.1.1.15 root 2456: static int FDC_UpdateReadSectorsCmd ( void )
1.1 root 2457: {
1.1.1.18 root 2458: int FdcCycles = 0;
1.1.1.15 root 2459: int SectorSize;
1.1.1.18 root 2460: int FrameCycles, HblCounterVideo, LineCycles;
2461: Uint8 Next_TR;
2462: Uint8 Next_SR;
2463: Uint8 Next_CRC_OK;
1.1.1.15 root 2464:
2465: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2466:
2467:
1.1.1.6 root 2468: /* Which command is running? */
1.1.1.15 root 2469: switch (FDC.CommandState)
1.1.1.6 root 2470: {
1.1.1.15 root 2471: case FDCEMU_RUN_READSECTORS_READDATA:
1.1.1.18 root 2472: if ( FDC_Set_MotorON ( FDC.CR ) )
2473: {
2474: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_SPIN_UP;
2475: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2476: }
2477: else
2478: {
2479: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_HEAD_LOAD;
2480: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2481: }
1.1.1.15 root 2482: break;
1.1.1.18 root 2483: case FDCEMU_RUN_READSECTORS_READDATA_SPIN_UP:
2484: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2485: {
2486: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2487: break;
2488: }
2489: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
2490: case FDCEMU_RUN_READSECTORS_READDATA_HEAD_LOAD:
2491: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
2492: {
2493: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON;
2494: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2495: break;
2496: }
2497: /* If there's no head settle, we go directly to the _MOTOR_ON state */
2498: case FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON:
2499: FDC.ReplaceCommandPossible = false;
2500: FDC.IndexPulse_Counter = 0;
2501: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER;
2502: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2503: break;
2504: case FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER:
1.1.1.17 root 2505: /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
1.1.1.18 root 2506: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
1.1.1.17 root 2507: {
2508: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
1.1.1.18 root 2509: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2510: break;
2511: }
2512:
1.1.1.18 root 2513: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2514: FdcCycles = -1;
2515: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2516: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2517: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2518: else
2519: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2520: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2521: if ( FdcCycles < 0 )
2522: {
2523: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2524: }
2525: else
2526: {
2527: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2528: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, TR, SIDE, SR, LEN, CRC1, CRC2 */
2529: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER;
2530: }
2531: break;
2532: case FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER:
2533: /* Check if the current ID Field is the one we're looking for (same track/sector and correct CRC) */
2534: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2535: {
2536: Next_TR = FDC_NextSectorID_TR_STX ();
2537: Next_SR = FDC_NextSectorID_SR_STX ();
2538: Next_CRC_OK = FDC_NextSectorID_CRC_OK_STX ();
2539: }
2540: else
2541: {
2542: Next_TR = FDC_NextSectorID_TR_ST ();
2543: Next_SR = FDC_NextSectorID_SR_ST ();
2544: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
2545: }
2546: if ( ( Next_TR == FDC.TR ) && ( Next_SR == FDC.SR ) && ( Next_CRC_OK ) )
1.1.1.17 root 2547: {
2548: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START;
1.1.1.18 root 2549: /* Read bytes to reach the sector's data : GAP3a + GAP3b + 3xA1 + FB */
2550: FdcCycles = FDC_TransferByte_FdcCycles ( FDC_TRACK_LAYOUT_STANDARD_GAP3a + FDC_TRACK_LAYOUT_STANDARD_GAP3b + 3 + 1 );
1.1.1.17 root 2551: }
2552: else
2553: {
2554: /* This is not the ID field we're looking for ; check the next one */
1.1.1.18 root 2555: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER;
2556: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2557: }
2558: break;
2559: case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START:
1.1.1.15 root 2560: /* Read a single sector into temporary buffer (512 bytes for ST/MSA) */
1.1.1.18 root 2561: FDC_Buffer_Reset();
1.1.1.17 root 2562:
1.1.1.18 root 2563: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2564: FDC.Status_Temp = FDC_ReadSector_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2565: FDC.SR , FDC.SideSignal , &SectorSize );
2566: else
2567: FDC.Status_Temp = FDC_ReadSector_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2568: FDC.SR , FDC.SideSignal , &SectorSize );
2569:
2570: if ( FDC.Status_Temp & FDC_STR_BIT_RNF ) /* Sector FDC.SR was not found */
1.1.1.15 root 2571: {
2572: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
1.1.1.18 root 2573: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2574: }
2575: else
2576: {
2577: if ( FDC.Status_Temp & FDC_STR_BIT_RECORD_TYPE )
2578: FDC_Update_STR ( 0 , FDC_STR_BIT_RECORD_TYPE );
2579: else
2580: FDC_Update_STR ( FDC_STR_BIT_RECORD_TYPE , 0 );
2581:
2582: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP;
2583: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the first byte */
1.1.1.15 root 2584: }
2585: break;
1.1.1.17 root 2586: case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP:
1.1.1.18 root 2587: /* Transfer the sector 1 byte at a time using DMA */
2588: FDC_DMA_FIFO_Push ( FDC_Buffer_Read_Byte () ); /* Add 1 byte to the DMA FIFO */
2589: if ( FDC_BUFFER.PosRead < FDC_Buffer_Get_Size () )
1.1.1.15 root 2590: {
1.1.1.18 root 2591: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
1.1.1.15 root 2592: }
1.1.1.17 root 2593: else /* Sector transferred, check the CRC */
1.1.1.15 root 2594: {
1.1.1.17 root 2595: FDC.CommandState = FDCEMU_RUN_READSECTORS_CRC;
1.1.1.18 root 2596: FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Read 2 bytes for CRC */
1.1.1.17 root 2597: }
2598: break;
2599: case FDCEMU_RUN_READSECTORS_CRC:
1.1.1.18 root 2600: /* Sector completely transferred, CRC is always good for ST/MSA, but not always for STX */
2601: if ( FDC.Status_Temp & FDC_STR_BIT_CRC_ERROR )
2602: {
2603: LOG_TRACE(TRACE_FDC, "fdc type II read sector=%d track=0x%x side=%d drive=%d CRC VBL=%d video_cyc=%d %d@%d pc=%x\n",
2604: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
2605:
2606: FDC_Update_STR ( 0 , FDC_STR_BIT_CRC_ERROR );
2607: FdcCycles = FDC_CmdCompleteCommon( true );
2608: }
2609: else
2610: {
2611: FDC.CommandState = FDCEMU_RUN_READSECTORS_MULTI;
2612: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2613: }
2614: break;
2615: case FDCEMU_RUN_READSECTORS_MULTI:
2616: /* Check for multi bit */
1.1.1.17 root 2617: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2618: {
2619: FDC.SR++; /* Try to read next sector and set RNF if not possible */
1.1.1.18 root 2620: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON;
2621: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2622: LOG_TRACE(TRACE_FDC, "fdc type II read sector with multi sector=0x%x track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
2623: FDC.SR, FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal ,
2624: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.17 root 2625: }
2626: else /* Multi=0, stop here with no error */
2627: {
2628: FDC.CommandState = FDCEMU_RUN_READSECTORS_COMPLETE;
1.1.1.18 root 2629: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2630: }
2631: break;
2632: case FDCEMU_RUN_READSECTORS_RNF:
1.1.1.18 root 2633: LOG_TRACE(TRACE_FDC, "fdc type II read sector=%d track=0x%x side=%d drive=%d RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2634: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.6 root 2635:
1.1.1.15 root 2636: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
1.1.1.18 root 2637: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2638: break;
1.1.1.15 root 2639: case FDCEMU_RUN_READSECTORS_COMPLETE:
1.1.1.18 root 2640: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2641: break;
2642: }
2643:
1.1.1.18 root 2644: return FdcCycles;
1.1.1.15 root 2645: }
2646:
2647:
2648: /*-----------------------------------------------------------------------*/
2649: /**
2650: * Run 'WRITE SECTOR/S' command
2651: */
2652: static int FDC_UpdateWriteSectorsCmd ( void )
2653: {
1.1.1.18 root 2654: int FdcCycles = 0;
1.1.1.15 root 2655: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 2656: Uint8 Next_TR;
2657: Uint8 Next_SR;
2658: Uint8 Next_LEN;
2659: Uint8 Next_CRC_OK;
2660: Uint8 Byte;
2661: Uint8 Status;
1.1.1.15 root 2662:
2663: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2664:
1.1.1.18 root 2665: /* Stop now if disk is write protected */
2666: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
2667: && ( FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted )
2668: && ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) ) )
1.1.1.15 root 2669: {
1.1.1.18 root 2670: LOG_TRACE(TRACE_FDC, "fdc type II write sector=%d track=0x%x side=%d drive=%d WPRT VBL=%d video_cyc=%d %d@%d pc=%x\n",
2671: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2672:
2673: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
1.1.1.18 root 2674: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2675: }
2676: else
2677: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit */
2678:
2679:
2680: /* Which command is running? */
2681: switch (FDC.CommandState)
2682: {
2683: case FDCEMU_RUN_WRITESECTORS_WRITEDATA:
1.1.1.18 root 2684: if ( FDC_Set_MotorON ( FDC.CR ) )
2685: {
2686: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_SPIN_UP;
2687: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2688: }
2689: else
2690: {
2691: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_HEAD_LOAD;
2692: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2693: }
1.1.1.15 root 2694: break;
1.1.1.18 root 2695: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_SPIN_UP:
2696: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2697: {
2698: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2699: break;
2700: }
2701: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
2702: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_HEAD_LOAD:
2703: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
2704: {
2705: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON;
2706: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2707: break;
2708: }
2709: /* If there's no head settle, we go directly to the _MOTOR_ON state */
2710: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON:
2711: FDC.ReplaceCommandPossible = false;
2712: FDC.IndexPulse_Counter = 0;
2713: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER:
1.1.1.17 root 2714: /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
1.1.1.18 root 2715: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
1.1.1.17 root 2716: {
1.1.1.18 root 2717: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_RNF;
2718: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2719: break;
2720: }
2721:
1.1.1.18 root 2722: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2723: FdcCycles = -1;
2724: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2725: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2726: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2727: else
2728: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2729: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2730: if ( FdcCycles < 0 )
1.1.1.17 root 2731: {
1.1.1.18 root 2732: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
1.1.1.17 root 2733: }
2734: else
2735: {
1.1.1.18 root 2736: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2737: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, TR, SIDE, SR, LEN, CRC1, CRC2 */
1.1.1.17 root 2738: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER;
2739: }
2740: break;
1.1.1.18 root 2741: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER:
2742: /* Check if the current ID Field is the one we're looking for (same track/sector and correct CRC) */
2743: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2744: {
2745: Next_TR = FDC_NextSectorID_TR_STX ();
2746: Next_SR = FDC_NextSectorID_SR_STX ();
2747: Next_CRC_OK = FDC_NextSectorID_CRC_OK_STX ();
2748: }
2749: else
1.1.1.15 root 2750: {
1.1.1.18 root 2751: Next_TR = FDC_NextSectorID_TR_ST ();
2752: Next_SR = FDC_NextSectorID_SR_ST ();
2753: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
1.1.1.15 root 2754: }
1.1.1.18 root 2755: if ( ( Next_TR == FDC.TR ) && ( Next_SR == FDC.SR ) && ( Next_CRC_OK ) )
1.1.1.15 root 2756: {
1.1.1.18 root 2757: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START;
2758: /* Read bytes to reach the sector's data : GAP3a + GAP3b + 3xA1 + FB */
2759: FdcCycles = FDC_TransferByte_FdcCycles ( FDC_TRACK_LAYOUT_STANDARD_GAP3a + FDC_TRACK_LAYOUT_STANDARD_GAP3b + 3 + 1 );
2760: }
2761: else
2762: {
2763: /* This is not the ID field we're looking for ; check the next one */
2764: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER;
2765: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 2766: }
2767: break;
1.1.1.18 root 2768: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START:
2769: /* Write a single sector from RAM (512 bytes for ST/MSA) */
2770: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2771: Next_LEN = FDC_NextSectorID_LEN_STX ();
2772: else
2773: Next_LEN = FDC_NextSectorID_LEN_ST ();
2774:
2775: FDC_Buffer_Reset();
2776: FDC_DMA.BytesToTransfer = 128 << ( Next_LEN & FDC_SECTOR_SIZE_MASK );
2777:
2778: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP;
2779: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2780: break;
1.1.1.17 root 2781: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP:
1.1.1.18 root 2782: /* Transfer the sector 1 byte at a time using DMA */
2783: if ( FDC_DMA.BytesToTransfer-- > 0 )
1.1.1.15 root 2784: {
1.1.1.18 root 2785: Byte = FDC_DMA_FIFO_Pull (); /* Get 1 byte from the DMA FIFO */
2786: //fprintf ( stderr , "byte %d %x\n" , FDC_DMA.BytesToTransfer , Byte );
2787: FDC_Buffer_Add ( Byte );
2788: FdcCycles = FDC_TransferByte_FdcCycles ( 1 );
1.1.1.15 root 2789: }
1.1.1.18 root 2790: else /* Sector transferred, add the CRC */
1.1.1.15 root 2791: {
1.1.1.17 root 2792: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_CRC;
1.1.1.18 root 2793: FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Write 2 bytes for CRC */
1.1.1.17 root 2794: }
2795: break;
2796: case FDCEMU_RUN_WRITESECTORS_CRC:
1.1.1.18 root 2797: /* Sector completely transferred, CRC is always good for ST/MSA */
2798: /* This is where we save the buffer to the disk image */
2799:
2800: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2801: Status = FDC_WriteSector_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2802: FDC.SR , FDC.SideSignal , FDC_Buffer_Get_Size () );
2803: else
2804: Status = FDC_WriteSector_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2805: FDC.SR , FDC.SideSignal , FDC_Buffer_Get_Size () );
2806:
2807: if ( Status & FDC_STR_BIT_RNF ) /* Sector FDC.SR was not correctly written */
2808: {
2809: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_RNF;
2810: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2811: }
2812: else
2813: {
2814: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_MULTI;
2815: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2816: }
2817: break;
2818: case FDCEMU_RUN_WRITESECTORS_MULTI:
2819: /* Check for multi bit */
1.1.1.17 root 2820: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2821: {
2822: FDC.SR++; /* Try to write next sector and set RNF if not possible */
1.1.1.18 root 2823: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON;
2824: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2825: LOG_TRACE(TRACE_FDC, "fdc type II write sector with multi sector=0x%x track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
2826: FDC.SR, FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal ,
2827: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.17 root 2828: }
2829: else /* Multi=0, stop here with no error */
2830: {
2831: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_COMPLETE;
1.1.1.18 root 2832: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2833: }
2834: break;
2835: case FDCEMU_RUN_WRITESECTORS_RNF:
1.1.1.18 root 2836: LOG_TRACE(TRACE_FDC, "fdc type II write sector=%d track=0x%x side=%d drive=%d RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2837: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2838:
2839: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
1.1.1.18 root 2840: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2841: break;
2842: case FDCEMU_RUN_WRITESECTORS_COMPLETE:
1.1.1.18 root 2843: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2844: break;
2845: }
1.1.1.15 root 2846:
1.1.1.18 root 2847: return FdcCycles;
1.1 root 2848: }
2849:
1.1.1.2 root 2850:
2851: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2852: /**
1.1.1.15 root 2853: * Run 'READ ADDRESS' command
1.1.1.9 root 2854: */
1.1.1.15 root 2855: static int FDC_UpdateReadAddressCmd ( void )
1.1 root 2856: {
1.1.1.18 root 2857: int FdcCycles = 0;
1.1.1.17 root 2858: int FrameCycles, HblCounterVideo, LineCycles;
2859:
2860: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2861:
1.1.1.6 root 2862: /* Which command is running? */
1.1.1.15 root 2863: switch (FDC.CommandState)
1.1.1.6 root 2864: {
1.1.1.15 root 2865: case FDCEMU_RUN_READADDRESS:
1.1.1.18 root 2866: if ( FDC_Set_MotorON ( FDC.CR ) )
2867: {
2868: FDC.CommandState = FDCEMU_RUN_READADDRESS_SPIN_UP;
2869: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2870: }
2871: else
2872: {
2873: FDC.CommandState = FDCEMU_RUN_READADDRESS_HEAD_LOAD;
2874: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2875: }
1.1.1.6 root 2876: break;
1.1.1.18 root 2877: case FDCEMU_RUN_READADDRESS_SPIN_UP:
2878: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2879: {
2880: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2881: break;
2882: }
2883: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
2884: case FDCEMU_RUN_READADDRESS_HEAD_LOAD:
2885: FDC.ReplaceCommandPossible = false;
2886: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
2887: {
2888: FDC.CommandState = FDCEMU_RUN_READADDRESS_MOTOR_ON;
2889: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2890: break;
2891: }
2892: /* If there's no head settle, we go directly to the _MOTOR_ON state */
2893: case FDCEMU_RUN_READADDRESS_MOTOR_ON:
2894: FDC.ReplaceCommandPossible = false;
2895: FDC.IndexPulse_Counter = 0;
2896: FDC.CommandState = FDCEMU_RUN_READADDRESS_NEXT_SECTOR_HEADER;
2897: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2898: break;
2899: case FDCEMU_RUN_READADDRESS_NEXT_SECTOR_HEADER:
2900: /* If we don't find a sector header after more than 5 revolutions, we abort with RNF */
2901: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2902: {
2903: FDC.CommandState = FDCEMU_RUN_READADDRESS_RNF;
2904: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2905: break;
2906: }
1.1.1.15 root 2907:
1.1.1.18 root 2908: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2909: FdcCycles = -1;
2910: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2911: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2912: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2913: else
2914: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2915: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2916: if ( FdcCycles < 0 )
2917: {
2918: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2919: }
2920: else
2921: {
2922: /* Read bytes to reach the next sector's ID field */
2923: FdcCycles += FDC_TransferByte_FdcCycles ( 4 ); /* Add delay to read 3xA1, FE */
2924: FDC.CommandState = FDCEMU_RUN_READADDRESS_TRANSFER_START;
2925: }
1.1.1.15 root 2926: break;
1.1.1.18 root 2927: case FDCEMU_RUN_READADDRESS_TRANSFER_START:
2928: /* Read the ID field into buffer */
2929: FDC_Buffer_Reset();
2930:
2931: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2932: FDC.Status_Temp = FDC_ReadAddress_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2933: FDC_NextSectorID_SR_STX () , FDC.SideSignal );
2934: else
2935: FDC.Status_Temp = FDC_ReadAddress_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2936: FDC_NextSectorID_SR_ST () , FDC.SideSignal );
1.1.1.17 root 2937:
1.1.1.18 root 2938: FDC.SR = FDC_Buffer_Read_Byte_pos ( 0 ); /* The 1st byte of the ID field is also copied into Sector Register */
1.1.1.17 root 2939:
1.1.1.18 root 2940: FDC.CommandState = FDCEMU_RUN_READADDRESS_TRANSFER_LOOP;
2941: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the first byte */
2942: break;
2943: case FDCEMU_RUN_READADDRESS_TRANSFER_LOOP:
2944: /* Transfer the ID field 1 byte at a time using DMA */
2945: FDC_DMA_FIFO_Push ( FDC_Buffer_Read_Byte () ); /* Add 1 byte to the DMA FIFO */
2946: if ( FDC_BUFFER.PosRead < FDC_Buffer_Get_Size () )
2947: {
2948: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
2949: }
2950: else
2951: {
2952: FDC.CommandState = FDCEMU_RUN_READADDRESS_COMPLETE;
2953: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2954: }
1.1.1.17 root 2955: break;
1.1.1.18 root 2956: case FDCEMU_RUN_READADDRESS_RNF:
2957: LOG_TRACE(TRACE_FDC, "fdc type III read address track=0x%x side=%d drive=%d RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2958: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.17 root 2959:
1.1.1.18 root 2960: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2961: FdcCycles = FDC_CmdCompleteCommon( true );
2962: break;
1.1.1.15 root 2963: case FDCEMU_RUN_READADDRESS_COMPLETE:
1.1.1.18 root 2964: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2965: break;
2966: }
1.1.1.15 root 2967:
1.1.1.18 root 2968: return FdcCycles;
1.1 root 2969: }
2970:
1.1.1.2 root 2971:
2972: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2973: /**
1.1.1.15 root 2974: * Run 'READ TRACK' command
1.1.1.9 root 2975: */
1.1.1.15 root 2976: static int FDC_UpdateReadTrackCmd ( void )
1.1 root 2977: {
1.1.1.18 root 2978: int FdcCycles = 0;
1.1.1.15 root 2979: int i;
1.1.1.18 root 2980: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2981:
1.1.1.18 root 2982: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2983:
1.1.1.6 root 2984: /* Which command is running? */
1.1.1.15 root 2985: switch (FDC.CommandState)
1.1.1.6 root 2986: {
1.1.1.15 root 2987: case FDCEMU_RUN_READTRACK:
1.1.1.18 root 2988: if ( FDC_Set_MotorON ( FDC.CR ) )
2989: {
2990: FDC.CommandState = FDCEMU_RUN_READTRACK_SPIN_UP;
2991: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2992: }
2993: else
2994: {
2995: FDC.CommandState = FDCEMU_RUN_READTRACK_HEAD_LOAD;
2996: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2997: }
2998: break;
2999: case FDCEMU_RUN_READTRACK_SPIN_UP:
3000: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
3001: {
3002: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
3003: break;
3004: }
3005: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
3006: case FDCEMU_RUN_READTRACK_HEAD_LOAD:
3007: FDC.ReplaceCommandPossible = false;
3008: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
3009: {
3010: FDC.CommandState = FDCEMU_RUN_READTRACK_MOTOR_ON;
3011: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
3012: break;
3013: }
3014: /* If there's no head settle, we go directly to the _MOTOR_ON state */
3015: case FDCEMU_RUN_READTRACK_MOTOR_ON:
3016: FdcCycles = FDC_NextIndexPulse_FdcCycles (); /* Wait for the next index pulse */
3017: //fprintf ( stderr , "read tr idx=%d %d\n" , FDC_IndexPulse_GetState() , FdcCycles );
3018: if ( FdcCycles < 0 )
3019: {
3020: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
3021: }
3022: else
3023: {
3024: FDC.CommandState = FDCEMU_RUN_READTRACK_INDEX;
3025: }
1.1.1.17 root 3026: break;
3027: case FDCEMU_RUN_READTRACK_INDEX:
1.1.1.18 root 3028: /* At this point, we have a valid drive/floppy, build the track data */
3029: FDC_Buffer_Reset();
1.1.1.6 root 3030:
1.1.1.18 root 3031: if ( ( FDC.SideSignal == 1 ) /* Try to read side 1 on a disk that doesn't have 2 sides or drive is single sided */
3032: && ( ( FDC_GetSidesPerDisk ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ) != 2 )
3033: || ( FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads == 1 ) ) )
1.1.1.15 root 3034: {
1.1.1.18 root 3035: LOG_TRACE(TRACE_FDC, "fdc type III read track drive=%d track=%d side=%d, side not found VBL=%d video_cyc=%d %d@%d pc=%x\n",
3036: FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal ,
3037: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3038:
3039: for ( i=0 ; i<FDC_GetBytesPerTrack ( FDC.DriveSelSignal ) ; i++ )
3040: FDC_Buffer_Add ( rand() & 0xff ); /* Fill the track buffer with random bytes */
3041: }
3042: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
3043: {
3044: FDC.Status_Temp = FDC_ReadTrack_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
1.1.1.6 root 3045: }
1.1.1.18 root 3046: else /* Track/side available in the disk image */
1.1.1.6 root 3047: {
1.1.1.18 root 3048: FDC.Status_Temp = FDC_ReadTrack_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
3049: }
1.1.1.15 root 3050:
1.1.1.18 root 3051: FDC.CommandState = FDCEMU_RUN_READTRACK_TRANSFER_LOOP;
3052: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the first byte */
3053: break;
3054: case FDCEMU_RUN_READTRACK_TRANSFER_LOOP:
3055: /* Transfer the track 1 byte at a time using DMA */
3056: FDC_DMA_FIFO_Push ( FDC_Buffer_Read_Byte () ); /* Add 1 byte to the DMA FIFO */
3057: if ( FDC_BUFFER.PosRead < FDC_Buffer_Get_Size () )
3058: {
3059: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
3060: }
3061: else /* Track completely transferred */
3062: {
3063: FDC.CommandState = FDCEMU_RUN_READTRACK_COMPLETE;
3064: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
3065: }
3066: break;
3067: case FDCEMU_RUN_READTRACK_COMPLETE:
3068: FdcCycles = FDC_CmdCompleteCommon( true );
3069: break;
3070: }
1.1.1.15 root 3071:
1.1.1.18 root 3072: return FdcCycles;
3073: }
1.1.1.15 root 3074:
3075:
1.1.1.18 root 3076: /*-----------------------------------------------------------------------*/
3077: /**
3078: * Run 'WRITE TRACK' command
3079: */
3080: static int FDC_UpdateWriteTrackCmd ( void )
3081: {
3082: int FdcCycles = 0;
3083: int FrameCycles, HblCounterVideo, LineCycles;
3084: Uint8 Byte;
3085: Uint8 Status;
1.1.1.15 root 3086:
1.1.1.18 root 3087: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3088:
3089: /* Which command is running? */
3090: switch (FDC.CommandState)
3091: {
3092: case FDCEMU_RUN_WRITETRACK:
3093: if ( FDC_Set_MotorON ( FDC.CR ) )
3094: {
3095: FDC.CommandState = FDCEMU_RUN_WRITETRACK_SPIN_UP;
3096: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
3097: }
3098: else
3099: {
3100: FDC.CommandState = FDCEMU_RUN_WRITETRACK_HEAD_LOAD;
3101: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
3102: }
3103: break;
3104: case FDCEMU_RUN_WRITETRACK_SPIN_UP:
3105: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
3106: {
3107: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
3108: break;
3109: }
3110: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
3111: case FDCEMU_RUN_WRITETRACK_HEAD_LOAD:
3112: FDC.ReplaceCommandPossible = false;
3113: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
3114: {
3115: FDC.CommandState = FDCEMU_RUN_WRITETRACK_MOTOR_ON;
3116: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
3117: break;
3118: }
3119: /* If there's no head settle, we go directly to the _MOTOR_ON state */
3120: case FDCEMU_RUN_WRITETRACK_MOTOR_ON:
3121: FdcCycles = FDC_NextIndexPulse_FdcCycles (); /* Wait for the next index pulse */
3122: //fprintf ( stderr , "write tr idx=%d %d\n" , FDC_IndexPulse_GetState() , FdcCycles );
3123: if ( FdcCycles < 0 )
3124: {
3125: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
3126: }
3127: else
3128: {
3129: FDC.CommandState = FDCEMU_RUN_WRITETRACK_INDEX;
3130: }
3131: break;
3132: case FDCEMU_RUN_WRITETRACK_INDEX:
3133: /* At this point, we have a valid drive/floppy, check write protection and write the track data */
3134: if ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) )
3135: {
3136: LOG_TRACE(TRACE_FDC, "fdc type III write track drive=%d track=0x%x side=%d WPRT VBL=%d video_cyc=%d %d@%d pc=%x\n",
3137: FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 3138:
1.1.1.18 root 3139: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
3140: FdcCycles = FDC_CmdCompleteCommon( true );
3141: break;
1.1.1.6 root 3142: }
1.1 root 3143:
1.1.1.18 root 3144: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit */
3145:
3146: FDC_Buffer_Reset();
3147: FDC_DMA.BytesToTransfer = FDC_GetBytesPerTrack ( FDC.DriveSelSignal );
1.1.1.2 root 3148:
1.1.1.18 root 3149: FDC.CommandState = FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP;
3150: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 3151: break;
1.1.1.18 root 3152: case FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP:
3153: /* Transfer the track 1 byte at a time using DMA */
3154: if ( FDC_DMA.BytesToTransfer-- > 0 )
1.1.1.6 root 3155: {
1.1.1.18 root 3156: Byte = FDC_DMA_FIFO_Pull (); /* Get 1 byte from the DMA FIFO */
3157: //fprintf ( stderr , "byte %d %x\n" , FDC_DMA.BytesToTransfer , Byte );
3158: FDC_Buffer_Add ( Byte );
3159: FdcCycles = FDC_TransferByte_FdcCycles ( 1 );
1.1.1.6 root 3160: }
1.1.1.18 root 3161: else /* Track written */
1.1.1.6 root 3162: {
1.1.1.18 root 3163: FDC.CommandState = FDCEMU_RUN_WRITETRACK_COMPLETE;
3164: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.6 root 3165: }
3166: break;
1.1.1.18 root 3167: case FDCEMU_RUN_WRITETRACK_COMPLETE:
3168: /* Track completely transferred */
3169: /* This is where we save the buffer to the disk image */
3170: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
3171: Status = FDC_WriteTrack_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
3172: FDC.SideSignal , FDC_Buffer_Get_Size () );
3173: else
3174: Status = FDC_WriteTrack_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
3175: FDC.SideSignal , FDC_Buffer_Get_Size () );
3176:
3177: if ( Status & FDC_STR_BIT_LOST_DATA ) /* Error while writing */
3178: FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* Set LOST_DATA bit */
3179:
3180: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 3181: break;
3182: }
1.1.1.15 root 3183:
1.1.1.18 root 3184: return FdcCycles;
1.1 root 3185: }
3186:
1.1.1.2 root 3187:
3188: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3189: /**
1.1.1.15 root 3190: * Common to types I, II and III
3191: *
3192: * Start motor / spin up sequence if needed
1.1.1.18 root 3193: * Return true if spin up sequence is needed, else false
1.1.1.11 root 3194: */
1.1.1.15 root 3195:
1.1.1.18 root 3196: static bool FDC_Set_MotorON ( Uint8 FDC_CR )
1.1.1.11 root 3197: {
1.1.1.15 root 3198: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 3199: bool SpinUp;
1.1.1.15 root 3200:
3201: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3202:
1.1.1.18 root 3203: if ( ( ( FDC_CR & FDC_COMMAND_BIT_SPIN_UP ) == 0 ) /* Command wants motor's spin up */
3204: && ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) == 0 ) ) /* Motor on not enabled yet */
3205: {
3206: LOG_TRACE(TRACE_FDC, "fdc start motor with spinup VBL=%d video_cyc=%d %d@%d pc=%x\n",
3207: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3208:
3209: FDC_Update_STR ( FDC_STR_BIT_SPIN_UP , 0 ); /* Unset spin up bit */
3210: FDC.IndexPulse_Counter = 0; /* Reset counter to measure the spin up sequence */
3211: SpinUp = true;
3212: }
3213: else /* No spin up : don't add delay to start the motor */
1.1.1.11 root 3214: {
1.1.1.18 root 3215: LOG_TRACE(TRACE_FDC, "fdc start motor without spinup VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.15 root 3216: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.18 root 3217:
3218: SpinUp = false;
1.1.1.11 root 3219: }
1.1.1.15 root 3220:
1.1.1.18 root 3221: FDC_Update_STR ( 0 , FDC_STR_BIT_MOTOR_ON ); /* Start motor */
3222:
3223: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
3224: || ( !FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted ) )
3225: {
3226: LOG_TRACE(TRACE_FDC, "fdc start motor : no disk/drive VBL=%d video_cyc=%d %d@%d pc=%x\n",
3227: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3228: }
3229: else if ( FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time == 0 )
3230: FDC_IndexPulse_Init ( FDC.DriveSelSignal ); /* Index Pulse's position is random when motor starts */
3231:
3232: return SpinUp;
1.1.1.11 root 3233: }
3234:
3235:
3236: /*-----------------------------------------------------------------------*/
3237: /**
1.1.1.9 root 3238: * Type I Commands
3239: *
3240: * Restore, Seek, Step, Step-In and Step-Out
3241: */
1.1 root 3242:
1.1.1.2 root 3243:
3244: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3245: static int FDC_TypeI_Restore(void)
1.1 root 3246: {
1.1.1.15 root 3247: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3248:
3249: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3250:
1.1.1.21! root 3251: LOG_TRACE(TRACE_FDC, "fdc type I restore spinup=%s verify=%s steprate_ms=%d drive=%d tr=0x%x head_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 3252: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3253: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3254: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3255: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal].HeadTrack : -1 ,
3256: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3257:
1.1.1.6 root 3258: /* Set emulation to seek to track zero */
1.1.1.15 root 3259: FDC.Command = FDCEMU_CMD_RESTORE;
3260: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO;
3261:
3262: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3263:
1.1.1.18 root 3264: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3265: }
3266:
1.1.1.2 root 3267:
3268: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3269: static int FDC_TypeI_Seek ( void )
1.1 root 3270: {
1.1.1.15 root 3271: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3272:
3273: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3274:
1.1.1.21! root 3275: LOG_TRACE(TRACE_FDC, "fdc type I seek dest_track=0x%x spinup=%s verify=%s steprate_ms=%d drive=%d tr=0x%x head_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.17 root 3276: FDC.DR,
1.1.1.18 root 3277: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3278: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3279: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3280: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3281: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3282:
1.1.1.6 root 3283: /* Set emulation to seek to chosen track */
1.1.1.15 root 3284: FDC.Command = FDCEMU_CMD_SEEK;
3285: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK;
3286:
3287: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3288:
1.1.1.18 root 3289: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3290: }
3291:
1.1.1.2 root 3292:
3293: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3294: static int FDC_TypeI_Step ( void )
1.1 root 3295: {
1.1.1.15 root 3296: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3297:
3298: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3299:
1.1.1.21! root 3300: LOG_TRACE(TRACE_FDC, "fdc type I step %d spinup=%s verify=%s steprate_ms=%d drive=%d tr=0x%x head_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.17 root 3301: FDC.StepDirection,
1.1.1.18 root 3302: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3303: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3304: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3305: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3306: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 3307:
3308: /* Set emulation to step (using same direction as latest seek executed, ie 'FDC.StepDirection') */
3309: FDC.Command = FDCEMU_CMD_STEP;
3310: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
1.1.1.10 root 3311:
1.1.1.15 root 3312: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3313:
1.1.1.18 root 3314: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3315: }
3316:
1.1.1.2 root 3317:
3318: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3319: static int FDC_TypeI_StepIn(void)
1.1 root 3320: {
1.1.1.15 root 3321: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3322:
3323: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3324:
1.1.1.21! root 3325: LOG_TRACE(TRACE_FDC, "fdc type I step in spinup=%s verify=%s steprate_ms=%d drive=%d tr=0x%x head_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 3326: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3327: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3328: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3329: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3330: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3331:
1.1.1.15 root 3332: /* Set emulation to step in (direction = +1) */
3333: FDC.Command = FDCEMU_CMD_STEP;
3334: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
3335: FDC.StepDirection = 1; /* Increment track*/
1.1 root 3336:
1.1.1.15 root 3337: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
3338:
1.1.1.18 root 3339: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3340: }
3341:
1.1.1.2 root 3342:
3343: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3344: static int FDC_TypeI_StepOut ( void )
1.1 root 3345: {
1.1.1.15 root 3346: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3347:
3348: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3349:
1.1.1.21! root 3350: LOG_TRACE(TRACE_FDC, "fdc type I step out spinup=%s verify=%s steprate_ms=%d drive=%d tr=0x%x head_track=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 3351: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3352: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3353: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3354: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3355: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 3356:
3357: /* Set emulation to step out (direction = -1) */
3358: FDC.Command = FDCEMU_CMD_STEP;
3359: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
3360: FDC.StepDirection = -1; /* Decrement track */
1.1.1.10 root 3361:
1.1.1.15 root 3362: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3363:
1.1.1.18 root 3364: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3365: }
3366:
1.1.1.2 root 3367:
3368: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3369: /**
3370: * Type II Commands
3371: *
1.1.1.15 root 3372: * Read Sector, Write Sector
1.1.1.9 root 3373: */
1.1 root 3374:
1.1.1.2 root 3375:
3376: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3377: static int FDC_TypeII_ReadSector ( void )
1.1 root 3378: {
1.1.1.15 root 3379: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3380:
3381: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3382:
1.1.1.17 root 3383: LOG_TRACE(TRACE_FDC, "fdc type II read sector sector=0x%x multi=%s spinup=%s settle=%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",
3384: FDC.SR, ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" ,
1.1.1.18 root 3385: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3386: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3387: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3388: FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount ,
1.1.1.15 root 3389: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.12 root 3390:
1.1.1.15 root 3391: /* Set emulation to read sector(s) */
3392: FDC.Command = FDCEMU_CMD_READSECTORS;
3393: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA;
1.1.1.10 root 3394:
1.1.1.15 root 3395: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3396: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
1.1 root 3397:
1.1.1.18 root 3398: return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
1.1 root 3399: }
3400:
1.1.1.2 root 3401:
3402: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3403: static int FDC_TypeII_WriteSector ( void )
1.1 root 3404: {
1.1.1.15 root 3405: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3406:
3407: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3408:
1.1.1.17 root 3409: LOG_TRACE(TRACE_FDC, "fdc type II write sector sector=0x%x multi=%s spinup=%s settle=%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",
3410: FDC.SR, ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" ,
1.1.1.18 root 3411: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3412: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3413: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3414: FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount,
1.1.1.15 root 3415: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.2 root 3416:
1.1.1.15 root 3417: /* Set emulation to write a sector(s) */
3418: FDC.Command = FDCEMU_CMD_WRITESECTORS;
3419: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA;
1.1.1.10 root 3420:
1.1.1.15 root 3421: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3422: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE , FDC_STR_BIT_BUSY );
1.1 root 3423:
1.1.1.18 root 3424: return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
1.1 root 3425: }
3426:
1.1.1.2 root 3427:
3428: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3429: /**
3430: * Type III Commands
3431: *
3432: * Read Address, Read Track, Write Track
3433: */
1.1 root 3434:
1.1.1.2 root 3435:
3436: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3437: static int FDC_TypeIII_ReadAddress ( void )
1.1 root 3438: {
1.1.1.15 root 3439: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3440:
3441: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3442:
1.1.1.17 root 3443: LOG_TRACE(TRACE_FDC, "fdc type III read address spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 3444: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3445: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3446: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3447: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.12 root 3448: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3449:
1.1.1.11 root 3450: /* Set emulation to seek to track zero */
1.1.1.15 root 3451: FDC.Command = FDCEMU_CMD_READADDRESS;
3452: FDC.CommandState = FDCEMU_RUN_READADDRESS;
1.1.1.11 root 3453:
1.1.1.15 root 3454: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3455: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
3456:
1.1.1.18 root 3457: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3458: }
3459:
1.1.1.2 root 3460:
3461: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3462: static int FDC_TypeIII_ReadTrack ( void )
1.1 root 3463: {
1.1.1.15 root 3464: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3465:
3466: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3467:
1.1.1.17 root 3468: LOG_TRACE(TRACE_FDC, "fdc type III read track spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 3469: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3470: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3471: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3472: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.15 root 3473: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3474:
1.1.1.15 root 3475: /* Set emulation to read a single track */
3476: FDC.Command = FDCEMU_CMD_READTRACK;
3477: FDC.CommandState = FDCEMU_RUN_READTRACK;
1.1.1.5 root 3478:
1.1.1.15 root 3479: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3480: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
1.1.1.5 root 3481:
1.1.1.18 root 3482: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3483: }
3484:
1.1.1.2 root 3485:
3486: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3487: static int FDC_TypeIII_WriteTrack ( void )
1.1 root 3488: {
1.1.1.15 root 3489: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3490:
3491: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3492:
1.1.1.17 root 3493: LOG_TRACE(TRACE_FDC, "fdc type III write track spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18 root 3494: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3495: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3496: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3497: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.15 root 3498: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3499:
1.1.1.6 root 3500: /* Set emulation to write a single track */
1.1.1.18 root 3501: FDC.Command = FDCEMU_CMD_WRITETRACK;
3502: FDC.CommandState = FDCEMU_RUN_WRITETRACK;
1.1 root 3503:
1.1.1.18 root 3504: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3505: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
3506:
3507: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3508: }
3509:
1.1.1.2 root 3510:
3511: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3512: /**
3513: * Type IV Commands
3514: *
3515: * Force Interrupt
3516: */
1.1 root 3517:
1.1.1.2 root 3518:
3519: /*-----------------------------------------------------------------------*/
1.1.1.18 root 3520: static int FDC_TypeIV_ForceInterrupt ( void )
1.1 root 3521: {
1.1.1.18 root 3522: int FdcCycles;
1.1.1.15 root 3523: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3524:
3525: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3526:
1.1.1.18 root 3527: LOG_TRACE(TRACE_FDC, "fdc type IV force int 0x%x irq=%d index=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.15 root 3528: FDC.CR , ( FDC.CR & 0x8 ) >> 3 , ( FDC.CR & 0x4 ) >> 2 ,
1.1.1.12 root 3529: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3530:
1.1.1.18 root 3531: /* If a command was running, just remove busy bit and keep the current content of Status reg */
3532: /* If FDC was idle, the content of Status reg is forced to type I */
3533: if ( ( FDC.STR & FDC_STR_BIT_BUSY ) == 0 )
1.1.1.15 root 3534: {
1.1.1.18 root 3535: FDC.StatusTypeI = true;
1.1.1.15 root 3536:
1.1.1.18 root 3537: /* Starting a Force Int command when idle should set the motor bit and clear the spinup bit (verified on STF) */
3538: FDC_Update_STR ( FDC_STR_BIT_SPIN_UP , FDC_STR_BIT_MOTOR_ON ); /* Clear spinup bit and set motor bit */
1.1.1.15 root 3539: }
3540:
1.1.1.18 root 3541: /* Get the interrupt's condition and set IRQ accordingly */
3542: /* Most of the time a 0xD8 command is followed by a 0xD0 command and a read STR to clear the IRQ signal */
3543: FDC.InterruptCond = FDC.CR & 0x0f; /* Keep the 4 lowest bits */
3544:
3545: if ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE )
3546: FDC_SetIRQ ( FDC_IRQ_SOURCE_FORCED );
3547: else
3548: FDC_ClearIRQ ();
3549:
3550: /* Remove busy bit, don't change IRQ's state and stop the motor */
3551: FdcCycles = FDC_CmdCompleteCommon( false );
1.1.1.6 root 3552:
1.1.1.18 root 3553: return FDC_DELAY_CYCLE_TYPE_IV_PREPARE + FdcCycles;
1.1 root 3554: }
3555:
1.1.1.2 root 3556:
3557: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3558: /**
3559: * Execute Type I commands
3560: */
1.1.1.15 root 3561: static int FDC_ExecuteTypeICommands ( void )
1.1 root 3562: {
1.1.1.18 root 3563: int FdcCycles = 0;
1.1.1.15 root 3564:
3565: FDC.CommandType = 1;
1.1.1.18 root 3566: FDC.StatusTypeI = true;
1.1 root 3567:
1.1.1.6 root 3568: /* Check Type I Command */
1.1.1.15 root 3569: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3570: {
3571: case 0x00: /* Restore */
1.1.1.18 root 3572: FdcCycles = FDC_TypeI_Restore();
1.1.1.6 root 3573: break;
3574: case 0x10: /* Seek */
1.1.1.18 root 3575: FdcCycles = FDC_TypeI_Seek();
1.1.1.6 root 3576: break;
3577: case 0x20: /* Step */
3578: case 0x30:
1.1.1.18 root 3579: FdcCycles = FDC_TypeI_Step();
1.1.1.6 root 3580: break;
3581: case 0x40: /* Step-In */
3582: case 0x50:
1.1.1.18 root 3583: FdcCycles = FDC_TypeI_StepIn();
1.1.1.6 root 3584: break;
3585: case 0x60: /* Step-Out */
3586: case 0x70:
1.1.1.18 root 3587: FdcCycles = FDC_TypeI_StepOut();
1.1.1.6 root 3588: break;
3589: }
1.1 root 3590:
1.1.1.18 root 3591: return FdcCycles;
1.1 root 3592: }
3593:
1.1.1.2 root 3594:
3595: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3596: /**
3597: * Execute Type II commands
3598: */
1.1.1.15 root 3599: static int FDC_ExecuteTypeIICommands ( void )
1.1 root 3600: {
1.1.1.18 root 3601: int FdcCycles = 0;
1.1.1.15 root 3602:
3603: FDC.CommandType = 2;
1.1.1.18 root 3604: FDC.StatusTypeI = false;
1.1 root 3605:
1.1.1.6 root 3606: /* Check Type II Command */
1.1.1.15 root 3607: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3608: {
1.1.1.15 root 3609: case 0x80: /* Read Sector multi=0*/
3610: case 0x90: /* Read Sectors multi=1 */
1.1.1.18 root 3611: FdcCycles = FDC_TypeII_ReadSector();
1.1.1.6 root 3612: break;
1.1.1.15 root 3613: case 0xa0: /* Write Sector multi=0 */
3614: case 0xb0: /* Write Sectors multi=1 */
1.1.1.18 root 3615: FdcCycles = FDC_TypeII_WriteSector();
1.1.1.6 root 3616: break;
3617: }
1.1 root 3618:
1.1.1.18 root 3619: return FdcCycles;
1.1 root 3620: }
3621:
1.1.1.2 root 3622:
3623: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3624: /**
3625: * Execute Type III commands
3626: */
1.1.1.15 root 3627: static int FDC_ExecuteTypeIIICommands ( void )
1.1 root 3628: {
1.1.1.18 root 3629: int FdcCycles = 0;
1.1.1.15 root 3630:
3631: FDC.CommandType = 3;
1.1.1.18 root 3632: FDC.StatusTypeI = false;
1.1 root 3633:
1.1.1.6 root 3634: /* Check Type III Command */
1.1.1.15 root 3635: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3636: {
3637: case 0xc0: /* Read Address */
1.1.1.18 root 3638: FdcCycles = FDC_TypeIII_ReadAddress();
1.1.1.6 root 3639: break;
3640: case 0xe0: /* Read Track */
1.1.1.18 root 3641: FdcCycles = FDC_TypeIII_ReadTrack();
1.1.1.6 root 3642: break;
3643: case 0xf0: /* Write Track */
1.1.1.18 root 3644: FdcCycles = FDC_TypeIII_WriteTrack();
1.1.1.6 root 3645: break;
3646: }
1.1 root 3647:
1.1.1.18 root 3648: return FdcCycles;
1.1 root 3649: }
3650:
1.1.1.2 root 3651:
3652: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3653: /**
3654: * Execute Type IV commands
3655: */
1.1.1.15 root 3656: static int FDC_ExecuteTypeIVCommands ( void )
1.1 root 3657: {
1.1.1.18 root 3658: int FdcCycles;
1.1 root 3659:
1.1.1.18 root 3660: FDC.CommandType = 4;
1.1.1.15 root 3661:
1.1.1.18 root 3662: FdcCycles = FDC_TypeIV_ForceInterrupt();
3663:
3664: return FdcCycles;
1.1 root 3665: }
3666:
1.1.1.2 root 3667:
3668: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3669: /**
3670: * Find FDC command type and execute
1.1.1.18 root 3671: *
3672: * NOTE [NP] : as verified on a real STF and contrary to what is written
3673: * in the WD1772 doc, any new command will reset the InterruptCond set by
3674: * a previous Dx command, not just a D0.
3675: * This means that a D8 command (force int) can be cancelled by a D0 command
3676: * or by any other command ; but in any case, IRQ will remain set until
3677: * status register is read or another new command is started.
3678: * -> 1st command clears force IRQ condition, 2nd command clears IRQ
1.1.1.9 root 3679: */
1.1.1.15 root 3680: static void FDC_ExecuteCommand ( void )
1.1 root 3681: {
1.1.1.18 root 3682: int FdcCycles;
3683: Uint8 Type;
3684:
3685: Type = FDC_GetCmdType ( FDC.CR );
3686:
3687: /* When a new command is started, FDC's IRQ is reset (except if "force interrupt immediate" is set) */
3688:
3689: /* If IRQ is forced but FDC_INTERRUPT_COND_IMMEDIATE is not set anymore, this means */
3690: /* the D8 command was stopped and we can clear the forced IRQ when starting a new command */
3691: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED )
3692: && ( ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE ) == 0 ) )
3693: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_FORCED; /* Really stop the forced IRQ */
3694:
3695: /* Starting a new type I/II/III should clear the IRQ (except when IRQ is forced) */
3696: /* For type IV, this is handled in FDC_TypeIV_ForceInterrupt() */
3697: if ( Type != 4 )
3698: FDC_ClearIRQ ();
3699:
3700: /* When a new command is executed, we clear InterruptCond */
3701: /* (not just when the new command is D0) */
3702: /* InterruptCond is cleared here, but it might be set again just after */
3703: /* when we call FDC_ExecuteTypeIVCommands() */
3704: FDC.InterruptCond = 0;
1.1.1.15 root 3705:
1.1.1.6 root 3706: /* Check type of command and execute */
1.1.1.18 root 3707: if ( Type == 1 ) /* Type I - Restore, Seek, Step, Step-In, Step-Out */
3708: FdcCycles = FDC_ExecuteTypeICommands();
3709: else if ( Type == 2 ) /* Type II - Read Sector, Write Sector */
3710: FdcCycles = FDC_ExecuteTypeIICommands();
3711: else if ( Type == 3 ) /* Type III - Read Address, Read Track, Write Track */
3712: FdcCycles = FDC_ExecuteTypeIIICommands();
1.1.1.15 root 3713: else /* Type IV - Force Interrupt */
1.1.1.18 root 3714: FdcCycles = FDC_ExecuteTypeIVCommands();
1.1.1.9 root 3715:
1.1.1.18 root 3716: FDC.ReplaceCommandPossible = true; /* This new command can be replaced during the prepare+spinup phase */
3717: FDC_StartTimer_FdcCycles ( FdcCycles , 0 );
1.1 root 3718: }
3719:
1.1.1.2 root 3720:
3721: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3722: /**
1.1.1.15 root 3723: * Write to SectorCount register $ff8604
1.1.1.9 root 3724: */
1.1.1.15 root 3725: static void FDC_WriteSectorCountRegister ( void )
1.1 root 3726: {
1.1.1.12 root 3727: int FrameCycles, HblCounterVideo, LineCycles;
3728:
3729: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3730:
1.1.1.15 root 3731: LOG_TRACE(TRACE_FDC, "fdc write 8604 dma sector count=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
3732: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.9 root 3733:
1.1.1.15 root 3734: FDC_DMA.SectorCount = IoMem_ReadByte(0xff8605);
1.1 root 3735: }
3736:
1.1.1.2 root 3737:
3738: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3739: /**
1.1.1.15 root 3740: * Write to Command register $ff8604
1.1.1.9 root 3741: */
1.1.1.15 root 3742: static void FDC_WriteCommandRegister ( void )
1.1 root 3743: {
1.1.1.12 root 3744: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 3745: Uint8 Type_new;
1.1.1.12 root 3746:
3747: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3748:
3749: 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 3750: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3751:
3752: /* If fdc is busy, only 'Force Interrupt' is possible */
3753: /* [NP] : it's also possible to start a new command just after another command */
1.1.1.18 root 3754: /* was started and spinup phase was not completed yet (eg Overdrive Demos by Phalanx) (see notes at the top of the file)*/
1.1.1.15 root 3755: if ( FDC.STR & FDC_STR_BIT_BUSY )
3756: {
1.1.1.18 root 3757: Type_new = FDC_GetCmdType ( IoMem_ReadByte(0xff8605) );
3758: if ( Type_new == 4 ) /* 'Force Interrupt' command */
1.1.1.15 root 3759: {
3760: 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",
3761: FDC.CR , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3762: }
3763:
1.1.1.18 root 3764: else if ( FDC.ReplaceCommandPossible
3765: && ( ( ( Type_new == 1 ) && ( FDC.CommandType == Type_new ) ) /* Replace a type I command with a type I */
3766: || ( ( Type_new == 2 ) && ( FDC.CommandType == Type_new ) ) ) ) /* Replace a type II command with a type II */
1.1.1.15 root 3767: {
3768: 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",
3769: FDC.CR , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3770: }
3771:
3772: else /* Other cases : new command is ignored */
3773: {
3774: LOG_TRACE(TRACE_FDC, "fdc write 8604 fdc busy, command=0x%x ignored VBL=%d video_cyc=%d %d@%d pc=%x\n",
3775: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3776: return;
3777: }
3778: }
1.1.1.9 root 3779:
1.1.1.15 root 3780: FDC.CR = IoMem_ReadByte(0xff8605);
1.1.1.6 root 3781: FDC_ExecuteCommand();
1.1 root 3782: }
3783:
1.1.1.2 root 3784:
3785: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3786: /**
1.1.1.15 root 3787: * Write to Track register $ff8604
1.1.1.9 root 3788: */
1.1.1.15 root 3789: static void FDC_WriteTrackRegister ( void )
1.1 root 3790: {
1.1.1.12 root 3791: int FrameCycles, HblCounterVideo, LineCycles;
3792:
3793: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3794:
3795: 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 3796: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 3797:
1.1.1.15 root 3798: /* [NP] Contrary to what is written in the WD1772 doc, Track Register can be changed */
1.1.1.18 root 3799: /* while the fdc is busy (the change will be ignored or not, depending on the current sub-state */
3800: /* in the state machine) */
1.1.1.15 root 3801: if ( FDC.STR & FDC_STR_BIT_BUSY )
3802: {
3803: 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",
3804: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.18 root 3805: //return;
1.1.1.15 root 3806: }
3807:
3808: FDC.TR = IoMem_ReadByte(0xff8605);
1.1 root 3809: }
3810:
1.1.1.2 root 3811:
3812: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3813: /**
1.1.1.15 root 3814: * Write to Sector register $ff8604
1.1.1.9 root 3815: */
1.1.1.15 root 3816: static void FDC_WriteSectorRegister ( void )
1.1 root 3817: {
1.1.1.12 root 3818: int FrameCycles, HblCounterVideo, LineCycles;
3819:
3820: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3821:
3822: 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 3823: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
3824:
3825: /* [NP] Contrary to what is written in the WD1772 doc, Sector Register can be changed */
3826: /* while the fdc is busy (but it will have no effect once the sector's header is found) */
3827: /* (fix Delirious Demo IV's loader, which is bugged and set SR after starting the Read Sector command) */
3828: if ( FDC.STR & FDC_STR_BIT_BUSY )
3829: {
3830: 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",
3831: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3832: }
1.1.1.9 root 3833:
1.1.1.15 root 3834: FDC.SR = IoMem_ReadByte(0xff8605);
1.1 root 3835: }
3836:
1.1.1.2 root 3837:
3838: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3839: /**
1.1.1.15 root 3840: * Write to Data register $ff8604
1.1.1.9 root 3841: */
1.1.1.15 root 3842: static void FDC_WriteDataRegister ( void )
1.1 root 3843: {
1.1.1.12 root 3844: int FrameCycles, HblCounterVideo, LineCycles;
3845:
3846: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3847:
3848: 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 3849: IoMem_ReadByte(0xff8605), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 3850:
1.1.1.15 root 3851: FDC.DR = IoMem_ReadByte(0xff8605);
1.1 root 3852: }
3853:
1.1.1.2 root 3854:
3855: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3856: /**
1.1.1.18 root 3857: * Store byte in FDC/HDC registers or DMA sector count, when writing to $ff8604
3858: * When accessing FDC/HDC registers, a copy of $ff8604 should be kept in ff8604_recent_val
3859: * to be used later when reading unused bits at $ff8604/$ff8606
3860: *
3861: * NOTE [NP] : add 4 cycles wait state in all cases (sector count / FDC / HDC)
1.1.1.9 root 3862: */
1.1.1.15 root 3863: void FDC_DiskController_WriteWord ( void )
1.1 root 3864: {
1.1.1.12 root 3865: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 3866: int EmulationMode;
3867: int FDC_reg;
1.1.1.12 root 3868:
1.1.1.15 root 3869: if ( nIoMemAccessSize == SIZE_BYTE )
1.1.1.6 root 3870: {
3871: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21! root 3872: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.6 root 3873: return;
3874: }
3875:
1.1.1.8 root 3876: M68000_WaitState(4);
3877:
1.1.1.12 root 3878: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.9 root 3879:
1.1.1.12 root 3880: 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 3881: IoMem_ReadWord(0xff8604), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.6 root 3882:
1.1.1.18 root 3883:
3884: /* Are we trying to set the DMA SectorCount ? */
3885: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.12 root 3886: {
1.1.1.18 root 3887: FDC_WriteSectorCountRegister();
1.1.1.10 root 3888: return;
1.1.1.12 root 3889: }
1.1.1.6 root 3890:
1.1.1.18 root 3891: /* Store the byte that was just accessed by this write */
3892: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | IoMem_ReadByte(0xff8605);
3893:
3894: if ( ( FDC_DMA.Mode & 0x0008 ) == 0x0008 ) /* Is it an ACSI (or Falcon SCSI) HDC command access ? */
1.1.1.8 root 3895: {
1.1.1.18 root 3896: /* Handle HDC access */
3897: LOG_TRACE(TRACE_FDC, "fdc write 8604 hdc command addr=%x command=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
3898: FDC_DMA.Mode & 0x7 , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3899:
3900: HDC_WriteCommandByte(FDC_DMA.Mode & 0x7, IoMem_ReadByte(0xff8605));
3901: return;
3902: }
3903:
3904: else /* It's a FDC register access */
3905: {
3906: FDC_reg = ( FDC_DMA.Mode & 0x6 ) >> 1; /* Bits 1,2 (A0,A1) */
3907:
3908: EmulationMode = FDC_GetEmulationMode();
3909: if ( EmulationMode == FDC_EMULATION_MODE_INTERNAL )
3910: {
3911: /* Update FDC's internal variables */
3912: FDC_UpdateAll ();
3913:
3914: /* Write to FDC registers */
3915: switch ( FDC_reg )
3916: {
3917: case 0x0: /* 0 0 - Command register */
3918: FDC_WriteCommandRegister();
3919: break;
3920: case 0x1: /* 0 1 - Track register */
3921: FDC_WriteTrackRegister();
3922: break;
3923: case 0x2: /* 1 0 - Sector register */
3924: FDC_WriteSectorRegister();
3925: break;
3926: case 0x3: /* 1 1 - Data register */
3927: FDC_WriteDataRegister();
3928: break;
3929: }
3930: }
3931: else if ( EmulationMode == FDC_EMULATION_MODE_IPF )
3932: {
3933: IPF_FDC_WriteReg ( FDC_reg , IoMem_ReadByte(0xff8605) );
1.1.1.6 root 3934: }
3935: }
1.1 root 3936: }
3937:
1.1.1.2 root 3938:
3939: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3940: /**
1.1.1.18 root 3941: * Return FDC/HDC registers or DMA sector count when reading from $ff8604
3942: * - When accessing FDC/HDC registers, a copy of $ff8604 should be kept in ff8604_recent_val
3943: * to be used later when reading unused bits at $ff8604/$ff8606
3944: * - DMA sector count can't be read, this will return ff8604_recent_val (verified on a real STF)
3945: *
3946: * NOTE [NP] : add 4 cycles wait state in that case, except when reading DMA sector count
1.1.1.9 root 3947: */
1.1.1.15 root 3948: void FDC_DiskControllerStatus_ReadWord ( void )
1.1 root 3949: {
1.1.1.15 root 3950: Uint16 DiskControllerByte = 0; /* Used to pass back the parameter */
1.1.1.12 root 3951: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 3952: int ForceWPRT;
1.1.1.18 root 3953: int EmulationMode;
3954: int FDC_reg;
1.1.1.6 root 3955:
1.1.1.20 root 3956: if (nIoMemAccessSize == SIZE_BYTE && !Config_IsMachineFalcon())
1.1.1.6 root 3957: {
3958: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21! root 3959: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.6 root 3960: return;
3961: }
3962:
1.1.1.18 root 3963: /* Are we trying to read the DMA SectorCount ? */
3964: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.6 root 3965: {
1.1.1.18 root 3966: DiskControllerByte = FDC_DMA.ff8604_recent_val; /* As verified on real STF, DMA sector count can't be read back */
1.1.1.6 root 3967: }
1.1.1.18 root 3968:
3969: else if ( ( FDC_DMA.Mode & 0x0008) == 0x0008) /* HDC status reg selected ? */
1.1.1.8 root 3970: {
1.1.1.18 root 3971: M68000_WaitState(4); /* [NP] : possible, but not tested on real HW */
3972:
3973: /* Return the HDC status byte */
3974: DiskControllerByte = HDC_ReadCommandByte(FDC_DMA.Mode & 0x7);
1.1.1.8 root 3975: }
1.1.1.18 root 3976:
3977: else /* It's a FDC register access */
1.1.1.6 root 3978: {
1.1.1.18 root 3979: M68000_WaitState(4);
3980:
3981: FDC_reg = ( FDC_DMA.Mode & 0x6 ) >> 1; /* Bits 1,2 (A0,A1) */
3982:
3983: EmulationMode = FDC_GetEmulationMode();
3984: if ( EmulationMode == FDC_EMULATION_MODE_INTERNAL )
1.1.1.6 root 3985: {
1.1.1.18 root 3986: /* Update FDC's internal variables */
3987: FDC_UpdateAll ();
3988:
3989: /* Read FDC registers */
3990: switch ( FDC_reg )
1.1.1.6 root 3991: {
1.1.1.18 root 3992: case 0x0: /* 0 0 - Status register */
3993: /* If we report a type I status, some bits are updated in real time */
3994: /* depending on the corresponding signals. If this is not a type I, we return STR unmodified */
3995: /* [NP] Contrary to what is written in the WD1772 doc, the WPRT bit */
3996: /* is updated after a Type I command */
3997: /* (eg : Procopy or Terminators Copy 1.68 do a Restore/Seek to test WPRT) */
3998: if ( FDC.StatusTypeI )
3999: {
4000: /* If no drive available, FDC's input signals TR00, INDEX and WPRT are off */
4001: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
4002: FDC_Update_STR ( FDC_STR_BIT_TR00 | FDC_STR_BIT_INDEX | FDC_STR_BIT_WPRT , 0 );
4003:
4004: else
4005: {
4006: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
4007: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set TR00 bit2 */
4008: else
4009: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset TR00 bit2 */
4010:
4011: if ( FDC_IndexPulse_GetState () )
4012: FDC_Update_STR ( 0 , FDC_STR_BIT_INDEX ); /* Set INDEX bit1 */
4013: else
4014: FDC_Update_STR ( FDC_STR_BIT_INDEX , 0 ); /* Unset INDEX bit1 */
4015:
4016: /* For Type I, always unset CRC ERROR bit3 */
4017: FDC_Update_STR ( FDC_STR_BIT_CRC_ERROR , 0 );
4018:
4019: /* When there's no disk in drive, the floppy drive hardware can't see */
4020: /* the difference with an inserted disk that would be write protected */
4021: if ( ! FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted )
4022: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit6 */
4023: else if ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) )
4024: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit6 */
4025: else
4026: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit6 */
4027:
4028: /* Temporarily change the WPRT bit if we're in a transition phase */
4029: /* regarding the disk in the drive (inserting or ejecting) */
4030: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC.DriveSelSignal );
4031: if ( ForceWPRT == 1 )
4032: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Force setting WPRT */
4033: else if ( ForceWPRT == -1 )
4034: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Force clearing WPRT */
4035:
4036: if ( ForceWPRT != 0 )
4037: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n",
4038: ForceWPRT==1?1:0, nVBLs, FDC.DriveSelSignal, FDC.STR );
4039: }
4040: }
1.1.1.17 root 4041:
1.1.1.18 root 4042: DiskControllerByte = FDC.STR;
4043:
4044: /* When Status Register is read, FDC's IRQ is reset (except if "force interrupt immediate" is set) */
4045:
4046: /* If IRQ is forced but FDC_INTERRUPT_COND_IMMEDIATE is not set anymore, this means */
4047: /* the D8 command was stopped and we can clear the forced IRQ while reading status register */
4048: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED )
4049: && ( ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE ) == 0 ) )
4050: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_FORCED; /* Really stop the forced IRQ */
4051:
4052: FDC_ClearIRQ ();
4053: break;
4054: case 0x1: /* 0 1 - Track register */
4055: DiskControllerByte = FDC.TR;
4056: break;
4057: case 0x2: /* 1 0 - Sector register */
4058: DiskControllerByte = FDC.SR;
4059: break;
4060: case 0x3: /* 1 1 - Data register */
4061: DiskControllerByte = FDC.DR;
4062: break;
1.1.1.6 root 4063: }
1.1.1.18 root 4064: }
4065: else if ( EmulationMode == FDC_EMULATION_MODE_IPF )
4066: {
4067: DiskControllerByte = IPF_FDC_ReadReg ( FDC_reg );
4068: if ( ( FDC_reg == 0x0 ) && ( FDC.DriveSelSignal >= 0 ) ) /* 0 0 - Status register */
4069: {
4070: /* Temporarily change the WPRT bit if we're in a transition phase */
4071: /* regarding the disk in the drive (inserting or ejecting) */
4072: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC.DriveSelSignal );
4073: if ( ForceWPRT == 1 )
4074: DiskControllerByte |= FDC_STR_BIT_WPRT; /* Force setting WPRT */
4075: if ( ForceWPRT == -1 )
4076: DiskControllerByte &= ~FDC_STR_BIT_WPRT; /* Force clearing WPRT */
1.1.1.6 root 4077:
1.1.1.18 root 4078: if ( ForceWPRT != 0 )
4079: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n", ForceWPRT==1?1:0, nVBLs, FDC.DriveSelSignal, DiskControllerByte );
4080: }
1.1.1.6 root 4081: }
4082: }
4083:
1.1.1.18 root 4084:
4085: /* Store the byte that was just returned by this read if we accessed fdc/hdc regs */
4086: if ( ( FDC_DMA.Mode & 0x10 ) == 0 ) /* Bit 4 */
4087: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | ( DiskControllerByte & 0xff );
4088:
1.1.1.7 root 4089: IoMem_WriteWord(0xff8604, DiskControllerByte);
1.1.1.9 root 4090:
1.1.1.12 root 4091: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4092:
4093: LOG_TRACE(TRACE_FDC, "fdc read 8604 ctrl status=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4094: DiskControllerByte , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1 root 4095: }
4096:
1.1.1.2 root 4097:
4098: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4099: /**
1.1.1.15 root 4100: * Write word to $ff8606 (DMA Mode Control)
4101: *
4102: * Eg.
4103: * $80 - Selects command/status register
4104: * $82 - Selects track register
4105: * $84 - Selects sector register
1.1.1.17 root 4106: * $86 - Selects data register
1.1.1.15 root 4107: * NOTE - OR above values with $100 is transfer from memory to floppy
4108: * Also if bit 4 is set, write to DMA sector count register
1.1.1.18 root 4109: *
4110: * NOTE [NP] : add 4 cycles wait state in that case
1.1.1.15 root 4111: */
4112: void FDC_DmaModeControl_WriteWord ( void )
4113: {
4114: Uint16 Mode_prev; /* Store previous write to 0xff8606 for 'toggle' checks */
4115: int FrameCycles, HblCounterVideo, LineCycles;
4116:
4117:
4118: if (nIoMemAccessSize == SIZE_BYTE)
4119: {
4120: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21! root 4121: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.15 root 4122: return;
4123: }
4124:
1.1.1.18 root 4125: M68000_WaitState(4);
4126:
1.1.1.15 root 4127: Mode_prev = FDC_DMA.Mode; /* Store previous to check for _read/_write toggle (DMA reset) */
4128: FDC_DMA.Mode = IoMem_ReadWord(0xff8606); /* Store to DMA Mode control */
4129:
4130: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4131:
4132: LOG_TRACE(TRACE_FDC, "fdc write 8606 ctrl=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4133: FDC_DMA.Mode , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4134:
4135: /* When write to 0xff8606, check bit '8' toggle. This causes DMA status reset */
4136: if ((Mode_prev ^ FDC_DMA.Mode) & 0x0100)
4137: FDC_ResetDMA();
4138: }
4139:
4140:
4141: /*-----------------------------------------------------------------------*/
4142: /**
4143: * Read DMA Status at $ff8606
4144: *
1.1.1.18 root 4145: * Only bits 0-2 are used :
4146: * Bit 0 - Error Status (0=Error)
4147: * Bit 1 - Sector Count Zero Status (0=Sector Count Zero)
4148: * Bit 2 - Data Request signal from the FDC
4149: *
4150: * NOTE [NP] : as verified on STF, bit 0 will be cleared (=error) if DMA sector count is 0
4151: * when we get some DRQ to process.
4152: *
4153: * NOTE [NP] : on the ST, the Data Register will always be read by the DMA when the FDC's DRQ
4154: * signal is set. This means bit 2 of DMA status will be '0' nearly all the time
4155: * (as verified on STF by constantly reading DMA Status, bit 2 can be '1' during
4156: * a few cycles, before the DMA read the Data Register, but for the emulation we
4157: * consider it's always '0')
4158: *
4159: * NOTE [NP] : unused bits 3-15 are the ones from the latest $ff8604 access (verified on real STF)
4160: *
4161: * NOTE [NP] : no 4 cycles wait state in that case
1.1.1.15 root 4162: */
4163: void FDC_DmaStatus_ReadWord ( void )
4164: {
1.1.1.20 root 4165: if (nIoMemAccessSize == SIZE_BYTE && !Config_IsMachineFalcon())
1.1.1.15 root 4166: {
4167: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21! root 4168: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.15 root 4169: return;
4170: }
4171:
1.1.1.18 root 4172: /* Update Bit1 for zero sector count */
4173: if ( FDC_DMA.SectorCount != 0 )
4174: FDC_DMA.Status |= 0x02;
1.1.1.15 root 4175: else
1.1.1.18 root 4176: FDC_DMA.Status &= ~0x02;
1.1.1.15 root 4177:
1.1.1.18 root 4178: /* In the case of the ST, Bit2 / DRQ is always 0 because it's handled by the DMA and its 16 bytes buffer */
1.1.1.15 root 4179:
1.1.1.18 root 4180: /* Return Status with unused bits replaced by latest bits from $ff8604 */
4181: IoMem_WriteWord( 0xff8606 , FDC_DMA.Status | ( FDC_DMA.ff8604_recent_val & 0xfff8 ) );
1.1.1.15 root 4182: }
4183:
4184:
4185: /*-----------------------------------------------------------------------*/
4186: /**
4187: * Read hi/med/low DMA address byte at $ff8609/0b/0d
4188: */
4189: void FDC_DmaAddress_ReadByte ( void )
4190: {
4191: int FrameCycles, HblCounterVideo, LineCycles;
4192:
4193: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4194:
4195: 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" ,
4196: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] , FDC_GetDMAAddress() ,
4197: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4198: }
4199:
4200:
4201: /*-----------------------------------------------------------------------*/
4202: /**
4203: * Write hi/med/low DMA address byte at $ff8609/0b/0d
1.1.1.21! root 4204: *
! 4205: * NOTE [NP] : as described by Ijor in http://atari-forum.com/viewtopic.php?f=16&t=30289
! 4206: * the STF DMA address counter uses a ripple carry adder that will increment middle byte
! 4207: * when bit 7 of lower byte goes from 1 to 0 (same for middle/high bytes)
! 4208: * To avoid possible error with this carry, DMA address bytes should be written in the order
! 4209: * low, middle, high (as specified by Atari documentations) and not high/middle/low
1.1.1.15 root 4210: */
4211: void FDC_DmaAddress_WriteByte ( void )
4212: {
1.1.1.21! root 4213: Uint32 Address;
! 4214: Uint32 Address_old;
1.1.1.15 root 4215: int FrameCycles, HblCounterVideo, LineCycles;
4216:
4217: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4218:
1.1.1.21! root 4219: LOG_TRACE(TRACE_FDC, "fdc write dma address %x val=0x%02x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
! 4220: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] ,
! 4221: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 4222:
! 4223: /* Build up 24-bit address from hardware registers */
! 4224: Address = ((Uint32)STMemory_ReadByte(0xff8609)<<16) | ((Uint32)STMemory_ReadByte(0xff860b)<<8) | (Uint32)STMemory_ReadByte(0xff860d);
! 4225:
! 4226: /* On STF, DMA address uses a "ripple carry adder" which can trigger when writing to $ff860b/0d */
! 4227: if ( Config_IsMachineST() )
1.1.1.20 root 4228: {
1.1.1.21! root 4229: Address_old = FDC_GetDMAAddress();
1.1.1.18 root 4230:
1.1.1.21! root 4231: if ( ( Address_old & 0x80 ) && !( Address & 0x80 ) ) /* Bit 7 goes from 1 to 0 */
! 4232: {
! 4233: Address += 0x100; /* Increase middle byte (and high byte if needed) */
! 4234: //fprintf ( stderr , "fdc write dma address detect ripple carry at $ff860d old=0x%x new=0x%x\n" , Address_old , Address );
! 4235: LOG_TRACE(TRACE_FDC, "fdc write dma address detect ripple carry at $ff860d old=0x%x new=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
! 4236: Address_old , Address ,
! 4237: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 4238: }
! 4239: else if ( ( Address_old & 0x8000 ) && !( Address & 0x8000 ) ) /* Bit 15 goes from 1 to 0 */
! 4240: {
! 4241: Address += 0x10000; /* Increase high byte */
! 4242: //fprintf ( stderr , "fdc write dma address detect ripple carry at $ff860b old=0x%x new=0x%x\n" , Address_old , Address );
! 4243: LOG_TRACE(TRACE_FDC, "fdc write dma address detect ripple carry at $ff860b old=0x%x new=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
! 4244: Address_old , Address ,
! 4245: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 4246: }
! 4247: }
1.1.1.18 root 4248:
1.1.1.21! root 4249: /* Store new address as DMA address and update $ff8609/0b/0d */
! 4250: FDC_WriteDMAAddress ( Address );
1.1.1.15 root 4251: }
4252:
4253:
4254: /*-----------------------------------------------------------------------*/
4255: /**
1.1.1.21! root 4256: * Get DMA address used to transfer data between FDC/HDC and RAM
1.1.1.9 root 4257: */
1.1.1.15 root 4258: Uint32 FDC_GetDMAAddress(void)
1.1 root 4259: {
1.1.1.21! root 4260: return FDC_DMA_Address;
1.1 root 4261: }
4262:
1.1.1.2 root 4263:
4264: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4265: /**
1.1.1.21! root 4266: * Write a new address to the FDC/HDC DMA address registers at $ff8909/0b/0d
1.1.1.18 root 4267: * As verified on real STF, DMA address high byte written at $ff8609 is masked
4268: * with 0x3f :
4269: * move.b #$ff,$ff8609
4270: * move.b $ff8609,d0 -> d0=$3f
4271: * DMA address must also be word-aligned, low byte at $ff860d is masked with 0xfe
4272: * move.b #$ff,$ff860d
4273: * move.b $ff860d,d0 -> d0=$fe
1.1.1.9 root 4274: */
1.1.1.15 root 4275: void FDC_WriteDMAAddress ( Uint32 Address )
1.1 root 4276: {
1.1.1.12 root 4277: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.21! root 4278: Uint32 dma_mask;
1.1.1.12 root 4279:
4280: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4281:
1.1.1.21! root 4282: LOG_TRACE(TRACE_FDC, "fdc write dma address new=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.12 root 4283: Address , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4284:
1.1.1.21! root 4285: /* Mask DMA address : */
! 4286: /* - DMA address must be word-aligned, bit 0 at $ff860d is always 0 */
! 4287: /* - On STF/STE machines limited to 4MB of RAM, DMA address is also limited to $3fffff */
! 4288: dma_mask = 0xff00fffe | ( DMA_MaskAddressHigh() << 16 ); /* Force bit 0 to 0 */
! 4289: FDC_DMA_Address = Address & dma_mask;
1.1.1.18 root 4290:
1.1.1.6 root 4291: /* Store as 24-bit address */
1.1.1.21! root 4292: STMemory_WriteByte(0xff8609, FDC_DMA_Address>>16);
! 4293: STMemory_WriteByte(0xff860b, FDC_DMA_Address>>8);
! 4294: STMemory_WriteByte(0xff860d, FDC_DMA_Address);
1.1 root 4295: }
4296:
1.1.1.2 root 4297:
4298: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4299: /**
1.1.1.18 root 4300: * Return the number of FDC cycles to wait before reaching the next
4301: * sector's ID Field in the track ($A1 $A1 $A1 $FE TR SIDE SR LEN CRC1 CRC2)
4302: * If no ID Field is found before the end of the track, we use the 1st
4303: * ID Field of the track (which simulates a full spin of the floppy).
4304: * We also store the next sector's number into NextSector_ID_Field_SR,
1.1.1.20 root 4305: * the next track's number into NextSector_ID_Field_TR, the next sector's length
1.1.1.18 root 4306: * into NextSector_ID_Field_LEN and if the CRC is correct or not into
4307: * NextSector_ID_Field_CRC_OK.
4308: * This function assumes some 512 byte sectors stored in ascending
4309: * order (for ST/MSA)
4310: * If there's no available drive/floppy, we return -1
1.1.1.9 root 4311: */
1.1.1.18 root 4312: static int FDC_NextSectorID_FdcCycles_ST ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side )
1.1 root 4313: {
1.1.1.18 root 4314: int CurrentPos;
4315: int MaxSector;
4316: int TrackPos;
4317: int i;
4318: int NextSector;
4319: int NbBytes;
4320:
4321: CurrentPos = FDC_IndexPulse_GetCurrentPos_NbBytes ();
4322: if ( CurrentPos < 0 ) /* No drive/floppy available at the moment */
4323: return -1;
4324:
4325: if ( ( Side == 1 ) && ( NumberOfHeads == 1 ) ) /* Can't read side 1 on a single sided drive */
4326: return -1;
4327:
1.1.1.19 root 4328: if ( Track >= FDC_GetTracksPerDisk ( Drive ) ) /* Try to access a non existing track */
4329: return -1;
4330:
1.1.1.18 root 4331: MaxSector = FDC_GetSectorsPerTrack ( Drive , Track , Side );
4332: TrackPos = FDC_TRACK_LAYOUT_STANDARD_GAP1; /* Position of 1st raw sector */
4333: TrackPos += FDC_TRACK_LAYOUT_STANDARD_GAP2; /* Position of ID Field in 1st raw sector */
4334:
4335: /* Compare CurrentPos with each sector's position in ascending order */
4336: for ( i=0 ; i<MaxSector ; i++ )
4337: {
4338: if ( CurrentPos < TrackPos )
4339: break; /* We found the next sector */
4340: else
4341: TrackPos += FDC_TRACK_LAYOUT_STANDARD_RAW_SECTOR_512;
4342: }
4343:
4344: if ( i == MaxSector ) /* CurrentPos is after the last ID Field of this track */
4345: {
4346: /* Reach end of track (new index pulse), then go to sector 1 */
4347: NbBytes = FDC_GetBytesPerTrack ( Drive ) - CurrentPos + FDC_TRACK_LAYOUT_STANDARD_GAP1 + FDC_TRACK_LAYOUT_STANDARD_GAP2;
4348: NextSector = 1;
4349: }
4350: else /* There's an ID Field before end of track */
4351: {
4352: NbBytes = TrackPos - CurrentPos;
4353: NextSector = i+1;
4354: }
4355:
4356: //fprintf ( stderr , "fdc bytes next sector pos=%d trpos=%d nbbytes=%d maxsr=%d nextsr=%d\n" , CurrentPos, TrackPos, NbBytes, MaxSector, NextSector );
4357: FDC.NextSector_ID_Field_TR = Track;
4358: FDC.NextSector_ID_Field_SR = NextSector;
4359: FDC.NextSector_ID_Field_LEN = FDC_SECTOR_SIZE_512; /* ST/MSA have 512 bytes per sector */
4360: FDC.NextSector_ID_Field_CRC_OK = 1; /* CRC is always correct for ST/MSA */
4361:
4362: return FDC_TransferByte_FdcCycles ( NbBytes );
4363: }
4364:
4365:
4366: /*-----------------------------------------------------------------------*/
4367: /**
4368: * Return the value of the track number in the next ID field set by
4369: * FDC_NextSectorID_FdcCycles_ST (for ST/MSA, it's always HeadTrack value)
4370: */
4371: static Uint8 FDC_NextSectorID_TR_ST ( void )
4372: {
4373: return FDC.NextSector_ID_Field_TR;
4374: }
4375:
4376:
4377: /*-----------------------------------------------------------------------*/
4378: /**
4379: * Return the value of the sector number in the next ID field set by
4380: * FDC_NextSectorID_FdcCycles_ST.
4381: */
4382: static Uint8 FDC_NextSectorID_SR_ST ( void )
4383: {
4384: return FDC.NextSector_ID_Field_SR;
4385: }
4386:
4387:
4388: /*-----------------------------------------------------------------------*/
4389: /**
4390: * Return the value of the sector's length in the next ID field set by
4391: * FDC_NextSectorID_FdcCycles_ST.
4392: * For ST/MSA, it's always 2 (512 bytes per sector)
4393: */
4394: static Uint8 FDC_NextSectorID_LEN_ST ( void )
4395: {
4396: return FDC.NextSector_ID_Field_LEN;
4397: }
4398:
4399:
4400: /*-----------------------------------------------------------------------*/
4401: /**
4402: * Return the status of the CRC in the next ID field set by
4403: * FDC_NextSectorID_FdcCycles_ST.
4404: * If '0', CRC is bad, else CRC is OK
4405: * For ST/MSA, CRC is always OK
4406: */
4407: static Uint8 FDC_NextSectorID_CRC_OK_ST ( void )
4408: {
4409: return FDC.NextSector_ID_Field_CRC_OK;
4410: }
4411:
4412:
4413: /*-----------------------------------------------------------------------*/
4414: /**
4415: * Read a sector from a floppy image in ST format (used in type II command)
4416: * Each byte of the sector is added to the FDC buffer with a default timing
4417: * (32 microsec)
4418: * Return 0 if sector was read without error, or FDC_STR_BIT_RNF if an error occurred
4419: * (FDC_STR_BIT_CRC_ERROR and FDC_STR_BIT_RECORD_TYPE are always set 0 for ST/MSA)
4420: */
4421: static Uint8 FDC_ReadSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int *pSectorSize )
4422: {
4423: int FrameCycles, HblCounterVideo, LineCycles;
4424: int i;
4425: Uint8 *pSectorData;
1.1.1.12 root 4426:
4427: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4428:
1.1.1.18 root 4429: LOG_TRACE(TRACE_FDC, "fdc read sector addr=0x%x drive=%d track=%d sect=%d side=%d VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4430: FDC_GetDMAAddress(), Drive, Track, Sector, Side,
1.1.1.12 root 4431: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4432:
1.1.1.18 root 4433: /* Get a pointer to the sector's data and convert into bytes/timings */
4434: if ( Floppy_ReadSectors ( Drive, &pSectorData, Sector, Track, Side, 1, NULL, pSectorSize ) )
4435: {
4436: for ( i=0 ; i<*pSectorSize ; i++ )
4437: FDC_Buffer_Add ( pSectorData[ i ] );
4438: return 0; /* No error */
4439: }
1.1 root 4440:
1.1.1.6 root 4441: /* Failed */
1.1.1.12 root 4442: LOG_TRACE(TRACE_FDC, "fdc read sector failed\n" );
1.1.1.18 root 4443: return FDC_STR_BIT_RNF;
1.1 root 4444: }
4445:
1.1.1.2 root 4446:
4447: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4448: /**
1.1.1.18 root 4449: * Write a sector to a floppy image in ST format (used in type II command)
4450: * Bytes were added to the FDC Buffer with a default timing (32 microsec) ;
4451: * we copy only the bytes into a temporary buffer, and write this buffer
4452: * to the floppy image.
1.1.1.15 root 4453: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
4454: * and some '0' bytes will be written to the disk.
1.1.1.18 root 4455: * Return 0 if sector was written without error, or FDC_STR_BIT_RNF if an error occurred
1.1.1.9 root 4456: */
1.1.1.18 root 4457: static Uint8 FDC_WriteSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize )
1.1 root 4458: {
1.1.1.18 root 4459: int FrameCycles, HblCounterVideo, LineCycles;
4460: int i;
4461: Uint8 SectorData[ 1024 ]; /* max sector size for WD1772 */
1.1.1.12 root 4462:
4463: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4464:
1.1.1.18 root 4465: LOG_TRACE(TRACE_FDC, "fdc write sector addr=0x%x drive=%d track=%d sect=%d side=%d VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4466: FDC_GetDMAAddress(), Drive, Track, Sector, Side,
1.1.1.12 root 4467: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4468:
1.1.1.18 root 4469: /* Get the sector's data (ignore timings) */
4470: for ( i=0 ; i<SectorSize ; i++ )
4471: SectorData[ i ] = FDC_Buffer_Read_Byte_pos ( i );
4472:
4473: /* Write the sector's data */
4474: if ( Floppy_WriteSectors ( Drive, SectorData, Sector, Track, Side, 1, NULL, NULL ) )
4475: return 0; /* No error */
4476:
4477: /* Failed */
4478: LOG_TRACE(TRACE_FDC, "fdc write sector failed\n" );
4479: return FDC_STR_BIT_RNF;
4480: }
4481:
4482:
4483: /*-----------------------------------------------------------------------*/
4484: /**
4485: * Read an address field from a floppy image in ST format (used in type III command)
4486: * As ST images don't have address field, we compute a standard one based
4487: * on the current track/sector/side.
4488: * Each byte of the ID field is added to the FDC buffer with a default timing
4489: * (32 microsec)
4490: * Always return 0 = OK (FDC_STR_BIT_CRC_ERROR and FDC_STR_BIT_RNF are
4491: * always set 0 for ST/MSA)
4492: */
4493: static Uint8 FDC_ReadAddress_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side )
4494: {
4495: int FrameCycles, HblCounterVideo, LineCycles;
4496: Uint8 buf_id[ 10 ]; /* 3 SYNC + IAM + TR + SIDE + SECTOR + SIZE + CRC1 + CRC2 */
4497: Uint8 *p;
4498: Uint16 CRC;
4499: int i;
4500:
4501: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4502:
1.1.1.19 root 4503: /* If trying to access address field on a non existing track, then return RNF */
4504: if ( Track >= FDC_GetTracksPerDisk ( Drive ) )
4505: {
4506: fprintf ( stderr , "fdc : read address drive=%d track=%d side=%d, but maxtrack=%d, return RNF\n" ,
4507: Drive , Track , Side , FDC_GetTracksPerDisk ( Drive ) );
4508: return STX_SECTOR_FLAG_RNF; /* Should not happen if FDC_NextSectorID_FdcCycles_ST succeeded before */
4509: }
4510:
1.1.1.18 root 4511: p = buf_id;
4512:
4513: *p++ = 0xa1; /* SYNC bytes and IAM byte are included in the CRC */
4514: *p++ = 0xa1;
4515: *p++ = 0xa1;
4516: *p++ = 0xfe;
4517: *p++ = Track;
4518: *p++ = Side;
4519: *p++ = Sector;
4520: *p++ = FDC_SECTOR_SIZE_512; /* ST/MSA images are 512 bytes per sector */
4521:
4522: FDC_CRC16 ( buf_id , 8 , &CRC );
4523:
4524: *p++ = CRC >> 8;
4525: *p++ = CRC & 0xff;
4526:
4527: /* 6 bytes per ID field, don't return the 3 x $A1 and $FE */
4528: for ( i=4 ; i<10 ; i++ )
4529: FDC_Buffer_Add ( buf_id[ i ] );
4530:
4531: LOG_TRACE(TRACE_FDC, "fdc read address 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x VBL=%d video_cyc=%d %d@%d pc=%x\n",
4532: buf_id[4] , buf_id[5] , buf_id[6] , buf_id[7] , buf_id[8] , buf_id[9] ,
4533: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
4534:
4535: return 0; /* No error */
4536: }
4537:
4538:
4539: /*-----------------------------------------------------------------------*/
4540: /**
4541: * Read a track from a floppy image in ST format (used in type III command)
4542: * As ST images don't have gaps,sync,..., we compute a standard track based
4543: * on the current track/side.
4544: * Each byte of the track is added to the FDC buffer with a default timing
4545: * (32 microsec)
4546: * Always return 0 = OK (we fill the track buffer in all cases)
4547: */
4548: static Uint8 FDC_ReadTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side )
4549: {
4550: int FrameCycles, HblCounterVideo, LineCycles;
4551: Uint8 buf_id[ 10 ]; /* 3 SYNC + IAM + TR + SIDE + SECTOR + SIZE + CRC1 + CRC2 */
4552: Uint8 *p;
4553: Uint16 CRC;
4554: int Sector;
4555: Uint8 *pSectorData;
4556: int SectorSize;
4557: int i;
4558:
4559: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4560:
4561: LOG_TRACE(TRACE_FDC, "fdc type III read track drive=%d track=%d side=%d VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4562: Drive, Track, Side, nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4563:
1.1.1.19 root 4564: /* If trying to access a non existing track, then return an empty / not formatted track */
4565: if ( Track >= FDC_GetTracksPerDisk ( Drive ) )
4566: {
4567: fprintf ( stderr , "fdc : read track drive=%d track=%d side=%d, but maxtrack=%d, building an unformatted track\n" ,
4568: Drive , Track , Side , FDC_GetTracksPerDisk ( Drive ) );
4569: for ( i=0 ; i<FDC_GetBytesPerTrack ( Drive ) ; i++ )
4570: FDC_Buffer_Add ( rand() & 0xff ); /* Fill the track buffer with random bytes */
4571: return 0;
4572: }
1.1.1.18 root 4573:
4574: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP1 ; i++ ) /* GAP1 */
4575: FDC_Buffer_Add ( 0x4e );
4576:
4577: for ( Sector=1 ; Sector <= FDC_GetSectorsPerTrack ( Drive , Track , Side ) ; Sector++ )
1.1.1.6 root 4578: {
1.1.1.18 root 4579: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP2 ; i++ ) /* GAP2 */
4580: FDC_Buffer_Add ( 0x00 );
4581:
4582: /* Add the ID field for the sector */
4583: p = buf_id;
4584: for ( i=0 ; i<3 ; i++ ) *p++ = 0xa1; /* SYNC (write $F5) */
4585: *p++ = 0xfe; /* Index Address Mark */
4586: *p++ = Track; /* Track */
4587: *p++ = Side; /* Side */
4588: *p++ = Sector; /* Sector */
4589: *p++ = FDC_SECTOR_SIZE_512; /* 512 bytes/sector for ST/MSA */
4590: FDC_CRC16 ( buf_id , 8 , &CRC );
4591: *p++ = CRC >> 8; /* CRC1 (write $F7) */
4592: *p++ = CRC & 0xff; /* CRC2 */
4593:
4594: for ( i=0 ; i<10 ; i++ ) /* Add the ID field to the track data */
4595: FDC_Buffer_Add ( buf_id[ i ] );
4596:
4597: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3a ; i++ ) /* GAP3a */
4598: FDC_Buffer_Add ( 0x4e );
4599: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3b ; i++ ) /* GAP3b */
4600: FDC_Buffer_Add ( 0x00 );
4601:
4602: /* Add the data for the sector + build the CRC */
4603: crc16_reset ( &CRC );
4604: for ( i=0 ; i<3 ; i++ )
4605: {
4606: FDC_Buffer_Add ( 0xa1 ); /* SYNC (write $F5) */
4607: crc16_add_byte ( &CRC , 0xa1 );
4608: }
4609:
4610: FDC_Buffer_Add ( 0xfb ); /* Data Address Mark */
4611: crc16_add_byte ( &CRC , 0xfb );
4612:
4613: if ( Floppy_ReadSectors ( Drive, &pSectorData, Sector, Track, Side, 1, NULL, &SectorSize ) )
4614: {
4615: for ( i=0 ; i<SectorSize ; i++ )
4616: {
4617: FDC_Buffer_Add ( pSectorData[ i ] );
4618: crc16_add_byte ( &CRC , pSectorData[ i ] );
4619: }
4620: }
4621: else
4622: {
4623: /* In case of error, we put some 0x00 bytes, but this case should */
4624: /* not happen with ST/MSA disk images, all sectors should be present on each track */
4625: for ( i=0 ; i<512 ; i++ )
4626: {
4627: FDC_Buffer_Add ( 0x00 );
4628: crc16_add_byte ( &CRC , 0x00 );
4629: }
4630: }
4631:
4632: FDC_Buffer_Add ( CRC >> 8 ); /* CRC1 (write $F7) */
4633: FDC_Buffer_Add ( CRC & 0xff ); /* CRC2 */
4634:
4635: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP4 ; i++ ) /* GAP4 */
4636: FDC_Buffer_Add ( 0x4e );
1.1.1.6 root 4637: }
1.1.1.18 root 4638:
4639: while ( FDC_Buffer_Get_Size () < FDC_GetBytesPerTrack ( Drive ) ) /* Complete the track buffer */
4640: FDC_Buffer_Add ( 0x4e ); /* GAP5 */
4641:
4642: return 0; /* No error */
4643: }
4644:
4645:
4646: /*-----------------------------------------------------------------------*/
4647: /**
4648: * Write a track to a floppy image in ST format (used in type III command)
4649: * Bytes were added to the FDC Buffer with a default timing (32 microsec) ;
4650: * we copy only the bytes into a temporary buffer, and write this buffer
4651: * to the floppy image.
4652: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
4653: * and some '0' bytes will be written to the disk.
4654: * Return 0 if track was written without error, or FDC_STR_BIT_LOST_DATA if an error occurred
4655: *
4656: * TODO : this is not supported for ST/MSA at the moment, so we return FDC_STR_BIT_LOST_DATA
4657: */
4658: static Uint8 FDC_WriteTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side , int TrackSize )
4659: {
4660: int FrameCycles, HblCounterVideo, LineCycles;
4661:
4662: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4663:
4664: LOG_TRACE(TRACE_FDC, "fdc write track not supported addr=0x%x drive=%d track=%d side=%d VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4665: FDC_GetDMAAddress(), Drive, Track, Side,
4666: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4667:
4668: Log_Printf(LOG_TODO, "FDC type III command 'write track' is not supported with ST/MSA files\n");
4669:
4670: /* TODO : "Write track" should write all the sectors after extracting them from the track data ? */
1.1 root 4671:
1.1.1.6 root 4672: /* Failed */
1.1.1.18 root 4673: LOG_TRACE(TRACE_FDC, "fdc write track failed\n" );
4674: return FDC_STR_BIT_LOST_DATA;
1.1 root 4675: }
4676:
4677:
1.1.1.2 root 4678: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4679: /**
4680: * Write to floppy mode/control (?) register (0xff860F).
4681: * Used on Falcon only!
4682: * FIXME: I've found hardly any documentation about this register, only
4683: * the following description of the bits:
4684: *
4685: * __________54__10 Floppy Controll-Register
4686: * || ||
4687: * || |+- Prescaler 1
4688: * || +-- Media detect 1
4689: * |+----- Prescaler 2
4690: * +------ Media detect 2
4691: *
4692: * For DD - disks: 0x00
4693: * For HD - disks: 0x03
4694: * for ED - disks: 0x30 (not supported by TOS)
4695: */
1.1.1.15 root 4696: void FDC_FloppyMode_WriteByte ( void )
1.1.1.9 root 4697: {
4698: // printf("Write to floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4699: }
4700:
4701:
4702: /*-----------------------------------------------------------------------*/
4703: /**
4704: * Read from floppy mode/control (?) register (0xff860F).
4705: * Used on Falcon only!
4706: * FIXME: I've found hardly any documentation about this register, only
4707: * the following description of the bits:
4708: *
4709: * ________76543210 Floppy Controll-Register
4710: * ||||||||
4711: * |||||||+- Prescaler 1
4712: * ||||||+-- Mode select 1
4713: * |||||+--- Media detect 1
4714: * ||||+---- accessed during DMA transfers (?)
4715: * |||+----- Prescaler 2
4716: * ||+------ Mode select 2
4717: * |+------- Media detect 2
4718: * +-------- Disk changed
4719: */
1.1.1.15 root 4720: void FDC_FloppyMode_ReadByte ( void )
1.1.1.9 root 4721: {
4722: IoMem_WriteByte(0xff860f, 0x80); // FIXME: Is this ok?
4723: // printf("Read from floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4724: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.