|
|
1.1 root 1: /*
1.1.1.4 root 2: Hatari - fdc.c
3:
1.1.1.17 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.15 root 7: Floppy Disk Controller(FDC) emulation.
8: All commands are emulated with good timings estimation, as many programs
9: (demo or cracked games) rely on accurate FDC timings and DMA transfer by blocks
10: of 16 bytes.
11: The behaviour of all FDC's registers matches the official docs and should not
12: cause programs to fail when accessing the FDC (especially for Status Register).
13: As Hatari only handles ST/MSA disk images that only support 512 bytes sectors as
14: well as a fixed number of sectors per track, a few parts of the FDC emulation are
15: simplified and would need to be changed to handle more complex disk images (Pasti).
1.1 root 16: */
1.1.1.9 root 17:
1.1.1.11 root 18: const char FDC_fileid[] = "Hatari fdc.c : " __DATE__ " " __TIME__;
1.1 root 19:
20: #include "main.h"
1.1.1.4 root 21: #include "configuration.h"
1.1 root 22: #include "fdc.h"
1.1.1.3 root 23: #include "hdc.h"
1.1 root 24: #include "floppy.h"
1.1.1.18! root 25: #include "floppy_ipf.h"
! 26: #include "floppy_stx.h"
1.1.1.6 root 27: #include "ioMem.h"
28: #include "log.h"
1.1 root 29: #include "m68000.h"
30: #include "memorySnapShot.h"
31: #include "mfp.h"
32: #include "psg.h"
33: #include "stMemory.h"
1.1.1.14 root 34: #include "screen.h"
1.1.1.10 root 35: #include "video.h"
1.1.1.15 root 36: #include "clocks_timings.h"
37: #include "utils.h"
1.1.1.17 root 38: #include "statusbar.h"
1.1 root 39:
1.1.1.3 root 40:
1.1 root 41: /*
1.1.1.7 root 42: Floppy Disk Controller
1.1 root 43:
44: Programmable Sound Generator (YM-2149)
45:
46: 0xff8800(even byte) - PSG Register Data (Read, used for parallel port)
47: - PSG Register Select (Write)
48:
49: Write to bits 0-3 to select PSG register to use(then write data to 0xfff8802)
50: Value Register
51:
52: 0000 Channel A Fine Tune
53: 0001 Channel A Coarse Tune
54: 0010 Channel B Fine Tune
55: 0011 Channel B Coarse Tune
56: 0100 Channel C Fine Tune
57: 0101 Channel C Coarse Tune
58: 0110 Noise Generator Control
59: 0111 Mixer Control - I/O enable
60: 1000 Channel A Amplitude
61: 1001 Channel B Amplitude
62: 1010 Channel C Amplitude
63: 1011 Envelope Period Fine Tune
64: 1100 Envelope Peroid Coarse Tune
65: 1101 Envelope Shape
66: 1110 I/O Port A Select (Write only)
67: 1111 I/O Port B Select
68:
69: 0xfff8802(even byte) - Bits according to 0xff8800 Register select
1.1.1.4 root 70:
1.1 root 71: 1110(Register 14) - I/O Port A
72: Bit 0 - Floppy side 0/1
73: Bit 1 - Floppy drive 0 select
74: Bit 2 - Floppy drive 1 select
75: Bit 3 - RS232 Ready to send (RTS)
76: Bit 4 - RS232 Data Terminal Ready (DTR)
77: Bit 5 - Centronics Strobe
78: Bit 6 - General Purpose Output
79: Bit 7 - Reserved
80:
1.1.1.7 root 81: ACSI DMA and Floppy Disk Controller(FDC)
1.1 root 82: 0xff8604 - information from file '1772.info.txt, by David Gahris' (register r0)
1.1.1.15 root 83: Word access only, but only lower byte (ff8605) is used
1.1 root 84: (write) - Disk controller
1.1.1.15 root 85: Set DMA sector count if ff8606 bit 4 == 1
86: Set FDC's internal registers depending on bit 1/2 of ff8606 if bit 4 == 0
1.1 root 87: (read) - Disk controller status
88: Bit 0 - Busy. This bit is 1 when the 177x is busy. This bit is 0 when the 177x is free for CPU commands.
89: Bit 1 - Index / Data Request. On Type I commands, this bit is high during the index pulse that occurs once
90: per disk rotation. This bit is low at all times other than the index pulse. For Type II and III commands,
91: Bit 1 high signals the CPU to handle the data register in order to maintain a continuous flow of data.
92: Bit 1 is high when the data register is full during a read or when the data register is empty during a write.
93: "Worst case service time" for Data Request is 23.5 cycles.
94: Bit 2 - Track Zero / Lost Data. After Type I commands, this bit is 0 if the mechanism is at track zero.
95: This bit is 1 if the head is not at track zero. After Type II or III commands, this bit is 1 if the
96: CPU did not respond to Data Request (Status bit 1) in time for the 177x to maintain a continuous data flow.
97: This bit is 0 if the CPU responded promptly to Data Request.
1.1.1.15 root 98: NOTE : on ST, Lost Data is never set because the DMA always handles the data request signal.
1.1 root 99: Bit 3 - CRC Error. This bit is high if a sector CRC on disk does not match the CRC which the 177x
100: computed from the data. The CRC polynomial is x^16+x^12+x^5+1. If the stored CRC matches the newly
101: calculated CRC, the CRC Error bit is low. If this bit and the Record Not Found bit are set, the error
102: was in an ID field. If this bit is set but Record Not Found is clear, the error was in a data field.
103: Bit 4 - Record Not Found. This bit is set if the 177x cannot find the track, sector, or side which
104: the CPU requested. Otherwise, this bit is clear.
105: Bit 5 - Spin-up / Record Type. For Type I commands, this bit is low during the 6-revolution motor
106: spin-up time. This bit is high after spin-up. For Type II and Type III commands, Bit 5 low
107: indicates a normal data mark. Bit 5 high indicates a deleted data mark.
108: Bit 6 - Write Protect. This bit is not used during reads. During writes, this bit is high when the disk is write protected.
1.1.1.15 root 109: After a type I command, this bit is constantly updated an give the current value of the WPT signal.
1.1 root 110: Bit 7 - Motor On. This bit is high when the drive motor is on, and low when the motor is off.
111:
112: 0xff8606 - DMA Status(read), DMA Mode Control(write) - NOTE bits 0,9-15 are not used
113: Bit 1 - FDC Pin A0 (See below)
114: Bit 2 - FDC Pin A1
115: Bit 3 - FDC/HDC Register Select
116: Bit 4 - FDC/Sector count select
117: Bit 5 - Reserved
118: Bit 6 - Enable/Disable DMA
119: Bit 7 - HDC/FDC
120: Bit 8 - Read/Write
121:
122: A1 A0 Read Write(bit 8==1)
123: 0 0 Status Command
124: 0 1 Track Register Track Register
125: 1 0 Sector Register Sector Register
126: 1 1 Data Register Data Register
127:
128:
1.1.1.15 root 129: NOTE [NP] : The DMA is connected to the FDC and its Data Register, each time a DRQ
130: is made by the FDC, it's handled by the DMA through its internal 16 bytes buffer.
131: This means that in the case of the Atari ST the LOST_DATA bit will never be set
132: in the Status Register (but data can be lost if FDC_DMA.SectorCount=0 as there
1.1.1.18! root 133: will be no transfer between DMA and RAM).
! 134: So, "read sector" and "write sector" type II command will never set LOST_DATA, but
! 135: strangely on a real STF the "read track" type III command will set LOST_DATA when
! 136: it succeeds (maybe it depends on the size of the track ?)
! 137: (eg Super Monaco GP on Superior 65 : loader fails if LOST_DATA is set when
! 138: there're not enough DMA sectors to transfer bytes with read sectors)
! 139:
! 140: NOTE [NP] : All commands that require to read data from the floppy (verify sequence,
! 141: searching next sector id, ...) will not fail if the drive is OFF or empty. They will
! 142: wait forever until the drive is enabled again or a floppy is inserted ; at this point
! 143: the command will complete as usual, even after several seconds/minutes of delay.
! 144:
! 145: NOTE [NP] : Although the doc says a new type I/II/III command can't be started while
! 146: the busy bit is set, it's in fact possible to do it under certain conditions. As seen
! 147: in the loader of 'The Overdrive Demos' by Phalanx, the 'restore' command should be
! 148: replaced by a 'seek' command when it occurs in less than 900 cycles.
! 149: A possible explanation from this could be seen in the WD1772's documentation, where
! 150: the specific type I command is in fact checked after the 'prepare + spinup' sequence
! 151: in the state machine diagram.
! 152: Similarly, we can guess that a type II command can be replaced by another type II as long
! 153: as the 'prepare + spinup + head settle' sequence is not over (this was not tested on real HW)
! 154:
! 155: NOTE [NP] : As verified on a real STF, when reading DMA status at $ff8606 or DMA sector
! 156: count at $ff8604, the unused bits are not set to 0, but they contain the value from the latest
! 157: read/write made at $ff8604 when accessing FDC or HDC registers. Moreover, it's not possible to
! 158: read DMA sector count, so we return the lowest 8 bits from the latest access at $ff8604.
! 159:
! 160:
! 161: Cycles and wait states :
! 162: ------------------------
! 163: As verified on a real STF, reading or writing to $ff8604 or $ff8606 can add some 4 cycles
! 164: wait state.
! 165: lea $ffff8604,a3
! 166: lea $ffff8606,a4
! 167:
! 168: move.w (a4),d0 ; dma status motorola=8 stf=8
! 169:
! 170: move.w #$90,(a4) ; dma ctrl sector count motorola=12 stf=12+4
! 171: move.w (a3),d0 ; read sector count / fdc reg motorola=8 stf=8
! 172: move.w #1,(a3) ; write sector count motorola=12 stf=12+4
! 173:
! 174: move.w #$80,(a4) ; dma ctrl status/cmd reg motorola=12 stf=12+4
! 175: move.w (a3),d0 ; read fdc reg motorola=8 stf=8+4
! 176: move.w #$d0,(a3) ; write fdc reg motorola=12 stf=12+4
! 177:
! 178: -> All accesses take 4 extra cycles, except reading DMA status and reading DMA sector count
! 179: (which can't really be read, see note above)
1.1.1.15 root 180:
181:
182: Detecting disk changes :
183: ------------------------
184: 3'1/2 floppy drives include a 'DSKCHG' signal on pin 34 to detect when a disk was changed.
185: Unfortunatelly on ST, this signal is not connected. Nevertheless, it's possible to detect
186: a disk was inserted or ejected by looking at the 'WPT' signal which tells if a disk is write
187: protected or not.
188: At the drive level, a light is emitted above the top left corner of the floppy :
189: - if the write protection hole on the floppy is opened, the light goes through and the disk
190: is considered to be write protected.
191: - if the write protection hole on the floppy is closed, the light can't go through and the
192: disk is write enabled.
193: The point is that when any "solid" part of the floppy obstructs the light signal, the WPT
1.1.1.17 root 194: signal will change immediately : it will be considered as if a write enabled disk was present.
1.1.1.15 root 195: So, when a floppy is ejected or inserted, the body of the floppy will briefly obstruct the light,
196: whatever the state of the protection hole could be.
197: Similarly, when there's no floppy inside the drive, the light signal can pass through, so it will
198: be considered as if a write protected disk was present.
199: So, let's call 'C' the state when protection hole is Closed (ie WPT = 0) and 'O' the state
200: when protection hole is Opened (ie WPT = 1). We have the following cases :
201: - floppy in drive : state can be C or O depending on the protection tab. Let's call it 'X'
202: - no floppy in drive : state is equivalent to O (because the light signal is not obstructed)
203: - ejecting a floppy : states will go from X to C and finally to O
204: - inserting a floppy : states will go from O to C and finally to X
205:
206: The TOS monitors the changes on the WPT signal to determine if a floppy was ejected or inserted.
207: On TOS 1.02fr, the code is located between $fc1bc4 and $fc1ebc. Every 8 VBL, one floppy drive is checked
208: to see if the WPT signal changed. When 1 drive is connected, this means a floppy change should keep the
209: WPT signal during at least 8 VBLs. When 2 drive are connected, each drive is checked every 16 VBLs, so
210: the WPT signal should be kept for at least 16 VBLs.
211:
212: During these transition phases between "ejected" and "inserted", we force the WPT signal to either 0 or 1,
213: depending on which transition we're emulating (see Floppy_DriveTransitionUpdateState()) :
214: - Ejecting : WPT will be X, then 0, then 1
215: - Inserting : WPT will be 1, then 0, then X
216:
1.1 root 217: */
218:
1.1.1.14 root 219: /*-----------------------------------------------------------------------*/
1.1.1.15 root 220:
221: #define FDC_STR_BIT_BUSY 0x01
222: #define FDC_STR_BIT_INDEX 0x02 /* type I */
223: #define FDC_STR_BIT_DRQ 0x02 /* type II and III */
224: #define FDC_STR_BIT_TR00 0x04 /* type I */
225: #define FDC_STR_BIT_LOST_DATA 0x04 /* type II and III */
226: #define FDC_STR_BIT_CRC_ERROR 0x08
227: #define FDC_STR_BIT_RNF 0x10
228: #define FDC_STR_BIT_SPIN_UP 0x20 /* type I */
229: #define FDC_STR_BIT_RECORD_TYPE 0x20 /* type II and III */
230: #define FDC_STR_BIT_WPRT 0x40
231: #define FDC_STR_BIT_MOTOR_ON 0x80
232:
233:
1.1.1.17 root 234: #define FDC_COMMAND_BIT_VERIFY (1<<2) /* 0=no verify after type I, 1=verify after type I */
1.1.1.15 root 235: #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 */
1.1.1.18! root 236: #define FDC_COMMAND_BIT_SPIN_UP (1<<3) /* 0=enable motor's spin up, 1=disable motor's spin up */
1.1.1.15 root 237: #define FDC_COMMAND_BIT_UPDATE_TRACK (1<<4) /* 0=don't update TR after type I, 1=update TR after type I */
238: #define FDC_COMMAND_BIT_MULTIPLE_SECTOR (1<<4) /* 0=read/write only 1 sector, 1=read/write many sectors */
239:
240:
1.1.1.18! root 241: #define FDC_INTERRUPT_COND_IP (1<<2) /* Force interrupt on Index Pulse */
! 242: #define FDC_INTERRUPT_COND_IMMEDIATE (1<<3) /* Force interrupt immediate */
! 243:
1.1.1.15 root 244:
245: /* FDC Emulation commands used in FDC.Command */
1.1.1.14 root 246: enum
247: {
248: FDCEMU_CMD_NULL = 0,
249: /* Type I */
250: FDCEMU_CMD_RESTORE,
251: FDCEMU_CMD_SEEK,
1.1.1.15 root 252: FDCEMU_CMD_STEP, /* Also used for STEP IN and STEP OUT */
1.1.1.14 root 253: /* Type II */
254: FDCEMU_CMD_READSECTORS,
255: FDCEMU_CMD_WRITESECTORS,
256: /* Type III */
257: FDCEMU_CMD_READADDRESS,
1.1.1.15 root 258: FDCEMU_CMD_READTRACK,
259: FDCEMU_CMD_WRITETRACK,
260:
261: /* Other fake commands used internally */
262: FDCEMU_CMD_MOTOR_STOP
1.1.1.14 root 263: };
264:
265:
1.1.1.15 root 266: /* FDC Emulation commands' sub-states used in FDC.CommandState */
1.1.1.14 root 267: enum
268: {
1.1.1.15 root 269: FDCEMU_RUN_NULL = 0,
1.1.1.14 root 270:
1.1.1.15 root 271: /* Restore */
272: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO,
1.1.1.18! root 273: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_SPIN_UP,
! 274: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_MOTOR_ON,
1.1.1.15 root 275: FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO_LOOP,
1.1.1.17 root 276: FDCEMU_RUN_RESTORE_VERIFY,
1.1.1.18! root 277: FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK,
! 278: FDCEMU_RUN_RESTORE_VERIFY_NEXT_SECTOR_HEADER,
! 279: FDCEMU_RUN_RESTORE_VERIFY_CHECK_SECTOR_HEADER,
1.1.1.15 root 280: FDCEMU_RUN_RESTORE_COMPLETE,
281: /* Seek */
1.1.1.14 root 282: FDCEMU_RUN_SEEK_TOTRACK,
1.1.1.18! root 283: FDCEMU_RUN_SEEK_TOTRACK_SPIN_UP,
! 284: FDCEMU_RUN_SEEK_TOTRACK_MOTOR_ON,
1.1.1.17 root 285: FDCEMU_RUN_SEEK_VERIFY,
1.1.1.18! root 286: FDCEMU_RUN_SEEK_VERIFY_HEAD_OK,
! 287: FDCEMU_RUN_SEEK_VERIFY_NEXT_SECTOR_HEADER,
! 288: FDCEMU_RUN_SEEK_VERIFY_CHECK_SECTOR_HEADER,
1.1.1.15 root 289: FDCEMU_RUN_SEEK_COMPLETE,
290: /* Step / Step In / Step Out */
1.1.1.14 root 291: FDCEMU_RUN_STEP_ONCE,
1.1.1.18! root 292: FDCEMU_RUN_STEP_ONCE_SPIN_UP,
! 293: FDCEMU_RUN_STEP_ONCE_MOTOR_ON,
1.1.1.17 root 294: FDCEMU_RUN_STEP_VERIFY,
1.1.1.18! root 295: FDCEMU_RUN_STEP_VERIFY_HEAD_OK,
! 296: FDCEMU_RUN_STEP_VERIFY_NEXT_SECTOR_HEADER,
! 297: FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER,
1.1.1.15 root 298: FDCEMU_RUN_STEP_COMPLETE,
299: /* Read Sector */
300: FDCEMU_RUN_READSECTORS_READDATA,
1.1.1.18! root 301: FDCEMU_RUN_READSECTORS_READDATA_SPIN_UP,
! 302: FDCEMU_RUN_READSECTORS_READDATA_HEAD_LOAD,
! 303: FDCEMU_RUN_READSECTORS_READDATA_MOTOR_ON,
! 304: FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER,
1.1.1.15 root 305: FDCEMU_RUN_READSECTORS_READDATA_CHECK_SECTOR_HEADER,
1.1.1.17 root 306: FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START,
307: FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP,
308: FDCEMU_RUN_READSECTORS_CRC,
1.1.1.18! root 309: FDCEMU_RUN_READSECTORS_MULTI,
1.1.1.15 root 310: FDCEMU_RUN_READSECTORS_RNF,
311: FDCEMU_RUN_READSECTORS_COMPLETE,
312: /* Write Sector */
313: FDCEMU_RUN_WRITESECTORS_WRITEDATA,
1.1.1.18! root 314: FDCEMU_RUN_WRITESECTORS_WRITEDATA_SPIN_UP,
! 315: FDCEMU_RUN_WRITESECTORS_WRITEDATA_HEAD_LOAD,
! 316: FDCEMU_RUN_WRITESECTORS_WRITEDATA_MOTOR_ON,
! 317: FDCEMU_RUN_WRITESECTORS_WRITEDATA_NEXT_SECTOR_HEADER,
1.1.1.15 root 318: FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER,
1.1.1.17 root 319: FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_START,
320: FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP,
321: FDCEMU_RUN_WRITESECTORS_CRC,
1.1.1.18! root 322: FDCEMU_RUN_WRITESECTORS_MULTI,
1.1.1.15 root 323: FDCEMU_RUN_WRITESECTORS_RNF,
324: FDCEMU_RUN_WRITESECTORS_COMPLETE,
325: /* Read Address */
326: FDCEMU_RUN_READADDRESS,
1.1.1.18! root 327: FDCEMU_RUN_READADDRESS_SPIN_UP,
! 328: FDCEMU_RUN_READADDRESS_HEAD_LOAD,
! 329: FDCEMU_RUN_READADDRESS_MOTOR_ON,
! 330: FDCEMU_RUN_READADDRESS_NEXT_SECTOR_HEADER,
! 331: FDCEMU_RUN_READADDRESS_TRANSFER_START,
! 332: FDCEMU_RUN_READADDRESS_TRANSFER_LOOP,
! 333: FDCEMU_RUN_READADDRESS_RNF,
1.1.1.15 root 334: FDCEMU_RUN_READADDRESS_COMPLETE,
335: /* Read Track */
336: FDCEMU_RUN_READTRACK,
1.1.1.18! root 337: FDCEMU_RUN_READTRACK_SPIN_UP,
! 338: FDCEMU_RUN_READTRACK_HEAD_LOAD,
! 339: FDCEMU_RUN_READTRACK_MOTOR_ON,
1.1.1.17 root 340: FDCEMU_RUN_READTRACK_INDEX,
1.1.1.18! root 341: FDCEMU_RUN_READTRACK_TRANSFER_LOOP,
1.1.1.15 root 342: FDCEMU_RUN_READTRACK_COMPLETE,
343: /* Write Track */
344: FDCEMU_RUN_WRITETRACK,
1.1.1.18! root 345: FDCEMU_RUN_WRITETRACK_SPIN_UP,
! 346: FDCEMU_RUN_WRITETRACK_HEAD_LOAD,
! 347: FDCEMU_RUN_WRITETRACK_MOTOR_ON,
1.1.1.17 root 348: FDCEMU_RUN_WRITETRACK_INDEX,
1.1.1.18! root 349: FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP,
! 350: FDCEMU_RUN_WRITETRACK_COMPLETE,
! 351:
! 352: /* Motor Stop */
! 353: FDCEMU_RUN_MOTOR_STOP,
! 354: FDCEMU_RUN_MOTOR_STOP_WAIT,
! 355: FDCEMU_RUN_MOTOR_STOP_COMPLETE
1.1.1.14 root 356: };
357:
358:
359:
1.1.1.18! root 360: /*
! 361: * Standard hardware values for the FDC. This should allow to get very good timings' emulation
! 362: * when dealing with non protected disks that still require a correct speed (MSA or ST images)
! 363: *
! 364: * - WD1772's datasheet is based on a reference clock of 8 MHz, so delays expressed in milli-seconds
! 365: * will be slightly different for the Atari ST, whose FDC's clock is around 8.021247 MHz (but this is
! 366: * not really noticeable in practice, less than 0.3 %)
! 367: * - DD MFM encoding defines a standard signal of 4 micro sec per bit (a possible variation of +/- 10 %
! 368: * should still be possible). This means the WD1772 will read/write at 250 kbits/sec.
! 369: * Taking 4 us per bit means 32 us for a full byte, and with a 8 MHz clock, 256 cycles per byte.
! 370: * - The floppy drives used in the Atari ST are spinning at 300 RPM. Variations are possible, as long
! 371: * as it keeps the duration of an MFM bit in the required 4 us +/- 10 % (in practice, ST drives are often
! 372: * at 299-301 RPM)
! 373: * - When FDC runs at 8 MHz, the 250 kbits/s and 300 RPM give 6250 bytes for a standard track
! 374: * - When FDC runs at 8.021247 MHz (Atari ST), the 250.664 kbit/s and 300 RPM give 6267 bytes per track
! 375: *
! 376: * Notes on timings required for precise emulation :
! 377: * For a standard floppy recorded with a constant speed, the FDC will take 32 microsec
! 378: * to read/write 1 byte on the floppy. On STF with a 8 MHz CPU clock, this means one byte can be
! 379: * transferred every 256 cpu cycles. So, to get some correct timings as required by some games' protection
! 380: * we must update the emulated FDC's state every 256 cycles (it could be less frequently and still work,
! 381: * due to the 16 bytes DMA FIFO that will transfer data only 16 bytes at a time, every 256*16=4096 cycles)
! 382: */
! 383:
1.1.1.14 root 384:
1.1.1.18! root 385: #define FDC_CLOCK_STANDARD (8000000.L) /* In the WD1772's datasheet, all timings are related to a reference clock of 8 MHz */
! 386: #define FDC_DELAY_CYCLE_MFM_BYTE ( 4 * 8 * 8 ) /* 4 us per bit, 8 bits per byte, 8 MHz clock -> 256 cycles */
1.1.1.15 root 387: #define FDC_BITRATE_STANDARD 250000 /* read/write speed of the WD1772 in bits per sec */
388: #define FDC_RPM_STANDARD 300 /* 300 RPM or 5 spins per sec */
1.1.1.18! root 389: //#define FDC_TRACK_BYTES_STANDARD ( ( FDC_BITRATE_STANDARD / 8 ) / ( FDC_RPM_STANDARD / 60 ) ) /* 6250 bytes */
! 390: #define FDC_TRACK_BYTES_STANDARD 6268
1.1.1.14 root 391:
1.1.1.15 root 392: #define FDC_TRANSFER_BYTES_US( n ) ( ( n ) * 8 * 1000000.L / FDC_BITRATE_STANDARD ) /* micro sec to read/write 'n' bytes in the WD1772 */
1.1.1.14 root 393:
1.1.1.18! root 394: #define FDC_DELAY_IP_SPIN_UP 6 /* 6 index pulses to reach correct speed during spin up */
! 395: #define FDC_DELAY_IP_MOTOR_OFF 9 /* Turn off motor 9 index pulses after the last command */
! 396: #define FDC_DELAY_IP_ADDRESS_ID 5 /* 5 index pulses max when looking for next sector's address id field */
! 397:
1.1.1.14 root 398:
1.1.1.18! root 399: /* Delays are in micro sec */
! 400: #define FDC_DELAY_US_HEAD_LOAD ( 15 * 1000 ) /* Additionnal 15 ms delay to load the head in type II/III */
1.1.1.14 root 401:
1.1.1.18! root 402: /* 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) */
! 403: #define FDC_DELAY_US_INDEX_PULSE_LENGTH ( 3.71 * 1000 )
1.1 root 404:
1.1.1.17 root 405:
1.1.1.18! root 406: /* Internal delays to process commands are in fdc cycles for a 8 MHz clock */
! 407: #define FDC_DELAY_CYCLE_TYPE_I_PREPARE (90*8) /* Types I commands take at least 0.09 ms to execute */
1.1.1.17 root 408: /* (~740 cpu cycles @ 8 Mhz). [NP] : this was measured on a 520 STF */
409: /* and avoid returning immediately when command has no effect */
1.1.1.18! root 410: #define FDC_DELAY_CYCLE_TYPE_II_PREPARE (1*8) // 65 /* Start Type II commands immediately */
! 411: #define FDC_DELAY_CYCLE_TYPE_III_PREPARE (1*8) /* Start Type III commands immediately */
! 412: #define FDC_DELAY_CYCLE_TYPE_IV_PREPARE (100*8) /* FIXME [NP] : this was not measured */
! 413: #define FDC_DELAY_CYCLE_COMMAND_COMPLETE (1*8) /* Number of cycles before going to the _COMPLETE state (~8 cpu cycles) */
! 414: #define FDC_DELAY_CYCLE_COMMAND_IMMEDIATE (0) /* Number of cycles to go immediately to another state */
! 415:
! 416: /* When the drive is switched off or if there's no floppy, some commands will wait forever */
! 417: /* as they can't find the next index pulse. Instead of continuously testing if a valid drive */
! 418: /* or floppy becomes available (which would slow down emulation), we only test every 50000 FDC cycles, */
! 419: /* which shouldn't give any noticeable emulation error */
! 420: #define FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY 50000
1.1.1.5 root 421:
1.1.1.18! root 422: /* Update the floppy's angular position on a regular basis to detect the index pulses */
! 423: #define FDC_DELAY_CYCLE_REFRESH_INDEX_PULSE 500
1.1.1.5 root 424:
425:
1.1.1.18! root 426: #define FDC_DMA_SECTOR_SIZE 512 /* Sector count at $ff8606 is for 512 bytes blocks */
! 427: #define FDC_DMA_FIFO_SIZE 16 /* DMA transfers blocks of 16 bytes at a time */
1.1.1.5 root 428:
1.1.1.15 root 429: #define FDC_PHYSICAL_MAX_TRACK 90 /* Head can't go beyond 90 tracks */
1.1 root 430:
1.1.1.2 root 431:
1.1.1.15 root 432: #define FDC_STEP_RATE ( FDC.CR & 0x03 ) /* Bits 0 and 1 of the current type I command */
433:
434: static int FDC_StepRate_ms[] = { 6 , 12 , 2 , 3 }; /* Controlled by bits 1 and 0 (r1/r0) in type I commands */
435:
436:
1.1.1.17 root 437:
438:
1.1.1.15 root 439: #define FDC_FAST_FDC_FACTOR 10 /* Divide all delays by this value when --fastfdc is used */
440:
1.1.1.18! root 441: /* Standard ST floppies are double density ; to simulate HD or ED floppies, we use */
! 442: /* a density factor to have x2 or x4 bytes more during 1 FDC cycle */
! 443: #define FDC_DENSITY_FACTOR_DD 1
! 444: #define FDC_DENSITY_FACTOR_HD 2 /* For a HD disk, we get x2 bytes than DD */
! 445: #define FDC_DENSITY_FACTOR_ED 4 /* For a ED disk, we get x4 bytes than DD */
! 446:
! 447:
! 448: #define FDC_EMULATION_MODE_INTERNAL 1 /* Use fdc.c to handle emulation (ST, MSA, DIM and STX images) */
! 449: #define FDC_EMULATION_MODE_IPF 2 /* Use floppy_ipf.c to handle emulation (IPF, CTR images) */
! 450:
1.1.1.15 root 451:
452: typedef struct {
453: /* WD1772 internal registers */
454: Uint8 DR; /* Data Register */
455: Uint8 TR; /* Track Register */
456: Uint8 SR; /* Sector Register */
457: Uint8 CR; /* Command Register */
458: Uint8 STR; /* Status Register */
459: int StepDirection; /* +1 (Step In) or -1 (Step Out) */
460:
1.1.1.18! root 461: Uint8 SideSignal; /* Side 0 or 1 */
! 462: int DriveSelSignal; /* 0 or 1 for drive A or B ; or -1 if no drive selected */
! 463: Uint8 IRQ_Signal; /* 0 if IRQ is not set, else value depends on the source of the IRQ */
! 464:
1.1.1.15 root 465: /* Other variables */
1.1.1.18! root 466: int Command; /* FDC emulation command currently being executed */
1.1.1.15 root 467: int CommandState; /* Current state for the running command */
468: Uint8 CommandType; /* Type of latest FDC command (1,2,3 or 4) */
1.1.1.18! root 469: bool ReplaceCommandPossible; /* true if the current command can be replaced by another one (see notes) */
! 470:
! 471: Uint8 Status_Temp; /* Temporary content of the status register */
! 472: bool StatusTypeI; /* When true, STR will report the status of a type I command */
! 473: int IndexPulse_Counter; /* To count the number of rotations when motor is ON */
! 474: Uint8 NextSector_ID_Field_TR; /* Track value in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
! 475: Uint8 NextSector_ID_Field_SR; /* Sector value in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
! 476: Uint8 NextSector_ID_Field_LEN; /* Sector's length in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
! 477: Uint8 NextSector_ID_Field_CRC_OK; /* CRC OK or not in the next ID Field after a call to FDC_NextSectorID_FdcCycles_ST() */
! 478: Uint8 InterruptCond; /* For a type IV force interrupt, contains the condition on the lower 4 bits */
1.1.1.15 root 479:
1.1.1.18! root 480: int EmulationMode; /* FDC_EMULATION_MODE_INTERNAL or FDC_EMULATION_MODE_IPF */
1.1.1.15 root 481: } FDC_STRUCT;
482:
483:
484: typedef struct {
485: /* DMA internal registers */
486: Uint16 Status;
487: Uint16 Mode;
488: Uint16 SectorCount;
1.1.1.18! root 489: Sint16 BytesInSector;
! 490:
! 491: Uint8 FIFO[ FDC_DMA_FIFO_SIZE ];
! 492: int FIFO_Size; /* Between 0 and FDC_DMA_FIFO_SIZE */
! 493:
! 494: Uint16 ff8604_recent_val; /* Most recent value read/written at $ff8604 in fdc/hdc mode (bit4=0 in $ff8606) */
1.1.1.15 root 495:
496: /* Variables to handle our DMA buffer */
497: int PosInBuffer;
1.1.1.18! root 498: int PosInBufferTransfer; /* FIXME REMOVE */
1.1.1.15 root 499: int BytesToTransfer;
500: } FDC_DMA_STRUCT;
501:
502:
1.1.1.18! root 503: typedef struct {
! 504: bool Enabled;
! 505: bool DiskInserted;
! 506: int RPM; /* Rotation Per Minutes * 1000 */
! 507: int Density; /* 1 for DD (720 kB), 2 for HD (1.4 MB), 4 for ED (2.8 MB) */
! 508: Uint8 HeadTrack; /* Current position of the head */
! 509: // Uint8 Motor; /* State of the drive's motor : 0=OFF 1=ON */
! 510: Uint8 NumberOfHeads; /* 1 for single sided drive, 2 for double sided */
! 511:
! 512: Uint64 IndexPulse_Time; /* CyclesGlobalClockCounter value last time we had an index pulse with motor ON */
! 513: } FDC_DRIVE_STRUCT;
! 514:
! 515:
! 516: /**
! 517: * Bytes to transfer with type II/III commands are stored in this buffer
! 518: * which associates a specific delay to each byte. This allows to
! 519: * have a common method to transfer data from ST/MSA disk images (with fixed
! 520: * timing), as well as data from STX disk images (with possible timing variations)
! 521: */
! 522: typedef struct {
! 523: int Size;
! 524: int PosRead;
! 525:
! 526: struct {
! 527: Uint8 Byte;
! 528: Uint16 Timing;
! 529: } Data [ FDC_TRACK_BYTES_STANDARD*4+1000 ];
! 530: } FDC_BUFFER_STRUCT;
! 531:
! 532:
1.1.1.15 root 533: static FDC_STRUCT FDC; /* All variables related to the WD1772 emulation */
534: static FDC_DMA_STRUCT FDC_DMA; /* All variables related to the DMA transfer */
1.1.1.18! root 535: static FDC_DRIVE_STRUCT FDC_DRIVES[ MAX_FLOPPYDRIVES ]; /* A: and B: */
! 536: static FDC_BUFFER_STRUCT FDC_BUFFER; /* Buffer of Timing/Byte to transfer with the FDC */
1.1.1.15 root 537:
1.1.1.18! root 538: static Uint8 DMADiskWorkSpace[ FDC_TRACK_BYTES_STANDARD*4+1000 ];/* Workspace used to transfer bytes between floppy and DMA */
1.1.1.15 root 539: /* It should be large enough to contain a whole track */
1.1.1.18! root 540: /* We use a x4 factor when we need to simulate HD and ED too */
! 541:
1.1.1.15 root 542:
543:
544: /*--------------------------------------------------------------*/
545: /* Local functions prototypes */
546: /*--------------------------------------------------------------*/
547:
1.1.1.18! root 548: static Uint32 FDC_DelayToFdcCycles ( Uint32 Delay_micro );
! 549: static Uint32 FDC_FdcCyclesToCpuCycles ( Uint32 FdcCycles );
! 550: static Uint32 FDC_CpuCyclesToFdcCycles ( Uint32 CpuCycles );
! 551: static void FDC_StartTimer_FdcCycles ( int FdcCycles , int InternalCycleOffset );
! 552: static int FDC_TransferByte_FdcCycles ( int NbBytes );
1.1.1.15 root 553: static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC );
554:
555: static void FDC_ResetDMA ( void );
1.1.1.18! root 556:
! 557: static int FDC_GetEmulationMode ( void );
! 558: static void FDC_UpdateAll ( void );
! 559: static int FDC_GetSectorsPerTrack ( int Drive , int Track , int Side );
! 560: static int FDC_GetSidesPerDisk ( int Drive , int Track );
! 561: static int FDC_GetDensity ( int Drive );
! 562:
! 563: static Uint32 FDC_GetCyclesPerRev_FdcCycles ( int Drive );
! 564: static void FDC_IndexPulse_Update ( void );
! 565: static void FDC_IndexPulse_Init ( int Drive );
1.1.1.17 root 566:
1.1.1.15 root 567: static void FDC_Update_STR ( Uint8 DisableBits , Uint8 EnableBits );
568: static int FDC_CmdCompleteCommon ( bool DoInt );
1.1.1.18! root 569: static bool FDC_VerifyTrack ( void );
1.1.1.15 root 570: static int FDC_UpdateMotorStop ( void );
571: static int FDC_UpdateRestoreCmd ( void );
572: static int FDC_UpdateSeekCmd ( void );
573: static int FDC_UpdateStepCmd ( void );
574: static int FDC_UpdateReadSectorsCmd ( void );
575: static int FDC_UpdateWriteSectorsCmd ( void );
576: static int FDC_UpdateReadAddressCmd ( void );
577: static int FDC_UpdateReadTrackCmd ( void );
1.1.1.18! root 578: static int FDC_UpdateWriteTrackCmd ( void );
1.1.1.15 root 579:
1.1.1.18! root 580: static bool FDC_Set_MotorON ( Uint8 FDC_CR );
1.1.1.15 root 581: static int FDC_TypeI_Restore ( void );
582: static int FDC_TypeI_Seek ( void );
583: static int FDC_TypeI_Step ( void );
584: static int FDC_TypeI_StepIn ( void );
585: static int FDC_TypeI_StepOut ( void );
586: static int FDC_TypeII_ReadSector ( void );
587: static int FDC_TypeII_WriteSector(void);
588: static int FDC_TypeIII_ReadAddress ( void );
589: static int FDC_TypeIII_ReadTrack ( void );
590: static int FDC_TypeIII_WriteTrack ( void );
1.1.1.18! root 591: static int FDC_TypeIV_ForceInterrupt ( void );
1.1.1.15 root 592:
593: static int FDC_ExecuteTypeICommands ( void );
594: static int FDC_ExecuteTypeIICommands ( void );
595: static int FDC_ExecuteTypeIIICommands ( void );
596: static int FDC_ExecuteTypeIVCommands ( void );
597: static void FDC_ExecuteCommand ( void );
598:
599: static void FDC_WriteSectorCountRegister ( void );
600: static void FDC_WriteCommandRegister ( void );
601: static void FDC_WriteTrackRegister ( void );
602: static void FDC_WriteSectorRegister ( void );
603: static void FDC_WriteDataRegister ( void );
604:
1.1.1.18! root 605: static int FDC_NextSectorID_FdcCycles_ST ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side );
! 606: static Uint8 FDC_NextSectorID_TR_ST ( void );
! 607: static Uint8 FDC_NextSectorID_SR_ST ( void );
! 608: static Uint8 FDC_NextSectorID_LEN_ST ( void );
! 609: static Uint8 FDC_NextSectorID_CRC_OK_ST ( void );
! 610: static Uint8 FDC_ReadSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int *pSectorSize );
! 611: static Uint8 FDC_WriteSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize );
! 612: static Uint8 FDC_ReadAddress_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side );
! 613: static Uint8 FDC_ReadTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side );
! 614: static Uint8 FDC_WriteTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side , int TrackSize );
1.1 root 615:
1.1.1.2 root 616:
617: /*-----------------------------------------------------------------------*/
1.1.1.9 root 618: /**
619: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
620: */
1.1.1.18! root 621: void FDC_MemorySnapShot_Capture(bool bSave)
1.1 root 622: {
1.1.1.15 root 623: MemorySnapShot_Store(&FDC, sizeof(FDC));
624: MemorySnapShot_Store(&FDC_DMA, sizeof(FDC_DMA));
1.1.1.18! root 625: MemorySnapShot_Store(&FDC_DRIVES, sizeof(FDC_DRIVES));
! 626: MemorySnapShot_Store(&FDC_BUFFER, sizeof(FDC_BUFFER_STRUCT));
1.1.1.15 root 627:
628: MemorySnapShot_Store(DMADiskWorkSpace, sizeof(DMADiskWorkSpace));
1.1 root 629: }
630:
1.1.1.2 root 631:
632: /*-----------------------------------------------------------------------*/
1.1.1.9 root 633: /**
1.1.1.17 root 634: * Change the color of the drive's led color in the statusbar, depending
1.1.1.18! root 635: * on the state of the busy bit in STR
1.1.1.17 root 636: */
1.1.1.18! root 637: void FDC_Drive_Set_BusyLed ( Uint8 STR )
1.1.1.17 root 638: {
1.1.1.18! root 639: //fprintf ( stderr ,"fdc led %d %x\n" , FDC.DriveSelSignal , STR );
! 640: if ( FDC.DriveSelSignal < 0 )
! 641: return; /* no drive selected */
1.1.1.17 root 642:
1.1.1.18! root 643: if ( STR & FDC_STR_BIT_BUSY )
! 644: Statusbar_SetFloppyLed ( FDC.DriveSelSignal , LED_STATE_ON_BUSY );
1.1.1.17 root 645: else
1.1.1.18! root 646: Statusbar_SetFloppyLed ( FDC.DriveSelSignal , LED_STATE_ON );
! 647: }
! 648:
! 649:
! 650: /*-----------------------------------------------------------------------*/
! 651: /**
! 652: * Return a small text + length with the current values of the FDC's registers
! 653: * This text is displayed in the statusbar and it looks like :
! 654: * CC:xx HH:TT:SS:s
! 655: * CC=command in 2 letters
! 656: * xx=command in hexa
! 657: * HH=physical head's position
! 658: * TT=track register
! 659: * SS=sector register
! 660: * s=side
! 661: */
! 662: int FDC_Get_Statusbar_Text ( char *text, size_t maxlen )
! 663: {
! 664: Uint8 Command , Head , Track , Sector , Side;
! 665: char CommandText[ 3 ];
! 666: size_t written;
! 667: int Drive;
! 668:
! 669: Drive = FDC.DriveSelSignal;
! 670: if ( Drive < 0 ) /* If no drive enabled, use drive O for Head */
! 671: Drive = 0;
! 672:
! 673: if ( FDC_GetEmulationMode() == FDC_EMULATION_MODE_INTERNAL )
! 674: {
! 675: Command = FDC.CR;
! 676: Head = FDC_DRIVES[ Drive ].HeadTrack;
! 677: Track = FDC.TR;
! 678: Sector = FDC.SR;
! 679: Side = FDC.SideSignal;
! 680: }
! 681: else /* FDC_EMULATION_MODE_IPF */
! 682: {
! 683: IPF_FDC_StatusBar ( &Command , &Head , &Track , &Sector , &Side );
! 684: }
! 685:
! 686: if ( ( Command & 0xf0 ) == 0x00 ) strcpy ( CommandText , "RE" ); /* Restore */
! 687: else if ( ( Command & 0xf0 ) == 0x10 ) strcpy ( CommandText , "SE" ); /* Seek */
! 688: else if ( ( Command & 0xe0 ) == 0x20 ) strcpy ( CommandText , "ST" ); /* Step */
! 689: else if ( ( Command & 0xe0 ) == 0x40 ) strcpy ( CommandText , "SI" ); /* Step In */
! 690: else if ( ( Command & 0xe0 ) == 0x50 ) strcpy ( CommandText , "SO" ); /* Step Out */
! 691: else if ( ( Command & 0xe0 ) == 0x80 ) strcpy ( CommandText , "RS" ); /* Read Sector */
! 692: else if ( ( Command & 0xe0 ) == 0xa0 ) strcpy ( CommandText , "WS" ); /* Write Sector */
! 693: else if ( ( Command & 0xf0 ) == 0xc0 ) strcpy ( CommandText , "RA" ); /* Read Address */
! 694: else if ( ( Command & 0xf0 ) == 0xe0 ) strcpy ( CommandText , "RT" ); /* Read Track */
! 695: else if ( ( Command & 0xf0 ) == 0xf0 ) strcpy ( CommandText , "WT" ); /* Write Track */
! 696: else strcpy ( CommandText , "FI" ); /* Force Int */
! 697:
! 698: written = snprintf ( text, maxlen, "%s:%02X %02X:%02X:%02X:%d" , CommandText , Command , Head , Track , Sector , Side );
! 699: assert(written < maxlen); /* more space needs to be allocated */
! 700: return written;
1.1.1.17 root 701: }
702:
703:
704: /*-----------------------------------------------------------------------*/
705: /**
1.1.1.18! root 706: * Convert a delay in micro seconds to its equivalent of fdc cycles
! 707: * (delays in the WD1772 specs are relative to a 8 MHz reference clock)
1.1.1.9 root 708: */
1.1.1.18! root 709: static Uint32 FDC_DelayToFdcCycles ( Uint32 Delay_micro )
1.1 root 710: {
1.1.1.18! root 711: Uint32 FdcCycles;
! 712:
! 713: FdcCycles = (Uint32) ( ( (Uint64) FDC_CLOCK_STANDARD * Delay_micro ) / 1000000 );
1.1.1.15 root 714:
1.1.1.18! root 715: //fprintf ( stderr , "fdc state %d delay %d us %d fdc cycles\n" , FDC.Command , Delay_micro , FdcCycles );
! 716: return FdcCycles;
! 717: }
! 718:
! 719:
! 720: /*-----------------------------------------------------------------------*/
! 721: /**
! 722: * Convert a number of fdc cycles at freq MachineClocks.FDC_Freq to a number
! 723: * of cpu cycles at freq MachineClocks.CPU_Freq
! 724: * TODO : we use a fixed 8 MHz clock and nCpuFreqShift to convert cycles for our
! 725: * internal timers in cycInt.c. This should be replaced some days by using
! 726: * MachineClocks.CPU_Freq and not using nCpuFreqShift anymore.
! 727: * (for Falcon, we multiply cycles by 2 to simulate a freq in the 8 MHz range)
! 728: */
! 729: static Uint32 FDC_FdcCyclesToCpuCycles ( Uint32 FdcCycles )
! 730: {
! 731: Uint32 CpuCycles;
1.1.1.15 root 732:
1.1.1.18! root 733: /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
1.1.1.15 root 734: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
735: /* FIXME : as stated above, this should be handled better, without involving 8 MHz CPU_Freq */
736: if ( ConfigureParams.System.nMachineType == MACHINE_FALCON )
1.1.1.18! root 737: FdcCycles *= 2; /* correct delays for a 8 MHz FDC_Freq clock instead of 16 */
! 738:
! 739: // CpuCycles = rint ( ( (Uint64)FdcCycles * MachineClocks.CPU_Freq ) / MachineClocks.FDC_Freq );
! 740: CpuCycles = rint ( ( (Uint64)FdcCycles * 8021247.L ) / MachineClocks.FDC_Freq );
! 741: CpuCycles >>= nCpuFreqShift; /* Compensate for x2 or x4 cpu speed */
1.1.1.15 root 742:
1.1.1.18! root 743: //fprintf ( stderr , "fdc command %d machine %d fdc cycles %d cpu cycles %d\n" , FDC.Command , ConfigureParams.System.nMachineType , FdcCycles , CpuCycles );
1.1.1.17 root 744: //if ( Delay==4104) Delay=4166; // 4166 : decade demo
1.1.1.18! root 745: return CpuCycles;
! 746: }
! 747:
! 748:
! 749: /*-----------------------------------------------------------------------*/
! 750: /**
! 751: * Convert a number of cpu cycles at freq MachineClocks.CPU_Freq to a number
! 752: * of fdc cycles at freq MachineClocks.FDC_Freq (this is the opposite
! 753: * of FDC_FdcCyclesToCpuCycles)
! 754: * TODO : we use a fixed 8 MHz clock and nCpuFreqShift to convert cycles for our
! 755: * internal timers in cycInt.c. This should be replaced some days by using
! 756: * MachineClocks.CPU_Freq and not using nCpuFreqShift anymore.
! 757: */
! 758: static Uint32 FDC_CpuCyclesToFdcCycles ( Uint32 CpuCycles )
! 759: {
! 760: int FdcCycles;
! 761:
! 762:
! 763: CpuCycles <<= nCpuFreqShift; /* Compensate for x2 or x4 cpu speed */
! 764: // FdcCycles = rint ( ( (Uint64)CpuCycles * MachineClocks.FDC_Freq ) / MachineClocks.CPU_Freq );
! 765: FdcCycles = rint ( ( (Uint64)CpuCycles * MachineClocks.FDC_Freq ) / 8021247.L );
! 766:
! 767: /* Our conversion expects FDC_Freq to be nearly the same as CPU_Freq (8 Mhz) */
! 768: /* but the Falcon uses a 16 MHz clock for the Ajax FDC */
! 769: /* FIXME : as stated above, this should be handled better, without involving 8 MHz CPU_Freq */
! 770: if ( ConfigureParams.System.nMachineType == MACHINE_FALCON )
! 771: FdcCycles /= 2; /* correct delays for a 8 MHz FDC_Freq clock instead of 16 */
! 772:
! 773: //fprintf ( stderr , "fdc state %d delay %d cpu cycles %d fdc cycles\n" , FDC.Command , CpuCycles , FdcCycles );
! 774: return FdcCycles;
1.1 root 775: }
776:
1.1.1.2 root 777:
778: /*-----------------------------------------------------------------------*/
1.1.1.9 root 779: /**
1.1.1.17 root 780: * Start an internal timer to handle the FDC's events.
781: * If "fast floppy" mode is used, we speed up the timer by dividing
782: * the number of cycles by a fixed number.
783: */
1.1.1.18! root 784: static void FDC_StartTimer_FdcCycles ( int FdcCycles , int InternalCycleOffset )
1.1.1.17 root 785: {
1.1.1.18! root 786: //fprintf ( stderr , "fdc start timer %d cycles\n" , FdcCycles );
! 787:
! 788: if ( ( ConfigureParams.DiskImage.FastFloppy ) && ( FdcCycles > FDC_FAST_FDC_FACTOR ) )
! 789: FdcCycles /= FDC_FAST_FDC_FACTOR;
! 790:
! 791: CycInt_AddRelativeInterruptWithOffset ( FDC_FdcCyclesToCpuCycles ( FdcCycles ) , INT_CPU_CYCLE , INTERRUPT_FDC , InternalCycleOffset );
! 792: }
1.1.1.17 root 793:
794:
1.1.1.18! root 795: /*-----------------------------------------------------------------------*/
! 796: /**
! 797: * Return the number of FDC cycles required to read/write 'nb' bytes
! 798: * This function will always be called when FDC.DriveSelSignal >= 0, as
! 799: * there's no case where we transfer bytes if no drive is enabled. This
! 800: * means we can safely call FDC_GetDensity() here to simulate HD/ED floppies.
! 801: */
! 802: static int FDC_TransferByte_FdcCycles ( int NbBytes )
! 803: {
! 804: //fprintf ( stderr , "fdc state %d transfer %d bytes\n" , FDC.Command , NbBytes );
! 805: return ( NbBytes * FDC_DELAY_CYCLE_MFM_BYTE ) / FDC_DRIVES[ FDC.DriveSelSignal ].Density;
1.1.1.17 root 806: }
807:
808:
809: /*-----------------------------------------------------------------------*/
810: /**
1.1.1.15 root 811: * Compute the CRC16 of 'nb' bytes stored in 'buf'.
1.1.1.9 root 812: */
1.1.1.15 root 813: static void FDC_CRC16 ( Uint8 *buf , int nb , Uint16 *pCRC )
1.1 root 814: {
1.1.1.15 root 815: int i;
816:
817: crc16_reset ( pCRC );
818: for ( i=0 ; i<nb ; i++ )
819: {
820: // fprintf ( stderr , "fdc crc16 %d 0x%x\n" , i , buf[ i ] );
821: crc16_add_byte ( pCRC , buf[ i ] );
822: }
823: // fprintf ( stderr , "fdc crc16 0x%x 0x%x\n" , *pCRC>>8 , *pCRC & 0xff );
1.1 root 824: }
825:
1.1.1.2 root 826:
827: /*-----------------------------------------------------------------------*/
1.1.1.9 root 828: /**
1.1.1.18! root 829: * Init variables used in FDC and DMA emulation
! 830: */
! 831: void FDC_Init ( void )
! 832: {
! 833: int i;
! 834:
! 835: LOG_TRACE ( TRACE_FDC , "fdc init\n" );
! 836:
! 837: for ( i=0 ; i<MAX_FLOPPYDRIVES ; i++ )
! 838: {
! 839: FDC_DRIVES[ i ].Enabled = true;
! 840: FDC_DRIVES[ i ].DiskInserted = false;
! 841: FDC_DRIVES[ i ].RPM = FDC_RPM_STANDARD * 1000;
! 842: FDC_DRIVES[ i ].Density = FDC_DENSITY_FACTOR_DD;
! 843: FDC_DRIVES[ i ].HeadTrack = 0; /* Set all drives to track 0 */
! 844: FDC_DRIVES[ i ].NumberOfHeads = 2; /* Double sided drive */
! 845: FDC_DRIVES[ i ].IndexPulse_Time = 0;
! 846: }
! 847:
! 848: FDC_Buffer_Reset();
! 849:
! 850: FDC.EmulationMode = FDC_EMULATION_MODE_INTERNAL;
! 851: }
! 852:
! 853:
! 854: /*-----------------------------------------------------------------------*/
! 855: /**
1.1.1.15 root 856: * Reset variables used in FDC and DMA emulation
1.1.1.9 root 857: */
1.1.1.18! root 858:
! 859: /* This function is called after a hardware reset of the FDC.
! 860: * Cold reset is when the computer is turned off/on.
! 861: * Warm reset is when the reset button is pressed or the 68000
! 862: * RESET instruction is used.
! 863: * On warm reset, TR and DR should not be reset.
! 864: * STR is set to 0 and SR is set to 1 (verified on a real STF)
! 865: */
! 866: void FDC_Reset ( bool bCold )
1.1 root 867: {
1.1.1.15 root 868: int i;
1.1.1.6 root 869:
1.1.1.18! root 870: LOG_TRACE ( TRACE_FDC , "fdc reset mode=%s\n" , bCold?"cold":"warm" );
1.1.1.15 root 871:
1.1.1.18! root 872: /* Clear out FDC registers */
1.1.1.15 root 873: FDC.CR = 0;
874: FDC.STR = 0;
1.1.1.18! root 875: FDC.SR = 1;
! 876: FDC.StatusTypeI = false;
! 877:
! 878: /* On cold reset, TR and DR should be reset */
! 879: /* On warm reset, TR and DR value should be kept */
! 880: if ( bCold )
! 881: {
! 882: FDC.TR = 0;
! 883: FDC.DR = 0;
! 884: FDC_DMA.ff8604_recent_val = 0; /* Only set to 0 on cold reset */
! 885: }
1.1.1.15 root 886: FDC.StepDirection = 1;
887:
888: FDC.Command = FDCEMU_CMD_NULL; /* FDC emulation command currently being executed */
889: FDC.CommandState = FDCEMU_RUN_NULL;
890: FDC.CommandType = 0;
1.1.1.18! root 891: FDC.InterruptCond = 0;
! 892: FDC.IRQ_Signal = 0;
! 893:
! 894: FDC.IndexPulse_Counter = 0;
! 895: for ( i=0 ; i<MAX_FLOPPYDRIVES ; i++ )
! 896: {
! 897: FDC_DRIVES[ i ].IndexPulse_Time = 0; /* Current IP's locations are lost after a reset (motor is now OFF) */
! 898: }
1.1.1.15 root 899:
900: FDC_DMA.Status = 1; /* no DMA error and SectorCount=0 */
901: FDC_DMA.Mode = 0;
1.1.1.18! root 902:
1.1.1.15 root 903: FDC_ResetDMA();
904:
1.1.1.18! root 905: FDC_Buffer_Reset();
! 906:
! 907: /* Also reset IPF emulation */
! 908: IPF_Reset();
1.1 root 909: }
910:
1.1.1.2 root 911:
912: /*-----------------------------------------------------------------------*/
1.1.1.9 root 913: /**
1.1.1.15 root 914: * Reset DMA (clear internal 16 bytes buffer)
1.1.1.9 root 915: *
916: * This is done by 'toggling' bit 8 of the DMA Mode Control register
1.1.1.18! root 917: * This will empty the FIFOs and reset Sector Count to 0
1.1.1.9 root 918: */
1.1.1.15 root 919: static void FDC_ResetDMA ( void )
1.1 root 920: {
1.1.1.15 root 921: int FrameCycles, HblCounterVideo, LineCycles;
1.1 root 922:
1.1.1.15 root 923: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
924: LOG_TRACE(TRACE_FDC, "fdc reset dma VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18! root 925: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 926:
! 927: /* Empty FIFO */
! 928: FDC_DMA.FIFO_Size = 0;
1.1.1.15 root 929:
930: /* Reset bytes count for current DMA sector */
1.1.1.18! root 931: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
! 932: FDC_DMA.SectorCount = 0; /* After a reset, sector count is 0 (verified on a real STF) */
1.1.1.15 root 933:
1.1.1.18! root 934: /* Reset internal variables used to handle DMA transfer */
1.1.1.15 root 935: FDC_DMA.PosInBuffer = 0;
936: FDC_DMA.PosInBufferTransfer = 0;
937: FDC_DMA.BytesToTransfer = 0;
1.1.1.3 root 938:
1.1.1.10 root 939: /* Reset HDC command status */
1.1.1.15 root 940: HDC_ResetCommandStatus();
1.1 root 941: }
942:
1.1.1.2 root 943:
944: /*-----------------------------------------------------------------------*/
1.1.1.9 root 945: /**
1.1.1.15 root 946: * Set DMA Status at $ff8606
1.1.1.9 root 947: *
1.1.1.18! root 948: * Bit 0 - Error Status (0=Error 1=No error)
! 949: * Bit 1 - Sector Count Zero Status (0=Sector Count Zero)
! 950: * Bit 2 - Data Request signal from the FDC
1.1.1.9 root 951: */
1.1.1.15 root 952: void FDC_SetDMAStatus ( bool bError )
1.1 root 953: {
1.1.1.15 root 954: /* Set error bit */
1.1.1.6 root 955: if (!bError)
1.1.1.15 root 956: FDC_DMA.Status |= 0x1; /* No Error, set bit 0 */
1.1.1.6 root 957: else
1.1.1.15 root 958: FDC_DMA.Status &= ~0x1; /* Error, clear bit 0 */
1.1 root 959: }
960:
1.1.1.2 root 961:
962: /*-----------------------------------------------------------------------*/
1.1.1.9 root 963: /**
1.1.1.18! root 964: * Return the value of bit 8 in the FDC's DMA mode control register.
! 965: * 0=dma read 0x100=dma write
1.1.1.9 root 966: */
1.1.1.18! root 967: int FDC_DMA_GetModeControl_R_WR ( void )
1.1 root 968: {
1.1.1.18! root 969: return FDC_DMA.Mode & 0x100;
1.1 root 970: }
971:
1.1.1.2 root 972:
973: /*-----------------------------------------------------------------------*/
1.1.1.9 root 974: /**
1.1.1.18! root 975: * Add a byte to the DMA's FIFO buffer (read from disk).
! 976: * If the buffer is full and DMA is ON, write the FIFO's 16 bytes to DMA's address.
1.1.1.15 root 977: *
1.1.1.18! root 978: * NOTE [NP] : the DMA is connected to the FDC, each time a DRQ is made by the FDC,
! 979: * it's handled by the DMA and stored in the DMA's 16 bytes buffer. This means
1.1.1.15 root 980: * FDC_STR_BIT_LOST_DATA will never be set (but data can be lost if FDC_DMA.SectorCount==0)
1.1.1.18! root 981: *
! 982: * NOTE [NP] : as seen on a real STF, the unused bits when reading DMA Status at $ff8606
! 983: * are also changed by the DMA operations (this might not be complete, but seems quite reproducible) :
! 984: * - reading a byte from the FDC to the DMA will change unused bits in the lowest byte at $ff8604
! 985: * - transferring the 16 byte DMA buffer to RAM will change the unused bits in the 2 bytes at $ff8604
! 986: *
! 987: * In all cases, the byte read from the FDC is transferred to the DMA, even if DMA sector count is 0, so
! 988: * we must always update lowest byte of ff8604_recent_val.
! 989: * DMA FIFO is transferred only when DMA sector count is >0, so high byte of ff8604_recent_val will be
! 990: * updated only in that case.
1.1.1.14 root 991: */
1.1.1.18! root 992: void FDC_DMA_FIFO_Push ( Uint8 Byte )
1.1.1.14 root 993: {
1.1.1.15 root 994: Uint32 Address;
1.1.1.14 root 995:
1.1.1.18! root 996: //fprintf ( stderr , "dma push pos=%d byte=%x %lld\n" , FDC_DMA.FIFO_Size , Byte , CyclesGlobalClockCounter );
! 997:
! 998: /* Store the byte that was just read from FDC's Data Register */
! 999: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | Byte;
1.1.1.14 root 1000:
1.1.1.15 root 1001: if ( FDC_DMA.SectorCount == 0 )
1002: {
1003: //FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* If DMA is OFF, data are lost -> Not on the ST */
1.1.1.18! root 1004: FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
! 1005: return;
1.1.1.15 root 1006: }
1007:
1.1.1.18! root 1008: FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
! 1009:
! 1010: FDC_DMA.FIFO [ FDC_DMA.FIFO_Size++ ] = Byte;
! 1011:
! 1012: if ( FDC_DMA.FIFO_Size < FDC_DMA_FIFO_SIZE ) /* FIFO is not full yet */
! 1013: return;
! 1014:
! 1015: /* FIFO full : transfer data from FIFO to RAM and update DMA address */
1.1.1.15 root 1016: Address = FDC_GetDMAAddress();
1.1.1.18! root 1017: STMemory_SafeCopy ( Address , FDC_DMA.FIFO , FDC_DMA_FIFO_SIZE , "FDC DMA push to fifo" );
! 1018: FDC_WriteDMAAddress ( Address + FDC_DMA_FIFO_SIZE );
! 1019: FDC_DMA.FIFO_Size = 0; /* FIFO is now empty again */
! 1020:
! 1021: /* Store the last word that was just transferred by the DMA */
! 1022: FDC_DMA.ff8604_recent_val = ( FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-2 ] << 8 ) | FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-1 ];
1.1.1.15 root 1023:
1024: /* Update Sector Count */
1.1.1.18! root 1025: FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
1.1.1.15 root 1026: if ( FDC_DMA.BytesInSector <= 0 )
1027: {
1028: FDC_DMA.SectorCount--;
1.1.1.18! root 1029: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
1.1.1.15 root 1030: }
1.1 root 1031: }
1032:
1.1.1.2 root 1033:
1034: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1035: /**
1.1.1.18! root 1036: * Get a byte from the DMA's FIFO buffer (write to disk).
! 1037: * If the buffer is empty and DMA is ON, load 16 bytes in the FIFO from DMA's address.
1.1.1.15 root 1038: *
1.1.1.18! root 1039: * NOTE [NP] : on a real ST, there're two 16 byte DMA FIFO, this allows to refill one FIFO
! 1040: * while the other FIFO is used to transfer data to the FDC. We don't emulate this at the
! 1041: * moment, as it doesn't cause any problem (when a DMA is set to write mode, we would need
! 1042: * to prefill 32 bytes instead of 16 bytes as we do now)
1.1.1.15 root 1043: *
1.1.1.18! root 1044: * NOTE [NP] : as with FDC_DMA_FIFO_Push, this also changes the unused bits at $ff8606
1.1.1.9 root 1045: */
1.1.1.18! root 1046: Uint8 FDC_DMA_FIFO_Pull ( void )
1.1 root 1047: {
1.1.1.15 root 1048: Uint32 Address;
1.1.1.18! root 1049: Uint8 Byte;
1.1.1.15 root 1050:
1.1.1.18! root 1051: //fprintf ( stderr , "fifo pull %d %d %d\n" , FDC_DMA.BytesToTransfer , FDC_DMA.BytesInSector , FDC_DMA.SectorCount );
1.1.1.15 root 1052: if ( FDC_DMA.SectorCount == 0 )
1053: {
1054: //FDC_Update_STR ( 0 , FDC_STR_BIT_LOST_DATA ); /* If DMA is OFF, data are lost -> Not on the ST */
1.1.1.18! root 1055: FDC_SetDMAStatus ( true ); /* Set DMA error (bit 0) */
! 1056: return 0; /* Write a '0' byte when dma is off */
1.1.1.15 root 1057: }
1.1 root 1058:
1.1.1.18! root 1059: FDC_SetDMAStatus ( false ); /* No DMA error (bit 0) */
1.1 root 1060:
1.1.1.18! root 1061: if ( FDC_DMA.FIFO_Size > 0 ) /* FIFO is not empty yet */
! 1062: Byte = FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE - ( FDC_DMA.FIFO_Size-- ) ]; /* return byte at pos 0, 1, .., 15 */
! 1063:
! 1064: else
1.1.1.6 root 1065: {
1.1.1.18! root 1066: /* FIFO empty : transfer data from RAM to FIFO and update DMA address */
! 1067: Address = FDC_GetDMAAddress();
! 1068: memcpy ( FDC_DMA.FIFO , &STRam[ Address ] , FDC_DMA_FIFO_SIZE );/* TODO : check we read from a valid RAM location ? */
! 1069: FDC_WriteDMAAddress ( Address + FDC_DMA_FIFO_SIZE );
! 1070: FDC_DMA.FIFO_Size = FDC_DMA_FIFO_SIZE - 1; /* FIFO is now full again (minus the byte we will return below) */
! 1071:
! 1072: /* Store the last word that was just transferred by the DMA */
! 1073: FDC_DMA.ff8604_recent_val = ( FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-2 ] << 8 ) | FDC_DMA.FIFO [ FDC_DMA_FIFO_SIZE-1 ];
! 1074:
! 1075: /* Update Sector Count */
! 1076: FDC_DMA.BytesInSector -= FDC_DMA_FIFO_SIZE;
! 1077: if ( FDC_DMA.BytesInSector < 0 )
! 1078: {
! 1079: FDC_DMA.SectorCount--;
! 1080: FDC_DMA.BytesInSector = FDC_DMA_SECTOR_SIZE;
! 1081: }
! 1082:
! 1083: Byte = FDC_DMA.FIFO [ 0 ]; /* return the 1st byte we just transfered in the FIFO */
1.1.1.6 root 1084: }
1085:
1.1.1.18! root 1086: /* Store the byte that will be written to FDC's Data Register */
! 1087: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | Byte;
! 1088:
! 1089: return Byte;
1.1 root 1090: }
1091:
1.1.1.2 root 1092:
1093: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1094: /**
1.1.1.18! root 1095: * Reset the buffer used to transfer data between the FDC and the DMA
1.1.1.16 root 1096: */
1.1.1.18! root 1097: void FDC_Buffer_Reset ( void )
1.1.1.16 root 1098: {
1.1.1.18! root 1099: FDC_BUFFER.Size = 0;
! 1100: FDC_BUFFER.PosRead = 0;
1.1.1.16 root 1101: }
1102:
1103:
1104: /*-----------------------------------------------------------------------*/
1105: /**
1.1.1.18! root 1106: * Add a byte to the FDC transfer buffer, using a specific timing
1.1.1.9 root 1107: */
1.1.1.18! root 1108: void FDC_Buffer_Add_Timing ( Uint8 Byte , Uint16 Timing )
1.1 root 1109: {
1.1.1.18! root 1110: FDC_BUFFER.Data[ FDC_BUFFER.Size ].Byte = Byte;
! 1111: FDC_BUFFER.Data[ FDC_BUFFER.Size ].Timing = Timing;
! 1112: FDC_BUFFER.Size++;
1.1 root 1113: }
1114:
1.1.1.2 root 1115:
1116: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1117: /**
1.1.1.18! root 1118: * Add a byte to the FDC transfer buffer, using a default timing
1.1.1.9 root 1119: */
1.1.1.18! root 1120: void FDC_Buffer_Add ( Uint8 Byte )
1.1 root 1121: {
1.1.1.18! root 1122: FDC_Buffer_Add_Timing ( Byte , FDC_TransferByte_FdcCycles ( 1 ) );
1.1 root 1123: }
1124:
1.1.1.2 root 1125:
1.1.1.17 root 1126: /*-----------------------------------------------------------------------*/
1127: /**
1.1.1.18! root 1128: * Return the timing needed to transfer the Byte at the current position
1.1.1.17 root 1129: */
1.1.1.18! root 1130: Uint16 FDC_Buffer_Read_Timing ( void )
1.1.1.17 root 1131: {
1.1.1.18! root 1132: //fprintf ( stderr , "read timing %d %x\n" , FDC_BUFFER.PosRead , FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Timing );
! 1133: return FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Timing;
1.1.1.17 root 1134: }
1135:
1136:
1137: /*-----------------------------------------------------------------------*/
1138: /**
1.1.1.18! root 1139: * Return the Byte at the current position and increment position
1.1.1.17 root 1140: */
1.1.1.18! root 1141: Uint8 FDC_Buffer_Read_Byte ( void )
1.1.1.17 root 1142: {
1.1.1.18! root 1143: //fprintf ( stderr , "read byte %d %x\n" , FDC_BUFFER.PosRead , FDC_BUFFER.Data[ FDC_BUFFER.PosRead ].Byte );
! 1144: return FDC_BUFFER.Data[ FDC_BUFFER.PosRead++ ].Byte;
1.1.1.17 root 1145: }
1146:
1147:
1148: /*-----------------------------------------------------------------------*/
1149: /**
1.1.1.18! root 1150: * Return the Byte at a given position
1.1.1.17 root 1151: */
1.1.1.18! root 1152: Uint8 FDC_Buffer_Read_Byte_pos ( int pos )
1.1.1.17 root 1153: {
1.1.1.18! root 1154: //fprintf ( stderr , "read byte pos %d %x\n" , pos , FDC_BUFFER.Data[ pos ].Byte );
! 1155: return FDC_BUFFER.Data[ pos ].Byte;
1.1.1.17 root 1156: }
1157:
1158:
1159: /*-----------------------------------------------------------------------*/
1160: /**
1.1.1.18! root 1161: * Return the number of bytes stored in FDC_BUFFER
1.1.1.17 root 1162: */
1.1.1.18! root 1163: int FDC_Buffer_Get_Size ( void )
1.1.1.17 root 1164: {
1.1.1.18! root 1165: return FDC_BUFFER.Size;
1.1.1.17 root 1166: }
1167:
1168:
1169: /*-----------------------------------------------------------------------*/
1170: /**
1.1.1.18! root 1171: * Return the mode to handle a read/write in $ff86xx
! 1172: * Depending on the images inserted in each floppy drive and on the
! 1173: * selected drive, we must choose which fdc emulation should be used.
! 1174: * Possible emulation methods are 'internal' or 'ipf'.
! 1175: * To avoid mixing emulation methods on both drives when possible (which
! 1176: * could lead to inconstancies when precise timings are required), we also
! 1177: * use the IPF mode for an empty drive if the other drive contains an IPF
! 1178: * image.
! 1179: * If no drive is selected, we must use the previous mode (before drives were
! 1180: * unselected), not the internal one : in case some commands are sent when
! 1181: * drives are deselected and the drive was in IPF mode, we must send
! 1182: * the command to IPF to ensure no command are lost if the drive is selected again
! 1183: * (eg : D0 command in "Saint Dragon" IPF)
1.1.1.17 root 1184: */
1.1.1.18! root 1185: static int FDC_GetEmulationMode ( void )
1.1.1.17 root 1186: {
1.1.1.18! root 1187: int Mode;
1.1.1.17 root 1188:
1.1.1.18! root 1189: Mode = FDC.EmulationMode; /* Default to previous mode if no drive is selected */
1.1.1.17 root 1190:
1.1.1.18! root 1191: /* Check drive 1 first */
! 1192: if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x04 ) == 0 )
1.1.1.17 root 1193: {
1.1.1.18! root 1194: if ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_IPF )
! 1195: Mode = FDC_EMULATION_MODE_IPF;
! 1196: else if ( ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_NONE ) /* Drive 1 is empty */
! 1197: && ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_IPF ) ) /* Drive 0 is an IPF image */
! 1198: Mode = FDC_EMULATION_MODE_IPF; /* Use IPF for drive 1 too */
1.1.1.17 root 1199: else
1.1.1.18! root 1200: Mode = FDC_EMULATION_MODE_INTERNAL;
1.1.1.17 root 1201: }
1202:
1.1.1.18! root 1203: /* If both drive 0 and drive 1 are enabled, we keep only drive 0 to choose emulation's mode */
! 1204: if ( ( PSGRegisters[PSG_REG_IO_PORTA] & 0x02 ) == 0 )
1.1.1.17 root 1205: {
1.1.1.18! root 1206: if ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_IPF )
! 1207: Mode = FDC_EMULATION_MODE_IPF;
! 1208: else if ( ( EmulationDrives[ 0 ].ImageType == FLOPPY_IMAGE_TYPE_NONE ) /* Drive 0 is empty */
! 1209: && ( EmulationDrives[ 1 ].ImageType == FLOPPY_IMAGE_TYPE_IPF ) ) /* Drive 1 is an IPF image */
! 1210: Mode = FDC_EMULATION_MODE_IPF; /* Use IPF for drive 0 too */
! 1211: else
! 1212: Mode = FDC_EMULATION_MODE_INTERNAL;
1.1.1.17 root 1213: }
1214:
1.1.1.18! root 1215: FDC.EmulationMode = Mode;
! 1216: //fprintf ( stderr , "emul mode %x %d\n" , PSGRegisters[PSG_REG_IO_PORTA] & 0x06 , Mode );
! 1217: // return FDC_EMULATION_MODE_INTERNAL;
! 1218: // return FDC_EMULATION_MODE_IPF;
! 1219: return Mode;
1.1.1.17 root 1220: }
1221:
1222:
1.1.1.2 root 1223: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1224: /**
1.1.1.18! root 1225: * Update the FDC's internal variables on a regular basis.
! 1226: * To get correct accuracy, this should be called every 200-500 FDC cycles
! 1227: * So far, we only need to update the index position for the valid
! 1228: * drive/floppy ; updating every 500 cycles is enough for this case.
1.1.1.9 root 1229: */
1.1.1.18! root 1230: void FDC_UpdateAll ( void )
1.1.1.8 root 1231: {
1.1.1.18! root 1232: FDC_IndexPulse_Update ();
1.1.1.8 root 1233: }
1234:
1235:
1236: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1237: /**
1.1.1.18! root 1238: * This function is used to enable/disable a drive when
! 1239: * using the UI or command line parameters
1.1.1.9 root 1240: */
1.1.1.18! root 1241: void FDC_Drive_Set_Enable ( int Drive , bool value )
1.1 root 1242: {
1.1.1.18! root 1243: //LOG_TRACE ( TRACE_FDC , "fdc enable drive=%d %s\n" , Drive , value?"on":"off" );
! 1244: fprintf ( stderr , "fdc enable drive=%d %s\n" , Drive , value?"on":"off" );
1.1.1.17 root 1245:
1.1.1.18! root 1246: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
! 1247: FDC_DRIVES[ Drive ].Enabled = value;
1.1.1.6 root 1248:
1.1.1.18! root 1249: /* Also forward change to IPF emulation */
! 1250: IPF_Drive_Set_Enable ( Drive , value );
! 1251: }
1.1.1.6 root 1252:
1.1.1.17 root 1253:
1.1.1.18! root 1254: /*-----------------------------------------------------------------------*/
! 1255: /**
! 1256: * This function is used to choose single sided or double sided for a drive
! 1257: * when using the UI or command line parameters
! 1258: */
! 1259: void FDC_Drive_Set_NumberOfHeads ( int Drive , int NbrHeads )
! 1260: {
! 1261: //LOG_TRACE ( TRACE_FDC , "fdc set nbr heads drive=%d %d\n" , Drive , NbrHeads );
! 1262: fprintf ( stderr , "fdc set nbr heads drive=%d %d\n" , Drive , NbrHeads );
1.1.1.17 root 1263:
1.1.1.18! root 1264: if ( ( Drive >= 0 ) && ( Drive < MAX_FLOPPYDRIVES ) )
! 1265: FDC_DRIVES[ Drive ].NumberOfHeads = NbrHeads;
1.1.1.6 root 1266:
1.1.1.18! root 1267: /* Also forward change to IPF emulation */
! 1268: IPF_Drive_Set_DoubleSided ( Drive , NbrHeads==2 ? true : false );
! 1269: }
1.1.1.11 root 1270:
1.1.1.15 root 1271:
1.1.1.18! root 1272: /*-----------------------------------------------------------------------*/
! 1273: /**
! 1274: * This function is called when a floppy is inserted in a drive
! 1275: * using the UI or command line parameters
! 1276: */
! 1277: void FDC_InsertFloppy ( int Drive )
! 1278: {
! 1279: LOG_TRACE ( TRACE_FDC , "fdc insert drive=%d\n" , Drive );
1.1.1.15 root 1280:
1.1.1.18! root 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: */
! 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: */
! 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 */
1.1.1.6 root 1359: }
1.1.1.15 root 1360: }
1.1.1.6 root 1361:
1.1.1.18! root 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: */
! 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: */
! 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: */
! 1422: static int FDC_GetDensity ( int Drive )
! 1423: {
! 1424: Uint16 SectorsPerTrack;
! 1425:
! 1426: if ( EmulationDrives[ Drive ].bDiskInserted )
1.1.1.15 root 1427: {
1.1.1.18! root 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 */
1.1.1.6 root 1435: }
1.1.1.18! root 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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: */
! 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;
1.1.1.15 root 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: */
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 */
1.1.1.17 root 1838:
1.1.1.18! root 1839: FDC_Drive_Set_BusyLed ( FDC.STR );
1.1.1.15 root 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
1.1.1.18! root 1848: * - set interrupt if necessary
1.1.1.15 root 1849: * - stop motor after 2 sec
1850: */
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 */
1.1.1.9 root 1860:
1.1.1.15 root 1861: if ( DoInt )
1.1.1.18! root 1862: FDC_SetIRQ ( FDC_IRQ_SOURCE_COMPLETE );
1.1.1.15 root 1863:
1864: FDC.Command = FDCEMU_CMD_MOTOR_STOP; /* Fake command to stop the motor */
1.1.1.18! root 1865: FDC.CommandState = FDCEMU_RUN_MOTOR_STOP;
! 1866: return FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 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.
1.1.1.18! root 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,
1.1.1.15 root 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: */
1.1.1.18! root 1884: static bool FDC_VerifyTrack ( void )
1.1.1.15 root 1885: {
1886: int FrameCycles, HblCounterVideo, LineCycles;
1887:
1888: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1889:
1.1.1.18! root 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 )
1.1.1.9 root 1902: {
1.1.1.18! root 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());
1.1.1.15 root 1906:
1.1.1.18! root 1907: return false;
1.1.1.15 root 1908: }
1909:
1.1.1.18! root 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 ) ) )
1.1.1.15 root 1914: {
1.1.1.18! root 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 ,
1.1.1.15 root 1917: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1918:
1.1.1.18! root 1919: return false;
1.1.1.9 root 1920: }
1.1.1.15 root 1921:
1.1.1.18! root 1922: /* The track is the correct one */
! 1923: return true;
1.1.1.15 root 1924: }
1925:
1926:
1927: /*-----------------------------------------------------------------------*/
1928: /**
1.1.1.18! root 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)
1.1.1.15 root 1932: */
1933: static int FDC_UpdateMotorStop ( void )
1934: {
1.1.1.18! root 1935: int FdcCycles = 0;
1.1.1.15 root 1936: int FrameCycles, HblCounterVideo, LineCycles;
1937:
1.1.1.18! root 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());
1.1.1.15 root 1955:
1.1.1.18! root 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 */
1.1.1.15 root 1959:
1.1.1.18! root 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;
1.1 root 1966: }
1967:
1.1.1.2 root 1968:
1969: /*-----------------------------------------------------------------------*/
1.1.1.9 root 1970: /**
1971: * Run 'RESTORE' command
1972: */
1.1.1.15 root 1973: static int FDC_UpdateRestoreCmd ( void )
1.1 root 1974: {
1.1.1.18! root 1975: int FdcCycles = 0;
! 1976: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 1977:
1.1.1.18! root 1978: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 1979:
1.1.1.6 root 1980: /* Which command is running? */
1.1.1.15 root 1981: switch (FDC.CommandState)
1.1.1.6 root 1982: {
1983: case FDCEMU_RUN_RESTORE_SEEKTOTRACKZERO:
1.1.1.18! root 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:
1.1.1.15 root 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. */
1.1.1.18! root 2009: /* This can happen if no drive is selected or if the selected drive is disabled */
1.1.1.15 root 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 ? */
1.1.1.18! root 2017: { /* (this can happen if the drive is disabled) */
1.1.1.15 root 2018: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
2019: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
1.1.1.18! root 2020: FdcCycles = FDC_CmdCompleteCommon( true );
! 2021: break;
1.1.1.15 root 2022: }
2023:
1.1.1.18! root 2024: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled )
! 2025: || ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack != 0 ) ) /* Are we at track zero ? */
1.1.1.15 root 2026: {
2027: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset bit TR00 */
2028: FDC.TR--; /* One less attempt */
1.1.1.18! root 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 );
1.1.1.15 root 2032: }
1.1.1.18! root 2033: else /* Drive is enabled and head is at track 0 */
1.1.1.6 root 2034: {
1.1.1.15 root 2035: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set bit TR00 */
2036: FDC.TR = 0; /* Update Track Register to 0 */
1.1.1.17 root 2037: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY;
1.1.1.18! root 2038: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2039: }
2040: break;
2041: case FDCEMU_RUN_RESTORE_VERIFY:
2042: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
2043: {
1.1.1.18! root 2044: FDC.CommandState = FDCEMU_RUN_RESTORE_VERIFY_HEAD_OK;
! 2045: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
1.1.1.17 root 2046: }
2047: else
2048: {
1.1.1.15 root 2049: FDC.CommandState = FDCEMU_RUN_RESTORE_COMPLETE;
1.1.1.18! root 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;
1.1.1.6 root 2085: }
2086: break;
1.1.1.18! root 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: }
1.1.1.17 root 2101: break;
1.1.1.6 root 2102: case FDCEMU_RUN_RESTORE_COMPLETE:
1.1.1.18! root 2103: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2104: break;
2105: }
1.1.1.15 root 2106:
1.1.1.18! root 2107: return FdcCycles;
1.1 root 2108: }
2109:
1.1.1.2 root 2110:
2111: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2112: /**
2113: * Run 'SEEK' command
2114: */
1.1.1.15 root 2115: static int FDC_UpdateSeekCmd ( void )
1.1 root 2116: {
1.1.1.18! root 2117: int FdcCycles = 0;
! 2118: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2119:
1.1.1.18! root 2120: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2121:
1.1.1.6 root 2122: /* Which command is running? */
1.1.1.15 root 2123: switch (FDC.CommandState)
1.1.1.6 root 2124: {
2125: case FDCEMU_RUN_SEEK_TOTRACK:
1.1.1.18! root 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:
1.1.1.15 root 2148: if ( FDC.TR == FDC.DR ) /* Are we at the selected track ? */
2149: {
1.1.1.17 root 2150: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY;
1.1.1.18! root 2151: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 2152: }
1.1.1.6 root 2153: else
2154: {
1.1.1.15 root 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:
1.1.1.18! root 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 */
1.1.1.15 root 2165:
1.1.1.18! root 2166: /* Check / move physical head only if an enabled drive is selected */
! 2167: if ( ( FDC.DriveSelSignal >= 0 ) && ( FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
1.1.1.15 root 2168: {
1.1.1.18! root 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: }
1.1.1.15 root 2174:
1.1.1.18! root 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 */
1.1.1.15 root 2187: }
1.1.1.6 root 2188: }
1.1.1.15 root 2189:
1.1.1.6 root 2190: break;
1.1.1.17 root 2191: case FDCEMU_RUN_SEEK_VERIFY:
1.1.1.15 root 2192: if ( FDC.CR & FDC_COMMAND_BIT_VERIFY )
1.1.1.17 root 2193: {
1.1.1.18! root 2194: FDC.CommandState = FDCEMU_RUN_SEEK_VERIFY_HEAD_OK;
! 2195: FdcCycles = FDC_DelayToFdcCycles ( FDC_DELAY_US_HEAD_LOAD ); /* Head settle delay */
1.1.1.17 root 2196: }
2197: else
2198: {
2199: FDC.CommandState = FDCEMU_RUN_SEEK_COMPLETE;
1.1.1.18! root 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;
1.1.1.17 root 2235: }
2236: break;
1.1.1.18! root 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: }
1.1.1.17 root 2251: break;
2252: case FDCEMU_RUN_SEEK_COMPLETE:
1.1.1.18! root 2253: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2254: break;
2255: }
1.1.1.15 root 2256:
1.1.1.18! root 2257: return FdcCycles;
1.1 root 2258: }
2259:
1.1.1.2 root 2260:
2261: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2262: /**
2263: * Run 'STEP' command
2264: */
1.1.1.15 root 2265: static int FDC_UpdateStepCmd ( void )
1.1 root 2266: {
1.1.1.18! root 2267: int FdcCycles = 0;
! 2268: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2269:
1.1.1.18! root 2270: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2271:
1.1.1.6 root 2272: /* Which command is running? */
1.1.1.15 root 2273: switch (FDC.CommandState)
1.1.1.6 root 2274: {
2275: case FDCEMU_RUN_STEP_ONCE:
1.1.1.18! root 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:
1.1.1.15 root 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:
1.1.1.18! root 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 */
1.1.1.15 root 2310:
1.1.1.18! root 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 */
1.1.1.6 root 2313:
1.1.1.18! root 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: }
1.1.1.15 root 2329: else
2330: {
1.1.1.18! root 2331: FDC.CommandState = FDCEMU_RUN_STEP_COMPLETE;
! 2332: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2333: }
1.1.1.18! root 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());
1.1.1.15 root 2343:
1.1.1.18! root 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: }
1.1.1.15 root 2362: else
1.1.1.18! root 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: }
1.1.1.17 root 2368: break;
1.1.1.18! root 2369: case FDCEMU_RUN_STEP_VERIFY_CHECK_SECTOR_HEADER:
! 2370: /* Check if the current ID Field matches the track number */
! 2371: if ( FDC_VerifyTrack () )
1.1.1.17 root 2372: {
1.1.1.18! root 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;
1.1.1.17 root 2376: }
2377: else
2378: {
1.1.1.18! root 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;
1.1.1.17 root 2382: }
2383: break;
1.1.1.6 root 2384: case FDCEMU_RUN_STEP_COMPLETE:
1.1.1.18! root 2385: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2386: break;
2387: }
1.1.1.15 root 2388:
1.1.1.18! root 2389: return FdcCycles;
1.1 root 2390: }
2391:
1.1.1.2 root 2392:
2393: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2394: /**
1.1.1.15 root 2395: * Run 'READ SECTOR/S' command
1.1.1.9 root 2396: */
1.1.1.15 root 2397: static int FDC_UpdateReadSectorsCmd ( void )
1.1 root 2398: {
1.1.1.18! root 2399: int FdcCycles = 0;
1.1.1.15 root 2400: int SectorSize;
1.1.1.18! root 2401: int FrameCycles, HblCounterVideo, LineCycles;
! 2402: Uint8 Next_TR;
! 2403: Uint8 Next_SR;
! 2404: Uint8 Next_CRC_OK;
1.1.1.15 root 2405:
2406: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2407:
2408:
1.1.1.6 root 2409: /* Which command is running? */
1.1.1.15 root 2410: switch (FDC.CommandState)
1.1.1.6 root 2411: {
1.1.1.15 root 2412: case FDCEMU_RUN_READSECTORS_READDATA:
1.1.1.18! root 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: }
1.1.1.15 root 2423: break;
1.1.1.18! root 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:
1.1.1.17 root 2446: /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
1.1.1.18! root 2447: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
1.1.1.17 root 2448: {
2449: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
1.1.1.18! root 2450: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2451: break;
2452: }
2453:
1.1.1.18! root 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 ) )
1.1.1.17 root 2488: {
2489: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START;
1.1.1.18! root 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 );
1.1.1.17 root 2492: }
2493: else
2494: {
2495: /* This is not the ID field we're looking for ; check the next one */
1.1.1.18! root 2496: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA_NEXT_SECTOR_HEADER;
! 2497: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2498: }
2499: break;
2500: case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_START:
1.1.1.15 root 2501: /* Read a single sector into temporary buffer (512 bytes for ST/MSA) */
1.1.1.18! root 2502: FDC_Buffer_Reset();
1.1.1.17 root 2503:
1.1.1.18! root 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 */
1.1.1.15 root 2512: {
2513: FDC.CommandState = FDCEMU_RUN_READSECTORS_RNF;
1.1.1.18! root 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 */
1.1.1.15 root 2525: }
2526: break;
1.1.1.17 root 2527: case FDCEMU_RUN_READSECTORS_READDATA_TRANSFER_LOOP:
1.1.1.18! root 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 () )
1.1.1.15 root 2531: {
1.1.1.18! root 2532: FdcCycles = FDC_Buffer_Read_Timing (); /* Delay to transfer the next byte */
1.1.1.15 root 2533: }
1.1.1.17 root 2534: else /* Sector transferred, check the CRC */
1.1.1.15 root 2535: {
1.1.1.17 root 2536: FDC.CommandState = FDCEMU_RUN_READSECTORS_CRC;
1.1.1.18! root 2537: FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Read 2 bytes for CRC */
1.1.1.17 root 2538: }
2539: break;
2540: case FDCEMU_RUN_READSECTORS_CRC:
1.1.1.18! root 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 */
1.1.1.17 root 2558: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2559: {
2560: FDC.SR++; /* Try to read next sector and set RNF if not possible */
1.1.1.18! root 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());
1.1.1.17 root 2566: }
2567: else /* Multi=0, stop here with no error */
2568: {
2569: FDC.CommandState = FDCEMU_RUN_READSECTORS_COMPLETE;
1.1.1.18! root 2570: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2571: }
2572: break;
2573: case FDCEMU_RUN_READSECTORS_RNF:
1.1.1.18! root 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());
1.1.1.6 root 2576:
1.1.1.15 root 2577: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
1.1.1.18! root 2578: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2579: break;
1.1.1.15 root 2580: case FDCEMU_RUN_READSECTORS_COMPLETE:
1.1.1.18! root 2581: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2582: break;
2583: }
2584:
1.1.1.18! root 2585: return FdcCycles;
1.1.1.15 root 2586: }
2587:
2588:
2589: /*-----------------------------------------------------------------------*/
2590: /**
2591: * Run 'WRITE SECTOR/S' command
2592: */
2593: static int FDC_UpdateWriteSectorsCmd ( void )
2594: {
1.1.1.18! root 2595: int FdcCycles = 0;
1.1.1.15 root 2596: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18! root 2597: Uint8 Next_TR;
! 2598: Uint8 Next_SR;
! 2599: Uint8 Next_LEN;
! 2600: Uint8 Next_CRC_OK;
! 2601: Uint8 Byte;
! 2602: Uint8 Status;
1.1.1.15 root 2603:
2604: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2605:
1.1.1.18! root 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 ) ) )
1.1.1.15 root 2610: {
1.1.1.18! root 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());
1.1.1.15 root 2613:
2614: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
1.1.1.18! root 2615: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 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:
1.1.1.18! root 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: }
1.1.1.15 root 2635: break;
1.1.1.18! root 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:
1.1.1.17 root 2655: /* If we're looking for sector FDC.SR for more than 5 revolutions, we abort with RNF */
1.1.1.18! root 2656: if ( FDC.IndexPulse_Counter >= FDC_DELAY_IP_ADDRESS_ID )
1.1.1.17 root 2657: {
1.1.1.18! root 2658: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_RNF;
! 2659: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.17 root 2660: break;
2661: }
2662:
1.1.1.18! root 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 )
1.1.1.17 root 2672: {
1.1.1.18! root 2673: FdcCycles = FDC_DELAY_CYCLE_WAIT_NO_DRIVE_FLOPPY; /* Wait for a valid drive/floppy */
1.1.1.17 root 2674: }
2675: else
2676: {
1.1.1.18! root 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 */
1.1.1.17 root 2679: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA_CHECK_SECTOR_HEADER;
2680: }
2681: break;
1.1.1.18! root 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
1.1.1.15 root 2691: {
1.1.1.18! root 2692: Next_TR = FDC_NextSectorID_TR_ST ();
! 2693: Next_SR = FDC_NextSectorID_SR_ST ();
! 2694: Next_CRC_OK = FDC_NextSectorID_CRC_OK_ST ();
1.1.1.15 root 2695: }
1.1.1.18! root 2696: if ( ( Next_TR == FDC.TR ) && ( Next_SR == FDC.SR ) && ( Next_CRC_OK ) )
1.1.1.15 root 2697: {
1.1.1.18! root 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;
1.1.1.15 root 2707: }
2708: break;
1.1.1.18! root 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;
1.1.1.17 root 2722: case FDCEMU_RUN_WRITESECTORS_WRITEDATA_TRANSFER_LOOP:
1.1.1.18! root 2723: /* Transfer the sector 1 byte at a time using DMA */
! 2724: if ( FDC_DMA.BytesToTransfer-- > 0 )
1.1.1.15 root 2725: {
1.1.1.18! root 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 );
1.1.1.15 root 2730: }
1.1.1.18! root 2731: else /* Sector transferred, add the CRC */
1.1.1.15 root 2732: {
1.1.1.17 root 2733: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_CRC;
1.1.1.18! root 2734: FdcCycles = FDC_TransferByte_FdcCycles ( 2 ); /* Write 2 bytes for CRC */
1.1.1.17 root 2735: }
2736: break;
2737: case FDCEMU_RUN_WRITESECTORS_CRC:
1.1.1.18! root 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 */
1.1.1.17 root 2761: if ( FDC.CR & FDC_COMMAND_BIT_MULTIPLE_SECTOR )
2762: {
2763: FDC.SR++; /* Try to write next sector and set RNF if not possible */
1.1.1.18! root 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());
1.1.1.17 root 2769: }
2770: else /* Multi=0, stop here with no error */
2771: {
2772: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_COMPLETE;
1.1.1.18! root 2773: FdcCycles = FDC_DELAY_CYCLE_COMMAND_COMPLETE;
1.1.1.15 root 2774: }
2775: break;
2776: case FDCEMU_RUN_WRITESECTORS_RNF:
1.1.1.18! root 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());
1.1.1.15 root 2779:
2780: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
1.1.1.18! root 2781: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.15 root 2782: break;
2783: case FDCEMU_RUN_WRITESECTORS_COMPLETE:
1.1.1.18! root 2784: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2785: break;
2786: }
1.1.1.15 root 2787:
1.1.1.18! root 2788: return FdcCycles;
1.1 root 2789: }
2790:
1.1.1.2 root 2791:
2792: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2793: /**
1.1.1.15 root 2794: * Run 'READ ADDRESS' command
1.1.1.9 root 2795: */
1.1.1.15 root 2796: static int FDC_UpdateReadAddressCmd ( void )
1.1 root 2797: {
1.1.1.18! root 2798: int FdcCycles = 0;
1.1.1.17 root 2799: int FrameCycles, HblCounterVideo, LineCycles;
2800:
2801: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2802:
1.1.1.6 root 2803: /* Which command is running? */
1.1.1.15 root 2804: switch (FDC.CommandState)
1.1.1.6 root 2805: {
1.1.1.15 root 2806: case FDCEMU_RUN_READADDRESS:
1.1.1.18! root 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: }
1.1.1.6 root 2817: break;
1.1.1.18! root 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: }
1.1.1.15 root 2848:
1.1.1.18! root 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: }
1.1.1.15 root 2867: break;
1.1.1.18! root 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 );
1.1.1.17 root 2878:
1.1.1.18! root 2879: FDC.SR = FDC_Buffer_Read_Byte_pos ( 0 ); /* The 1st byte of the ID field is also copied into Sector Register */
1.1.1.17 root 2880:
1.1.1.18! root 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: }
1.1.1.17 root 2896: break;
1.1.1.18! root 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());
1.1.1.17 root 2900:
1.1.1.18! root 2901: FDC_Update_STR ( 0 , FDC_STR_BIT_RNF );
! 2902: FdcCycles = FDC_CmdCompleteCommon( true );
! 2903: break;
1.1.1.15 root 2904: case FDCEMU_RUN_READADDRESS_COMPLETE:
1.1.1.18! root 2905: FdcCycles = FDC_CmdCompleteCommon( true );
1.1.1.6 root 2906: break;
2907: }
1.1.1.15 root 2908:
1.1.1.18! root 2909: return FdcCycles;
1.1 root 2910: }
2911:
1.1.1.2 root 2912:
2913: /*-----------------------------------------------------------------------*/
1.1.1.9 root 2914: /**
1.1.1.15 root 2915: * Run 'READ TRACK' command
1.1.1.9 root 2916: */
1.1.1.15 root 2917: static int FDC_UpdateReadTrackCmd ( void )
1.1 root 2918: {
1.1.1.18! root 2919: int FdcCycles = 0;
1.1.1.15 root 2920: int i;
1.1.1.18! root 2921: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 2922:
1.1.1.18! root 2923: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.15 root 2924:
1.1.1.6 root 2925: /* Which command is running? */
1.1.1.15 root 2926: switch (FDC.CommandState)
1.1.1.6 root 2927: {
1.1.1.15 root 2928: case FDCEMU_RUN_READTRACK:
1.1.1.18! root 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: }
1.1.1.17 root 2967: break;
2968: case FDCEMU_RUN_READTRACK_INDEX:
1.1.1.18! root 2969: /* At this point, we have a valid drive/floppy, build the track data */
! 2970: FDC_Buffer_Reset();
1.1.1.6 root 2971:
1.1.1.18! root 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 ) ) )
1.1.1.15 root 2975: {
1.1.1.18! root 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 );
1.1.1.6 root 2986: }
1.1.1.18! root 2987: else /* Track/side available in the disk image */
1.1.1.6 root 2988: {
1.1.1.18! root 2989: FDC.Status_Temp = FDC_ReadTrack_ST ( FDC.DriveSelSignal , FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack , FDC.SideSignal );
! 2990: }
1.1.1.15 root 2991:
1.1.1.18! root 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: }
1.1.1.15 root 3012:
1.1.1.18! root 3013: return FdcCycles;
! 3014: }
1.1.1.15 root 3015:
3016:
1.1.1.18! root 3017: /*-----------------------------------------------------------------------*/
! 3018: /**
! 3019: * Run 'WRITE TRACK' command
! 3020: */
! 3021: static int FDC_UpdateWriteTrackCmd ( void )
! 3022: {
! 3023: int FdcCycles = 0;
! 3024: int FrameCycles, HblCounterVideo, LineCycles;
! 3025: Uint8 Byte;
! 3026: Uint8 Status;
1.1.1.15 root 3027:
1.1.1.18! root 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());
1.1.1.15 root 3079:
1.1.1.18! root 3080: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit */
! 3081: FdcCycles = FDC_CmdCompleteCommon( true );
! 3082: break;
1.1.1.6 root 3083: }
1.1 root 3084:
1.1.1.18! root 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 );
1.1.1.2 root 3089:
1.1.1.18! root 3090: FDC.CommandState = FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP;
! 3091: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.15 root 3092: break;
1.1.1.18! root 3093: case FDCEMU_RUN_WRITETRACK_TRANSFER_LOOP:
! 3094: /* Transfer the track 1 byte at a time using DMA */
! 3095: if ( FDC_DMA.BytesToTransfer-- > 0 )
1.1.1.6 root 3096: {
1.1.1.18! root 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 );
1.1.1.6 root 3101: }
1.1.1.18! root 3102: else /* Track written */
1.1.1.6 root 3103: {
1.1.1.18! root 3104: FDC.CommandState = FDCEMU_RUN_WRITETRACK_COMPLETE;
! 3105: FdcCycles = FDC_DELAY_CYCLE_COMMAND_IMMEDIATE;
1.1.1.6 root 3106: }
3107: break;
1.1.1.18! root 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 );
1.1.1.6 root 3122: break;
3123: }
1.1.1.15 root 3124:
1.1.1.18! root 3125: return FdcCycles;
1.1 root 3126: }
3127:
1.1.1.2 root 3128:
3129: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3130: /**
1.1.1.15 root 3131: * Common to types I, II and III
3132: *
3133: * Start motor / spin up sequence if needed
1.1.1.18! root 3134: * Return true if spin up sequence is needed, else false
1.1.1.11 root 3135: */
1.1.1.15 root 3136:
1.1.1.18! root 3137: static bool FDC_Set_MotorON ( Uint8 FDC_CR )
1.1.1.11 root 3138: {
1.1.1.15 root 3139: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18! root 3140: bool SpinUp;
1.1.1.15 root 3141:
3142: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3143:
1.1.1.18! root 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 */
1.1.1.11 root 3155: {
1.1.1.18! root 3156: LOG_TRACE(TRACE_FDC, "fdc start motor without spinup VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.15 root 3157: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.18! root 3158:
! 3159: SpinUp = false;
1.1.1.11 root 3160: }
1.1.1.15 root 3161:
1.1.1.18! root 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;
1.1.1.11 root 3174: }
3175:
3176:
3177: /*-----------------------------------------------------------------------*/
3178: /**
1.1.1.9 root 3179: * Type I Commands
3180: *
3181: * Restore, Seek, Step, Step-In and Step-Out
3182: */
1.1 root 3183:
1.1.1.2 root 3184:
3185: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3186: static int FDC_TypeI_Restore(void)
1.1 root 3187: {
1.1.1.15 root 3188: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3189:
3190: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3191:
1.1.1.17 root 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",
1.1.1.18! root 3193: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3194: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3195: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18! root 3196: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal].HeadTrack : -1 ,
! 3197: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3198:
1.1.1.6 root 3199: /* Set emulation to seek to track zero */
1.1.1.15 root 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 );
1.1 root 3204:
1.1.1.18! root 3205: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3206: }
3207:
1.1.1.2 root 3208:
3209: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3210: static int FDC_TypeI_Seek ( void )
1.1 root 3211: {
1.1.1.15 root 3212: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3213:
3214: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3215:
1.1.1.17 root 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,
1.1.1.18! root 3218: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3219: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3220: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18! root 3221: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3222: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3223:
1.1.1.6 root 3224: /* Set emulation to seek to chosen track */
1.1.1.15 root 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 );
1.1 root 3229:
1.1.1.18! root 3230: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3231: }
3232:
1.1.1.2 root 3233:
3234: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3235: static int FDC_TypeI_Step ( void )
1.1 root 3236: {
1.1.1.15 root 3237: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3238:
3239: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3240:
1.1.1.17 root 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,
1.1.1.18! root 3243: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3244: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3245: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18! root 3246: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3247: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 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;
1.1.1.10 root 3252:
1.1.1.15 root 3253: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3254:
1.1.1.18! root 3255: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3256: }
3257:
1.1.1.2 root 3258:
3259: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3260: static int FDC_TypeI_StepIn(void)
1.1 root 3261: {
1.1.1.15 root 3262: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3263:
3264: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3265:
1.1.1.17 root 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",
1.1.1.18! root 3267: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3268: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3269: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18! root 3270: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3271: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3272:
1.1.1.15 root 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*/
1.1 root 3277:
1.1.1.15 root 3278: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
3279:
1.1.1.18! root 3280: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3281: }
3282:
1.1.1.2 root 3283:
3284: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3285: static int FDC_TypeI_StepOut ( void )
1.1 root 3286: {
1.1.1.15 root 3287: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3288:
3289: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3290:
1.1.1.17 root 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",
1.1.1.18! root 3292: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3293: ( FDC.CR & FDC_COMMAND_BIT_VERIFY ) ? "on" : "off" ,
3294: FDC_StepRate_ms[ FDC_STEP_RATE ] ,
1.1.1.18! root 3295: FDC.DriveSelSignal , FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3296: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.15 root 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 */
1.1.1.10 root 3302:
1.1.1.15 root 3303: FDC_Update_STR ( FDC_STR_BIT_INDEX | FDC_STR_BIT_CRC_ERROR | FDC_STR_BIT_RNF , FDC_STR_BIT_BUSY );
1.1 root 3304:
1.1.1.18! root 3305: return FDC_DELAY_CYCLE_TYPE_I_PREPARE;
1.1 root 3306: }
3307:
1.1.1.2 root 3308:
3309: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3310: /**
3311: * Type II Commands
3312: *
1.1.1.15 root 3313: * Read Sector, Write Sector
1.1.1.9 root 3314: */
1.1 root 3315:
1.1.1.2 root 3316:
3317: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3318: static int FDC_TypeII_ReadSector ( void )
1.1 root 3319: {
1.1.1.15 root 3320: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3321:
3322: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3323:
1.1.1.17 root 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" ,
1.1.1.18! root 3326: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3327: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18! root 3328: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3329: FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount ,
1.1.1.15 root 3330: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.12 root 3331:
1.1.1.15 root 3332: /* Set emulation to read sector(s) */
3333: FDC.Command = FDCEMU_CMD_READSECTORS;
3334: FDC.CommandState = FDCEMU_RUN_READSECTORS_READDATA;
1.1.1.10 root 3335:
1.1.1.15 root 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 );
1.1 root 3338:
1.1.1.18! root 3339: return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
1.1 root 3340: }
3341:
1.1.1.2 root 3342:
3343: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3344: static int FDC_TypeII_WriteSector ( void )
1.1 root 3345: {
1.1.1.15 root 3346: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3347:
3348: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3349:
1.1.1.17 root 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" ,
1.1.1.18! root 3352: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3353: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18! root 3354: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3355: FDC.SideSignal , FDC.DriveSelSignal , FDC_DMA.SectorCount,
1.1.1.15 root 3356: FDC_GetDMAAddress(), nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.2 root 3357:
1.1.1.15 root 3358: /* Set emulation to write a sector(s) */
3359: FDC.Command = FDCEMU_CMD_WRITESECTORS;
3360: FDC.CommandState = FDCEMU_RUN_WRITESECTORS_WRITEDATA;
1.1.1.10 root 3361:
1.1.1.15 root 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 );
1.1 root 3364:
1.1.1.18! root 3365: return FDC_DELAY_CYCLE_TYPE_II_PREPARE;
1.1 root 3366: }
3367:
1.1.1.2 root 3368:
3369: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3370: /**
3371: * Type III Commands
3372: *
3373: * Read Address, Read Track, Write Track
3374: */
1.1 root 3375:
1.1.1.2 root 3376:
3377: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3378: static int FDC_TypeIII_ReadAddress ( void )
1.1 root 3379: {
1.1.1.15 root 3380: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3381:
3382: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3383:
1.1.1.17 root 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",
1.1.1.18! root 3385: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3386: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18! root 3387: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3388: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.12 root 3389: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3390:
1.1.1.11 root 3391: /* Set emulation to seek to track zero */
1.1.1.15 root 3392: FDC.Command = FDCEMU_CMD_READADDRESS;
3393: FDC.CommandState = FDCEMU_RUN_READADDRESS;
1.1.1.11 root 3394:
1.1.1.15 root 3395: FDC_Update_STR ( FDC_STR_BIT_DRQ | FDC_STR_BIT_LOST_DATA | FDC_STR_BIT_CRC_ERROR
3396: | FDC_STR_BIT_RNF | FDC_STR_BIT_RECORD_TYPE | FDC_STR_BIT_WPRT , FDC_STR_BIT_BUSY );
3397:
1.1.1.18! root 3398: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3399: }
3400:
1.1.1.2 root 3401:
3402: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3403: static int FDC_TypeIII_ReadTrack ( void )
1.1 root 3404: {
1.1.1.15 root 3405: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3406:
3407: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3408:
1.1.1.17 root 3409: LOG_TRACE(TRACE_FDC, "fdc type III read track spinup=%s settle=%s tr=0x%x head_track=0x%x side=%d drive=%d addr=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n",
1.1.1.18! root 3410: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3411: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18! root 3412: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3413: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.15 root 3414: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3415:
1.1.1.15 root 3416: /* Set emulation to read a single track */
3417: FDC.Command = FDCEMU_CMD_READTRACK;
3418: FDC.CommandState = FDCEMU_RUN_READTRACK;
1.1.1.5 root 3419:
1.1.1.15 root 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 );
1.1.1.5 root 3422:
1.1.1.18! root 3423: return FDC_DELAY_CYCLE_TYPE_III_PREPARE;
1.1 root 3424: }
3425:
1.1.1.2 root 3426:
3427: /*-----------------------------------------------------------------------*/
1.1.1.15 root 3428: static int FDC_TypeIII_WriteTrack ( void )
1.1 root 3429: {
1.1.1.15 root 3430: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3431:
3432: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3433:
1.1.1.17 root 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",
1.1.1.18! root 3435: ( FDC.CR & FDC_COMMAND_BIT_SPIN_UP ) ? "off" : "on" ,
1.1.1.17 root 3436: ( FDC.CR & FDC_COMMAND_BIT_HEAD_LOAD ) ? "on" : "off" ,
1.1.1.18! root 3437: FDC.TR , FDC.DriveSelSignal >= 0 ? FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack : -1 ,
! 3438: FDC.SideSignal , FDC.DriveSelSignal , FDC_GetDMAAddress(),
1.1.1.15 root 3439: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3440:
1.1.1.6 root 3441: /* Set emulation to write a single track */
1.1.1.18! root 3442: FDC.Command = FDCEMU_CMD_WRITETRACK;
! 3443: FDC.CommandState = FDCEMU_RUN_WRITETRACK;
1.1 root 3444:
1.1.1.18! root 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;
1.1 root 3449: }
3450:
1.1.1.2 root 3451:
3452: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3453: /**
3454: * Type IV Commands
3455: *
3456: * Force Interrupt
3457: */
1.1 root 3458:
1.1.1.2 root 3459:
3460: /*-----------------------------------------------------------------------*/
1.1.1.18! root 3461: static int FDC_TypeIV_ForceInterrupt ( void )
1.1 root 3462: {
1.1.1.18! root 3463: int FdcCycles;
1.1.1.15 root 3464: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.12 root 3465:
3466: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3467:
1.1.1.18! root 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",
1.1.1.15 root 3469: FDC.CR , ( FDC.CR & 0x8 ) >> 3 , ( FDC.CR & 0x4 ) >> 2 ,
1.1.1.12 root 3470: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
1.1.1.10 root 3471:
1.1.1.18! root 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 )
1.1.1.15 root 3475: {
1.1.1.18! root 3476: FDC.StatusTypeI = true;
1.1.1.15 root 3477:
1.1.1.18! root 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 */
1.1.1.15 root 3480: }
3481:
1.1.1.18! root 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 );
1.1.1.6 root 3493:
1.1.1.18! root 3494: return FDC_DELAY_CYCLE_TYPE_IV_PREPARE + FdcCycles;
1.1 root 3495: }
3496:
1.1.1.2 root 3497:
3498: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3499: /**
3500: * Execute Type I commands
3501: */
1.1.1.15 root 3502: static int FDC_ExecuteTypeICommands ( void )
1.1 root 3503: {
1.1.1.18! root 3504: int FdcCycles = 0;
1.1.1.15 root 3505:
3506: FDC.CommandType = 1;
1.1.1.18! root 3507: FDC.StatusTypeI = true;
1.1 root 3508:
1.1.1.6 root 3509: /* Check Type I Command */
1.1.1.15 root 3510: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3511: {
3512: case 0x00: /* Restore */
1.1.1.18! root 3513: FdcCycles = FDC_TypeI_Restore();
1.1.1.6 root 3514: break;
3515: case 0x10: /* Seek */
1.1.1.18! root 3516: FdcCycles = FDC_TypeI_Seek();
1.1.1.6 root 3517: break;
3518: case 0x20: /* Step */
3519: case 0x30:
1.1.1.18! root 3520: FdcCycles = FDC_TypeI_Step();
1.1.1.6 root 3521: break;
3522: case 0x40: /* Step-In */
3523: case 0x50:
1.1.1.18! root 3524: FdcCycles = FDC_TypeI_StepIn();
1.1.1.6 root 3525: break;
3526: case 0x60: /* Step-Out */
3527: case 0x70:
1.1.1.18! root 3528: FdcCycles = FDC_TypeI_StepOut();
1.1.1.6 root 3529: break;
3530: }
1.1 root 3531:
1.1.1.18! root 3532: return FdcCycles;
1.1 root 3533: }
3534:
1.1.1.2 root 3535:
3536: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3537: /**
3538: * Execute Type II commands
3539: */
1.1.1.15 root 3540: static int FDC_ExecuteTypeIICommands ( void )
1.1 root 3541: {
1.1.1.18! root 3542: int FdcCycles = 0;
1.1.1.15 root 3543:
3544: FDC.CommandType = 2;
1.1.1.18! root 3545: FDC.StatusTypeI = false;
1.1 root 3546:
1.1.1.6 root 3547: /* Check Type II Command */
1.1.1.15 root 3548: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3549: {
1.1.1.15 root 3550: case 0x80: /* Read Sector multi=0*/
3551: case 0x90: /* Read Sectors multi=1 */
1.1.1.18! root 3552: FdcCycles = FDC_TypeII_ReadSector();
1.1.1.6 root 3553: break;
1.1.1.15 root 3554: case 0xa0: /* Write Sector multi=0 */
3555: case 0xb0: /* Write Sectors multi=1 */
1.1.1.18! root 3556: FdcCycles = FDC_TypeII_WriteSector();
1.1.1.6 root 3557: break;
3558: }
1.1 root 3559:
1.1.1.18! root 3560: return FdcCycles;
1.1 root 3561: }
3562:
1.1.1.2 root 3563:
3564: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3565: /**
3566: * Execute Type III commands
3567: */
1.1.1.15 root 3568: static int FDC_ExecuteTypeIIICommands ( void )
1.1 root 3569: {
1.1.1.18! root 3570: int FdcCycles = 0;
1.1.1.15 root 3571:
3572: FDC.CommandType = 3;
1.1.1.18! root 3573: FDC.StatusTypeI = false;
1.1 root 3574:
1.1.1.6 root 3575: /* Check Type III Command */
1.1.1.15 root 3576: switch ( FDC.CR & 0xf0 )
1.1.1.6 root 3577: {
3578: case 0xc0: /* Read Address */
1.1.1.18! root 3579: FdcCycles = FDC_TypeIII_ReadAddress();
1.1.1.6 root 3580: break;
3581: case 0xe0: /* Read Track */
1.1.1.18! root 3582: FdcCycles = FDC_TypeIII_ReadTrack();
1.1.1.6 root 3583: break;
3584: case 0xf0: /* Write Track */
1.1.1.18! root 3585: FdcCycles = FDC_TypeIII_WriteTrack();
1.1.1.6 root 3586: break;
3587: }
1.1 root 3588:
1.1.1.18! root 3589: return FdcCycles;
1.1 root 3590: }
3591:
1.1.1.2 root 3592:
3593: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3594: /**
3595: * Execute Type IV commands
3596: */
1.1.1.15 root 3597: static int FDC_ExecuteTypeIVCommands ( void )
1.1 root 3598: {
1.1.1.18! root 3599: int FdcCycles;
1.1 root 3600:
1.1.1.18! root 3601: FDC.CommandType = 4;
1.1.1.15 root 3602:
1.1.1.18! root 3603: FdcCycles = FDC_TypeIV_ForceInterrupt();
! 3604:
! 3605: return FdcCycles;
1.1 root 3606: }
3607:
1.1.1.2 root 3608:
3609: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3610: /**
3611: * Find FDC command type and execute
1.1.1.18! root 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
1.1.1.9 root 3620: */
1.1.1.15 root 3621: static void FDC_ExecuteCommand ( void )
1.1 root 3622: {
1.1.1.18! root 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;
1.1.1.15 root 3646:
1.1.1.6 root 3647: /* Check type of command and execute */
1.1.1.18! root 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();
1.1.1.15 root 3654: else /* Type IV - Force Interrupt */
1.1.1.18! root 3655: FdcCycles = FDC_ExecuteTypeIVCommands();
1.1.1.9 root 3656:
1.1.1.18! root 3657: FDC.ReplaceCommandPossible = true; /* This new command can be replaced during the prepare+spinup phase */
! 3658: FDC_StartTimer_FdcCycles ( FdcCycles , 0 );
1.1 root 3659: }
3660:
1.1.1.2 root 3661:
3662: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3663: /**
1.1.1.15 root 3664: * Write to SectorCount register $ff8604
1.1.1.9 root 3665: */
1.1.1.15 root 3666: static void FDC_WriteSectorCountRegister ( void )
1.1 root 3667: {
1.1.1.12 root 3668: int FrameCycles, HblCounterVideo, LineCycles;
3669:
3670: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
3671:
1.1.1.15 root 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());
1.1.1.9 root 3674:
1.1.1.15 root 3675: FDC_DMA.SectorCount = IoMem_ReadByte(0xff8605);
1.1 root 3676: }
3677:
1.1.1.2 root 3678:
3679: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3680: /**
1.1.1.15 root 3681: * Write to Command register $ff8604
1.1.1.9 root 3682: */
1.1.1.15 root 3683: static void FDC_WriteCommandRegister ( void )
1.1 root 3684: {
1.1.1.12 root 3685: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18! root 3686: Uint8 Type_new;
1.1.1.12 root 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",
1.1.1.15 root 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 */
1.1.1.18! root 3695: /* was started and spinup phase was not completed yet (eg Overdrive Demos by Phalanx) (see notes at the top of the file)*/
1.1.1.15 root 3696: if ( FDC.STR & FDC_STR_BIT_BUSY )
3697: {
1.1.1.18! root 3698: Type_new = FDC_GetCmdType ( IoMem_ReadByte(0xff8605) );
! 3699: if ( Type_new == 4 ) /* 'Force Interrupt' command */
1.1.1.15 root 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:
1.1.1.18! root 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 */
1.1.1.15 root 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: }
1.1.1.9 root 3720:
1.1.1.15 root 3721: FDC.CR = IoMem_ReadByte(0xff8605);
1.1.1.6 root 3722: FDC_ExecuteCommand();
1.1 root 3723: }
3724:
1.1.1.2 root 3725:
3726: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3727: /**
1.1.1.15 root 3728: * Write to Track register $ff8604
1.1.1.9 root 3729: */
1.1.1.15 root 3730: static void FDC_WriteTrackRegister ( void )
1.1 root 3731: {
1.1.1.12 root 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" ,
1.1.1.15 root 3737: IoMem_ReadByte(0xff8605) , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 3738:
1.1.1.15 root 3739: /* [NP] Contrary to what is written in the WD1772 doc, Track Register can be changed */
1.1.1.18! root 3740: /* while the fdc is busy (the change will be ignored or not, depending on the current sub-state */
! 3741: /* in the state machine) */
1.1.1.15 root 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());
1.1.1.18! root 3746: //return;
1.1.1.15 root 3747: }
3748:
3749: FDC.TR = IoMem_ReadByte(0xff8605);
1.1 root 3750: }
3751:
1.1.1.2 root 3752:
3753: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3754: /**
1.1.1.15 root 3755: * Write to Sector register $ff8604
1.1.1.9 root 3756: */
1.1.1.15 root 3757: static void FDC_WriteSectorRegister ( void )
1.1 root 3758: {
1.1.1.12 root 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" ,
1.1.1.15 root 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: }
1.1.1.9 root 3774:
1.1.1.15 root 3775: FDC.SR = IoMem_ReadByte(0xff8605);
1.1 root 3776: }
3777:
1.1.1.2 root 3778:
3779: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3780: /**
1.1.1.15 root 3781: * Write to Data register $ff8604
1.1.1.9 root 3782: */
1.1.1.15 root 3783: static void FDC_WriteDataRegister ( void )
1.1 root 3784: {
1.1.1.12 root 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" ,
1.1.1.15 root 3790: IoMem_ReadByte(0xff8605), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 3791:
1.1.1.15 root 3792: FDC.DR = IoMem_ReadByte(0xff8605);
1.1 root 3793: }
3794:
1.1.1.2 root 3795:
3796: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3797: /**
1.1.1.18! root 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)
1.1.1.9 root 3803: */
1.1.1.15 root 3804: void FDC_DiskController_WriteWord ( void )
1.1 root 3805: {
1.1.1.12 root 3806: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.18! root 3807: int EmulationMode;
! 3808: int FDC_reg;
1.1.1.12 root 3809:
1.1.1.15 root 3810: if ( nIoMemAccessSize == SIZE_BYTE )
1.1.1.6 root 3811: {
3812: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.8 root 3813: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_WRITE);
1.1.1.6 root 3814: return;
3815: }
3816:
1.1.1.8 root 3817: M68000_WaitState(4);
3818:
1.1.1.12 root 3819: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
1.1.1.9 root 3820:
1.1.1.12 root 3821: LOG_TRACE(TRACE_FDC, "fdc write 8604 data=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
1.1.1.15 root 3822: IoMem_ReadWord(0xff8604), nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.6 root 3823:
1.1.1.18! root 3824:
! 3825: /* Are we trying to set the DMA SectorCount ? */
! 3826: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.12 root 3827: {
1.1.1.18! root 3828: FDC_WriteSectorCountRegister();
1.1.1.10 root 3829: return;
1.1.1.12 root 3830: }
1.1.1.6 root 3831:
1.1.1.18! root 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 ? */
1.1.1.8 root 3836: {
1.1.1.18! root 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) );
1.1.1.6 root 3875: }
3876: }
1.1 root 3877: }
3878:
1.1.1.2 root 3879:
3880: /*-----------------------------------------------------------------------*/
1.1.1.9 root 3881: /**
1.1.1.18! root 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
1.1.1.9 root 3888: */
1.1.1.15 root 3889: void FDC_DiskControllerStatus_ReadWord ( void )
1.1 root 3890: {
1.1.1.15 root 3891: Uint16 DiskControllerByte = 0; /* Used to pass back the parameter */
1.1.1.12 root 3892: int FrameCycles, HblCounterVideo, LineCycles;
1.1.1.15 root 3893: int ForceWPRT;
1.1.1.18! root 3894: int EmulationMode;
! 3895: int FDC_reg;
1.1.1.6 root 3896:
3897: if (nIoMemAccessSize == SIZE_BYTE)
3898: {
3899: /* This register does not like to be accessed in byte mode on a normal ST */
1.1.1.8 root 3900: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_READ);
1.1.1.6 root 3901: return;
3902: }
3903:
1.1.1.8 root 3904:
1.1.1.18! root 3905: /* Are we trying to read the DMA SectorCount ? */
! 3906: if ( FDC_DMA.Mode & 0x10 ) /* Bit 4 */
1.1.1.6 root 3907: {
1.1.1.18! root 3908: DiskControllerByte = FDC_DMA.ff8604_recent_val; /* As verified on real STF, DMA sector count can't be read back */
1.1.1.6 root 3909: }
1.1.1.18! root 3910:
! 3911: else if ( ( FDC_DMA.Mode & 0x0008) == 0x0008) /* HDC status reg selected ? */
1.1.1.8 root 3912: {
1.1.1.18! root 3913: M68000_WaitState(4); /* [NP] : possible, but not tested on real HW */
! 3914:
! 3915: /* Return the HDC status byte */
! 3916: DiskControllerByte = HDC_ReadCommandByte(FDC_DMA.Mode & 0x7);
1.1.1.8 root 3917: }
1.1.1.18! root 3918:
! 3919: else /* It's a FDC register access */
1.1.1.6 root 3920: {
1.1.1.18! root 3921: M68000_WaitState(4);
! 3922:
! 3923: FDC_reg = ( FDC_DMA.Mode & 0x6 ) >> 1; /* Bits 1,2 (A0,A1) */
! 3924:
! 3925: EmulationMode = FDC_GetEmulationMode();
! 3926: if ( EmulationMode == FDC_EMULATION_MODE_INTERNAL )
1.1.1.6 root 3927: {
1.1.1.18! root 3928: /* Update FDC's internal variables */
! 3929: FDC_UpdateAll ();
! 3930:
! 3931: /* Read FDC registers */
! 3932: switch ( FDC_reg )
1.1.1.6 root 3933: {
1.1.1.18! root 3934: case 0x0: /* 0 0 - Status register */
! 3935: /* If we report a type I status, some bits are updated in real time */
! 3936: /* depending on the corresponding signals. If this is not a type I, we return STR unmodified */
! 3937: /* [NP] Contrary to what is written in the WD1772 doc, the WPRT bit */
! 3938: /* is updated after a Type I command */
! 3939: /* (eg : Procopy or Terminators Copy 1.68 do a Restore/Seek to test WPRT) */
! 3940: if ( FDC.StatusTypeI )
! 3941: {
! 3942: /* If no drive available, FDC's input signals TR00, INDEX and WPRT are off */
! 3943: if ( ( FDC.DriveSelSignal < 0 ) || ( !FDC_DRIVES[ FDC.DriveSelSignal ].Enabled ) )
! 3944: FDC_Update_STR ( FDC_STR_BIT_TR00 | FDC_STR_BIT_INDEX | FDC_STR_BIT_WPRT , 0 );
! 3945:
! 3946: else
! 3947: {
! 3948: if ( FDC_DRIVES[ FDC.DriveSelSignal ].HeadTrack == 0 )
! 3949: FDC_Update_STR ( 0 , FDC_STR_BIT_TR00 ); /* Set TR00 bit2 */
! 3950: else
! 3951: FDC_Update_STR ( FDC_STR_BIT_TR00 , 0 ); /* Unset TR00 bit2 */
! 3952:
! 3953: if ( FDC_IndexPulse_GetState () )
! 3954: FDC_Update_STR ( 0 , FDC_STR_BIT_INDEX ); /* Set INDEX bit1 */
! 3955: else
! 3956: FDC_Update_STR ( FDC_STR_BIT_INDEX , 0 ); /* Unset INDEX bit1 */
! 3957:
! 3958: /* For Type I, always unset CRC ERROR bit3 */
! 3959: FDC_Update_STR ( FDC_STR_BIT_CRC_ERROR , 0 );
! 3960:
! 3961: /* When there's no disk in drive, the floppy drive hardware can't see */
! 3962: /* the difference with an inserted disk that would be write protected */
! 3963: if ( ! FDC_DRIVES[ FDC.DriveSelSignal ].DiskInserted )
! 3964: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit6 */
! 3965: else if ( Floppy_IsWriteProtected ( FDC.DriveSelSignal ) )
! 3966: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Set WPRT bit6 */
! 3967: else
! 3968: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Unset WPRT bit6 */
! 3969:
! 3970: /* Temporarily change the WPRT bit if we're in a transition phase */
! 3971: /* regarding the disk in the drive (inserting or ejecting) */
! 3972: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC.DriveSelSignal );
! 3973: if ( ForceWPRT == 1 )
! 3974: FDC_Update_STR ( 0 , FDC_STR_BIT_WPRT ); /* Force setting WPRT */
! 3975: else if ( ForceWPRT == -1 )
! 3976: FDC_Update_STR ( FDC_STR_BIT_WPRT , 0 ); /* Force clearing WPRT */
! 3977:
! 3978: if ( ForceWPRT != 0 )
! 3979: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n",
! 3980: ForceWPRT==1?1:0, nVBLs, FDC.DriveSelSignal, FDC.STR );
! 3981: }
! 3982: }
1.1.1.17 root 3983:
1.1.1.18! root 3984: DiskControllerByte = FDC.STR;
! 3985:
! 3986: /* When Status Register is read, FDC's IRQ is reset (except if "force interrupt immediate" is set) */
! 3987:
! 3988: /* If IRQ is forced but FDC_INTERRUPT_COND_IMMEDIATE is not set anymore, this means */
! 3989: /* the D8 command was stopped and we can clear the forced IRQ while reading status register */
! 3990: if ( ( FDC.IRQ_Signal & FDC_IRQ_SOURCE_FORCED )
! 3991: && ( ( FDC.InterruptCond & FDC_INTERRUPT_COND_IMMEDIATE ) == 0 ) )
! 3992: FDC.IRQ_Signal &= ~FDC_IRQ_SOURCE_FORCED; /* Really stop the forced IRQ */
! 3993:
! 3994: FDC_ClearIRQ ();
! 3995: break;
! 3996: case 0x1: /* 0 1 - Track register */
! 3997: DiskControllerByte = FDC.TR;
! 3998: break;
! 3999: case 0x2: /* 1 0 - Sector register */
! 4000: DiskControllerByte = FDC.SR;
! 4001: break;
! 4002: case 0x3: /* 1 1 - Data register */
! 4003: DiskControllerByte = FDC.DR;
! 4004: break;
1.1.1.6 root 4005: }
1.1.1.18! root 4006: }
! 4007: else if ( EmulationMode == FDC_EMULATION_MODE_IPF )
! 4008: {
! 4009: DiskControllerByte = IPF_FDC_ReadReg ( FDC_reg );
! 4010: if ( ( FDC_reg == 0x0 ) && ( FDC.DriveSelSignal >= 0 ) ) /* 0 0 - Status register */
! 4011: {
! 4012: /* Temporarily change the WPRT bit if we're in a transition phase */
! 4013: /* regarding the disk in the drive (inserting or ejecting) */
! 4014: ForceWPRT = Floppy_DriveTransitionUpdateState ( FDC.DriveSelSignal );
! 4015: if ( ForceWPRT == 1 )
! 4016: DiskControllerByte |= FDC_STR_BIT_WPRT; /* Force setting WPRT */
! 4017: if ( ForceWPRT == -1 )
! 4018: DiskControllerByte &= ~FDC_STR_BIT_WPRT; /* Force clearing WPRT */
1.1.1.6 root 4019:
1.1.1.18! root 4020: if ( ForceWPRT != 0 )
! 4021: LOG_TRACE(TRACE_FDC, "force wprt=%d VBL=%d drive=%d str=%x\n", ForceWPRT==1?1:0, nVBLs, FDC.DriveSelSignal, DiskControllerByte );
! 4022: }
1.1.1.6 root 4023: }
4024: }
4025:
1.1.1.18! root 4026:
! 4027: /* Store the byte that was just returned by this read if we accessed fdc/hdc regs */
! 4028: if ( ( FDC_DMA.Mode & 0x10 ) == 0 ) /* Bit 4 */
! 4029: FDC_DMA.ff8604_recent_val = ( FDC_DMA.ff8604_recent_val & 0xff00 ) | ( DiskControllerByte & 0xff );
! 4030:
1.1.1.7 root 4031: IoMem_WriteWord(0xff8604, DiskControllerByte);
1.1.1.9 root 4032:
1.1.1.12 root 4033: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4034:
4035: LOG_TRACE(TRACE_FDC, "fdc read 8604 ctrl status=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4036: DiskControllerByte , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1 root 4037: }
4038:
1.1.1.2 root 4039:
4040: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4041: /**
1.1.1.15 root 4042: * Write word to $ff8606 (DMA Mode Control)
4043: *
4044: * Eg.
4045: * $80 - Selects command/status register
4046: * $82 - Selects track register
4047: * $84 - Selects sector register
1.1.1.17 root 4048: * $86 - Selects data register
1.1.1.15 root 4049: * NOTE - OR above values with $100 is transfer from memory to floppy
4050: * Also if bit 4 is set, write to DMA sector count register
1.1.1.18! root 4051: *
! 4052: * NOTE [NP] : add 4 cycles wait state in that case
1.1.1.15 root 4053: */
4054: void FDC_DmaModeControl_WriteWord ( void )
4055: {
4056: Uint16 Mode_prev; /* Store previous write to 0xff8606 for 'toggle' checks */
4057: int FrameCycles, HblCounterVideo, LineCycles;
4058:
4059:
4060: if (nIoMemAccessSize == SIZE_BYTE)
4061: {
4062: /* This register does not like to be accessed in byte mode on a normal ST */
4063: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_WRITE);
4064: return;
4065: }
4066:
1.1.1.18! root 4067: M68000_WaitState(4);
! 4068:
1.1.1.15 root 4069: Mode_prev = FDC_DMA.Mode; /* Store previous to check for _read/_write toggle (DMA reset) */
4070: FDC_DMA.Mode = IoMem_ReadWord(0xff8606); /* Store to DMA Mode control */
4071:
4072: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4073:
4074: LOG_TRACE(TRACE_FDC, "fdc write 8606 ctrl=0x%x VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4075: FDC_DMA.Mode , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4076:
4077: /* When write to 0xff8606, check bit '8' toggle. This causes DMA status reset */
4078: if ((Mode_prev ^ FDC_DMA.Mode) & 0x0100)
4079: FDC_ResetDMA();
4080: }
4081:
4082:
4083: /*-----------------------------------------------------------------------*/
4084: /**
4085: * Read DMA Status at $ff8606
4086: *
1.1.1.18! root 4087: * Only bits 0-2 are used :
! 4088: * Bit 0 - Error Status (0=Error)
! 4089: * Bit 1 - Sector Count Zero Status (0=Sector Count Zero)
! 4090: * Bit 2 - Data Request signal from the FDC
! 4091: *
! 4092: * NOTE [NP] : as verified on STF, bit 0 will be cleared (=error) if DMA sector count is 0
! 4093: * when we get some DRQ to process.
! 4094: *
! 4095: * NOTE [NP] : on the ST, the Data Register will always be read by the DMA when the FDC's DRQ
! 4096: * signal is set. This means bit 2 of DMA status will be '0' nearly all the time
! 4097: * (as verified on STF by constantly reading DMA Status, bit 2 can be '1' during
! 4098: * a few cycles, before the DMA read the Data Register, but for the emulation we
! 4099: * consider it's always '0')
! 4100: *
! 4101: * NOTE [NP] : unused bits 3-15 are the ones from the latest $ff8604 access (verified on real STF)
! 4102: *
! 4103: * NOTE [NP] : no 4 cycles wait state in that case
1.1.1.15 root 4104: */
4105: void FDC_DmaStatus_ReadWord ( void )
4106: {
4107: if (nIoMemAccessSize == SIZE_BYTE)
4108: {
4109: /* This register does not like to be accessed in byte mode on a normal ST */
4110: M68000_BusError(IoAccessBaseAddress, BUS_ERROR_READ);
4111: return;
4112: }
4113:
1.1.1.18! root 4114: /* Update Bit1 for zero sector count */
! 4115: if ( FDC_DMA.SectorCount != 0 )
! 4116: FDC_DMA.Status |= 0x02;
1.1.1.15 root 4117: else
1.1.1.18! root 4118: FDC_DMA.Status &= ~0x02;
1.1.1.15 root 4119:
1.1.1.18! root 4120: /* In the case of the ST, Bit2 / DRQ is always 0 because it's handled by the DMA and its 16 bytes buffer */
1.1.1.15 root 4121:
1.1.1.18! root 4122: /* Return Status with unused bits replaced by latest bits from $ff8604 */
! 4123: IoMem_WriteWord( 0xff8606 , FDC_DMA.Status | ( FDC_DMA.ff8604_recent_val & 0xfff8 ) );
1.1.1.15 root 4124: }
4125:
4126:
4127: /*-----------------------------------------------------------------------*/
4128: /**
4129: * Read hi/med/low DMA address byte at $ff8609/0b/0d
4130: */
4131: void FDC_DmaAddress_ReadByte ( void )
4132: {
4133: int FrameCycles, HblCounterVideo, LineCycles;
4134:
4135: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4136:
4137: 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" ,
4138: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] , FDC_GetDMAAddress() ,
4139: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4140: }
4141:
4142:
4143: /*-----------------------------------------------------------------------*/
4144: /**
4145: * Write hi/med/low DMA address byte at $ff8609/0b/0d
4146: */
4147: void FDC_DmaAddress_WriteByte ( void )
4148: {
4149: int FrameCycles, HblCounterVideo, LineCycles;
4150:
4151: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4152:
1.1.1.18! root 4153: /* On STF/STE machines limited to 4MB of RAM, DMA address is also limited to $3fffff */
! 4154: if ( ( IoAccessCurrentAddress == 0xff8609 )
! 4155: && ( (ConfigureParams.System.nMachineType == MACHINE_ST)
! 4156: || (ConfigureParams.System.nMachineType == MACHINE_STE)
! 4157: || (ConfigureParams.System.nMachineType == MACHINE_MEGA_STE) ) )
! 4158: IoMem[ 0xff8609 ] &= 0x3f;
! 4159:
! 4160: /* DMA address must be word-aligned, bit 0 at $ff860d is always 0 */
! 4161: if ( IoAccessCurrentAddress == 0xff860d )
! 4162: IoMem[ 0xff860d ] &= 0xfe;
! 4163:
1.1.1.15 root 4164: 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" ,
4165: IoAccessCurrentAddress , IoMem[ IoAccessCurrentAddress ] , FDC_GetDMAAddress() ,
4166: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
4167: }
4168:
4169:
4170: /*-----------------------------------------------------------------------*/
4171: /**
4172: * Get DMA address used to transfer data between FDC and RAM
1.1.1.9 root 4173: */
1.1.1.15 root 4174: Uint32 FDC_GetDMAAddress(void)
1.1 root 4175: {
1.1.1.6 root 4176: Uint32 Address;
1.1 root 4177:
1.1.1.6 root 4178: /* Build up 24-bit address from hardware registers */
4179: Address = ((Uint32)STMemory_ReadByte(0xff8609)<<16) | ((Uint32)STMemory_ReadByte(0xff860b)<<8) | (Uint32)STMemory_ReadByte(0xff860d);
1.1 root 4180:
1.1.1.6 root 4181: return Address;
1.1 root 4182: }
4183:
1.1.1.2 root 4184:
4185: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4186: /**
1.1.1.15 root 4187: * Write a new address to the FDC DMA address registers at $ff8909/0b/0d
1.1.1.18! root 4188: * As verified on real STF, DMA address high byte written at $ff8609 is masked
! 4189: * with 0x3f :
! 4190: * move.b #$ff,$ff8609
! 4191: * move.b $ff8609,d0 -> d0=$3f
! 4192: * DMA address must also be word-aligned, low byte at $ff860d is masked with 0xfe
! 4193: * move.b #$ff,$ff860d
! 4194: * move.b $ff860d,d0 -> d0=$fe
1.1.1.9 root 4195: */
1.1.1.15 root 4196: void FDC_WriteDMAAddress ( Uint32 Address )
1.1 root 4197: {
1.1.1.12 root 4198: int FrameCycles, HblCounterVideo, LineCycles;
4199:
4200: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4201:
4202: LOG_TRACE(TRACE_FDC, "fdc write 0x%x to dma address VBL=%d video_cyc=%d %d@%d pc=%x\n" ,
4203: Address , nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4204:
1.1.1.18! root 4205: /* On STF/STE machines limited to 4MB of RAM, DMA address is also limited to $3fffff */
! 4206: if ( (ConfigureParams.System.nMachineType == MACHINE_ST)
! 4207: || (ConfigureParams.System.nMachineType == MACHINE_STE)
! 4208: || (ConfigureParams.System.nMachineType == MACHINE_MEGA_STE) )
! 4209: Address &= 0x3fffff;
! 4210:
! 4211: Address &= 0xfffffffe; /* Force bit 0 to 0 */
! 4212:
1.1.1.6 root 4213: /* Store as 24-bit address */
4214: STMemory_WriteByte(0xff8609, Address>>16);
4215: STMemory_WriteByte(0xff860b, Address>>8);
4216: STMemory_WriteByte(0xff860d, Address);
1.1 root 4217: }
4218:
1.1.1.2 root 4219:
4220: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4221: /**
1.1.1.18! root 4222: * Return the number of FDC cycles to wait before reaching the next
! 4223: * sector's ID Field in the track ($A1 $A1 $A1 $FE TR SIDE SR LEN CRC1 CRC2)
! 4224: * If no ID Field is found before the end of the track, we use the 1st
! 4225: * ID Field of the track (which simulates a full spin of the floppy).
! 4226: * We also store the next sector's number into NextSector_ID_Field_SR,
! 4227: * the next track's number into NextSector_ID_Field_TR, the next sector's lenght
! 4228: * into NextSector_ID_Field_LEN and if the CRC is correct or not into
! 4229: * NextSector_ID_Field_CRC_OK.
! 4230: * This function assumes some 512 byte sectors stored in ascending
! 4231: * order (for ST/MSA)
! 4232: * If there's no available drive/floppy, we return -1
1.1.1.9 root 4233: */
1.1.1.18! root 4234: static int FDC_NextSectorID_FdcCycles_ST ( Uint8 Drive , Uint8 NumberOfHeads , Uint8 Track , Uint8 Side )
1.1 root 4235: {
1.1.1.18! root 4236: int CurrentPos;
! 4237: int MaxSector;
! 4238: int TrackPos;
! 4239: int i;
! 4240: int NextSector;
! 4241: int NbBytes;
! 4242:
! 4243: CurrentPos = FDC_IndexPulse_GetCurrentPos_NbBytes ();
! 4244: if ( CurrentPos < 0 ) /* No drive/floppy available at the moment */
! 4245: return -1;
! 4246:
! 4247: if ( ( Side == 1 ) && ( NumberOfHeads == 1 ) ) /* Can't read side 1 on a single sided drive */
! 4248: return -1;
! 4249:
! 4250: MaxSector = FDC_GetSectorsPerTrack ( Drive , Track , Side );
! 4251: TrackPos = FDC_TRACK_LAYOUT_STANDARD_GAP1; /* Position of 1st raw sector */
! 4252: TrackPos += FDC_TRACK_LAYOUT_STANDARD_GAP2; /* Position of ID Field in 1st raw sector */
! 4253:
! 4254: /* Compare CurrentPos with each sector's position in ascending order */
! 4255: for ( i=0 ; i<MaxSector ; i++ )
! 4256: {
! 4257: if ( CurrentPos < TrackPos )
! 4258: break; /* We found the next sector */
! 4259: else
! 4260: TrackPos += FDC_TRACK_LAYOUT_STANDARD_RAW_SECTOR_512;
! 4261: }
! 4262:
! 4263: if ( i == MaxSector ) /* CurrentPos is after the last ID Field of this track */
! 4264: {
! 4265: /* Reach end of track (new index pulse), then go to sector 1 */
! 4266: NbBytes = FDC_GetBytesPerTrack ( Drive ) - CurrentPos + FDC_TRACK_LAYOUT_STANDARD_GAP1 + FDC_TRACK_LAYOUT_STANDARD_GAP2;
! 4267: NextSector = 1;
! 4268: }
! 4269: else /* There's an ID Field before end of track */
! 4270: {
! 4271: NbBytes = TrackPos - CurrentPos;
! 4272: NextSector = i+1;
! 4273: }
! 4274:
! 4275: //fprintf ( stderr , "fdc bytes next sector pos=%d trpos=%d nbbytes=%d maxsr=%d nextsr=%d\n" , CurrentPos, TrackPos, NbBytes, MaxSector, NextSector );
! 4276: FDC.NextSector_ID_Field_TR = Track;
! 4277: FDC.NextSector_ID_Field_SR = NextSector;
! 4278: FDC.NextSector_ID_Field_LEN = FDC_SECTOR_SIZE_512; /* ST/MSA have 512 bytes per sector */
! 4279: FDC.NextSector_ID_Field_CRC_OK = 1; /* CRC is always correct for ST/MSA */
! 4280:
! 4281: return FDC_TransferByte_FdcCycles ( NbBytes );
! 4282: }
! 4283:
! 4284:
! 4285: /*-----------------------------------------------------------------------*/
! 4286: /**
! 4287: * Return the value of the track number in the next ID field set by
! 4288: * FDC_NextSectorID_FdcCycles_ST (for ST/MSA, it's always HeadTrack value)
! 4289: */
! 4290: static Uint8 FDC_NextSectorID_TR_ST ( void )
! 4291: {
! 4292: return FDC.NextSector_ID_Field_TR;
! 4293: }
! 4294:
! 4295:
! 4296: /*-----------------------------------------------------------------------*/
! 4297: /**
! 4298: * Return the value of the sector number in the next ID field set by
! 4299: * FDC_NextSectorID_FdcCycles_ST.
! 4300: */
! 4301: static Uint8 FDC_NextSectorID_SR_ST ( void )
! 4302: {
! 4303: return FDC.NextSector_ID_Field_SR;
! 4304: }
! 4305:
! 4306:
! 4307: /*-----------------------------------------------------------------------*/
! 4308: /**
! 4309: * Return the value of the sector's length in the next ID field set by
! 4310: * FDC_NextSectorID_FdcCycles_ST.
! 4311: * For ST/MSA, it's always 2 (512 bytes per sector)
! 4312: */
! 4313: static Uint8 FDC_NextSectorID_LEN_ST ( void )
! 4314: {
! 4315: return FDC.NextSector_ID_Field_LEN;
! 4316: }
! 4317:
! 4318:
! 4319: /*-----------------------------------------------------------------------*/
! 4320: /**
! 4321: * Return the status of the CRC in the next ID field set by
! 4322: * FDC_NextSectorID_FdcCycles_ST.
! 4323: * If '0', CRC is bad, else CRC is OK
! 4324: * For ST/MSA, CRC is always OK
! 4325: */
! 4326: static Uint8 FDC_NextSectorID_CRC_OK_ST ( void )
! 4327: {
! 4328: return FDC.NextSector_ID_Field_CRC_OK;
! 4329: }
! 4330:
! 4331:
! 4332: /*-----------------------------------------------------------------------*/
! 4333: /**
! 4334: * Read a sector from a floppy image in ST format (used in type II command)
! 4335: * Each byte of the sector is added to the FDC buffer with a default timing
! 4336: * (32 microsec)
! 4337: * Return 0 if sector was read without error, or FDC_STR_BIT_RNF if an error occurred
! 4338: * (FDC_STR_BIT_CRC_ERROR and FDC_STR_BIT_RECORD_TYPE are always set 0 for ST/MSA)
! 4339: */
! 4340: static Uint8 FDC_ReadSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int *pSectorSize )
! 4341: {
! 4342: int FrameCycles, HblCounterVideo, LineCycles;
! 4343: int i;
! 4344: Uint8 *pSectorData;
1.1.1.12 root 4345:
4346: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4347:
1.1.1.18! root 4348: 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" ,
! 4349: FDC_GetDMAAddress(), Drive, Track, Sector, Side,
1.1.1.12 root 4350: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4351:
1.1.1.18! root 4352: /* Get a pointer to the sector's data and convert into bytes/timings */
! 4353: if ( Floppy_ReadSectors ( Drive, &pSectorData, Sector, Track, Side, 1, NULL, pSectorSize ) )
! 4354: {
! 4355: for ( i=0 ; i<*pSectorSize ; i++ )
! 4356: FDC_Buffer_Add ( pSectorData[ i ] );
! 4357: return 0; /* No error */
! 4358: }
1.1 root 4359:
1.1.1.6 root 4360: /* Failed */
1.1.1.12 root 4361: LOG_TRACE(TRACE_FDC, "fdc read sector failed\n" );
1.1.1.18! root 4362: return FDC_STR_BIT_RNF;
1.1 root 4363: }
4364:
1.1.1.2 root 4365:
4366: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4367: /**
1.1.1.18! root 4368: * Write a sector to a floppy image in ST format (used in type II command)
! 4369: * Bytes were added to the FDC Buffer with a default timing (32 microsec) ;
! 4370: * we copy only the bytes into a temporary buffer, and write this buffer
! 4371: * to the floppy image.
1.1.1.15 root 4372: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
4373: * and some '0' bytes will be written to the disk.
1.1.1.18! root 4374: * Return 0 if sector was written without error, or FDC_STR_BIT_RNF if an error occurred
1.1.1.9 root 4375: */
1.1.1.18! root 4376: static Uint8 FDC_WriteSector_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side , int SectorSize )
1.1 root 4377: {
1.1.1.18! root 4378: int FrameCycles, HblCounterVideo, LineCycles;
! 4379: int i;
! 4380: Uint8 SectorData[ 1024 ]; /* max sector size for WD1772 */
1.1.1.12 root 4381:
4382: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
4383:
1.1.1.18! root 4384: 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" ,
! 4385: FDC_GetDMAAddress(), Drive, Track, Sector, Side,
1.1.1.12 root 4386: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
1.1.1.9 root 4387:
1.1.1.18! root 4388: /* Get the sector's data (ignore timings) */
! 4389: for ( i=0 ; i<SectorSize ; i++ )
! 4390: SectorData[ i ] = FDC_Buffer_Read_Byte_pos ( i );
! 4391:
! 4392: /* Write the sector's data */
! 4393: if ( Floppy_WriteSectors ( Drive, SectorData, Sector, Track, Side, 1, NULL, NULL ) )
! 4394: return 0; /* No error */
! 4395:
! 4396: /* Failed */
! 4397: LOG_TRACE(TRACE_FDC, "fdc write sector failed\n" );
! 4398: return FDC_STR_BIT_RNF;
! 4399: }
! 4400:
! 4401:
! 4402: /*-----------------------------------------------------------------------*/
! 4403: /**
! 4404: * Read an address field from a floppy image in ST format (used in type III command)
! 4405: * As ST images don't have address field, we compute a standard one based
! 4406: * on the current track/sector/side.
! 4407: * Each byte of the ID field is added to the FDC buffer with a default timing
! 4408: * (32 microsec)
! 4409: * Always return 0 = OK (FDC_STR_BIT_CRC_ERROR and FDC_STR_BIT_RNF are
! 4410: * always set 0 for ST/MSA)
! 4411: */
! 4412: static Uint8 FDC_ReadAddress_ST ( Uint8 Drive , Uint8 Track , Uint8 Sector , Uint8 Side )
! 4413: {
! 4414: int FrameCycles, HblCounterVideo, LineCycles;
! 4415: Uint8 buf_id[ 10 ]; /* 3 SYNC + IAM + TR + SIDE + SECTOR + SIZE + CRC1 + CRC2 */
! 4416: Uint8 *p;
! 4417: Uint16 CRC;
! 4418: int i;
! 4419:
! 4420: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 4421:
! 4422: p = buf_id;
! 4423:
! 4424: *p++ = 0xa1; /* SYNC bytes and IAM byte are included in the CRC */
! 4425: *p++ = 0xa1;
! 4426: *p++ = 0xa1;
! 4427: *p++ = 0xfe;
! 4428: *p++ = Track;
! 4429: *p++ = Side;
! 4430: *p++ = Sector;
! 4431: *p++ = FDC_SECTOR_SIZE_512; /* ST/MSA images are 512 bytes per sector */
! 4432:
! 4433: FDC_CRC16 ( buf_id , 8 , &CRC );
! 4434:
! 4435: *p++ = CRC >> 8;
! 4436: *p++ = CRC & 0xff;
! 4437:
! 4438: /* 6 bytes per ID field, don't return the 3 x $A1 and $FE */
! 4439: for ( i=4 ; i<10 ; i++ )
! 4440: FDC_Buffer_Add ( buf_id[ i ] );
! 4441:
! 4442: 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",
! 4443: buf_id[4] , buf_id[5] , buf_id[6] , buf_id[7] , buf_id[8] , buf_id[9] ,
! 4444: nVBLs, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC());
! 4445:
! 4446: return 0; /* No error */
! 4447: }
! 4448:
! 4449:
! 4450: /*-----------------------------------------------------------------------*/
! 4451: /**
! 4452: * Read a track from a floppy image in ST format (used in type III command)
! 4453: * As ST images don't have gaps,sync,..., we compute a standard track based
! 4454: * on the current track/side.
! 4455: * Each byte of the track is added to the FDC buffer with a default timing
! 4456: * (32 microsec)
! 4457: * Always return 0 = OK (we fill the track buffer in all cases)
! 4458: */
! 4459: static Uint8 FDC_ReadTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side )
! 4460: {
! 4461: int FrameCycles, HblCounterVideo, LineCycles;
! 4462: Uint8 buf_id[ 10 ]; /* 3 SYNC + IAM + TR + SIDE + SECTOR + SIZE + CRC1 + CRC2 */
! 4463: Uint8 *p;
! 4464: Uint16 CRC;
! 4465: int Sector;
! 4466: Uint8 *pSectorData;
! 4467: int SectorSize;
! 4468: int i;
! 4469:
! 4470: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 4471:
! 4472: 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" ,
! 4473: Drive, Track, Side, nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 4474:
! 4475:
! 4476: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP1 ; i++ ) /* GAP1 */
! 4477: FDC_Buffer_Add ( 0x4e );
! 4478:
! 4479: for ( Sector=1 ; Sector <= FDC_GetSectorsPerTrack ( Drive , Track , Side ) ; Sector++ )
1.1.1.6 root 4480: {
1.1.1.18! root 4481: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP2 ; i++ ) /* GAP2 */
! 4482: FDC_Buffer_Add ( 0x00 );
! 4483:
! 4484: /* Add the ID field for the sector */
! 4485: p = buf_id;
! 4486: for ( i=0 ; i<3 ; i++ ) *p++ = 0xa1; /* SYNC (write $F5) */
! 4487: *p++ = 0xfe; /* Index Address Mark */
! 4488: *p++ = Track; /* Track */
! 4489: *p++ = Side; /* Side */
! 4490: *p++ = Sector; /* Sector */
! 4491: *p++ = FDC_SECTOR_SIZE_512; /* 512 bytes/sector for ST/MSA */
! 4492: FDC_CRC16 ( buf_id , 8 , &CRC );
! 4493: *p++ = CRC >> 8; /* CRC1 (write $F7) */
! 4494: *p++ = CRC & 0xff; /* CRC2 */
! 4495:
! 4496: for ( i=0 ; i<10 ; i++ ) /* Add the ID field to the track data */
! 4497: FDC_Buffer_Add ( buf_id[ i ] );
! 4498:
! 4499: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3a ; i++ ) /* GAP3a */
! 4500: FDC_Buffer_Add ( 0x4e );
! 4501: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP3b ; i++ ) /* GAP3b */
! 4502: FDC_Buffer_Add ( 0x00 );
! 4503:
! 4504: /* Add the data for the sector + build the CRC */
! 4505: crc16_reset ( &CRC );
! 4506: for ( i=0 ; i<3 ; i++ )
! 4507: {
! 4508: FDC_Buffer_Add ( 0xa1 ); /* SYNC (write $F5) */
! 4509: crc16_add_byte ( &CRC , 0xa1 );
! 4510: }
! 4511:
! 4512: FDC_Buffer_Add ( 0xfb ); /* Data Address Mark */
! 4513: crc16_add_byte ( &CRC , 0xfb );
! 4514:
! 4515: if ( Floppy_ReadSectors ( Drive, &pSectorData, Sector, Track, Side, 1, NULL, &SectorSize ) )
! 4516: {
! 4517: for ( i=0 ; i<SectorSize ; i++ )
! 4518: {
! 4519: FDC_Buffer_Add ( pSectorData[ i ] );
! 4520: crc16_add_byte ( &CRC , pSectorData[ i ] );
! 4521: }
! 4522: }
! 4523: else
! 4524: {
! 4525: /* In case of error, we put some 0x00 bytes, but this case should */
! 4526: /* not happen with ST/MSA disk images, all sectors should be present on each track */
! 4527: for ( i=0 ; i<512 ; i++ )
! 4528: {
! 4529: FDC_Buffer_Add ( 0x00 );
! 4530: crc16_add_byte ( &CRC , 0x00 );
! 4531: }
! 4532: }
! 4533:
! 4534: FDC_Buffer_Add ( CRC >> 8 ); /* CRC1 (write $F7) */
! 4535: FDC_Buffer_Add ( CRC & 0xff ); /* CRC2 */
! 4536:
! 4537: for ( i=0 ; i<FDC_TRACK_LAYOUT_STANDARD_GAP4 ; i++ ) /* GAP4 */
! 4538: FDC_Buffer_Add ( 0x4e );
1.1.1.6 root 4539: }
1.1.1.18! root 4540:
! 4541: while ( FDC_Buffer_Get_Size () < FDC_GetBytesPerTrack ( Drive ) ) /* Complete the track buffer */
! 4542: FDC_Buffer_Add ( 0x4e ); /* GAP5 */
! 4543:
! 4544: return 0; /* No error */
! 4545: }
! 4546:
! 4547:
! 4548: /*-----------------------------------------------------------------------*/
! 4549: /**
! 4550: * Write a track to a floppy image in ST format (used in type III command)
! 4551: * Bytes were added to the FDC Buffer with a default timing (32 microsec) ;
! 4552: * we copy only the bytes into a temporary buffer, and write this buffer
! 4553: * to the floppy image.
! 4554: * If DMASectorsCount==0, the DMA won't transfer any byte from RAM to the FDC
! 4555: * and some '0' bytes will be written to the disk.
! 4556: * Return 0 if track was written without error, or FDC_STR_BIT_LOST_DATA if an error occurred
! 4557: *
! 4558: * TODO : this is not supported for ST/MSA at the moment, so we return FDC_STR_BIT_LOST_DATA
! 4559: */
! 4560: static Uint8 FDC_WriteTrack_ST ( Uint8 Drive , Uint8 Track , Uint8 Side , int TrackSize )
! 4561: {
! 4562: int FrameCycles, HblCounterVideo, LineCycles;
! 4563:
! 4564: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 4565:
! 4566: 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" ,
! 4567: FDC_GetDMAAddress(), Drive, Track, Side,
! 4568: nVBLs , FrameCycles, LineCycles, HblCounterVideo , M68000_GetPC() );
! 4569:
! 4570: Log_Printf(LOG_TODO, "FDC type III command 'write track' is not supported with ST/MSA files\n");
! 4571:
! 4572: /* TODO : "Write track" should write all the sectors after extracting them from the track data ? */
1.1 root 4573:
1.1.1.6 root 4574: /* Failed */
1.1.1.18! root 4575: LOG_TRACE(TRACE_FDC, "fdc write track failed\n" );
! 4576: return FDC_STR_BIT_LOST_DATA;
1.1 root 4577: }
4578:
4579:
1.1.1.2 root 4580: /*-----------------------------------------------------------------------*/
1.1.1.9 root 4581: /**
4582: * Write to floppy mode/control (?) register (0xff860F).
4583: * Used on Falcon only!
4584: * FIXME: I've found hardly any documentation about this register, only
4585: * the following description of the bits:
4586: *
4587: * __________54__10 Floppy Controll-Register
4588: * || ||
4589: * || |+- Prescaler 1
4590: * || +-- Media detect 1
4591: * |+----- Prescaler 2
4592: * +------ Media detect 2
4593: *
4594: * For DD - disks: 0x00
4595: * For HD - disks: 0x03
4596: * for ED - disks: 0x30 (not supported by TOS)
4597: */
1.1.1.15 root 4598: void FDC_FloppyMode_WriteByte ( void )
1.1.1.9 root 4599: {
4600: // printf("Write to floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4601: }
4602:
4603:
4604: /*-----------------------------------------------------------------------*/
4605: /**
4606: * Read from floppy mode/control (?) register (0xff860F).
4607: * Used on Falcon only!
4608: * FIXME: I've found hardly any documentation about this register, only
4609: * the following description of the bits:
4610: *
4611: * ________76543210 Floppy Controll-Register
4612: * ||||||||
4613: * |||||||+- Prescaler 1
4614: * ||||||+-- Mode select 1
4615: * |||||+--- Media detect 1
4616: * ||||+---- accessed during DMA transfers (?)
4617: * |||+----- Prescaler 2
4618: * ||+------ Mode select 2
4619: * |+------- Media detect 2
4620: * +-------- Disk changed
4621: */
1.1.1.15 root 4622: void FDC_FloppyMode_ReadByte ( void )
1.1.1.9 root 4623: {
4624: IoMem_WriteByte(0xff860f, 0x80); // FIXME: Is this ok?
4625: // printf("Read from floppy mode reg.: 0x%02x\n", IoMem_ReadByte(0xff860f));
4626: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.