The Resident Disk Handler
Frank Kastenholz
This technical article explores, with commentary and examples, the use of the operating system's Resident Disk Handler for accessing disk sectors … without DOS. If you're interested in learning more about the Atari DOS itself, see Inside Atari DOS from COMPUTE! Books.
Would you like to be able to hide data on your disks without having DOS signal its presence with a file name and directory entry? Would you like to be able to access any sector on a disk, independent of DOS? You could check the disks that you are using for bad sectors without destroying what's on the disk. You could create your own unique disk format to suit your own unique needs.
The keys that open the door to this wonderland of direct access storage are the Resident Disk Handler and the Device Control Block.
The Resident Disk Handler is a section of code that exists in the ROMS of your Atari computer (both the 400 and the 800), and the Device Control Block is a section of RAM that contains the various parameters which control the actions of the Resident Disk Handler. The Resident Disk Handler is capable of performing four different operations: Get Sector, Put Sector with Verify, Status Request, and Format Disk. For the purposes of disk access only the first two operations are important, and I shall discuss only those two operations.
The Get Sector operation will retrieve any one sector from the disk and place it in any 128 byte block of RAM. The Put Sector with Verify operation will take 128 bytes of data anywhere from memory (RAM or ROM) and write that data to any sector on the disk. It will then check to make sure that the data were written correctly.
And how, I hear you cry, does one make use of this miracle of modern science? To use the Resident Disk Handler is an extremely simple task. It just sounds hard. At the end of this article I have included a program that should adequately demonstrate the basics of using the Resident Disk Handler.
The key to using the Resident Disk Handler is a chunk of memory called the Device Control Block. The Device Control Block is similar in function to the IOCBs that are used in BASIC. The Device Control Block is 11 bytes long and begins at location $0300 (768 decimal). (All hexadecimal numbers are preceded by a $ and followed by their decimal equivalent in parentheses.) For our purposes only seven bytes are needed, the other four being used internally by the Resident Disk Handler. The bytes we shall use are the device unit number byte, the command number byte, the status byte, the two buffer address bytes, and the two sector number bytes.
The device unit number byte is located at location $0301 (769) and contains the unit number of the disk drive you wish to access (1, 2, 3, or 4).
The command number byte is at location $0302 (770) and contains the command number of the operation to be performed. The command number for the Get Sector operation is $52 (82), and the number for the Put Sector with Verify operation is $57 (87).
The status byte is the only byte that you do not have to put something into before you use the Resident Disk Handler. It sets up the status byte to reflect the success (or lack of it) of the operation that was just attempted. The status byte is at location $0303 (771) and may have one of seven values. A l in this byte indicates that the operation was completed successfully. The other six values indicate an error occurred during the operation. The values are $8A(138), $8B(139), $8C(140), $8E(142), $8F(143), and $90(144). The meaning of these status codes is the same as for the error codes of the same number in BASIC.
The two buffer address bytes are at locations $0304 (772) for the low order byte of the address and location $0305 (773) for the high order byte of the address. These two bytes contain the address in memory of the source of the data, for a Put Sector with Verify, or the destination of the data, for a Get Sector. To set these bytes up in BASIC you must divide the address that is to be used by 256 and place the remainder in byte 772 and the quotient in byte 773.
The two sector number bytes contain the number of the sector on the disk that is to be accessed. This number may be any number from 1 to 720 inclusive. If you were not the trusting type you might say "Sector 720 is not addressable – it says so in the DOS manual." But since you are the trusting type, you will not say that, and I will not be forced to reply "True, but we are not using DOS here and sector 720 is addressable when using the Resident Disk Handler!"
Once the Device Control Block has been properly set up you have to call the Resident Disk Handler so it can do its work. If you are programming in machine language, this is a trivial job. You merely do a JSR $E453. It will do its work and then do a RTS when it is done. Nothing could be simpler. If you are a BASIC programmer it is slightly harder to call the Resident Disk Handler. You must load the following assembly code into RAM and then do a USR to it.
PROGRAM 1.
Object Code HEX | Decimal | Source Code | Comments |
$68 | 104 | PLA | The Extra PLA required by USR | .
$20$53$E4 | 3283228 | JSR$E453 | Call the Resident Disk Handler | .
$60 | 96 | RTS | Return to BASIC | .
The BASIC code in Program 2 will load the assembler code of Program 1 into RAM beginning at location 1536. To call the Resident Disk Handler you would then do a X = USR(1536).
PROGRAM 2.
10 DATA 104, 32, 83, 228, 96
20 FOR 1 = 1536 TO 1540
30 READ J : POKE I, J
40 NEXT I
The code in Program 3 is a short BASIC program that will show you how to use the Resident Disk Handler. The program will either put or get one sector of data. If you get a sector of data, the program will print out that data as character data (if a byte is a 65, it will print A). If you are going to put a sector of data to the disk, the program will ask you to enter the data to put in character form. Comments on the program follow the listing.
PROGRAM 3.
10 DATA 104, 32, 83, 228, 96
20 FOR 1 = 1536 TO 1540
30 READ J : POKE I, J
40 NEXT I
50 DIM A$(128)
60 PRINT "GET OR PUT"; : INPUT A$
70 IF A$ = "GET" THEN 100
80 IF A$ = "PUT" THEN 200
90 GOTO 60
100 LET COMMAND = 82
110 GOSUB 1000
120 STAT = PEEK(771)
130 IF STAT = 1 THEN 160
140 PRINT "ERROR #";STAT;" ON GET"
150 GOTO 60
160 FOR I = 0 TO 127
170 A$(I + 1, I + 1) = CHR$(PEEK(1664 + I)) : NEXT I
180 PRINT A$
190 GOTO 60
200 FOR I = 1 TO 128 : A$(I,I) = " " : NEXT I
210 PRINT "ENTER DATA"; : INPUT A$
220 FOR I = 0 TO LEN(A$) -1
230 POKE 1664 + 1, ASC(A$(I + 1, I + 1)) : NEXT I
240 FOR I = LEN(A$) TO 127
250 POKE 1664 + I, 0 : NEXT I : LET COMMAND = 87
260 GOSUB 1000
270 STAT = PEEK(771)
280 IF STAT = 1 THEN PRINT "OPERATION COMPLETE" : GOTO 60
290 PRINT "ERROR #";STAT; "ON PUT" : GOTO 60
1000 REM DISK ACCESS ROUTINE FOLLOWS
1010 PRINT "SECTOR NUMBER TO ACCESS"; : INPUT SNUM
1020 POKE 779, INT(SNUM/256) : POKE 778, INT((SNUM/256-INT(SNUM/256)) * 256)
1030 POKE 769, 1
1040 POKE 772, 128 : POKE 773, 6
1050 POKE 770, COMMAND
1060 X = USR(1536)
1070 RETURN
Lines 10–40 load the short assembler routine needed to call the Resident Disk Handler into memory.
Line 50 dimensions A$ as a text string so we can use it to store data.
Lines 60–90 input an operation from the keyboard, determine which operation it is, and jump to the appropriate routine to handle that operation.
Lines 100-190 are the Get operation.
Line 100 sets the command as Get Sector.
Line 110 calls the disk access subroutine.
Line 120 sets STAT equal to the status of the operation.
Line 130 determines if the operation was successful. If it was it goes to
Line 160.
Lines 140 and 150 print an error message and then start another operation.
Lines 160, 170, and 180 get the input data from the input buffer, put them into A$ and print them.
Line 190 starts another operation.
Lines 200-290 are the Put Sector Lines.
Line 200 clears A$ out.
Line 210 enters the data from the keyboard.
Lines 220 and 230 put the output data into the output buffer.
Lines 240 and 250 fill any bytes remaining after the last data byte and the 128th byte of the buffer with zeros. Line 240 also sets the command to the Put Sector with Verify operation.
Line 260 calls the disk access subroutine.
Line 270 sets STAT equal to the status of the operation.
Line 280 determines if the operation was successful or not and if it was, prints "OPERATION COMPLETE" and starts another operation.
Line 290 prints an error message and then starts another operation.
Lines 1000-1070 set up the Device Control Block and then call the Resident Disk Handler.
Line 1010 inputs the sector number to access.
Line 1020 puts the sector number into the sector number bytes. The first POKE statement takes the quotient of the sector number divided by 256 and puts it into the high byte of the two sector number bytes.
The second POKE statement takes the remainder of the sector number divided by 256 and puts it into the low byte of the sector number bytes.
Line 1030 sets the unit number to 1.
Line 1040 sets the two buffer address bytes to 1664.
Line 1050 sets up the Command Number Byte.
Line 1060 calls the short assembler routine which then calls the Resident Disk Handler.
Line 1070 returns to the main routine.
This program is tutorial, intended to help you understand the Resident Disk Handler, what it is and how to use it. If you wish to use the Resident Disk Handler, you would want to make some improvements to the subroutine that I have presented here. First you would pass the sector number from the main program instead of entering it from the keyboard. Other improvements would include combining some of the statements to make the program shorter, or converting the program into machine language. One improvement that I have found particularly valuable is to have the subroutine repeat an error several times if an error occurs. Since most disk errors are recoverable if the operation is retried, this has the effect of reducing the number of disk errors I get to almost nothing.
Now that you have finished reading this article, I can hear you grumbling "This just looks like a hard way of doing a POINT!" But it isn't! The Resident Disk Handler allows you to access any sector on the disk. The POINT command in BASIC allows you to access any sector in a particular file. If you POINT to sector 3, you will access sector 3 of the file, and not absolute sector 3. The Resident Disk Handler will get you absolute sector 3. It can access a sector that is un-allocated, POINT can't. It can access the sectors that DOS uses, POINT can't.
While using these methods takes more work than just OPENing, PRINTing, and CLOSEing a file in BASIC, the added flexibility more than compensates for the extra work.
Return to Table of Contents | Previous Section | Next Section