[comp.sources.atari.st] v01i035: flip -- disk copier, part02/02

koreth@ssyx.ucsc.edu (Steven Grimm) (05/25/88)

Submitted-by: uunet!mcvax!mirsa.inria.fr!colas (Colas Nahaboo)
Posting-number: Volume 1, Issue 35
Archive-name: flip/part02

#--------------------------Cut Here--------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before the "#! /bin/sh" line,
# then unpack it by saving it in a file and typing "sh file."
#
# Wrapped by Steven Grimm (koreth) at ssyx on Tue May 24 15:54:23 1988
#
# unpacks with default permissions
#
# Contents : MAKEFILE MAN.C README TWST.C
#
if `test ! -s MAKEFILE`
then
echo "x - MAKEFILE"
cat > MAKEFILE << '@\Rogue\Monster\'
VERSION=26b

flip_$(VERSION).tos:	flip.o twst.o man.o
	mmlink -o flip_$(VERSION).tos flip.o twst.o man.o

imp:	flip.o twst.o man.o
	mmimp flip.o
	mmimp twst.o
	mmimp man.o
	mmlink -o flip_$(VERSION).tos flip.o twst.o man.o

@\Rogue\Monster\
else
  echo "shar: Will not over write MAKEFILE"
fi
if `test ! -s MAN.C`
then
echo "x - MAN.C"
cat > MAN.C << '@\Rogue\Monster\'
/* FLIP HISTORY & MANUAL */

/* HISTORY:
 *=========
 *   2.6b tells when copies an executable boot to detect virii
 *   2.6a bug de 'r' repaired
 *        No checksum done if no previous checksum
 *        option 's' for single instead of double (double=default)
 * v 2.6  27 fevrier 88
 *   2.5c re-designing of command parsing
 *	  formats disk ('f' command)
 *	  randomization of serial # at each copy
 *   2.5b t can override disk
 *	  ESC aborts without confirm
 *   2.5a no ESC to another disk, BS!
 *	  aborts on key even in format
 *	  man in another C file
 * v 2.5  released
 *  2.4.5 write-protect detected
 *  2.4.3 option v to verify source only
 *	  multipass copies possible
 *  2.4.2 all sects# outputted on any kind of error.
 *        twister modified to accept starttrack parameter
 *	  track_map to know what to copy
 *	  t option to specify track
 *	  r takes options fvV
 *  2.4.1 i for interactive: default no stop
 *	  V verify after format (case-sensitive options)
 *	  bug of verify reading into main buffer repaired
 * v 2.4: Jan 15 88
 *  2.3.2 If esc on insert abort
 *	  track_read first
 *	  no more skip opt
 * 	  # of sect printed
 *  2.3.1 Drive B presence is really tested
 * v 2.3: Dec 28 87
 *  2.2.3 comparisons were not done!!! -- repaired
 * 	  copy by 3 sectors
 *        stops if copy sector only by 1
 *  2.2.2 bug de a1a1 repare
 *  2.2.1 no verify by default
 *  	  r for re-copy
 * v 2.2: Dec 19 87
 *  2.1.1 prints buffer size on start
 *	  sector_read done 4 times with comparison!
 *	  RETURN repeats last command
 * v 2.1: Nov 28 87
 *  2.0.1 retrys in writes and verify work now.
 *	  verify there is a drive B before copying
 * v 2.0: Nov 22 87
 *  1.6.3 verify is another pass
 *  1.6.2 format does not verify anymore
 *	  format outputs a char
 *  1.6.1 uses TWISTER's code mmtwst.c to format
 * v 1.6: Nov 11 87
 *  1.5.4 abort on ESC
 *        option "2" for 2-sided.
 *  1.5.3 abort on key with confirm
 *  1.5.2 track, sectors begin at 1 instead of 0
 *        retrys on writes retry format-write-verify loop
 *	  on write errors, ESC to go to another disk
 *  1.5.1 port to megamax
 *	  uses all memory
 * v 1.5: more checking of entry syntax.
 *        on-line help
 *        fills bad sectors with 0s before writing
 *	  more sensible memory allocation
 *	  s option for auto skip
 * v 1.4: one automatic retry without prompting. If skips, copies good
 * 	  sectors anyway.
 * v 1.3: when error on a track, asks for retrys, which are done sector 
 *        by sector.
 * v 1.2: when double sided, copy the two sides
 * v 1.1: whole side in memory for one drive use.
 * v 1.0: original one in mark williams. quick and really dirty.
 */

/* ON_LINE_MANUAL:
 *================
 */

