Atari Diskfile Tutorial - Part III

Disk I/O is a very slow process for the Atari. The disk must be physically spun, which takes a while, and data must be shipped back and forth, which is even slower. The idea behind Atari's DOS is to minimize disk I/O. So we come to the concept of a "buffer."

In Atari's DOS, whenever you read or write to a file, you are reading/writing into a 128-byte reserved area in memory called a buffer. You are not writing to the disk at all. The Atari keeps the contents of one of the sectors of the disk in that buffer. So if you read/write to that buffer, the operation occurs at very high speed, which is what we want. For instance:

10 OPEN #1,4,0,"D1:FILE"
20 FOR A=1 TO 10000
30 GET #1,A
40 NEXT A

merely reads, byte by byte, 10000 bytes from disk. But if you run this program, you will note that the disk isn't being accessed continually; only every once in a while will you hear a beep to indicate another read. What happens is that the Atari opens the file and pulls the first sector full of data into the memory buffer. When you do the first 125 GETs, the bytes are pulled out of that memory buffer. Then you try to read another byte, but the Atari doesn't have that one in memory yet. So it requests the disk to send it the next 128 bytes, fills the buffer with those 128 bytes, and starts reading at the beginning of the buffer again.

(If you are wondering why I said the 125th byte, it is because the Atari DOS reserves three bytes per sector for its own uses, which we will talk about later.)

Similarly, when you PUT to a disk file, the Atari lets you PUT 125 bytes to the buffer, then dumps it to the disk, moves in another 125, and so on.

The result of all this confusion is that the Atari doesn't have to go to disk for every individual byte; rather, it stays in memory for a large number of "disk" accesses, and things run much faster.

When the Atari is LOADing or SAVEing a program, again it uses these buffers. The process is invisible to you, but you can hear the beep as each individual sector in the program is LOADed, or a clunk as a sector is SAVEd. Remember, the Atari can only talk to the disk in terms of complete 128-byte sectors.

Understanding the buffer is important in solving some of the mysterious problems that can occur while using the disk. For instance; let's say you write something out to the buffer, and then your program bombs. You examine the disk file, and find the data never reached the disk. This is because the buffer was never written to disk. If you didn't know about the buffer, you'd be wondering why the disk didn't record what you wrote to it.

The CLOSE Statement

The CLOSE statement is provided for this case. It makes sure that everything in the buffer gets to the disk. It also updates the directory, where all file names are listed.

The DOS program handling all this keeps a table of free sectors on the disk, by the way, and when the buffer fills up and a place is needed to store the 128 bytes, a sector number is taken from that table. When a file is deleted, its sectors are returned to that table. (The XXX FREE SECTORS message at the end of the DOS directory is determined by this).

So much for Basic I/O. Everything done with Basic I/O is a version of the above; everything goes through the 128-byte buffer.

 0300 DDEVIC
 0301 DUNIT
 0302 DCOMND





 0303 DSTATS
 0304,0305

 0306
 0308,0309
 030A,030B
         
--Serial Bus ID. Not used by user.
--Disk number. 1-4.
--Command Byte. This is:
$21 Format Disk
$50 Put Sector, without verify
$52 Get Sector
$53 Get Status
$57 Put Sector, with verify
--Disk Status. This is returned to you after the operation is done.
DBUFLO,HI. Buffer Address. This is a 16-bit address in
memory that is the starting address of where to get or put 128 bytes.
DTIMLO Disk Timeout Value. Leave it alone.
DBYTLO,HI Set by handler. Leave it alone.
DAUX1,DAUX2 Sector Number. Which disk sector, 1-720, to read/write.
       

Table 1.

Program 1.

