/* Copyright (c) 1990 Commodore-Amiga, Inc.
 *
 * This example is provided in electronic form by Commodore-Amiga, Inc. for
 * use with the 1.3 revisions of the Addison-Wesley Amiga reference manuals.
 * The 1.3 Addison-Wesley Amiga Reference Manual series contains additional
 * information on the correct usage of the techniques and operating system
 * functions presented in this example.  The source and executable code of
 * this example may only be distributed in free electronic form, via bulletin
 * board or as part of a fully non-commercial and freely redistributable
 * diskette.  Both the source and executable code (including comments) must
 * be included, without modification, in any copy.  This example may not be
 * published in printed form or distributed with any commercial product.
 * However, the programming techniques and support routines set forth in
 * this example may be used in the development of original executable
 * software products for Commodore Amiga computers.
 * All other rights reserved.
 * This example is provided "as-is" and is subject to change; no warranties
 * are made.  All use is at your own risk.  No liability or responsibility
 * is assumed.
 */

/*
 * TrackDisk example code...
 *
 * This program does a track by track copy from DF0: to DF1:
 *
 * This program will only run from the CLI.  If started from
 * the workbench, it will just exit...
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/trackdisk.h>
#include <libraries/dosextens.h>

#include <proto/all.h>

#include <stdio.h>

/* This prevents Lattice ctrl-C processing... */
int CXBRK(VOID) { return(0); }

#define	TRACK_SIZE	((LONG)(NUMSECS * TD_SECTOR))

/*
 * Turn the BUSY flag off/on for the drive
 * If onflag is TRUE, the disk will be marked as busy...
 *
 * This is to stop the validator from executing while
 * we are playing with the disks.
 */
VOID disk_busy(UBYTE *drive,LONG onflag)
{
struct StandardPacket *pk;
struct Process        *tsk;

    tsk=(struct Process *)FindTask(NULL);
    if (pk=AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))
    {
        pk->sp_Msg.mn_Node.ln_Name=(UBYTE *)&(pk->sp_Pkt);

        pk->sp_Pkt.dp_Link=&(pk->sp_Msg);
        pk->sp_Pkt.dp_Port=&(tsk->pr_MsgPort);
        pk->sp_Pkt.dp_Type=ACTION_INHIBIT;
        pk->sp_Pkt.dp_Arg1=(onflag ? -1L : 0L);

        PutMsg(DeviceProc(drive),(struct Message *)pk);
        WaitPort(&(tsk->pr_MsgPort));
        GetMsg(&(tsk->pr_MsgPort));
        FreeMem(pk,(long)sizeof(*pk));
    }
}

/*
 * This turns the motor off
 */
VOID Motor_Off(struct IOExtTD *disk)
{
    disk->iotd_Req.io_Length=0;
    disk->iotd_Req.io_Command=TD_MOTOR;
    DoIO((struct IORequest *)disk);
}

/*
 * This turns the motor on
 */
VOID	Motor_On(struct IOExtTD *disk)
{
    disk->iotd_Req.io_Length=1;
    disk->iotd_Req.io_Command=TD_MOTOR;
    DoIO((struct IORequest *)disk);
}

/*
 * This reads a track, reporting any errors...
 */
SHORT Read_Track(struct IOExtTD *disk,UBYTE *buffer,SHORT track)
{
SHORT All_OK=TRUE;

    disk->iotd_Req.io_Length=TRACK_SIZE;
    disk->iotd_Req.io_Data=(APTR)buffer;
    disk->iotd_Req.io_Command=CMD_READ;
    disk->iotd_Req.io_Offset=(ULONG)(TRACK_SIZE * track);
    DoIO((struct IORequest *)disk);
    if (disk->iotd_Req.io_Error)
    {
        All_OK=FALSE;
        printf("Error %u when reading track %d",disk->iotd_Req.io_Error,track);
    }
    return(All_OK);
}

/*
 * This writes a track, reporting any errors...
 */
SHORT Write_Track(struct IOExtTD *disk,UBYTE *buffer,SHORT track)
{
SHORT All_OK=TRUE;

    disk->iotd_Req.io_Length=TRACK_SIZE;
    disk->iotd_Req.io_Data=(APTR)buffer;
    disk->iotd_Req.io_Command=TD_FORMAT;
    disk->iotd_Req.io_Offset=(ULONG)(TRACK_SIZE * track);
    DoIO((struct IORequest *)disk);
    if (disk->iotd_Req.io_Error)
    {
        All_OK=FALSE;
        printf("Error %d when writing track %d",disk->iotd_Req.io_Error,track);
    }
    return(All_OK);
}

