Direct Atari Disk Access
Andrew Lieberman
Here are three programs that make disk access easier, display the contents of any sector on disk, and allow you to save screen displays to disk. Caution: these techniques write directly to disk. Be certain that you fully understand how to use these methods or you risk damaging existing disk files. The program opens the door to many interesting and valuable applications. And it's only 67 bytes long.
Even with a fast-formatted disk, the Atari disk drive is slow for many applications if BASIC commands like INPUT, PRINT, PUT, and GET are used. With the machine language subroutine in this article, you can transfer the contents of a specified area of memory to disk, and vice versa, quickly and easily, eliminating the need for the slower BASIC commands.
Program 1 is the source code for the program. Type in the program on your Assembler/Editor, assemble it, and save it with "SAVE#D:SECRAM. OBJ < 601,643". If you do not have the Assembler/Editor, use Program 2. Type in the program, save it, run it, go to DOS, and use option K, binary save, by typing: D:SECRAM.OBJ < 601,643″. It may be a good idea to lock the file.
To use this subroutine in a BASIC program, just add Program 2 to the BASIC program. Be sure the DATA has been put into memory before the routine is used; otherwise, you will crash the system. To call the routine, simply type: I = USR (1537,RAM,SECTOR,NUMSEC,DCOMD).
"I" can be any variable; RAM is the starting memory location; SECTOR is the first sector to be read from or written to. Each disk has 720 sectors, numbered from 1 to 720. The computer fills these sectors starting with 1 and works up, so you should plan to use sectors from 650 to 720 depending upon how many you need.
These sectors are not protected; if the disk starts getting full, your information may be overwritten. Program 3, which is described later, will be a help in preserving your data. NUMSEC is the number of sectors to be copied. There are 128 bytes to a sector and eight sectors to a kilobyte. DCOMD refers to read or write. An 82 here means read from disk to RAM, and 87 means copy memory to disk.
Let's look at an example of all this. Suppose you wanted to copy a modified character set to disk. Suppose further that your character set is located in memory locations 30720 through 31743 and you wanted it stored starting at sector 700. You would first have to calculate that you need eight sectors for the 1024 bytes of character set. Then simply type I = USR(1537,30720,700,8,87). If you did not understand this example, go back and look at what each number means; it should then be clear.
Saving Data And Graphics Displays
There are many applications for this program. Program 3 will display the contents of any sector on a disk. Another application that you are sure to find useful is saving screen displays to disk for quick recovery from within a program. Suppose you wanted to save your current Graphics 0 screen to disk. Simply type: I = USR(1537,PEEK(88) + PEEK(89)*256,680,8,87). Clear the screen and then type: I = USR(1537,PEEK(88) + PEEK(89)* 256,680,8,82).
Voilà! After eight beeps you recover your old screen. If you have a customized display list, you may want to save it also by using: PEEK(560) + PEEK(561)*256 instead of PEEK(88) + PEEK(89)* 256.
You should also find that this program works well when saving and loading character sets and player/missile data. The program should be used in any situation in which the contents of any area of memory should be the same every time, like a character set or a graphics display.
Program 3 is a simple program that copies the contents of a disk sector into a string and then prints the string on the screen. RETURNS are printed as "(RET)", and other editing characters are printed as their graphics symbols, i.e., with an ESCape printed first. This is very useful for finding free space on a disk for saving DATA. If, for example, you wanted to check sectors 700 to 710 to make sure they are empty, just RUN the program, start with 700, then use the right arrow to see what is on 701, etc.
A whole string of hearts (CHR$(0)) indicates an empty sector. Anything else means there is DATA on that sector. This program may also be used to modify DOS and other programs that cannot normally be modified. Look at sector 43 of any DOS II disk. It should be the top of the menu. If it isn't, find the correct sector. Now, BREAK the program and type "PRINT A$". It will be the same as what appeared on the screen except RETURNs and CLEAR SCREENs will be printed.
Try making some changes in the middle of the string. For example, type: A$(71,87) = "A. DISK MENU ". Then save this modified string back to the disk by typing: I = USR(1537, ADR(A$), 43, 1, 87). Now go to DOS, and if all went correctly your change has been made.
Now that you know how to use this program, you probably want to know how it works. Lines 1 through 40 should be fairly obvious. Line 50 clears the keyboard of any key pressed earlier. Line 60 reads the keyboard. A 7 means the right arrow was hit, so the variable SEC is incremented. Line 70 checks for a left arrow in the same way. If no key has been pressed, the program jumps back to line 60 to wait for a key to be pressed. If a key other than left or right arrow was pressed, line 90 accepts the input.
Lines 100 and 110 check to make sure the sector is within the legal limit. Line 130 loads the requested sector into the RAM area of string A$. Instead of just printing the string to the screen, each character is printed one at a time. Before the character is printed, it is checked for being a RETURN (CHR$(155)); if it is a RETURN, "(RET)" is printed instead. Furthermore, an ESCape is printed before each character. If these precautions were not taken, many sectors would clear the screen and do other strange, undesirable things when printed. The extra spaces are printed at the end of the sector to clear away any loose ends left over from the last sector.
Easy Programming
Now for the good stuff: how does this program work in only 67 bytes? The real key to this program is the Operating System subroutine at $E453. Each time it is JSRed to, it takes the information in the lower page three memory locations and processes it, and it does that very quickly. There are many handy subroutines in the Operating System for things like print to the screen, plot, drawto, set up VBLANK, change graphics modes, etc. For more information on how to use the graphics subroutines, get the February 1982 issue of COMPUTE! and look at "Insight: Atari," page 77. These subroutines can make life very easy on a programmer.
You should be able to interpret how the assembly language program works by looking at the comments in the source code. The only part that is likely to be unfamiliar to you is the first part. The first number in the USR command is the starting memory location. The other numbers are all placed on the stack as shown in the table. Lines 260 to 390 pull the values off the stack and put them into the memory locations in which they belong.
There is one other memory location that you may find useful: $303 (decimal 771) shows the status after the most recent operation. A 1 means everything is all right. Any other number is an error code. Errors are usually the result of trying to read a bad, or nonexistent, sector.
|
Program 1.
0100 ;********************************** 0110 ;**A routine for storing RAM on ** 0120 ;**a disk or for reading it back ** 0130 ;**by ANDREW LIEBERMAN 7/10/82 ** 0140 ;********************************** 0150 NUMSEC = $600 ;Number of sectors still to be done 0160 DUNIT = $301 ;Which drive?(1-4) 0170 DCOMD = $302 ;$52 = Read, $57 = Write 0180 DBUFL0 = $304 ;Pointer for Lo byte of RAM 0190 DBUFHI = $305 ;Pointer for Hi byte of RAM 0200 DAUXLO = $30A ;Pointer for Lo byte of sector 0210 DAUXHI = $30B ;Pointer for Hi byte of sector 0220 * = $601 0230 ;The USR command places data on the stack 0240 ;This part of the program pulls the data off and puts it in the 0250 ;proper memory locations 0260 PLA ;We don't care about this 0270 PLA 0280 STA DBUFHI 0290 PLA 0300 STA DBUFLO 0310 PLA 0320 STA DAUXHI 0330 PLA 0340 STA DAUXLO 0350 PLA ;This is assumed to be 0 0360 PLA0370 STA NUMSEC 0380 PLA ; This is assumed to be 0 0390 PLA 0400 STA DCOMD 0410 LDA #$01 ;Assume drive 1 0420 STA DUNIT 0430 LOOP DEC NUMSEC ;One less sector to be done 0440 BMI END ; If minus result, last sector was 0, so branch to END 0450 JSR $E453 ; This is the O.S. subroutine that does all the work 0460 CLC 0470 INC DAUXLO ; Increment sector pointer 0480 BCC SKIP1 ; Check for carry 0490 INC DAUXHI ; There was a carry so hi byte is incremented 0500 SKIP1 LDA DBUFLO ; Since each sector is $80 bytes long, the 0510 CLC ; RAM pointer, DBUF, must be incremented by $80 0520 ADC #$80 ; Add $80 to lo byte 0530 BVC SKIP2 ; If it didn't overflow everything's O.K. 0540 INC DBUFHI ; Lo byte overflowed, so increment hi byte 0550 SKIP2 STA DBUFLO ; Don't forget to store the lo byte 0560 CLC ; A Jump done this way makes the program relocatable in RAM 0570 BCC LOOP 0580 END RTS ; A11 done
Program 2.
1 GOSUB 31000 30999 END 31000 RESTORE 31010 : FOR I = 1537 TO 1603 : READ J : POKE I, J : NEXT I : RETURN 31010 DATA 104, 104, 141, 5, 3, 104, 141, 4, 3, 104, 141, 11, 3, 104, 141, 10, 3, 104, 104, 141, 0, 6, 104, 104, 141, 2, 3, 169, 1, 141, 1, 3, 206 31020 DATA 0, 6, 48, 29, 32, 83, 228, 24, 238, 10, 3, 144, 3, 238, 11, 3, 173, 4, 3, 24, 105, 128, 80, 3, 238, 5, 3, 141, 4, 3, 24, 144, 222, 96
Program 3.
1 REM A program to examine disk sectors 5 GOSUB 31000 10 DIM A$(128) : A$(128) = " " 20 GRAPHICS 0 : ? "Type sector number, or use right arrow for next sector, left arrow for last sector." 30 POSITION 2, 5 : ? "{11 M{CLEAR}" 40 POSITION 2, 12 : ? "{4 DEL - LINE {CLEAR} WHAT SECTOR?"; 50 POKE 764, 255 60 I = PEEK(764) : IF I = 7 THEN SEC = SEC + 1 : GOTO 100 70 IF I = 6 THEN SEC = SEC - 1 : GOTO 100 80 IF I = 255 THEN 60 90 TRAP 40 : INPUT SEC 100 IF SEC < 1 THEN SEC = 1 110 IF SEC > 720 THEN SEC = 720 120 POSITION 2, 4 : ? "SECTOR #" ; SEC ;"" 130 I = USR (1537, ADR (A$), SEC, 1, 82) : POSITION 2, 6 : FOR I = 1 TO 128 : J = ASC (A$(I, I)) 140 IF J = 155 THEN ? "(RET)" ; : GOTO 160 150 ? CHR$(27) ; CHR$(J) ; 160 NEXT I : ? "{28 SPACES}" : GOTO 40 : REM about 30 spaces 30999 END 31000 RESTORE 31010 : FOR I = 1537 TO 160 3 : READ J : POKE I, J : NEXT I : RETURN 31010 DATA 104, 104, 141, 5, 3, 104, 141, 4, 3, 104, 141, 11, 3, 104, 141, 10, 3, 104, 104, 141, 0, 6, 104, 104, 141, 2, 3, 16, 9, 1, 141, 1, 3, 206 31020 DATA 0, 6, 48, 29, 32, 83, 228, 24, 238, 10, 3, 144, 3, 238, 11, 3, 173, 4, 3, 24, 105, 128, 80, 3, 238, 5, 3, 141, 4, 3, 2, 4, 144, 222, 96