5 REM SAMPLE PROGRAM TO DO DIRECT
6 REM DISK I/O. DAVID SMALL,12/21/81
7 REM
10 DCB=768:REM START OF DISK DC3
20 POKE DCB+1,1:REM DISK 1
30 POKE DCB+2,82:REM $52 = GET SECTOR
35 REM *** GET 128 BYTE OPEN BUFFER
40 DIM BUFFER$(128)
45 DIM CALL$(10)
50 FOR X=1 TO 128:BUFFER$(X)=" ":NEXT X
60 ADDR=ADR(BUFFER$)
70 ADDRHI=INT(ADDR/256)
80 ADDRLO=ADDR-(ADDRHI*256)
90 POKE DCB+4,ADDRLO:REM BUFFER ADR LOW
100 POKE DCB+5,ADDRHI:REM BUFFER ADR HI
110 REM *** SECTOR NUMBER ***
120 PRINT "INPUT SECTOR NUMBER"
130 INPUT SECTOR
140 SECTORHI=INT(SECTOR/256)
150 SECTORLO=SECTOR-(SECTORHI*256)
160 POKE DCB+10,SECTORLO
170 POKE DCB+11,SECTORHI
200 REM *** BUILD SHORT ASSY PROGRAM
210 REM *** OF PLA, JMP DSKINV.
230 CALL$(1)=CHR$(104):REM PLA
240 CALL$(2)=CHR$(32):REM JSR
250 CALL$(3)=CHR$(83):REM $53
260 CALL$(4)=CHR$(228):REM $E4
270 CALL$(5)=CHR$(96):REM RTS
300 X=USR(ADR(CALL$))
310 DSTATS=PEEK(DCB+3)
320 PRINT "DISK STATUS=";DSTATS;" (1=OK)"
330 PRINT "DATA:"
340 PRINT BUFFER$
341 REM *** CLEAN OUT BUFFER$
342 FOR X=1 TO 128:BUFFER$(X)=" ":NEXT X
350 GOTO 110
400 END

How about DOS?

When you issue the COPY disk command from DOS, each sector of the file is sent to memory. In other words, DOS copies the contents of that file into memory, 128 bytes at a time. It then takes the file in memory and writes it out to disk, from memory, again in 128 byte chunks.

When you issue a Duplicate Disk command, DOS reads in every sector on the disk that is marked as "used" and stores it in memory. The unused sectors are ignored to save time. DOS then writes those used sectors out to the new disk, in the same position it found them on the old one, and doesn't worry about the empty sectors. Since there are 92,000 bytes on a disk, and only 48K maximum of memory, it may have to do this in smaller pieces--let's say 32K at a time. You then must physically change the disk several times on a single-drive system. If you are using a multiple disk system, you will see it do the copy in multiple stages.

Byte 1: Flag byte.
    Bit 7 = 1 if this file has been deleted Bit 6 = 1 if this file is in use Bit 5 = 1 if this file is locked Bit 0 = 1 if this file is OPEN.
Bytes 2,3. Sector Count; the number of sectors in the file, stored low, then high bytes. Bytes 4,5. Starting Sector. Where the file begins (what sector number). Bytes 6-16. File name, Last three bytes are the extension if you add one, otherwise they are blanks.

Table 2.


Byte 1 .. Data
Byte 125..
Byte 126 File Number (6 bits) and high two bits of "forward pointer"
Byte 127 Forward Pointer
Byte 128 Short Sector count .. indicates this sector not completely full.

Table 3.

Note: If you are copying disks for backups, remove any cartridge; each cartridge selects 8K of RAM and makes for more disk swaps.

In order to know what sectors are used, and what files are on the disk, some tables must be kept on that disk. The DOS reserves certain sectors on the disk for these tables. One is known as the VTOC (Volume Table of Contents) and is sector number 360. In it is a table of all 720 sectors on disk, stored as bit-map; i.e., one bit per sector.

The disk directory is stored in sectors 361-368; this is the actual list of the file names and the sectors they occupy. To find a file, DOS must use this directory.

By the way, these reserved sectors explain why only 707 sectors are available on a "clean" disk; the rest are used for the directory tables, etc. You will also hear, during the disk formatting process, right near the end, the clunks as DOS writes out a fresh directory to the disk, after the disk 6507 has finished formatting it.

Remember, DOS at the lowest level, past all of the directory opens, XIO's, and so forth, is only doing get sectors/put sectors. Let's learn about those calls.

Get Sector/Put Sector Calls

