Floppy Disk Controller: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content deleted Content added
Auto motor off info, fixed a couple more errors
Better info on "disk change" flag
Line 10: Line 10:
More detailed info can be found in the [[ATA in x86 RealMode (BIOS)]] article, because accessing a floppy is identical to accessing a hard disk
More detailed info can be found in the [[ATA in x86 RealMode (BIOS)]] article, because accessing a floppy is identical to accessing a hard disk
(using CHS) in Real Mode. The rest of this article deals with creating Protected Mode drivers for the floppy subsystem.
(using CHS) in Real Mode. The rest of this article deals with creating Protected Mode drivers for the floppy subsystem.

Note: the BIOS IRQ0 handler remembers a timeout for turning the motor off, from the last BIOS floppy access. The last access may have been
attempting to load your bootloader. So, in the distant future, if the BIOS ever receives 36 more IRQ0 ticks (if you ever return to Real Mode)
it may turn off your floppy motors for you, once.


=== Accessing USB Floppy Drives ===
=== Accessing USB Floppy Drives ===
Line 73: Line 77:


If you are using PIO mode in a singletasking environment then the IRQ6s just waste CPU cycles, and you should be using polling instead.
If you are using PIO mode in a singletasking environment then the IRQ6s just waste CPU cycles, and you should be using polling instead.
You can usually shut the IRQs off by setting bit 3 (value = 8) of the Digital Ouptput Register (see below).
You can usually shut the IRQs off by setting bit 3 (value = 8) of the Digital Ouptput Register (see below). If you want, you can even
toggle that bit on and off for specific commands, so that you receive some types of interrupts but not others.


To do PIO data transfers: init/reset the controller if needed (see below), select the drive if needed (see below),
To do PIO data transfers: init/reset the controller if needed (see below), select the drive if needed (see below),
Line 88: Line 93:
There were several generations of floppy controller chips on several generations of 80286 (and prior) computers, before
There were several generations of floppy controller chips on several generations of 80286 (and prior) computers, before
the 82077AA chip existed. The 82077AA was built to emulate all of them, by setting various pins on the chip to 5 volts.
the 82077AA chip existed. The 82077AA was built to emulate all of them, by setting various pins on the chip to 5 volts.
The three modes are: PC-AT mode, PS/2 mode, and Model 30 mode.
The three modes are: PC-AT mode, PS/2 mode, and Model 30 mode. The most likely mode you will see on any hardware that
The only mode you will ever see on any hardware that still runs is Model 30 mode. Sadly, most emulator programs run in PS/2 mode!
still runs is Model 30 mode. You may find some pre-1996 Pentium machines using PS/2 mode. Sadly, most emulator programs run in PS/2 mode!
In the documentation, you can ignore PC-AT mode. Or you can try to handle all three, by only using registers and commands that are identical
In the documentation, you can ignore PC-AT mode. Or you can try to handle all three, by only using registers and commands that are identical
in all 3 modes.
in all 3 modes.
Line 102: Line 107:
On real hardware, there are definite timing issues. Seek delays and motor delays are just what any programmer would expect. It
On real hardware, there are definite timing issues. Seek delays and motor delays are just what any programmer would expect. It
would be nice if the drive would send an IRQ when the motor was up to speed, but it does not. Two things that you may not
would be nice if the drive would send an IRQ when the motor was up to speed, but it does not. Two things that you may not
expect are that quite new hardware may still need artificial delays between outputting "command/parameter" bytes, and that you
expect are that quite new hardware probably still needs artificial delays between outputting "command/parameter" bytes, and that you
probably also need artificial delays between inputting "result" bytes. There is a bit in the MSR
probably also need artificial delays between inputting "result" bytes. There is a bit in the MSR to test in order to know when the
to test, to know when the next byte can be sent/retrieved. It is not a good idea to simply hardcode specific delays between output/input bytes.
next byte can be sent/retrieved. It is not a good idea to simply hardcode specific delays between output/input bytes.
Looping 20 times, and testing the value of the RMQ bit in the MSR each time, should always be a sufficient "timeout".
Looping 20 times, and testing the value of the RQM bit in the MSR each time, should always be a sufficient "timeout".