char           *help_text[] = {
"",
"				FLIP 2.6 manual",
"				 Colas Nahaboo",
"",
"    flip - disk copier with error-recovery, on either sides.",
"           with FAST formatting as TWISTER or DCFORMAT",
"",
"SYNTAX",
"  the commands are given as: (items in [] are optional)",
"",
"      <source-drive>[<dest-drive>][fvVtios9]                  : copy disks",
"      f[<drive>][fvVtis9]                                     : format disk",
"      v[<drive>][tios9]                                       : verify disk",
"      r[<drive>][fvVi]                            : re-write disk in memory",
"      q                                                       : quits",
"      ?                                                       : help",
"",
"  A drive is specified by the letter \"a\" or \"b\", followed optionally by",
"  \"1\" or \"2\" to indicate the side. (default drive is \"a\", side is \"1\")",
"",
"OPTIONS",
"     f - to format destination side",
"     V - verify disk by re-reading track just after being formatted.",
"     v - verify copy by re-reading track just after being written",
"     t - to specify what track(s) to copy (used with \"z\" to give the",
"           number of tracks of the new disk, default 80)",
"     i - interactive: ask user in case of error (default don't)",
"     o - override: do not look at the source disk boot sector to determine",
"           its type, take 10 sectors, 1 sided, 80 tracks, unless told",
"           otherwise by options \"9\", \"d\" or \"t\"",
"     9 - disk is 9 sectors per track (default 10). Used with \"z\" or \"o\"",
"     s - disk is single-sided (default double). Used with \"z\" or \"o\"",
"",
"EXAMPLES",
"  ab2fv - to copy a non-protected SS disk in drive A on the unformatted",
"          back of a SS disk in drive B, verifying the copy.",
"  afv   - (or fv) to copy ANY disk with format and verify, in drive A.",
"  rfv   - to re-write last copy into drive A",
"  ffv   - to format a double-sided disk in drive A",
"  fs    - to erase all files of a previously formatted single sided disk",
"",
"OPERATION",
"  During copy, flip prints for each track, a:",
"  ,    for reading track without problem",
"  ;    for reading track successfully after 4 retrys sector-by-sector",
"  -    for formatting a track (in FAST format)",
"  v    for verifying a track",
"",
"  You can pause during the copy at any time by pressing a key, but pressing",
"  ESC will abort. ^C always quits.",
"",
"  Without i option, on read error, it retries 4 times to read the track,",
"  then reads sector by sector. With option i, it prompts you for what to do:",
"  (the track and sector number given start at 1, but sector number at 0)",
"    - RETURN or SPACE retries. You'd better try to extract, then re-insert",
"      the disk to try to re-center it. (or try on another drive later)",
"    - \"s\" skips the bad sectors, going on with the rest of the copy.",
"      the bad sectors will be filled with 0s on the copy",
"      (but all that could be read is kept, of course!)",
"    - if it is writing the copy, you can press BACKSPACE if you want to try",
"      to write to another disk.",
"    - any other key aborts the copy.",
"  Experience shows that you can recover most of your damaged disks this way.",
"  If it cannot read the track, it reads it sector by sector, but tells you so",
"  because due to a bug in the TOS, a sector can be read incorrectly without",
"  giving an error. So FLIP does 4 reads of the same sector and compares",
"  them to be sure, but even if reads are equals, it warns you because the",
"  risk subsists, due to the bug in old TOS roms...",
"",
"DESCRIPTION",
"  The program copies TOS disks as a whole, and only non-protected",
"  ones. It can consider the back side of a single-sided disk as another disk",
"  so, it is very useful to use the back sides of protected disks, or",
"  single sided disks, such as MAGIC! ones. (with a two-side drive, of",
"  course!)",
"",
"  But it also reads disks with care, being able to read CORRECTLY most disks",
"  causing a read error to copiers or the GEM desktop. Thus, if you get a",
"  read error on a disk, copy it with FLIP!. Moreover, if it cannot read a",
"  sector, it tells you so! (Unlike the HELP program)",
"",
"  Version 2.6 can format and changes randomly the serial # of the disk",
"  when copying, (without the override option). It copies also executable",
"  boot sectors, but tells you so (in 2.6b) so that you can check for a VIRUS.",
"  it gives you the values at $3A offset to identify the virus",
"  Known virus values are: 41,FA,FF                                         ",
"                                                                           ",
"  When it reads a disk, it first tries to read the boot sector in order",
"  to know the size (tracks and sectors per track) of the disk. If it",
"  can't make sense out of it, it uses 80 tracks by 9 sector, unless you",
"  used the \"o\" option",
"  Thus you don't have to tell him the type of the disk you want to copy.",
"  It will recognize double sided disks automatically, but you can only",
"  copy double sided disks, without flipping their faces!",
"",
"  So, NEVER COPY A DISK WICH GIVES READ ERRORS WITH A BIT-COPIER (procopy)",
"  use FLIP instead!",
"",
"  From version 2.0 on, FLIP format in FAST format, all disks accesses on",
"  this disk will be twice faster compared to a normal disk!. It ALWAYS ",
"  formats disks in 10 sectors, even if only to store 9 sectors.",
"  Be aware that I didn't test the validy of the formatting code on the",
"  new roms, so make some tests if you have them.",
"",
"COPYRIGHT",
"  This program is PUBLIC DOMAIN, and should be given with its sources",
"  Feel free to improve it!, but please mention my name.",
"  I would be very glad if you send me back any improved version, such as",
"  a GEM port",
"  CREDIT: It uses code from TWISTER from Dave SMALL & Dan MOORE.",
"",
"BUGS",
"  Not under GEM - yet!",
"  Cannot copy IBM boot sectors (use DCFORMAT)",
"",
"AUTHOR",
"  Colas NAHABOO",
"  383 ch du clos d'Embertrand",
"  06250 MOUGINS",
"  FRANCE.",
"  Personal phone: 93 75 68 29, give me a call...",
"  Electronic mail address:    colas@mirsa.inria.fr",
0L
};

#include "osbind.h"
#include "stdio.h"

more(text)
char          **text;
{
    char          **p = text, c;
    int             line = 0;

    while (*p) {
	printf("%s\n", *p++);
	line++;
	if (line > 22) {
	    printf("%cp more (SP, CR, BS or ESC) %cq", 27, 27);
	    fflush(stdout);
	    while (!Cconis());
	    c = Cnecin();
	    printf("%cM", 27);
	    fflush(stdout);
	    switch (c) {
	    case 27:
	    case 'q':
		printf("\n");
		return;
		break;
	    case ' ':
		line = 0;
		break;
	    case 8:
		line = 0;
		p -= 48;
		if (p < text)
		    p = text;
		printf("%cp...Backing 1 page...%cq\n", 27, 27);
		break;
	    }
	}
    }
    printf("\n");
}

@\Rogue\Monster\
else
  echo "shar: Will not over write MAN.C"
fi
if `test ! -s README`
then
echo "x - README"
cat > README << '@\Rogue\Monster\'
Flip is a small copier that I wrote for my personal use to:

- use the back of SS disks

- try to save data from damaged disks (getting frustrated with the abort
  message of DCFORMAT)

Since I and my friends are using it all the time, it might be of use to some
netlanders. As it was developed for my own use, it is not under GEM.
It is in no way as great a program as is GULAM or UNITERM

It is completly public domain, files are: (MEGAMAX C v1.1)

binaries:
	flip_26b.tos			the executable

sources:
	makefile
	flip.c		main code
	man.c		manual & history
	twst.c		formatter in in-line assembly, in fact TWISTER code
			slightly modified (MEGAMAX v1.1)
@\Rogue\Monster\
else
  echo "shar: Will not over write README"
fi
if `test ! -s TWST.C`
then
echo "x - TWST.C"
cat > TWST.C << '@\Rogue\Monster\'
/*
 *		Megamax C inline ASM version of Twister.
 *		Converted from AS68 format  dlm  12/14/86.
 *		AS68 version by David Small before that date.
 *		Used for the MegAMin HD backup program.
 *
 */

/* patched by colas for use with FLIP:
 * add params:
 *	flipped: 0= no, 1=use back as front (must be SS then)
 *	endtrack: 80 for normal disks
 *	starttrack: 0 normally
 * outputs a '-' for each formatted cylinder (see  PUTCHAR comments)
 * no more verify
 */

