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