/*
 * This function finds the number of TRACKS on the device.
 * NOTE That this is TRACKS and not cylinders.  On a Two-Head
 * drive (such as the standard 3.5" drives) the number of tracks
 * is 160, 80 cylinders, 2-heads.
 */
SHORT FindNumTracks(struct IOExtTD *disk)
{
    disk->iotd_Req.io_Command=TD_GETNUMTRACKS;
    DoIO((struct IORequest *)disk);
    return((SHORT)disk->iotd_Req.io_Actual);
}

/*
 * This routine allocates the memory for one track and does
 * the copy loop.
 */
VOID Do_Copy(struct IOExtTD *diskreq0,struct IOExtTD *diskreq1)
{
UBYTE *buffer;
SHORT track;
SHORT All_OK;
SHORT NumTracks;

    if (buffer=AllocMem(TRACK_SIZE,MEMF_CHIP|MEMF_PUBLIC))
    {
        printf(" Starting Motors\r");
        Motor_On(diskreq0);
        Motor_On(diskreq1);
        All_OK=TRUE;

        NumTracks=FindNumTracks(diskreq0);

        for (track=0;(track<NumTracks) && All_OK;track++)
        {
            printf(" Reading track %d\r",track);

            if (All_OK=Read_Track(diskreq0,buffer,track))
            {
                printf(" Writing track %d\r",track);

                All_OK=Write_Track(diskreq1,buffer,track);
            }
        }
        if (All_OK) printf(" * Copy complete *");
        printf("\n");
        Motor_Off(diskreq0);
        Motor_Off(diskreq1);
        FreeMem(buffer,TRACK_SIZE);
    }
    else printf("No memory for track buffer...\n");
}

/*
 * Prompts the user to remove one of the disks.
 * Since this program makes an EXACT copy of the disks
 * AmigaDOS would get confused by them so one must be removed
 * before the validator is let loose.  Also, note that the
 * disks may NEVER be in drives on the SAME computer at the
 * SAME time unless one of the disks is renamed.  This is due
 * to a bug in the system.  It would normally be prevented
 * by a diskcopy program that knew the disk format and modified
 * the creation date by one clock-tick such that the disks would
 * be different.
 */
VOID Remove_Disks(VOID)
{
    printf("\nYou *MUST* remove at least one of the disks now.\n");
    printf("\nPress RETURN when ready\n");
    while(getchar()!='\n');
}

/*
 * Prompts the user to insert the disks.
 */
VOID Insert_Disks(VOID)
{
    printf("\nPlease insert source disk in DF0:");
    printf("\n          and destination in DF1:\n");
    printf("\nPress RETURN when ready\n");
    while(getchar()!='\n');
}

/*
 * Open the devices and mark them as busy
 */
VOID Do_OpenDevice(struct IOExtTD *diskreq0,struct IOExtTD *diskreq1)
{
    if (!OpenDevice(TD_NAME,0L,(struct IORequest *)diskreq0,0L))
    {
        disk_busy("DF0:",TRUE);

        if (!OpenDevice(TD_NAME,1L,(struct IORequest *)diskreq1,0L))
        {
            disk_busy("DF1:",TRUE);

            Insert_Disks();
            Do_Copy(diskreq0,diskreq1);
            Remove_Disks();

            disk_busy("DF1:",FALSE);
            CloseDevice((struct IORequest *)diskreq1);
        }
        else printf("Could not open DF1:\n");

        disk_busy("DF0:",FALSE);
        CloseDevice((struct IORequest *)diskreq0);
    }
    else printf("Could not open DF0:\n");
}

VOID main(int argc,char *argv[])
{
struct IOExtTD *diskreq0;
struct IOExtTD *diskreq1;
struct MsgPort *diskPort;

    if (argc)       /* Check if started from the CLI... */
    {
        if (diskPort=CreatePort(NULL,NULL))
        {
            if (diskreq0=(struct IOExtTD *)CreateExtIO(diskPort,
                                                 sizeof(struct IOExtTD)))
            {
                if (diskreq1=(struct IOExtTD *)CreateExtIO(diskPort,
                                                 sizeof(struct IOExtTD)))
                {
                    Do_OpenDevice(diskreq0,diskreq1);
                    DeleteExtIO((struct IORequest *)diskreq1);
                }
                else printf("Out of memory\n");
                DeleteExtIO((struct IORequest *)diskreq0);
            }
            else printf("Out of memory\n");
            DeletePort(diskPort);
        }
        else printf("Could not create diskReq port\n");
    }
}
