home *** CD-ROM | disk | FTP | other *** search
- /*
- * This is dfc, a disk copying and formatting utility written by Radical
- * Eye Software. This program is in the public domain; anyone can do
- * anything they want with it. This software is made available on an as-is
- * basis; don't come to me if you destroy your entire fish disk library
- * with it! Of course, it was tested rather extensively before it was
- * released . . .
- *
- * This code was written with Manx 3.4b on February 8, 1988.
- *
- * dfc is meant to replace both format and diskcopy, and be smaller than
- * either. It is meant to work exactly the same as either, depending on
- * the command line arguments you give it. In addition, it has several
- * additional options, such as buffering of a disk for a quick additional
- * copy, the ability to toggle verify mode on and off, and the ability to
- * write to multiple disks at the same time. It also has a nice Intuition
- * interface. It will not replace the `DiskCopy' or `Format' options from
- * the workbench, however; these have to work with the Workbench startup
- * message, which this program does not.
- *
- * This program can be invoked with no command line options. In this case,
- * it defaults to a diskcopy from df0: to df1: with verify on and buffering
- * on. All keyword options can be preceded by a dash, for you Unix lovers
- * out there, and can be abbreviated to one character. The following
- * parameters are accepted:
- *
- * f[rom] disk Use `disk' as the source drive. The format for disk
- * is completely free; only the numerals in the argument
- * are looked at.
- * t[o] disks Use `disks' as the destination drives. Again, the
- * format is unspecified.
- * v[erify] Verify all writes (default).
- * nov[erify] Turn off the verify mode.
- * b[uffer] Use extra buffer memory. If your system has enough
- * memory, an entire disk will be cached in RAM.
- * Otherwise, almost all of the system memory will be
- * used.
- * nob[uffer] Do not buffer; use minimum memory. (Default.)
- * n[ame] diskname Format the destination disk with name `diskname'.
- * Otherwise a diskcopy is assumed.
- * d[...] A drive argument. Might not have a d; simple numbers
- * work as well. The first occurance of such an argument
- * sets the source and destination fields; a subsequent
- * occurance only sets the destination.
- *
- * Thus,
- * dfc df0: name "Foo bar baz"
- * formats drive df0: and names the resultant disk "Foo bar baz".
- * dfc df0: df1:
- * diskcopies from df0: to df1:.
- * dfc -nov 0 123
- * diskcopies from drive 0 to drives 1, 2, and 3, with verify turned off.
- * dfc from df0: to df0:,df1:,df2:,df3:
- * diskcopies from drive df0: to all four drives.
- * dfc name Foo
- * formats drive df1 with the name "Foo".
- *
- * Once the program opens its window up, you will have a bunch of gadgets.
- * The left-most column of gadgets is the source selection; here you can
- * choose either format, or one of the four drives. The next column is
- * the destination column; choose any combination of the four drives by
- * clicking on the gadgets. Lines will be drawn between these two columns
- * indicating the current selections. If a drive is not available, no line
- * will be drawn to it.
- *
- * The topmost gadget is the name gadget. This is used to set the name of
- * your disk while formatting.
- *
- * The `Go' gadget starts the program up; you had better have the disks in
- * the drives when you hit this gadget! The `Again' gadget makes another
- * copy of the last disk, if buffering is on and enough buffers were
- * allocated for an entire disk. This gadget requires all destination
- * disks to be in their drives already.
- *
- * The `Retry' gadget tells the system to retry after a disk error. The
- * `Quit' gadget tells the system to abort an operation; you can hit this
- * at an error, which aborts the current operation; you can hit this during
- * a copy or format operation, which does the same; or you can hit this
- * with the drives inactive, which exits the program.
- *
- * The `Verify' gadget turns the verify mode on and off. The `Buffer'
- * gadget turns buffering on and off.
- *
- * All of these gadgets have keyboard shortcuts which are simply the first
- * letter of the gadget name. The source gadgets can be selected with the
- * digits `0', `1', `2', or `3'; the destination gadgets with the shifted
- * versions of these keys (')', `!', `@', and `#'.) The carriage return
- * key is a synonym for `Go', for added diskcopy and format compatibility.
- *
- * Enjoy this program! Please send any bug reports to Tomas Rokicki,
- * Box 2081, Stanford, CA 94309.
- */
- #define TITLE "dfc 1 Radical Eye Software"
- /*
- * A handful of includes for good luck.
- */
- #include "intuition/intuition.h"
- #include "functions.h"
- #include <exec/exec.h>
- #include <exec/execbase.h>
- #include <devices/trackdisk.h>
- #include <libraries/dosextens.h>
- /*
- * These are the various globals this routine uses.
- */
- struct StandardPacket *gpacket ; /* packet to send various things */
- struct IntuiMessage *message ; /* the message we are working on */
- struct Gadget *gadad ; /* address of gadget from message */
- struct Window *window ; /* our window */
- struct MsgPort *port ; /* I/O port for dos communication */
- struct IntuitionBase *IntuitionBase ;
- /* we need Intuition */
- struct GfxBase *GfxBase ; /* and graphics */
- int havedisk ; /* do we have a full disk in RAM? */
- int hibuf ; /* the highest buffer we have */
- long output ; /* can we write output? */
- char *buffers[81] ; /* pointers to our buffers */
- struct IOExtTD *diskreq[4] ; /* our I/O request blocks */
- long diskChangeCount[4] ; /* last time disk was changed */
- int source = 1 ; /* the source disk, not a mask */
- int dest = 2 ; /* all destination drives */
- int verify = 1 ; /* verify writes? */
- int buffer = 0 ; /* are we using lots of memory? */
- char namebuf[32] ; /* this buffer holds disk names */
- char df0[] = "DF0:" ; /* use only one copy of this name */
- char df1[] = "DF1:" ; /* ditto */
- char df2[] = "DF2:" ; /* ditto */
- char df3[] = "DF3:" ; /* ditto */
- char *df[]={df0, df1, df2, df3}; /* for easy access to drive names */
- char blank[] = " " ;
- /* used to blank out the middle line */
- char msg[] = "Disk df is not complete" ;
- /* error message for a disk */
- char reading[] = "Reading track xx dfx" ;
- /* message for reading disks */
- char writing[] = "Writing track xx dfx" ;
- /* message for writing disks */
- char veriing[] = "Ver'ing track xx dfx" ;
- /* message for ver'ing disks */
- char nobuf[] = "! couldn't get buffer" ;
- /* if we can't get a buffer */
- /*
- * Always use topaz 80, and let's define an intuition buffer for our
- * general string writing stuff.
- */
- struct TextAttr myfont = {(STRPTR)"topaz.font", TOPAZ_EIGHTY, 0, 0 };
- struct IntuiText intuitext = {1, 0, JAM2, 0, 0, &myfont} ;
- /*
- * Some defines setting the sizes of various things. Play with these to
- * resize things. Too bad string gadgets still have problems with large
- * fonts.
- */
- #define WIDEGADG 83
- #define NARROWGADG 51
- #define STRGADG 256
- #define GADGHEIGHT 13
- #define LINE1 13
- #define LINE2 LINE1+11
- #define LINE3 LINE2+GADGHEIGHT
- #define LINE4 LINE3+GADGHEIGHT
- #define LINE5 LINE4+GADGHEIGHT
- #define LINE6 LINE5+GADGHEIGHT
- #define WINDOWHEIGHT LINE6+GADGHEIGHT+2
- #define COL1 4
- #define LINESTART COL1+NARROWGADG
- #define STRSTART COL1+NARROWGADG+23
- #define COL2 COL1+NARROWGADG+42
- #define LINEEND COL2-1
- #define COL3 COL2+NARROWGADG+10
- #define COL4 COL3+WIDEGADG+10
- #define WINDOWWIDTH COL4+WIDEGADG+4
- #define TRACKSIZE (2*512*11)
- /*
- * Now, some defines to assist in declaring a few things, and
- * centering strings, and the like. These macros make the Intuition
- * crap a lot easier to put together.
- */
- #define makeintuitext(name,string,size) struct IntuiText name={1,0,JAM2,\
- (1+size-8*(sizeof(string)-1))/2,3,&myfont,(UBYTE*)string}
- #define makebox(name,tname,width,height,off) short tname[]={0,0,width,0,\
- width,1,0,1,0,height-1,width,height-1,width,height,0,height,width,height,\
- width,0};\
- struct Border name={off,off,1,0,JAM2,10,tname}
- #define makengadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
- xpos,ypos,NARROWGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
- (APTR)&narrowbox,NULL,&text,NULL,NULL,ch}
- #define makewgadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
- xpos,ypos,WIDEGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
- (APTR)&widebox,NULL,&text,NULL,NULL,ch}
- /*
- * Now we declare all of our structures with the above macros.
- * First, the text for our gadgets, and then the gadgets. No sweat.
- */
- makeintuitext(formattext,"Format",NARROWGADG);
- makeintuitext(df0text,df0,NARROWGADG);
- makeintuitext(df1text,df1,NARROWGADG);
- makeintuitext(df2text,df2,NARROWGADG);
- makeintuitext(df3text,df3,NARROWGADG);
- makeintuitext(againtext,"Again",WIDEGADG);
- makeintuitext(retrytext,"Retry",WIDEGADG);
- makeintuitext(quittext,"Quit",WIDEGADG);
- makeintuitext(verifyontext,"Verify On ",WIDEGADG);
- makeintuitext(verifyofftext,"Verify Off",WIDEGADG);
- makeintuitext(bufferontext,"Buffer On ",WIDEGADG);
- makeintuitext(bufferofftext,"Buffer Off",WIDEGADG);
- makeintuitext(gotext,"Go",WIDEGADG);
- makebox(narrowbox,tn1,NARROWGADG-1,GADGHEIGHT-1,0);
- makebox(widebox,tn2,WIDEGADG-1,GADGHEIGHT-1,0);
- makebox(strbox,tn3,STRGADG-1,GADGHEIGHT-1,-2);
- makengadg(formatgadg,NULL,COL1,LINE2,formattext,'f');
- makengadg(sdf0gadg,&formatgadg,COL1,LINE3,df0text,'0');
- makengadg(sdf1gadg,&sdf0gadg,COL1,LINE4,df1text,'1');
- makengadg(sdf2gadg,&sdf1gadg,COL1,LINE5,df2text,'2');
- makengadg(sdf3gadg,&sdf2gadg,COL1,LINE6,df3text,'3');
- makengadg(ddf0gadg,&sdf3gadg,COL2,LINE3,df0text,')');
- makengadg(ddf1gadg,&ddf0gadg,COL2,LINE4,df1text,'!');
- makengadg(ddf2gadg,&ddf1gadg,COL2,LINE5,df2text,'@');
- makengadg(ddf3gadg,&ddf2gadg,COL2,LINE6,df3text,'#');
- makewgadg(gogadg,&ddf3gadg,COL3,LINE4,gotext,'g');
- makewgadg(retrygadg,&gogadg,COL3,LINE5,retrytext,'r');
- makewgadg(verifygadg,&retrygadg,COL3,LINE6,verifyontext,'v');
- makewgadg(againgadg,&verifygadg,COL4,LINE4,againtext,'a');
- makewgadg(quitgadg,&againgadg,COL4,LINE5,quittext,'q');
- makewgadg(buffergadg,&quitgadg,COL4,LINE6,bufferontext,'b');
- /*
- * We need one last gadget, the string gadget, and its associated
- * special info structure.
- */
- struct StringInfo nameinfo={(UBYTE*)namebuf,NULL,0,31};
- struct Gadget namegadg={&buffergadg,STRSTART+2,LINE2+2,STRGADG-4,
- GADGHEIGHT-4,GADGHCOMP,STRINGCENTER|RELVERIFY,STRGADGET,(APTR)&strbox,
- NULL,NULL,NULL,(APTR)&nameinfo,'n'};
- /*
- * Now we have our window structure. Initially not resizeable.
- */
- struct NewWindow newwindow = {200,20,WINDOWWIDTH,WINDOWHEIGHT,0,1,
- CLOSEWINDOW|VANILLAKEY|GADGETDOWN|GADGETUP,
- WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|SMART_REFRESH|ACTIVATE,
- &namegadg,NULL,(UBYTE *)TITLE,NULL,NULL,-1,-1,-1,-1,WBENCHSCREEN};
- /*
- * Now we start coding. This routine draws a string into the window at
- * a specific X, Y location.
- */
- draw(s, x, y)
- char *s ;
- int x, y ;
- {
- intuitext.IText = (UBYTE *)s ;
- PrintIText(window->RPort, &intuitext, (long)x, (long)y) ;
- }
- /*
- * This routine gives us those pretty little DF0:BUSY things, which keep
- * AmigaDOS from futzing with the drives when we are playing with them.
- */
- inhibit(d, t)
- int d ;
- long t ;
- {
- struct MsgPort *handler ;
- struct StandardPacket *packet = gpacket ;
-
- handler = (struct MsgPort *)DeviceProc(df[d]) ;
- if (handler == NULL || port == NULL)
- return ;
- packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt) ;
- packet->sp_Pkt.dp_Link = &(packet->sp_Msg) ;
- packet->sp_Pkt.dp_Port = port ;
- packet->sp_Pkt.dp_Type = ACTION_INHIBIT ;
- packet->sp_Pkt.dp_Arg1 = t ;
- PutMsg(handler, packet) ;
- WaitPort(port) ;
- GetMsg(port) ;
- }
- /*
- * This routine looks at the global message and returns a character
- * indicating the selected gadget. This gives us easy equivalence
- * of gadgets to vanillakeys, for instance. This also replymsg()'s
- * the message.
- */
- int getop() {
- register long class ;
- register int op ;
- short code ;
-
- class = message->Class ;
- code = message->Code ;
- op = ' ' ;
- gadad = (struct Gadget *)(message->IAddress) ;
- ReplyMsg(message) ;
- message = NULL ;
- if (class == CLOSEWINDOW) {
- op = 'q' ;
- } else if (class == GADGETDOWN || class == GADGETUP) {
- op = gadad->GadgetID ;
- } else if (class == VANILLAKEY) {
- op = code ;
- gadad = NULL ;
- }
- return(upcase(op)) ;
- }
- /*
- * We don't want to queue up messages, because the user might hit
- * 'g' 100 times accidentally. We flush all pending messages, and
- * return the last operation. This way, this routine can be used
- * to see if the user typed 'Q' to exit some operation.
- */
- int disposemsgs() {
- register int op = 0 ;
-
- while (message = (struct IntuiMessage *)
- GetMsg(window->UserPort))
- op = getop() ;
- return(op) ;
- }
- /*
- * This is put into a function to help make the program smaller.
- * It takes a character and makes sure it is uppercase.
- */
- int upcase(c)
- register int c ;
- {
- if ('a' <= c && c <= 'z')
- return(c-32) ;
- else
- return(c) ;
- }
- /*
- * Here we wait for a quit or retry key. We also accept go, space,
- * and carriage return.
- */
- int abortretry() {
- register int op ;
-
- disposemsgs() ;
- while (1) {
- while ((message = (struct IntuiMessage *)
- GetMsg(window->UserPort))==NULL)
- WaitPort(window->UserPort) ;
- op = getop() ;
- if (op == 'Q' || op == 'R' || op == ' ' || op == 'G' || op == 10)
- return(op) ;
- }
- }
- /*
- * This routine pads a string out to 40 characters. Used to write to
- * the top line of the window.
- */
- static char ibuf[41] ;
- char *to40(s)
- register char *s ;
- {
- register int i = 0 ;
- register char *p = ibuf ;
-
- while (*s != 0)
- p[i++] = *s++ ;
- while (i < 40)
- p[i++] = ' ' ;
- return(p) ;
- }
- /*
- * This error routine draws a message up at the top of the screen, and
- * waits for a response. After it gets one, it returns the response.
- * It clears the top line afterwards.
- */
- int error(s)
- register char *s ;
- {
- int op ;
-
- DisplayBeep(NULL) ;
- if (*s == '!' || !window) {
- if (output) {
- Write(output, s, (long)strlen(s)) ;
- Write(output, "\n", 1L) ;
- }
- cleanup() ;
- } else {
- draw(to40(s), COL1, LINE1+2) ;
- op = abortretry() ;
- }
- draw(to40(""), COL1, LINE1+2) ;
- return(op) ;
- }
- /*
- * If the user selects a set of drives, this routine first tries to
- * allocate them. Then, based on the success of the allocation, it
- * draws lines in the window indicating the source and destination
- * drives.
- */
- redrawlines() {
- register int i, j ;
-
- allocdisks() ;
- SetAPen(window->RPort, 0L) ;
- RectFill(window->RPort, (long)LINESTART, (long)LINE2, (long)STRSTART-1,
- (long) LINE6+GADGHEIGHT) ;
- RectFill(window->RPort, (long)STRSTART, (long)LINE3, (long)LINEEND,
- (long)LINE6+GADGHEIGHT) ;
- SetAPen(window->RPort, 1L) ;
- i = LINE3 + (GADGHEIGHT+1)/2 + source * GADGHEIGHT ;
- for (j=0; j<4; j++)
- if (dest & (1 << j)) {
- Move(window->RPort, (long)LINESTART, (long)i) ;
- Draw(window->RPort, (long)LINEEND,
- (long)(LINE3 + (GADGHEIGHT+1)/2 + j * GADGHEIGHT)) ;
- }
- }
- /*
- * This routine parses a string, usually something like 'df0:' or
- * 'df0:,df1:,df2:', but can be even '012', into just a mask indicating
- * which drives were selected. It simply looks for the characters '0',
- * '1', '2', and '3'.
- */
- int getmask(s)
- register char *s ;
- {
- register int t = 0 ;
-
- while (*s != 0) {
- if ('0' <= *s && *s <= '3')
- t |= 1 << (*s - '0') ;
- s++ ;
- }
- return(t) ;
- }
- /*
- * This is our exit routine. It frees the drives, deletes the ports,
- * buffers, closes the window, and libraries. Then it exits.
- */
- cleanup() {
- register int i ;
-
- for (i=0; i<4; i++)
- FreeDisk(i) ;
- if (gpacket)
- FreeMem(gpacket, (long)sizeof(struct StandardPacket)) ;
- if (port)
- DeletePort(port) ;
- freebuffers() ;
- if (window)
- CloseWindow(window) ;
- if (GfxBase)
- CloseLibrary(GfxBase) ;
- if (IntuitionBase)
- CloseLibrary(IntuitionBase) ;
- exit(0) ;
- }
- /*
- * This routine attempts to allocate m buffers. We try and leave at
- * least 32K of memory, even with the allocations. We set hibuf at the
- * exit point.
- */
- getbuffers(m)
- int m ;
- {
- register int i ;
-
- if (buffers[80]==NULL &&
- (buffers[80]=(char *)
- AllocMem((long)TRACKSIZE, MEMF_CHIP | MEMF_PUBLIC))==NULL)
- error(nobuf) ;
- hibuf = 0 ;
- for (i=0; i<m; i++)
- if (buffers[i]==NULL &&
- (AvailMem(MEMF_PUBLIC) < 32000 ||
- (buffers[i]=(char *)AllocMem((long)TRACKSIZE, MEMF_PUBLIC))==NULL))
- break ;
- else
- hibuf = i+1 ;
- if (hibuf < 1)
- error(nobuf) ;
- for (; i<80; i++)
- if (buffers[i] != NULL) {
- FreeMem(buffers[i], (long)TRACKSIZE) ;
- buffers[i] = NULL ;
- }
- }
- /*
- * And this routine lets them all go.
- */
- int freebuffers() {
- register int i ;
-
- for (i=0; i<81; i++)
- if (buffers[i] != NULL) {
- FreeMem(buffers[i], (long)TRACKSIZE) ;
- buffers[i] = NULL ;
- }
- }
- /*
- * We attempt to create an I/O request. We allocate memory for it, and
- * then initialize some of the ports.
- */
- struct IORequest *CreatExtIO() {
- register struct IORequest *ioReq ;
-
- ioReq = (struct IORequest *)AllocMem((long)sizeof(struct IOExtTD),
- MEMF_CLEAR | MEMF_PUBLIC) ;
- if (ioReq == NULL)
- return (NULL) ;
- ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE ;
- ioReq->io_Message.mn_Node.ln_Pri = 0 ;
- ioReq->io_Message.mn_ReplyPort = port ;
- return(ioReq) ;
- }
- /*
- * This routine frees an I/O request.
- */
- DeleteIO(ioExt)
- struct IORequest *ioExt ;
- {
- FreeMem(ioExt, (long)sizeof(struct IOExtTD)) ;
- }
- /*
- * Now we try to allocate a disk. First, we attempt to allocate an
- * I/O request, and then we actually open the device. If either of
- * these fail, we return 0. This can occur if a drive is opened that
- * doesn't exist. After we have the disk, we inhibit AmigaDOS from
- * putzing with it.
- */
- int OpenDisk(i)
- register int i ;
- {
- register struct IOExtTD **p = diskreq+i ;
-
- if (*p)
- return(1) ;
- if ((*p = (struct IOExtTD *)CreatExtIO()) &&
- OpenDevice(TD_NAME, (long)i, *p, 0L) == 0) {
- inhibit(i, TRUE) ;
- return(1) ;
- } else {
- if (*p) {
- DeleteIO(*p) ;
- *p = NULL ;
- }
- return(0) ;
- }
- }
- /*
- * Here we release a disk. We check that we have it first! Then, we
- * close the device, uninhibit the drive, kill the I/O request, and
- * exit.
- */
- FreeDisk(i)
- register int i ;
- {
- register struct IOExtTD **p = diskreq+i ;
-
- if (*p) {
- CloseDevice(*p) ;
- inhibit(i, FALSE) ;
- DeleteIO(*p) ;
- *p = NULL ;
- }
- }
- /*
- * This routine attempts to allocate all of the disks we need, based on the
- * current settings of source and dest. If a disk cannot be allocated, it
- * is removed from both source or dest. Source might be set to point to df0,
- * if this occurs. Then, if we can't allocate df0, we exit fatally. This
- * occurance can actually happen, if some other program has df0:. (I think.)
- */
- allocdisks() {
- register int i, need ;
-
- top:
- need = dest ;
- if (source != -1)
- need |= 1 << source ;
- for (i=0; i<4; i++, need >>= 1) {
- if (need & 1) {
- if (! OpenDisk(i)) {
- dest &= ~(1 << i) ;
- if (source == i) {
- if (i == 0)
- error("! I couldn't allocate the internal drive") ;
- source = 0 ;
- goto top ;
- }
- }
- } else
- FreeDisk(i) ;
- }
- }
- /*
- * We call this routine if we are going to be accessing this
- * disk. It gets the changecount, so our read/write routines work.
- */
- int InitDisk(d)
- register int d ;
- {
- register struct IOExtTD *p = diskreq[d] ;
- int result ;
-
- p->iotd_Req.io_Command = TD_CHANGENUM ;
- DoIO(p) ;
- result = (diskChangeCount[d] != p->iotd_Req.io_Actual) ;
- diskChangeCount[d] = p->iotd_Req.io_Actual ;
- return(result) ;
- }
- /*
- * The plural of the above routine takes a mask and initializes all of
- * the drives. It adds the source drive to the list automatically.
- */
- initdisks(mask)
- int mask ;
- {
- int d ;
-
- if (source != -1)
- mask |= (1 << source) ;
- for (d=0; d<4; d++)
- if (mask & (1 << d))
- InitDisk(d) ;
- }
- /*
- * Kill the motor of a drive. So someone can stick disks in and out.
- */
- motoroff(d)
- register int d ;
- {
- register struct IOExtTD *p = diskreq[d] ;
-
- p->iotd_Req.io_Length = 0 ;
- p->iotd_Req.io_Command = TD_MOTOR ;
- DoIO(p) ;
- }
- /*
- * This routine is a fast machine-language block move, that moves
- * exactly one block of data. Do not change TRACKSIZE and expect
- * this still to work!
- */
- fcpy(dest, src)
- long *dest, *src ;
- {
- #asm
- movem.l a0-a6/d0-d7,-(a7)
- move.l 12(a5),a0
- move.l 8(a5),a1
- move.l #43,d0
- lsdf:
- movem.l (a0)+,a2-a6/d1-d7
- movem.l a2-a6/d1-d7,(a1)
- add.w #48,a1
- movem.l (a0)+,a2-a6/d1-d7
- movem.l a2-a6/d1-d7,(a1)
- add.w #48,a1
- movem.l (a0)+,a2-a6/d1-d7
- movem.l a2-a6/d1-d7,(a1)
- add.w #48,a1
- movem.l (a0)+,a2-a6/d1-d7
- movem.l a2-a6/d1-d7,(a1)
- add.w #48,a1
- movem.l (a0)+,a2-a6/d1-d7
- movem.l a2-a6/d1-d7,(a1)
- add.w #48,a1
- movem.l (a0)+,a2-a5
- movem.l a2-a5,(a1)
- add.w #16,a1
- dbra d0,lsdf
- movem.l (a7)+,a0-a6/d0-d7
- #endasm
- }
- /*
- * Another fast assembly language routine for verifying a buffer. This
- * routine returns 0 if the two buffers are the same, and something else
- * otherwise.
- */
- int fcmp(dest, src)
- long *dest, *src ;
- {
- register int foo = 0 ;
- #asm
- movem.l d0/a0/a1,-(a7)
- move.l 12(a5),a0
- move.l 8(a5),a1
- move.l #2815,d0
- alsdf:
- cmp.l (a0)+,(a1)+
- dbne d0,alsdf
- move.w d0,d4
- addq.w #1,d4
- movem.l (a7)+,a0/a1/d0
- #endasm
- return(foo) ;
- }
- /*
- * This routine turns on a particular gadget.
- */
- turnon(g)
- register struct Gadget *g ;
- {
- if (g->Flags & GADGDISABLED) {
- SetAPen(window->RPort, 0L) ;
- RectFill(window->RPort, (long)g->LeftEdge, (long)g->TopEdge,
- (long)g->LeftEdge+g->Width-1,
- (long)g->TopEdge+g->Height-1) ;
- SetAPen(window->RPort, 1L) ;
- OnGadget(g, window, NULL) ;
- }
- }
- /*
- * This routine turns off a particular gadget.
- */
- turnoff(g)
- register struct Gadget *g ;
- {
- if (!(g->Flags & GADGDISABLED))
- OffGadget(g, window, NULL) ;
- }
- /*
- * Our main copy routine. The variable j holds the current destinations
- * that are being written into; as disks drop like flies, j will drop
- * them; at the end, we print a message about all the disks that dropped
- * out. We start by initializing the disks.
- */
- goforit(again)
- int again ;
- {
- register int i, j, k, t ;
- register int ohbuf ;
-
- turnoff(&gogadg) ;
- turnoff(&verifygadg) ;
- turnoff(&buffergadg) ;
- turnoff(&againgadg) ;
- turnon(&retrygadg) ;
- turnoff(&namegadg) ;
- RefreshGadgets(window->FirstGadget, window, NULL) ;
- j = dest ;
- initdisks(j) ;
- ohbuf = hibuf ;
- /*
- * If we are formatting, we only use one buffer. This avoids the
- * unsightly delay which happens if we build up the formatted disk in
- * memory first; the user wonders what the hell is going on. We first
- * check that the user isn't trying to read and write from the same disk;
- * if he is, he will have to swap about 160 times, so we tell him no go.
- */
- if (source == -1)
- ohbuf = 1 ;
- if (ohbuf == 1 && source >= 0 && (dest & (1 << source))) {
- while (error("No buffering?") == 'R') ;
- goto finishup ;
- }
- for (t=0; t<80; t+=ohbuf) {
- if (! again) {
- for (k=0; k<ohbuf && t+k<80; k++) {
- if (disposemsgs()=='Q')
- goto aborted ;
- if (getdata(source, t+k, k)==0)
- goto finishup ;
- }
- if (source != -1) {
- if (dest & (1 << source)) {
- for (i=0; i<4; i++)
- if (j & (1 << i))
- motoroff(i) ;
- do {
- if (error("Enter destination disks")=='Q')
- goto finishup ;
- } while (! InitDisk(source)) ;
- initdisks(j) ;
- }
- }
- if (ohbuf == 80)
- havedisk = 1 ;
- }
- for (k=0; k<ohbuf && t+k<80; k++) {
- if (disposemsgs()=='Q')
- goto aborted ;
- for (i=0; i<4; i++) {
- if (j & (1 << i)) {
- if (writedata(i, t+k, k)==0) {
- j &= ~(1 << i) ;
- motoroff(i) ;
- }
- }
- }
- }
- if (! again) {
- if (source != -1 && (dest & (1 << source)) && t+k < 80) {
- motoroff(source) ;
- do {
- if (error("Enter source disk")=='Q')
- goto finishup ;
- } while (! InitDisk(source)) ;
- }
- }
- }
- goto finishup ;
- /*
- * On exit, we turn off all the motors, clear out the middle line, and
- * return.
- */
- aborted:
- j = 0 ;
- finishup:
- for (i=0; i<4; i++)
- if (diskreq[i])
- motoroff(i) ;
- for (i=0; i<4; i++)
- if ((dest-j) & (1<<i)) {
- msg[7] = i + '0' ;
- error(msg) ;
- }
- draw(blank, COL3, LINE3+3) ;
- disposemsgs() ;
- }
- /*
- * This routine writes a given message to the screen; either reading,
- * writing, or ver'ing. It fills in the track and disk number. It
- * checks first that the source isn't `format', which is created behind
- * the scenes instead of being read from an actual disk. If we are
- * reading from track 40, we get the name of the disk and put it up on
- * the screen.
- */
- writeop(s, t, d)
- register char *s ;
- register int t, d ;
- {
- if (d != -1) {
- s[14] = '0' + t / 10 ;
- s[15] = '0' + t % 10 ;
- s[19] = '0' + d ;
- draw(s, COL3, LINE3+3) ;
- }
- }
- /*
- * This routine gets data from disk d, track t, into buffer b.
- * If the disk is -1, it gets it from the format routine (later.)
- * We return 1 if success; 0 if failure. If there is an error, we allow
- * the user to retry as many times as he likes.
- */
- int getdata(d,t,b)
- register int d, t, b ;
- {
- register struct IOExtTD *p = diskreq[d] ;
- register int i = 0 ;
-
- if (d==-1) {
- makeformatdata(t, b) ;
- return(1) ;
- }
- do {
- writeop(reading, t, d) ;
- p->iotd_Req.io_Length = TRACKSIZE ;
- p->iotd_Req.io_Data = (APTR)buffers[80] ;
- p->iotd_Req.io_Command = ETD_READ ;
- p->iotd_Count = diskChangeCount[d] ;
- p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
- DoIO(p) ;
- fcpy(buffers[b], buffers[80]) ;
- } while (p->iotd_Req.io_Error != 0 &&
- ((i=error("Read Error; Quit/Retry?"))!='Q')) ;
- if (t==40 && i != 'Q')
- writename(b) ;
- return (i != 'Q') ;
- }
- /*
- * writedata is analagous to the above routine. However, if verify is
- * turned on, then we read the data back in from the disk and make sure
- * that it is correct. Note that whenever we write track 40, we first
- * update the root block creation date and last modified date.
- */
- int writedata(d,t,b)
- register int d, t, b ;
- {
- register struct IOExtTD *p = diskreq[d] ;
- register int i = 0 ;
-
- top:
- if (t==40)
- updaterootblock(b) ;
- do {
- writeop(writing, t, d) ;
- fcpy(buffers[80], buffers[b]) ;
- p->iotd_Req.io_Length = TRACKSIZE ;
- p->iotd_Req.io_Data = (APTR)buffers[80] ;
- p->iotd_Req.io_Command = TD_FORMAT ;
- p->iotd_Count = diskChangeCount[d] ;
- p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
- DoIO(p) ;
- } while (p->iotd_Req.io_Error != 0 &&
- ((i=error("Write Error; Quit/Retry?"))!='Q')) ;
- if (i=='Q')
- return(0) ;
- if (verify) {
- writeop(veriing, t, d) ;
- p->iotd_Req.io_Length = TRACKSIZE ;
- p->iotd_Req.io_Data = (APTR)buffers[80] ;
- p->iotd_Req.io_Command = ETD_READ ;
- p->iotd_Count = diskChangeCount[d] ;
- p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
- DoIO(p) ;
- if ((p->iotd_Req.io_Error != 0 ||
- fcmp(buffers[80], buffers[b])) &&
- ((i=error("Verify Error; Quit/Retry?"))!='Q')) {
- if (t==0)
- InitDisk(d) ;
- goto top ;
- }
- }
- return (i != 'Q') ;
- }
- /*
- * This routine creates data for the format option. Note the clever
- * way the data is built up; this routine should build a disk exactly
- * the same way the standard AmigaDOS format does. If we are on track
- * 40, some additional work must be done to create a root block and
- * bitmap, but it's not too bad.
- */
- makeformatdata(t, b)
- int t, b ;
- {
- register long *p ;
- register long cs ;
- register long i ;
- unsigned char *q ;
-
- p = (long *)buffers[b] ;
- cs = 'DOS\0' + (((long)t & 48) << 16) ;
- for (i=0; i<TRACKSIZE/4; i++)
- *p++ = cs + (i & 3327) ;
- if (t != 40)
- return ;
- p = (long *)buffers[b] ;
- for (i=0; i<256; i++)
- *p++ = 0 ;
- p = (long *)buffers[b] ;
- p[0] = 2 ;
- p[3] = 0x48 ;
- p[78] = 1 ;
- p[79] = 0x371 ;
- q = (unsigned char *)(p + 108) ;
- *q++ = strlen(namebuf) ;
- strcpy(q, namebuf) ;
- p[127] = 1 ;
- p += 128 ;
- for (i=1; i<55; i++)
- p[i] = 0xffffffff ;
- p[0] = 0xc000c037 ;
- p[28] = 0xffff3fff ;
- p[55] = 0x3fffffff ;
- }
- /*
- * This routine recalculates the checksum for a block, and updates it in
- * word 5. The sum of all the words in a block must be 0.
- */
- recheck(w)
- register long *w ;
- {
- register int i ;
- register long cs ;
-
- cs = 0 ;
- for (i=0; i<128; i++)
- cs += w[i] ;
- w[5] -= cs ;
- }
- /*
- * We simply DateStamp the creation date, modification date, and
- * rechecksum the block.
- */
- updaterootblock(b)
- register int b ;
- {
- DateStamp(buffers[b] + 420) ;
- DateStamp(buffers[b] + 484) ;
- recheck(buffers[b]) ;
- }
- /*
- * If we read from track 40, we write the name on the screen in
- * the string gadget supplied for that purpose.
- */
- writename(b)
- int b ;
- {
- int i = buffers[b][432] ;
- int j ;
-
- RemoveGadget(window, &namegadg) ;
- if (i > 31)
- i = 31 ;
- for (j=0; j<i; j++)
- namebuf[j] = buffers[b][j + 433] ;
- namebuf[j] = 0 ;
- AddGadget(window, &namegadg, -1L) ;
- RefreshGadgets(&namegadg, window, NULL) ;
- }
- /*
- * And finally, our main routine! This thing is awfully long; they do
- * get that way sometimes, don't they?
- */
- main(argc, argv)
- int argc ;
- char *argv[] ;
- {
- int op ;
- register char *p, *q ;
- int seen = 0 ;
-
- output = (long)Output() ;
- /*
- * First, we parse the arguments. Arguments allowed are documented at
- * the top of this file. If there is a dash as the first character of
- * an argument, we ignore it, thus allowing Unix-style options.
- */
- while (argc > 1) {
- argc-- ;
- argv++ ;
- p = *argv ;
- if (argc > 1)
- q = argv[1] ;
- else
- q = "" ;
- if (*p == '-')
- p++ ;
- switch (upcase(*p)) {
- /*
- * If the argument starts with a D, or a number, it specifies a drive.
- * Actually, it might be a `DRIVE' keyword, which would return a mask of
- * 0, so we check the return value for 0.
- */
- case '0' : case '1' : case '2' : case '3' : case 'D' :
- dest = getmask(p) ;
- if (dest != 0) {
- if (! seen)
- source = dest ;
- seen = 1 ;
- }
- break ;
- /*
- * The from keyword sets the source . . .
- */
- case 'F' :
- argc-- ;
- argv++ ;
- source = getmask(q) ;
- seen = 1 ;
- break ;
- /*
- * The to keyword sets the destination . . .
- */
- case 'T' :
- argc-- ;
- argv++ ;
- dest = getmask(q) ;
- break ;
- /*
- * This could either be a `nobuffer', `noverify', or `name' keyword.
- * We check for any of these.
- */
- case 'N' :
- if (p[1]=='o' || p[1]=='O') {
- if (p[2]=='v' || p[2]=='V')
- verify = 0 ;
- else if (p[2]=='b' || p[2]=='B')
- buffer = 0 ;
- else goto errorarg ;
- } else {
- if (strlen(q) > 30)
- q[30] = 0 ;
- strcpy(namebuf, q) ;
- source = 0 ;
- argc-- ;
- argv++ ;
- }
- break ;
- /*
- * Verify keyword is easy
- */
- case 'V' :
- verify = 1 ;
- break ;
- /*
- * As is the buffer keyword.
- */
- case 'B' :
- buffer = 1 ;
- break ;
- /*
- * We go ahead and print an error message if we didn't understand an
- * option.
- */
- default:
- errorarg:
- if (output) {
- Write(output, "Unknown option ", 15L) ;
- Write(output, p, (long)strlen(p)) ;
- Write(output, "\n", 1L) ;
- }
- }
- }
- /*
- * Up to this point, the source has been a mask. Now we turn it into an
- * integer.
- */
- if (source & 8)
- source = 3 ;
- else if (source & 4)
- source = 2 ;
- else if (source & 2)
- source = 1 ;
- else if (source & 1)
- source = 0 ;
- else
- source = -1 ;
- /*
- * We initialize a few gadgets based on the parameters the user selected.
- */
- if (verify) {
- verifygadg.GadgetText = &verifyontext ;
- } else {
- verifygadg.GadgetText = &verifyofftext ;
- }
- if (buffer) {
- buffergadg.GadgetText = &bufferontext ;
- hibuf = 80 ;
- } else {
- buffergadg.GadgetText = &bufferofftext ;
- hibuf = 1 ;
- }
- /*
- * And now we try and open things up! First intuition, then graphics,
- * then our window and an I/O port. If any of these fail, we simply
- * exit.
- */
- if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
- "intuition.library",33L))!=NULL &&
- (GfxBase = (struct GfxBase *)OpenLibrary(
- "graphics.library",0L))!=NULL &&
- (window=OpenWindow(&newwindow))!=NULL &&
- (port=CreatePort(0L, 0L)) &&
- (gpacket=(struct StandardPacket *)AllocMem(
- (long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))) {
- /*
- * We draw the lines between what the user has selected, and enter into
- * our main command loop. Then, we wait for a message. After getting one,
- * we drop into a case statement keying off what message it was.
- */
- redrawlines() ;
- getbuffers(hibuf) ;
- while (1) {
- if (havedisk)
- turnon(&againgadg) ;
- else
- turnoff(&againgadg) ;
- turnoff(&retrygadg) ;
- turnon(&gogadg) ;
- turnon(&verifygadg) ;
- turnon(&buffergadg) ;
- if (source == -1)
- turnon(&namegadg) ;
- else
- turnoff(&namegadg) ;
- RefreshGadgets(window->FirstGadget, window, NULL) ;
- while ((message = (struct IntuiMessage *)
- GetMsg(window->UserPort))==NULL)
- WaitPort(window->UserPort) ;
- op = getop() ;
- switch(op) {
- /*
- * A shift-0, shift-1, shift-2, or shift-3 toggles the appropriate bit
- * in the destination, and redraws the lines. A 0, 1, 2, or 3 sets the
- * source to that drive, and continues.
- */
- case ')' :
- dest ^= 1 ;
- goto redrawem ;
- case '!' :
- dest ^= 2 ;
- goto redrawem ;
- case '@' :
- dest ^= 4 ;
- goto redrawem ;
- case '#' :
- dest ^= 8 ;
- goto redrawem ;
- case '0' : case '1' : case '2' : case '3' :
- source = op - '0' ;
- redrawem :
- redrawlines() ;
- break ;
- /*
- * If verify is selected, we toggle the state of the verify flag, and
- * update the gadget to reflect this state.
- */
- case 'V' :
- verify = ! verify ;
- RemoveGadget(window, &verifygadg) ;
- if (verify) {
- verifygadg.GadgetText = &verifyontext ;
- } else {
- verifygadg.GadgetText = &verifyofftext ;
- }
- AddGadget(window, &verifygadg, -1L) ;
- RefreshGadgets(&verifygadg, window, NULL) ;
- break ;
- /*
- * The buffer option does essentially the same thing, only for buffering.
- */
- case 'B' :
- buffer = ! buffer ;
- RemoveGadget(window, &buffergadg) ;
- if (buffer) {
- buffergadg.GadgetText = &bufferontext ;
- getbuffers(80) ;
- } else {
- buffergadg.GadgetText = &bufferofftext ;
- getbuffers(1) ;
- }
- havedisk = 0 ;
- AddGadget(window, &buffergadg, -1L) ;
- RefreshGadgets(&buffergadg, window, NULL) ;
- break ;
- /*
- * If the user selects `format', then we set the source appropriately and
- * redraw the lines. Of course, we no longer have a disk, as when the
- * format is executed, it will destroy buffer 0.
- */
- case 'F' :
- source = -1 ;
- havedisk = 0 ;
- goto redrawem ;
- /*
- * The name gadget, if invoked from the keyboard with the `n' key,
- * Activates the string gadget.
- */
- case 'N' :
- if (gadad == NULL && source == -1) {
- ActivateGadget(&namegadg, window, NULL) ;
- }
- break ;
- /*
- * The `G' gadget, or carriage return, starts us on our merry way.
- */
- case 'G' : case 10 : case 32 : case 13 :
- goforit(0) ;
- break ;
- /*
- * The `A' gadget insures that we have a disk first, otherwise it
- * complains.
- */
- case 'A' :
- if (! havedisk) {
- while (error("No disk in memory")=='R') ;
- } else {
- goforit(1) ;
- }
- break ;
- /*
- * Retry is ignored in the main command loop, as there is nothing to
- * retry!
- */
- case 'R' :
- /*
- * Quit is handled at the bottom of the loop.
- */
- case 'Q' :
- /*
- * As a default, we do nothing; not even an error message.
- */
- default :
- break ;
- }
- /*
- * If the last gadget selected was 'Q', we exit.
- */
- if (op == 'Q')
- break ;
- }
- } else {
- error("! couldn't open window") ;
- }
- /*
- * Release memory and exit.
- */
- cleanup() ;
- }
-