home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
datafiles
/
text
/
c_manual
/
devices
/
trackdiskdevice
/
trackdiskdevice.doc
< prev
next >
Wrap
Text File
|
1995-02-27
|
26KB
|
746 lines
6 TRACKDISK DEVICE
6.1 INTRODUCTION
All Amiga models are delivered with at least one internal
disk drive, but you may connect up to three extra disk drives.
The disk drivers are using normal double sided double density
(2DD) 3 1/2" disks. 5 1/4" disks may also be used, but this is
not supported by the trackdisk device.
The trackdisk device consists of a set of low level routines
which are used by other higher level processes like AmigaDOS.
When you want to read and write data you should normally only
use AmigaDOS or the special file commands in C. The trackdisk
device should only be used when you want to write disk
viewers/editors, or disk-copy programs.
Although the disk routines are normally considered to be very
slow on the Amiga, it should be noted that it is actually not
the disk drivers which are slow. AmigaDOS is using a very
flexible disk operating system and because of this the disk
drivers appears to be very slow. The low level routines are
on the other hand very quick and are turbocharged with the
"Blitter" (special coprocessor on the Amiga) which is
specialized in moving large quantities of data extremely fast.
To read and write so called "raw data" with the lowlevel
routines is much faster that to use the higher and more
sophisticated AmigaDOS routines. Many games use these low
level routines to quickly load graphics and sound effects.
Normal programs should, as said before, not use the lowlevel
routines described here. However, it can still be interesting
to read about it anyway, since it is always good to know what
is actually happening inside the Amiga.
6.2 AMIGA DISK DRIVERS
The trackdisk device was designed to handle normal double sided
double density (2DD) 3 1/2" disks (also called "1 megabyte
disks"). The disk can be logically (fysically too, but that is
not recommended) be divided into several small data areas where
the actual data is stored.
Each data area, normally called "Sector", can store 512 bytes
of data and 16 bytes of so called "label" data. The label area
is used to identify the sector and what is stored here, and can
therefore not be used to store raw data.
A group of eleven sectors is called "Cylinder" or "Track", and
there exist 80 cylinders on each side of the disk. In total you
can store 512 (bytes/sector) * 11 (sectors/cylinder) * 80
(cylinders/side) * 2 (double sided disks) = 901120 bytes = 880KB
(901120 / 1024).
The trackdisk device will only handle whole cylinders. Even if
you only want to read some sectors of a cylinder the complete
cylinder will be loaded into the trackdisk's own memory buffer.
This technique of reading and writing complete cylinders
greatly improves the storage capacity and speed of the drives.
The actual reading and writing process is very quick compared
to the time it takes for the drive to move the head to the right
position and start rotating the disk.
Your program will not notice that the trackdisk device is only
using complete cylinders. If you want to read some sectors, the
trackdisk device will read a complete cylinder, but gives you
only those sectors you wanted. If you later want to read some
sectors more it may happen that the device immediately can give
you the data you wanted without having to access the disk.
6.3 TRACKDISK DEVICE
The trackdisk device is controlled in the same manner as all
other devices. You send your commands to it with help of a
request block, and the device will send messages back to a
specific reply port. So, to use the trackdisk device you have
to: (Same as with most other devices.)
1. Create a message port with which the device can send
messages back to us. See chapter 1 "Devices" for more
information.
2. Allocate and preinitialize a request block.
3. Open the trackdisk device.
6.3.1 REQUESTBLOCK
A normal IOStdReq structure ("standard request block") is in
most cases possible to use, but if you want to use some special
features described further down you have to use the extended
request block "IOExtTD". This IOExtTD structure is declared in
header file "devices/trackdisk.h" as:
struct IOExtTD
{
struct IOStdReq iotd_Req;
ULONG iotd_Count;
ULONG iotd_SecLabel;
};
iotd_Req: The top part of the request block consists of
a standard request block (IOStdReq structure).
iotd_Count: Each disk drive you are working with is assigned
a counter value which is increased each time a
disk is removed. If you are using the "extended"
commands (described below) the trackdisk device
will check the driver's count value with this
value. If the counter is greater than this value
the request will be aborted.
If you are going to working with a disk it is
best to first get the driver's current count
value and store it here. You will then be sure
that the user will not change disks without you
knowing about it.
iotd_SecLabel: As described above, each sector has a 16 bytes
long "label" are. This label area is not used by
the track disk device. However, if you want to
read and/or write to this label area you should
use the "extended" read and write commands
described below, and this field should contain
a pointer to some memory where the label data
can be stored/read from.
NOTE! If you intend to read some sectors' label
areas you must make sure you have allocated a
buffer that is large enough. 16 bytes is needed
for every sector you intend to read. If you want
to write new sector labels the memory must of
course also be large enough, but also
initialized with the values you want to write.
Since this is an extended request block you have to use the
CreateExtIO() function rather than CreateStdIO().
Synopsis: ext_req = CreateExtIO( msg_port, size );
ext_req: (struct IORequest *) Pointer to the new extended
request block, or NULL if the request block could
not be created.
msg_port: (struct MsgPort *) Pointer to the message port
the device should use to communicate with you.
size: (long) The number of bytes that should be allocated
for the extended request block. Use the function
sizeof() to find the exact number of bytes needed.
If you are not going to use the "extended" commands which are
described below, but only the normal commands, you do not need
to use this extended request block. A normal standard request
block will be enough. However, if you later would change your
program so it starts to use the extended commands you have to
remember to also change the type of request block. Usually it
is best to always use an extended request block, although it
sometimes it not strictly necessary.
6.3.2 OPEN THE TRACKDISK DEVICE
Once you have opened a (reply) message port and created the
request block you can open the trackdisk device. This is done
with help of the now for us famous OpenDevice() function.
Synopsis: error = OpenDevice( name, unit, req, flags );
error: (long) If OpenDevice() managed to open the device
it returns 0, else an error number is returned.
name: (char *) Name of the device you want to open.
The name of the trackdisk device is (surprise)
"trackdisk.device". This name has been defined
is the header file as "TD_NAME".
When you are opening devices you should always try
to use the defined names rather than the actual
names. If Commodore would some day change the name
of the device (very unlikely though) you only have
to recompile your program with the new header
files, and your program will work again.
unit: (long) Which disk drive you want to use. All Amigas
are sold with at leas one internal drive (df0:),
but many have bought at lest one more drive. In
total there may exist up to four disk drivers.
Drive df0: has been given the unit number 0, df1:
the number 1, and so on...
If you are trying to access a drive which does
not exist, OpenDevice() will fail. See Example 2.
req: (struct IORequest *) Pointer to the request block
you have created and initialized.
flags: (long) This field is ignored by the trackdisk
device.
6.3.3 CLEAN UP
As always, do NOT forget to clean up after yourself! All opened
message ports, request blocks, devices etc. must be closed and
removed before your program may terminate. Here is what you
have to do:
1. Make sure that all your requests have been completed or
aborted.
2. Close the trackdisk device with help of the CloseDevice()
function.
Synopsis: CloseDevice( req );
reg: (struct IORequest *) Pointer to the device's
request block.
3. Delete all request blocks you have created. Note that if
you have allocated extended request blocks you must use
the DeleteExtIO() function, but if you have allocated
standard request blocks you can use the DeleteStdIO()
function. As recommended above you should only use the
extended request blocks with the trackdisk device.
Synopsis: DeleteExtIO( std_req, size );
std_req: (struct IOStdReq *) Pointer to the extended
request block you want to delete.
size: (long) The size of the request block, in bytes.
4. Close all message ports you have opened. Do this by
calling the DeletePort() function. Note that all messages
still left at the port must be removed before you may
close the message port.
Synopsis: DeletePort( msg_port );
msg_port: (struct MsgPort *) Pointer to the MsgPort
structure that should be deallocated.
6.4 COMMANDS
When the trackdisk device has successfully been opened you may
start to send requests to it. The trackdisk device can do a lot
of useful stuff, so there exist several commands that could be
used. These commands can be divided into two groups. The first
group consists of the "normal" commands. These commands will
not check if the disk has been removed or changed. Note also
that these commands can not be used to read or write the label
areas of the sectors.
The second group consists of the "extended" commands - those
commands that needs the extended IOExtTD structure. These
commands will check the unit counter too see if the disk has
been changed, and if so, the commands will automatically be
aborted. These commands can be used if you want to access
the sectors' label areas.
When you want to read and write data you have to give the
"io_Data" field of the request block a pointer to some memory
where all data you read will be placed, or if you are writing,
where the data that should be written is stored. This buffer
should be "word aligned" which means that it must start on
an even byte address. The memory must also be of the type
"Chip Memory" since the blitter will be used to speed up the
operations. To fulfill both demands you should allocate the
memory with help of the AllocMem function. See examples further
down for more information.
The "io_Offset" value contains the offset value from which
the trackdisk device will start to read/write from. The
correct offset value is calculated with this formula:
TD_SECTOR*( NUMSECS * 2 * cylinder + NUMSECS * head + sector )
The words in capital letters are defined in the header file
"devices/trackdisk.h" as:
TD_SECTOR 512 (512 bytes / sector)
NUMSECS 11 (11 sectors / cylinder)
For example, if you want to start to read at side 0, cylinder 5,
and sector 3, you should set the offset value to:
TD_SECTOR * ( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
You tell the trackdisk device to read/write a specific number
of bytes by setting the "io_Length" field to the number of
bytes that should be transferred. Note that you must read
complete sectors, and the length should therefore be set to:
TD_SECTOR * "nr of desired sectors". (The trackdisk device
will only read complete cylinders, but has nothing to do with
this restriction of only reading complete sectors.)
The "io_Actual" filed will contain the number of actual bytes
written/read. If this value is not the same as the "io_Length"
something has happened. Check the "io_Error" field to find
any error values. See below for more inforamtion about error
messages.
6.4.1 READ
Probably one of the most commonly used commands is undoubtedly
the read command. If you simply want to read some data, and
you do not care if the user has changed disks, you should use
the "CMD_READ" command. However, most times it is best to check
if the disk has been changed, before you start to read, and
then you should use the extended "ETD_READ" command.
Here is an example: ("exreq" is a pointer to the request block,
"buffer" is a pointer to some memory which is word aligned and
is of the type chip memory. The buffer must at least be 2 * 512
bytes.)
/* We want to read: */
exreq->iotd_Req.io_Command = ETD_READ;
/* Pointer to our buffer: */
exreq->iotd_Req.io_Data = (APTR) buffer;
/* Read two sectors: */
exreq->iotd_Req.io_Length = TD_SECTORS * 2;
/* Start to read at side 0, cylinder 5, and sector 3: */
exreq->iotd_Req.io_Offset = (LONG)
TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
/* Do our request: */
DoIO( exreq );
6.4.2 WRITE
Same as with reading, there exist two commands to write data.
The shorter "CMD_WRITE" will simply write to the disk which is
currently in the drive. The extended "ETD_WRITE" will check
that the disk has not been changed.
Here is a similar example as the one above. This time we will
try to write two sectors of data:
/* We want to write: */
exreq->iotd_Req.io_Command = ETD_WRITE;
/* Pointer to our buffer: */
exreq->iotd_Req.io_Data = (APTR) buffer;
/* Write two sectors: */
exreq->iotd_Req.io_Length = TD_SECTORS * 2;
/* Start to write at side 0, cylinder 5, and sector 3: */
exreq->iotd_Req.io_Offset = (LONG)
TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 + 3 );
/* Do our request: */
DoIO( exreq );
6.4.3 MOTOR ON/OFF
You can tell the trackdisk device to start or stop the motor of
the disk drive. Although you do not need to start the motor
(this will be done automatically if you start to read or write),
you must make sure to stop the drive after you have used it.
You can either use the command "TD_MOTOR" or "ETD_MOTOR". The
later will check that the disk has not been changed.
If the "io_Length" field is set to 0 the drive will be turned
off. If the field is set to 1 the drive will be turned on. Here
is an example ("exreq" is a pointer to the request block):
/* Turn motor on/off: */
exreq->iotd_Req.io_Command = ETD_MOTOR;
/* Turn the motor on: (0 = off, 1 = on) */
exreq->iotd_Req.io_Length = 1;
/* Do our request: */
DoIO( exreq );
Remember to always turn off the motor after you have used the
drive! (If the motor already was on before you started to use
it you do not need to turn it off. This means that some other
device is also using the drive.)
6.4.4 UPDATE THE DISK
When you write data to the disk it will not immediately be
stored. The trackdisk device will as explained above only read
and write complete cylinders, and if you do not write exactly
an even number of cylinders, some data will be temporarily
stored in the trackdisk device's internal memory buffer. When
you later change cylinder the buffer will be copied out before
the head is moved.
You can tell the trackdisk device to immediately copy the
data in the buffer to the disk by sending a "CMD_UPDATE" or
"ETD_UPDATE" command. The later command will as usual check if
the disk has been removed or changed before the buffer is
copied onto the disk.
After you have written something to the disk and before you
turn the motor off you should use this command. You will then
be sure that all data you have written will actually be on the
disk.
Here is an example:
/* Update the disk (move any data still left in the buffer */
/* to the disk: */
exreq->iotd_Req.io_Command = ETD_UPDATE;
/* Do our request: */
DoIO( exreq );
6.4.5 CLEAR BUFFER
You can tell the trackdisk device to clear it's internal
buffer by sending a "CMD_CLEAR" or "ETD_CLEAR" command. Be
sure that you do not clear anything that still has not been
moved out onto the disk.
This clear command is recommended to use after you have noticed
that the disks have been changed. This will ensure that old
data from another disk will not be written to the new disk.
Here is an example:
/* Clear the temporary buffer: */
exreq->iotd_Req.io_Command = ETD_CLEAR;
/* Do our request: */
DoIO( exreq );
6.4.6 POSITION THE HEAD
You can move the head to a specific position by sending a
"TD_SEEK" or "ETD_SEEK" command. The head will simply be moved
to the specified position in the "io_Offset" field, but no
data will be read or written. This command is usually not
needed since the head will always be placed on the correct
position when you read or write data.
Here is an example:
/* Position the head: */
exreq->iotd_Req.io_Command = ETD_SEEK;
/* Move to position: side 1, cylinder 2, and sector 4: */
exreq->iotd_Req.io_Offset = (LONG)
TD_SECTOR*( NUMSECS * 2 * 2 + NUMSECS * 1 + 4 );
/* Do our request: */
DoIO( exreq );
6.4.7 FORMAT
With help of the "TD_FORMAT" or "ETD_FORMAT" command you can
format a cylinder. You give the "io_Data" field a pointer to
some data which will be written onto the new cylinder, and
set the "io_Length" to NUMSECS * TD_SECTOR (one cylinder). The
"io_Offset" field should be set to an offset value to the
cylinder you want to format.
If you write data to a cylinder and you receive a hard write
error (the cylinder is not formatted), you can try to reformat
that cylinder. Note that it is usually best to tell the user
to use some other disk rather than trying to correct this one.
The user can then try to use "Diskdoctor" or similar programs
to solve the disk problem.
Here is an example:
/* We want to format one cylinder: */
exreq->iotd_Req.io_Command = ETD_FORMAT;
/* Pointer to our buffer: (Must be at least one */
/* cylinder of bytes large, NUMSECS * TD_SECTOR. */
exreq->iotd_Req.io_Data = (APTR) buffer;
/* Format one cylinder: */
exreq->iotd_Req.io_Length = NUMSECS * TD_SECTOR;
/* Format cylinder 5 on side 0: */
exreq->iotd_Req.io_Offset = (LONG)
TD_SECTOR*( NUMSECS * 2 * 5 + NUMSECS * 0 );
/* Do our request: */
DoIO( exreq );
6.4.8 REMOVE
You can send a "TD_REMOVE" command to the trackdisk device,
and it will then increase the driver's count value. It will
look like that the user has changed disks.
Here is an example:
/* Change disks: */
exreq->iotd_Req.io_Command = TD_REMOVE;
/* Do our request: */
DoIO( exreq );
6.4.9 GET THE DISK'S CURRENT COUNT NUMBER
The "extended" commands check if the disk has changed or not
before they are executed. If the disk has changed the command
is aborted. The commands compare the "iotd_Count" value of the
request block with the disk's current count value. If the
disk's count value is larger than the "iotd_Count" the command
is aborted and the error flag "TDERR_DiskChanged" is set.
Before you use these extended commands you should therefore
get the disk's current count value and store it in the request
block. To get the disk's current count value you send an
extended request block with the "TD_CHANGENUM" command set to
the trackdisk device. The device will as soon as possible
return your request with a copy of the current count value in
the "io_Actual" field of the request block.
Here is an example:
/* Store the current count value here: */
ULONG count_value;
/* Get the disk's change count value: */
extreq->iotd_Req.io_Command = TD_CHANGENUM;
/* Do our request, and return when completed: */
DoIO( extreq );
/* Save the count value in the variable: */
count_value = extreq->iotd_Req.io_Actual;
After you have got the current count value you should give it
the the request blocks which should later be used. The request
block can then use the extended commands. Example:
/* Give the request block the current count value: */
extreg->iotd_Count = count_value;
After your program has received a "TDERR_DiskChanged" you should
try to get the new count value before you attempt to access the
new disk.
6.4.10 CHECK IF THERE IS A DISK IN THE DRIVE OR NOT
With help of the trackdisk device can you check if there is a
disk in a drive or not. To do this you use the "TD_CHANGESTATE"
command. If there is a disk in the drive will the "io_Actual"
field of the request will contain zero. On the other hand, if
there is not a disk in the drive the field will contain a non
zero value.
/* Check if there is a disk in the drive or not: */
extreq->iotd_Req.io_Command = TD_CHANGESTATE;
/* Do our request, and return when completed: */
DoIO( extreq );
/* Is there a disk in the drive? */
if( extreq->iotd_Req.io_Actual )
printf( "No disk in the drive!\n" );
else
printf( "There is a disk in the drive!\n" );
6.4.11 CHECK IF THE DISK IS WRITE PROTECTED OR NOT
It can sometimes be very useful to know if the disk is write
protected or not. With help of the "TD_PROTSTATUS" command we
can easily check this. If the disk is write protected the
"io_Actual" field will contain a non zero value. On the other
hand, if the disk is not write protected the field will contain
zero.
/* Check if the disk is write protected or not: */
extreq->iotd_Req.io_Command = TD_PROTSTATUS;
/* Do our request, and return when completed: */
DoIO( extreq );
/* Is the disk write protected or not? */
if( extreq->iotd_Req.io_Actual )
printf( "The disk is write protected!\n" );
else
printf( "The disk is not write protected!\n" );
6.4.12 GET DRIVE TYPE
The trackdisk device can also be used to tell you what type of
disk drives are connected. If you send a request block with the
"TD_GETDRIVETYPE" command set, the "io_Actual" field of the
request block will either contain the flag "DRIVE3_5" or
"DRIVE5_25" when it is returned. The "DRIVE3_5" means that it
is a normal 3 1/2" disk drive, and the "DRIVE5_25" means that
it is a 5 1/4 (IBM) disk drive.
/* Check drive type: */
extreq->iotd_Req.io_Command = TD_GETDRIVETYPE;
/* Do our request, and return when completed: */
DoIO( extreq );
/* What type is it? */
if( extreq->iotd_Req.io_Actual == DISK3_5 )
printf( "It is a normal 3 1/2\" disk drive.\n" );
else
if( extreq->iotd_Req.io_Actual == DISK5_25 )
printf( "It is a 5 1/4\" disk drive.\n" );
else
printf( "It is a very strange disk drive!\n" );
6.4.13 GET THE NUMBER OF TRACKS
The "TD_GETNUMTRACKS" command is used to check how many tracks
are used by the drive. When the request is returned you can
check the "io_Actual" field of the request to get the number of
tracks. A normal 3 1/2 disk drive uses 160 tracks (80 tracks /
side).
/* Get the number of tracks this drive is using: */
extreq->iotd_Req.io_Command = TD_GETNUMTRACKS;
/* Do our request, and return when completed: */
DoIO( extreq );
/* How many tracks? */
printf( "No tracks: %d\n", extreq->iotd_Req.io_Actual );
6.5 ERRORS
When you are using the trackdisk device you will most definitely
receive many errors. The "io_Error" field of the requestblock
will contain 0 if the request was successfully executed, but if
something failed it will contain one of the following error
numbers: (Defined in the header file "devices/trackdisk.h".)
Error code Description
-------------------------------------------------------------
TDERR_NotSpecified A strange error has occurred.
TDERR_NoSecHdr Could not find a sector.
TDERR_BadSecPreamble Strange sector.
TDERR_BadSecID Also a strange sector.
TDERR_BadHdrSum Strange header.
TDERR_BadSecSum Strange data areas.
TDERR_TooFewSecs Missing some sectors.
TDERR_BadSecHdr Yet another strange sector.
TDERR_WriteProt The disk is write protected.
TDERR_DiskChanged The disk has been removed/changed.
TDERR_SeekError Could not reach track zero.
TDERR_NoMem Not enough memory.
TDERR_BadUnitNum Wrong unit number.
TDERR_BadDriveType Strange drive type.
TDERR_DriveInUse Someone is already using the drive.
TDERR_PostReset Tea time, the system is resetted.
There exist also four general device errors which all devices
may use: (Defined in header file "exec/errors.h".)
IOERR_OPENFAIL Could not open the device.
IOERR_ABORTED Request aborted.
IOERR_NOCMD Not a valid command.
IOERR_BADLENGTH Bad length or value.
-------------------------------------------------------------
6.6 EXAMPLES
Example 1
This program will use the Trackdisk Device to turn on and off
the internal disk drive's motor.
Example 2
This program demonstrates how you can check what went wrong
while you were using the Trackdisk Device. This example will
try to use drive DF3:, which most of us does not have, and
thus we will receive an error message. (Well if you have four
disk drives connected to your Amiga there will not be any
error message.)
Example 3
This example demonstrates how you can read data with help of
the Trackdisk Device. You give this program four arguments
(drive, head, cylinder and sector), and it will print out all
data in that sector. You only have to expand this program a
little and you will end up with a nice disk viewer.
Example3 drive (0-3) head (0-1) cylinder (0-79) sector (0-10)
Example 4
This example contains a lot of small and useful functions
that does almost everything you ever would like to do with
the trackdisk device. The example has been written so you can
easily use the functions in your own programs.