CPC Guide - Chips - Floppy Disc ControllerFloppy Disk Controller FDC ports The Floppy disk controller (NEC uPD765-A or INTEL 8272), is controlled using two ports. &FB7E - Main Status Register. Used for data transfer. See below. &FB7F - Data Register. When transfer is from CPU to FDC, you poke the values for the FDC to this port. When transfer is from FDC to CPU, the data returned by the FDC can be read from this port. There is also one other port (&FA7E). This port controls the Disk Drive Motor. The Drive motor MUST be switched on, before any commands are sent to the FDC. FDC pins _______ _______ RESET =|1 U 40|= Vcc /RD =| |= /RW/SEEK /WR =| |= LCT/DIR /CS =| |= FR/STP A0 =| |= HDL DB0 =| |= RDY DB1 =| |= WP/TS DB2 =|8 33|= FLT/TR0 DB3 =| |= PS0 DB4 =| |= PS1 DB5 =| uPD765-A |= WDA DB6 =| |= US0 DB7 =| |= US1 DRQ =| |= HD /DACK =| |= MFM TC =|16 25|= WE IDX =| |= VCO INT =| |= RD CLK =| |= RDW GND =|20___________21|= WCK Disk Drive Motor Control The disc drive motor is controlled by OUTing values to port &FA7E. Any odd value (bit 0 = 1), will switch the drive motor ON. Any even value (bit 0 = 0), will switch the drive motor OFF. This is a single bit port. The value of bit 0 is important, all other bits are ignored. When the drive is switched ON, the disk starts to pick up speed. A little time is needed before the disc is rotating at FULL speed. Many drives are different, and the time needed before the motor has picked up full speed will be different. If a command is sent to the FDC when the drive has not picked up FULL speed, then there will be lots of errors. Some drives will take less time for their motors to be rotating at full speed. The long pause is needed for compatibility with all drives. So, as soon as the drive has been switched off, pause before any commands are sent. The drive motor can be switched off immediatly, there is no need to wait for it to slow down. Although, it might make more reliable reading and writing if some time is allowed for it to slow down. But, I have not noticed any difference. Bit 7 ** ignored ** Bit 6 ** ignored ** Bit 5 ** ignored ** Bit 4 ** ignored ** Bit 3 ** ignored ** Bit 2 ** ignored ** Bit 1 ** ignored ** Bit 0 Disk drive motor control 1: ON, 0: OFF FDC Main Status Register The main status register is used to control data transfer between the FDC and the CPU. The Main Status Register is a byte, with each bit having a particular function. The function of each bit is shown below. The Main Status Register can be read from port &FB7E. (This port is INPUT only) Bit 7 Data flow flag 1: FDC is ready for data transfer. Bit 6 Data Direction flag 1: FDC --> CPU, 0: CPU --> FDC The data direction for different phases is shown below: Command phase: CPU->FDC CPU sends command bytes to FDC. Execution phase: Read data, read deleted data, read a track: FDC->CPU FDC sends sector contents to CPU. Write data, write deleted data, scan equal, scan low or equal, scan high or equal: CPU->FDC CPU sends sector contents to FDC. Format track CPU->FDC CPU sends sector ID information to FDC. Result phase: FDC->CPU FDC sends result bytes to CPU. Bit 5 Execution phase flag 1: FDC is performing execution phase of command This bit is set to 1 after the FDC has read all command bytes. It will be set to 0 when the execution phase has been completed or if an error is received. Bit 4 FDC Busy flag 1: FDC is busy executing a command. 0: FDC is in standby mode. This flag is set to 1 after the FDC has read the first command byte, and it is cleared as soon as all result bytes have been read. Bit 3 FDD 3 Busy (Disk Drive 3) ** NOT CONNECTED ON CPC ** Bit 2 FDD 2 Busy (Disk Drive 2) ** NOT CONNECTED ON CPC ** Bit 1 FDD 1 Busy (Disk Drive 1/Drive B) 1: FDD 1 is performing a seek (head is moving) Bit 0 FDD 0 Busy (Disk Drive 0/Drive A) 1: FDD 0 is performing a seek (head is moving) FDC Commands READ DATA Read sectors from current track. There is an option to skip sectors with deleted data address marks. Command phase MT MFM SK 0 0 1 1 0 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution Phase The FDC sends the sector data to the CPU. The amount of data depends on N. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> READ DELETED DATA Read sectors from current track. Read them if they contain deleted data. Command phase MT MFM SK 0 1 1 0 0 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution phase CPU must read sector data from FDC. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> WRITE DATA Write sectors to current track. Mark sectors with data address mark. Command phase MT MFM 0 0 0 1 0 1 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution phase CPU must write sector data to FDC. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> WRITE DELETED DATA Write sectors to current track. Mark sectors with deleted data address mark. Command phase MT MFM 0 0 1 0 0 1 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution phase CPU must write sector data to FDC. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> READ A TRACK This command will read EOT number of sectors from the current track. It will start by reading the first sector after the index pulse, and then read the data from each sector in turn. The sectors are read in the order they are found on the track (the order is specified in the format command). If the number of sectors to read is greater than the number of sectors stored on the track, then the read is repeated and the sectors are read in order again starting from the first sector. (data wraps) Command phase 0 MFM SK 0 0 0 1 0 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> number of sectors to read <------- GPL ---------> <------- DTL ---------> Execution phase CPU must read sector data from FDC. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> READ SECTOR ID Read sector ID from current position on current track. Repeat this command to read the sector ID for each sector on the track in the order that they were specified when the track was formatted. If you continue this command, the IDs will repeat. I do not know of any way to make sure that the Fdc will start reading with the first sector id actually on the track. Command phase 0 MFM 0 0 1 0 1 0 0 0 0 0 0 S DS1 DS0 Execution phase Internal execution phase. The FDC will read the ID for the current sector. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> } sector ID <-------- H ----------> } <-------- R ----------> } <-------- N ----------> } FORMAT A TRACK This command operates on the current track. This command writes the ID fields and data fields to initialise the track for storing data. Command phase 0 MFM 0 0 1 1 0 1 Command 0 0 0 0 0 S DS1 DS0 <-------- N ---------> Physical Sector size <-------- SC --------> Number of sectors per track <------- GPL --------> Formatting Gap length (usually &52) <-------- D ---------> Filler byte (byte to write into sector) (usually &e5) Execution phase The CPU must send the sector ID information for each sector. The sector ID is 4 bytes long and contains C,H,R,N values. The total number of bytes to transfer is number of sectors*4 For DATA format the following data would be transfered: &00,&00,&c1,&02, &00,&00,&c6,&02, &00,&00,&c2,&02, &00,&00,&c7,&02, &00,&00,&c3,&02, &00,&00,&c8,&02, &00,&00,&c4,&02, &00,&00,&c9,&02, &00,&00,&c5,&02 The fdc will create each of the sectors and fill them with the filler byte. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> SCAN EQUAL Command phase MT MFM SK 1 0 0 0 1 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution phase CPU must write sector data to FDC to be compared against the sector specified in the command. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> SCAN LOW OR EQUAL Command phase MT MFM SK 1 1 0 0 1 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution phase CPU must write sector data to FDC to be compared against the sector specified in the command. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> SCAN HIGH OR EQUAL Command phase MT MFM SK 1 1 1 0 1 0 0 0 0 0 S DS1 DS0 <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> <------- EOT ---------> <------- GPL ---------> <------- DTL ---------> Execution phase CPU must write sector data to FDC to be compared against the sector specified in the command. Result phase <------- ST0 ---------> <------- ST1 ---------> <------- ST2 ---------> <-------- C ----------> <-------- H ----------> <-------- R ----------> <-------- N ----------> RECALIBRATE Move the drive head to track 0. Command phase 0 0 0 0 0 1 1 1 0 0 0 0 0 0 DS1 DS0 Execution phase SENSE INTERRUPT STATUS Return Status Register 0, and the current position of the head. Command phase 0 0 0 0 1 0 0 0 Result phase <-------- ST0 --------> <-------- CTK --------> SPECIFY Specify physical drive operation times. Command phase 0 0 0 0 0 0 1 1 <--SRT---><--- HUT --> <---------HLT-----> ND SENSE DRIVE STATUS Return status information from selected drive. Command phase 0 0 0 0 0 1 0 0 0 0 0 0 0 S DS1 DS0 Result <------- ST3 --------> SEEK (Move read/write head) Command phase 0 0 0 0 1 1 1 1 0 0 0 0 0 S DS1 DS0 <------ DTK ---------> Execution The FDC will move the head. A sense interrupt status command must be sent to monitor the progress of this command. The sense interrupt status register is used to check if the seek command has been completed, and if the head is over the track we wanted. There is not any data transfer between CPU and FDC. INVALID (Not recognised command) If a bit pattern is specified which doesnt match one of the commands above then it is invalid. Command phase ? ? ? ? ? ? ? ? Result <-------- ST 0 -------> Key CTK = Physical track head is over, this changes when a SEEK or RECALIBRATE command is sent. (0..255) DTK = Destination track (physical track we want to move to) (0..255) SK = READ DATA command: Skip a sector if it is has deleted data mark READ DELETED DATA command: Skip a sector if is has a data mark Read data Skip bit: If skip=0 and the FDC finds a sector with a Deleted Data address mark, then Control Mark is set to 1 and stops read command after reading the data from the sector into memory. If skip=1 and the FDC finds a sector with deleted data address mark, then it will ignore the sector (not transfer any data) and read the next sector. Read deleted data Skip bit: If skip=0 and the FDc finds a data address mark, then control mark is set to 1 and it reads the sector data in and ends the command. If skip=1 then it skips the sector with data address mark and reads the next deleted data sector. MT = 1: Multi-track. In this mode, the FDC will automatically change side if needed. 0: Not multi-track. (not used much on the CPC) MFM = 1: Use double density (MFM), 0: use single density (FM) (not supported on CPC) DS1/DS0 = Drive DS1 DS0 Drive --------------------- 0 0 0/A 0 1 1/B 1 0 2 0 1 3 On the CPC, DS1 bit is ignored. S = Physical side (0=top, 1=bottom) C,H,R,N are the sector ID information. These values are stored before each sector on the track. They identify the sector and describe some information about it. C = Track value } These values are compared with the sector ID information H = Side value } on the disc. They may not be the same as the physical track and side. In some copyprotections these values are totally different from the physical track and side. R = Sector ID for first sector to read DATA FORMAT - &c1 SYSTEM FORMAT - &41 IBM FORMAT - &01 EOT = Sector ID for last sector to read. IF R=EOT then the FDC will only transfer 1 sector of data. IF R<>EOT then the FDC will start with the sector ID specified and after each sector read it will increment this value until it is the same as EOT. N = Sector size 0 - 128 bytes 1 - 256 bytes 2 - 512 bytes 3 - 1024 bytes 4 - 2048 bytes 5 - 4096 bytes 6 - 8192 bytes A track can only hold about 6.5k of data. Copyprotections which use a sector size of 6 only use &1800 bytes maximum (The remaining data transfered is garbage). These proove very difficult to copy by software alone. GPL = Gap Length for reading/writing data. SRT = Step Rate Time This is the time between each step pulse in ms. 1 = 1ms 2 = 2ms 3 = 3ms etc After this time, the drive head will have moved to the next track or previous track. HUT = Head unload time (time before head is removed from disk surface. This doesnt apply to 3" or 3.5" drives, and so therefore doesnt apply on CPC) HLT = Head load time (time before head is put to disk surface. This doesnt apply to 3" or 3.5" drives, and so therefore doesnt apply on CPC) ND = DMA mode 1=Do not use DMA control, 0=Use DMA control (not supported on CPC). This needs DMA support chips to control data transfer.) ST0 = FDC Status Register 0 ST1 = FDC Status Register 1 ST2 = FDC Status Register 2 ST3 = FDC Status Register 3 (drive status) DTL = Data length to read (Only makes sense if N=0. Then DTL is the number of bytes to read. DTL bytes will be returned by READ DATA and other similar commands). (If N<>0 (say 2), then DTL must be &ff). Peculiarities We can assume that when a READ DATA, READ DELETED DATA, WRITE DATA, WRITE DELETED DATA,READ A TRACK, READ SECTOR ID, FORMAT TRACK, SEEK, RECALIBRATE and the SCAN commands will clear ST0, ST1 and ST2 before execution. ST0=ST1=ST2=0. SENSE INTERRUPT STATUS will not clear ST0 before execution. It will return the current value of ST0. So when a SEEK is performed, it will hold ST0 bits saying when the seek has ended. If SENSE INTERRUPT STATUS is sent the first time, it will return the true value of ST0. But if it is sent for the second time, then ST0 will return &80. Subsequently it will continue to do this (if SENSE INTERRUPT STATUS) is repeatidly sent, until a command is executed which will change ST0. ST0 bit 7 and 6 reflect if there were any errors. Bit_7=Bit_6=ST0_Bit_3=1 when there is not a disk in the drive. (The drive will be not-ready). Bit_7=0, Bit_6=1,ST1_Bit_7=1 after a READ DATA/WRITE DATA operation (assuming the sector data is not corrupted). FDC status registers Status Register 0 Bit 7 Error code (see below) Bit 6 Bit 5 Seek end When the FDC has finished a SEEK, then this bit is set. Bit 4 Equipment Check If the FDC detects a fault from the FDD then this bit is set. Bit 3 Not Ready When the FDC tried to do a read or write command, the drive was not ready, then this bit is set. Also, if a read command is issued to side 2 of a single sided drive then this bit is set. Indicates that the drive motor is not running to full speed. A small amount of time is needed after a "start motor" has been issued before the motor is at full speed and the disk drive is ready for the commands. Bit 2 Head address Currently selected side of drive being used (0=top, 1=bottom) Bit 1 Unit Select 1 } drive number being used Bit 0 Unit select 0 } Error code 7 6 Error 0 0 Command was executed correctly. No error. Command was finished properly. 0 1 Command was started but not completed properly. 1 0 Invalid (not recognised) command was sent to FDC, and was not started. 1 1 Command was stopped before completion, because READY state changed from drive. (Drive was suddenly not ready, so the FDC gave up the rest of the command). Status Register 1 Bit 7 End of Cylinder When the FDC tries to get a sector beyond the end of a track this bit is set. (On the CPC this bit is always 1 - do not worry because the data will still be sent to the CPU). Bit 6 Not used (This bit is 0) Bit 5 Data Error When the FDC is reading the sectors from the track, it checks the data to make sure it is correct. This bit means that the check failed for the ID field or the Data field of a sector. This bit is 1, if ST2 bit 5=1. Bit 4 Overrun The CPU is taking to long to get the data the FDC is sending. (All FDC routines invloving reading or writing must run with interrupts DISABLED, otherwise the data will NOT be read into memory correctly. Interrupt loaders work, by disabling the interrupts whilst the loading is done, and then re-enabling the interrupts again.) Bit 3 Not used (This bit is 0) Bit 2 No data i) During a READ command, if the FDC cannot find the sector specified in the command, then this bit is set. ii) During a READ ID command, if the FDC cannot read the ID field without an error, this bit is set. This means that the track has not been formatted. iii) During a READ TRACK command, if the starting sector cannot be found this bit is set. Bit 1 Not Writeable If the FDC wants to do a write to the disk, and the disk is WRITE PROTECTED, then this bit is set. Bit 0 Missing Address Mark This indicates that the FDC has found the sector ID, but it cannot find the data for the sector. (Missing Address Mark in Data Field will also be set) Status register 2 (ST2) Bit 7 Not used. (This bit is 0) Bit 6 Control mark. When performing a read command. This bit indicates that a sector has been found which contains DELETED DATA. Bit 5 Data Error in Data Field When reading the sector data, the FDC has found that the CRC check doesnt match, which means the sector data could be corrupted. Bit 4 Wrong Cylinder/Wrong Track value When reading the sector, the C value specified in the command doesnt equal the C value stored for the sector on the disk. Bit 3 Scan Equal Hit When the FDC is executing a SCAN command, this indicates that the condition "equal" is true. Bit 2 Scan Not satisfied When the FDC is executing a SCAN command, this indicates that no sector has been found which matches the condition. Bit 1 Bad Cylinder Bit 0 Missing Address Mark in Data Field When the FDC is reading a track, this indicates that the FDC cannot detect if the sector contains DATA or DELETED DATA. The mark which identifies which one the sector contains cannot be found. Status Register 3 (ST3) This status register will always reflect the state of the drive hardware. Bit 7 Fault This is a signal from the FDD. It indicates if there is a fault with the drive. Bit 6 Disk Write protected This is a signal from the FDD. This indicates if the disk in the selected drive is write protected (1), or write enabled (0). Bit 5 Drive Ready 1: Drive Ready, 0: drive not ready. This is a signal from the FDD. This indicates the disk drive motor is on, and the disk is rotating at full speed, and the drive is ready to recieve commands. Bit 4 Disk drive head is positioned over track 0 1: Drive head is over track 0, 0: Drive is on another track. This is a signal from the FDD. This indicates that the read/write head is positioned over track 0 of the disc. Bit 3 Two side This is a signal from the FDD. This probably indicates that the drive is double sided. Bit 2 Head address This is a signal from the FDD. This indicates the current side being used. Bit 1 Unit select 1 } Drive number being used Bit 0 Unit select 0 } When a track is formatted, the FDC initialises a data-structure on the disc. For each sector there is an ID field followed by a Data Field. Both of these are followed by a CRC checksum which is used by the FDC to check the data is not corrupted. The ID field contains the C,H,R and N for the sector and the Data Field contains the data in the sector. The FDC can mark the sector data with a special byte to show it contains DELETED DATA. This does not mean that the data has been deleted, or it is stored in a different way, but it is a way of showing some difference between sectors which do not have this mark. Programming Examples 1) switch off drive motor, LD A,0 ; even = drive off LD BC,&FA7E ; disk drive motor control port OUT (C),A ; drive off 2) switch on drive motor and wait for it to pick up speed, LD A,1 ; odd = drive on LD BC,&FA7E ; disk drive motor control port OUT (C),A ; disk drive motor on LD B,5 .pause LD HL,0000 .pause2 DEC HL LD A,H OR L JR NZ,pause2 DJNZ pause ; wait a little while, and make sure drive ; is rotating at full speed. ; now our drive routines will be compatible ; with ALL drives. 3) Sending command byte to the FDC, ; Send a command byte to the FDC ; ; IN: ; A = command byte to send to FDC .send_command_byte LD BC,&FB7E ;; FDC Main status register PUSH AF .wait IN A,(C) ; get main status register ADD A,A JR NC,wait ; is fdc ready for data? ADD A,A JR NC,wait ; cpu->fdc? POP AF INC C ; BC = &fb7f (data register) OUT (C),A ; send command byte LD A,5 ; small pause to allow data register to pick up ; byte .DELAY DEC A JR NZ,DELAY RET 4) Getting result data from FDC, ; Get result data from FDC. ; ; IN: ; HL = 7 byte buffer to store result phase data .result_phase LD BC,&FB7E .l1 IN A,(C) ; check data register is ready to send data ; back to us CP &C0 ; FDC ready to send data and FDC->CPU? JP C,l1 INC C ; BC=&FB7F (data register) IN A,(C) ; get result byte LD (HL),A ; store in our buffer INC HL ; increment buffer position DEC C ; BC=&FB7F (main status register) LD A,5 ; small pause to wait for next byte from data ; register .delay DEC A JR NZ,delay IN A,(C) ; is fdc busy? (is there more data?) AND 16 JR NZ,l1 RET 5) Execution phase: read data from FDC, ; This is used to send data to the CPU. (FDC->CPU) ; ; IN: ; HL = address of buffer to put data in .execution_read LD BC,&FB7E JP r2 .r1 INC C ; &FB7F=data register IN A,(C) ; read byte LD (HL),A ; store in buffer INC HL ; increment position in buffer DEC C ; &FB7E=main status register .r2 IN A,(C) ; FDC ready for data? JP P,r2 AND 32 ; are we in the execution phase? JP NZ,r1 RET 6) Execution phase: write data to FDC, ; This routine is used to send data to the FDC. (CPU->FDC) ; ; IN: ; HL = address of buffer to write out .execution_write LD BC,&FB7E ; main status register JP w2 .w1 INC C ; &FB7F=data register LD A,(HL) ; get byte from buffer OUT (C),A ; send to FDC INC HL ; increment buffer position DEC C ; &FB7E=main status register .w2 IN A,(C) ; FDC ready for data? JP P,w2 AND 32 ; are we still in execution phase? JP NZ,w1 RET 7)Furthur examples 8)Disabling disk reading/writing, This is actually a bit weird. It is used in new Speedlock Tape protections to prevent those tape-to-disc routines from saving the main file to disk. The following makes the fdc a bit confused. The fdc can be reset so that it can save to disc again, but the code to do this is a bit lengthy, and often too much to fit into a tape-to-disc transfer program. ld bc,&fb7f ld a,3 out (c),a