/* routine that are callable from C are: */
extern twister();
extern confirm_abort();
extern printf();

#include "osbind.h" 
#include "stdio.h"

/* global variables */
int thedisk, dblsided, sectoroffset;   /* input parameters */
int flipped, endtrack, starttrack, keypress;     /* colas params */
long buffer;

int badflag;					    /* bad sector error flag */

/* local variables */
static int theside, thetrack, twistsectorno, sectorno, retrycnt,
		 cdev, ctrack, csect, cside, ccount, A7t, interlv,
		 virgin, def_error, curr_err;
static long cdma, edma, tmpdma, saveA2;
static int saveA0, saveA1, saveD0;
/* now the defines (AS68 equ's) */

/*	tunable values */
#define retries   2 		/* default # of retries -1 */
#define midretry  1 		/* "middle" retry, when to reseek  */
#define timeout   0x40000	/* short timeout, motor already on */
#define ltimeout  0x60000	/* long timeout, to startup motor	*/

/*	Floppy state variables in DSB: RAM usage */
#define recal	   0xff00 	/* recalibrate flag 			*/
#define dcurtrack 0 		/* current track number			*/
#define dseekrt   dcurtrack+2 /* seek rate	*/
#define dsbsiz    dseekrt+2	/* size of a DSB	*/

/* hardware equates */
/* DMA chip		*/
#define diskctl	0xffff8604	/* disk controller data access */
#define fifo		0xffff8606	/* DMA mode control / status	 */
#define dmahigh	0xffff8609	/* DMA base high			 */
#define dmamid 	0xffff860b	/* DMA base mid			 */
#define dmalow 	0xffff860d	/* DMA base low			 */

/* 1170 select values	*/
#define cmdreg 	0x80 		/* select command register	*/
#define trkreg 	0x82 		/* select track register 	*/
#define secreg 	0x84 		/* select sector register	*/
#define datareg	0x86 		/* select data register		*/

/* GI sound chip (drive a/b select lines and side select) */
#define giselect	0xffff8800	/* (W) sound chip register select */
#define giread 	0xffff8800	/* (R) sound chip read data	*/
#define giwrite	0xffff8802	/* (W) sound chip write data	*/
#define giporta	0x0e 		/* GI register # for i/o port A */

/* 68901 mfp chip */
#define mfp		0xfffffa00	/* mfp base			*/
#define gpip		mfp+1		/* general purpose i/o		*/

/* misc defines */
#define seekrate 3
#define dsb0	  0xa06
#define dsb1	  0xa0a
#define flock	  0x43e

char *abort_message = "\n\007KEY PRESSED! CR/SP = continue, other abort:\n";

