1 /*
2 Hatari - fdc.c
3
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.
6
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).
16 */
17
18 const char FDC_fileid[] = "Hatari fdc.c : " __DATE__ " " __TIME__;
19
20 #include <inttypes.h>
21
22 #include "main.h"
23 #include "configuration.h"
24 #include "fdc.h"
25 #include "hdc.h"
26 #include "floppy.h"
27 #include "floppy_ipf.h"
28 #include "floppy_stx.h"
29 #include "ioMem.h"
30 #include "log.h"
31 #include "m68000.h"
32 #include "memorySnapShot.h"
33 #include "mfp.h"
34 #include "psg.h"
35 #include "stMemory.h"
36 #include "screen.h"
37 #include "video.h"
38 #include "clocks_timings.h"
39 #include "utils.h"
40 #include "statusbar.h"
41
42
43 /*
44 Floppy Disk Controller
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
72
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
83 ACSI DMA and Floppy Disk Controller(FDC)
84 0xff8604 - information from file '1772.info.txt, by David Gahris' (register r0)
85 Word access only, but only lower byte (ff8605) is used
86 (write) - Disk controller
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
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.
100 NOTE : on ST, Lost Data is never set because the DMA always handles the data request signal.
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.
111 After a type I command, this bit is constantly updated an give the current value of the WPT signal.
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
131 NOTE [NP] : The DMA is connected to the FDC and its Data Register, each time a DRQ
132 is made by the FDC, it's handled by the DMA through its internal 16 bytes buffer.
133 This means that in the case of the Atari ST the LOST_DATA bit will never be set
134 in the Status Register (but data can be lost if FDC_DMA.SectorCount=0 as there
135 will be no transfer between DMA and RAM).
136 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)
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
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.
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
209 During these transition phases between "ejected" and "inserted", we force the WPT signal to 1,
210 depending on which transition we're emulating (see Floppy_DriveTransitionUpdateState()) :
211 - Ejecting : WPT will be X, then 1
212 - Inserting : WPT will be 1, then X
213
214 */
215
216 /*-----------------------------------------------------------------------*/
217
218 /* Status register */
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 */
234 enum
235 {
236 FDCEMU_CMD_NULL = 0,
237 /* Type I */
238 FDCEMU_CMD_RESTORE,
239 FDCEMU_CMD_SEEK,
240 FDCEMU_CMD_STEP, /* Also used for STEP IN and STEP OUT */
241 /* Type II */
242 FDCEMU_CMD_READSECTORS,
243 FDCEMU_CMD_WRITESECTORS,
244 /* Type III */
245 FDCEMU_CMD_READADDRESS,
246 FDCEMU_CMD_READTRACK,
247 FDCEMU_CMD_WRITETRACK,
248
249 /* Other fake commands used internally */
250 FDCEMU_CMD_MOTOR_STOP
251 };
252
253
254 /* FDC Emulation commands' sub-states used in FDC.CommandState */
255 enum
256 {
257 FDCEMU_RUN_NULL = 0,
258
259 /* Restore */
260 FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO,
261 FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP,
262 FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON,
263 FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP,
264 FDCEMU_RUN_RESTORE_VERIFY,
265 FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK,
266 FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER,
267 FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER,
268 FDCEMU_RUN_RESTORE_COMPLETE,
269 /* Seek */
270 FDCEMU_RUN_SEEK_TOTRACK,
271 FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP,
272 FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON,
273 FDCEMU_RUN_SEEK_VERIFY,
274 FDCEMU_RUN_SEEK_VERIFY_HEAD_OK,
275 FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER,
276 FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER,
277 FDCEMU_RUN_SEEK_COMPLETE,
278 /* Step / Step In / Step Out */
279 FDCEMU_RUN_STEP_ONCE,
280 FDCEMU_RUN_STEP_ONCE_SPIN_UP,
281 FDCEMU_RUN_STEP_ONCE_MOTOR_ON,
282 FDCEMU_RUN_STEP_VERIFY,
283 FDCEMU_RUN_STEP_VERIFY_HEAD_OK,
284 FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER,
285 FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER,
286 FDCEMU_RUN_STEP_COMPLETE,
287 /* Read Sector */
288 FDCEMU_RUN_READSECTORS_READDATA,
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,
293 FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER,
294 FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START,
295 FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP,
296 FDCEMU_RUN_READSECTORS_CRC,
297 FDCEMU_RUN_READSECTORS_MULTI,
298 FDCEMU_RUN_READSECTORS_RNF,
299 FDCEMU_RUN_READSECTORS_COMPLETE,
300 /* Write Sector */
301 FDCEMU_RUN_WRITESECTORS_WRITEDATA,
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,
306 FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER,
307 FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START,
308 FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP,
309 FDCEMU_RUN_WRITESECTORS_CRC,
310 FDCEMU_RUN_WRITESECTORS_MULTI,
311 FDCEMU_RUN_WRITESECTORS_RNF,
312 FDCEMU_RUN_WRITESECTORS_COMPLETE,
313 /* Read Address */
314 FDCEMU_RUN_READADDRESS,
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,
322 FDCEMU_RUN_READADDRESS_COMPLETE,
323 /* Read Track */
324 FDCEMU_RUN_READTRACK,
325 FDCEMU_RUN_READTRACK_SPIN_UP,
326 FDCEMU_RUN_READTRACK_HEAD_LOAD,
327 FDCEMU_RUN_READTRACK_MOTOR_ON,
328 FDCEMU_RUN_READTRACK_INDEX,
329 FDCEMU_RUN_READTRACK_TRANSFER_LOOP,
330 FDCEMU_RUN_READTRACK_COMPLETE,
331 /* Write Track */
332 FDCEMU_RUN_WRITETRACK,
333 FDCEMU_RUN_WRITETRACK_SPIN_UP,
334 FDCEMU_RUN_WRITETRACK_HEAD_LOAD,
335 FDCEMU_RUN_WRITETRACK_MOTOR_ON,
336 FDCEMU_RUN_WRITETRACK_INDEX,
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
344 };
345
346
347
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
372
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 */
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 */
377 //#define FDC_TRACK_BYTES_STANDARD ( ( FDC_BITRATE_STANDARD / 8 ) / ( FDC_RPM_STANDARD / 60 ) ) /* 6250 bytes */
378 #define FDC_TRACK_BYTES_STANDARD 6268
379
380 #define FDC_TRANSFER_BYTES_US( n ) ( ( n ) * 8 * 1000000.L / FDC_BITRATE_STANDARD ) /* micro sec to read/write 'n' bytes in the WD1772 */
381
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
386
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 */
389
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 )
392
393
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 */
396 /* (~740 cpu cycles @ 8 Mhz). [NP] : this was measured on a 520 STF */
397 /* and avoid returning immediately when command has no effect */
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
409
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
412
413
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 */
416
417 #define FDC_PHYSICAL_MAX_TRACK 90 /* Head can't go beyond 90 tracks */
418
419
420 #define FDC_STEP_RATE ( FDC.CR & 0x03 ) /* Bits 0 and 1 of the current type I command */
421
422 int FDC_StepRate_ms[] = { 6 , 12 , 2 , 3 }; /* Controlled by bits 1 and 0 (r1/r0) in type I commands */
423
424
425
426
427 #define FDC_FAST_FDC_FACTOR 10 /* Divide all delays by this value when --fastfdc is used */
428
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
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
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
453 /* Other variables */
454 int Command; /* FDC emulation command currently being executed */
455 int CommandState; /* Current state for the running command */
456 Uint8 CommandType; /* Type of latest FDC command (1,2,3 or 4) */
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 */
467
468 int EmulationMode; /* FDC_EMULATION_MODE_INTERNAL or FDC_EMULATION_MODE_IPF */
469 } FDC_STRUCT;
470
471
472 typedef struct {
473 /* DMA internal registers */
474 Uint16 Status;
475 Uint16 Mode;
476 Uint16 SectorCount;
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) */
483
484 /* Variables to handle our DMA buffer */
485 int PosInBuffer;
486 int PosInBufferTransfer; /* FIXME REMOVE */
487 int BytesToTransfer;
488 } FDC_DMA_STRUCT;
489
490 Uint32 FDC_DMA_Address; // TODO : move into FDC_DMA_STRUCT (which will change memory snapshot)
491
492
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
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 */
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 */
527
528 static Uint8 DMADiskWorkSpace[ FDC_TRACK_BYTES_STANDARD*4+1000 ];/* Workspace used to transfer bytes between floppy and DMA */
529 /* It should be large enough to contain a whole track */
530 /* We use a x4 factor when we need to simulate HD and ED too */
531
532
533
534 /*--------------------------------------------------------------*/
535 /* Local functions prototypes */
536 /*--------------------------------------------------------------*/
537
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 );
543 static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC );
544
545 static void FDC_ResetDMA ( void );
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 );
550 static int FDC_GetTracksPerDisk ( int Drive );
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 );
556
557 static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits );
558 static int FDC_CmdCompleteCommon ( bool DoInt );
559 static bool FDC_VerifyTrack ( void );
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 );
568 static int FDC_UpdateWriteTrackCmd ( void );
569
570 static bool FDC_Set_MotorON ( Uint8 FDC_CR );
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 );
581 static int FDC_TypeIV_ForceInterrupt ( void );
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
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 );
605
606
607 /*-----------------------------------------------------------------------*/
608 /**
609 * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
610 */
FDC_MemorySnapShot_Capture(bool bSave)611 void FDC_MemorySnapShot_Capture(bool bSave)
612 {
613 MemorySnapShot_Store(&FDC, sizeof(FDC));
614 MemorySnapShot_Store(&FDC_DMA, sizeof(FDC_DMA));
615 MemorySnapShot_Store(&FDC_DRIVES, sizeof(FDC_DRIVES));
616 MemorySnapShot_Store(&FDC_BUFFER, sizeof(FDC_BUFFER_STRUCT));
617
618 MemorySnapShot_Store(DMADiskWorkSpace, sizeof(DMADiskWorkSpace));
619 }
620
621
622 /*-----------------------------------------------------------------------*/
623 /**
624 * Change the color of the drive's led color in the statusbar, depending
625 * on the state of the busy bit in STR
626 */
FDC_Drive_Set_BusyLed(Uint8 STR)627 void FDC_Drive_Set_BusyLed ( Uint8 STR )
628 {
629 //fprintf ( stderr ,"fdc led %d %x\n" , FDC.DriveSelSignal , STR );
630 if ( FDC.DriveSelSignal < 0 )
631 return; /* no drive selected */
632
633 if ( STR & FDC_STR_BIT_BUSY )
634 Statusbar_SetFloppyLed ( FDC.DriveSelSignal , LED_STATE_ON_BUSY );
635 else
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 */
FDC_Get_Statusbar_Text(char * text,size_t maxlen)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 */
680 else if ( ( Command & 0xe0 ) == 0x60 ) strcpy ( CommandText , "SO" ); /* Step Out */
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;
691 }
692
693
694 /*-----------------------------------------------------------------------*/
695 /**
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)
698 */
FDC_DelayToFdcCycles(Uint32 Delay_micro)699 static Uint32 FDC_DelayToFdcCycles ( Uint32 Delay_micro )
700 {
701 Uint32 FdcCycles;
702
703 FdcCycles = (Uint32) ( ( (Uint64) FDC_CLOCK_STANDARD * Delay_micro ) / 1000000 );
704
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
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.
716 * (for Falcon, we multiply cycles by 2 to simulate a freq in the 8 MHz range)
717 */
FDC_FdcCyclesToCpuCycles(Uint32 FdcCycles)718 static Uint32 FDC_FdcCyclesToCpuCycles ( Uint32 FdcCycles )
719 {
720 Uint32 CpuCycles;
721
722 /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
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 */
725 if (Config_IsMachineFalcon())
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 );
730 CpuCycles <<= nCpuFreqShift; /* Convert to x1 or x2 or x4 cpu speed */
731
732 //fprintf ( stderr , "fdc command %d machine %d fdc cycles %d cpu cycles %d\n" , FDC.Command , ConfigureParams.System.nMachineType , FdcCycles , CpuCycles );
733 //if ( Delay==4104) Delay=4166; // 4166 : decade demo
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)
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.
745 */
FDC_CpuCyclesToFdcCycles(Uint32 CpuCycles)746 static Uint32 FDC_CpuCyclesToFdcCycles ( Uint32 CpuCycles )
747 {
748 int FdcCycles;
749
750
751 CpuCycles >>= nCpuFreqShift; /* Compensate for x2 or x4 cpu speed */
752
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 */
759 if (Config_IsMachineFalcon())
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;
764 }
765
766
767 /*-----------------------------------------------------------------------*/
768 /**
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 */
FDC_StartTimer_FdcCycles(int FdcCycles,int InternalCycleOffset)773 static void FDC_StartTimer_FdcCycles ( int FdcCycles , int InternalCycleOffset )
774 {
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
780 CycInt_AddRelativeInterruptWithOffset ( FDC_FdcCyclesToCpuCycles ( FdcCycles ) , INT_CPU_CYCLE , INTERRUPT_FDC , InternalCycleOffset );
781 }
782
783
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.
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.
794 */
FDC_TransferByte_FdcCycles(int NbBytes)795 static int FDC_TransferByte_FdcCycles ( int NbBytes )
796 {
797 //fprintf ( stderr , "fdc state %d transfer %d bytes\n" , FDC.Command , NbBytes );
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
804 return ( NbBytes * FDC_DELAY_CYCLE_MFM_BYTE ) / FDC_DRIVES[ FDC.DriveSelSignal ].Density;
805 }
806
807
808 /*-----------------------------------------------------------------------*/
809 /**
810 * Compute the CRC16 of 'nb' bytes stored in 'buf'.
811 */
FDC_CRC16(Uint8 * buf,int nb,Uint16 * pCRC)812 static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC )
813 {
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 );
823 }
824
825
826 /*-----------------------------------------------------------------------*/
827 /**
828 * Init variables used in FDC and DMA emulation
829 */
FDC_Init(void)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 /**
855 * Reset variables used in FDC and DMA emulation
856 */
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 */
FDC_Reset(bool bCold)865 void FDC_Reset ( bool bCold )
866 {
867 int i;
868
869 LOG_TRACE ( TRACE_FDC , "fdc reset mode=%s\n" , bCold?"cold":"warm" );
870
871 /* Clear out FDC registers */
872 FDC.CR = 0;
873 FDC.STR = 0;
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 }
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;
890 FDC.InterruptCond = 0;
891 FDC.IRQ_Signal = 0;
892 FDC_ClearIRQ(); /* Propagate IRQ signal to MFP GPIP5 */
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 }
899
900 FDC_DMA.Status = 1; /* no DMA error and SectorCount=0 */
901 FDC_DMA.Mode = 0;
902
903 FDC_ResetDMA();
904
905 FDC_Buffer_Reset();
906
907 /* Also reset IPF emulation */
908 IPF_Reset();
909 }
910
911
912 /*-----------------------------------------------------------------------*/
913 /**
914 * Reset DMA (clear internal 16 bytes buffer)
915 *
916 * This is done by 'toggling' bit 8 of the DMA Mode Control register
917 * This will empty the FIFOs and reset Sector Count to 0
918 */
FDC_ResetDMA(void)919 static void FDC_ResetDMA ( void )
920 {
921 int FrameCycles, HblCounterVideo, LineCycles;
922
923 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
924 LOG_TRACE(TRACE_FDC, "fdc reset dma VBL=%d video_cyc=%d %d@%d pc=%x\n",
925 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
926
927 /* Empty FIFO */
928 FDC_DMA.FIFO_Size = 0;
929
930 /* Reset bytes count for current DMA sector */
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) */
933
934 /* Reset internal variables used to handle DMA transfer */
935 FDC_DMA.PosInBuffer = 0;
936 FDC_DMA.PosInBufferTransfer = 0;
937 FDC_DMA.BytesToTransfer = 0;
938
939 /* Reset HDC command status */
940 HDC_ResetCommandStatus();
941 }
942
943
944 /*-----------------------------------------------------------------------*/
945 /**
946 * Set DMA Status at $ff8606
947 *
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
951 */
FDC_SetDMAStatus(bool bError)952 void FDC_SetDMAStatus ( bool bError )
953 {
954 /* Set error bit */
955 if (!bError)
956 FDC_DMA.Status |= 0x1; /* No Error, set bit 0 */
957 else
958 FDC_DMA.Status &= ~0x1; /* Error, clear bit 0 */
959 }
960
961
962 /*-----------------------------------------------------------------------*/
963 /**
964 * Return the value of bit 8 in the FDC's DMA mode control register.
965 * 0=dma read 0x100=dma write
966 */
FDC_DMA_GetModeControl_R_WR(void)967 int FDC_DMA_GetModeControl_R_WR ( void )
968 {
969 return FDC_DMA.Mode & 0x100;
970 }
971
FDC_DMA_GetMode(void)972 int FDC_DMA_GetMode(void)
973 {
974 return FDC_DMA.Mode;
975 }
976
977
978 /*-----------------------------------------------------------------------*/
979 /**
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.
982 *
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
985 * FDC_STR_BIT_LOST_DATA will never be set (but data can be lost if FDC_DMA.SectorCount==0)
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.
996 */
FDC_DMA_FIFO_Push(Uint8 Byte)997 void FDC_DMA_FIFO_Push ( Uint8 Byte )
998 {
999 Uint32 Address;
1000
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;
1005
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 */
1009 FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
1010 return;
1011 }
1012
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 */
1021 Address = FDC_GetDMAAddress();
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 ];
1028
1029 /* Update Sector Count */
1030 FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
1031 if ( FDC_DMA.BytesInSector <= 0 )
1032 {
1033 FDC_DMA.SectorCount--;
1034 FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
1035 }
1036 }
1037
1038
1039 /*-----------------------------------------------------------------------*/
1040 /**
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.
1043 *
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)
1048 *
1049 * NOTE [NP] : as with FDC_DMA_FIFO_Push, this also changes the unused bits at $ff8606
1050 */
FDC_DMA_FIFO_Pull(void)1051 Uint8 FDC_DMA_FIFO_Pull ( void )
1052 {
1053 Uint32 Address;
1054 Uint8 Byte;
1055
1056 //fprintf ( stderr , "fifo pull %d %d %d\n" , FDC_DMA.BytesToTransfer , FDC_DMA.BytesInSector , FDC_DMA.SectorCount );
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 */
1060 FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
1061 return 0; /* Write a '0' byte when dma is off */
1062 }
1063
1064 FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
1065
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
1070 {
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
1088 Byte = FDC_DMA.FIFO [ 0 ]; /* return the 1st byte we just transferred in the FIFO */
1089 }
1090
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;
1095 }
1096
1097
1098 /*-----------------------------------------------------------------------*/
1099 /**
1100 * Return the value of FDC_DMA.SectorCount (used in floppy_ipf.c)
1101 */
FDC_DMA_GetSectorCount(void)1102 int FDC_DMA_GetSectorCount ( void )
1103 {
1104 return FDC_DMA.SectorCount;
1105 }
1106
1107
1108 /*-----------------------------------------------------------------------*/
1109 /**
1110 * Reset the buffer used to transfer data between the FDC and the DMA
1111 */
FDC_Buffer_Reset(void)1112 void FDC_Buffer_Reset ( void )
1113 {
1114 FDC_BUFFER.Size = 0;
1115 FDC_BUFFER.PosRead = 0;
1116 }
1117
1118
1119 /*-----------------------------------------------------------------------*/
1120 /**
1121 * Add a byte to the FDC transfer buffer, using a specific timing
1122 */
FDC_Buffer_Add_Timing(Uint8 Byte,Uint16 Timing)1123 void FDC_Buffer_Add_Timing ( Uint8 Byte , Uint16 Timing )
1124 {
1125 FDC_BUFFER.Data[ FDC_BUFFER.Size ].Byte = Byte;
1126 FDC_BUFFER.Data[ FDC_BUFFER.Size ].Timing = Timing;
1127 FDC_BUFFER.Size++;
1128 }
1129
1130
1131 /*-----------------------------------------------------------------------*/
1132 /**
1133 * Add a byte to the FDC transfer buffer, using a default timing
1134 */
FDC_Buffer_Add(Uint8 Byte)1135 void FDC_Buffer_Add ( Uint8 Byte )
1136 {
1137 FDC_Buffer_Add_Timing ( Byte , FDC_TransferByte_FdcCycles ( 1 ) );
1138 }
1139
1140
1141 /*-----------------------------------------------------------------------*/
1142 /**
1143 * Return the timing needed to transfer the Byte at the current position
1144 */
FDC_Buffer_Read_Timing(void)1145 Uint16 FDC_Buffer_Read_Timing ( void )
1146 {
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;
1149 }
1150
1151
1152 /*-----------------------------------------------------------------------*/
1153 /**
1154 * Return the Byte at the current position and increment position
1155 */
FDC_Buffer_Read_Byte(void)1156 Uint8 FDC_Buffer_Read_Byte ( void )
1157 {
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;
1160 }
1161
1162
1163 /*-----------------------------------------------------------------------*/
1164 /**
1165 * Return the Byte at a given position
1166 */
FDC_Buffer_Read_Byte_pos(int pos)1167 Uint8 FDC_Buffer_Read_Byte_pos ( int pos )
1168 {
1169 //fprintf ( stderr , "read byte pos %d %x\n" , pos , FDC_BUFFER.Data[ pos ].Byte );
1170 return FDC_BUFFER.Data[ pos ].Byte;
1171 }
1172
1173
1174 /*-----------------------------------------------------------------------*/
1175 /**
1176 * Return the number of bytes stored in FDC_BUFFER
1177 */
FDC_Buffer_Get_Size(void)1178 int FDC_Buffer_Get_Size ( void )
1179 {
1180 return FDC_BUFFER.Size;
1181 }
1182
1183
1184 /*-----------------------------------------------------------------------*/
1185 /**
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)
1199 */
FDC_GetEmulationMode(void)1200 static int FDC_GetEmulationMode ( void )
1201 {
1202 int Mode;
1203
1204 Mode = FDC.EmulationMode; /* Default to previous mode if no drive is selected */
1205
1206 /* Check drive 1 first */
1207 if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x04 ) == 0 )
1208 {
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 */
1214 else
1215 Mode = FDC_EMULATION_MODE_INTERNAL;
1216 }
1217
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 )
1220 {
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;
1228 }
1229
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;
1235 }
1236
1237
1238 /*-----------------------------------------------------------------------*/
1239 /**
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.
1244 */
FDC_UpdateAll(void)1245 static void FDC_UpdateAll(void)
1246 {
1247 FDC_IndexPulse_Update ();
1248 }
1249
1250
1251 /*-----------------------------------------------------------------------*/
1252 /**
1253 * This function is used to enable/disable a drive when
1254 * using the UI or command line parameters
1255 */
FDC_Drive_Set_Enable(int Drive,bool value)1256 void FDC_Drive_Set_Enable ( int Drive , bool value )
1257 {
1258 LOG_TRACE ( TRACE_FDC , "fdc enable drive=%d %s\n" , Drive , value?"on":"off" );
1259
1260 if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1261 FDC_DRIVES[ Drive ].Enabled = value;
1262
1263 /* Also forward change to IPF emulation */
1264 IPF_Drive_Set_Enable ( Drive , value );
1265 }
1266
1267
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 */
FDC_Drive_Set_NumberOfHeads(int Drive,int NbrHeads)1273 void FDC_Drive_Set_NumberOfHeads ( int Drive , int NbrHeads )
1274 {
1275 LOG_TRACE ( TRACE_FDC , "fdc set nbr heads drive=%d %d\n" , Drive , NbrHeads );
1276
1277 if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
1278 FDC_DRIVES[ Drive ].NumberOfHeads = NbrHeads;
1279
1280 /* Also forward change to IPF emulation */
1281 IPF_Drive_Set_DoubleSided ( Drive , NbrHeads==2 ? true : false );
1282 }
1283
1284
1285 /*-----------------------------------------------------------------------*/
1286 /**
1287 * This function is called when a floppy is inserted in a drive
1288 * using the UI or command line parameters
1289 */
FDC_InsertFloppy(int Drive)1290 void FDC_InsertFloppy ( int Drive )
1291 {
1292 LOG_TRACE ( TRACE_FDC , "fdc insert drive=%d\n" , Drive );
1293
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 */
FDC_EjectFloppy(int Drive)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 */
FDC_SetDriveSide(Uint8 io_porta_old,Uint8 io_porta_new)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 */
1372 }
1373 }
1374
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 */
FDC_GetSectorsPerTrack(int Drive,int Track,int Side)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 */
FDC_GetSidesPerDisk(int Drive,int Track)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 /**
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 */
FDC_GetTracksPerDisk(int Drive)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 /**
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 */
FDC_GetDensity(int Drive)1456 static int FDC_GetDensity ( int Drive )
1457 {
1458 Uint16 SectorsPerTrack;
1459
1460 if ( EmulationDrives[ Drive ].bDiskInserted )
1461 {
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 */
1469 }
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 */
FDC_GetBytesPerTrack(int Drive)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 */
FDC_GetCyclesPerRev_FdcCycles(int Drive)1499 static Uint32 FDC_GetCyclesPerRev_FdcCycles ( int Drive )
1500 {
1501 Uint32 FdcCyclesPerRev;
1502
1503 assert(Drive == 0 || Drive == 1);
1504
1505 /* If the inserted disk is an STX, we use the supplied track length to compute cycles per rev */
1506 if ( EmulationDrives[Drive].ImageType == FLOPPY_IMAGE_TYPE_STX )
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 */
1515 if (Config_IsMachineFalcon())
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 */
FDC_IndexPulse_Update(void)1535 static void FDC_IndexPulse_Update(void)
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 */
FDC_IndexPulse_Init(int Drive)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 */
FDC_IndexPulse_GetCurrentPos_FdcCycles(Uint32 * pFdcCyclesPerRev)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 */
FDC_IndexPulse_GetCurrentPos_NbBytes(void)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 */
FDC_IndexPulse_GetState(void)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 */
FDC_NextIndexPulse_FdcCycles(void)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)
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.
1713 */
FDC_SetIRQ(Uint8 IRQ_Source)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 */
1724 MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_LOW );
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)
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.
1754 */
FDC_ClearIRQ(void)1755 void FDC_ClearIRQ ( void )
1756 {
1757 if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED ) == 0 )
1758 {
1759 FDC.IRQ_Signal = 0;
1760 MFP_GPIP_Set_Line_Input ( MFP_GPIP_LINE_FDC_HDC , MFP_GPIP_STATE_HIGH );
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
FDC_ClearHdcIRQ(void)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 }
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 */
FDC_InterruptHandler_Update(void)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 */
FDC_GetCmdType(Uint8 CR)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;
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 */
FDC_Update_STR(Uint8 DisableBits,Uint8 EnableBits)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 */
1889
1890 FDC_Drive_Set_BusyLed ( FDC.STR );
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
1899 * - set interrupt if necessary
1900 * - stop motor after 2 sec
1901 */
FDC_CmdCompleteCommon(bool DoInt)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 */
1911
1912 if ( DoInt )
1913 FDC_SetIRQ ( FDC_IRQ_SOURCE_COMPLETE );
1914
1915 FDC.Command = FDCEMU_CMD_MOTOR_STOP; /* Fake command to stop the motor */
1916 FDC.CommandState = FDCEMU_RUN_MOTOR_STOP;
1917 return FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
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.
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,
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)
1932 * For STX images, verify track might fail on purpose with some protection
1933 */
FDC_VerifyTrack(void)1934 static bool FDC_VerifyTrack ( void )
1935 {
1936 int FrameCycles, HblCounterVideo, LineCycles;
1937 Uint8 Next_TR;
1938 Uint8 Next_CRC_OK;
1939
1940 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1941
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
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 )
1953 {
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 ,
1968 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1969
1970 return false;
1971 }
1972
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 ) ) )
1977 {
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 ,
1980 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1981
1982 return false;
1983 }
1984
1985 /* The track is the correct one */
1986 return true;
1987 }
1988
1989
1990 /*-----------------------------------------------------------------------*/
1991 /**
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)
1995 */
FDC_UpdateMotorStop(void)1996 static int FDC_UpdateMotorStop ( void )
1997 {
1998 int FdcCycles = 0;
1999 int FrameCycles, HblCounterVideo, LineCycles;
2000
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;
2007 /* Continue to next state */
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());
2019
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 */
2023
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;
2030 }
2031
2032
2033 /*-----------------------------------------------------------------------*/
2034 /**
2035 * Run 'RESTORE' command
2036 */
FDC_UpdateRestoreCmd(void)2037 static int FDC_UpdateRestoreCmd ( void )
2038 {
2039 int FdcCycles = 0;
2040 int FrameCycles, HblCounterVideo, LineCycles;
2041
2042 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2043
2044 /* Which command is running? */
2045 switch (FDC.CommandState)
2046 {
2047 case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO:
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
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. */
2073 /* This can happen if no drive is selected or if the selected drive is disabled */
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;
2078 FDC.CommandState = FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP;
2079 /* Continue in the _LOOP state */
2080 case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP:
2081 if ( FDC.TR == 0 ) /* Track 0 not reached after 255 attempts ? */
2082 { /* (this can happen if the drive is disabled) */
2083 FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2084 FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
2085 FdcCycles = FDC_CmdCompleteCommon( true );
2086 break;
2087 }
2088
2089 if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
2090 || ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack != 0 ) ) /* Are we at track zero ? */
2091 {
2092 FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
2093 FDC.TR--; /* One less attempt */
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 );
2097 }
2098 else /* Drive is enabled and head is at track 0 */
2099 {
2100 FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
2101 FDC.TR = 0; /* Update Track Register to 0 */
2102 FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY;
2103 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2104 }
2105 break;
2106 case FDCEMU_RUN_RESTORE_VERIFY:
2107 if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2108 {
2109 FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK;
2110 FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2111 }
2112 else
2113 {
2114 FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
2115 FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2116 }
2117 break;
2118 case FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK:
2119 FDC.IndexPulse_Counter = 0;
2120 /* Head OK, continue and look for sector header */
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 {
2144 FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER;
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;
2152 }
2153 break;
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 }
2168 break;
2169 case FDCEMU_RUN_RESTORE_COMPLETE:
2170 FdcCycles = FDC_CmdCompleteCommon( true );
2171 break;
2172 }
2173
2174 return FdcCycles;
2175 }
2176
2177
2178 /*-----------------------------------------------------------------------*/
2179 /**
2180 * Run 'SEEK' command
2181 */
FDC_UpdateSeekCmd(void)2182 static int FDC_UpdateSeekCmd ( void )
2183 {
2184 int FdcCycles = 0;
2185 int FrameCycles, HblCounterVideo, LineCycles;
2186
2187 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2188
2189 /* Which command is running? */
2190 switch (FDC.CommandState)
2191 {
2192 case FDCEMU_RUN_SEEK_TOTRACK:
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
2215 if ( FDC.TR == FDC.DR ) /* Are we at the selected track ? */
2216 {
2217 FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
2218 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2219 }
2220 else
2221 {
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
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 */
2232
2233 /* Check / move physical head only if an enabled drive is selected */
2234 if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
2235 {
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 }
2241
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 */
2254 }
2255 }
2256
2257 break;
2258 case FDCEMU_RUN_SEEK_VERIFY:
2259 if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2260 {
2261 FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_HEAD_OK;
2262 FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
2263 }
2264 else
2265 {
2266 FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
2267 FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2268 }
2269 break;
2270 case FDCEMU_RUN_SEEK_VERIFY_HEAD_OK:
2271 FDC.IndexPulse_Counter = 0;
2272 /* Head OK, continue and look for sector header */
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 {
2296 FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER;
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;
2304 }
2305 break;
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 }
2320 break;
2321 case FDCEMU_RUN_SEEK_COMPLETE:
2322 FdcCycles = FDC_CmdCompleteCommon( true );
2323 break;
2324 }
2325
2326 return FdcCycles;
2327 }
2328
2329
2330 /*-----------------------------------------------------------------------*/
2331 /**
2332 * Run 'STEP' command
2333 */
FDC_UpdateStepCmd(void)2334 static int FDC_UpdateStepCmd ( void )
2335 {
2336 int FdcCycles = 0;
2337 int FrameCycles, HblCounterVideo, LineCycles;
2338
2339 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2340
2341 /* Which command is running? */
2342 switch (FDC.CommandState)
2343 {
2344 case FDCEMU_RUN_STEP_ONCE:
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
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
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 */
2379
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 */
2382
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 }
2398 else
2399 {
2400 FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
2401 FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2402 }
2403 break;
2404 case FDCEMU_RUN_STEP_VERIFY_HEAD_OK:
2405 FDC.IndexPulse_Counter = 0;
2406 /* Head OK, continue and look for sector header */
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());
2413
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 {
2430 FDC.CommandState = FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER;
2431 FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2432 }
2433 else
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 }
2439 break;
2440 case FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER:
2441 /* Check if the current ID Field matches the track number */
2442 if ( FDC_VerifyTrack () )
2443 {
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;
2447 }
2448 else
2449 {
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;
2453 }
2454 break;
2455 case FDCEMU_RUN_STEP_COMPLETE:
2456 FdcCycles = FDC_CmdCompleteCommon( true );
2457 break;
2458 }
2459
2460 return FdcCycles;
2461 }
2462
2463
2464 /*-----------------------------------------------------------------------*/
2465 /**
2466 * Run 'READ SECTOR/S' command
2467 */
FDC_UpdateReadSectorsCmd(void)2468 static int FDC_UpdateReadSectorsCmd ( void )
2469 {
2470 int FdcCycles = 0;
2471 int SectorSize;
2472 int FrameCycles, HblCounterVideo, LineCycles;
2473 Uint8 Next_TR;
2474 Uint8 Next_SR;
2475 Uint8 Next_CRC_OK;
2476
2477 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2478
2479
2480 /* Which command is running? */
2481 switch (FDC.CommandState)
2482 {
2483 case FDCEMU_RUN_READSECTORS_READDATA:
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 }
2494 break;
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:
2517 /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
2518 if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2519 {
2520 FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
2521 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2522 break;
2523 }
2524
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 ) )
2559 {
2560 FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START;
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 );
2563 }
2564 else
2565 {
2566 /* This is not the ID field we're looking for ; check the next one */
2567 FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER;
2568 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2569 }
2570 break;
2571 case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START:
2572 /* Read a single sector into temporary buffer (512 bytes for ST/MSA) */
2573 FDC_Buffer_Reset();
2574
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 */
2583 {
2584 FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
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 */
2596 }
2597 break;
2598 case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP:
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 () )
2602 {
2603 FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
2604 }
2605 else /* Sector transferred, check the CRC */
2606 {
2607 FDC.CommandState = FDCEMU_RUN_READSECTORS_CRC;
2608 FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Read 2 bytes for CRC */
2609 }
2610 break;
2611 case FDCEMU_RUN_READSECTORS_CRC:
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 */
2629 if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2630 {
2631 FDC.SR++; /* Try to read next sector and set RNF if not possible */
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());
2637 }
2638 else /* Multi=0, stop here with no error */
2639 {
2640 FDC.CommandState = FDCEMU_RUN_READSECTORS_COMPLETE;
2641 FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2642 }
2643 break;
2644 case FDCEMU_RUN_READSECTORS_RNF:
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());
2647
2648 FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2649 FdcCycles = FDC_CmdCompleteCommon( true );
2650 break;
2651 case FDCEMU_RUN_READSECTORS_COMPLETE:
2652 FdcCycles = FDC_CmdCompleteCommon( true );
2653 break;
2654 }
2655
2656 return FdcCycles;
2657 }
2658
2659
2660 /*-----------------------------------------------------------------------*/
2661 /**
2662 * Run 'WRITE SECTOR/S' command
2663 */
FDC_UpdateWriteSectorsCmd(void)2664 static int FDC_UpdateWriteSectorsCmd ( void )
2665 {
2666 int FdcCycles = 0;
2667 int FrameCycles, HblCounterVideo, LineCycles;
2668 Uint8 Next_TR;
2669 Uint8 Next_SR;
2670 Uint8 Next_LEN;
2671 Uint8 Next_CRC_OK;
2672 Uint8 Byte;
2673 Uint8 Status;
2674
2675 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2676
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 ) ) )
2681 {
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());
2684
2685 FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
2686 FdcCycles = FDC_CmdCompleteCommon( true );
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:
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 }
2706 break;
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;
2725 FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER;
2726 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2727 break;
2728 case FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER:
2729 /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
2730 if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
2731 {
2732 FDC.CommandState = FDCEMU_RUN_WRITESECTORS_RNF;
2733 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
2734 break;
2735 }
2736
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 )
2746 {
2747 FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
2748 }
2749 else
2750 {
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 */
2753 FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER;
2754 }
2755 break;
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
2765 {
2766 Next_TR = FDC_NextSectorID_TR_ST ();
2767 Next_SR = FDC_NextSectorID_SR_ST ();
2768 Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
2769 }
2770 if ( ( Next_TR == FDC.TR ) && ( Next_SR == FDC.SR ) && ( Next_CRC_OK ) )
2771 {
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;
2781 }
2782 break;
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;
2796 case FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP:
2797 /* Transfer the sector 1 byte at a time using DMA */
2798 if ( FDC_DMA.BytesToTransfer-- > 0 )
2799 {
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 );
2804 }
2805 else /* Sector transferred, add the CRC */
2806 {
2807 FDC.CommandState = FDCEMU_RUN_WRITESECTORS_CRC;
2808 FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Write 2 bytes for CRC */
2809 }
2810 break;
2811 case FDCEMU_RUN_WRITESECTORS_CRC:
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 */
2835 if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2836 {
2837 FDC.SR++; /* Try to write next sector and set RNF if not possible */
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());
2843 }
2844 else /* Multi=0, stop here with no error */
2845 {
2846 FDC.CommandState = FDCEMU_RUN_WRITESECTORS_COMPLETE;
2847 FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
2848 }
2849 break;
2850 case FDCEMU_RUN_WRITESECTORS_RNF:
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());
2853
2854 FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2855 FdcCycles = FDC_CmdCompleteCommon( true );
2856 break;
2857 case FDCEMU_RUN_WRITESECTORS_COMPLETE:
2858 FdcCycles = FDC_CmdCompleteCommon( true );
2859 break;
2860 }
2861
2862 return FdcCycles;
2863 }
2864
2865
2866 /*-----------------------------------------------------------------------*/
2867 /**
2868 * Run 'READ ADDRESS' command
2869 */
FDC_UpdateReadAddressCmd(void)2870 static int FDC_UpdateReadAddressCmd ( void )
2871 {
2872 int FdcCycles = 0;
2873 int FrameCycles, HblCounterVideo, LineCycles;
2874
2875 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2876
2877 /* Which command is running? */
2878 switch (FDC.CommandState)
2879 {
2880 case FDCEMU_RUN_READADDRESS:
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 }
2891 break;
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 }
2922
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 }
2941 break;
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 );
2952
2953 FDC.SR = FDC_Buffer_Read_Byte_pos ( 0 ); /* The 1st byte of the ID field is also copied into Sector Register */
2954
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 }
2970 break;
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());
2974
2975 FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2976 FdcCycles = FDC_CmdCompleteCommon( true );
2977 break;
2978 case FDCEMU_RUN_READADDRESS_COMPLETE:
2979 FdcCycles = FDC_CmdCompleteCommon( true );
2980 break;
2981 }
2982
2983 return FdcCycles;
2984 }
2985
2986
2987 /*-----------------------------------------------------------------------*/
2988 /**
2989 * Run 'READ TRACK' command
2990 */
FDC_UpdateReadTrackCmd(void)2991 static int FDC_UpdateReadTrackCmd ( void )
2992 {
2993 int FdcCycles = 0;
2994 int i;
2995 int FrameCycles, HblCounterVideo, LineCycles;
2996
2997 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2998
2999 /* Which command is running? */
3000 switch (FDC.CommandState)
3001 {
3002 case FDCEMU_RUN_READTRACK:
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 }
3041 break;
3042 case FDCEMU_RUN_READTRACK_INDEX:
3043 /* At this point, we have a valid drive/floppy, build the track data */
3044 FDC_Buffer_Reset();
3045
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 ) ) )
3049 {
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 );
3060 }
3061 else /* Track/side available in the disk image */
3062 {
3063 FDC.Status_Temp = FDC_ReadTrack_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
3064 }
3065
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 }
3086
3087 return FdcCycles;
3088 }
3089
3090
3091 /*-----------------------------------------------------------------------*/
3092 /**
3093 * Run 'WRITE TRACK' command
3094 */
FDC_UpdateWriteTrackCmd(void)3095 static int FDC_UpdateWriteTrackCmd ( void )
3096 {
3097 int FdcCycles = 0;
3098 int FrameCycles, HblCounterVideo, LineCycles;
3099 Uint8 Byte;
3100 Uint8 Status;
3101
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());
3153
3154 FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
3155 FdcCycles = FDC_CmdCompleteCommon( true );
3156 break;
3157 }
3158
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 );
3163
3164 FDC.CommandState = FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP;
3165 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
3166 break;
3167 case FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP:
3168 /* Transfer the track 1 byte at a time using DMA */
3169 if ( FDC_DMA.BytesToTransfer-- > 0 )
3170 {
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 );
3175 }
3176 else /* Track written */
3177 {
3178 FDC.CommandState = FDCEMU_RUN_WRITETRACK_COMPLETE;
3179 FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
3180 }
3181 break;
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 );
3196 break;
3197 }
3198
3199 return FdcCycles;
3200 }
3201
3202
3203 /*-----------------------------------------------------------------------*/
3204 /**
3205 * Common to types I, II and III
3206 *
3207 * Start motor / spin up sequence if needed
3208 * Return true if spin up sequence is needed, else false
3209 */
3210
FDC_Set_MotorON(Uint8 FDC_CR)3211 static bool FDC_Set_MotorON ( Uint8 FDC_CR )
3212 {
3213 int FrameCycles, HblCounterVideo, LineCycles;
3214 bool SpinUp;
3215
3216 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3217
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 */
3229 {
3230 LOG_TRACE(TRACE_FDC, "fdc start motor without spinup VBL=%d video_cyc=%d %d@%d pc=%x\n",
3231 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3232
3233 SpinUp = false;
3234 }
3235
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;
3248 }
3249
3250
3251 /*-----------------------------------------------------------------------*/
3252 /**
3253 * Type I Commands
3254 *
3255 * Restore, Seek, Step, Step-In and Step-Out
3256 */
3257
3258
3259 /*-----------------------------------------------------------------------*/
FDC_TypeI_Restore(void)3260 static int FDC_TypeI_Restore(void)
3261 {
3262 int FrameCycles, HblCounterVideo, LineCycles;
3263
3264 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3265
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",
3267 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3268 ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3269 FDC_StepRate_ms[ FDC_STEP_RATE ] ,
3270 FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal].HeadTrack : -1 ,
3271 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3272
3273 /* Set emulation to seek to track zero */
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 );
3278
3279 return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
3280 }
3281
3282
3283 /*-----------------------------------------------------------------------*/
FDC_TypeI_Seek(void)3284 static int FDC_TypeI_Seek ( void )
3285 {
3286 int FrameCycles, HblCounterVideo, LineCycles;
3287
3288 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3289
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",
3291 FDC.DR,
3292 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3293 ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3294 FDC_StepRate_ms[ FDC_STEP_RATE ] ,
3295 FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3296 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3297
3298 /* Set emulation to seek to chosen track */
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 );
3303
3304 return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
3305 }
3306
3307
3308 /*-----------------------------------------------------------------------*/
FDC_TypeI_Step(void)3309 static int FDC_TypeI_Step ( void )
3310 {
3311 int FrameCycles, HblCounterVideo, LineCycles;
3312
3313 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3314
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",
3316 FDC.StepDirection,
3317 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3318 ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3319 FDC_StepRate_ms[ FDC_STEP_RATE ] ,
3320 FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3321 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
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;
3326
3327 FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
3328
3329 return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
3330 }
3331
3332
3333 /*-----------------------------------------------------------------------*/
FDC_TypeI_StepIn(void)3334 static int FDC_TypeI_StepIn(void)
3335 {
3336 int FrameCycles, HblCounterVideo, LineCycles;
3337
3338 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3339
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",
3341 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3342 ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3343 FDC_StepRate_ms[ FDC_STEP_RATE ] ,
3344 FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3345 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3346
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*/
3351
3352 FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
3353
3354 return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
3355 }
3356
3357
3358 /*-----------------------------------------------------------------------*/
FDC_TypeI_StepOut(void)3359 static int FDC_TypeI_StepOut ( void )
3360 {
3361 int FrameCycles, HblCounterVideo, LineCycles;
3362
3363 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3364
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",
3366 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3367 ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3368 FDC_StepRate_ms[ FDC_STEP_RATE ] ,
3369 FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3370 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
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 */
3376
3377 FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
3378
3379 return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
3380 }
3381
3382
3383 /*-----------------------------------------------------------------------*/
3384 /**
3385 * Type II Commands
3386 *
3387 * Read Sector, Write Sector
3388 */
3389
3390
3391 /*-----------------------------------------------------------------------*/
FDC_TypeII_ReadSector(void)3392 static int FDC_TypeII_ReadSector ( void )
3393 {
3394 int FrameCycles, HblCounterVideo, LineCycles;
3395
3396 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3397
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" ,
3400 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3401 ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
3402 FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3403 FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount ,
3404 FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3405
3406 /* Set emulation to read sector(s) */
3407 FDC.Command = FDCEMU_CMD_READSECTORS;
3408 FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA;
3409
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 );
3412
3413 return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
3414 }
3415
3416
3417 /*-----------------------------------------------------------------------*/
FDC_TypeII_WriteSector(void)3418 static int FDC_TypeII_WriteSector ( void )
3419 {
3420 int FrameCycles, HblCounterVideo, LineCycles;
3421
3422 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3423
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" ,
3426 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3427 ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
3428 FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3429 FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount,
3430 FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3431
3432 /* Set emulation to write a sector(s) */
3433 FDC.Command = FDCEMU_CMD_WRITESECTORS;
3434 FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA;
3435
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 );
3438
3439 return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
3440 }
3441
3442
3443 /*-----------------------------------------------------------------------*/
3444 /**
3445 * Type III Commands
3446 *
3447 * Read Address, Read Track, Write Track
3448 */
3449
3450
3451 /*-----------------------------------------------------------------------*/
FDC_TypeIII_ReadAddress(void)3452 static int FDC_TypeIII_ReadAddress ( void )
3453 {
3454 int FrameCycles, HblCounterVideo, LineCycles;
3455
3456 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3457
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",
3459 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3460 ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
3461 FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3462 FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
3463 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3464
3465 /* Set emulation to seek to track zero */
3466 FDC.Command = FDCEMU_CMD_READADDRESS;
3467 FDC.CommandState = FDCEMU_RUN_READADDRESS;
3468
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
3472 return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
3473 }
3474
3475
3476 /*-----------------------------------------------------------------------*/
FDC_TypeIII_ReadTrack(void)3477 static int FDC_TypeIII_ReadTrack ( void )
3478 {
3479 int FrameCycles, HblCounterVideo, LineCycles;
3480
3481 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3482
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",
3484 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3485 ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
3486 FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3487 FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
3488 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3489
3490 /* Set emulation to read a single track */
3491 FDC.Command = FDCEMU_CMD_READTRACK;
3492 FDC.CommandState = FDCEMU_RUN_READTRACK;
3493
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 );
3496
3497 return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
3498 }
3499
3500
3501 /*-----------------------------------------------------------------------*/
FDC_TypeIII_WriteTrack(void)3502 static int FDC_TypeIII_WriteTrack ( void )
3503 {
3504 int FrameCycles, HblCounterVideo, LineCycles;
3505
3506 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3507
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",
3509 ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
3510 ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
3511 FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
3512 FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
3513 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3514
3515 /* Set emulation to write a single track */
3516 FDC.Command = FDCEMU_CMD_WRITETRACK;
3517 FDC.CommandState = FDCEMU_RUN_WRITETRACK;
3518
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;
3523 }
3524
3525
3526 /*-----------------------------------------------------------------------*/
3527 /**
3528 * Type IV Commands
3529 *
3530 * Force Interrupt
3531 */
3532
3533
3534 /*-----------------------------------------------------------------------*/
FDC_TypeIV_ForceInterrupt(void)3535 static int FDC_TypeIV_ForceInterrupt ( void )
3536 {
3537 int FdcCycles;
3538 int FrameCycles, HblCounterVideo, LineCycles;
3539
3540 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3541
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",
3543 FDC.CR , ( FDC.CR & 0x8 ) >> 3 , ( FDC.CR & 0x4 ) >> 2 ,
3544 nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3545
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 )
3549 {
3550 FDC.StatusTypeI = true;
3551
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 */
3554 }
3555
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 );
3567
3568 return FDC_DELAY_CYCLE_TYPE_IV_PREPARE + FdcCycles;
3569 }
3570
3571
3572 /*-----------------------------------------------------------------------*/
3573 /**
3574 * Execute Type I commands
3575 */
FDC_ExecuteTypeICommands(void)3576 static int FDC_ExecuteTypeICommands ( void )
3577 {
3578 int FdcCycles = 0;
3579
3580 FDC.CommandType = 1;
3581 FDC.StatusTypeI = true;
3582
3583 /* Check Type I Command */
3584 switch ( FDC.CR & 0xf0 )
3585 {
3586 case 0x00: /* Restore */
3587 FdcCycles = FDC_TypeI_Restore();
3588 break;
3589 case 0x10: /* Seek */
3590 FdcCycles = FDC_TypeI_Seek();
3591 break;
3592 case 0x20: /* Step */
3593 case 0x30:
3594 FdcCycles = FDC_TypeI_Step();
3595 break;
3596 case 0x40: /* Step-In */
3597 case 0x50:
3598 FdcCycles = FDC_TypeI_StepIn();
3599 break;
3600 case 0x60: /* Step-Out */
3601 case 0x70:
3602 FdcCycles = FDC_TypeI_StepOut();
3603 break;
3604 }
3605
3606 return FdcCycles;
3607 }
3608
3609
3610 /*-----------------------------------------------------------------------*/
3611 /**
3612 * Execute Type II commands
3613 */
FDC_ExecuteTypeIICommands(void)3614 static int FDC_ExecuteTypeIICommands ( void )
3615 {
3616 int FdcCycles = 0;
3617
3618 FDC.CommandType = 2;
3619 FDC.StatusTypeI = false;
3620
3621 /* Check Type II Command */
3622 switch ( FDC.CR & 0xf0 )
3623 {
3624 case 0x80: /* Read Sector multi=0*/
3625 case 0x90: /* Read Sectors multi=1 */
3626 FdcCycles = FDC_TypeII_ReadSector();
3627 break;
3628 case 0xa0: /* Write Sector multi=0 */
3629 case 0xb0: /* Write Sectors multi=1 */
3630 FdcCycles = FDC_TypeII_WriteSector();
3631 break;
3632 }
3633
3634 return FdcCycles;
3635 }
3636
3637
3638 /*-----------------------------------------------------------------------*/
3639 /**
3640 * Execute Type III commands
3641 */
FDC_ExecuteTypeIIICommands(void)3642 static int FDC_ExecuteTypeIIICommands ( void )
3643 {
3644 int FdcCycles = 0;
3645
3646 FDC.CommandType = 3;
3647 FDC.StatusTypeI = false;
3648
3649 /* Check Type III Command */
3650 switch ( FDC.CR & 0xf0 )
3651 {
3652 case 0xc0: /* Read Address */
3653 FdcCycles = FDC_TypeIII_ReadAddress();
3654 break;
3655 case 0xe0: /* Read Track */
3656 FdcCycles = FDC_TypeIII_ReadTrack();
3657 break;
3658 case 0xf0: /* Write Track */
3659 FdcCycles = FDC_TypeIII_WriteTrack();
3660 break;
3661 }
3662
3663 return FdcCycles;
3664 }
3665
3666
3667 /*-----------------------------------------------------------------------*/
3668 /**
3669 * Execute Type IV commands
3670 */
FDC_ExecuteTypeIVCommands(void)3671 static int FDC_ExecuteTypeIVCommands ( void )
3672 {
3673 int FdcCycles;
3674
3675 FDC.CommandType = 4;
3676
3677 FdcCycles = FDC_TypeIV_ForceInterrupt();
3678
3679 return FdcCycles;
3680 }
3681
3682
3683 /*-----------------------------------------------------------------------*/
3684 /**
3685 * Find FDC command type and execute
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
3694 */
FDC_ExecuteCommand(void)3695 static void FDC_ExecuteCommand ( void )
3696 {
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;
3720
3721 /* Check type of command and execute */
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();
3728 else /* Type IV - Force Interrupt */
3729 FdcCycles = FDC_ExecuteTypeIVCommands();
3730
3731 FDC.ReplaceCommandPossible = true; /* This new command can be replaced during the prepare+spinup phase */
3732 FDC_StartTimer_FdcCycles ( FdcCycles , 0 );
3733 }
3734
3735
3736 /*-----------------------------------------------------------------------*/
3737 /**
3738 * Write to SectorCount register $ff8604
3739 */
FDC_WriteSectorCountRegister(void)3740 static void FDC_WriteSectorCountRegister ( void )
3741 {
3742 int FrameCycles, HblCounterVideo, LineCycles;
3743
3744 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3745
3746 FDC_DMA.SectorCount = IoMem_ReadWord(0xff8604);
3747 if (!Config_IsMachineFalcon())
3748 FDC_DMA.SectorCount &= 0xff;
3749
3750 LOG_TRACE(TRACE_FDC, "fdc write 8604 dma sector count=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
3751 FDC_DMA.SectorCount, nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
3752
3753 }
3754
3755
3756 /*-----------------------------------------------------------------------*/
3757 /**
3758 * Write to Command register $ff8604
3759 */
FDC_WriteCommandRegister(void)3760 static void FDC_WriteCommandRegister ( void )
3761 {
3762 int FrameCycles, HblCounterVideo, LineCycles;
3763 Uint8 Type_new;
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",
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 */
3772 /* was started and spinup phase was not completed yet (eg Overdrive Demos by Phalanx) (see notes at the top of the file)*/
3773 if ( FDC.STR & FDC_STR_BIT_BUSY )
3774 {
3775 Type_new = FDC_GetCmdType ( IoMem_ReadByte(0xff8605) );
3776 if ( Type_new == 4 ) /* 'Force Interrupt' command */
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
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 */
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 }
3797
3798 FDC.CR = IoMem_ReadByte(0xff8605);
3799 FDC_ExecuteCommand();
3800 }
3801
3802
3803 /*-----------------------------------------------------------------------*/
3804 /**
3805 * Write to Track register $ff8604
3806 */
FDC_WriteTrackRegister(void)3807 static void FDC_WriteTrackRegister ( void )
3808 {
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" ,
3814 IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
3815
3816 /* [NP] Contrary to what is written in the WD1772 doc, Track Register can be changed */
3817 /* while the fdc is busy (the change will be ignored or not, depending on the current sub-state */
3818 /* in the state machine) */
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());
3823 //return;
3824 }
3825
3826 FDC.TR = IoMem_ReadByte(0xff8605);
3827 }
3828
3829
3830 /*-----------------------------------------------------------------------*/
3831 /**
3832 * Write to Sector register $ff8604
3833 */
FDC_WriteSectorRegister(void)3834 static void FDC_WriteSectorRegister ( void )
3835 {
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" ,
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 }
3851
3852 FDC.SR = IoMem_ReadByte(0xff8605);
3853 }
3854
3855
3856 /*-----------------------------------------------------------------------*/
3857 /**
3858 * Write to Data register $ff8604
3859 */
FDC_WriteDataRegister(void)3860 static void FDC_WriteDataRegister ( void )
3861 {
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" ,
3867 IoMem_ReadByte(0xff8605), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
3868
3869 FDC.DR = IoMem_ReadByte(0xff8605);
3870 }
3871
3872
3873 /*-----------------------------------------------------------------------*/
3874 /**
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)
3880 */
FDC_DiskController_WriteWord(void)3881 void FDC_DiskController_WriteWord ( void )
3882 {
3883 int FrameCycles, HblCounterVideo, LineCycles;
3884 int EmulationMode;
3885 int FDC_reg;
3886
3887 if ( nIoMemAccessSize == SIZE_BYTE )
3888 {
3889 /* This register does not like to be accessed in byte mode on a normal ST */
3890 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
3891 return;
3892 }
3893
3894 M68000_WaitState(4);
3895
3896 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3897
3898 LOG_TRACE(TRACE_FDC, "fdc write 8604 data=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
3899 IoMem_ReadWord(0xff8604), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
3900
3901
3902 /* Are we trying to set the DMA SectorCount ? */
3903 if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
3904 {
3905 FDC_WriteSectorCountRegister();
3906 return;
3907 }
3908
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 ? */
3913 {
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) );
3952 }
3953 }
3954 }
3955
3956
3957 /*-----------------------------------------------------------------------*/
3958 /**
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
3965 */
FDC_DiskControllerStatus_ReadWord(void)3966 void FDC_DiskControllerStatus_ReadWord ( void )
3967 {
3968 Uint16 DiskControllerByte = 0; /* Used to pass back the parameter */
3969 int FrameCycles, HblCounterVideo, LineCycles;
3970 int ForceWPRT;
3971 int EmulationMode;
3972 int FDC_reg;
3973
3974 if (nIoMemAccessSize == SIZE_BYTE && !Config_IsMachineFalcon())
3975 {
3976 /* This register does not like to be accessed in byte mode on a normal ST */
3977 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
3978 return;
3979 }
3980
3981 /* Are we trying to read the DMA SectorCount ? */
3982 if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
3983 {
3984 DiskControllerByte = FDC_DMA.ff8604_recent_val; /* As verified on real STF, DMA sector count can't be read back */
3985 }
3986
3987 else if ( ( FDC_DMA.Mode & 0x0008) == 0x0008) /* HDC status reg selected ? */
3988 {
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);
3993 }
3994
3995 else /* It's a FDC register access */
3996 {
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 )
4003 {
4004 /* Update FDC's internal variables */
4005 FDC_UpdateAll ();
4006
4007 /* Read FDC registers */
4008 switch ( FDC_reg )
4009 {
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 }
4059
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;
4081 }
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 */
4095
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 }
4099 }
4100 }
4101
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
4107 IoMem_WriteWord(0xff8604, DiskControllerByte);
4108
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() );
4113 }
4114
4115
4116 /*-----------------------------------------------------------------------*/
4117 /**
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
4124 * $86 - Selects data register
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
4127 *
4128 * NOTE [NP] : add 4 cycles wait state in that case
4129 */
FDC_DmaModeControl_WriteWord(void)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 */
4139 M68000_BusError(IoAccessFullAddress, BUS_ERROR_WRITE, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
4140 return;
4141 }
4142
4143 M68000_WaitState(4);
4144
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();
4156
4157 if ((Mode_prev & 0xc0) != 0 && (FDC_DMA.Mode & 0xc0) == 0)
4158 HDC_DmaTransfer();
4159 }
4160
4161
4162 /*-----------------------------------------------------------------------*/
4163 /**
4164 * Read DMA Status at $ff8606
4165 *
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
4183 */
FDC_DmaStatus_ReadWord(void)4184 void FDC_DmaStatus_ReadWord ( void )
4185 {
4186 if (nIoMemAccessSize == SIZE_BYTE && !Config_IsMachineFalcon())
4187 {
4188 /* This register does not like to be accessed in byte mode on a normal ST */
4189 M68000_BusError(IoAccessFullAddress, BUS_ERROR_READ, BUS_ERROR_SIZE_BYTE, BUS_ERROR_ACCESS_DATA);
4190 return;
4191 }
4192
4193 /* Update Bit1 for zero sector count */
4194 if ( FDC_DMA.SectorCount != 0 )
4195 FDC_DMA.Status |= 0x02;
4196 else
4197 FDC_DMA.Status &= ~0x02;
4198
4199 /* In the case of the ST, Bit2 / DRQ is always 0 because it's handled by the DMA and its 16 bytes buffer */
4200
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 ) );
4203 }
4204
4205
4206 /*-----------------------------------------------------------------------*/
4207 /**
4208 * Read hi/med/low DMA address byte at $ff8609/0b/0d
4209 */
FDC_DmaAddress_ReadByte(void)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
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
4231 */
FDC_DmaAddress_WriteByte(void)4232 void FDC_DmaAddress_WriteByte ( void )
4233 {
4234 Uint32 Address;
4235 Uint32 Address_old;
4236 int FrameCycles, HblCounterVideo, LineCycles;
4237
4238 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4239
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() )
4249 {
4250 Address_old = FDC_GetDMAAddress();
4251
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 }
4269
4270 /* Store new address as DMA address and update $ff8609/0b/0d */
4271 FDC_WriteDMAAddress ( Address );
4272 }
4273
4274
4275 /*-----------------------------------------------------------------------*/
4276 /**
4277 * Get DMA address used to transfer data between FDC/HDC and RAM
4278 */
FDC_GetDMAAddress(void)4279 Uint32 FDC_GetDMAAddress(void)
4280 {
4281 return FDC_DMA_Address;
4282 }
4283
4284
4285 /*-----------------------------------------------------------------------*/
4286 /**
4287 * Write a new address to the FDC/HDC DMA address registers at $ff8909/0b/0d
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
4295 */
FDC_WriteDMAAddress(Uint32 Address)4296 void FDC_WriteDMAAddress ( Uint32 Address )
4297 {
4298 int FrameCycles, HblCounterVideo, LineCycles;
4299 Uint32 dma_mask;
4300
4301 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4302
4303 LOG_TRACE(TRACE_FDC, "fdc write dma address new=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4304 Address , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4305
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;
4311
4312 /* Store as 24-bit address */
4313 STMemory_WriteByte(0xff8609, FDC_DMA_Address>>16);
4314 STMemory_WriteByte(0xff860b, FDC_DMA_Address>>8);
4315 STMemory_WriteByte(0xff860d, FDC_DMA_Address);
4316 }
4317
4318
4319 /*-----------------------------------------------------------------------*/
4320 /**
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,
4326 * the next track's number into NextSector_ID_Field_TR, the next sector's length
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
4332 */
FDC_NextSectorID_FdcCycles_ST(Uint8 Drive,Uint8 NumberOfHeads,Uint8 Track,Uint8 Side)4333 static int FDC_NextSectorID_FdcCycles_ST ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side )
4334 {
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
4349 if ( Track >= FDC_GetTracksPerDisk ( Drive ) ) /* Try to access a non existing track */
4350 return -1;
4351
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 */
FDC_NextSectorID_TR_ST(void)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 */
FDC_NextSectorID_SR_ST(void)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 */
FDC_NextSectorID_LEN_ST(void)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 */
FDC_NextSectorID_CRC_OK_ST(void)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 */
FDC_ReadSector_ST(Uint8 Drive,Uint8 Track,Uint8 Sector,Uint8 Side,int * pSectorSize)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;
4447
4448 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4449
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,
4452 nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4453
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 }
4461
4462 /* Failed */
4463 LOG_TRACE(TRACE_FDC, "fdc read sector failed\n" );
4464 return FDC_STR_BIT_RNF;
4465 }
4466
4467
4468 /*-----------------------------------------------------------------------*/
4469 /**
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.
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.
4476 * Return 0 if sector was written without error, or FDC_STR_BIT_RNF if an error occurred
4477 */
FDC_WriteSector_ST(Uint8 Drive,Uint8 Track,Uint8 Sector,Uint8 Side,int SectorSize)4478 static Uint8 FDC_WriteSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize )
4479 {
4480 int FrameCycles, HblCounterVideo, LineCycles;
4481 int i;
4482 Uint8 SectorData[ 1024 ]; /* max sector size for WD1772 */
4483
4484 Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4485
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,
4488 nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4489
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 */
FDC_ReadAddress_ST(Uint8 Drive,Uint8 Track,Uint8 Sector,Uint8 Side)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
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
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 */
FDC_ReadTrack_ST(Uint8 Drive,Uint8 Track,Uint8 Side)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
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 }
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++ )
4599 {
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 );
4658 }
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 */
FDC_WriteTrack_ST(Uint8 Drive,Uint8 Track,Uint8 Side,int TrackSize)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 ? */
4692
4693 /* Failed */
4694 LOG_TRACE(TRACE_FDC, "fdc write track failed\n" );
4695 return FDC_STR_BIT_LOST_DATA;
4696 }
4697
4698
4699 /*-----------------------------------------------------------------------*/
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 */
FDC_FloppyMode_WriteByte(void)4717 void FDC_FloppyMode_WriteByte ( void )
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 */
FDC_FloppyMode_ReadByte(void)4741 void FDC_FloppyMode_ReadByte ( void )
4742 {
4743 IoMem_WriteByte(0xff860f, 0x80); // FIXME: Is this ok?
4744 // printf("Read from floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4745 }
4746