All get sector/put sector calls rely on a table called the Device Control Block. Data is put into this table, and a jump is made into the operating system, which in turn uses that table. Table 1 is the disk table, starting at $300 (or 768 decimal).

One POKEs values into this table, and then does a JSR to DSKINV ($E453). DSKINV then does the requested operation and returns to you. Program 1 is a sample program that does the needed POKEs and requests from you a sector number (1-720) to read.

This program POKEs the DCB parameters, reserves 128 bytes in memory as a string for the buffer area, requests the sector number, then runs a very short assembly program which does a PLA, needed by Basic, then jumps off to DSKINV. DSKINV then does the operation, and returns to Basic.

There the status code placed in the table by DSKINV is printed, and the buffer area, as a string, is also printed. Sure, it will be strange, but you can examine individual bytes easily. If you read one of the directory sectors (try 361) you will see the directory entries.

If an 87 instead of an 82 were POKEd into DCOMND, then a Put Sector would occur ($57=Put Sector, $52=Get Sector). Then the contents of that buffer would be written to disk.

The status code will be a 1 if all goes well. If there is a problem, you may find a 144 as the error code. This Device Not Done Error occurs if the disk is had, the disk is write protected, etc.

We have just done disk access the same way DOS does it, at the sector level. Note we have not done an OPEN or an XIO; we have directly accessed the disk. This can be handy if you have a need for a disk allocation scheme free of DOS; I use it to store directly fixed-length records. It is fast and efficient, and removes the DOS overhead.

Also, it enables you to access the directory and modify it as necessary; this allows you, for instance, to un-delete files.

Because we are not using DOS, but are still accessing the disk, we are not dependent on the DOS directory or "In-Use-Table." We can read or write any sector on the disk, regardless of what DOS thinks that sector is for. You may want to take note: This is how to write a "boot record," a special disk record that enables your disk to boot by itself, as so many games do. The boot record is record 1; see the hardware manual for further details.

Now that you can access the directory data directly, you can see all the data stored rather than just what OPENing "*.*" returns. A directory entry is 16 bytes long for each file. There are eight entries per sector in the directory, and eight directory sectors, hence 64 files maximum. See Table 2.

As an example of how the directory is used, if a file is LOCKed from DOS, all that occurs is that its flag byte has bit 5 set to 1.

When you delete a file, all that occurs is that the delete flag on the file is set. Then, next time DOS needs a place to put a new file entry, it knows it can overwrite the current entry.

Reading the Data into a File

In order to read the data in this file, one goes to the Starting Sector number found in the directory. Each sector contains information as shown in Table 3.

The "forward pointer" is where the next sector of the file can be found. It is a sector number from 1 to 720. When that next sector is read in, it in turn tells where the next sector can be found, and so on. This is called a "linked list" or chained sector scheme. This way, files don't have to be in any particular order on the disk (e.g., running continuously from sector 30 to 40). The sectors can jump around all over the disk, yet to DOS they are still linked together.

The file number is the file number in the directory. It is set to the same number as the entry number in the directory for all sectors. This is a safety measure. Let's say we are reading directory entry 6, and are going along, sector by sector. In the file number position in a given sector, we suddenly find a 13 (or whatever) instead of the 6 that should be there.

We know that the data for this sector has gotten scrambled, and the sector chain terminates there. This is an ERROR 164, which I am sure you have seen before; it is a warning that the data in this sector are likely to be bad. The sector link is also likely to be bad, so DOS normally stops an ERROR 164.

Reading in sectors one at a time, by getting the next sector number, reading, and so on, is called "sector tracing." Atari DOS does it every time it reads in a file. Atari also sells a "disk fix" program through their program exchange which uses the above information; you now know enough to use it. For instance, the forward "link" of a given section may be modified if desired.

We can do the same sector tracing out of Basic. We will find a given directory entry, read it in starting at the sector number given by the directory and proceed through each sector, with each new sector number obtained from the data in the previous one, until we have read in the number of sectors specified in the directory. Should anything be wrong (file number, etc.) we will know the file has gone bad.

