|
|
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.22! root 972: int FDC_DMA_GetMode(void)
! 973: {
! 974: return FDC_DMA.Mode;
! 975: }
! 976:
1.1.1.2 root 977:
978: /*-----------------------------------------------------------------------*/
1.1.1.9 root 979: /**
1.1.1.18 root 980: * Add a byte to the DMA's FIFO buffer (read from disk).
981: * If the buffer is full and DMA is ON, write the FIFO's 16 bytes to DMA's address.
1.1.1.15 root 982: *
1.1.1.18 root 983: * NOTE [NP] : the DMA is connected to the FDC, each time a DRQ is made by the FDC,
984: * it's handled by the DMA and stored in the DMA's 16 bytes buffer. This means
1.1.1.15 root 985: * FDC_STR_BIT_LOST_DATA will never be set (but data can be lost if FDC_DMA.SectorCount==0)
1.1.1.18 root 986: *
987: * NOTE [NP] : as seen on a real STF, the unused bits when reading DMA Status at $ff8606
988: * are also changed by the DMA operations (this might not be complete, but seems quite reproducible) :
989: * - reading a byte from the FDC to the DMA will change unused bits in the lowest byte at $ff8604
990: * - transferring the 16 byte DMA buffer to RAM will change the unused bits in the 2 bytes at $ff8604
991: *
992: * In all cases, the byte read from the FDC is transferred to the DMA, even if DMA sector count is 0, so
993: * we must always update lowest byte of ff8604_recent_val.
994: * DMA FIFO is transferred only when DMA sector count is >0, so high byte of ff8604_recent_val will be
995: * updated only in that case.
1.1.1.14 root 996: */
1.1.1.18 root 997: void FDC_DMA_FIFO_Push ( Uint8 Byte )
1.1.1.14 root 998: {
1.1.1.15 root 999: Uint32 Address;
1.1.1.14 root 1000:
1.1.1.18 root 1001: //fprintf ( stderr , "dma push pos=%d byte=%x %lld\n" , FDC_DMA.FIFO_Size , Byte , CyclesGlobalClockCounter );
1002:
1003: /* Store the byte that was just read from FDC's Data Register */
1004: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | Byte;
1.1.1.14 root 1005:
1.1.1.15 root 1006: if ( FDC_DMA.SectorCount == 0 )
1007: {
1008: //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 1009: FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
1010: return;
1.1.1.15 root 1011: }
1012:
1.1.1.18 root 1013: FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
1014:
1015: FDC_DMA.FIFO [ FDC_DMA.FIFO_Size++ ] = Byte;
1016:
1017: if ( FDC_DMA.FIFO_Size < FDC_DMA_FIFO_SIZE ) /* FIFO is not full yet */
1018: return;
1019:
1020: /* FIFO full : transfer data from FIFO to RAM and update DMA address */
1.1.1.15 root 1021: Address = FDC_GetDMAAddress();
1.1.1.18 root 1022: STMemory_SafeCopy ( Address , FDC_DMA.FIFO , FDC_DMA_FIFO_SIZE , "FDC DMA push to fifo" );
1023: FDC_WriteDMAAddress ( Address + FDC_DMA_FIFO_SIZE );
1024: FDC_DMA.FIFO_Size = 0; /* FIFO is now empty again */
1025:
1026: /* Store the last word that was just transferred by the DMA */
1027: 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 1028:
1029: /* Update Sector Count */
1.1.1.18 root 1030: FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
1.1.1.15 root 1031: if ( FDC_DMA.BytesInSector <= 0 )
1032: {
1033: FDC_DMA.SectorCount--;
1.1.1.18 root 1034: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
1.1.1.15 root 1035: }
1.1 root 1036: }
1037:
1.1.1.2 root 1038:
1039: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1040: /**
1.1.1.18 root 1041: * Get a byte from the DMA's FIFO buffer (write to disk).
1042: * If the buffer is empty and DMA is ON, load 16 bytes in the FIFO from DMA's address.
1.1.1.15 root 1043: *
1.1.1.18 root 1044: * NOTE [NP] : on a real ST, there're two 16 byte DMA FIFO, this allows to refill one FIFO
1045: * while the other FIFO is used to transfer data to the FDC. We don't emulate this at the
1046: * moment, as it doesn't cause any problem (when a DMA is set to write mode, we would need
1047: * to prefill 32 bytes instead of 16 bytes as we do now)
1.1.1.15 root 1048: *
1.1.1.18 root 1049: * NOTE [NP] : as with FDC_DMA_FIFO_Push, this also changes the unused bits at $ff8606
1.1.1.9 root 1050: */
1.1.1.18 root 1051: Uint8 FDC_DMA_FIFO_Pull ( void )
1.1 root 1052: {
1.1.1.15 root 1053: Uint32 Address;
1.1.1.18 root 1054: Uint8 Byte;
1.1.1.15 root 1055:
1.1.1.18 root 1056: //fprintf ( stderr , "fifo pull %d %d %d\n" , FDC_DMA.BytesToTransfer , FDC_DMA.BytesInSector , FDC_DMA.SectorCount );
1.1.1.15 root 1057: if ( FDC_DMA.SectorCount == 0 )
1058: {
1059: //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 1060: FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
1061: return 0; /* Write a '0' byte when dma is off */
1.1.1.15 root 1062: }
1.1 root 1063:
1.1.1.18 root 1064: FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
1.1 root 1065:
1.1.1.18 root 1066: if ( FDC_DMA.FIFO_Size > 0 ) /* FIFO is not empty yet */
1067: Byte = FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE - ( FDC_DMA.FIFO_Size-- ) ]; /* return byte at pos 0, 1, .., 15 */
1068:
1069: else
1.1.1.6 root 1070: {
1.1.1.18 root 1071: /* FIFO empty : transfer data from RAM to FIFO and update DMA address */
1072: Address = FDC_GetDMAAddress();
1073: memcpy ( FDC_DMA.FIFO , &STRam[ Address ] , FDC_DMA_FIFO_SIZE );/* TODO : check we read from a valid RAM location ? */
1074: FDC_WriteDMAAddress ( Address + FDC_DMA_FIFO_SIZE );
1075: FDC_DMA.FIFO_Size = FDC_DMA_FIFO_SIZE - 1; /* FIFO is now full again (minus the byte we will return below) */
1076:
1077: /* Store the last word that was just transferred by the DMA */
1078: FDC_DMA.ff8604_recent_val = ( FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-2 ] << 8 ) | FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-1 ];
1079:
1080: /* Update Sector Count */
1081: FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
1082: if ( FDC_DMA.BytesInSector < 0 )
1083: {
1084: FDC_DMA.SectorCount--;
1085: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
1086: }
1087:
1.1.1.20 root 1088: Byte = FDC_DMA.FIFO [ 0 ]; /* return the 1st byte we just transferred in the FIFO */
1.1.1.6 root 1089: }
1090:
1.1.1.18 root 1091: /* Store the byte that will be written to FDC's Data Register */
1092: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | Byte;
1093:
1094: return Byte;
1.1 root 1095: }
1096:
1.1.1.2 root 1097:
1098: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1099: /**
1.1.1.21 root 1100: * Return the value of FDC_DMA.SectorCount (used in floppy_ipf.c)
1101: */
1102: int FDC_DMA_GetSectorCount ( void )
1103: {
1104: return FDC_DMA.SectorCount;
1105: }
1106:
1107:
1108: /*-----------------------------------------------------------------------*/
1109: /**
1.1.1.18 root 1110: * Reset the buffer used to transfer data between the FDC and the DMA
1.1.1.16 root 1111: */
1.1.1.18 root 1112: void FDC_Buffer_Reset ( void )
1.1.1.16 root 1113: {
1.1.1.18 root 1114: FDC_BUFFER.Size = 0;
1115: FDC_BUFFER.PosRead = 0;
1.1.1.16 root 1116: }
1117:
1118:
1119: /*-----------------------------------------------------------------------*/
1120: /**
1.1.1.18 root 1121: * Add a byte to the FDC transfer buffer, using a specific timing
1.1.1.9 root 1122: */
1.1.1.18 root 1123: void FDC_Buffer_Add_Timing ( Uint8 Byte , Uint16 Timing )
1.1 root 1124: {
1.1.1.18 root 1125: FDC_BUFFER.Data[ FDC_BUFFER.Size ].Byte = Byte;
1126: FDC_BUFFER.Data[ FDC_BUFFER.Size ].Timing = Timing;
1127: FDC_BUFFER.Size++;
1.1 root 1128: }
1129:
1.1.1.2 root 1130:
1131: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1132: /**
1.1.1.18 root 1133: * Add a byte to the FDC transfer buffer, using a default timing
1.1.1.9 root 1134: */
1.1.1.18 root 1135: void FDC_Buffer_Add ( Uint8 Byte )
1.1 root 1136: {
1.1.1.18 root 1137: FDC_Buffer_Add_Timing ( Byte , FDC_TransferByte_FdcCycles ( 1 ) );
1.1 root 1138: }
1139:
1.1.1.2 root 1140:
1.1.1.17 root 1141: /*-----------------------------------------------------------------------*/
1142: /**
1.1.1.18 root 1143: * Return the timing needed to transfer the Byte at the current position
1.1.1.17 root 1144: */
1.1.1.18 root 1145: Uint16 FDC_Buffer_Read_Timing ( void )
1.1.1.17 root 1146: {
1.1.1.18 root 1147: //fprintf ( stderr , "read timing %d %x\n" , FDC_BUFFER.PosRead , FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Timing );
1148: return FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Timing;
1.1.1.17 root 1149: }
1150:
1151:
1152: /*-----------------------------------------------------------------------*/
1153: /**
1.1.1.18 root 1154: * Return the Byte at the current position and increment position
1.1.1.17 root 1155: */
1.1.1.18 root 1156: Uint8 FDC_Buffer_Read_Byte ( void )
1.1.1.17 root 1157: {
1.1.1.18 root 1158: //fprintf ( stderr , "read byte %d %x\n" , FDC_BUFFER.PosRead , FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Byte );
1159: return FDC_BUFFER.Data[ FDC_BUFFER.PosRead++ ].Byte;
1.1.1.17 root 1160: }
1161:
1162:
1163: /*-----------------------------------------------------------------------*/
1164: /**
1.1.1.18 root 1165: * Return the Byte at a given position
1.1.1.17 root 1166: */
1.1.1.18 root 1167: Uint8 FDC_Buffer_Read_Byte_pos ( int pos )
1.1.1.17 root 1168: {
1.1.1.18 root 1169: //fprintf ( stderr , "read byte pos %d %x\n" , pos , FDC_BUFFER.Data[ pos ].Byte );
1170: return FDC_BUFFER.Data[ pos ].Byte;
1.1.1.17 root 1171: }
1172:
1173:
1174: /*-----------------------------------------------------------------------*/
1175: /**
1.1.1.18 root 1176: * Return the number of bytes stored in FDC_BUFFER
1.1.1.17 root 1177: */
1.1.1.18 root 1178: int FDC_Buffer_Get_Size ( void )
1.1.1.17 root 1179: {
1.1.1.18 root 1180: return FDC_BUFFER.Size;
1.1.1.17 root 1181: }
1182:
1183:
1184: /*-----------------------------------------------------------------------*/
1185: /**
1.1.1.18 root 1186: * Return the mode to handle a read/write in $ff86xx
1187: * Depending on the images inserted in each floppy drive and on the
1188: * selected drive, we must choose which fdc emulation should be used.
1189: * Possible emulation methods are 'internal' or 'ipf'.
1190: * To avoid mixing emulation methods on both drives when possible (which
1191: * could lead to inconstancies when precise timings are required), we also
1192: * use the IPF mode for an empty drive if the other drive contains an IPF
1193: * image.
1194: * If no drive is selected, we must use the previous mode (before drives were
1195: * unselected), not the internal one : in case some commands are sent when
1196: * drives are deselected and the drive was in IPF mode, we must send
1197: * the command to IPF to ensure no command are lost if the drive is selected again
1198: * (eg : D0 command in "Saint Dragon" IPF)
1.1.1.17 root 1199: */
1.1.1.18 root 1200: static int FDC_GetEmulationMode ( void )
1.1.1.17 root 1201: {
1.1.1.18 root 1202: int Mode;
1.1.1.17 root 1203:
1.1.1.18 root 1204: Mode = FDC.EmulationMode; /* Default to previous mode if no drive is selected */
1.1.1.17 root 1205:
1.1.1.18 root 1206: /* Check drive 1 first */
1207: if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x04 ) == 0 )
1.1.1.17 root 1208: {
1.1.1.18 root 1209: if ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_IPF )
1210: Mode = FDC_EMULATION_MODE_IPF;
1211: else if ( ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_NONE ) /* Drive 1 is empty */
1212: && ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_IPF ) ) /* Drive 0 is an IPF image */
1213: Mode = FDC_EMULATION_MODE_IPF; /* Use IPF for drive 1 too */
1.1.1.17 root 1214: else
1.1.1.18 root 1215: Mode = FDC_EMULATION_MODE_INTERNAL;
1.1.1.17 root 1216: }
1217:
1.1.1.18 root 1218: /* If both drive 0 and drive 1 are enabled, we keep only drive 0 to choose emulation's mode */
1219: if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x02 ) == 0 )
1.1.1.17 root 1220: {
1.1.1.18 root 1221: if ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_IPF )
1222: Mode = FDC_EMULATION_MODE_IPF;
1223: else if ( ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_NONE ) /* Drive 0 is empty */
1224: && ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_IPF ) ) /* Drive 1 is an IPF image */
1225: Mode = FDC_EMULATION_MODE_IPF; /* Use IPF for drive 0 too */
1226: else
1227: Mode = FDC_EMULATION_MODE_INTERNAL;
1.1.1.17 root 1228: }
1229:
1.1.1.18 root 1230: FDC.EmulationMode = Mode;
1231: //fprintf ( stderr , "emul mode %x %d\n" , PSGRegisters[PSG_REG_IO_PORTA] & 0x06 , Mode );
1232: // return FDC_EMULATION_MODE_INTERNAL;
1233: // return FDC_EMULATION_MODE_IPF;
1234: return Mode;
1.1.1.17 root 1235: }
1236:
1237:
1.1.1.2 root 1238: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1239: /**
1.1.1.18 root 1240: * Update the FDC's internal variables on a regular basis.
1241: * To get correct accuracy, this should be called every 200-500 FDC cycles
1242: * So far, we only need to update the index position for the valid
1243: * drive/floppy ; updating every 500 cycles is enough for this case.
1.1.1.9 root 1244: */
1.1.1.19 root 1245: static void FDC_UpdateAll(void)
1.1.1.8 root 1246: {
1.1.1.18 root 1247: FDC_IndexPulse_Update ();
1.1.1.8 root 1248: }
1249:
1250:
1251: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1252: /**
1.1.1.18 root 1253: * This function is used to enable/disable a drive when
1254: * using the UI or command line parameters
1.1.1.9 root 1255: */
1.1.1.18 root 1256: void FDC_Drive_Set_Enable ( int Drive , bool value )
1.1 root 1257: {
1.1.1.19 root 1258: LOG_TRACE ( TRACE_FDC , "fdc enable drive=%d %s\n" , Drive , value?"on":"off" );
1.1.1.17 root 1259:
1.1.1.18 root 1260: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1261: FDC_DRIVES[ Drive ].Enabled = value;
1.1.1.6 root 1262:
1.1.1.18 root 1263: /* Also forward change to IPF emulation */
1264: IPF_Drive_Set_Enable ( Drive , value );
1265: }
1.1.1.6 root 1266:
1.1.1.17 root 1267:
1.1.1.18 root 1268: /*-----------------------------------------------------------------------*/
1269: /**
1270: * This function is used to choose single sided or double sided for a drive
1271: * when using the UI or command line parameters
1272: */
1273: void FDC_Drive_Set_NumberOfHeads ( int Drive , int NbrHeads )
1274: {
1.1.1.19 root 1275: LOG_TRACE ( TRACE_FDC , "fdc set nbr heads drive=%d %d\n" , Drive , NbrHeads );
1.1.1.17 root 1276:
1.1.1.18 root 1277: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1278: FDC_DRIVES[ Drive ].NumberOfHeads = NbrHeads;
1.1.1.6 root 1279:
1.1.1.18 root 1280: /* Also forward change to IPF emulation */
1281: IPF_Drive_Set_DoubleSided ( Drive , NbrHeads==2 ? true : false );
1282: }
1.1.1.11 root 1283:
1.1.1.15 root 1284:
1.1.1.18 root 1285: /*-----------------------------------------------------------------------*/
1286: /**
1287: * This function is called when a floppy is inserted in a drive
1288: * using the UI or command line parameters
1289: */
1290: void FDC_InsertFloppy ( int Drive )
1291: {
1292: LOG_TRACE ( TRACE_FDC , "fdc insert drive=%d\n" , Drive );
1.1.1.15 root 1293:
1.1.1.18 root 1294: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1295: {
1296: FDC_DRIVES[ Drive ].DiskInserted = true;
1297: if ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) != 0 ) /* If we insert a floppy while motor is already on, we must */
1298: FDC_IndexPulse_Init ( Drive ); /* init the index pulse's position */
1299: else
1300: FDC_DRIVES[ Drive ].IndexPulse_Time = 0; /* Index pulse's position not known yet */
1301: FDC_DRIVES[ Drive ].Density = FDC_GetDensity ( Drive );
1302: }
1303: }
1304:
1305:
1306: /*-----------------------------------------------------------------------*/
1307: /**
1308: * This function is called when a floppy is ejected from a drive
1309: * using the UI or command line parameters
1310: */
1311: void FDC_EjectFloppy ( int Drive )
1312: {
1313: LOG_TRACE ( TRACE_FDC , "fdc eject drive=%d\n" , Drive );
1314:
1315: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1316: {
1317: FDC_DRIVES[ Drive ].DiskInserted = false;
1318: FDC_DRIVES[ Drive ].IndexPulse_Time = 0; /* Stop counting index pulses on an empty drive */
1319: }
1320: }
1321:
1322:
1323: /*-----------------------------------------------------------------------*/
1324: /**
1325: * Handle a write in the IO_PORTA register $E through $ff8802. Only bits
1326: * 0-2 are available here, others are masked to 0.
1327: * bit 0 : side select
1328: * bit 1-2 : drive select
1329: *
1330: * - For internal FDC emulation, we init index pulse if the active drive
1331: * changed
1332: * - We also forward the change to IPF emulation, as it doesn't have direct access
1333: * to this IO_PORTA register.
1334: *
1335: * If both drives are selected, we keep only drive 0
1336: */
1337: void FDC_SetDriveSide ( Uint8 io_porta_old , Uint8 io_porta_new )
1338: {
1339: int Side;
1340: int Drive;
1341:
1342: if ( io_porta_old == io_porta_new )
1343: return; /* No change */
1344:
1345: Side = ( (~io_porta_new) & 0x01 ); /* Side 0 or 1 */
1346:
1347: Drive = -1; /* By default, don't select any drive */
1348:
1349: /* Check drive 1 first */
1350: if ( ( io_porta_new & 0x04 ) == 0 )
1351: Drive = 1; /* Select drive 1 */
1352:
1353: /* If both drive 0 and drive 1 are enabled, we keep only drive 0 as newdrive */
1354: if ( ( io_porta_new & 0x02 ) == 0 )
1355: Drive = 0; /* Select drive 0 (and un-select drive 1 if set above) */
1356:
1357: 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" ,
1358: io_porta_old , io_porta_new , FDC.SideSignal , Side , FDC.DriveSelSignal , Drive , nVBLs , nHBL );
1359:
1360: if ( FDC.DriveSelSignal != Drive )
1361: {
1362: if ( FDC.DriveSelSignal >= 0 ) /* A drive was previously enabled */
1363: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time = 0; /* Stop counting index pulses on the previous drive */
1364:
1365: if ( Drive >= 0 ) /* A new drive is enabled */
1366: {
1367: if ( ( FDC_DRIVES[ Drive ].DiskInserted ) /* If there's a disk in the new drive and motor is already */
1368: && ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) != 0 ) ) /* on, we must init the index pulse's position */
1369: FDC_IndexPulse_Init ( Drive );
1370: else
1371: FDC_DRIVES[ Drive ].IndexPulse_Time = 0; /* Index pulse's position not known yet */
1.1.1.6 root 1372: }
1.1.1.15 root 1373: }
1.1.1.6 root 1374:
1.1.1.18 root 1375: FDC.SideSignal = Side;
1376: FDC.DriveSelSignal = Drive;
1377:
1378:
1379: /* Also forward change to IPF emulation */
1380: IPF_SetDriveSide ( io_porta_old , io_porta_new );
1381: }
1382:
1383:
1384: /*-----------------------------------------------------------------------*/
1385: /**
1386: * Return the number of sectors for track/side for the current floppy in a drive
1387: * TODO [NP] : this function calls Floppy_FindDiskDetails which handles only ST/MSA
1388: * disk images so far, so this implies all tracks have in fact the same number
1389: * of sectors (we don't use Track and Side for now)
1390: * Drive should be a valid drive (0 or 1)
1391: */
1392: static int FDC_GetSectorsPerTrack ( int Drive , int Track , int Side )
1393: {
1394: Uint16 SectorsPerTrack;
1395:
1396: if (EmulationDrives[ Drive ].bDiskInserted)
1397: {
1398: Floppy_FindDiskDetails ( EmulationDrives[ Drive ].pBuffer , EmulationDrives[ Drive ].nImageBytes , &SectorsPerTrack , NULL );
1399: return SectorsPerTrack;
1400: }
1401: else
1402: return 0;
1403: }
1404:
1405:
1406: /*-----------------------------------------------------------------------*/
1407: /**
1408: * Return the number of sides for a track for the current floppy in a drive
1409: * Drive should be a valid drive (0 or 1)
1410: */
1411: static int FDC_GetSidesPerDisk ( int Drive , int Track )
1412: {
1413: Uint16 SidesPerDisk;
1414:
1415: if (EmulationDrives[ Drive ].bDiskInserted)
1416: {
1417: Floppy_FindDiskDetails ( EmulationDrives[ Drive ].pBuffer , EmulationDrives[ Drive ].nImageBytes , NULL , &SidesPerDisk );
1418: return SidesPerDisk; /* 1 or 2 */
1419: }
1420: else
1421: return 0;
1422: }
1423:
1424:
1425: /*-----------------------------------------------------------------------*/
1426: /**
1.1.1.19 root 1427: * Return the number of tracks for the current floppy in a drive
1428: * For ST/MSA, this assumes both sides have the same number of tracks
1429: * Drive should be a valid drive (0 or 1)
1430: */
1431: static int FDC_GetTracksPerDisk ( int Drive )
1432: {
1433: Uint16 SectorsPerTrack;
1434: Uint16 SidesPerDisk;
1435:
1436: if (EmulationDrives[ Drive ].bDiskInserted)
1437: {
1438: Floppy_FindDiskDetails ( EmulationDrives[ Drive ].pBuffer , EmulationDrives[ Drive ].nImageBytes , &SectorsPerTrack , &SidesPerDisk );
1439: return ( ( EmulationDrives[Drive].nImageBytes / NUMBYTESPERSECTOR ) / SectorsPerTrack ) / SidesPerDisk;
1440: }
1441: else
1442: return 0;
1443: }
1444:
1445:
1446: /*-----------------------------------------------------------------------*/
1447: /**
1.1.1.18 root 1448: * Return a density factor for the current floppy in a drive
1449: * A DD track is usually 9 or 10 sectors and has a x1 factor,
1450: * but to handle HD or ED ST/MSA disk images, we check if we
1451: * have more than 18 or 36 sectors.
1452: * In that case, we use a x2 or x4 factor for theses disks,
1453: * to simulate more bytes per FDC cycles.
1454: * Drive should be a valid drive (0 or 1)
1455: */
1456: static int FDC_GetDensity ( int Drive )
1457: {
1458: Uint16 SectorsPerTrack;
1459:
1460: if ( EmulationDrives[ Drive ].bDiskInserted )
1.1.1.15 root 1461: {
1.1.1.18 root 1462: SectorsPerTrack = FDC_GetSectorsPerTrack ( Drive , FDC_DRIVES[ Drive ].HeadTrack , FDC.SideSignal );
1463: if ( SectorsPerTrack >= 36 )
1464: return FDC_DENSITY_FACTOR_ED; /* Simulate a ED disk, 36 sectors or more */
1465: else if ( SectorsPerTrack >= 18 )
1466: return FDC_DENSITY_FACTOR_HD; /* Simulate a HD disk, between 18 and 36 sectors */
1467: else
1468: return FDC_DENSITY_FACTOR_DD; /* Normal DD disk */
1.1.1.6 root 1469: }
1.1.1.18 root 1470: else
1471: return FDC_DENSITY_FACTOR_DD; /* No disk, default to Double Density */
1472: }
1473:
1474:
1475: /*-----------------------------------------------------------------------*/
1476: /**
1477: * Return the number of bytes in a raw track
1478: * For ST/MSA disk images, we consider all the tracks have the same size.
1479: * To simulate HD/ED floppies, we multiply the size by a density factor.
1480: * Drive should be a valid drive (0 or 1)
1481: */
1482: int FDC_GetBytesPerTrack ( int Drive )
1483: {
1484: int TrackSize;
1485:
1486: TrackSize = FDC_TRACK_BYTES_STANDARD; /* For a standard DD disk */
1487: return TrackSize * FDC_DRIVES[ Drive ].Density; /* Take density into account for HD/ED floppies */
1488: }
1489:
1490:
1491: /*-----------------------------------------------------------------------*/
1492: /**
1493: * Get the number of FDC cycles for one revolution of the floppy
1494: * RPM is already multiplied by 1000 to simulate non-integer values
1495: * (for Falcon, we divide cycles by 2 to simulate a FDC freq in the 8 MHz range)
1496: * For STX image, the number of cycles depends on drive/track/side.
1497: * Drive should be a valid drive (0 or 1)
1498: */
1499: static Uint32 FDC_GetCyclesPerRev_FdcCycles ( int Drive )
1500: {
1501: Uint32 FdcCyclesPerRev;
1502:
1.1.1.22! root 1503: assert(Drive == 0 || Drive == 1);
! 1504:
1.1.1.18 root 1505: /* If the inserted disk is an STX, we use the supplied track length to compute cycles per rev */
1.1.1.22! root 1506: if ( EmulationDrives[Drive].ImageType == FLOPPY_IMAGE_TYPE_STX )
1.1.1.18 root 1507: return FDC_GetCyclesPerRev_FdcCycles_STX ( Drive , FDC_DRIVES[ Drive ].HeadTrack , FDC.SideSignal );
1508:
1509: /* Assume a standard length for all tracks for ST/MSA images */
1510: FdcCyclesPerRev = (Uint64)(MachineClocks.FDC_Freq * 1000.L) / ( FDC_DRIVES[ Drive ].RPM / 60 );
1511:
1512: /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
1513: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
1514: /* FIXME : this should be handled better, without involving 8 MHz CPU_Freq */
1.1.1.20 root 1515: if (Config_IsMachineFalcon())
1.1.1.18 root 1516: FdcCyclesPerRev /= 2; /* correct delays for a 8 MHz FDC_Freq clock instead of 16 */
1517:
1518: return FdcCyclesPerRev;
1519: }
1520:
1521:
1522:
1523: /*-----------------------------------------------------------------------*/
1524: /**
1525: * If some valid drive/floppy are available and the motor signal is on,
1526: * update the current angular position for the drive and check if
1527: * a new index pulse was reached. Increase Index Pulse counter in that case.
1528: *
1529: * This function should be called at least every 500 FDC cycles when motor
1530: * is ON to get good accuracy.
1531: *
1532: * [NP] TODO : should we have 2 different Index Pulses for each side or do they
1533: * happen at the same time ?
1534: */
1.1.1.19 root 1535: static void FDC_IndexPulse_Update(void)
1.1.1.18 root 1536: {
1537: Uint32 FdcCyclesPerRev;
1538: int FrameCycles, HblCounterVideo, LineCycles;
1539:
1540: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1541:
1542: //fprintf ( stderr , "update index drive=%d side=%d counter=%d VBL=%d HBL=%d\n" , FDC.DriveSelSignal , FDC.SideSignal , FDC.IndexPulse_Counter , nVBLs , nHBL );
1543:
1544: if ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) == 0 )
1545: return; /* Motor is OFF, nothing to update */
1546:
1547: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
1548: || ( !FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted ) )
1549: return; /* No valid drive/floppy, nothing to update */
1550:
1551: if ( FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time == 0 ) /* No reference Index Pulse for this drive */
1552: FDC_IndexPulse_Init ( FDC.DriveSelSignal ); /* (could be the case after a 'reset') */
1553:
1554: FdcCyclesPerRev = FDC_GetCyclesPerRev_FdcCycles ( FDC.DriveSelSignal );
1555:
1556: if ( CyclesGlobalClockCounter - FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time >= FDC_FdcCyclesToCpuCycles ( FdcCyclesPerRev ) )
1557: {
1558: /* Store new position of the most recent Index Pulse */
1559: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time += FDC_FdcCyclesToCpuCycles ( FdcCyclesPerRev );
1560: FDC.IndexPulse_Counter++;
1561: LOG_TRACE(TRACE_FDC, "fdc update index drive=%d side=%d counter=%d ip_time=%"PRIu64" VBL=%d HBL=%d\n" ,
1562: FDC.DriveSelSignal , FDC.SideSignal , FDC.IndexPulse_Counter ,
1563: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time , nVBLs , nHBL );
1564:
1565: if ( FDC.InterruptCond & FDC_INTERRUPT_COND_IP ) /* Do we have a "force int on index pulse" command running ? */
1566: {
1567: LOG_TRACE(TRACE_FDC, "fdc type IV force int on index, set irq VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1568: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1569: FDC_SetIRQ ( FDC_IRQ_SOURCE_INDEX );
1570: }
1571: }
1572: }
1573:
1574:
1575: /*-----------------------------------------------------------------------*/
1576: /**
1577: * When motor is started, the position of the next index pulse will be random,
1578: * as we don't know how much the floppy rotated when the motor was stopped or
1579: * the floppy was inserted.
1580: * We compute a random position in the "past" (less than one revolution)
1581: * and use it as a reference to detect the next index pulse.
1582: *
1583: */
1584: static void FDC_IndexPulse_Init ( int Drive )
1585: {
1586: Uint32 FdcCyclesPerRev;
1587: Uint64 IndexPulse_Time;
1588:
1589: FdcCyclesPerRev = FDC_GetCyclesPerRev_FdcCycles ( Drive );
1590: IndexPulse_Time = CyclesGlobalClockCounter - rand () % FDC_FdcCyclesToCpuCycles ( FdcCyclesPerRev );
1591: if ( IndexPulse_Time <= 0 ) /* Should not happen (only if FDC_IndexPulse_Init is */
1592: IndexPulse_Time = 1; /* called just after emulation starts) */
1593: FDC_DRIVES[ Drive ].IndexPulse_Time = IndexPulse_Time;
1594:
1595: LOG_TRACE(TRACE_FDC, "fdc init index drive=%d side=%d counter=%d ip_time=%"PRIu64" VBL=%d HBL=%d\n" ,
1596: FDC.DriveSelSignal , FDC.SideSignal , FDC.IndexPulse_Counter ,
1597: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time , nVBLs , nHBL );
1598: }
1599:
1600:
1601: /*-----------------------------------------------------------------------*/
1602: /**
1603: * Return the number of FDC cycles since the previous index pulse signal
1604: * for the current drive.
1605: * If there's no available drive/floppy (i.e. no index), we return -1
1606: */
1607: int FDC_IndexPulse_GetCurrentPos_FdcCycles ( Uint32 *pFdcCyclesPerRev )
1608: {
1609: Uint32 FdcCyclesPerRev;
1610: Uint32 CpuCyclesSinceIndex;
1611:
1612: if ( ( FDC.DriveSelSignal < 0 ) || ( FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time == 0 ) )
1613: return -1;
1614:
1615: FdcCyclesPerRev = FDC_GetCyclesPerRev_FdcCycles ( FDC.DriveSelSignal );
1616: CpuCyclesSinceIndex = CyclesGlobalClockCounter - FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time;
1617:
1618: if ( pFdcCyclesPerRev )
1619: *pFdcCyclesPerRev = FdcCyclesPerRev;
1620:
1621: //fprintf ( stderr , "current pos %d %lld %d %lld\n" , FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time ,
1622: // FdcCyclesPerRev , CyclesGlobalClockCounter - FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time );
1623:
1624: return FDC_CpuCyclesToFdcCycles ( CpuCyclesSinceIndex );
1625: }
1626:
1627:
1628: /*-----------------------------------------------------------------------*/
1629: /**
1630: * Return the current position in the track relative to the index pulse.
1631: * For standard floppy, this is a number of bytes in the range [0,6250[
1632: * If there's no available drive/floppy and no index, we return -1
1633: * To simulate HD/ED floppies, we multiply the number of bytes by a density factor.
1634: */
1635: int FDC_IndexPulse_GetCurrentPos_NbBytes ( void )
1636: {
1637: int FdcCyclesSinceIndex;
1638:
1639: FdcCyclesSinceIndex = FDC_IndexPulse_GetCurrentPos_FdcCycles ( NULL );
1640: if ( FdcCyclesSinceIndex < 0 ) /* No drive/floppy available at the moment */
1641: return -1;
1642: //fprintf ( stderr , "fdc index current pos new=%d\n" , FdcCyclesSinceIndex / FDC_DELAY_CYCLE_MFM_BYTE );
1643:
1644: return FdcCyclesSinceIndex * FDC_DRIVES[ FDC.DriveSelSignal ].Density / FDC_DELAY_CYCLE_MFM_BYTE;
1645: }
1646:
1647:
1648:
1649: /*-----------------------------------------------------------------------*/
1650: /**
1651: * Return the current state of the index pulse signal.
1652: * The signal goes to 1 when reaching the index pulse location and remain
1653: * to 1 during 1.5 ms (approx 46 bytes).
1654: * During the rest of the track, the signal will be 0 (it will also be 0
1655: * if the drive if OFF or empty)
1656: */
1657: int FDC_IndexPulse_GetState ( void )
1658: {
1659: int state;
1660: int FdcCyclesSinceIndex;
1661:
1662: FdcCyclesSinceIndex = FDC_IndexPulse_GetCurrentPos_FdcCycles ( NULL );
1663:
1664: state = 0;
1665: if ( ( FdcCyclesSinceIndex >= 0 ) /* We have a valid drive/floppy */
1666: && ( (Uint32)FdcCyclesSinceIndex < FDC_DelayToFdcCycles ( FDC_DELAY_US_INDEX_PULSE_LENGTH ) ) )
1667: state = 1;
1668:
1669: //fprintf ( stderr , "fdc index state 2 pos pos=%d state=%d\n" , FdcCyclesSinceIndex , state );
1670: return state;
1671: }
1672:
1673:
1674: /*-----------------------------------------------------------------------*/
1675: /**
1676: * Return the number of FDC cycles before reaching the next index pulse signal.
1677: * If there's no available drive/floppy and no index, we return -1
1678: */
1679: int FDC_NextIndexPulse_FdcCycles ( void )
1680: {
1681: Uint32 FdcCyclesPerRev;
1682: int FdcCyclesSinceIndex;
1683: int res;
1684:
1685: FdcCyclesSinceIndex = FDC_IndexPulse_GetCurrentPos_FdcCycles ( &FdcCyclesPerRev );
1686: if ( FdcCyclesSinceIndex < 0 ) /* No drive/floppy available at the moment */
1687: return -1;
1688:
1689: res = FdcCyclesPerRev - FdcCyclesSinceIndex;
1690:
1691: /* If the next IP is in 0 or 1 cycle, we consider this is a rounding error */
1692: /* and we wait for one full revolution (this can happen in Force Int on Index Pulse */
1693: /* when we call FDC_NextIndexPulse_FdcCycles in a loop) */
1694: if ( res <= 1 )
1695: res = FdcCyclesPerRev; // TODO : 0 should be allowed
1696:
1697: //fprintf ( stderr , "fdc next index current pos new=%d\n" , res );
1698: return res;
1699: }
1700:
1701:
1702: /*-----------------------------------------------------------------------*/
1703: /**
1704: * Set the IRQ signal
1705: * This is called either on command completion, or when an index pulse
1706: * is received or when the "force interrupt immediate" command is used.
1707: * This function can also be called from the HDC emulation or from another
1708: * FDC emulation module (IPF for example)
1.1.1.19 root 1709: *
1710: * NOTE : although high/1 on the IRQ pin of the FDC means an interrupt is
1711: * requested, this signal is inverted before going into MFP's GPIP5.
1712: * So, we must set the line to low/0 to request an interrupt.
1.1.1.18 root 1713: */
1714: void FDC_SetIRQ ( Uint8 IRQ_Source )
1715: {
1716: if ( FDC.IRQ_Signal != 0 ) /* Don't set MFP's IRQ again if already set */
1717: {
1718: LOG_TRACE(TRACE_FDC, "fdc set irq, irq 0x%x already set VBL=%d HBL=%d\n" , FDC.IRQ_Signal , nVBLs , nHBL );
1719: }
1720:
1721: else
1722: {
1723: /* Acknowledge in MFP circuit, pass bit, enable, pending */
1.1.1.19 root 1724: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_LOW );
1.1.1.18 root 1725: LOG_TRACE(TRACE_FDC, "fdc set irq 0x%x source 0x%x VBL=%d HBL=%d\n" , FDC.IRQ_Signal , IRQ_Source , nVBLs , nHBL );
1726: }
1727:
1728: /* If IRQ comes from HDC or other sources (IPF), we don't need */
1729: /* to handle the forced IRQ case used in FDC */
1730: if ( IRQ_Source == FDC_IRQ_SOURCE_HDC )
1731: FDC.IRQ_Signal = FDC_IRQ_SOURCE_HDC;
1732:
1733: else if ( IRQ_Source == FDC_IRQ_SOURCE_OTHER )
1734: FDC.IRQ_Signal = FDC_IRQ_SOURCE_OTHER;
1735:
1736: else /* IRQ comes from FDC */
1737: {
1738: FDC.IRQ_Signal &= ~( FDC_IRQ_SOURCE_HDC | FDC_IRQ_SOURCE_OTHER );
1739: FDC.IRQ_Signal |= IRQ_Source;
1740: }
1741: }
1742:
1743:
1744: /*-----------------------------------------------------------------------*/
1745: /**
1746: * Reset the IRQ signal ; in case the source of the interrupt is also
1747: * a "force interrupt immediate" command, the IRQ signal should not be cleared
1748: * (only command 0xD0 or any new command followed by a read of status reg
1749: * can clear the forced IRQ)
1.1.1.19 root 1750: *
1751: * NOTE : although low/0 on the IRQ pin of the FDC means interrupt is
1752: * cleared, this signal is inverted before going into MFP's GPIP5.
1753: * So, we must set the line to high/1 to clear interrupt request.
1.1.1.18 root 1754: */
1755: void FDC_ClearIRQ ( void )
1756: {
1757: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED ) == 0 )
1758: {
1759: FDC.IRQ_Signal = 0;
1.1.1.19 root 1760: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_HIGH );
1.1.1.18 root 1761: LOG_TRACE(TRACE_FDC, "fdc clear irq VBL=%d HBL=%d\n" , nVBLs , nHBL );
1762: }
1763:
1764: else
1765: {
1766: FDC.IRQ_Signal &= FDC_IRQ_SOURCE_FORCED; /* Clear all sources except 'forced irq' and keep IRQ set in MFP */
1767: LOG_TRACE(TRACE_FDC, "fdc clear irq not done, irq forced VBL=%d HBL=%d\n" , nVBLs , nHBL );
1768: }
1769: }
1770:
1.1.1.19 root 1771: void FDC_ClearHdcIRQ(void)
1772: {
1773: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_HDC;
1774: if (FDC.IRQ_Signal == 0)
1775: {
1776: MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_HIGH );
1777: }
1778: }
1.1.1.18 root 1779:
1780: /*-----------------------------------------------------------------------*/
1781: /**
1782: * Handle the current FDC command.
1783: * We use a timer to go from one state to another to emulate the different
1784: * phases of an FDC command.
1785: * When the command completes (success or failure), FDC.Command will be
1786: * set to FDCEMU_CMD_NULL. Until then, this function will be called to
1787: * handle each state of the command and the corresponding delay in micro
1788: * seconds.
1789: * This handler is called after a first delay corresponding to the prepare
1790: * delay and the eventual motor on delay.
1791: * Once we reach this point, the current command can not be replaced by
1792: * another command (except 'Force Interrupt')
1793: */
1794: void FDC_InterruptHandler_Update ( void )
1795: {
1796: int FdcCycles = 0;
1797: int PendingCyclesOver;
1798:
1799: /* Number of internal cycles we went over for this timer ( <= 0 ) */
1800: /* Used to restart the next timer and keep a constant rate (important for DMA transfers) */
1801: PendingCyclesOver = -PendingInterruptCount; /* >= 0 */
1802:
1803: //fprintf ( stderr , "fdc int handler %lld delay %d\n" , CyclesGlobalClockCounter, PendingCyclesOver );
1804:
1805: CycInt_AcknowledgeInterrupt();
1806:
1807: do /* We loop as long as FdcCycles == 0 (immediate change of state) */
1808: {
1809: /* Update FDC's internal variables */
1810: FDC_UpdateAll ();
1811:
1812: /* Is FDC active? */
1813: if (FDC.Command!=FDCEMU_CMD_NULL)
1814: {
1815: /* Which command are we running ? */
1816: switch(FDC.Command)
1817: {
1818: case FDCEMU_CMD_RESTORE:
1819: FdcCycles = FDC_UpdateRestoreCmd();
1820: break;
1821: case FDCEMU_CMD_SEEK:
1822: FdcCycles = FDC_UpdateSeekCmd();
1823: break;
1824: case FDCEMU_CMD_STEP:
1825: FdcCycles = FDC_UpdateStepCmd();
1826: break;
1827:
1828: case FDCEMU_CMD_READSECTORS:
1829: FdcCycles = FDC_UpdateReadSectorsCmd();
1830: break;
1831: case FDCEMU_CMD_WRITESECTORS:
1832: FdcCycles = FDC_UpdateWriteSectorsCmd();
1833: break;
1834:
1835: case FDCEMU_CMD_READADDRESS:
1836: FdcCycles = FDC_UpdateReadAddressCmd();
1837: break;
1838:
1839: case FDCEMU_CMD_READTRACK:
1840: FdcCycles = FDC_UpdateReadTrackCmd();
1841: break;
1842:
1843: case FDCEMU_CMD_WRITETRACK:
1844: FdcCycles = FDC_UpdateWriteTrackCmd();
1845: break;
1846:
1847: case FDCEMU_CMD_MOTOR_STOP:
1848: FdcCycles = FDC_UpdateMotorStop();
1849: break;
1850: }
1851: }
1852: }
1853: while ( ( FDC.Command != FDCEMU_CMD_NULL ) && ( FdcCycles == 0 ) );
1854:
1855: if ( FDC.Command != FDCEMU_CMD_NULL )
1856: {
1857: FDC_StartTimer_FdcCycles ( FdcCycles , -PendingCyclesOver );
1858: }
1859: }
1860:
1861:
1862: /*-----------------------------------------------------------------------*/
1863: /**
1864: * Return the type of a command, based on the upper bits of CR
1865: */
1866: Uint8 FDC_GetCmdType ( Uint8 CR )
1867: {
1868: if ( ( CR & 0x80 ) == 0 ) /* Type I - Restore, Seek, Step, Step-In, Step-Out */
1869: return 1;
1870: else if ( ( CR & 0x40 ) == 0 ) /* Type II - Read Sector, Write Sector */
1871: return 2;
1872: else if ( ( CR & 0xf0 ) != 0xd0 ) /* Type III - Read Address, Read Track, Write Track */
1873: return 3;
1874: else /* Type IV - Force Interrupt */
1875: return 4;
1.1.1.15 root 1876: }
1877:
1878:
1879: /*-----------------------------------------------------------------------*/
1880: /**
1881: * Update the FDC's Status Register.
1882: * All bits in DisableBits are cleared in STR, then all bits in EnableBits
1883: * are set in STR.
1884: */
1885: static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits )
1886: {
1887: FDC.STR &= (~DisableBits); /* Clear bits in DisableBits */
1888: FDC.STR |= EnableBits; /* Set bits in EnableBits */
1.1.1.17 root 1889:
1.1.1.18 root 1890: FDC_Drive_Set_BusyLed ( FDC.STR );
1.1.1.15 root 1891: //fprintf ( stderr , "fdc str 0x%x\n" , FDC.STR );
1892: }
1893:
1894:
1895: /*-----------------------------------------------------------------------*/
1896: /**
1897: * Common to all commands once they're completed :
1898: * - remove busy bit
1.1.1.18 root 1899: * - set interrupt if necessary
1.1.1.15 root 1900: * - stop motor after 2 sec
1901: */
1902: static int FDC_CmdCompleteCommon ( bool DoInt )
1903: {
1904: int FrameCycles, HblCounterVideo, LineCycles;
1905:
1906: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1907: LOG_TRACE(TRACE_FDC, "fdc complete command VBL=%d video_cyc=%d %d@%d pc=%x\n",
1908: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1909:
1910: FDC_Update_STR ( FDC_STR_BIT_BUSY , 0 ); /* Remove busy bit */
1.1.1.9 root 1911:
1.1.1.15 root 1912: if ( DoInt )
1.1.1.18 root 1913: FDC_SetIRQ ( FDC_IRQ_SOURCE_COMPLETE );
1.1.1.15 root 1914:
1915: FDC.Command = FDCEMU_CMD_MOTOR_STOP; /* Fake command to stop the motor */
1.1.1.18 root 1916: FDC.CommandState = FDCEMU_RUN_MOTOR_STOP;
1917: return FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 1918: }
1919:
1920:
1921: /*-----------------------------------------------------------------------*/
1922: /**
1923: * Verify track after a type I command.
1924: * The FDC will read the first ID field of the current track and will
1925: * compare the track number in this ID field with the current Track Register.
1.1.1.18 root 1926: * If they don't match, we try again with the next ID field until we
1927: * reach 5 revolutions, in which case we set RNF.
1928: *
1929: * NOTE [NP] : in the case of Hatari when using ST/MSA images, the track is always the correct one,
1.1.1.15 root 1930: * so the verify will always be good (except if no disk is inserted or the physical head is
1931: * not on the same track as FDC.TR)
1.1.1.21 root 1932: * For STX images, verify track might fail on purpose with some protection
1.1.1.15 root 1933: */
1.1.1.18 root 1934: static bool FDC_VerifyTrack ( void )
1.1.1.15 root 1935: {
1936: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.21 root 1937: Uint8 Next_TR;
1938: Uint8 Next_CRC_OK;
1.1.1.15 root 1939:
1940: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1941:
1.1.1.18 root 1942: /* Return false if no drive selected, or drive not enabled, or no disk in drive */
1943: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
1944: || ( !FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted ) )
1945: {
1946: LOG_TRACE(TRACE_FDC, "fdc type I verify track failed disabled/empty drive=%d VBL=%d video_cyc=%d %d@%d pc=%x\n",
1947: FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1948: return false;
1949: }
1950:
1.1.1.21 root 1951: /* Check if the current ID Field is the one we're looking for (same track and correct CRC) */
1952: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
1.1.1.9 root 1953: {
1.1.1.21 root 1954: Next_TR = FDC_NextSectorID_TR_STX ();
1955: Next_CRC_OK = FDC_NextSectorID_CRC_OK_STX ();
1956: }
1957: else
1958: {
1959: Next_TR = FDC_NextSectorID_TR_ST ();
1960: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
1961: }
1962:
1963: /* ST/MSA image will always be correct, only STX can fail depending on some protections */
1964: if ( ( Next_TR != FDC.TR ) || ( Next_CRC_OK == 0 ) )
1965: {
1966: 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",
1967: Next_TR , FDC.TR , Next_CRC_OK , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal ,
1.1.1.18 root 1968: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 1969:
1.1.1.18 root 1970: return false;
1.1.1.15 root 1971: }
1972:
1.1.1.18 root 1973: /* If disk image has only one side or drive is single sided and we're trying to verify on 2nd side, then return false */
1974: if ( ( FDC.SideSignal == 1 )
1975: && ( ( FDC_GetSidesPerDisk ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ) != 2 )
1976: || ( FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads == 1 ) ) )
1.1.1.15 root 1977: {
1.1.1.18 root 1978: 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",
1979: FDC.TR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal ,
1.1.1.15 root 1980: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1981:
1.1.1.18 root 1982: return false;
1.1.1.9 root 1983: }
1.1.1.15 root 1984:
1.1.1.18 root 1985: /* The track is the correct one */
1986: return true;
1.1.1.15 root 1987: }
1988:
1989:
1990: /*-----------------------------------------------------------------------*/
1991: /**
1.1.1.18 root 1992: * Run the 'motor stop' sequence : wait for 9 revolutions (1.8 sec)
1993: * and stop the motor.
1994: * We clear motor bit, but spinup bit remains to 1 (verified on a real STF)
1.1.1.15 root 1995: */
1996: static int FDC_UpdateMotorStop ( void )
1997: {
1.1.1.18 root 1998: int FdcCycles = 0;
1.1.1.15 root 1999: int FrameCycles, HblCounterVideo, LineCycles;
2000:
1.1.1.18 root 2001: /* Which command is running? */
2002: switch (FDC.CommandState)
2003: {
2004: case FDCEMU_RUN_MOTOR_STOP:
2005: FDC.IndexPulse_Counter = 0;
2006: FDC.CommandState = FDCEMU_RUN_MOTOR_STOP_WAIT;
1.1.1.22! root 2007: /* Continue to next state */
1.1.1.18 root 2008: case FDCEMU_RUN_MOTOR_STOP_WAIT:
2009: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_MOTOR_OFF )
2010: {
2011: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2012: break;
2013: }
2014: /* If IndexPulse_Counter reached, we go directly to the _COMPLETE state */
2015: case FDCEMU_RUN_MOTOR_STOP_COMPLETE:
2016: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2017: LOG_TRACE(TRACE_FDC, "fdc motor stopped VBL=%d video_cyc=%d %d@%d pc=%x\n",
2018: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2019:
1.1.1.18 root 2020: FDC.IndexPulse_Counter = 0;
2021: if ( FDC.DriveSelSignal >= 0 ) /* A drive was previously enabled */
2022: FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time = 0; /* Stop counting index pulses on the drive */
1.1.1.15 root 2023:
1.1.1.18 root 2024: FDC_Update_STR ( FDC_STR_BIT_MOTOR_ON , 0 ); /* Unset motor bit and keep spin up bit */
2025: FDC.Command = FDCEMU_CMD_NULL; /* Motor stopped, this is the last state */
2026: FdcCycles = 0;
2027: break;
2028: }
2029: return FdcCycles;
1.1 root 2030: }
2031:
1.1.1.2 root 2032:
2033: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2034: /**
2035: * Run 'RESTORE' command
2036: */
1.1.1.15 root 2037: static int FDC_UpdateRestoreCmd ( void )
1.1 root 2038: {
1.1.1.18 root 2039: int FdcCycles = 0;
2040: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2041:
1.1.1.18 root 2042: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2043:
1.1.1.6 root 2044: /* Which command is running? */
1.1.1.15 root 2045: switch (FDC.CommandState)
1.1.1.6 root 2046: {
2047: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO:
1.1.1.18 root 2048: if ( FDC_Set_MotorON ( FDC.CR ) )
2049: {
2050: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP;
2051: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2052: }
2053: else
2054: {
2055: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON;
2056: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2057: }
2058: break;
2059: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP:
2060: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2061: {
2062: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2063: break;
2064: }
2065: /* If IndexPulse_Counter reached, we go directly to the _MOTOR_ON state */
2066: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON:
2067: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* At this point, spin up sequence is ok */
2068: FDC.ReplaceCommandPossible = false;
2069:
1.1.1.15 root 2070: /* The FDC will try 255 times to reach track 0 using step out signals */
2071: /* If track 0 signal is not detected after 255 attempts, the command is interrupted */
2072: /* and FDC_STR_BIT_RNF is set in the Status Register. */
1.1.1.18 root 2073: /* This can happen if no drive is selected or if the selected drive is disabled */
1.1.1.15 root 2074: /* TR should be set to 255 once the spin-up sequence is made and the command */
2075: /* can't be interrupted anymore by another command (else TR value will be wrong */
2076: /* for other type I commands) */
2077: FDC.TR = 0xff;
1.1.1.22! root 2078: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP;
! 2079: /* Continue in the _LOOP state */
1.1.1.15 root 2080: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP:
2081: if ( FDC.TR == 0 ) /* Track 0 not reached after 255 attempts ? */
1.1.1.18 root 2082: { /* (this can happen if the drive is disabled) */
1.1.1.15 root 2083: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2084: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
1.1.1.18 root 2085: FdcCycles = FDC_CmdCompleteCommon( true );
2086: break;
1.1.1.15 root 2087: }
2088:
1.1.1.18 root 2089: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
2090: || ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack != 0 ) ) /* Are we at track zero ? */
1.1.1.15 root 2091: {
2092: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
2093: FDC.TR--; /* One less attempt */
1.1.1.18 root 2094: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
2095: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack--; /* Move physical head only if an enabled drive is selected */
2096: FdcCycles = FDC_DelayToFdcCycles ( FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000 );
1.1.1.15 root 2097: }
1.1.1.18 root 2098: else /* Drive is enabled and head is at track 0 */
1.1.1.6 root 2099: {
1.1.1.15 root 2100: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
2101: FDC.TR = 0; /* Update Track Register to 0 */
1.1.1.17 root 2102: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY;
1.1.1.18 root 2103: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2104: }
2105: break;
2106: case FDCEMU_RUN_RESTORE_VERIFY:
2107: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2108: {
1.1.1.18 root 2109: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK;
2110: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
1.1.1.17 root 2111: }
2112: else
2113: {
1.1.1.15 root 2114: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
1.1.1.18 root 2115: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2116: }
2117: break;
2118: case FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK:
2119: FDC.IndexPulse_Counter = 0;
1.1.1.22! root 2120: /* Head OK, continue and look for sector header */
1.1.1.18 root 2121: case FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER:
2122: /* If 'verify' doesn't succeed after 5 revolutions, we abort with RNF */
2123: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2124: {
2125: LOG_TRACE(TRACE_FDC, "fdc type I restore track=%d drive=%d verify RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2126: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
2127:
2128: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
2129: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
2130: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2131: break;
2132: }
2133:
2134: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2135: FdcCycles = -1;
2136: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2137: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2138: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2139: else
2140: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2141: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2142: if ( FdcCycles < 0 )
2143: {
1.1.1.19 root 2144: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER;
1.1.1.18 root 2145: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2146: }
2147: else
2148: {
2149: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2150: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, ID field */
2151: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER;
1.1.1.6 root 2152: }
2153: break;
1.1.1.18 root 2154: case FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER:
2155: /* Check if the current ID Field matches the track number */
2156: if ( FDC_VerifyTrack () )
2157: {
2158: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* Track is correct, remove RNF bit */
2159: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
2160: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2161: }
2162: else
2163: {
2164: /* Verify failed with this ID field ; check the next one */
2165: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER;
2166: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2167: }
1.1.1.17 root 2168: break;
1.1.1.6 root 2169: case FDCEMU_RUN_RESTORE_COMPLETE:
1.1.1.18 root 2170: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2171: break;
2172: }
1.1.1.15 root 2173:
1.1.1.18 root 2174: return FdcCycles;
1.1 root 2175: }
2176:
1.1.1.2 root 2177:
2178: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2179: /**
2180: * Run 'SEEK' command
2181: */
1.1.1.15 root 2182: static int FDC_UpdateSeekCmd ( void )
1.1 root 2183: {
1.1.1.18 root 2184: int FdcCycles = 0;
2185: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2186:
1.1.1.18 root 2187: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2188:
1.1.1.6 root 2189: /* Which command is running? */
1.1.1.15 root 2190: switch (FDC.CommandState)
1.1.1.6 root 2191: {
2192: case FDCEMU_RUN_SEEK_TOTRACK:
1.1.1.18 root 2193: if ( FDC_Set_MotorON ( FDC.CR ) )
2194: {
2195: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP;
2196: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2197: }
2198: else
2199: {
2200: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON;
2201: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2202: }
2203: break;
2204: case FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP:
2205: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2206: {
2207: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2208: break;
2209: }
2210: /* If IndexPulse_Counter reached, we go directly to the _MOTOR_ON state */
2211: case FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON:
2212: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* At this point, spin up sequence is ok */
2213: FDC.ReplaceCommandPossible = false;
2214:
1.1.1.15 root 2215: if ( FDC.TR == FDC.DR ) /* Are we at the selected track ? */
2216: {
1.1.1.17 root 2217: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
1.1.1.18 root 2218: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 2219: }
1.1.1.6 root 2220: else
2221: {
1.1.1.15 root 2222: if ( FDC.DR < FDC.TR ) /* Set StepDirection to the correct value */
2223: FDC.StepDirection = -1;
2224: else
2225: FDC.StepDirection = 1;
2226:
2227: /* Move head by one track depending on FDC.StepDirection and update Track Register */
2228: FDC.TR += FDC.StepDirection;
2229:
1.1.1.18 root 2230: FdcCycles = FDC_DelayToFdcCycles ( FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000 );
2231: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* By default, unset bit TR00 */
1.1.1.15 root 2232:
1.1.1.18 root 2233: /* Check / move physical head only if an enabled drive is selected */
2234: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
1.1.1.15 root 2235: {
1.1.1.18 root 2236: if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == FDC_PHYSICAL_MAX_TRACK ) && ( FDC.StepDirection == 1 ) )
2237: {
2238: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
2239: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No delay if trying to go after max track */
2240: }
1.1.1.15 root 2241:
1.1.1.18 root 2242: else if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 ) && ( FDC.StepDirection == -1 ) )
2243: {
2244: FDC.TR = 0; /* If we reach track 0, we stop there */
2245: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
2246: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2247: }
2248:
2249: else
2250: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack += FDC.StepDirection; /* Move physical head */
2251:
2252: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
2253: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
1.1.1.15 root 2254: }
1.1.1.6 root 2255: }
1.1.1.15 root 2256:
1.1.1.6 root 2257: break;
1.1.1.17 root 2258: case FDCEMU_RUN_SEEK_VERIFY:
1.1.1.15 root 2259: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
1.1.1.17 root 2260: {
1.1.1.18 root 2261: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_HEAD_OK;
2262: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
1.1.1.17 root 2263: }
2264: else
2265: {
2266: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
1.1.1.18 root 2267: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2268: }
2269: break;
2270: case FDCEMU_RUN_SEEK_VERIFY_HEAD_OK:
2271: FDC.IndexPulse_Counter = 0;
1.1.1.22! root 2272: /* Head OK, continue and look for sector header */
1.1.1.18 root 2273: case FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER:
2274: /* If 'verify' doesn't succeed after 5 revolutions, we abort with RNF */
2275: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2276: {
2277: LOG_TRACE(TRACE_FDC, "fdc type I seek track=%d drive=%d verify RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2278: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
2279:
2280: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
2281: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
2282: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2283: break;
2284: }
2285:
2286: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2287: FdcCycles = -1;
2288: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2289: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2290: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2291: else
2292: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2293: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2294: if ( FdcCycles < 0 )
2295: {
1.1.1.19 root 2296: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER;
1.1.1.18 root 2297: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2298: }
2299: else
2300: {
2301: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2302: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, ID field */
2303: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER;
1.1.1.17 root 2304: }
2305: break;
1.1.1.18 root 2306: case FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER:
2307: /* Check if the current ID Field matches the track number */
2308: if ( FDC_VerifyTrack () )
2309: {
2310: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* Track is correct, remove RNF bit */
2311: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
2312: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2313: }
2314: else
2315: {
2316: /* Verify failed with this ID field ; check the next one */
2317: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER;
2318: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2319: }
1.1.1.17 root 2320: break;
2321: case FDCEMU_RUN_SEEK_COMPLETE:
1.1.1.18 root 2322: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2323: break;
2324: }
1.1.1.15 root 2325:
1.1.1.18 root 2326: return FdcCycles;
1.1 root 2327: }
2328:
1.1.1.2 root 2329:
2330: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2331: /**
2332: * Run 'STEP' command
2333: */
1.1.1.15 root 2334: static int FDC_UpdateStepCmd ( void )
1.1 root 2335: {
1.1.1.18 root 2336: int FdcCycles = 0;
2337: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2338:
1.1.1.18 root 2339: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2340:
1.1.1.6 root 2341: /* Which command is running? */
1.1.1.15 root 2342: switch (FDC.CommandState)
1.1.1.6 root 2343: {
2344: case FDCEMU_RUN_STEP_ONCE:
1.1.1.18 root 2345: if ( FDC_Set_MotorON ( FDC.CR ) )
2346: {
2347: FDC.CommandState = FDCEMU_RUN_STEP_ONCE_SPIN_UP;
2348: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2349: }
2350: else
2351: {
2352: FDC.CommandState = FDCEMU_RUN_STEP_ONCE_MOTOR_ON;
2353: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2354: }
2355: break;
2356: case FDCEMU_RUN_STEP_ONCE_SPIN_UP:
2357: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2358: {
2359: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2360: break;
2361: }
2362: /* If IndexPulse_Counter reached, we go directly to the _MOTOR_ON state */
2363: case FDCEMU_RUN_STEP_ONCE_MOTOR_ON:
2364: FDC_Update_STR ( 0 , FDC_STR_BIT_SPIN_UP ); /* At this point, spin up sequence is ok */
2365: FDC.ReplaceCommandPossible = false;
2366:
1.1.1.15 root 2367: /* Move head by one track depending on FDC.StepDirection */
2368: if ( FDC.CR & FDC_COMMAND_BIT_UPDATE_TRACK )
2369: FDC.TR += FDC.StepDirection; /* Update Track Register */
2370:
1.1.1.18 root 2371: FdcCycles = FDC_DelayToFdcCycles ( FDC_StepRate_ms[ FDC_STEP_RATE ] * 1000 );
2372: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* By default, unset bit TR00 */
2373:
2374: /* Check / move physical head only if an enabled drive is selected */
2375: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
2376: {
2377: if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == FDC_PHYSICAL_MAX_TRACK ) && ( FDC.StepDirection == 1 ) )
2378: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No delay if trying to go after max track */
1.1.1.15 root 2379:
1.1.1.18 root 2380: else if ( ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 ) && ( FDC.StepDirection == -1 ) )
2381: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No delay if trying to go before track 0 */
1.1.1.6 root 2382:
1.1.1.18 root 2383: else
2384: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack += FDC.StepDirection; /* Move physical head */
2385:
2386: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
2387: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
2388: }
2389:
2390: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY;
2391: break;
2392: case FDCEMU_RUN_STEP_VERIFY:
2393: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2394: {
2395: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_HEAD_OK;
2396: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2397: }
1.1.1.15 root 2398: else
2399: {
1.1.1.18 root 2400: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2401: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2402: }
1.1.1.18 root 2403: break;
2404: case FDCEMU_RUN_STEP_VERIFY_HEAD_OK:
2405: FDC.IndexPulse_Counter = 0;
1.1.1.22! root 2406: /* Head OK, continue and look for sector header */
1.1.1.18 root 2407: case FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER:
2408: /* If 'verify' doesn't succeed after 5 revolutions, we abort with RNF */
2409: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2410: {
2411: LOG_TRACE(TRACE_FDC, "fdc type I step track=%d drive=%d verify RNF VBL=%d video_cyc=%d %d@%d pc=%x\n",
2412: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2413:
1.1.1.18 root 2414: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF ); /* Set RNF bit */
2415: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2416: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2417: break;
2418: }
2419:
2420: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2421: FdcCycles = -1;
2422: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2423: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2424: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2425: else
2426: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2427: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2428: if ( FdcCycles < 0 )
2429: {
1.1.1.19 root 2430: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER;
1.1.1.18 root 2431: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2432: }
1.1.1.15 root 2433: else
1.1.1.18 root 2434: {
2435: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2436: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, ID field */
2437: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER;
2438: }
1.1.1.17 root 2439: break;
1.1.1.18 root 2440: case FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER:
2441: /* Check if the current ID Field matches the track number */
2442: if ( FDC_VerifyTrack () )
1.1.1.17 root 2443: {
1.1.1.18 root 2444: FDC_Update_STR ( FDC_STR_BIT_RNF , 0 ); /* Track is correct, remove RNF bit */
2445: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2446: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.17 root 2447: }
2448: else
2449: {
1.1.1.18 root 2450: /* Verify failed with this ID field ; check the next one */
2451: FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER;
2452: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2453: }
2454: break;
1.1.1.6 root 2455: case FDCEMU_RUN_STEP_COMPLETE:
1.1.1.18 root 2456: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2457: break;
2458: }
1.1.1.15 root 2459:
1.1.1.18 root 2460: return FdcCycles;
1.1 root 2461: }
2462:
1.1.1.2 root 2463:
2464: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2465: /**
1.1.1.15 root 2466: * Run 'READ SECTOR/S' command
1.1.1.9 root 2467: */
1.1.1.15 root 2468: static int FDC_UpdateReadSectorsCmd ( void )
1.1 root 2469: {
1.1.1.18 root 2470: int FdcCycles = 0;
1.1.1.15 root 2471: int SectorSize;
1.1.1.18 root 2472: int FrameCycles, HblCounterVideo, LineCycles;
2473: Uint8 Next_TR;
2474: Uint8 Next_SR;
2475: Uint8 Next_CRC_OK;
1.1.1.15 root 2476:
2477: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2478:
2479:
1.1.1.6 root 2480: /* Which command is running? */
1.1.1.15 root 2481: switch (FDC.CommandState)
1.1.1.6 root 2482: {
1.1.1.15 root 2483: case FDCEMU_RUN_READSECTORS_READDATA:
1.1.1.18 root 2484: if ( FDC_Set_MotorON ( FDC.CR ) )
2485: {
2486: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_SPIN_UP;
2487: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2488: }
2489: else
2490: {
2491: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_HEAD_LOAD;
2492: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2493: }
1.1.1.15 root 2494: break;
1.1.1.18 root 2495: case FDCEMU_RUN_READSECTORS_READDATA_SPIN_UP:
2496: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2497: {
2498: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2499: break;
2500: }
2501: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
2502: case FDCEMU_RUN_READSECTORS_READDATA_HEAD_LOAD:
2503: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
2504: {
2505: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON;
2506: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2507: break;
2508: }
2509: /* If there's no head settle, we go directly to the _MOTOR_ON state */
2510: case FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON:
2511: FDC.ReplaceCommandPossible = false;
2512: FDC.IndexPulse_Counter = 0;
2513: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER;
2514: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2515: break;
2516: case FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER:
1.1.1.17 root 2517: /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
1.1.1.18 root 2518: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
1.1.1.17 root 2519: {
2520: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
1.1.1.18 root 2521: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2522: break;
2523: }
2524:
1.1.1.18 root 2525: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2526: FdcCycles = -1;
2527: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2528: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2529: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2530: else
2531: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2532: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2533: if ( FdcCycles < 0 )
2534: {
2535: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2536: }
2537: else
2538: {
2539: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2540: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, TR, SIDE, SR, LEN, CRC1, CRC2 */
2541: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER;
2542: }
2543: break;
2544: case FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER:
2545: /* Check if the current ID Field is the one we're looking for (same track/sector and correct CRC) */
2546: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2547: {
2548: Next_TR = FDC_NextSectorID_TR_STX ();
2549: Next_SR = FDC_NextSectorID_SR_STX ();
2550: Next_CRC_OK = FDC_NextSectorID_CRC_OK_STX ();
2551: }
2552: else
2553: {
2554: Next_TR = FDC_NextSectorID_TR_ST ();
2555: Next_SR = FDC_NextSectorID_SR_ST ();
2556: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
2557: }
2558: if ( ( Next_TR == FDC.TR ) && ( Next_SR == FDC.SR ) && ( Next_CRC_OK ) )
1.1.1.17 root 2559: {
2560: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START;
1.1.1.18 root 2561: /* Read bytes to reach the sector's data : GAP3a + GAP3b + 3xA1 + FB */
2562: FdcCycles = FDC_TransferByte_FdcCycles ( FDC_TRACK_LAYOUT_STANDARD_GAP3a + FDC_TRACK_LAYOUT_STANDARD_GAP3b + 3 + 1 );
1.1.1.17 root 2563: }
2564: else
2565: {
2566: /* This is not the ID field we're looking for ; check the next one */
1.1.1.18 root 2567: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER;
2568: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2569: }
2570: break;
2571: case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START:
1.1.1.15 root 2572: /* Read a single sector into temporary buffer (512 bytes for ST/MSA) */
1.1.1.18 root 2573: FDC_Buffer_Reset();
1.1.1.17 root 2574:
1.1.1.18 root 2575: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2576: FDC.Status_Temp = FDC_ReadSector_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2577: FDC.SR , FDC.SideSignal , &SectorSize );
2578: else
2579: FDC.Status_Temp = FDC_ReadSector_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2580: FDC.SR , FDC.SideSignal , &SectorSize );
2581:
2582: if ( FDC.Status_Temp & FDC_STR_BIT_RNF ) /* Sector FDC.SR was not found */
1.1.1.15 root 2583: {
2584: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
1.1.1.18 root 2585: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2586: }
2587: else
2588: {
2589: if ( FDC.Status_Temp & FDC_STR_BIT_RECORD_TYPE )
2590: FDC_Update_STR ( 0 , FDC_STR_BIT_RECORD_TYPE );
2591: else
2592: FDC_Update_STR ( FDC_STR_BIT_RECORD_TYPE , 0 );
2593:
2594: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP;
2595: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the first byte */
1.1.1.15 root 2596: }
2597: break;
1.1.1.17 root 2598: case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP:
1.1.1.18 root 2599: /* Transfer the sector 1 byte at a time using DMA */
2600: FDC_DMA_FIFO_Push ( FDC_Buffer_Read_Byte () ); /* Add 1 byte to the DMA FIFO */
2601: if ( FDC_BUFFER.PosRead < FDC_Buffer_Get_Size () )
1.1.1.15 root 2602: {
1.1.1.18 root 2603: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
1.1.1.15 root 2604: }
1.1.1.17 root 2605: else /* Sector transferred, check the CRC */
1.1.1.15 root 2606: {
1.1.1.17 root 2607: FDC.CommandState = FDCEMU_RUN_READSECTORS_CRC;
1.1.1.18 root 2608: FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Read 2 bytes for CRC */
1.1.1.17 root 2609: }
2610: break;
2611: case FDCEMU_RUN_READSECTORS_CRC:
1.1.1.18 root 2612: /* Sector completely transferred, CRC is always good for ST/MSA, but not always for STX */
2613: if ( FDC.Status_Temp & FDC_STR_BIT_CRC_ERROR )
2614: {
2615: 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",
2616: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
2617:
2618: FDC_Update_STR ( 0 , FDC_STR_BIT_CRC_ERROR );
2619: FdcCycles = FDC_CmdCompleteCommon( true );
2620: }
2621: else
2622: {
2623: FDC.CommandState = FDCEMU_RUN_READSECTORS_MULTI;
2624: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2625: }
2626: break;
2627: case FDCEMU_RUN_READSECTORS_MULTI:
2628: /* Check for multi bit */
1.1.1.17 root 2629: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2630: {
2631: FDC.SR++; /* Try to read next sector and set RNF if not possible */
1.1.1.18 root 2632: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON;
2633: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2634: 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",
2635: FDC.SR, FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal ,
2636: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.17 root 2637: }
2638: else /* Multi=0, stop here with no error */
2639: {
2640: FDC.CommandState = FDCEMU_RUN_READSECTORS_COMPLETE;
1.1.1.18 root 2641: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2642: }
2643: break;
2644: case FDCEMU_RUN_READSECTORS_RNF:
1.1.1.18 root 2645: 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",
2646: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.6 root 2647:
1.1.1.15 root 2648: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
1.1.1.18 root 2649: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2650: break;
1.1.1.15 root 2651: case FDCEMU_RUN_READSECTORS_COMPLETE:
1.1.1.18 root 2652: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2653: break;
2654: }
2655:
1.1.1.18 root 2656: return FdcCycles;
1.1.1.15 root 2657: }
2658:
2659:
2660: /*-----------------------------------------------------------------------*/
2661: /**
2662: * Run 'WRITE SECTOR/S' command
2663: */
2664: static int FDC_UpdateWriteSectorsCmd ( void )
2665: {
1.1.1.18 root 2666: int FdcCycles = 0;
1.1.1.15 root 2667: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 2668: Uint8 Next_TR;
2669: Uint8 Next_SR;
2670: Uint8 Next_LEN;
2671: Uint8 Next_CRC_OK;
2672: Uint8 Byte;
2673: Uint8 Status;
1.1.1.15 root 2674:
2675: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2676:
1.1.1.18 root 2677: /* Stop now if disk is write protected */
2678: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
2679: && ( FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted )
2680: && ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) ) )
1.1.1.15 root 2681: {
1.1.1.18 root 2682: 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",
2683: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2684:
2685: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
1.1.1.18 root 2686: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2687: }
2688: else
2689: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit */
2690:
2691:
2692: /* Which command is running? */
2693: switch (FDC.CommandState)
2694: {
2695: case FDCEMU_RUN_WRITESECTORS_WRITEDATA:
1.1.1.18 root 2696: if ( FDC_Set_MotorON ( FDC.CR ) )
2697: {
2698: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_SPIN_UP;
2699: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2700: }
2701: else
2702: {
2703: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_HEAD_LOAD;
2704: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2705: }
1.1.1.15 root 2706: break;
1.1.1.18 root 2707: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_SPIN_UP:
2708: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2709: {
2710: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2711: break;
2712: }
2713: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
2714: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_HEAD_LOAD:
2715: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
2716: {
2717: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON;
2718: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2719: break;
2720: }
2721: /* If there's no head settle, we go directly to the _MOTOR_ON state */
2722: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON:
2723: FDC.ReplaceCommandPossible = false;
2724: FDC.IndexPulse_Counter = 0;
1.1.1.22! root 2725: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER;
! 2726: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
! 2727: break;
1.1.1.18 root 2728: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER:
1.1.1.17 root 2729: /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
1.1.1.18 root 2730: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
1.1.1.17 root 2731: {
1.1.1.18 root 2732: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_RNF;
2733: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2734: break;
2735: }
2736:
1.1.1.18 root 2737: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2738: FdcCycles = -1;
2739: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2740: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2741: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2742: else
2743: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2744: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2745: if ( FdcCycles < 0 )
1.1.1.17 root 2746: {
1.1.1.18 root 2747: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
1.1.1.17 root 2748: }
2749: else
2750: {
1.1.1.18 root 2751: /* Read bytes to reach the next sector's ID field and skip 10 more bytes to read the whole ID field */
2752: FdcCycles += FDC_TransferByte_FdcCycles ( 10 ); /* Add delay to read 3xA1, FE, TR, SIDE, SR, LEN, CRC1, CRC2 */
1.1.1.17 root 2753: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER;
2754: }
2755: break;
1.1.1.18 root 2756: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER:
2757: /* Check if the current ID Field is the one we're looking for (same track/sector and correct CRC) */
2758: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2759: {
2760: Next_TR = FDC_NextSectorID_TR_STX ();
2761: Next_SR = FDC_NextSectorID_SR_STX ();
2762: Next_CRC_OK = FDC_NextSectorID_CRC_OK_STX ();
2763: }
2764: else
1.1.1.15 root 2765: {
1.1.1.18 root 2766: Next_TR = FDC_NextSectorID_TR_ST ();
2767: Next_SR = FDC_NextSectorID_SR_ST ();
2768: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
1.1.1.15 root 2769: }
1.1.1.18 root 2770: if ( ( Next_TR == FDC.TR ) && ( Next_SR == FDC.SR ) && ( Next_CRC_OK ) )
1.1.1.15 root 2771: {
1.1.1.18 root 2772: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START;
2773: /* Read bytes to reach the sector's data : GAP3a + GAP3b + 3xA1 + FB */
2774: FdcCycles = FDC_TransferByte_FdcCycles ( FDC_TRACK_LAYOUT_STANDARD_GAP3a + FDC_TRACK_LAYOUT_STANDARD_GAP3b + 3 + 1 );
2775: }
2776: else
2777: {
2778: /* This is not the ID field we're looking for ; check the next one */
2779: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER;
2780: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 2781: }
2782: break;
1.1.1.18 root 2783: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START:
2784: /* Write a single sector from RAM (512 bytes for ST/MSA) */
2785: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2786: Next_LEN = FDC_NextSectorID_LEN_STX ();
2787: else
2788: Next_LEN = FDC_NextSectorID_LEN_ST ();
2789:
2790: FDC_Buffer_Reset();
2791: FDC_DMA.BytesToTransfer = 128 << ( Next_LEN & FDC_SECTOR_SIZE_MASK );
2792:
2793: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP;
2794: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2795: break;
1.1.1.17 root 2796: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP:
1.1.1.18 root 2797: /* Transfer the sector 1 byte at a time using DMA */
2798: if ( FDC_DMA.BytesToTransfer-- > 0 )
1.1.1.15 root 2799: {
1.1.1.18 root 2800: Byte = FDC_DMA_FIFO_Pull (); /* Get 1 byte from the DMA FIFO */
2801: //fprintf ( stderr , "byte %d %x\n" , FDC_DMA.BytesToTransfer , Byte );
2802: FDC_Buffer_Add ( Byte );
2803: FdcCycles = FDC_TransferByte_FdcCycles ( 1 );
1.1.1.15 root 2804: }
1.1.1.18 root 2805: else /* Sector transferred, add the CRC */
1.1.1.15 root 2806: {
1.1.1.17 root 2807: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_CRC;
1.1.1.18 root 2808: FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Write 2 bytes for CRC */
1.1.1.17 root 2809: }
2810: break;
2811: case FDCEMU_RUN_WRITESECTORS_CRC:
1.1.1.18 root 2812: /* Sector completely transferred, CRC is always good for ST/MSA */
2813: /* This is where we save the buffer to the disk image */
2814:
2815: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2816: Status = FDC_WriteSector_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2817: FDC.SR , FDC.SideSignal , FDC_Buffer_Get_Size () );
2818: else
2819: Status = FDC_WriteSector_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2820: FDC.SR , FDC.SideSignal , FDC_Buffer_Get_Size () );
2821:
2822: if ( Status & FDC_STR_BIT_RNF ) /* Sector FDC.SR was not correctly written */
2823: {
2824: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_RNF;
2825: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2826: }
2827: else
2828: {
2829: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_MULTI;
2830: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2831: }
2832: break;
2833: case FDCEMU_RUN_WRITESECTORS_MULTI:
2834: /* Check for multi bit */
1.1.1.17 root 2835: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2836: {
2837: FDC.SR++; /* Try to write next sector and set RNF if not possible */
1.1.1.18 root 2838: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON;
2839: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2840: 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",
2841: FDC.SR, FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal ,
2842: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.17 root 2843: }
2844: else /* Multi=0, stop here with no error */
2845: {
2846: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_COMPLETE;
1.1.1.18 root 2847: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2848: }
2849: break;
2850: case FDCEMU_RUN_WRITESECTORS_RNF:
1.1.1.18 root 2851: 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",
2852: FDC.SR , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 2853:
2854: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
1.1.1.18 root 2855: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2856: break;
2857: case FDCEMU_RUN_WRITESECTORS_COMPLETE:
1.1.1.18 root 2858: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2859: break;
2860: }
1.1.1.15 root 2861:
1.1.1.18 root 2862: return FdcCycles;
1.1 root 2863: }
2864:
1.1.1.2 root 2865:
2866: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2867: /**
1.1.1.15 root 2868: * Run 'READ ADDRESS' command
1.1.1.9 root 2869: */
1.1.1.15 root 2870: static int FDC_UpdateReadAddressCmd ( void )
1.1 root 2871: {
1.1.1.18 root 2872: int FdcCycles = 0;
1.1.1.17 root 2873: int FrameCycles, HblCounterVideo, LineCycles;
2874:
2875: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2876:
1.1.1.6 root 2877: /* Which command is running? */
1.1.1.15 root 2878: switch (FDC.CommandState)
1.1.1.6 root 2879: {
1.1.1.15 root 2880: case FDCEMU_RUN_READADDRESS:
1.1.1.18 root 2881: if ( FDC_Set_MotorON ( FDC.CR ) )
2882: {
2883: FDC.CommandState = FDCEMU_RUN_READADDRESS_SPIN_UP;
2884: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
2885: }
2886: else
2887: {
2888: FDC.CommandState = FDCEMU_RUN_READADDRESS_HEAD_LOAD;
2889: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
2890: }
1.1.1.6 root 2891: break;
1.1.1.18 root 2892: case FDCEMU_RUN_READADDRESS_SPIN_UP:
2893: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
2894: {
2895: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
2896: break;
2897: }
2898: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
2899: case FDCEMU_RUN_READADDRESS_HEAD_LOAD:
2900: FDC.ReplaceCommandPossible = false;
2901: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
2902: {
2903: FDC.CommandState = FDCEMU_RUN_READADDRESS_MOTOR_ON;
2904: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2905: break;
2906: }
2907: /* If there's no head settle, we go directly to the _MOTOR_ON state */
2908: case FDCEMU_RUN_READADDRESS_MOTOR_ON:
2909: FDC.ReplaceCommandPossible = false;
2910: FDC.IndexPulse_Counter = 0;
2911: FDC.CommandState = FDCEMU_RUN_READADDRESS_NEXT_SECTOR_HEADER;
2912: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2913: break;
2914: case FDCEMU_RUN_READADDRESS_NEXT_SECTOR_HEADER:
2915: /* If we don't find a sector header after more than 5 revolutions, we abort with RNF */
2916: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2917: {
2918: FDC.CommandState = FDCEMU_RUN_READADDRESS_RNF;
2919: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2920: break;
2921: }
1.1.1.15 root 2922:
1.1.1.18 root 2923: if ( FDC.DriveSelSignal < 0 ) /* No drive selected */
2924: FdcCycles = -1;
2925: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2926: FdcCycles = FDC_NextSectorID_FdcCycles_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2927: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2928: else
2929: FdcCycles = FDC_NextSectorID_FdcCycles_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads ,
2930: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
2931: if ( FdcCycles < 0 )
2932: {
2933: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2934: }
2935: else
2936: {
2937: /* Read bytes to reach the next sector's ID field */
2938: FdcCycles += FDC_TransferByte_FdcCycles ( 4 ); /* Add delay to read 3xA1, FE */
2939: FDC.CommandState = FDCEMU_RUN_READADDRESS_TRANSFER_START;
2940: }
1.1.1.15 root 2941: break;
1.1.1.18 root 2942: case FDCEMU_RUN_READADDRESS_TRANSFER_START:
2943: /* Read the ID field into buffer */
2944: FDC_Buffer_Reset();
2945:
2946: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
2947: FDC.Status_Temp = FDC_ReadAddress_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2948: FDC_NextSectorID_SR_STX () , FDC.SideSignal );
2949: else
2950: FDC.Status_Temp = FDC_ReadAddress_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
2951: FDC_NextSectorID_SR_ST () , FDC.SideSignal );
1.1.1.17 root 2952:
1.1.1.18 root 2953: 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 2954:
1.1.1.18 root 2955: FDC.CommandState = FDCEMU_RUN_READADDRESS_TRANSFER_LOOP;
2956: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the first byte */
2957: break;
2958: case FDCEMU_RUN_READADDRESS_TRANSFER_LOOP:
2959: /* Transfer the ID field 1 byte at a time using DMA */
2960: FDC_DMA_FIFO_Push ( FDC_Buffer_Read_Byte () ); /* Add 1 byte to the DMA FIFO */
2961: if ( FDC_BUFFER.PosRead < FDC_Buffer_Get_Size () )
2962: {
2963: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
2964: }
2965: else
2966: {
2967: FDC.CommandState = FDCEMU_RUN_READADDRESS_COMPLETE;
2968: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2969: }
1.1.1.17 root 2970: break;
1.1.1.18 root 2971: case FDCEMU_RUN_READADDRESS_RNF:
2972: 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",
2973: FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , FDC.DriveSelSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.17 root 2974:
1.1.1.18 root 2975: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2976: FdcCycles = FDC_CmdCompleteCommon( true );
2977: break;
1.1.1.15 root 2978: case FDCEMU_RUN_READADDRESS_COMPLETE:
1.1.1.18 root 2979: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2980: break;
2981: }
1.1.1.15 root 2982:
1.1.1.18 root 2983: return FdcCycles;
1.1 root 2984: }
2985:
1.1.1.2 root 2986:
2987: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2988: /**
1.1.1.15 root 2989: * Run 'READ TRACK' command
1.1.1.9 root 2990: */
1.1.1.15 root 2991: static int FDC_UpdateReadTrackCmd ( void )
1.1 root 2992: {
1.1.1.18 root 2993: int FdcCycles = 0;
1.1.1.15 root 2994: int i;
1.1.1.18 root 2995: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2996:
1.1.1.18 root 2997: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2998:
1.1.1.6 root 2999: /* Which command is running? */
1.1.1.15 root 3000: switch (FDC.CommandState)
1.1.1.6 root 3001: {
1.1.1.15 root 3002: case FDCEMU_RUN_READTRACK:
1.1.1.18 root 3003: if ( FDC_Set_MotorON ( FDC.CR ) )
3004: {
3005: FDC.CommandState = FDCEMU_RUN_READTRACK_SPIN_UP;
3006: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
3007: }
3008: else
3009: {
3010: FDC.CommandState = FDCEMU_RUN_READTRACK_HEAD_LOAD;
3011: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
3012: }
3013: break;
3014: case FDCEMU_RUN_READTRACK_SPIN_UP:
3015: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
3016: {
3017: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
3018: break;
3019: }
3020: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
3021: case FDCEMU_RUN_READTRACK_HEAD_LOAD:
3022: FDC.ReplaceCommandPossible = false;
3023: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
3024: {
3025: FDC.CommandState = FDCEMU_RUN_READTRACK_MOTOR_ON;
3026: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
3027: break;
3028: }
3029: /* If there's no head settle, we go directly to the _MOTOR_ON state */
3030: case FDCEMU_RUN_READTRACK_MOTOR_ON:
3031: FdcCycles = FDC_NextIndexPulse_FdcCycles (); /* Wait for the next index pulse */
3032: //fprintf ( stderr , "read tr idx=%d %d\n" , FDC_IndexPulse_GetState() , FdcCycles );
3033: if ( FdcCycles < 0 )
3034: {
3035: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
3036: }
3037: else
3038: {
3039: FDC.CommandState = FDCEMU_RUN_READTRACK_INDEX;
3040: }
1.1.1.17 root 3041: break;
3042: case FDCEMU_RUN_READTRACK_INDEX:
1.1.1.18 root 3043: /* At this point, we have a valid drive/floppy, build the track data */
3044: FDC_Buffer_Reset();
1.1.1.6 root 3045:
1.1.1.18 root 3046: if ( ( FDC.SideSignal == 1 ) /* Try to read side 1 on a disk that doesn't have 2 sides or drive is single sided */
3047: && ( ( FDC_GetSidesPerDisk ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ) != 2 )
3048: || ( FDC_DRIVES[ FDC.DriveSelSignal ].NumberOfHeads == 1 ) ) )
1.1.1.15 root 3049: {
1.1.1.18 root 3050: 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",
3051: FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal ,
3052: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3053:
3054: for ( i=0 ; i<FDC_GetBytesPerTrack ( FDC.DriveSelSignal ) ; i++ )
3055: FDC_Buffer_Add ( rand() & 0xff ); /* Fill the track buffer with random bytes */
3056: }
3057: else if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
3058: {
3059: FDC.Status_Temp = FDC_ReadTrack_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
1.1.1.6 root 3060: }
1.1.1.18 root 3061: else /* Track/side available in the disk image */
1.1.1.6 root 3062: {
1.1.1.18 root 3063: FDC.Status_Temp = FDC_ReadTrack_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
3064: }
1.1.1.15 root 3065:
1.1.1.18 root 3066: FDC.CommandState = FDCEMU_RUN_READTRACK_TRANSFER_LOOP;
3067: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the first byte */
3068: break;
3069: case FDCEMU_RUN_READTRACK_TRANSFER_LOOP:
3070: /* Transfer the track 1 byte at a time using DMA */
3071: FDC_DMA_FIFO_Push ( FDC_Buffer_Read_Byte () ); /* Add 1 byte to the DMA FIFO */
3072: if ( FDC_BUFFER.PosRead < FDC_Buffer_Get_Size () )
3073: {
3074: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
3075: }
3076: else /* Track completely transferred */
3077: {
3078: FDC.CommandState = FDCEMU_RUN_READTRACK_COMPLETE;
3079: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
3080: }
3081: break;
3082: case FDCEMU_RUN_READTRACK_COMPLETE:
3083: FdcCycles = FDC_CmdCompleteCommon( true );
3084: break;
3085: }
1.1.1.15 root 3086:
1.1.1.18 root 3087: return FdcCycles;
3088: }
1.1.1.15 root 3089:
3090:
1.1.1.18 root 3091: /*-----------------------------------------------------------------------*/
3092: /**
3093: * Run 'WRITE TRACK' command
3094: */
3095: static int FDC_UpdateWriteTrackCmd ( void )
3096: {
3097: int FdcCycles = 0;
3098: int FrameCycles, HblCounterVideo, LineCycles;
3099: Uint8 Byte;
3100: Uint8 Status;
1.1.1.15 root 3101:
1.1.1.18 root 3102: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3103:
3104: /* Which command is running? */
3105: switch (FDC.CommandState)
3106: {
3107: case FDCEMU_RUN_WRITETRACK:
3108: if ( FDC_Set_MotorON ( FDC.CR ) )
3109: {
3110: FDC.CommandState = FDCEMU_RUN_WRITETRACK_SPIN_UP;
3111: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Spin up needed */
3112: }
3113: else
3114: {
3115: FDC.CommandState = FDCEMU_RUN_WRITETRACK_HEAD_LOAD;
3116: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE; /* No spin up needed */
3117: }
3118: break;
3119: case FDCEMU_RUN_WRITETRACK_SPIN_UP:
3120: if ( FDC.IndexPulse_Counter < FDC_DELAY_IP_SPIN_UP )
3121: {
3122: FdcCycles = FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE; /* Wait for the correct number of IP */
3123: break;
3124: }
3125: /* If IndexPulse_Counter reached, we go directly to the _HEAD_LOAD state */
3126: case FDCEMU_RUN_WRITETRACK_HEAD_LOAD:
3127: FDC.ReplaceCommandPossible = false;
3128: if ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD )
3129: {
3130: FDC.CommandState = FDCEMU_RUN_WRITETRACK_MOTOR_ON;
3131: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
3132: break;
3133: }
3134: /* If there's no head settle, we go directly to the _MOTOR_ON state */
3135: case FDCEMU_RUN_WRITETRACK_MOTOR_ON:
3136: FdcCycles = FDC_NextIndexPulse_FdcCycles (); /* Wait for the next index pulse */
3137: //fprintf ( stderr , "write tr idx=%d %d\n" , FDC_IndexPulse_GetState() , FdcCycles );
3138: if ( FdcCycles < 0 )
3139: {
3140: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
3141: }
3142: else
3143: {
3144: FDC.CommandState = FDCEMU_RUN_WRITETRACK_INDEX;
3145: }
3146: break;
3147: case FDCEMU_RUN_WRITETRACK_INDEX:
3148: /* At this point, we have a valid drive/floppy, check write protection and write the track data */
3149: if ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) )
3150: {
3151: 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",
3152: FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal , nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 3153:
1.1.1.18 root 3154: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
3155: FdcCycles = FDC_CmdCompleteCommon( true );
3156: break;
1.1.1.6 root 3157: }
1.1 root 3158:
1.1.1.18 root 3159: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit */
3160:
3161: FDC_Buffer_Reset();
3162: FDC_DMA.BytesToTransfer = FDC_GetBytesPerTrack ( FDC.DriveSelSignal );
1.1.1.2 root 3163:
1.1.1.18 root 3164: FDC.CommandState = FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP;
3165: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 3166: break;
1.1.1.18 root 3167: case FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP:
3168: /* Transfer the track 1 byte at a time using DMA */
3169: if ( FDC_DMA.BytesToTransfer-- > 0 )
1.1.1.6 root 3170: {
1.1.1.18 root 3171: Byte = FDC_DMA_FIFO_Pull (); /* Get 1 byte from the DMA FIFO */
3172: //fprintf ( stderr , "byte %d %x\n" , FDC_DMA.BytesToTransfer , Byte );
3173: FDC_Buffer_Add ( Byte );
3174: FdcCycles = FDC_TransferByte_FdcCycles ( 1 );
1.1.1.6 root 3175: }
1.1.1.18 root 3176: else /* Track written */
1.1.1.6 root 3177: {
1.1.1.18 root 3178: FDC.CommandState = FDCEMU_RUN_WRITETRACK_COMPLETE;
3179: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.6 root 3180: }
3181: break;
1.1.1.18 root 3182: case FDCEMU_RUN_WRITETRACK_COMPLETE:
3183: /* Track completely transferred */
3184: /* This is where we save the buffer to the disk image */
3185: if ( EmulationDrives[ FDC.DriveSelSignal ].ImageType == FLOPPY_IMAGE_TYPE_STX )
3186: Status = FDC_WriteTrack_STX ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
3187: FDC.SideSignal , FDC_Buffer_Get_Size () );
3188: else
3189: Status = FDC_WriteTrack_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack ,
3190: FDC.SideSignal , FDC_Buffer_Get_Size () );
3191:
3192: if ( Status & FDC_STR_BIT_LOST_DATA ) /* Error while writing */
3193: FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* Set LOST_DATA bit */
3194:
3195: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 3196: break;
3197: }
1.1.1.15 root 3198:
1.1.1.18 root 3199: return FdcCycles;
1.1 root 3200: }
3201:
1.1.1.2 root 3202:
3203: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3204: /**
1.1.1.15 root 3205: * Common to types I, II and III
3206: *
3207: * Start motor / spin up sequence if needed
1.1.1.18 root 3208: * Return true if spin up sequence is needed, else false
1.1.1.11 root 3209: */
1.1.1.15 root 3210:
1.1.1.18 root 3211: static bool FDC_Set_MotorON ( Uint8 FDC_CR )
1.1.1.11 root 3212: {
1.1.1.15 root 3213: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 3214: bool SpinUp;
1.1.1.15 root 3215:
3216: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3217:
1.1.1.18 root 3218: if ( ( ( FDC_CR & FDC_COMMAND_BIT_SPIN_UP ) == 0 ) /* Command wants motor's spin up */
3219: && ( ( FDC.STR & FDC_STR_BIT_MOTOR_ON ) == 0 ) ) /* Motor on not enabled yet */
3220: {
3221: LOG_TRACE(TRACE_FDC, "fdc start motor with spinup VBL=%d video_cyc=%d %d@%d pc=%x\n",
3222: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3223:
3224: FDC_Update_STR ( FDC_STR_BIT_SPIN_UP , 0 ); /* Unset spin up bit */
3225: FDC.IndexPulse_Counter = 0; /* Reset counter to measure the spin up sequence */
3226: SpinUp = true;
3227: }
3228: else /* No spin up : don't add delay to start the motor */
1.1.1.11 root 3229: {
1.1.1.18 root 3230: LOG_TRACE(TRACE_FDC, "fdc start motor without spinup VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.15 root 3231: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.18 root 3232:
3233: SpinUp = false;
1.1.1.11 root 3234: }
1.1.1.15 root 3235:
1.1.1.18 root 3236: FDC_Update_STR ( 0 , FDC_STR_BIT_MOTOR_ON ); /* Start motor */
3237:
3238: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
3239: || ( !FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted ) )
3240: {
3241: LOG_TRACE(TRACE_FDC, "fdc start motor : no disk/drive VBL=%d video_cyc=%d %d@%d pc=%x\n",
3242: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3243: }
3244: else if ( FDC_DRIVES[ FDC.DriveSelSignal ].IndexPulse_Time == 0 )
3245: FDC_IndexPulse_Init ( FDC.DriveSelSignal ); /* Index Pulse's position is random when motor starts */
3246:
3247: return SpinUp;
1.1.1.11 root 3248: }
3249:
3250:
3251: /*-----------------------------------------------------------------------*/
3252: /**
1.1.1.9 root 3253: * Type I Commands
3254: *
3255: * Restore, Seek, Step, Step-In and Step-Out
3256: */
1.1 root 3257:
1.1.1.2 root 3258:
3259: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3260: static int FDC_TypeI_Restore(void)
1.1 root 3261: {
1.1.1.15 root 3262: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3263:
3264: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3265:
1.1.1.21 root 3266: 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 3267: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3268: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3269: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3270: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal].HeadTrack : -1 ,
3271: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3272:
1.1.1.6 root 3273: /* Set emulation to seek to track zero */
1.1.1.15 root 3274: FDC.Command = FDCEMU_CMD_RESTORE;
3275: FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO;
3276:
3277: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3278:
1.1.1.18 root 3279: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3280: }
3281:
1.1.1.2 root 3282:
3283: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3284: static int FDC_TypeI_Seek ( void )
1.1 root 3285: {
1.1.1.15 root 3286: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3287:
3288: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3289:
1.1.1.21 root 3290: 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 3291: FDC.DR,
1.1.1.18 root 3292: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3293: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3294: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3295: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3296: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3297:
1.1.1.6 root 3298: /* Set emulation to seek to chosen track */
1.1.1.15 root 3299: FDC.Command = FDCEMU_CMD_SEEK;
3300: FDC.CommandState = FDCEMU_RUN_SEEK_TOTRACK;
3301:
3302: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3303:
1.1.1.18 root 3304: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3305: }
3306:
1.1.1.2 root 3307:
3308: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3309: static int FDC_TypeI_Step ( void )
1.1 root 3310: {
1.1.1.15 root 3311: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3312:
3313: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3314:
1.1.1.21 root 3315: 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 3316: FDC.StepDirection,
1.1.1.18 root 3317: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3318: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3319: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3320: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3321: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 3322:
3323: /* Set emulation to step (using same direction as latest seek executed, ie 'FDC.StepDirection') */
3324: FDC.Command = FDCEMU_CMD_STEP;
3325: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
1.1.1.10 root 3326:
1.1.1.15 root 3327: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3328:
1.1.1.18 root 3329: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3330: }
3331:
1.1.1.2 root 3332:
3333: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3334: static int FDC_TypeI_StepIn(void)
1.1 root 3335: {
1.1.1.15 root 3336: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3337:
3338: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3339:
1.1.1.21 root 3340: 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 3341: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3342: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3343: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3344: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3345: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3346:
1.1.1.15 root 3347: /* Set emulation to step in (direction = +1) */
3348: FDC.Command = FDCEMU_CMD_STEP;
3349: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
3350: FDC.StepDirection = 1; /* Increment track*/
1.1 root 3351:
1.1.1.15 root 3352: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
3353:
1.1.1.18 root 3354: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3355: }
3356:
1.1.1.2 root 3357:
3358: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3359: static int FDC_TypeI_StepOut ( void )
1.1 root 3360: {
1.1.1.15 root 3361: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3362:
3363: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3364:
1.1.1.21 root 3365: 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 3366: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3367: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3368: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18 root 3369: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3370: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 3371:
3372: /* Set emulation to step out (direction = -1) */
3373: FDC.Command = FDCEMU_CMD_STEP;
3374: FDC.CommandState = FDCEMU_RUN_STEP_ONCE;
3375: FDC.StepDirection = -1; /* Decrement track */
1.1.1.10 root 3376:
1.1.1.15 root 3377: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3378:
1.1.1.18 root 3379: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3380: }
3381:
1.1.1.2 root 3382:
3383: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3384: /**
3385: * Type II Commands
3386: *
1.1.1.15 root 3387: * Read Sector, Write Sector
1.1.1.9 root 3388: */
1.1 root 3389:
1.1.1.2 root 3390:
3391: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3392: static int FDC_TypeII_ReadSector ( void )
1.1 root 3393: {
1.1.1.15 root 3394: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3395:
3396: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3397:
1.1.1.17 root 3398: 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",
3399: FDC.SR, ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" ,
1.1.1.18 root 3400: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3401: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3402: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3403: FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount ,
1.1.1.15 root 3404: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.12 root 3405:
1.1.1.15 root 3406: /* Set emulation to read sector(s) */
3407: FDC.Command = FDCEMU_CMD_READSECTORS;
3408: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA;
1.1.1.10 root 3409:
1.1.1.15 root 3410: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3411: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
1.1 root 3412:
1.1.1.18 root 3413: return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
1.1 root 3414: }
3415:
1.1.1.2 root 3416:
3417: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3418: static int FDC_TypeII_WriteSector ( void )
1.1 root 3419: {
1.1.1.15 root 3420: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3421:
3422: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3423:
1.1.1.17 root 3424: 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",
3425: FDC.SR, ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR ) ? "on" : "off" ,
1.1.1.18 root 3426: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3427: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3428: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3429: FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount,
1.1.1.15 root 3430: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.2 root 3431:
1.1.1.15 root 3432: /* Set emulation to write a sector(s) */
3433: FDC.Command = FDCEMU_CMD_WRITESECTORS;
3434: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA;
1.1.1.10 root 3435:
1.1.1.15 root 3436: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3437: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE , FDC_STR_BIT_BUSY );
1.1 root 3438:
1.1.1.18 root 3439: return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
1.1 root 3440: }
3441:
1.1.1.2 root 3442:
3443: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3444: /**
3445: * Type III Commands
3446: *
3447: * Read Address, Read Track, Write Track
3448: */
1.1 root 3449:
1.1.1.2 root 3450:
3451: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3452: static int FDC_TypeIII_ReadAddress ( void )
1.1 root 3453: {
1.1.1.15 root 3454: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3455:
3456: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3457:
1.1.1.17 root 3458: 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 3459: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3460: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3461: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3462: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.12 root 3463: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3464:
1.1.1.11 root 3465: /* Set emulation to seek to track zero */
1.1.1.15 root 3466: FDC.Command = FDCEMU_CMD_READADDRESS;
3467: FDC.CommandState = FDCEMU_RUN_READADDRESS;
1.1.1.11 root 3468:
1.1.1.15 root 3469: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3470: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
3471:
1.1.1.18 root 3472: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3473: }
3474:
1.1.1.2 root 3475:
3476: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3477: static int FDC_TypeIII_ReadTrack ( void )
1.1 root 3478: {
1.1.1.15 root 3479: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3480:
3481: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3482:
1.1.1.17 root 3483: 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 3484: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3485: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3486: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3487: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.15 root 3488: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3489:
1.1.1.15 root 3490: /* Set emulation to read a single track */
3491: FDC.Command = FDCEMU_CMD_READTRACK;
3492: FDC.CommandState = FDCEMU_RUN_READTRACK;
1.1.1.5 root 3493:
1.1.1.15 root 3494: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3495: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
1.1.1.5 root 3496:
1.1.1.18 root 3497: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3498: }
3499:
1.1.1.2 root 3500:
3501: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3502: static int FDC_TypeIII_WriteTrack ( void )
1.1 root 3503: {
1.1.1.15 root 3504: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3505:
3506: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3507:
1.1.1.17 root 3508: 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 3509: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3510: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18 root 3511: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3512: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.15 root 3513: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3514:
1.1.1.6 root 3515: /* Set emulation to write a single track */
1.1.1.18 root 3516: FDC.Command = FDCEMU_CMD_WRITETRACK;
3517: FDC.CommandState = FDCEMU_RUN_WRITETRACK;
1.1 root 3518:
1.1.1.18 root 3519: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3520: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
3521:
3522: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3523: }
3524:
1.1.1.2 root 3525:
3526: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3527: /**
3528: * Type IV Commands
3529: *
3530: * Force Interrupt
3531: */
1.1 root 3532:
1.1.1.2 root 3533:
3534: /*-----------------------------------------------------------------------*/
1.1.1.18 root 3535: static int FDC_TypeIV_ForceInterrupt ( void )
1.1 root 3536: {
1.1.1.18 root 3537: int FdcCycles;
1.1.1.15 root 3538: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3539:
3540: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3541:
1.1.1.18 root 3542: 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 3543: FDC.CR , ( FDC.CR & 0x8 ) >> 3 , ( FDC.CR & 0x4 ) >> 2 ,
1.1.1.12 root 3544: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3545:
1.1.1.18 root 3546: /* If a command was running, just remove busy bit and keep the current content of Status reg */
3547: /* If FDC was idle, the content of Status reg is forced to type I */
3548: if ( ( FDC.STR & FDC_STR_BIT_BUSY ) == 0 )
1.1.1.15 root 3549: {
1.1.1.18 root 3550: FDC.StatusTypeI = true;
1.1.1.15 root 3551:
1.1.1.18 root 3552: /* Starting a Force Int command when idle should set the motor bit and clear the spinup bit (verified on STF) */
3553: 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 3554: }
3555:
1.1.1.18 root 3556: /* Get the interrupt's condition and set IRQ accordingly */
3557: /* Most of the time a 0xD8 command is followed by a 0xD0 command and a read STR to clear the IRQ signal */
3558: FDC.InterruptCond = FDC.CR & 0x0f; /* Keep the 4 lowest bits */
3559:
3560: if ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE )
3561: FDC_SetIRQ ( FDC_IRQ_SOURCE_FORCED );
3562: else
3563: FDC_ClearIRQ ();
3564:
3565: /* Remove busy bit, don't change IRQ's state and stop the motor */
3566: FdcCycles = FDC_CmdCompleteCommon( false );
1.1.1.6 root 3567:
1.1.1.18 root 3568: return FDC_DELAY_CYCLE_TYPE_IV_PREPARE + FdcCycles;
1.1 root 3569: }
3570:
1.1.1.2 root 3571:
3572: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3573: /**
3574: * Execute Type I commands
3575: */
1.1.1.15 root 3576: static int FDC_ExecuteTypeICommands ( void )
1.1 root 3577: {
1.1.1.18 root 3578: int FdcCycles = 0;
1.1.1.15 root 3579:
3580: FDC.CommandType = 1;
1.1.1.18 root 3581: FDC.StatusTypeI = true;
1.1 root 3582:
1.1.1.6 root 3583: /* Check Type I Command */
1.1.1.15 root 3584: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3585: {
3586: case 0x00: /* Restore */
1.1.1.18 root 3587: FdcCycles = FDC_TypeI_Restore();
1.1.1.6 root 3588: break;
3589: case 0x10: /* Seek */
1.1.1.18 root 3590: FdcCycles = FDC_TypeI_Seek();
1.1.1.6 root 3591: break;
3592: case 0x20: /* Step */
3593: case 0x30:
1.1.1.18 root 3594: FdcCycles = FDC_TypeI_Step();
1.1.1.6 root 3595: break;
3596: case 0x40: /* Step-In */
3597: case 0x50:
1.1.1.18 root 3598: FdcCycles = FDC_TypeI_StepIn();
1.1.1.6 root 3599: break;
3600: case 0x60: /* Step-Out */
3601: case 0x70:
1.1.1.18 root 3602: FdcCycles = FDC_TypeI_StepOut();
1.1.1.6 root 3603: break;
3604: }
1.1 root 3605:
1.1.1.18 root 3606: return FdcCycles;
1.1 root 3607: }
3608:
1.1.1.2 root 3609:
3610: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3611: /**
3612: * Execute Type II commands
3613: */
1.1.1.15 root 3614: static int FDC_ExecuteTypeIICommands ( void )
1.1 root 3615: {
1.1.1.18 root 3616: int FdcCycles = 0;
1.1.1.15 root 3617:
3618: FDC.CommandType = 2;
1.1.1.18 root 3619: FDC.StatusTypeI = false;
1.1 root 3620:
1.1.1.6 root 3621: /* Check Type II Command */
1.1.1.15 root 3622: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3623: {
1.1.1.15 root 3624: case 0x80: /* Read Sector multi=0*/
3625: case 0x90: /* Read Sectors multi=1 */
1.1.1.18 root 3626: FdcCycles = FDC_TypeII_ReadSector();
1.1.1.6 root 3627: break;
1.1.1.15 root 3628: case 0xa0: /* Write Sector multi=0 */
3629: case 0xb0: /* Write Sectors multi=1 */
1.1.1.18 root 3630: FdcCycles = FDC_TypeII_WriteSector();
1.1.1.6 root 3631: break;
3632: }
1.1 root 3633:
1.1.1.18 root 3634: return FdcCycles;
1.1 root 3635: }
3636:
1.1.1.2 root 3637:
3638: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3639: /**
3640: * Execute Type III commands
3641: */
1.1.1.15 root 3642: static int FDC_ExecuteTypeIIICommands ( void )
1.1 root 3643: {
1.1.1.18 root 3644: int FdcCycles = 0;
1.1.1.15 root 3645:
3646: FDC.CommandType = 3;
1.1.1.18 root 3647: FDC.StatusTypeI = false;
1.1 root 3648:
1.1.1.6 root 3649: /* Check Type III Command */
1.1.1.15 root 3650: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3651: {
3652: case 0xc0: /* Read Address */
1.1.1.18 root 3653: FdcCycles = FDC_TypeIII_ReadAddress();
1.1.1.6 root 3654: break;
3655: case 0xe0: /* Read Track */
1.1.1.18 root 3656: FdcCycles = FDC_TypeIII_ReadTrack();
1.1.1.6 root 3657: break;
3658: case 0xf0: /* Write Track */
1.1.1.18 root 3659: FdcCycles = FDC_TypeIII_WriteTrack();
1.1.1.6 root 3660: break;
3661: }
1.1 root 3662:
1.1.1.18 root 3663: return FdcCycles;
1.1 root 3664: }
3665:
1.1.1.2 root 3666:
3667: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3668: /**
3669: * Execute Type IV commands
3670: */
1.1.1.15 root 3671: static int FDC_ExecuteTypeIVCommands ( void )
1.1 root 3672: {
1.1.1.18 root 3673: int FdcCycles;
1.1 root 3674:
1.1.1.18 root 3675: FDC.CommandType = 4;
1.1.1.15 root 3676:
1.1.1.18 root 3677: FdcCycles = FDC_TypeIV_ForceInterrupt();
3678:
3679: return FdcCycles;
1.1 root 3680: }
3681:
1.1.1.2 root 3682:
3683: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3684: /**
3685: * Find FDC command type and execute
1.1.1.18 root 3686: *
3687: * NOTE [NP] : as verified on a real STF and contrary to what is written
3688: * in the WD1772 doc, any new command will reset the InterruptCond set by
3689: * a previous Dx command, not just a D0.
3690: * This means that a D8 command (force int) can be cancelled by a D0 command
3691: * or by any other command ; but in any case, IRQ will remain set until
3692: * status register is read or another new command is started.
3693: * -> 1st command clears force IRQ condition, 2nd command clears IRQ
1.1.1.9 root 3694: */
1.1.1.15 root 3695: static void FDC_ExecuteCommand ( void )
1.1 root 3696: {
1.1.1.18 root 3697: int FdcCycles;
3698: Uint8 Type;
3699:
3700: Type = FDC_GetCmdType ( FDC.CR );
3701:
3702: /* When a new command is started, FDC's IRQ is reset (except if "force interrupt immediate" is set) */
3703:
3704: /* If IRQ is forced but FDC_INTERRUPT_COND_IMMEDIATE is not set anymore, this means */
3705: /* the D8 command was stopped and we can clear the forced IRQ when starting a new command */
3706: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED )
3707: && ( ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE ) == 0 ) )
3708: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_FORCED; /* Really stop the forced IRQ */
3709:
3710: /* Starting a new type I/II/III should clear the IRQ (except when IRQ is forced) */
3711: /* For type IV, this is handled in FDC_TypeIV_ForceInterrupt() */
3712: if ( Type != 4 )
3713: FDC_ClearIRQ ();
3714:
3715: /* When a new command is executed, we clear InterruptCond */
3716: /* (not just when the new command is D0) */
3717: /* InterruptCond is cleared here, but it might be set again just after */
3718: /* when we call FDC_ExecuteTypeIVCommands() */
3719: FDC.InterruptCond = 0;
1.1.1.15 root 3720:
1.1.1.6 root 3721: /* Check type of command and execute */
1.1.1.18 root 3722: if ( Type == 1 ) /* Type I - Restore, Seek, Step, Step-In, Step-Out */
3723: FdcCycles = FDC_ExecuteTypeICommands();
3724: else if ( Type == 2 ) /* Type II - Read Sector, Write Sector */
3725: FdcCycles = FDC_ExecuteTypeIICommands();
3726: else if ( Type == 3 ) /* Type III - Read Address, Read Track, Write Track */
3727: FdcCycles = FDC_ExecuteTypeIIICommands();
1.1.1.15 root 3728: else /* Type IV - Force Interrupt */
1.1.1.18 root 3729: FdcCycles = FDC_ExecuteTypeIVCommands();
1.1.1.9 root 3730:
1.1.1.18 root 3731: FDC.ReplaceCommandPossible = true; /* This new command can be replaced during the prepare+spinup phase */
3732: FDC_StartTimer_FdcCycles ( FdcCycles , 0 );
1.1 root 3733: }
3734:
1.1.1.2 root 3735:
3736: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3737: /**
1.1.1.15 root 3738: * Write to SectorCount register $ff8604
1.1.1.9 root 3739: */
1.1.1.15 root 3740: static void FDC_WriteSectorCountRegister ( void )
1.1 root 3741: {
1.1.1.12 root 3742: int FrameCycles, HblCounterVideo, LineCycles;
3743:
3744: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3745:
1.1.1.22! root 3746: FDC_DMA.SectorCount = IoMem_ReadWord(0xff8604);
! 3747: if (!Config_IsMachineFalcon())
! 3748: FDC_DMA.SectorCount &= 0xff;
! 3749:
1.1.1.15 root 3750: LOG_TRACE(TRACE_FDC, "fdc write 8604 dma sector count=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.22! root 3751: FDC_DMA.SectorCount, nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.9 root 3752:
1.1 root 3753: }
3754:
1.1.1.2 root 3755:
3756: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3757: /**
1.1.1.15 root 3758: * Write to Command register $ff8604
1.1.1.9 root 3759: */
1.1.1.15 root 3760: static void FDC_WriteCommandRegister ( void )
1.1 root 3761: {
1.1.1.12 root 3762: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 3763: Uint8 Type_new;
1.1.1.12 root 3764:
3765: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3766:
3767: 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 3768: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3769:
3770: /* If fdc is busy, only 'Force Interrupt' is possible */
3771: /* [NP] : it's also possible to start a new command just after another command */
1.1.1.18 root 3772: /* 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 3773: if ( FDC.STR & FDC_STR_BIT_BUSY )
3774: {
1.1.1.18 root 3775: Type_new = FDC_GetCmdType ( IoMem_ReadByte(0xff8605) );
3776: if ( Type_new == 4 ) /* 'Force Interrupt' command */
1.1.1.15 root 3777: {
3778: 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",
3779: FDC.CR , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3780: }
3781:
1.1.1.18 root 3782: else if ( FDC.ReplaceCommandPossible
3783: && ( ( ( Type_new == 1 ) && ( FDC.CommandType == Type_new ) ) /* Replace a type I command with a type I */
3784: || ( ( Type_new == 2 ) && ( FDC.CommandType == Type_new ) ) ) ) /* Replace a type II command with a type II */
1.1.1.15 root 3785: {
3786: 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",
3787: FDC.CR , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3788: }
3789:
3790: else /* Other cases : new command is ignored */
3791: {
3792: LOG_TRACE(TRACE_FDC, "fdc write 8604 fdc busy, command=0x%x ignored VBL=%d video_cyc=%d %d@%d pc=%x\n",
3793: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3794: return;
3795: }
3796: }
1.1.1.9 root 3797:
1.1.1.15 root 3798: FDC.CR = IoMem_ReadByte(0xff8605);
1.1.1.6 root 3799: FDC_ExecuteCommand();
1.1 root 3800: }
3801:
1.1.1.2 root 3802:
3803: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3804: /**
1.1.1.15 root 3805: * Write to Track register $ff8604
1.1.1.9 root 3806: */
1.1.1.15 root 3807: static void FDC_WriteTrackRegister ( void )
1.1 root 3808: {
1.1.1.12 root 3809: int FrameCycles, HblCounterVideo, LineCycles;
3810:
3811: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3812:
3813: 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 3814: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 3815:
1.1.1.15 root 3816: /* [NP] Contrary to what is written in the WD1772 doc, Track Register can be changed */
1.1.1.18 root 3817: /* while the fdc is busy (the change will be ignored or not, depending on the current sub-state */
3818: /* in the state machine) */
1.1.1.15 root 3819: if ( FDC.STR & FDC_STR_BIT_BUSY )
3820: {
3821: 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",
3822: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.18 root 3823: //return;
1.1.1.15 root 3824: }
3825:
3826: FDC.TR = IoMem_ReadByte(0xff8605);
1.1 root 3827: }
3828:
1.1.1.2 root 3829:
3830: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3831: /**
1.1.1.15 root 3832: * Write to Sector register $ff8604
1.1.1.9 root 3833: */
1.1.1.15 root 3834: static void FDC_WriteSectorRegister ( void )
1.1 root 3835: {
1.1.1.12 root 3836: int FrameCycles, HblCounterVideo, LineCycles;
3837:
3838: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3839:
3840: 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 3841: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
3842:
3843: /* [NP] Contrary to what is written in the WD1772 doc, Sector Register can be changed */
3844: /* while the fdc is busy (but it will have no effect once the sector's header is found) */
3845: /* (fix Delirious Demo IV's loader, which is bugged and set SR after starting the Read Sector command) */
3846: if ( FDC.STR & FDC_STR_BIT_BUSY )
3847: {
3848: 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",
3849: IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3850: }
1.1.1.9 root 3851:
1.1.1.15 root 3852: FDC.SR = IoMem_ReadByte(0xff8605);
1.1 root 3853: }
3854:
1.1.1.2 root 3855:
3856: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3857: /**
1.1.1.15 root 3858: * Write to Data register $ff8604
1.1.1.9 root 3859: */
1.1.1.15 root 3860: static void FDC_WriteDataRegister ( void )
1.1 root 3861: {
1.1.1.12 root 3862: int FrameCycles, HblCounterVideo, LineCycles;
3863:
3864: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3865:
3866: 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 3867: IoMem_ReadByte(0xff8605), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 3868:
1.1.1.15 root 3869: FDC.DR = IoMem_ReadByte(0xff8605);
1.1 root 3870: }
3871:
1.1.1.2 root 3872:
3873: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3874: /**
1.1.1.18 root 3875: * Store byte in FDC/HDC registers or DMA sector count, when writing to $ff8604
3876: * When accessing FDC/HDC registers, a copy of $ff8604 should be kept in ff8604_recent_val
3877: * to be used later when reading unused bits at $ff8604/$ff8606
3878: *
3879: * NOTE [NP] : add 4 cycles wait state in all cases (sector count / FDC / HDC)
1.1.1.9 root 3880: */
1.1.1.15 root 3881: void FDC_DiskController_WriteWord ( void )
1.1 root 3882: {
1.1.1.12 root 3883: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18 root 3884: int EmulationMode;
3885: int FDC_reg;
1.1.1.12 root 3886:
1.1.1.15 root 3887: if ( nIoMemAccessSize == SIZE_BYTE )
1.1.1.6 root 3888: {
3889: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21 root 3890: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.6 root 3891: return;
3892: }
3893:
1.1.1.8 root 3894: M68000_WaitState(4);
3895:
1.1.1.12 root 3896: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.9 root 3897:
1.1.1.12 root 3898: 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 3899: IoMem_ReadWord(0xff8604), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.6 root 3900:
1.1.1.18 root 3901:
3902: /* Are we trying to set the DMA SectorCount ? */
3903: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.12 root 3904: {
1.1.1.18 root 3905: FDC_WriteSectorCountRegister();
1.1.1.10 root 3906: return;
1.1.1.12 root 3907: }
1.1.1.6 root 3908:
1.1.1.18 root 3909: /* Store the byte that was just accessed by this write */
3910: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | IoMem_ReadByte(0xff8605);
3911:
3912: if ( ( FDC_DMA.Mode & 0x0008 ) == 0x0008 ) /* Is it an ACSI (or Falcon SCSI) HDC command access ? */
1.1.1.8 root 3913: {
1.1.1.18 root 3914: /* Handle HDC access */
3915: LOG_TRACE(TRACE_FDC, "fdc write 8604 hdc command addr=%x command=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
3916: FDC_DMA.Mode & 0x7 , IoMem_ReadByte(0xff8605), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3917:
3918: HDC_WriteCommandByte(FDC_DMA.Mode & 0x7, IoMem_ReadByte(0xff8605));
3919: return;
3920: }
3921:
3922: else /* It's a FDC register access */
3923: {
3924: FDC_reg = ( FDC_DMA.Mode & 0x6 ) >> 1; /* Bits 1,2 (A0,A1) */
3925:
3926: EmulationMode = FDC_GetEmulationMode();
3927: if ( EmulationMode == FDC_EMULATION_MODE_INTERNAL )
3928: {
3929: /* Update FDC's internal variables */
3930: FDC_UpdateAll ();
3931:
3932: /* Write to FDC registers */
3933: switch ( FDC_reg )
3934: {
3935: case 0x0: /* 0 0 - Command register */
3936: FDC_WriteCommandRegister();
3937: break;
3938: case 0x1: /* 0 1 - Track register */
3939: FDC_WriteTrackRegister();
3940: break;
3941: case 0x2: /* 1 0 - Sector register */
3942: FDC_WriteSectorRegister();
3943: break;
3944: case 0x3: /* 1 1 - Data register */
3945: FDC_WriteDataRegister();
3946: break;
3947: }
3948: }
3949: else if ( EmulationMode == FDC_EMULATION_MODE_IPF )
3950: {
3951: IPF_FDC_WriteReg ( FDC_reg , IoMem_ReadByte(0xff8605) );
1.1.1.6 root 3952: }
3953: }
1.1 root 3954: }
3955:
1.1.1.2 root 3956:
3957: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3958: /**
1.1.1.18 root 3959: * Return FDC/HDC registers or DMA sector count when reading from $ff8604
3960: * - When accessing FDC/HDC registers, a copy of $ff8604 should be kept in ff8604_recent_val
3961: * to be used later when reading unused bits at $ff8604/$ff8606
3962: * - DMA sector count can't be read, this will return ff8604_recent_val (verified on a real STF)
3963: *
3964: * NOTE [NP] : add 4 cycles wait state in that case, except when reading DMA sector count
1.1.1.9 root 3965: */
1.1.1.15 root 3966: void FDC_DiskControllerStatus_ReadWord ( void )
1.1 root 3967: {
1.1.1.15 root 3968: Uint16 DiskControllerByte = 0; /* Used to pass back the parameter */
1.1.1.12 root 3969: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 3970: int ForceWPRT;
1.1.1.18 root 3971: int EmulationMode;
3972: int FDC_reg;
1.1.1.6 root 3973:
1.1.1.20 root 3974: if (nIoMemAccessSize == SIZE_BYTE && !Config_IsMachineFalcon())
1.1.1.6 root 3975: {
3976: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21 root 3977: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.6 root 3978: return;
3979: }
3980:
1.1.1.18 root 3981: /* Are we trying to read the DMA SectorCount ? */
3982: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.6 root 3983: {
1.1.1.18 root 3984: DiskControllerByte = FDC_DMA.ff8604_recent_val; /* As verified on real STF, DMA sector count can't be read back */
1.1.1.6 root 3985: }
1.1.1.18 root 3986:
3987: else if ( ( FDC_DMA.Mode & 0x0008) == 0x0008) /* HDC status reg selected ? */
1.1.1.8 root 3988: {
1.1.1.18 root 3989: M68000_WaitState(4); /* [NP] : possible, but not tested on real HW */
3990:
3991: /* Return the HDC status byte */
3992: DiskControllerByte = HDC_ReadCommandByte(FDC_DMA.Mode & 0x7);
1.1.1.8 root 3993: }
1.1.1.18 root 3994:
3995: else /* It's a FDC register access */
1.1.1.6 root 3996: {
1.1.1.18 root 3997: M68000_WaitState(4);
3998:
3999: FDC_reg = ( FDC_DMA.Mode & 0x6 ) >> 1; /* Bits 1,2 (A0,A1) */
4000:
4001: EmulationMode = FDC_GetEmulationMode();
4002: if ( EmulationMode == FDC_EMULATION_MODE_INTERNAL )
1.1.1.6 root 4003: {
1.1.1.18 root 4004: /* Update FDC's internal variables */
4005: FDC_UpdateAll ();
4006:
4007: /* Read FDC registers */
4008: switch ( FDC_reg )
1.1.1.6 root 4009: {
1.1.1.18 root 4010: case 0x0: /* 0 0 - Status register */
4011: /* If we report a type I status, some bits are updated in real time */
4012: /* depending on the corresponding signals. If this is not a type I, we return STR unmodified */
4013: /* [NP] Contrary to what is written in the WD1772 doc, the WPRT bit */
4014: /* is updated after a Type I command */
4015: /* (eg : Procopy or Terminators Copy 1.68 do a Restore/Seek to test WPRT) */
4016: if ( FDC.StatusTypeI )
4017: {
4018: /* If no drive available, FDC's input signals TR00, INDEX and WPRT are off */
4019: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
4020: FDC_Update_STR ( FDC_STR_BIT_TR00 | FDC_STR_BIT_INDEX | FDC_STR_BIT_WPRT , 0 );
4021:
4022: else
4023: {
4024: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
4025: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set TR00 bit2 */
4026: else
4027: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset TR00 bit2 */
4028:
4029: if ( FDC_IndexPulse_GetState () )
4030: FDC_Update_STR ( 0 , FDC_STR_BIT_INDEX ); /* Set INDEX bit1 */
4031: else
4032: FDC_Update_STR ( FDC_STR_BIT_INDEX , 0 ); /* Unset INDEX bit1 */
4033:
4034: /* For Type I, always unset CRC ERROR bit3 */
4035: FDC_Update_STR ( FDC_STR_BIT_CRC_ERROR , 0 );
4036:
4037: /* When there's no disk in drive, the floppy drive hardware can't see */
4038: /* the difference with an inserted disk that would be write protected */
4039: if ( ! FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted )
4040: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit6 */
4041: else if ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) )
4042: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit6 */
4043: else
4044: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit6 */
4045:
4046: /* Temporarily change the WPRT bit if we're in a transition phase */
4047: /* regarding the disk in the drive (inserting or ejecting) */
4048: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC.DriveSelSignal );
4049: if ( ForceWPRT == 1 )
4050: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Force setting WPRT */
4051: else if ( ForceWPRT == -1 )
4052: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Force clearing WPRT */
4053:
4054: if ( ForceWPRT != 0 )
4055: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n",
4056: ForceWPRT==1?1:0, nVBLs, FDC.DriveSelSignal, FDC.STR );
4057: }
4058: }
1.1.1.17 root 4059:
1.1.1.18 root 4060: DiskControllerByte = FDC.STR;
4061:
4062: /* When Status Register is read, FDC's IRQ is reset (except if "force interrupt immediate" is set) */
4063:
4064: /* If IRQ is forced but FDC_INTERRUPT_COND_IMMEDIATE is not set anymore, this means */
4065: /* the D8 command was stopped and we can clear the forced IRQ while reading status register */
4066: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED )
4067: && ( ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE ) == 0 ) )
4068: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_FORCED; /* Really stop the forced IRQ */
4069:
4070: FDC_ClearIRQ ();
4071: break;
4072: case 0x1: /* 0 1 - Track register */
4073: DiskControllerByte = FDC.TR;
4074: break;
4075: case 0x2: /* 1 0 - Sector register */
4076: DiskControllerByte = FDC.SR;
4077: break;
4078: case 0x3: /* 1 1 - Data register */
4079: DiskControllerByte = FDC.DR;
4080: break;
1.1.1.6 root 4081: }
1.1.1.18 root 4082: }
4083: else if ( EmulationMode == FDC_EMULATION_MODE_IPF )
4084: {
4085: DiskControllerByte = IPF_FDC_ReadReg ( FDC_reg );
4086: if ( ( FDC_reg == 0x0 ) && ( FDC.DriveSelSignal >= 0 ) ) /* 0 0 - Status register */
4087: {
4088: /* Temporarily change the WPRT bit if we're in a transition phase */
4089: /* regarding the disk in the drive (inserting or ejecting) */
4090: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC.DriveSelSignal );
4091: if ( ForceWPRT == 1 )
4092: DiskControllerByte |= FDC_STR_BIT_WPRT; /* Force setting WPRT */
4093: if ( ForceWPRT == -1 )
4094: DiskControllerByte &= ~FDC_STR_BIT_WPRT; /* Force clearing WPRT */
1.1.1.6 root 4095:
1.1.1.18 root 4096: if ( ForceWPRT != 0 )
4097: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n", ForceWPRT==1?1:0, nVBLs, FDC.DriveSelSignal, DiskControllerByte );
4098: }
1.1.1.6 root 4099: }
4100: }
4101:
1.1.1.18 root 4102:
4103: /* Store the byte that was just returned by this read if we accessed fdc/hdc regs */
4104: if ( ( FDC_DMA.Mode & 0x10 ) == 0 ) /* Bit 4 */
4105: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | ( DiskControllerByte & 0xff );
4106:
1.1.1.7 root 4107: IoMem_WriteWord(0xff8604, DiskControllerByte);
1.1.1.9 root 4108:
1.1.1.12 root 4109: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4110:
4111: LOG_TRACE(TRACE_FDC, "fdc read 8604 ctrl status=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4112: DiskControllerByte , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1 root 4113: }
4114:
1.1.1.2 root 4115:
4116: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4117: /**
1.1.1.15 root 4118: * Write word to $ff8606 (DMA Mode Control)
4119: *
4120: * Eg.
4121: * $80 - Selects command/status register
4122: * $82 - Selects track register
4123: * $84 - Selects sector register
1.1.1.17 root 4124: * $86 - Selects data register
1.1.1.15 root 4125: * NOTE - OR above values with $100 is transfer from memory to floppy
4126: * Also if bit 4 is set, write to DMA sector count register
1.1.1.18 root 4127: *
4128: * NOTE [NP] : add 4 cycles wait state in that case
1.1.1.15 root 4129: */
4130: void FDC_DmaModeControl_WriteWord ( void )
4131: {
4132: Uint16 Mode_prev; /* Store previous write to 0xff8606 for 'toggle' checks */
4133: int FrameCycles, HblCounterVideo, LineCycles;
4134:
4135:
4136: if (nIoMemAccessSize == SIZE_BYTE)
4137: {
4138: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21 root 4139: M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.15 root 4140: return;
4141: }
4142:
1.1.1.18 root 4143: M68000_WaitState(4);
4144:
1.1.1.15 root 4145: Mode_prev = FDC_DMA.Mode; /* Store previous to check for _read/_write toggle (DMA reset) */
4146: FDC_DMA.Mode = IoMem_ReadWord(0xff8606); /* Store to DMA Mode control */
4147:
4148: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4149:
4150: LOG_TRACE(TRACE_FDC, "fdc write 8606 ctrl=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4151: FDC_DMA.Mode , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4152:
4153: /* When write to 0xff8606, check bit '8' toggle. This causes DMA status reset */
4154: if ((Mode_prev ^ FDC_DMA.Mode) & 0x0100)
4155: FDC_ResetDMA();
1.1.1.22! root 4156:
! 4157: if ((Mode_prev & 0xc0) != 0 && (FDC_DMA.Mode & 0xc0) == 0)
! 4158: HDC_DmaTransfer();
1.1.1.15 root 4159: }
4160:
4161:
4162: /*-----------------------------------------------------------------------*/
4163: /**
4164: * Read DMA Status at $ff8606
4165: *
1.1.1.18 root 4166: * Only bits 0-2 are used :
4167: * Bit 0 - Error Status (0=Error)
4168: * Bit 1 - Sector Count Zero Status (0=Sector Count Zero)
4169: * Bit 2 - Data Request signal from the FDC
4170: *
4171: * NOTE [NP] : as verified on STF, bit 0 will be cleared (=error) if DMA sector count is 0
4172: * when we get some DRQ to process.
4173: *
4174: * NOTE [NP] : on the ST, the Data Register will always be read by the DMA when the FDC's DRQ
4175: * signal is set. This means bit 2 of DMA status will be '0' nearly all the time
4176: * (as verified on STF by constantly reading DMA Status, bit 2 can be '1' during
4177: * a few cycles, before the DMA read the Data Register, but for the emulation we
4178: * consider it's always '0')
4179: *
4180: * NOTE [NP] : unused bits 3-15 are the ones from the latest $ff8604 access (verified on real STF)
4181: *
4182: * NOTE [NP] : no 4 cycles wait state in that case
1.1.1.15 root 4183: */
4184: void FDC_DmaStatus_ReadWord ( void )
4185: {
1.1.1.20 root 4186: if (nIoMemAccessSize == SIZE_BYTE && !Config_IsMachineFalcon())
1.1.1.15 root 4187: {
4188: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.21 root 4189: M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
1.1.1.15 root 4190: return;
4191: }
4192:
1.1.1.18 root 4193: /* Update Bit1 for zero sector count */
4194: if ( FDC_DMA.SectorCount != 0 )
4195: FDC_DMA.Status |= 0x02;
1.1.1.15 root 4196: else
1.1.1.18 root 4197: FDC_DMA.Status &= ~0x02;
1.1.1.15 root 4198:
1.1.1.18 root 4199: /* 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 4200:
1.1.1.18 root 4201: /* Return Status with unused bits replaced by latest bits from $ff8604 */
4202: IoMem_WriteWord( 0xff8606 , FDC_DMA.Status | ( FDC_DMA.ff8604_recent_val & 0xfff8 ) );
1.1.1.15 root 4203: }
4204:
4205:
4206: /*-----------------------------------------------------------------------*/
4207: /**
4208: * Read hi/med/low DMA address byte at $ff8609/0b/0d
4209: */
4210: void FDC_DmaAddress_ReadByte ( void )
4211: {
4212: int FrameCycles, HblCounterVideo, LineCycles;
4213:
4214: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4215:
4216: 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" ,
4217: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] , FDC_GetDMAAddress() ,
4218: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4219: }
4220:
4221:
4222: /*-----------------------------------------------------------------------*/
4223: /**
4224: * Write hi/med/low DMA address byte at $ff8609/0b/0d
1.1.1.21 root 4225: *
4226: * NOTE [NP] : as described by Ijor in http://atari-forum.com/viewtopic.php?f=16&t=30289
4227: * the STF DMA address counter uses a ripple carry adder that will increment middle byte
4228: * when bit 7 of lower byte goes from 1 to 0 (same for middle/high bytes)
4229: * To avoid possible error with this carry, DMA address bytes should be written in the order
4230: * low, middle, high (as specified by Atari documentations) and not high/middle/low
1.1.1.15 root 4231: */
4232: void FDC_DmaAddress_WriteByte ( void )
4233: {
1.1.1.21 root 4234: Uint32 Address;
4235: Uint32 Address_old;
1.1.1.15 root 4236: int FrameCycles, HblCounterVideo, LineCycles;
4237:
4238: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4239:
1.1.1.21 root 4240: LOG_TRACE(TRACE_FDC, "fdc write dma address %x val=0x%02x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4241: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] ,
4242: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4243:
4244: /* Build up 24-bit address from hardware registers */
4245: Address = ((Uint32)STMemory_ReadByte(0xff8609)<<16) | ((Uint32)STMemory_ReadByte(0xff860b)<<8) | (Uint32)STMemory_ReadByte(0xff860d);
4246:
4247: /* On STF, DMA address uses a "ripple carry adder" which can trigger when writing to $ff860b/0d */
4248: if ( Config_IsMachineST() )
1.1.1.20 root 4249: {
1.1.1.21 root 4250: Address_old = FDC_GetDMAAddress();
1.1.1.18 root 4251:
1.1.1.21 root 4252: if ( ( Address_old & 0x80 ) && !( Address & 0x80 ) ) /* Bit 7 goes from 1 to 0 */
4253: {
4254: Address += 0x100; /* Increase middle byte (and high byte if needed) */
4255: //fprintf ( stderr , "fdc write dma address detect ripple carry at $ff860d old=0x%x new=0x%x\n" , Address_old , Address );
4256: 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" ,
4257: Address_old , Address ,
4258: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4259: }
4260: else if ( ( Address_old & 0x8000 ) && !( Address & 0x8000 ) ) /* Bit 15 goes from 1 to 0 */
4261: {
4262: Address += 0x10000; /* Increase high byte */
4263: //fprintf ( stderr , "fdc write dma address detect ripple carry at $ff860b old=0x%x new=0x%x\n" , Address_old , Address );
4264: 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" ,
4265: Address_old , Address ,
4266: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4267: }
4268: }
1.1.1.18 root 4269:
1.1.1.21 root 4270: /* Store new address as DMA address and update $ff8609/0b/0d */
4271: FDC_WriteDMAAddress ( Address );
1.1.1.15 root 4272: }
4273:
4274:
4275: /*-----------------------------------------------------------------------*/
4276: /**
1.1.1.21 root 4277: * Get DMA address used to transfer data between FDC/HDC and RAM
1.1.1.9 root 4278: */
1.1.1.15 root 4279: Uint32 FDC_GetDMAAddress(void)
1.1 root 4280: {
1.1.1.21 root 4281: return FDC_DMA_Address;
1.1 root 4282: }
4283:
1.1.1.2 root 4284:
4285: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4286: /**
1.1.1.21 root 4287: * Write a new address to the FDC/HDC DMA address registers at $ff8909/0b/0d
1.1.1.18 root 4288: * As verified on real STF, DMA address high byte written at $ff8609 is masked
4289: * with 0x3f :
4290: * move.b #$ff,$ff8609
4291: * move.b $ff8609,d0 -> d0=$3f
4292: * DMA address must also be word-aligned, low byte at $ff860d is masked with 0xfe
4293: * move.b #$ff,$ff860d
4294: * move.b $ff860d,d0 -> d0=$fe
1.1.1.9 root 4295: */
1.1.1.15 root 4296: void FDC_WriteDMAAddress ( Uint32 Address )
1.1 root 4297: {
1.1.1.12 root 4298: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.21 root 4299: Uint32 dma_mask;
1.1.1.12 root 4300:
4301: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4302:
1.1.1.21 root 4303: 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 4304: Address , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4305:
1.1.1.21 root 4306: /* Mask DMA address : */
4307: /* - DMA address must be word-aligned, bit 0 at $ff860d is always 0 */
4308: /* - On STF/STE machines limited to 4MB of RAM, DMA address is also limited to $3fffff */
4309: dma_mask = 0xff00fffe | ( DMA_MaskAddressHigh() << 16 ); /* Force bit 0 to 0 */
4310: FDC_DMA_Address = Address & dma_mask;
1.1.1.18 root 4311:
1.1.1.6 root 4312: /* Store as 24-bit address */
1.1.1.21 root 4313: STMemory_WriteByte(0xff8609, FDC_DMA_Address>>16);
4314: STMemory_WriteByte(0xff860b, FDC_DMA_Address>>8);
4315: STMemory_WriteByte(0xff860d, FDC_DMA_Address);
1.1 root 4316: }
4317:
1.1.1.2 root 4318:
4319: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4320: /**
1.1.1.18 root 4321: * Return the number of FDC cycles to wait before reaching the next
4322: * sector's ID Field in the track ($A1 $A1 $A1 $FE TR SIDE SR LEN CRC1 CRC2)
4323: * If no ID Field is found before the end of the track, we use the 1st
4324: * ID Field of the track (which simulates a full spin of the floppy).
4325: * We also store the next sector's number into NextSector_ID_Field_SR,
1.1.1.20 root 4326: * the next track's number into NextSector_ID_Field_TR, the next sector's length
1.1.1.18 root 4327: * into NextSector_ID_Field_LEN and if the CRC is correct or not into
4328: * NextSector_ID_Field_CRC_OK.
4329: * This function assumes some 512 byte sectors stored in ascending
4330: * order (for ST/MSA)
4331: * If there's no available drive/floppy, we return -1
1.1.1.9 root 4332: */
1.1.1.18 root 4333: static int FDC_NextSectorID_FdcCycles_ST ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side )
1.1 root 4334: {
1.1.1.18 root 4335: int CurrentPos;
4336: int MaxSector;
4337: int TrackPos;
4338: int i;
4339: int NextSector;
4340: int NbBytes;
4341:
4342: CurrentPos = FDC_IndexPulse_GetCurrentPos_NbBytes ();
4343: if ( CurrentPos < 0 ) /* No drive/floppy available at the moment */
4344: return -1;
4345:
4346: if ( ( Side == 1 ) && ( NumberOfHeads == 1 ) ) /* Can't read side 1 on a single sided drive */
4347: return -1;
4348:
1.1.1.19 root 4349: if ( Track >= FDC_GetTracksPerDisk ( Drive ) ) /* Try to access a non existing track */
4350: return -1;
4351:
1.1.1.18 root 4352: MaxSector = FDC_GetSectorsPerTrack ( Drive , Track , Side );
4353: TrackPos = FDC_TRACK_LAYOUT_STANDARD_GAP1; /* Position of 1st raw sector */
4354: TrackPos += FDC_TRACK_LAYOUT_STANDARD_GAP2; /* Position of ID Field in 1st raw sector */
4355:
4356: /* Compare CurrentPos with each sector's position in ascending order */
4357: for ( i=0 ; i<MaxSector ; i++ )
4358: {
4359: if ( CurrentPos < TrackPos )
4360: break; /* We found the next sector */
4361: else
4362: TrackPos += FDC_TRACK_LAYOUT_STANDARD_RAW_SECTOR_512;
4363: }
4364:
4365: if ( i == MaxSector ) /* CurrentPos is after the last ID Field of this track */
4366: {
4367: /* Reach end of track (new index pulse), then go to sector 1 */
4368: NbBytes = FDC_GetBytesPerTrack ( Drive ) - CurrentPos + FDC_TRACK_LAYOUT_STANDARD_GAP1 + FDC_TRACK_LAYOUT_STANDARD_GAP2;
4369: NextSector = 1;
4370: }
4371: else /* There's an ID Field before end of track */
4372: {
4373: NbBytes = TrackPos - CurrentPos;
4374: NextSector = i+1;
4375: }
4376:
4377: //fprintf ( stderr , "fdc bytes next sector pos=%d trpos=%d nbbytes=%d maxsr=%d nextsr=%d\n" , CurrentPos, TrackPos, NbBytes, MaxSector, NextSector );
4378: FDC.NextSector_ID_Field_TR = Track;
4379: FDC.NextSector_ID_Field_SR = NextSector;
4380: FDC.NextSector_ID_Field_LEN = FDC_SECTOR_SIZE_512; /* ST/MSA have 512 bytes per sector */
4381: FDC.NextSector_ID_Field_CRC_OK = 1; /* CRC is always correct for ST/MSA */
4382:
4383: return FDC_TransferByte_FdcCycles ( NbBytes );
4384: }
4385:
4386:
4387: /*-----------------------------------------------------------------------*/
4388: /**
4389: * Return the value of the track number in the next ID field set by
4390: * FDC_NextSectorID_FdcCycles_ST (for ST/MSA, it's always HeadTrack value)
4391: */
4392: static Uint8 FDC_NextSectorID_TR_ST ( void )
4393: {
4394: return FDC.NextSector_ID_Field_TR;
4395: }
4396:
4397:
4398: /*-----------------------------------------------------------------------*/
4399: /**
4400: * Return the value of the sector number in the next ID field set by
4401: * FDC_NextSectorID_FdcCycles_ST.
4402: */
4403: static Uint8 FDC_NextSectorID_SR_ST ( void )
4404: {
4405: return FDC.NextSector_ID_Field_SR;
4406: }
4407:
4408:
4409: /*-----------------------------------------------------------------------*/
4410: /**
4411: * Return the value of the sector's length in the next ID field set by
4412: * FDC_NextSectorID_FdcCycles_ST.
4413: * For ST/MSA, it's always 2 (512 bytes per sector)
4414: */
4415: static Uint8 FDC_NextSectorID_LEN_ST ( void )
4416: {
4417: return FDC.NextSector_ID_Field_LEN;
4418: }
4419:
4420:
4421: /*-----------------------------------------------------------------------*/
4422: /**
4423: * Return the status of the CRC in the next ID field set by
4424: * FDC_NextSectorID_FdcCycles_ST.
4425: * If '0', CRC is bad, else CRC is OK
4426: * For ST/MSA, CRC is always OK
4427: */
4428: static Uint8 FDC_NextSectorID_CRC_OK_ST ( void )
4429: {
4430: return FDC.NextSector_ID_Field_CRC_OK;
4431: }
4432:
4433:
4434: /*-----------------------------------------------------------------------*/
4435: /**
4436: * Read a sector from a floppy image in ST format (used in type II command)
4437: * Each byte of the sector is added to the FDC buffer with a default timing
4438: * (32 microsec)
4439: * Return 0 if sector was read without error, or FDC_STR_BIT_RNF if an error occurred
4440: * (FDC_STR_BIT_CRC_ERROR and FDC_STR_BIT_RECORD_TYPE are always set 0 for ST/MSA)
4441: */
4442: static Uint8 FDC_ReadSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int *pSectorSize )
4443: {
4444: int FrameCycles, HblCounterVideo, LineCycles;
4445: int i;
4446: Uint8 *pSectorData;
1.1.1.12 root 4447:
4448: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4449:
1.1.1.18 root 4450: 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" ,
4451: FDC_GetDMAAddress(), Drive, Track, Sector, Side,
1.1.1.12 root 4452: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4453:
1.1.1.18 root 4454: /* Get a pointer to the sector's data and convert into bytes/timings */
4455: if ( Floppy_ReadSectors ( Drive, &pSectorData, Sector, Track, Side, 1, NULL, pSectorSize ) )
4456: {
4457: for ( i=0 ; i<*pSectorSize ; i++ )
4458: FDC_Buffer_Add ( pSectorData[ i ] );
4459: return 0; /* No error */
4460: }
1.1 root 4461:
1.1.1.6 root 4462: /* Failed */
1.1.1.12 root 4463: LOG_TRACE(TRACE_FDC, "fdc read sector failed\n" );
1.1.1.18 root 4464: return FDC_STR_BIT_RNF;
1.1 root 4465: }
4466:
1.1.1.2 root 4467:
4468: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4469: /**
1.1.1.18 root 4470: * Write a sector to a floppy image in ST format (used in type II command)
4471: * Bytes were added to the FDC Buffer with a default timing (32 microsec) ;
4472: * we copy only the bytes into a temporary buffer, and write this buffer
4473: * to the floppy image.
1.1.1.15 root 4474: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
4475: * and some '0' bytes will be written to the disk.
1.1.1.18 root 4476: * Return 0 if sector was written without error, or FDC_STR_BIT_RNF if an error occurred
1.1.1.9 root 4477: */
1.1.1.18 root 4478: static Uint8 FDC_WriteSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize )
1.1 root 4479: {
1.1.1.18 root 4480: int FrameCycles, HblCounterVideo, LineCycles;
4481: int i;
4482: Uint8 SectorData[ 1024 ]; /* max sector size for WD1772 */
1.1.1.12 root 4483:
4484: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4485:
1.1.1.18 root 4486: 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" ,
4487: FDC_GetDMAAddress(), Drive, Track, Sector, Side,
1.1.1.12 root 4488: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4489:
1.1.1.18 root 4490: /* Get the sector's data (ignore timings) */
4491: for ( i=0 ; i<SectorSize ; i++ )
4492: SectorData[ i ] = FDC_Buffer_Read_Byte_pos ( i );
4493:
4494: /* Write the sector's data */
4495: if ( Floppy_WriteSectors ( Drive, SectorData, Sector, Track, Side, 1, NULL, NULL ) )
4496: return 0; /* No error */
4497:
4498: /* Failed */
4499: LOG_TRACE(TRACE_FDC, "fdc write sector failed\n" );
4500: return FDC_STR_BIT_RNF;
4501: }
4502:
4503:
4504: /*-----------------------------------------------------------------------*/
4505: /**
4506: * Read an address field from a floppy image in ST format (used in type III command)
4507: * As ST images don't have address field, we compute a standard one based
4508: * on the current track/sector/side.
4509: * Each byte of the ID field is added to the FDC buffer with a default timing
4510: * (32 microsec)
4511: * Always return 0 = OK (FDC_STR_BIT_CRC_ERROR and FDC_STR_BIT_RNF are
4512: * always set 0 for ST/MSA)
4513: */
4514: static Uint8 FDC_ReadAddress_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side )
4515: {
4516: int FrameCycles, HblCounterVideo, LineCycles;
4517: Uint8 buf_id[ 10 ]; /* 3 SYNC + IAM + TR + SIDE + SECTOR + SIZE + CRC1 + CRC2 */
4518: Uint8 *p;
4519: Uint16 CRC;
4520: int i;
4521:
4522: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4523:
1.1.1.19 root 4524: /* If trying to access address field on a non existing track, then return RNF */
4525: if ( Track >= FDC_GetTracksPerDisk ( Drive ) )
4526: {
4527: fprintf ( stderr , "fdc : read address drive=%d track=%d side=%d, but maxtrack=%d, return RNF\n" ,
4528: Drive , Track , Side , FDC_GetTracksPerDisk ( Drive ) );
4529: return STX_SECTOR_FLAG_RNF; /* Should not happen if FDC_NextSectorID_FdcCycles_ST succeeded before */
4530: }
4531:
1.1.1.18 root 4532: p = buf_id;
4533:
4534: *p++ = 0xa1; /* SYNC bytes and IAM byte are included in the CRC */
4535: *p++ = 0xa1;
4536: *p++ = 0xa1;
4537: *p++ = 0xfe;
4538: *p++ = Track;
4539: *p++ = Side;
4540: *p++ = Sector;
4541: *p++ = FDC_SECTOR_SIZE_512; /* ST/MSA images are 512 bytes per sector */
4542:
4543: FDC_CRC16 ( buf_id , 8 , &CRC );
4544:
4545: *p++ = CRC >> 8;
4546: *p++ = CRC & 0xff;
4547:
4548: /* 6 bytes per ID field, don't return the 3 x $A1 and $FE */
4549: for ( i=4 ; i<10 ; i++ )
4550: FDC_Buffer_Add ( buf_id[ i ] );
4551:
4552: 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",
4553: buf_id[4] , buf_id[5] , buf_id[6] , buf_id[7] , buf_id[8] , buf_id[9] ,
4554: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
4555:
4556: return 0; /* No error */
4557: }
4558:
4559:
4560: /*-----------------------------------------------------------------------*/
4561: /**
4562: * Read a track from a floppy image in ST format (used in type III command)
4563: * As ST images don't have gaps,sync,..., we compute a standard track based
4564: * on the current track/side.
4565: * Each byte of the track is added to the FDC buffer with a default timing
4566: * (32 microsec)
4567: * Always return 0 = OK (we fill the track buffer in all cases)
4568: */
4569: static Uint8 FDC_ReadTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side )
4570: {
4571: int FrameCycles, HblCounterVideo, LineCycles;
4572: Uint8 buf_id[ 10 ]; /* 3 SYNC + IAM + TR + SIDE + SECTOR + SIZE + CRC1 + CRC2 */
4573: Uint8 *p;
4574: Uint16 CRC;
4575: int Sector;
4576: Uint8 *pSectorData;
4577: int SectorSize;
4578: int i;
4579:
4580: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4581:
4582: 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" ,
4583: Drive, Track, Side, nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4584:
1.1.1.19 root 4585: /* If trying to access a non existing track, then return an empty / not formatted track */
4586: if ( Track >= FDC_GetTracksPerDisk ( Drive ) )
4587: {
4588: fprintf ( stderr , "fdc : read track drive=%d track=%d side=%d, but maxtrack=%d, building an unformatted track\n" ,
4589: Drive , Track , Side , FDC_GetTracksPerDisk ( Drive ) );
4590: for ( i=0 ; i<FDC_GetBytesPerTrack ( Drive ) ; i++ )
4591: FDC_Buffer_Add ( rand() & 0xff ); /* Fill the track buffer with random bytes */
4592: return 0;
4593: }
1.1.1.18 root 4594:
4595: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP1 ; i++ ) /* GAP1 */
4596: FDC_Buffer_Add ( 0x4e );
4597:
4598: for ( Sector=1 ; Sector <= FDC_GetSectorsPerTrack ( Drive , Track , Side ) ; Sector++ )
1.1.1.6 root 4599: {
1.1.1.18 root 4600: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP2 ; i++ ) /* GAP2 */
4601: FDC_Buffer_Add ( 0x00 );
4602:
4603: /* Add the ID field for the sector */
4604: p = buf_id;
4605: for ( i=0 ; i<3 ; i++ ) *p++ = 0xa1; /* SYNC (write $F5) */
4606: *p++ = 0xfe; /* Index Address Mark */
4607: *p++ = Track; /* Track */
4608: *p++ = Side; /* Side */
4609: *p++ = Sector; /* Sector */
4610: *p++ = FDC_SECTOR_SIZE_512; /* 512 bytes/sector for ST/MSA */
4611: FDC_CRC16 ( buf_id , 8 , &CRC );
4612: *p++ = CRC >> 8; /* CRC1 (write $F7) */
4613: *p++ = CRC & 0xff; /* CRC2 */
4614:
4615: for ( i=0 ; i<10 ; i++ ) /* Add the ID field to the track data */
4616: FDC_Buffer_Add ( buf_id[ i ] );
4617:
4618: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3a ; i++ ) /* GAP3a */
4619: FDC_Buffer_Add ( 0x4e );
4620: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3b ; i++ ) /* GAP3b */
4621: FDC_Buffer_Add ( 0x00 );
4622:
4623: /* Add the data for the sector + build the CRC */
4624: crc16_reset ( &CRC );
4625: for ( i=0 ; i<3 ; i++ )
4626: {
4627: FDC_Buffer_Add ( 0xa1 ); /* SYNC (write $F5) */
4628: crc16_add_byte ( &CRC , 0xa1 );
4629: }
4630:
4631: FDC_Buffer_Add ( 0xfb ); /* Data Address Mark */
4632: crc16_add_byte ( &CRC , 0xfb );
4633:
4634: if ( Floppy_ReadSectors ( Drive, &pSectorData, Sector, Track, Side, 1, NULL, &SectorSize ) )
4635: {
4636: for ( i=0 ; i<SectorSize ; i++ )
4637: {
4638: FDC_Buffer_Add ( pSectorData[ i ] );
4639: crc16_add_byte ( &CRC , pSectorData[ i ] );
4640: }
4641: }
4642: else
4643: {
4644: /* In case of error, we put some 0x00 bytes, but this case should */
4645: /* not happen with ST/MSA disk images, all sectors should be present on each track */
4646: for ( i=0 ; i<512 ; i++ )
4647: {
4648: FDC_Buffer_Add ( 0x00 );
4649: crc16_add_byte ( &CRC , 0x00 );
4650: }
4651: }
4652:
4653: FDC_Buffer_Add ( CRC >> 8 ); /* CRC1 (write $F7) */
4654: FDC_Buffer_Add ( CRC & 0xff ); /* CRC2 */
4655:
4656: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP4 ; i++ ) /* GAP4 */
4657: FDC_Buffer_Add ( 0x4e );
1.1.1.6 root 4658: }
1.1.1.18 root 4659:
4660: while ( FDC_Buffer_Get_Size () < FDC_GetBytesPerTrack ( Drive ) ) /* Complete the track buffer */
4661: FDC_Buffer_Add ( 0x4e ); /* GAP5 */
4662:
4663: return 0; /* No error */
4664: }
4665:
4666:
4667: /*-----------------------------------------------------------------------*/
4668: /**
4669: * Write a track to a floppy image in ST format (used in type III command)
4670: * Bytes were added to the FDC Buffer with a default timing (32 microsec) ;
4671: * we copy only the bytes into a temporary buffer, and write this buffer
4672: * to the floppy image.
4673: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
4674: * and some '0' bytes will be written to the disk.
4675: * Return 0 if track was written without error, or FDC_STR_BIT_LOST_DATA if an error occurred
4676: *
4677: * TODO : this is not supported for ST/MSA at the moment, so we return FDC_STR_BIT_LOST_DATA
4678: */
4679: static Uint8 FDC_WriteTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side , int TrackSize )
4680: {
4681: int FrameCycles, HblCounterVideo, LineCycles;
4682:
4683: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4684:
4685: 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" ,
4686: FDC_GetDMAAddress(), Drive, Track, Side,
4687: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4688:
4689: Log_Printf(LOG_TODO, "FDC type III command 'write track' is not supported with ST/MSA files\n");
4690:
4691: /* TODO : "Write track" should write all the sectors after extracting them from the track data ? */
1.1 root 4692:
1.1.1.6 root 4693: /* Failed */
1.1.1.18 root 4694: LOG_TRACE(TRACE_FDC, "fdc write track failed\n" );
4695: return FDC_STR_BIT_LOST_DATA;
1.1 root 4696: }
4697:
4698:
1.1.1.2 root 4699: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4700: /**
4701: * Write to floppy mode/control (?) register (0xff860F).
4702: * Used on Falcon only!
4703: * FIXME: I've found hardly any documentation about this register, only
4704: * the following description of the bits:
4705: *
4706: * __________54__10 Floppy Controll-Register
4707: * || ||
4708: * || |+- Prescaler 1
4709: * || +-- Media detect 1
4710: * |+----- Prescaler 2
4711: * +------ Media detect 2
4712: *
4713: * For DD - disks: 0x00
4714: * For HD - disks: 0x03
4715: * for ED - disks: 0x30 (not supported by TOS)
4716: */
1.1.1.15 root 4717: void FDC_FloppyMode_WriteByte ( void )
1.1.1.9 root 4718: {
4719: // printf("Write to floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4720: }
4721:
4722:
4723: /*-----------------------------------------------------------------------*/
4724: /**
4725: * Read from floppy mode/control (?) register (0xff860F).
4726: * Used on Falcon only!
4727: * FIXME: I've found hardly any documentation about this register, only
4728: * the following description of the bits:
4729: *
4730: * ________76543210 Floppy Controll-Register
4731: * ||||||||
4732: * |||||||+- Prescaler 1
4733: * ||||||+-- Mode select 1
4734: * |||||+--- Media detect 1
4735: * ||||+---- accessed during DMA transfers (?)
4736: * |||+----- Prescaler 2
4737: * ||+------ Mode select 2
4738: * |+------- Media detect 2
4739: * +-------- Disk changed
4740: */
1.1.1.15 root 4741: void FDC_FloppyMode_ReadByte ( void )
1.1.1.9 root 4742: {
4743: IoMem_WriteByte(0xff860f, 0x80); // FIXME: Is this ok?
4744: // printf("Read from floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4745: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.