Using IO Port reads to generate delays leads to poor performance in a multitasking environment, and you may want to put the
However, using IO Port reads to generate delays (or polling MSR) leads to poor performance in a multitasking environment, and
driver to sleep for the shortest possible period (microsleep), instead.
you may want to put the driver to sleep for the shortest possible period (microsleep), instead.


== Registers ==
== Registers ==
Line 146: Line 151:
=== Bitflags and Registers that you can rely on ===
=== Bitflags and Registers that you can rely on ===
As said above, the most common controller chip has 3 modes, and many bitflags in the registers are different (or opposite!) depending on
As said above, the most common controller chip has 3 modes, and many bitflags in the registers are different (or opposite!) depending on
the mode. Some registers and bitflags remain the same, however. They are the following:
the mode. However, all of the important registers and bitflags remain the same between modes.
They are the following:


FIFO: The FIFO register may not have a 16byte buffer in all modes, but this is a minor difference that does not really affect its operation.
FIFO: The FIFO register may not have a 16byte buffer in all modes, but this is a minor difference that does not really affect its operation.
But be sure to place a fairly big delay (1 microsecond) betweeen each "outb" to the FIFO.


TDR: The Tape Drive Register is identical in all modes, but it is useless (you will never find functional equipment that requires it).
TDR: The Tape Drive Register is identical in all modes, but it is useless (you will never find functional equipment that requires it).
Line 260: Line 265:
The two important bits are RQM and DIO. NDMA and BUSY are also useful in polling PIO mode.
The two important bits are RQM and DIO. NDMA and BUSY are also useful in polling PIO mode.
Most important is RQM, which is set when it is OK (or necessary!) to read/write data from/to the FIFO port.
Most important is RQM, which is set when it is OK (or necessary!) to read/write data from/to the FIFO port.
DIO and BUSY should be checked to verify proper command termination (the end of "result phase" and beginning of "command phase").
NDMA signals the end of the "execution phase" of a command, and the beginning of "result phase".
NDMA signals the end of the "execution phase" of a command, and the beginning of "result phase".
DIO and BUSY should be checked to verify proper command termination (the end of "result phase" and beginning of "command phase").


==== CCR and DSR ====
==== CCR and DSR ====
Line 275: Line 280:
use different datarates, then you need to switch the datarate when you select the other drive. It also seems to be possible
use different datarates, then you need to switch the datarate when you select the other drive. It also seems to be possible
to modify this register while the FDC is in "reset state".
to modify this register while the FDC is in "reset state".

Note2: some tutorials seem to claim that changing/setting the datarate causes an IRQ6. This is false.


Datarates used for setting either DSR or CCR:
Datarates used for setting either DSR or CCR:
Line 285: Line 292:


Note: There is also a 300Kbps (value = 1), and a 250Kbps setting (value = 2) but they are only for utterly obsolete drives/media.
Note: There is also a 300Kbps (value = 1), and a 250Kbps setting (value = 2) but they are only for utterly obsolete drives/media.

=== Important Bitflags that depend on the Mode ===


==== DIR register, Disk Change bit ====
==== DIR register, Disk Change bit ====
This bit (bit 7, value = 0x80) could be very useful, if it actually works for you. It is supposed to tell you if the floppy door was
This bit (bit 7, value = 0x80) is fairly useful. It gets set if the floppy door was opened/closed. Sadly, almost all the emulator
opened. Sadly, almost all the emulator programs set and clear this bit completely inappropriately. Do not trust your handling of this
programs set and clear this bit completely inappropriately (especially after a reset).
bit until you have tested the functionality on real hardware.
Do not trust your handling of this bit until you have tested the functionality on real hardware.


Note: many sources say that you must turn on the drive motor bit before you access the DIR register for a selected drive.
Note: The datasheet is very confusing about the value of the bit, because Model 30 mode shows the bit as being inverted. But
in Model 30 mode, the '''signal''' is also inverted, so it comes out the same. "False" always means the bit is cleared, and
This claim needs further testing.
"true" always means the bit is set.


