holloway@drivax.UUCP (Bruce Holloway) (02/27/86)
The following is a little program I wrote to transfer files from a TOS disk to a CP/M disk. It works all right in test, but when I run it on the ST all the block numbers are off by one. Kludging the program to increment the block number doesn't seem to work. Could someone tell me the format used for the CP/M disks? The "#define"s show the values I've used, and they seem to work mostly, except that the files all begin one block farther along then I think they should. The program follows. /***************************************************************************** * STXFER.C * * Compile with Alcyon C, using the CLIB supplied with the ST Developers * Kit. * * Program to transfer files from a TOS formatted diskette to a CP/M * formatted disk. CP/M formatted disks are the same as TOS disks, but * they have no TOS files, or even a volume label, on them. * * This program is my own creation, and has absolutely nothing to do with * Digital Research, Inc., or Atari, and should not be construed as * being any part nor portion of any product, announced or no, from either * of these two companies. * * This is released to the public domain, and shouldn't be sold or any * of that. I reserve the copyright, and the right to change it, or * fix it, etc. * * Copyright (c) 1986, Bruce Holloway ******************************************************************************/ /* ***** Note: This will only work on disks which have a sector size <= 1K. ***** */ #include "stdio.h" #define TOSDISK 0 /* Code for TOS disk */ #define CPMDISK 1 /* Code for CP/M disk */ #define CPMUSER 0 /* CP/M User Number */ #define SECSIZE 512 /* Size of a sector */ #define RECSIZE 128 /* Size of a record */ #define TRACKS 80 /* Number of tracks */ #define SECS 9 /* Sectors per track */ #define BLKSIZE 2048 /* Bytes per block */ #define HIDDEN 18 /* Number of "hidden" sectors */ #define DIRSIZE 128 /* Max number of directory entries */ #define FCBSIZE 32 /* Size of an FCB */ #define DSKSIZE ((TRACKS*SECS)/(1024/SECSIZE)) /* Kilobytes on disk */ #define FILSIZE ((32767/BLKSIZE)*BLKSIZE) /* Size of file buffer */ #define BLOCK0 (HIDDEN+(FCBSIZE*DIRSIZE)/SECSIZE) /* 1st data sector */ #define NUMBLKS ((TRACKS*SECS-BLOCK0)/(BLKSIZE/SECSIZE)) /* # blocks on disk */ #define MAPSIZE (NUMBLKS/8+1) /* Size of free block map, in bytes */ char fcbs[SECSIZE/FCBSIZE][FCBSIZE]; /* Buffer for directory entries */ char blkmap[MAPSIZE]; /* Free block map */ char buffer[FILSIZE/BLKSIZE][BLKSIZE]; /* File buffer */ char tosname[40]; /* TOS file name */ char cpmname[40]; /* CP/M name */ int curdisk; /* Current disk in drive, 0 == TOS */ int mapmade; /* Is the map made? */ main(){ intro(); do{ get_names(); xfer(); } while(morefiles()); extro(); pexit(); } intro(){ curdisk = -1; mapmade = 0; cprintf("Atari TOS -> CP/M File Transfer Program\n\n"); } get_names(){ cprintf("Name of the TOS file to transfer? "); getline(tosname,35); cprintf("\nName of the CP/M file? "); getline(cpmname,35); cprintf("\n"); } xfer(){ int handle, left, blocks, fcbnum, blknum, extent, block; char fcb[FCBSIZE]; long f_read(); select(TOSDISK); handle = f_open(tosname,0); if(handle < 0){ cprintf("%%% File Not Found %%%\n"); return; } blknum = FCBSIZE/2; extent = -1; do{ if(!(left = (int)f_read(handle,(long)FILSIZE,buffer))) continue; if(left < 0){ cprintf("%%% Error while reading TOS file %%%\n"); return; } blocks = left/BLKSIZE + ((left%BLKSIZE) > 0); for(left=0; left++ < blocks;){ if(blknum == FCBSIZE/2){ if(extent++ != -1){ if(left == blocks) fcb[15] = 0x80; if((fcbnum = getfcb()) == -1){ cprintf("%%% Out of directory entries %%%\n"); return; } putfcb(fcbnum,fcb); } makfcb(cpmname,fcb); fcb[0] = CPMUSER; fcb[12] = extent; blknum = 0; } if((block = firstfree()) == -1){ cprintf("%%% Out of disk space %%%\n"); return; } fcb[16+blknum++] = block+1; fcb[15] += BLKSIZE/RECSIZE; writeblock(block,buffer[left-1]); } } while(left == FILSIZE); if((fcbnum = getfcb()) == -1){ cprintf("%%% Out of directory entries %%%\n"); return; } putfcb(fcbnum,fcb); f_close(handle); cprintf("Transfer complete!\n"); } /* Select the CP/M or TOS disk. Init internal tables if selecting CP/M disk for the first time. */ select(disk) int disk; { char iline[5]; int i, j, dleft, cursec, indx; if(disk == curdisk) return; cprintf("Please insert %s disk and press <RETURN> -- ", (disk==CPMDISK)?"CP/M":"TOS"); getline(iline,3); if(((curdisk = disk) == CPMDISK) && !mapmade){ for(i=0; i<MAPSIZE;) blkmap[i++] = 0; for(dleft=i=0, cursec=HIDDEN; i<DIRSIZE; ++i){ if(!dleft){ indx=0; readsector(cursec++,fcbs); dleft = SECSIZE/FCBSIZE; } if(fcbs[indx][0] != 0xE5) for(j=16; j<32; ++j) setblock(fcbs[indx][j]); ++indx; --dleft; } mapmade = 1; } } /* Mark a block as used */ setblock(block) int block; { blkmap[block/8] |= (1 << block%8); } /* Return the first unused block number, or -1 if the disk is full */ firstfree(){ int byte, bit, block, found; if(!mapmade) select(CPMDISK); for(byte=block=found=0, bit=1; block<NUMBLKS; ++block){ if(!(blkmap[byte] & bit)){ found=1; break; } if((bit <<= 1) == 0x100){ ++byte; bit=1; } } return((found) ? block : -1); } /* Write a block to the CPM disk */ writeblock(block,buf) int block; char buf[][SECSIZE]; { int secnum, i; select(CPMDISK); setblock(block); secnum = BLOCK0 + (block*(BLKSIZE/SECSIZE)); for(i=0; i<BLKSIZE/SECSIZE; ++i, ++secnum) writesector(secnum,buf[i]); } /* Return the number of the first free FCB, or -1 if all are used */ getfcb(){ int free, left, i; select(CPMDISK); for(free=left=0; free<DIRSIZE; ++free, ++i){ if(!left){ readsector(HIDDEN+free/(SECSIZE/FCBSIZE),fcbs); left = SECSIZE/FCBSIZE; i = 0; } if(fcbs[i][0] == 0xE5) return(free); --left; } return(-1); } /* Write out an FCB */ putfcb(entry,fcb) int entry; char fcb[]; { int i, j, sec; select(CPMDISK); readsector(sec=HIDDEN+entry/(SECSIZE/FCBSIZE),fcbs); i = entry%(SECSIZE/FCBSIZE); for(j=0; j<FCBSIZE; ++j) fcbs[i][j] = fcb[j]; writesector(sec,fcbs); } /* Write a sector to the CP/M disk */ writesector(secnum,buf) int secnum; char *buf; { select(CPMDISK); rwabs(1,buf,1,secnum,0); } /* Read a sector from the CP/M disk */ readsector(secnum,buf) int secnum; char *buf; { select(CPMDISK); rwabs(0,buf,1,secnum,0); } #define FCB struct _fcb FCB{ char f_dr, f_name[8], f_ext[3], f_junk[20]; }; makfcb(fspec,fcb) char *fspec; FCB *fcb; { static int state, index, i, ch; static char thold[40], *ts; state = index = 0; for(i=0, ts = (char *)fcb ; i++ < sizeof(FCB); *ts++ = 0 ); for(i=1, ts = (char *)fcb+1; i++ < 12 ; *ts++ = ' '); do{ switch(ch = toupper(*fspec++)){ case ':': if(!state){ state=1; thold[index] = 0; for(index=0; i = thold[index++];) if(isupper(i)) fcb->f_dr = i-'A'+1; index=0; } else goto nch; break; case '.': if(state < 2){ state=2; thold[index]=0; for(index=0; (i=thold[index]) && index<8;) fcb->f_name[index++] = i; index=0; } else goto nch; break; case '\0': thold[index]=0; if(state < 2) for(index=0; (i=thold[index]) && index<8;) fcb->f_name[index++] = i; else for(index=0; (i=thold[index]) && index<3;) fcb->f_ext[index++] = i; goto fixup; default: nch: thold[index++] = ch; break; } } while(1); fixup: } /* Oddly, I *don't* have the Atari Developer's Kit, so these routines make up the TOS entry points. These can be removed or modified if you don't need them. */ getline(buf,count) char buf[]; int count; { buf[0] = count; trap1(10,buf); buf[buf[1]+2] = 0; strcpy(buf,buf+2); printf("\n"); } f_open(pname,mode) char *pname; int mode; { return(trap1(0x3D,pname,mode)); } long f_read(handle,count,buf) int handle; long count; char *buf; { select(TOSDISK); return((long)trap1(0x3F,handle,count,buf)); } f_close(handle) int handle; { trap1(0x3e,handle); } pexit(){ trap1(0x4c,0); } rwabs(rwflag,buf,count,recno,dev) int rwflag,count,recno,dev; char *buf; { trap13(4,rwflag,buf,count,recno,dev); } morefiles(){ char iline[10]; printf("Transfer any more files? "); getline(iline,5); return(iline[0] == 'y' || iline[0] == 'Y'); } extro(){ printf("All transfers completed.\n"); } co(ch) int ch; { trap1(2,ch); } flush(){} /**************************************************************************** * These are the assembler routines needed to talk to TOS. * * Break these off into a file called "XMSTASM.S" or somesuch and * assemble and link them seperately. *****************************************************************************/ #if 0 .Globl _trap1,_trap13,_trap14 ************************************************************************* * TRAP1(fn,p1,p2,...,pn) * * * * Call GEMDOS with the specified parameter block. * ************************************************************************* _trap1: Move.L (SP)+,tr1ra Trap #1 Move.L tr1ra,-(SP) Rts ************************************************************************* * TRAP14(fn,p1,p2,...,pn) * * * * Call Atari BIOS Extension with the specified parameters. * ************************************************************************* _trap14: Move.L (SP)+,tr14ra Trap #14 Move.L tr14ra,-(SP) Rts ************************************************************************* * TRAP13(fn,p1,p2,...,pn) * * * * Call GEMDOS BIOS with the specified parameters. * ************************************************************************* _trap13: Move.L (SP)+,tr13ra Trap #13 Move.L tr13ra,-(SP) Rts .Bss tr14ra: Ds.L 1 Return address from TRAP 14 tr13ra: Ds.L 1 Return address from TRAP 13 tr1ra: Ds.L 1 Return address from TRAP 1 .End #endif -- +----------------------------------------------------------------------------+ |Whatever I write are not the opinions or policies of Digital Research, Inc.,| |and probably won't be in the foreseeable future. | +----------------------------------------------------------------------------+ Bruce Holloway ....!ucbvax!hplabs!amdahl!drivax!holloway (I'm not THAT Bruce Holloway, I'm the other one.)
Bicer.ES@XEROX.COM (03/04/86)
I have just received CPM.TOS. When I execute it it asks for a CPM disk, and none of the disks I seem to be good enough. What do I have to do to get it to work with my CPM programs? Also, is the speed comparable to a, lets say a 4Meg Z80? Thaks in advance, Jack Bicer Bicer.ES@Xerox.COM -or- Bicer.ES@Xerox.ARPA