Program 2 illustrates first how to read in the directory and interpret it, and second how to follow a sector chain for a given file. It reads in the directory, lists it to the screen, and asks which file to trace; if A is entered, all files will be traced (this takes a while). It will then trace the file through, stopping at any "broken links" or bad file numbers. It is an excellent way to check a disk which has some files going bad, and find out which ones are still readable and which aren't. After each check the program produces a directory listing showing which files are good, bad, or still unknown.

The techniques used within the program will probably prove more useful than the program; however, remember that this is an example. It can serve as the basis for custom disk inspect/modify routines if you wish to write them. Also, the program is written in MicroSoft Basic but translating it to the old Basic should be no problem at all. I highly recommend MicroSoft Basic in terms of speed and features as compared to the old Basic; an indepth review of the Atari version of MicroSoft Basic will appear in an upcoming issue.

Sadly, the software pirate community also found out about direct disk I/O, literally years ago. Back then manufacturers were copy protecting disks by fouling up the directory and sector maps so that DOS couldn't perform the copy. With the advent of direct disk I/O, all these schemes were bypassed with what is known as a "sector copier."

Program 2.

10 REM DISK CHECKER -- DAVE SMALL
20 REM
30 GOTO 290
40 REM SUBROUTINES PLACED IN FRONT
50 REM FOR SPEED. NO COMMENTING.
60 REM
70 REM DISK READ SUBROUTINE
80 POKE DCB+1,DFROM
90 POKE DCB+2,82
100 BHI=INT(BUFF/256):BLO=BUFF-(BHI*256)
110 POKE DCB+4,BLO
120 POKE DCB+5,BHI
130 SHI=INT(RSECTOR/256):SLO=RSECTOR-(SHI*256)
140 POKE DCB+10,SLO:POKE DCB+11,SHI
150 X=USR(CALL)
160 DSTATS=PEEK(DCB+3)
170 RETURN
180 REM DISK WRITE SUBROUTINE-(SAMPLE)
190 POKE DCB+1,BLO
200 POKE DCB+2,87
210 BHI=INT(BUFF/256):BLO=BUFF-(BHI*256)
220 POKE DCB+4,BLO
230 POKE DCB+5,BHI
240 SHI=INT(RSECTOR/256):SLO=RSECTOR-(SHI*256)
250 POKE DCB+10,SLO:POKE DCB+11,SHI
260 PRINT "ERROR":GRAPHICS 0
270 DSTATS=PEEK(DCB+3)
280 RETURN
290 REM DISK CHECKER.
300 REM DOES SECTOR TRACE OF ALL
310 REM FILES IN THE DIRTECTORY
320 REM PRODUCES LISTING WITH OK/
330 REM BAD.
340 REM ASSUMES 48K, MICROSOFT.
350 REM
360 REM GET DISK NUMBER, WELCOM.---
370 GRAPHICS 0
380 CLEAR
390 PRINT "WELCOME TO SMALL'S DISK CHECKER."
400 PRINT "INPUT DRIVE NUMBER";
410 INPUT DF$
420 IF DF$="" THEN DFROM=1:GOTO 440
430 DFROM=VAL(DF$)
440 DTO=DFROM
450 CALL=&E453   !DSKINV ADDRESS
460 REM NOTE: MICROSOFT CAN CALL
470 REM DSKINV$ DIRECTLY, NO PLA
480 REM NEEDED.
490 DCB=&300
500 OPTION RESERVE 20*128
510 B=VARPTR(RESERVE)
520 IF B<0 THEN B=B+65536
530 REM FIRST, READ IN DIRECTORY ----
540 PRINT "GETTING DIRECTORY."
550 INCR=0
560 FOR RSECTOR =360 TO 368
570 BUFF=B+(128*INCR)  !INPUT AREA
580 GOSUB 70           !GET SECTOR
590 IF DSTATS=1 THEN 630
600 PRINT "***DISK RETRY, DIRECTORY SECTOR ";RSECTOR
610 PRINT "***STATUS=";DSTATS
520 GOTO 570
630 INCR=INCR+1
640 PRINT INCR;
650 NEXT RSECTOR
660 PRINT
670 PRINT "WAIT -- BUILDING TABLES."
680 REM OK, NOW EXAMINE DIRECTORY.
690 REM PRODUCE LISTING FIRST OFF.
700 REM NOTE 360=VTOC, NOT DIR.
710 DIM INFO(64,6) !FILE INFO
720 DIM NIME(64) !NAME INFO
730 DIM DEAD(64) !FILE BOMBED?
740 FOR Z%=1 TO 64:DEAD(Z%)=0:NEXT Z%
750 REM
760 Z=B+128 !START OF DIR ENTRY
770 REM BUILD FLAG & INFO TABLE------
780 FNUM=1
790 FOR LOC=Z TO Z+(64*16) STEP 16
800 REM LOC=ENTRIES' STARTING ADDR
810 FLAG%=PEEK(LOC)
820 IF(FLAG% AND &80)=&80 THEN INFO(FNUM,3)=1
830 IF(FLAG% AND &40)=&40 THEN INFO(FNUM,4)=1
840 IF(FLAG% AND &20)=&20 THEN INFO(FNUM,5)=1
850 IF(FLAG% AND 1)=1 THEN INFO(FNUM,6)=1
860 REM SECTOR INFO----------------------
870 INFO(FNUM,1)=PEEK(LOC+1)+256*PEEK(LOC+2)
880 INFO(FNUM,2)=PEEK(LOC+3)+256*PEEK(LOC+4)
890 REM NOW GET NAME DATA..-------------
900 NIME$(FNUM)=""
910 FOR Z%=5 TO 15
920 NIME$(FNUM)=NIME$(FNUM)+CHR$(PEEK(LOC+Z%))
930 NEXT Z%
940 REM CHECK FOR END OF ENTRIES
950 IF PEEK(LOC+5)=0 THEN 1020
960 REM
970 FNUM=FNUM+1
980 IF FNUM/5=INT(FNUM/5) THEN PRINT FNUM;
990 NEXT LOC
1000 PRINT "DIR TOTALLY FULL."
1010 GOTO 1030
1020 PRINT "DIR PARTLY FULL."
1030 FNUM=FNUM-1
1040 PRINT
1050 PRINT "NUMBER OF ENTRIES=";FNUM
1060 REM OUTPUT DIRECTORY
1070 REM ENTRY POINT (FROM BELOW)
1080 IF FNUM=0 THEN 1320 !EMPTY DISK
1090 PRINT
1100 PRINT "D=DELETED/U=USED/L=LOCKED/O=OPEN"
1110 PRINT "SC=SECTOR COUNT(LEN)"
1120 PRINT "SS=STARTING SECTOR(LEN)"
1130 PRINT " + = GOOD FILE / - = BAD FILE / ? = UNKNOWN"
1140 PRINT
1150 FOR F=1 TO FNUM
1160 PRINT USING "##";F;
1170 IF DEAD(F)=-1 THEN PRINT "+";
1180 IF DEAD(F)=1 THEN PRINT "-";
1190 IF DEAD(F)=0 THEN PRINT "?";
1200 PRINT "!";NIME$(F);"!";
1210 IF INFO(F,3)=1 THEN PRINT "D"; ELSE PRINT " ";
1220 IF INFO(F,4)=1 THEN PRINT "U"; ELSE PRINT " ";
1230 IF INFO(F,5)=1 THEN PRINT "L"; ELSE PRINT " ";
1240 IF INFO(F,6)=1 THEN PRINT "O"; ELSE PRINT " ";
1250 PRINT "!SC:";
1260 PRINT USING "###";INFO(F,1);
1270 PRINT "!SS:";
1280 PRINT USING "###";INFO(F,2)
1290 IF F/20=INT(F/20) THEN INPUT RET$
1300 NEXT F
1310 GOTO 1390
1320 PRINT "EMPTY DISK -- NO ENTRIES"
1330 STOP
1340 PRINT
1350 REM ENTRY POINT (BELOW)
1360 PRINT "PRESS RETURN."
1370 INPUT RET$:CLS:GOTO 1420
1380 GOTO 1400
1390 PRINT "PRESS RETURN."
1400 INPUT RET$
1410 REM NEXT IS ENTRY POINT
1420 PRINT "SELECT :"
1430 PRINT "1. A FILE NUMBER TO CHECK;"
1440 PRINT "2. 'A' TO CHECK ALL FILES;"
1450 PRINT "3. 'D' TO SEE DIRECTORY LIST AGAIN"
1460 PRINT "NOTE: ERRORS SHOW UP ON DIR LISTING "
1470 PRINT "AFTER THEY OCCUR IN SEARCH."
1480 INPUT N$
1490 IF N$="A" THEN FILE=128:GOTO 1690
1500 IF N$"D" THEN GRAPHICS 0:GOTO 1070
1510 ON ERROR 1540
1520 FILE=VAL(N$)
1530 GOTO 1560
1540 PRINT "ERROR. RE-ENTER."
1550 RESUME 1420
1560 REM --- PROCESS. CHECK.
1570 IFFILE>FNUM OR FILE<0 OR FILE<>INT(FILE) THEN PRINT "ERROR.":GOTO 1420
1580 REM SINGLE ENTRY HANDLER
1590 PRINT "TRACING=='SELECT' TO ABORT."
1600 SS=INFO(FILE,2) ! STARTING SECT
1610 SC=INFO(FILE,1) ! SECTOR COUNT
1620 RSECTOR=SS
1630 REM GO TRACE IT ...ARROR=RETURN
1640 GOSUB 1850
1650 IF ARROR=1 THEN PRINT "***BAD FILE***"
1660 IF ARROR=1 THEN DEAD(FILE)=1 ELSE DEAD(FILE)=-1
1670 GOSUB 2110 ! MARK FILE NAME
1680 GOTO 1350
1690 REM ALL ENTRIES HANDLER
1700 FOR FILE=1 TO FNUM
1710 CLS
1720 PRINT "TRACING FILE #";FILE
1730 IF LP=1 THEN PRINT #3,"TRACING FILE #";FILE
1740 PRINT "PRESS 'SELECT' TO ABORT."
1750 SS=INFO(FILE,2) ! STARTING SECT
1760 SC=INFO(FILE,1) ! SECTOR COUNT
1770 RSECTOR=SS
1780 GOSUB 1850
1790 IF ARROR=1 THEN PRINT "***BAD FILE***" **"
1800 IF ARROR=1 THEN DEAD(FILE)=1 ELSE DEAD(FILE)=-1
1810 GOSUB 2110
1820 NEXT FILE
1830 PRINT "COMPLETED."
1840 GOTO 1350
1850 REM TRACING SUBROUTINE
1860 ARROR=0
1870 PRINT
1880 PRINT "---SEC CNT=";SC;" READING SEC ";RSECTOR
1890 POKE &D01F,0 ' CLEAR BUTTONS
1900 GOSUB 70 'READ
1910 REM CHECK BUTTONS
1920 IF PEEK(&D01F)<>7 THEN RETURN
1930 IF DSTATS=1 THEN 1960
1940 PRINT "***ERROR. DSTATS=;DSTATS;" ON SECTOR ";RSECTOR
1950 GOTO 1900 !RETRY
1960 IF SC=0 THEN RETURN
1970 SC=SC-1 ! DECR S COUNT
1980 REM GET FORWARD POINTER + FNUM
1990 FWD=(PEEK(BUFF+125) AND 3)*256
2000 FWD=FWD+PEEK(BUFF+126)
2010 FN=INT((PEEK(BUFF+125) AND &FC)/4) ! WENCE, SWIFTED 2 BITS RIGHT
2020 FN=FN+1 ! (DISK STARTS AT 0)
2030 PRINT "---DATA: FILE NUM=";FN;" FWD SEC =";FWD
2040 IF FN<>FILE THEN PRINT "FILE NUMBER ERROR.":ARROR=1
2050 IF FWD>720 OR FWD<0 THEN PRINT "FORWARD PTR OUT OF RANGE,ABORT.:ARROR=1:RETURN
2060 IF FWD=0 THEN 2090
2070 RSECTOR=FWD
2080 GOTO 1870
2090 PRINT "COMPLETED."
2100 RETURN
2110 REM MARK A FILE AS BAD IN NAME.
2120 REM SET HIGH BIT SO IT SHOWS
2130 REM UP IN INVERSE VIDEO..
2140 IF ARROR=0 THEN RETURN
2150 Z$=NIME$(FILE)
2160 NIME$(FILE)=""
2170 FOR Z=1 TO LEN(Z$)
2180 NIME$(FILE)=NIME$(FILE)+CHR$((ASC(MID$(Z$,Z,1)) OR &80)) !SET HIGH BIT
2190 NEXT Z
2200 RETURN