asm {		/* start of inline ASM */

/*
 *	All the following is munged code from AS68 (that's why the
 *	weird format).  The AS68 code was munged from the BIOS
 *	source.  And most of this is from the FDC article in Start
 *	issue number 3.
 *
 *	Anyway, we apologize for the mess, but heh it works.
 *
 *	Oh yes.  The labels are usually on nops, not empty lines.  Thats
 *	because AS68 occasionally pukes on labels on empty lines.  And
 *	I didn't take the time to remove them.  (Megamax doesn't care.)
 *
 */

;****************************************************************
; Twister: Faster formatter (zipformat) hack.  MM version	    *
; Copyright (c) 1986, 1987 START Magazine (as usual)		    *
; Written by David Y. Small & Dan Moore 				    *
;****************************************************************
;
;			 Dedication:
;
;	 For Bill The Cat: because he's twisted, too.
;						
;****************************************************************
;
; code begins.
;
;  PASS ME THIS STUFF DAN! Then SHAVE! For God's sakes! What
; are you trying to do, look like Fidel Castro?
;
; And get SOME BEER in here okay? Sheesh!
;
; input integers (if coming from C):
;
; dblsided: 0000 or ffff
; thedisk: 0 = a, 1 = b
;*************************************************************


;********************************************************
; Frammatter		
;********************************************************
twister:	move.l	A2, saveA2(A4) 	; everything else saved by the Supexec

	   clr.w		badflag(A4)		; assume no errors
;
;*** Init vars for formatting loop
;
	move.w D0, saveD0(A4);
	move.w starttrack(A4), D0	;start at starttrack
	move.w D0, thetrack(A4)
	move.w saveD0(A4), D0	
;WAS	   move.w  #0,thetrack(A4)	 ; start at track 0

	   move.w  #1,twistsectorno(A4) ; twister value
						 
	move.w A0, saveA0(A4)
	move.w A1, saveA1(A4)
trackloop: nop 			  ; comes here for subseq tracks
	move.w saveA0(A4), A0
	move.w saveA1(A4), A1

;***** FRONT SIDE ***********
;**** Setup stack: .. based on read sec command, tweaked for frammat

	   move.w  #0xe5e5,-(A7)    ; virgin data -- e5's work okay
	   move.l  #0x87654321,-(A7) ; magic # to make format work / fmt only
	   move.w  #1,-(A7) 	  ; sector interleave factor / fmt only
	   move.w  flipped(A4),-(A7) 	  ; ** side.w (relevant) / side: FRONT
	   move.w  thetrack(A4),-(A7)  ; ** track.w (relevant) / same
	   move.w  #10,-(A7)	  ; sector.w (irrelevant) / sectors per track
	   move.w  thedisk(A4),-(A7)	 ; ** device.w  (relevant) / same
	   move.l  #0,-(A7) 	  ; dummy.l (irreleveant) / same
	   move.l  buffer(A4),-(A7)	; ** buffer address.l (relevant) / same
	   jsr	 _flopfmt
	   adda.l  #24,A7		  ; Fix stack.
	   cmpi.w  #0xffff, badflag(A4)   ; aborts on error
	   beq     gemexit
;****
; Jazz twistsectorno(A4). Look at what it ended up as; set it to
; the next track twist depending on that.
;
; It will end up as "+1" from the last sector formatted, because
; the formatter bumps it up.
;
	   cmpi.w  #1,twistsectorno(A4)	    ; 0-1 transition
	   beq	 fgoto1
	   cmpi.w  #9,twistsectorno(A4)	    ; 1-2 transition
	   beq	 fgoto2
	   cmpi.w  #7,twistsectorno(A4)	    ; 2-3 transition
	   beq	 fgoto3
	   cmpi.w  #5,twistsectorno(A4)	    ; 3-4 transition
	   beq	 fgoto4
	   cmpi.w  #3,twistsectorno(A4)	    ; 4-0 transition
	   beq	 fgoto0
	   
	   dc.b	0x4a
	   dc.b	0xfc 			 ; oops, die (illegal instruction)
;*****
; Really class coding, eh? Oh,well, it's easy and it works, which
; twelve other fancy ways of doing this *don't*.
;
fgoto1: move.w  #0x9,twistsectorno(A4)		; 9,a,1,2,3,4,5,6,7,8
	   bra	 fkeepformatting
fgoto2: move.w  #0x7,twistsectorno(A4)		; 7,8,9,a,1,2,3,4,5,6
	   bra	 fkeepformatting
fgoto3: move.w  #0x5,twistsectorno(A4)		; 5,6,7,8,9,a,1,2,3,4
	   bra	 fkeepformatting
fgoto4: move.w  #0x3,twistsectorno(A4)		; 3,4,5,6,7,8,9,a,1,2
	   bra	 fkeepformatting
fgoto0: move.w  #0x1,twistsectorno(A4)		; 1,2,3,4,5,6,7,8,9,a
	   bra	 fkeepformatting
	   nop
;*****
fkeepformatting: nop
;*****
; now, do a verify on those sectors.
;**** Setup stack: .. based on read sec command, tweaked for frammat
;
;	 tst.w	 sectoroffset(A4)	; no verify if 11 to 20 sector
;	 ; numbering
;	 bne	 nover1
;	 
;	 move.w  #0xa,-(A7)	   ; count.w 2
;	 move.w  flipped(A4),-(A7) 	  ; ** side.w (relevant) / side: FRONT 4
;	 move.w  thetrack(A4),-(A7)  ; ** track.w (relevant) / same 6
;	 move.w  #0x1,-(A7)	   ; sector.w (relevant) / starting sector 8
;	 move.w  thedisk(A4),-(A7)	 ; ** device.w  (relevant) / same 0xa
;	 move.l  #0,-(A7) 	  ; dummy.l (irreleveant) / same 0xe
;	 move.l  buffer(A4),-(A7) ; ** buffer address.l (relevant) / same 0x12
;	 
;	 move.w  #19,-(A7)	  ; do BIOS _flopver 0x14
;	 trap	 #14
;	 adda.l  #20, A7		 ; fix stack.
;	 
;	 tst.l	 D0
;	 bne	 badsecs
;	 
;****

;***** BACK SIDE ******		(How kinky Dave.  What _are_ you talking about?)

nover1:	tst.w   dblsided(A4)
		beq	   nobackside

;**** Setup stack: .. based on read sec command, tweaked for frammat

	   move.w  #0xe5e5,-(A7)    ; virgin data -- e5's work okay
; and with a virgin.  My, you really are weird Dave.
	   move.l  #0x87654321,-(A7) ; magic # to make format work / fmt only
	   move.w  #1,-(A7) 	  ; sector interleave factor / fmt only
	   move.w  #1,-(A7) 	  ; ** side.w (relevant) / side: BACK
	   move.w  thetrack(A4),-(A7)  ; ** track.w (relevant) / same
	   move.w  #10,-(A7)	  ; sector.w (irrelevant) / sectors per track
	   move.w  thedisk(A4),-(A7)	 ; ** device.w  (relevant) / same
	   move.l  #0,-(A7) 	  ; dummy.l (irreleveant) / same
	   move.l  buffer(A4),-(A7)	; ** buffer address.l (relevant) / same
	   jsr	 _flopfmt
	   adda.l  #24,A7		  ; Fix stack.
	   cmpi.w  #0xffff, badflag(A4)   ; aborts on error
	   beq     gemexit
;*****
;
; Jazz twistsectorno(A4). Look at what it ended up as; set it to
; the next track twist depending on that.
;
; It will end up as "+1" from the last sector formatted, because
; the formatter bumps it up.
;
	   cmpi.w  #1,twistsectorno(A4)	    ; 0-1 transition
	   beq	 goto1
	   cmpi.w  #9,twistsectorno(A4)	    ; 1-2 transition
	   beq	 goto2
	   cmpi.w  #7,twistsectorno(A4)	    ; 2-3 transition
	   beq	 goto3
	   cmpi.w  #5,twistsectorno(A4)	    ; 3-4 transition
	   beq	 goto4
	   cmpi.w  #3,twistsectorno(A4)	    ; 4-0 transition
	   beq	 goto0

	   dc.b	 0x4a
	   dc.b	 0xfc			  ; oops, die

;
goto1:  move.w  #0x9,twistsectorno(A4)		; 9,a,1,2,3,4,5,6,7,8
	   bra	 keepformatting
goto2:  move.w  #0x7,twistsectorno(A4)		; 7,8,9,a,1,2,3,4,5,6
	   bra	 keepformatting
goto3:  move.w  #0x5,twistsectorno(A4)		; 5,6,7,8,9,a,1,2,3,4
	   bra	 keepformatting
goto4:  move.w  #0x3,twistsectorno(A4)		; 3,4,5,6,7,8,9,a,1,2
	   bra	 keepformatting
goto0:  move.w  #0x1,twistsectorno(A4)		; 1,2,3,4,5,6,7,8,9,a
	   bra	 keepformatting
	   nop
;
keepformatting: nop 		  ; entry after twisting.
;
;	 
;	 ;**** Setup stack: .. based on read sec command, tweaked for frammat
;	 
;	 tst.w	 sectoroffset(A4) ; no verify for strange sector numbers
;	 bne	 nobackside
;	 
;	 move.w  #0xa,-(A7)	   ; count.w 2
;	 move.w  #1,-(A7) 	  ; ** side.w (relevant) / side: BACK 4
;	 move.w  thetrack(A4),-(A7)  ; ** track.w (relevant) / same 6
;	 move.w  #0x1,-(A7)	   ; sector.w (relevant) / starting sector # 8
;	 move.w  thedisk(A4),-(A7)	 ; ** device.w  (relevant) / same 0xa
;	 move.l  #0,-(A7) 	  ; dummy.l (irreleveant) / same 0xe
;	 move.l  buffer(A4),-(A7) ; ** buffer address.l (relevant) / same 0x12
;	 
;	 move.w  #19,-(A7)	  ; do BIOS _flopver 0x14 (why not)
;	 trap	 #14
;	 adda.l  #20, A7		  ; fix stack
;	 
;	 tst.l	 D0
;	 bne	 badsecs		  ; wham
;	 
;****
nobackside: nop	  ; entry if not DS  (well at least this isn't kinky Dave)

; add 1 to thetrack. If endtrack, quit.
	   addi.w   #1, thetrack(A4)
	move.w A0, saveA0(A4)
	move.w A1, saveA1(A4)

	move.w D0, saveD0(A4)		/* PUTCHAR */
	move.w #0x2D, -(A7)		/* outputs a '-' */
	move.w #6, -(A7)
	trap #1
	adda.l #4, A7
	move.w #0xB, -(A7)		/* CNONIS */
	trap #1
	adda.l #2, A7
	cmpi.w  #0,D0
	    bne key_pressed			/* aborts on keypress */
resume:	move.w saveD0(A4), D0		/* PUTCHAR end */

	move.w endtrack(A4), A0
	move.w thetrack(A4), A1
	cmpa A0, A1
	   bne	 trackloop	  ; "do another"

	move.w saveA0(A4), A0
	move.w saveA1(A4), A1
	   bra	 gemexit		  ; Continue, etc, and so forth

;*********
key_pressed:	
	MOVE.w	#8,-(A7)
	trap #1
	ADDa.L	#2,A7
	CMPI.w	#27,D0
	beq	key_abort
	move.l  abort_message(A4),-(A7)
	move.w  #9,-(A7)
	trap #1
	ADDa.L	#6,A7
	MOVE.w	#8,-(A7)
	trap	#1
	ADDa.L	#2,A7
	CMPI.w	#32,D0
	beq	resume
	CMPI.w	#10,D0
	beq	resume
	CMPI.w	#13,D0
	beq	resume
key_abort:	MOVE.w	#1,keypress(A4)
	move.w  saveD0(A4), D0
	bra gemexit

;*********
badsecs:	move.w #0xffff, badflag(A4)  ; bad sectors found

;**************
;
; End. Exit to calling routine.
;
gemexit:	movea.l saveA2(A4), A2  ; only reg not saved by Supexec
		rts

;******************************************************
;
;------------------------------------------------------------------------
;	   130-ST / 520-ST	
;	   Floppy Disk Driver	
;	   (C)1985 Atari Corp.	
;	   From the FDC article (Start issue 3); shortened to just
;	   the formatter routine and the low level I/O.		
;								
;------------------------------------------------------------------------
;
;************************************************************
;
; _flopfmt - format a track
; Passed (on the stack):
;	   0x1a(A7) initial sector data
;	   0x16(A7) magic number
;	   0x14(A7) interleave
;	   0x12(A7) side
;	   0x10(A7) track
;	    0xe(A7) A7t(A4)
;	    0xc(A7) drive
;	    0x8(A7) pointer to state block
;	    0x4(A7) dma address
;	    0x0(A7) [return]
;
; Returns:	 EQ: track successfully written.  Zero.W-terminated list of
;			 bad sectors left in buffer (they might /all/ be bad.)
;
;			 NE: could not write track (write-protected, drive 
;			 failure, or something catastrophic happened).
;-
_flopfmt:
	   cmpi.l   #0x87654321, 0x16(A7)	    ; check for magic# on stack
	   bne	 flopfail 		; no magic, so we just saved the world
;	   bsr	 change			; check for disk flip
;	   moveq	 #e_error,D0		; set default error number
	   bsr	 floplock 			; lock floppies, setup parms
	   bsr	 select				; select drive and side
	   move.w  0xe(A7),A7t(A4)		; save sectors-per-track
	   move.w  0x14(A7),interlv(A4)		; save interleave factor
	   move.w  0x1a(A7),virgin(A4)		; save initial sector data

;--- put drive into "changed" mode
;	   moveq	 #m_changed,D0 		; D0 = "CHANGED"
;	   bsr	 setdmode 			; set media change mode

;--- seek to track (hard seek):
;debug* move.l  #0xff00ff00,0xf8030
	   bsr	 hseek				; hard seek to 'ctrack'
;debug* move.l  #0x00000000,0xf8030
	   bne	 flopfail 			; (return error on seek failure)
	   move.w  ctrack(A4),dcurtrack(A1)    ; record current track#

;--- format track, then verify it:
;*	   move.w  #e_error,curr_err(A4)(A5)   ; vanilla error mode
;debug* move.l  #0xff00ff00,0xfA030
	   bsr	 fmtrack				; format track
;debug* move.l  #0x0,0xfA030

	   bne	 flopfail 			; (return error on seek failure)
	   bra	 flopok


;****************************
;+
; fmtrack - format a track. Tweaked for skewed interleave.
;
; Passed: 	 variables setup by _flopfmt
; Returns:	 NE on failure, EQ on success
; Uses:		 almost everything
; Called-by:	 _flopfmt
;
;-
fmtrack:
;*	   move.w  #e_write,def_error(A4)	; set default error number
	   move.w  #1,D3		; start with sector 1, first pass
;**
	   movea.l  cdma(A4),A2			; A2 =  prototyping area
; Lay down beginning of track

	   move.w  #60-1,D1 			; 60 x 0x4e (track leadin)
	   move.b  #0x4e,D0
	   bsr	 wmult
; Repeat 10 times: sector data.
	   move.w  #1,sectorno(A4)		    ; how many secs written
; Note that twistsectorno is initialized out of this routine.
;--- address mark
secloop: nop
;
ot1:    move.w  #12-1,D1 			; 12 x 0x00
	   clr.b	 D0
	   bsr	 wmult
	   move.w  #3-1,D1				; 3 x 0xf5
	   move.b  #0xf5,D0
	   bsr	 wmult
	   move.b  #0xfe,(A2)+			 ; 0xfe -- address mark intro
	   move.b  ctrack+1(A4),(A2)+ 	    ; track# - low half of word
	   move.b  cside+1(A4),(A2)+		    ; side# - low half of word

;* #1: just use a plain sector number
;*	   move.b  D4,(A2)+ 			; sector#

;* #2: use a twisted sector number
;*	   move.b  twistsectorno+1(A4),(A2)+   ; new: sector #.

	   move.w  twistsectorno(A4),D0	    ; fetch	 1-10 sector #
;
; Note that meg-a-minute backup uses sectors # 11-20 to force GEM
; not to use MM disks.
;
	   add.w   sectoroffset(A4),D0	; add possible shift to 11-20 sector #
	   move.b  D0,(A2)+ 			; plug it into sector table

; Add 1 to sector #. If it is b, wrap it to 1.
	   addi.w  #1,twistsectorno(A4)	    ; real sector # being plopped
	   cmpi.w  #0xb,twistsectorno(A4)
	   bne	 notb
	   move.w  #1,twistsectorno(A4)
;
notb:   nop
	   move.b  #0x02,(A2)+			 ; sector size (512)
	   move.b  #0xf7,(A2)+			 ; write checksum

;--- gap between AM and data:
	   move.w  #22-1,D1 			; 22 x 0x4e
	   move.b  #0x4e,D0
	   bsr	 wmult
	   move.w  #12-1,D1 			; 12 x 0x00
	   clr.b	 D0
	   bsr	 wmult
	   move.w  #3-1,D1				; 3 x 0xf5
	   move.b  #0xf5,D0
	   bsr	 wmult

;--- data block:
	   move.b  #0xfb,(A2)+			 ; 0xfb -- data intro
	   move.w  #256-1,D1		; 256 x virgin.w (initial sector data)
ot2:    move.b  virgin(A4),(A2)+		    ; copy high byte
	   move.b  virgin+1(A4),(A2)+ 	    ; copy low byte
	   dbf	 D1,ot2				; fill 512 bytes
	   move.b  #0xf7,(A2)+			 ; 0xf7 -- write checksum
	   move.w  #40-1,D1 			; 40 x 0x4e
	   move.b  #0x4e,D0
	   bsr	 wmult
;
; Next sector, or, end-of-track. If we hit sector 11, time to quit.
;
	   addi.w   #1,sectorno(A4)
	   cmpi.w  #11,sectorno(A4)
	   bne	 secloop		  ; loop again
;
; Okay, 10 sectors laid down. 
;
;--- end-of-track
	   move.w  #1400,D1 		; 1401 x 0x4e -- end of track trailer
	   move.b  #0x4e,D0
	   bsr	 wmult

;--- setup to write the track:
	   move.b  cdma+3(A4),dmalow	 ; load dma pointer
	   move.b  cdma+2(A4),dmamid
	   move.b  cdma+1(A4),dmahigh
	   move.w  #0x190,(A6)			 ; toggle R/W flag and
	   move.w  #0x090,(A6)			 ; select sector-count register
	   move.w  #0x190,(A6)
	   move.w  #0x1f,D7 			 ; (absurd sector count)
	   bsr	 wdiskctl
	   move.w  #0x180,(A6)			 ; select 1770 cmd register
	   move.w  #0xf0,D7 			 ; write format_track command
;debug* move.l  #0xff00ff00,0xfc030
	   bsr	 wdiskctl
	   move.l  #timeout,D7			; D7 = timeout value

;--- wait for 1770 to complete:
otw1:   btst.b  #5,gpip				; is 1770 done?
	   beq	 otw2				; (yes)
	   subq.l  #1,D7				; if(--D7) continue;
	   bne	 otw1
;debug* move.l  #0xffffffff,0xfc030

	   bsr	 reset1770			; timed out -- reset 1770
oterr:  moveq	 #1,D7				; return NE (error status)
	   rts

;--- see if the write-track won:
otw2:   nop
;debug* move.l  #0x0,0xfc030

	   move.w  #0x190,(A6)			 ; check DMA status bit
	   move.w  (A6),D0
	   btst	 #0,D0			; if its zero, there was a DMA error
	   beq	 oterr				; (so return NE)
	   move.w  #0x180,(A6)			 ; get 1770 status
	   bsr	 rdiskctl
;**	   bsr	 err_bits 			; set 1770 error bits
	   and.b	 #0x44,D0	 ; check for writeProtect & lostData
	   rts				; return NE on 1770 error

;------ write 'D1+1' copies of D0.B into A2, A2+1, ...
wmult:  move.b  D0,(A2)+ 			; record byte in proto buffer
	   dbf	 D1,wmult 			; (do it again)
	   rts
;
;************************************************************
;
; floplock - lock floppies and setup floppy parameters
;
; Passed (on the stack):
;	   0x18(A7) - count.W (sector count)
;	   0x16(A7) - side.W (side#)
;	   0x14(A7) - track.W (track#)
;	   0x12(A7) - sect.W (sector#)
;	   0x10(A7) - dev.W (device#)
;	    0xc(A7) - obsolete.L
;		8(A7) - dma.L (dma pointer)
;		4(A7) - ret1.L (caller's return address)
;		0(A7) - ret.L (floplock's return address)
;
; It is not coincidence that this matches the read and write input list;
; they call us to get stuff off stack and plug into parameters. Then,
; routine "select" actually pops this stuff into hardware.
;
; Passed: 	 D0.W = default error number
;
; Also, we helpfully point A6 at the DMA chip, and A1 at the DSB.
;-
floplock:
	   lea	regsave, A0
	   movem.l D3-D7/A3-A6, (A0)		; save C registers
	   lea	 fifo,A6				; A6 -> fifo
; Start setting up param block..
	   move.w  D0,def_error(A4)		    ; set default error number
	   move.w  D0,curr_err(A4)		    ; set current error number
; Kick VBL off floppies..
	   move.w  #1,flock 			; tell vbl not to touch floppies
; Stuff off stack:
	   move.l  8(A7),cdma(A4)		    ; cdma -> /even/ DMA address
	   move.w  0x10(A7),cdev(A4)	  ; save device# (0 .. 1)
	   move.w  0x12(A7),csect(A4)   ; save sector# (1 .. 9, usually)
	   move.w  0x14(A7),ctrack(A4)  ; save track# (0 .. 39 .. 79 ..)
	   move.w  0x16(A7),cside(A4)   ; save side# (0 .. 1)
	   move.w  0x18(A7),ccount(A4)  ; save sector count (1..A)
;--- pick a DSB: Point A1 at it.
	   lea	 dsb0,A1		  ; pick dsb 0 (drive A)
	   tst.w	 cdev(A4)
	   beq	 flock2
	   lea	 dsb1,A1		  ; pick dsb 1 (drive B)
;--- compute ending DMA address from count parameter: Plug into edma.
; This is used in multisector transfers in read-multiple-sector,
; but we don't use it here.
flock2: moveq	 #0,D7
	   move.w  ccount(A4),D7 		    ; edma = cdma + (ccount * 512)
	   lsl.w	 #8,D7
	   lsl.w	 #1,D7				; do a 9 shift..
	   movea.l  cdma(A4),A0
	   adda.l   D7,A0
	   move.l  A0,edma(A4)			    ; save in edma
;--- recalibrate drive, if it needs it. This only happens when the DSB
; says that this drive has never awoken before, and needs an initial
; recal to match its current track # with the DSB track #. 
	   tst.w	 dcurtrack(A1) 		; if (curtrack < 0) recalibrate()
	   bpl	 flockr
; RECAL needed. Show flag about it..
	   move.l  #0xf0f0f0f0,0xfe000
	   move.l  #0xf0f0f0f0,0xfe004

	   bsr	 select				; select drive & side
	   clr.w	 dcurtrack(A1) 		; we're optimistic -- assume winnage
; Restore.
	   bsr	 restore				; attempt restore
	   beq	 flockr				; (it won)
	   moveq	 #10,D7				; attempt seek to track 10
	   bsr	 hseek1
	   bne	 flock1				; (failed)
	   bsr	 restore				; attempt restore again
	   beq	 flockr				; (it won)
;
flock1:  move.w  #recal, dcurtrack(A1)	  ; complete failure (what can we do?)

	   movem.l regsave(PC), D3-D7/A3-A6
flockr: rts
;
;********************************************************************
;+
; flopfail - unlock floppies and return error.
; Common way for read and write to return.
;
; Note: Returns via unlok1.
;-
flopfail:
	   move.w #0xffff, badflag(A4)           ; bad sectors found
	   move.l  #0xffffffff,D0		 ; aargh, error
	   bra   unlok1

;***************************************************
;+
; flopok - unlock floppies and return success status. Also a common
; way for r/w to return.
;
;-
flopok: clr.l	 D0					; return 0 (success)
; Entry point from flopfail..
unlok1: move.l  D0,-(A7) 			; (save return value)
; I believe this code returns the FDC's status to a type-1 status, where
; the write protect switch is available for VBL to look at.
	   move.w  #datareg,(A6) 		; force WP to real-time mode
; Sets FDC's current track register to track we are on right now.
	   move.w  dcurtrack(A1),D7		; dest-track = current track
	   bsr	 wdiskctl
; Does it with a "noop seek" (source=dest). Only forces FDC to type 1 status.
	   move.w  #0x10,D6 			 ; cmd = seek w/o verify
	   bsr	 flopcmds 			; do it
;
unlok2: move.l  (A7)+,D0 			; restore return value
	   movem.l  regsave(PC), D3-D7/A3-A6
; clear floppy lock of vblank..
	   clr.w	 flock		; allow vblank .. unlock floppies
; wave byebye at the pretty camera, jenny...
	   rts
;************************ Seek routines ********************
;+
; hseek  - seek to 'ctrack(A4)' without verify
; hseek1 - seek to 'D7' without verify
; hseek2 - seek to 'D7' without verify, keep current error number
;
; Returns:	 NE on seek failure ("cannot happen"?)
;			 EQ if seek wins
;
; Uses:		 D7, D6, ...
; Exits to:	 flopcmds
; Called-by:	 _flopfmt, _flopini
;
;-
hseek:  move.w  ctrack(A4),D7 		    ; dest track = 'ctrack'
hseek1: nop
hseek2: move.w  #datareg,(A6) 		; write destination track# to data reg
	   bsr	 wdiskctl 			; write D7 to FDC data register
; seek command:
	   move.w  #0x10,D6 			 ; execute "seek" command
;								* Note: no spinup time.
	   bra	 flopcmds 			; (without verify...)
;
;***********************************************
;+
; reseek - home head, then reseek track
; Returns:	 EQ/NE on success/failure
; Falls-into:	 go2track
;
;-
reseek:
	   bsr	 restore				; restore head
	   bne	 go2trr				; (punt if home fails)

	   clr.w	 dcurtrack(A1) 		; current track = 0
	   move.w  #trkreg,(A6)			; set "current track" reg on 1770
	   clr.w	 D7
	   bsr	 wdiskctl 			; write a 00 to trk register.

	   move.w  #datareg,(A6) 		; seek out to track five
	   move.w  #5,D7
	   bsr	 wdiskctl 			; dest track = 5
	   move.w  #0x10,D6
	   bsr	 flopcmds 			; seek
;								* Note: no spinup time.
	   bne	 go2trr				; return error on seek failure
	   move.w  #5,dcurtrack(A1)		; set current track#

;***********************************************+
; go2track - seek proper track
; Passed: 	 Current floppy parameters (ctrack, et al.).
; Returns:	 EQ/NE on success/failure
; Calls:		 flopcmds
; Called by: read sector, for instance. Lots of places.
;-
go2track:
	   move.w  #datareg,(A6) 		; set destination track# in
	   move.w  ctrack(A4),D7 		    ;  1770's data register
	   bsr	 wdiskctl 			; (write track#)
	   moveq	 #0x14,D6 			 ; execute 1770 "seek_with_verify"
	   bsr	 flopcmds 			; (include seek-rate bits)
	   bne	 go2trr				; return error on seek failure
	   move.w  ctrack(A4),dcurtrack(A1)    ; update current track number
	   and.b	 #0x18,D7 			 ; check for RNF, CRC_error, lost_data
go2trr: rts						; return EQ/NE on succes/failure

;*************************************************
;+
; restore - home head
; Passed: 	 nothing
; Returns:	 EQ/NE on success/failure
;-
restore:
	   clr.w	 D6					; 0x00 = 1770 "restore" command
	   bsr	 flopcmds 			; do restore
	   bne	 res_r				; punt on timeout
	   btst	 #2,D7				; test TRK00 bit
	   eori.w  #0x04, CCR			; flip Z bit (return NE if bit is zero)
	   bne	 res_r				; punt if didn't win
	   clr.w	 dcurtrack(A1) 		; set current track#
res_r:  rts

;****************************************************
; Special floppy cmd just for seeking:
;+
; flopcmds - floppy command (or-in seek speed bits from database)
; Passed: 	 D6.w = 1770 command
; Sets-up:	 seek bits (bits 0 and 1) in D6.w
; Falls-into:	 flopcmd
; Returns:	 EQ/NE on success/failure
;
; I get the impression this is only used for seeking. I am right.
;-
flopcmds:
	   move.w  dseekrt(A1),D0		; get floppy's seek rate bits
	   andi.b  #3,D0				; OR into command
	   or.b	 D0,D6
; Fall in...
;******************************************************+
; flopcmd - execute any ol' 1770 command (with timeout)
; Passed: 	 D6.w = 1770 command
;
; Returns:	 EQ/NE on success/failure
;			 D7 = 1770 status bits
;
; Note: does motor spinup, if cmd in D6 specs it (I would *hope*!)
;-
flopcmd:
	   move.l  #timeout,D7			; setup timeout count (assume short)
	   move.w  #cmdreg,(A6)			; select 1770 command register
	   bsr	 rdiskctl 			; read it to clobber READY status
	   btst	 #7,D0				; is motor on?
	   bne	 flopcm				; (yes, keep short timeout)
	   move.l  #ltimeout,D7			; extra timeout for motor startup
flopcm: bsr	 wdiskct6 			; write command (in D6)

flopc1: subq.l  #1,D7				; timeout?
	   beq	 flopcto				; (yes, reset and return failure)
	   btst.b  #5,gpip				; 1770 completion?
	   bne	 flopc1				; (not yet, so wait some more)
	   bsr	 rdiskct7 			; return EQ + 1770 status in D7
	   clr.w	 D6
	   rts
;****
flopcto:
; We timed out.. a gruesome death indeed.
; Whap controller back to life.	(Hit him harder Dave, he likes it that way)
	   bsr	 reset1770			; bash controller
	   moveq	 #1,D6				; and return NE
	   rts

;*******************************************************
;+
; reset1770 - reset disk controller after a catastrophe
; Passed: 	 nothing
; Returns:	 nothing
; Uses:		 D7
;-
reset1770:
	   move.w  #cmdreg,(A6)			; execute 1770 "reset" command
	   move.w  #0xD0,D7 			 ; force interrupt
	   bsr	 wdiskctl
	   move.w  #15,D7				; wait for 1770 to stop convulsing
r1770:  dbf	 D7,r1770 			; (short delay loop)
	   bsr	 rdiskct7 			; return 1770 status in D7
	   rts

;******************************************************
;+
; select - setup drive select, 1770 and DMA registers
; Passed: 	 cside(A4), cdev(A4)
; Returns:	 appropriate drive and side selected
;
; Called: All over the place.
;
;-
select: move.w  #0,0x9c0 			 ; floppies NOT deselected
;								* lets VBL shut them off...
	   move.w  cdev(A4),D0			    ; get device number
	   addq.b  #1,D0				; add and shift to get select bits
	   lsl.b	 #1,D0				; into bits 1 and 2
	   or.w	 cside(A4),D0			    ; or-in side number (bit 0)
	   eori.b   #7,D0				 ; negate bits for funky hardware select
	   andi.b   #7,D0				 ; strip anything else out there
	   bsr	 setporta 			; do drive select
; Have to restore 1770's track register from table..in dsb.
	   move.w  #trkreg,(A6)			; setup 1770 track register
	   move.w  dcurtrack(A1),D7		; from current track number
	   bsr	 wdiskctl
	   clr.b	 tmpdma(A4)			    ; zero bits 24..32 of target DMA addr

; Setup R/W parameters on 1770. Used by
; r/w sector, among others. This is where the sector register gets set.
; 
select1:
	   move.w  #secreg,(A6)			; setup requested sector_number from
	   move.w  csect(A4),D7			    ;	  caller's parameters
	   bsr	 wdiskctl
	   move.b  cdma+3(A4),dmalow		    ; setup DMA chip's DMA pointer
	   move.b  cdma+2(A4),dmamid
	   move.b  cdma+1(A4),dmahigh
	   rts

;************************************************
;+
; setporta - set floppy select bits in PORT A on the sound chip
; Passed: 	 D0.b (low three bits)
; Returns:	 D1 = value written to port A
;			 D2 = old value read from port A
; Uses:		 D1
;-
setporta:
	   move	 SR,-(A7) 			; save our IPL
	   ori	 #0x0700,SR			 ; start critical section
	   move.b  #giporta,giselect		; select port on GI chip
	   move.b  giread,D1			; get current bits
	   move.b  D1,D2				; save old bits for caller
	   andi.b   #0xff-7,D1			  ; strip low three bits there
	   or.b	 D0,D1				; or-in our new bits
	   move.b  D1,giwrite			; and write 'em back out there
	   move	 (A7)+,SR 			; restore IPL to terminate CS, return
	   rts

;**************************************************
;+
; Primitives to read/write 1770 controller chip (DISKCTL register).
;
; The 1770 can't keep up with full-tilt CPU accesses, so
; we have to surround reads and writes with delay loops.
; This is not really as slow as it sounds.
;
wdiskct6: 						; write D6 to diskctl
	   bsr	 rwdelay				;	   delay
	   move.w  D6,diskctl			;	   write it
	   bra	 rwdelay				;	   delay and return

wdiskctl: 						; write D7 to diskctl
	   bsr	 rwdelay				;	   delay
	   move.w  D7,diskctl			;	   write it
	   bra	 rwdelay				;	   delay and return

rdiskct7: 						; read diskctl into D7
	   bsr	 rwdelay				;	   delay
	   move.w  diskctl,D7			;	   read it
	   bra	 rwdelay				;	   delay and return

rdiskctl: 						; read diskctl into D0
	   bsr	 rwdelay				;	   delay
	   move.w  diskctl,D0			;	   read it
; And here's the delay loop:
rwdelay:
	   move	 SR,-(A7) 			; save flags
	   move.w  D7,-(A7) 			; save counter register
	   move.w  #0x20,D7 			; 0x20 seems about right...
rwdly1: dbf	 D7,rwdly1			; busy-loop: give 1770 time to settle
	   move.w  (A7)+,D7 			; restore register, flags, and return
	   move	 (A7)+,SR
	   rts

;-------------------------------------
regsave:	dc.l 	1	  ; PC relative data area
		dc.l 	2
		dc.l 	3
		dc.l 	4
		dc.l 	5
		dc.l 	6
		dc.l 	7
		dc.l 	8
		dc.l 	9


}		/* MY GOD!  It's DONE!  */

@\Rogue\Monster\
else
  echo "shar: Will not over write TWST.C"
fi
echo "Finished archive 2 of 2"
# to concatenate archives, remove anything after this line
exit 0