Note2: You must turn on the drive motor bit before you access the DIR register for a selected drive (you do not have to wait
Basically, you want to keep a flag for whether there is media in each drive. if Disk Change is "true" and there was media,
for the motor to spin up, though). It may also be necessary to read the register twice (and discard the first value), when
the OS should get a signal that the previous media was ejected, and the driver should automatically try to recalibrate the drive
changing the selected drive -- because "selecting" sometimes takes a little time.
for the new media. The recalibration serves two purposes. The heads may have moved when the new media was inserted, and the
recalibrate will correct for this. Also, the recalibrate will clear the Disk Change flag to "false" if there is '''new''' media
in the drive. You can find out initially whether there is media in the drives by issuing Recalibrate or Seek commands to all the drives
(see the [[#Reinitialization|reinitialization]] procedure below).


Basically, you want to keep a flag for whether there is media in each drive. If Disk Change is set and there was media,
If a drive has no media in it, and the driver wants to automatically check for new media, it can periodically do a Seek command
the OS should get a signal that the previous media was ejected.
on the drive. If Disk Change remains "true", then the drive is still empty. If it goes "false", it means that a new
floppy has been inserted, and the OS should get a "media inserted" signal. On some/most hardware, it is possible to
simply toggle the drive motor bit on, off, and then on again, to achieve the same result as doing a Seek (Linux calls this a "twaddle").


Once the Disk Change bit gets set (and you have "processed that message"), you need to try to clear the bit. The main way to
But there is a problem. As the section title says, the problem is that the '''value''' of the Disk Change bitflag depends
clear the bit is with a '''successful''' Seek to a '''new''' cylinder on the media. (Recalibrate or Reset do not work. And if the
on the mode. And there are 3 possible modes.
controller thinks the heads are already on the correct cylinder, it will eat the Seek command without clearing the Disk
And the idiots at Intel who made this chip failed utterly in giving you a definitive way of figuring out the mode.
Change bit.) If the seek fails, you can be fairly certain that there is no media in the drive anymore. It is important to note that
The three modes, again, are PC-AT mode, PS/2 mode, and Model 30 mode. It is easy to distinguish PS/2 mode from
this means you should check the value of Disk Change just prior to every Seek command that you do, because otherwise you
Model 30 mode. So how do you distinguish PC-AT mode from the other two? You can't. There isn't a bitflag anywhere
will lose any Disk Change information. This is also true for implied seeks, and relative seeks.
that will tell you whether the chip is in PC-AT mode.


Apparently a small number of floppy drives also support one additional way to clear the bit -- something that
What can you do? There are a few possibilities. The two main ones:
Linux calls a "twaddle". Simply toggle the drive motor bit on, off, and then on again. When your driver tries to clear the
# Ignore PC-AT mode. This is really a very safe assumption.
Disk Change bit the first time, it can try a twaddle, and see if it works, and keep a flag if it does.
# Do a workaround to figure out if this Disk Change bit means "true" when set, or "false" when set.


If you ignore PC-AT mode, then you can tell whether Disk Change is true or false by looking at the other bits in upper DIR.
Bits 4, 5, and 6 of DIR tell you what value of Disk Change is "false". Usually "true" = 1 on real hardware.

The workaround (to do ONCE): Read DIR. Assume that the current value of Disk Change means "true". Send a Recalibrate command.
If it works, then there is media in the drive, and Disk Change should have toggled to "false". So
read DIR again, check the Disk Change bit, and see if it flipped values. If it did, your initial guess for
the value of "true" is correct. (Otherwise, it was the opposite.)
If the Recalibrate fails, then there is no media in the drive, and Disk Change should continue to always be "true".
So your initial guess was correct.

Logic diagram:
{| {{wikitable}}
|-
! State of media
! actual DskChg flag
! guess
! Recalibrate result
! new actual DskChg flag
! guess good?
|-
| None in drive
| true
| true
| Failure
| true
| yes
|-
| Just inserted
| true
| true
| Success
| false
| yes
|-
| accessed many times
| false
| true
| Success
| false
| no
|}


==== Other bitflags in other registers ====
If you really want to try using non-essential bitflags that are mode-dependent, you will need to look in the
datasheet linked below for their locations and meanings. Including them would add too much extraneous info to
this article. As mentioned in the case of the Disk Change bit, it is not logically possible to completely
determine what mode the controller chip is in, so it is not really possible to know the meaning of any bitflag,
if there is any ambiguity between modes.


== Programming Details ==
== Programming Details ==
Line 373: Line 325:
accessed. To do that, you:
accessed. To do that, you:
# Turn on the drive's motor and select the drive, using an "outb" command to the DOR IO port.
# Turn on the drive's motor and select the drive, using an "outb" command to the DOR IO port.
# Wait for awhile for the motor to get up to speed, using some waiting scheme.
# Wait for awhile for the motor to get up to speed, using some waiting scheme (but don't wait too long).
# Issue your command byte plus some parameter bytes (the "command phase") to the FIFO IO port.
# Issue your command byte plus some parameter bytes (the "command phase") to the FIFO IO port.
# Exchange data with the drive / "seek" the drive heads (the "execution phase"), on the FIFO IO port.
# Exchange data with the drive / "seek" the drive heads (the "execution phase"), on the FIFO IO port.
Line 386: Line 338:
can completely lock up some systems. This claim seems unlikely?
can completely lock up some systems. This claim seems unlikely?


When you turn a floppy drive motor on, it takes quite a few milliseconds to "spin up" -- to reach the (stabilized) speed
Note2: modern floppy drives only spin the disk for one or two seconds, and then automatically turn the motors back off by themselves.
You need to access the drive within that window, or you will get an error. The motor might not even visibly turn on until you send
a command.

When you turn a floppy drive motor on, it takes quite a few milliseconds to "spinup" -- to reach the (stabilized) speed
needed for data transfer. The controller has electronics to handle a large variation in rotation speed, but it has its limits.
needed for data transfer. The controller has electronics to handle a large variation in rotation speed, but it has its limits.
If you start reading/writing immediately after the motor is turned on, "the PLL will fail to lock on to the data signal" and you will get an error.
If you start reading/writing immediately after the motor is turned on, "the PLL will fail to lock on to the data signal" and you will get an error.


After you are done reading (or writing), you should typically wait an additional half second to force the motor off. (It may also be smart
After you are done reading (or writing), you should typically wait an additional two seconds to turn the motor off. (It may also be smart
to seek the heads back to cylinder 0 just before turning the motor off.) You could leave the motor on longer,
to seek the heads back to cylinder 0 just before turning the motor off.) You could leave the motor on longer,
but it might give the user the idea that the floppy is still transferring data and that it's taking an awfully long time --
but it might give the user the idea that the floppy is still transferring data and that it's taking an awfully long time.
The reason to leave the motor on is that your driver may not know if there is a queue of sector reads or writes
and, as said above, the floppy drive may well turn its own motor off anyway.
that are going to be executed next. If there are going to be more drive accesses immediately, they won't need to wait
for the motor to spin up, if the motor is still on.


The suggested delays when turning the motor on are:
The suggested delays when turning the motor on are:
Line 461: Line 411:
## Bit 2 (value = 4) in the DOR: Save the current/"original" value of the DOR, write a 0 to the DOR, wait 4 microseconds, then write the original value (bit 2 is always set) back to the DOR.
## Bit 2 (value = 4) in the DOR: Save the current/"original" value of the DOR, write a 0 to the DOR, wait 4 microseconds, then write the original value (bit 2 is always set) back to the DOR.
## '''or''' Bit 7 (value = 0x80) in the DSR: Precalculate a good value for the DSR (generally 0), and OR it with 0x80. Write that value to the DSR.
## '''or''' Bit 7 (value = 0x80) in the DSR: Precalculate a good value for the DSR (generally 0), and OR it with 0x80. Write that value to the DSR.
# Wait for the resulting IRQ6.
# Wait for the resulting IRQ6. HIHI!! only in polling mode?
# Send a Sense Interrupt command (required).
# Send a Sense Interrupt command (required).
# If your OS/driver never turned off polling mode (see the Configure command), then you have to send 3 more Sense Interrupt commands. (Alternately, you can tell if you need the extra 3 by examining the top 2 bits of the st0 byte returned by the first Sense Interrupt. If both bits are set, you need to send 3 more.)
# If your OS/driver never turned off polling mode (see the Configure command), then you have to send 3 more Sense Interrupt commands. (Alternately, you can tell if you need the extra 3 by examining the top 2 bits of the st0 byte returned by the first Sense Interrupt. If both bits are set, you need to send 3 more.)
Line 488: Line 438:
command fails -- this may be the reason why.
command fails -- this may be the reason why.


# Read DIR. If the "Disk Change" bitflag is set to "true", then the floppy drive door was opened, so the OS needs to test if a new disk is in the drive.
# You need to either know or assume the "true" value for the Disk Change bitflag in DIR.
# Read DIR. If the "Disk Change" bitflag is set to "true", then the floppy drive door was opened, so the driver needs to Recalibrate the drive,
then the OS needs to test if a new disk is in the drive.


==== Waiting ====
==== Waiting ====
Line 516: Line 464:
## Loop on reading MSR until RQM = 1, verify that DIO = 1.
## Loop on reading MSR until RQM = 1, verify that DIO = 1.
## In a loop: read the next result byte from the FIFO, loop on reading MSR until RQM = 1, verify CMD BSY = 1 and DIO = 1 ((Value & 0x50) == 0x50).
## In a loop: read the next result byte from the FIFO, loop on reading MSR until RQM = 1, verify CMD BSY = 1 and DIO = 1 ((Value & 0x50) == 0x50).
# After reading all the expected result bytes: check them for error conditions, verify that RQM = 1, CMD BSY = 0, and DIO = 0. '''If not''' retry the
# After reading all the expected result bytes: check them for error conditions, verify that RQM = 1, CMD BSY = 0, and DIO = 0. '''If not''' retry the entire command again, several times, starting from step 2!
entire command again, several times, starting from step 2!


Note: implementing a failure timeout for each loop and the IRQ is pretty much required -- it is the only way to detect many command errors.
Note: implementing a failure timeout for each loop and the IRQ is pretty much required -- it is the only way to detect many command errors.
Line 572: Line 519:
==== Status Registers ====
==== Status Registers ====
There are 3 registers that hold information about the last error encountered. The st0 register information
There are 3 registers that hold information about the last error encountered. The st0 register information
is passed back with the result bytes of most commands, and can be retrieved at any time with a Sense
is passed back with the result bytes of most commands. The st1 and st2 information is returned in the result
bytes of read/write commands. They can also be retrieved with a Dumpreg command.
Interrupt command. The st1 and st2 information is returned in the result bytes of read/write commands. They
can also be retrieved with a Dumpreg command.


===== st0 =====
===== st0 =====
The top bit (value = 0x80) being set indicates two types of errors that you should never see.
The top 2 bits (value = 0xC0) are set after a reset procedure, with polling on. Either bit being set at any other
time is an error indication.
Bit 6 (value = 0x40) is set for any "normal" error condition, unless the error locks up the controller completely.
Bit 4 (value = 0x10) is set if Recalibrate, Seek, or an implied seek succeeds. The other bits are not useful.
Bit 5 (value = 0x20) is set after every Recalibrate, Seek, or an implied seek. The other bits are not useful.

===== st1 =====
===== st1 =====
The st1 register provides more detail about errors during read/write operations.
The st1 register provides more detail about errors during read/write operations.
Line 586: Line 533:
caused by '''not subtracting 1''' when setting the DMA byte count.
caused by '''not subtracting 1''' when setting the DMA byte count.
Bit 4 (value = 0x10) is set if your driver is too slow to get bytes in or out of the FIFO port in time.
Bit 4 (value = 0x10) is set if your driver is too slow to get bytes in or out of the FIFO port in time.
Bit 1 (value = 2) is set if the floppy drive is indicating that the media is write protected.
Bit 1 (value = 2) is set if the media is write protected.
The rest of the bits are various types of data errors, indicating bad media, or a bad drive.
The rest of the bits are for various types of data errors; indicating bad media, or a bad drive.

===== st2 =====
===== st2 =====
The st2 register provides more detail about errors during read/write operations.
The st2 register provides more (useless) detail about errors during read/write operations.


The bits all indicate various types of data errors, indicating bad media, or a bad drive.
The bits all indicate various types of data errors for either bad media, or a bad drive.




==== Configure ====
==== Configure ====
This command initializes controller-specific values: the data buffer "threshold" value, implied seek enable, FIFO enable, polling enable.
This command initializes controller-specific values: the data buffer "threshold" value, implied seek enable, FIFO disable, polling enable.
(And "Write Precompensation".) A good setting is: implied seek on, FIFO on, polling off, threshold = 8, precompensation 0.
(And "Write Precompensation".) A good setting is: implied seek on, FIFO on, polling off, threshold = 8, precompensation 0.


Line 656: Line 604:


===== SRT, HLT and HUT =====
===== SRT, HLT and HUT =====
These parameters sent with the Specify command to the controller are meant to optimize drive performance.
These parameters sent with the Specify command to the controller are meant to optimize drive performance, and head lifetime.


They are specific to the exact model, condition, and age of floppy drive installed on the system. The values sent by the driver to the
The values are specific to the exact model, condition, and age of floppy drive installed on the system. The values sent by the driver to the
controller were always meant
controller were always meant
to be '''adaptive'''. That is, your driver is theoretically supposed to keep statistics of how often Seek commands fail with the current
to be '''adaptive'''. That is, your driver is theoretically supposed to keep statistics of how often Seek commands fail with the current
setting of SRT. If they always work, and your driver wants to optimize performance, then it can send a new Specify command, with the SRT
setting of SRT. If they always work, and your driver wants to optimize performance, then it can send a new Specify command, with the SRT
value reduced by 1. Then begin keeping new statistics. Similarly for HLT and HUT regarding Read/Write operations. As drives age and collect
value reduced by 1. Then begin keeping new statistics. Similarly for HLT regarding Read/Write operations. As drives age and collect
dirt, the driver would automatically compensate by seeing higher statistical error rates, and increase the values of SRT, HLT, and HUT.
dirt, the driver would automatically compensate by seeing higher statistical error rates, and increase the values of SRT and HLT.


Keeping statistics in that way only works when the drive in question is used often. Now that internal floppy drives are nearly obsolete,
Keeping statistics in that way only works when the drive in question is used often. Now that internal floppy drives are nearly obsolete,
Line 703: Line 651:


Note: if you try to read the result bytes without waiting for RQM to set, then you are likely to always get an incorrect result value
Note: if you try to read the result bytes without waiting for RQM to set, then you are likely to always get an incorrect result value
of 0. This is also likely to get your driver out of sync with the FDC for input/output.
of 0. This is also likely to get your driver out of sync with the FDC for input/output. The correct value of st0 after a reset should
be 0xC0 | drive number. After a Recalibrate/Seek it should be 0x20 | drive number.


==== Recalibrate ====
==== Recalibrate ====
Line 742: Line 691:
A Sense Interrupt command is required after this command completes, to clear it from being BUSY. (Multiple Sense Interrupts,
A Sense Interrupt command is required after this command completes, to clear it from being BUSY. (Multiple Sense Interrupts,
if you ran multiple Seeks.)
if you ran multiple Seeks.)

Note: the controller tries to remember what cylinder each drive's heads are currently on. If you try to seek to that same
cylinder, then the controller will silently ignore the command (and return a "success" value). One of the things this means
is that you can get a "success" return value on a seek '''even if there is no media in the drive''', if you happen to seek
to the wrong cylinder number.


===== Relative seek =====
===== Relative seek =====
Line 752: Line 706:


==== Read/Write ====
==== Read/Write ====

Note: Remember that this is in CHS format, so the sector number starts at 1.
Note: Remember that this is in CHS format, so the sector number starts at 1.


Line 778: Line 731:


Note2: Floppy media and electronics are well known for being unreliable. Any read or write command that fails should be retried
Note2: Floppy media and electronics are well known for being unreliable. Any read or write command that fails should be retried
at least twice.
at least twice, unless it was a write and failed on "write protect".


=== Perpendicular Mode and 2.88M floppies ===
=== Perpendicular Mode and 2.88M floppies ===

If you are using an emulator and you need a floppy disk image that is bigger than 1440Kb, there is a 2880Kb image available.
If you are using an emulator and you need a floppy disk image that is bigger than 1440Kb, there is a 2880Kb image available.
In order to access it in Pmode, you need to modify your driver to handle Perpendicular Mode. Basically, it is an extra configuration
In order to access it in Pmode, you need to modify your driver to handle Perpendicular Mode. Basically, it is an extra configuration
Line 794: Line 746:


You also need to set CCR/DSR for the 1M datarate (value = 3) to access a 2.88M drive.
You also need to set CCR/DSR for the 1M datarate (value = 3) to access a 2.88M drive.

=== Additional Programming Notes ===
If you are doing a transfer between 2 floppy drives (so that both motors are on), and you are toggling "selection" between the two,
there may be a short delay required.


== Code Examples ==
== Code Examples ==