As a side note, a sector copier is sometimes advertised as a "nibble copier" or "byte copier." This is an Apple term, and refers to individual bytes being read off the disk. It is completely inaccurate when applied to the Atari; all Atari disk I/O is in 128-byte blocks.

A sector copier, as you have guessed, just issues a read of all the possible sectors, then writes them.

Sector Copying

The program is simple to write now that you understand about sector I/O and how to call up DSKINV. It is just a matter of reading in 720 sectors and writing out 720 sectors. As I said, about 20 lines of Basic code are required. And now you understand why, as I mentioned last month, selling such a program, at the prices currently being charged, is such a ripoff.

But ...how can a disk be protected against this sort of thing? Fortunately, there is a good way. Several software manufacturers hit on the same idea at the same time: bad sectoring. Let's assume we have a special disk, with some sectors on it that have never been formatted, as with the FORMAT DISK command. Most of them are OK; just a few are bad, as if there were holes in the disk. Now when the disk is told to read these sectors, it can't find them, for they were never formatted. So it returns an ERROR 144, instead of a normal return (1). Then, in the software on the disk, that sector is called, and if an ERROR 144 doesn't result, the program quits.

Let's say the program is then copied to another disk by an average sector copier. The "bad" spots on the disks will not copy; they will return ERROR 144, but on the destination disk, there will still be formatting information on those sectors. This is because the average Atari user cannot create a disk with bad spots in it; the FORMAT command is handled completely inside the disk by the disk 6507 microprocessor. The program will find that where it expected a bad sector, it now finds a good sector. Hence the program knows it is not residing on the original disk, and can quit.

The only way to defeat this scheme is to disassemble code and remove the sector check, and even if the author is slightly clever, it will take so long to find the sector checking routine that the program will be outdated by the time it is finally made copyable. Besides, consider what a person's time is worth compared to the cost of a program; if it takes two weeks to break a copy protection scheme, isn't it a better idea just to buy the program?

This bad-sectoring scheme works quite well in preventing a sector copier from being useful. Sure, a copy of the disk may be made, but without the bad spots on the disk, it will never run. And your average user cannot create these bad spots, for only access to the disk controller program allows that, and only very advanced users can accomplish this.

Atari currently uses this bad sector scheme in several of their disk-based programs. Jawbreakers uses the same scheme (hence the disk retry, or "snaaark," sound when you first boot it up). The Wizard and The Princess use it also. In other words, the move is toward this sort of protection.

Software manufacturers can probably figure out a way to write some bad sectors onto a disk. For instance, what popular personal computer gives you complete control over the disk read/write process? There are other ways, too.

Another manufacturer doesn't bother copy protecting his disks at all. However, a small module must be plugged into the front joystick port before the program will run. The module probably contains some sort of ROM-type circuit which is then read by the program through the PIA chip (joystick ports).

Well, we have covered a great deal, from piracy philosophy to sector chaining in these two columns. I hope you have enjoyed it and have learned something about the Atari disk, and how it really works. I also hope I have described some effective protection schemes for software manufacturers.

Direct disk I/O is a very powerful tool for Atari users; use it to make your programs faster and more efficient.

Table of Contents
Previous Section: Atari Diskfile Tutorial Part II
Next Section: Using Disks With Atari Basic