|
Volume Number: | 3 | |
Issue Number: | 6 | |
Column Tag: | Advanced Mac'ing |
SCSI Formatter Project, Part II
By Tim Standing, University of Calif. at SF
Where are we going
This article is the second in a series on how to construct and format a SCSI hard disk. The first (MacTutor, February 1987) described in detail how to assemble a SCSI hard disk. This article describes a program called Format that performs the low level formatting of a SCSI hard disk. I have divided this article into three parts. In the first section, I cover some of the theory regarding the SCSI protocol. Included in this section is a description of how Apple’s implementation of the protocol has made writing SCSI software much easier. The second section describes in detail how a SCSI command is sent to a SCSI hard disk and how data transfer is accomplished. I use U2 Formatter as an example. In the last section, I give step by step instructions on how to use this program including comments as to what may go wrong at each step and how to fix it.
During low level formatting, the Macintosh sends information to the controller card describing the disk drive. This information includes both the physical characteristics (e.g. number or cylinders and number of heads) and the type of sectors that are to be used (sector size and interleaving). The controller card uses this information to write track markings on the disk surface so that information can be stored there. It also stores this information on a dedicated track on the disk surface so that it can be read into the controller card RAM when the power is turned on. Once the low level formatting has been completed, a program such as Apple’s Generic SCSI Installer (available from Apple Software Support) can be used to construct a files system, install a driver and write the boot blocks. Although the drive can store information after low level formatting, it cannot be recognized as a volume by the Macintosh until a file system has been constructed on it. (A SCSI driver will be the subject of a future article in this series)
The program Format should work with any hard disk that uses the Adaptec ACB-4000A controller card or the Adaptec ACB-4070 which is their RLL controller card. In addition, it should work with any of the Seagate disk drives that have built in controllers. These drives all have an “N” at the end of the model number (e.g. “ST225N”). I have referred to the documentation for a Seagate ST225N while writing this program.
I have developed this program using a 40 megabyte Seagate (ST4051) hard disk connected to an Adaptec ACB-4000A controller card. I have also tested it with a 155 megabyte CDC Wren III (see side bar article on the Wren drive.) It should work with other drives and controller cards, but I cannot guarantee it. There are subtle differences in the way different manufactures implement the SCSI commands that make writing a universal formatting program difficult.
The Wren III is the fastest drive I have used on the Macintosh. Using the time needed to assemble and link the Format program as a benchmark, the Wren III is more than 40% faster than a Data Frame XP-40. Disk Timer II rates the 155 Mbyte drive as 118 for write, 108 for read and 9 for access time with an interleave of 3:1. The average access time on the drive is 15 msec. That’s more than twice as fast as any other drive available for the Mac. In addition, the drive can transfer data at 1.25 Mbytes/second, which is as fast as the Mac II can read it in. The Wren III comes in three sizes: 86, 121, and 155 Mbytes which range in price from $1510 to $1710. They are available from Arrow Electronics 521 Weddell Drive, Sunnyvale, CA 94089, (800-325-3329).
The only problem with the Wren III is that the biege colored Macintosh Plus will not boot properly from them. If you buy a platinum Macintosh Plus, or get a copy of the ROMs from one, the Wren III will work fine. The EPROM’s you need to do this are 27C512, 150 nsec. They are made by Atmel and are available from Insight Electronics, San Diego, (619)-587-0471. (The ROMS in the platinum Mac Plus have a modified boot code that does a reset on power on and keeps polling until a hard disk responds. In the standard Mac Plus, they did a reset and poll in a loop of 1 second, which is shorter than the recovery tme of the Wren III to respond to a reset command. Hence, the Wren III will never come up! The new ROMS re-poll the driver when they get a drive not ready signal rather than resetting every second. This is not strict SCSI protocol and the people at CDC pointed this out to Apple, which then changed it’s ROMS. To get the ROMS, you need a friend in an Apple dealership who can work with you since the dealers are not allowed to swap the ROMS unless you fry your board.)
MPW
Before I get into the details of SCSI commands, I will say a few things about MPW. I switched from the MDS system to MPW because I got tired of waiting for a good linker to show up for MDS. MPW is powerful and allows you to develop in PASCAL, C or assembly language and link the results together. More importantly, the MPW environment provides a lot of tools that make development easier. In this section, I will describe both a modification that I have made to the “Make utility” to make it entirely automatic and a few bugs that I have found in the assembler that you should look out for. This program was developed using MPW version 1.0.1. I have included all the files that you will need for this project including the Makefile which establishes the dependency rules used for automatic assembly and linking. You will notice that the dependencies in the Makefile specify that the program is relinked anytime after the Rez tool is used. This is because Rez tends to trash the code resources in the program being built.
I have modified the Make command so that it automatically executes its output. By doing this, you can build your application by simply typing the command “Make”. The modification requires that you create a new folder inside the “MPW” folder called “Scratch”. The enclosed file “MakeIt” must then be placed in the “Tools” folder. Lastly, the line “Alias Make Makeit” must be placed in the UserStartup file.
Although I have not uncovered any bugs in the linker, the MPW assembler does not always create the instruction that you specify! On three occasions it assembled instructions incorrectly. In all three instances, I got the assembler to correctly assemble the instructions by either changing the instruction order or adding a label to the line above. If it looks like something funny is happening with your code, check that the executing code is what you think it is by using the disassemble feature in a debugger like TMON. Lets hope the 2.0 version will fix this bug as an assembler that lies is not much good for anything.
Lastly, I use the “dump” assembly directive to load symbols into a file. You will see that all the files like “Traps.d” are loaded in at the beginning of each source file. To use this you must place the following lines:
Dump ‘FileName.d’ end
at the end of each of the files in the AIncludes folder and then assemble these files. This will create a file called “Filename.d” for each file which is a lot like the packed symbol files in MDS and will make your assembly runs a lot faster.
SCSI Bus Phases
There are seven different states that the SCSI bus can be in. These are called Bus Phases and are determined by which device has control of the bus and what information is being transfered on the bus. The bus phases that the SCSI bus goes through during a SCSI command are: arbitration, selection, command, data, status, message and lastly bus free. During the arbitration phase, the Mac (the initiator) tries to gain control of the bus. If it is successful, the initiator tries to select your hard disk (target device) during a phase called the selection phase. Once a target device has been selected, the target determines what information will be transferred down the bus and when. The target also determines which direction the information travels down the bus. The target device then requests a SCSI command from the initiator in the command phase. If the SCSI command that was transfered during the command phase indicated that there was data to be transferred between the target and the initiator, the bus will then enter the Data phase. An example of this would be a read or write command where blocks of data are transfered down the bus. Once the target has transfered the number of bytes specified by the command, the bus enters the status phase. Here the target sends a single byte of information to the initiator. This status byte is an error code which describes any errors that may have occured. The status byte is cleared if the command was completed successfully. The message phase is used to transfer additional information between the target and the initiator. The Adaptec ACB-4000A always sends a clear byte in the message phase which is the command complete message. I have therefore ignored the message byte in my program. At the end of the message phase, the target determines that there is no more information to transfer down the bus and disconnects from the initiator. The bus then enters the Bus Free Phase where no device has control of the bus.
During all of these phases, timing is critical. After each bus phase change, there is a required amount of time necessary to let the signal settle. There is also a maximum time delay after which the target device will time out. There are also timing specifications for the arbitration phase that describe how long an initiator device should wait to determine if he has gained control of the bus. Apple decided to use the NCR 5380 chip to connect to the SCSI bus. This has several advantages including the fact that the chip performs many SCSI bus functions in hardware. It arbitrates for use of the bus, including performing retries, can select a target, send commands and transfer data. All of these actions are done in hardware. The timing is determined on the chip using a gate delay, rather than a clock pulse, which allows Apple to use the same chip in any computer regardless of the clock speed. (Of course, a vendor change or manufacturing quirk could change the average gate delay time, rendering some Macs less reliable than others.)
This SCSI code is collectively known as the SCSI Manager and includes traps for all types of SCSI operations. The _SCSIGet trap arbitrates for control of the SCSI bus. The _SCSISelect trap selects the target device. The _SCSICmd trap sends the command in the Command Phase. It includes a count for the size of the command which is either 6 or 10 bytes. The format and size of the various SCSI commands are described in the next section. Data is transfered by sending the correct SCSI read or write command using the _SCSIcmd trap. The Mac must then be instructed to transfer the data using one of the SCSI read or write traps. These traps use a pseudo program that describes the number of bytes to be transfered and where they are to be transfered to or from. I will discuss pseudo programs in detail later.
Lastly, the _SCSIComplete trap instructs the Mac to wait for the target to complete the command. It includes the number of ticks to wait for completion and returns the status byte and message byte. I have found that the Status byte is often returned with a value of 2. This should signify that the drive was unable to finish seeking to the desired track. A status byte equal to 2 is returned even for commands which cannot return this error code, or for commands which completed successfully (status byte should equal 0). Accurate status byte reporting was obviously not a prioirty for the software engineer at Apple who wrote the SCSI manager! This bug is in the Mac Plus, but I haven’t checked if it is still in the Platinum Mac Plus ROMS. Obviously, the Mac Plus ROMS have been changed considerably in the Platinum Macs and it is inexcusable that Apple does not at least offer the opportunity for Mac Plus owners to purchase new ROM sets.
SCSI Commands
SCSI commands are divided into two types: class 0 commands which are 6 bytes long and class 1 commands which are 10 bytes long. The data structure that includes all the command information (6 or 10 bytes) is called a command descriptor block (CDB). SCSI commands often contain fields that are less than 1 byte long. For example, bytes 0 and 1 of all SCSI commands contain four fields: the class code, the OP code, the logical unit number (for devices that have more than one disk drive connected to a single controller card) and 5 bits of command specific information. The format of these four fields is the same for all SCSI commands used in this program.
Some commands require more information than there is room for in the command descriptor block. In these commands, the command descriptor block includes a count byte which tells the SCSI hard disk how many bytes it should request from the Macintosh. The information is then transfered in the same way as in a read or write command, using a pseudo program and a _SCSIRead or _SCSIWrite trap. An example of this is the Mode Select command which is sent just prior to a Format Unit command, when formatting a SCSI hard disk. This information is included in a 24 byte parameter list and includes information such as write pre-compensation cylinder, reduced write current cylinder, cylinder count and head count.
Pseudo Programs
So what is this pseudo program and why not use a real program instead? The Pseudo program (Apple refers to them as Transfer Instruction Blocks) is a short subroutine of interpreted instructions. The ANSI standard for SCSI specifies the OP codes for pseudo instructions and the parameters used with them. Why were interpreted instructions specified? By specifying a set of OP codes for data transfer subroutines, the transfer functions can be performed by an I/O channel controller. This dedicated hardware would have these OP codes as its instruction set and would reduce the load on the CPU during I/O. This is important for multitasking systems.
While Apple adheres to this part of the ANSI SCSI standard, they have not used hardware to perform the transfer instructions. Instead, all Macintoshs interpret these instructions. This adherence to the standard comes at a price; lower transfer speed. The alternative method of writing transfer subroutines is to use instructions that can be executed directly by the MC 68000. Some SCSI hard disk manufactures (e.g. Super Mac Technologies and Micha) have used this method. This allows them to use a faster transfer rate and therefore use a smaller interleave. The driver in the Data Frame intializer version 2.1 uses a block of 256 “move.b (A0),(A2)+” instructions for blind reads and a block of 256 “move.b (A2)+,(A1)” instructions for blind writes. Although this method of writing data routines is faster than that employed by Apple, it is very dependent on timing to work properly. Any change in the transfer rate of the drive or the CPU speed (e.g. as in a Macintosh II) would cripple this technique, and require a modification by the manufacturer.
The SCSI Manager section of IM volume IV includes a description of each of the pseudo instructions and their parameters. All instructions are 10 bytes long; a word for the pseudo instruction OP code followed by two long word parameters. For a read or write operation, we simply create a loop with a transfer instruction and a branch with a loop counter. I have heard that the code which interprets the SCSI pseudo instructions is not terribly robust, so it is better to not try and do anything too fancy with it.
You will notice that the pseudo program that I have put in the WriteDisk routine could have been written with two instructions rather than the three that I used, by using the transfer byte count as a parameter in the SCInc instruction. Instead, I placed 1 here and made a loop which transferes only 1 byte at a time. I did this solely to demonstrate how one constructs a loop in the pseudo program.
Examples of SCSI commands
The simplest example of a SCSI command is found in the SelAddrProc procedure. Here I send a Test Unit Ready command to see if the target device is ready. I then test the Status byte to see if the target device is connected and ready to be formatted.
More complex examples of SCSI commands can be found in the FormatProc procedure. This procedure sends two commands including a Mode Select command which must be sent imediately before formatting the disk. This command tells the controller card what type of hard disk is connected to it and how to format it. As I explain in the source code comments, the mode select command is followed by a call to WriteDisk which is a subroutine that writes to the hard disk using the _SCSI write trap and a pseudo program. I then use the Format Unit command to format the hard disk. This command includes the interleave value and is followed by data which describes where the disk media defects are found.
A Few comments about error handling
I have included in the error dialog box, not only the cause of the error but its most probable cause. This is not always the reason you got this error, but it is 90% of the time. I check for all the SCSI command errors that you are likely to get at any part of the program. If you are getting Phase errors, the most likely problem is that the timing loop in the ResetDisk procedure is too short. Try increasing the delay from 60 ticks to 120 or 180 ticks. This will give your controller more time to complete a reset. Selection errors are always cabling or termination problems.
Step-by-Step
1). Connect your hard disk to the Macintosh and turn both devices on. Make sure that the hard disk is correctly terminated and note what SCSI address it has. The drive’s address cannot be 7 as this is the address of the Macintosh. If you use DIP switches or jumpers to set the drive’s address, you are setting it in binary, with closed switches signifying 1 and open switches signifying 0. The switch that closes the connection between pins A and B on the Adaptec ACB-4000A is the low bit on the SCSI Address.
2). Open the Format program and choose Select Address from the SCSI menu. Choose the address that you have given the disk drive and then hit the select button. The program then goes and resets the SCSI bus and tries to select the device. (If it did work, you will not see an error message.) If you get a drive selection error, the drive is either the wrong address, is not correctly terminated or you have incorrectly attached one of the connectors on the ribbon cable.
3). Next choose the Enter Parameters from the SCSI menu. Enter all the values for your drive. If your drive does not need a parameter, use the default value as this will send a 0 in this field and will work properly. The CDC Wren III only needs to be told which interleave to use, so this is the only thing you need to enter in this dialog box. If you are uncertain of which value to use for the step rate, use the 25 msec value. All but the most ancient drives will work correctly with this value. To determine which interleave to use, you must use trial and error. The value that is most likely the best is 3:1. To determine the correct value, you must format your drive using different interleave values. Then install a driver and copy a large file or folder onto the disk. I use a 1.2 Mbyte system file for this purpose. Now determine the amount of time it takes to duplicate the file. Do this with each interleave value. The value that gives you the shortest time for duplicating the file is the one you should use.
4). Now choose Enter Defects from the SCSI menu. You can enter up to 60 defects. You should have received a list of defects when you purchased the drive. Some of the better drives contain this information in firmware on the controller card and you can skip this step. Drives of this type include the CDC Wren III and the Seagate “N” drives (e.g. the ST225N). Note that you must enter the defects in order of increasing cylinder number and you are not permited to have any defects on cylinder 0.
5). Next choose Format from the SCSI menu. You should see the drive activity light (that little red LED) light up on your drive and it will probably stay on for between 3 and 15 minutes (the Wren III takes 18 minutes and my Seagate ST4051 takes 2). Your drive is now being formatted. You should also be able to hear the head postioning motor or voice coil moving the head as it formats the drive. If the light stays on for more than 20 minutes, or if you cannot hear the head moving, something is probably wrong. This is not always the case as I cannot hear the head moving on the Wren III when it is formatting. Be patient the first time.
6). Now use a driver installer program to install a driver on your hard disk. You can use Apple’s Generic SCSI Installer for this purpose, but be forwarned that this driver has bugs and the data on your drive will eventually become corrupted and lost after about 20 hours of use. There are a few public domain programs that will also do this. Or you can wait for my next article!
My next MacTutor article will include a driver that works on all three Macintoshes (Mac Plus, Mac SE and Mac II). I will include subroutines that can be added to this program to allow you to install this driver.
Build you own Macintosh Hard Disk using the Control Data Wren III
Paul L. Derby, Control Data Corporation
P.O. Box 0,Minneapolis, MN 55420-2028
612-853-5986
With the introduction of the Macintosh Plus, connection of computer peripheral devices that attach to the Small Computer Standard Interface bus (SCSI connection) is fairly straightforward. Quite a few SCSI hard disk drives are available on the market with capacity and prices running from 20MB for around $600 to 172MB for around $6,000. There is also quite a variance in drive quality and reliability. If you are willing to put together a few pieces to make your own disk drive, you can have a top quality Wren III 156MB drive made by MPI, Inc. for around $2,000. (This is the same drive Sony, a disk manufacturer, chose to place in their commercial workstation hardware.)
Here are some of the technical specifications on the Wren III: 16.5 ms average seek time with a 10 Mbits/second data transfer rate. A SCSI controller is integral to the drive as well as termination resistors. Typical average seek time is 18.5ms with controller overhead. If the head is already positioned single track seek time is 6 ms. Seek time with full stroke of the read/write head is 42ms. It takes about 15 minutes to format the drive. Sector interleave is 1 to 1, although the speed of the Mac SCSI bus cannot keep up with the data transfer rate of the Wren. The SCSI controller takes care of transferring the data to the Mac at the speed the Mac can handle.
The Wren is very reliable with a service life rating of 30,000 hours (5 years).
If you want to put together you own Wren disk system, there are 4 pieces to acquire: (1) the actual disk unit with the integral controller, (2) a cabinet with power, fuse, fan, and mounting rails, (3) the connectors and cables to attach the drive to the Macintosh, and (4) software to format, install, and access the disk.
The disk is available from a number of electronics distribution companies such as Arrow Electronics, Kierulff Electronics, MTI, and Duccomun. The disk is an MPI Wren III (or a CDC Wren III), model number 94161-155 and sells for around $1850.
An enclosure for the disk that includes a 75 watt switching power supply, fuse, fan, on/off switch, power cord and mounting rails, plus the cut outs to accommodate the two SCSI connectors is available from Microware Inc. for $185.
Two cables are needed to connect the Wren to the Mac. One cable is needed to connect the controller on the Wren III to the connector on the rear of the enclosure. The other cable goes from the disk enclosure case to the Macintosh. Apple sells the second cable that goes from the DB25 connector on the rear of the Mac to the back panel of the disk enclosure. The first cable you build as follows:
Buy two industrial Amphenol 50 pin F57 connectors (or equivalent) and one 50 pin IDC connector. Use a bench vise to press the connectors onto 50 conductor flat ribbon cable. Thread the ribbon cable through the cutouts on the back panel of the disk enclosure before pressing the connectors onto the cable.
Assembly of the unit is very straightforward. Remove the power supply by unscrewing the two screws that secure it from the bottom of the enclosure. Slide the Wren into the hole in the front of the disk enclosure and secure it with four 6-32 screws and washers. Connect the ground wire from the power supply to the ground spade on the Wren. Plug the power supply cable into the Wren power socket. At this time it is a good idea to power up the drive and make sure the voltage from the power supply is within specification for the Wren. The 5 volt output should be between 4.75 and 5.25 volts with the drive running. If it is not within these voltage limits then you gently rotate the trim potentiometer located at the bottom center of the power supply. Set the 5 volt line as close to 5 volts as you can and then check the 12 volt side to make sure it is 12 volts ±5%. When both the 5 and 12 volt supplies are within tolerance, power down the drive and secure the power supply board. Install the cable between the Wren and the rear panel of the enclosure. Attach the Mac and you are ready to use software to prepare and use the drive.
There are 3 pieces of software needed to use the Wren on the Mac Plus. A disk format program is needed to format the disk into 512 byte sectors (See Tim Standing’s Format program in this issue). After formatting, an installer program is run to place the correct SCSI driver information on the disk. The last piece of software is a special file called an INIT file that is placed in the system folder on the floppy disk used to boot the Macintosh. This file loads the driver during boot time. On the Mac SE, the INIT file is not needed and the Wren drive can be booted directly. These pieces of software are available from an independent software development company, Carl Nelson & Associates, for a very reasonable licensing fee. You can also use Tim Standing’s software that he is presenting in a series of articles in MacTutor.
After the disk is initialized and the install process completed, using the drive is fairly straightforward. You use a floppy with the special INIT file in the system folder to boot up the Mac Plus. All the Wren drives that are on line will then come up. To run from the Wren double click on the FINDER icon in the system folder on the Wren to switch-launch to the Wren. The disk icon for the Wren will then be in the upper left position on the desktop and you can eject the floppy that was used for booting. The reason a floppy has to be used to bring the drive up is because Apple does not follow the SCSI protocol in the code located in the Mac Plus Roms. If you try to boot the Wren directly, the Apple SCSI routine in the ROM causes the Wren to hang in a loop. The Wren SCSI implementation strictly follows the ANSI specification. The MAC II and MAC SE ROMS fix this problem.
Even with the hassle of booting the Mac from a floppy on the older Macintosh systems, the price, speed and capacity of the Wren are well worth the slower boot up process. This drive is especially suited as a file server an a Mac running Apple Share.
Here are some more details on where to obtain parts:
Wren III disk, Model 94161-155, SCSI interface, 512 byte sectors. Distributors are Arrow, MTI, Duccomun Data, and Kierulff. You can call Dave Johnson at Kierulff in Minneapolis at 612-941-7500 to find out which Kierulff office has the drives in stock and the closest outlet to you. Kierulff sells the drive for around $1850.
The disk case and power supply are available from Microware Inc., 41711 Joy Road, Canton, MI 48187, telephone 313-459-3557. You need a Beige tape case with a 75 watt power supply, part number 900014. This unit includes a fuse, fan, line cord and switch. It will run either 110V or 220V by moving a jumper on the power supply card. The power supply also runs fine on a 220V to 110V transformer if you want to use it temporarily on European trips. The enclosure costs around $185.
Amphenol 50 pin F57 type receptor connectors. Part number 850-57F-40500-20. These connectors cost about $8.27. They are solderless and not reusable so you may want to buy an extra just to practice on. In Minneapolis the only industrial Amphenol Distributor is Newark Electronics at 612-331-6350.
50 conductor flat ribbon cable is available most anywhere in 100 foot rolls. Gopher Electronics, 222 E. Little Canada Rd, St. Paul, telephone 612-483-3322, will sell this stuff by the foot. All you need i s about 2 feet to make the cable with some left over for mistakes.
The 50 pin IDC connector to the Wren is a Belden 8S0J050 or an A P Products 925110-50-R. These cost around $7.00 each and you need one. Both Newark and Gopher Electronics sell these.
The software is available from Carl Nelson & Associates, 4007Nassau Place, Everett, WA, 98201-4855 telephone 206-252-6897.
You will need four 6-32 x 3/8" pan head screws and washers to mount the disk in the enclosure. You will need four 4-40 x 3/8" screws, nuts, washers, and lock washers to mount the two connectors in the disk enclosure back panel.
Continued in next frame
Volume Number: | 3 | |||
Issue Number: | 6 | |||
Column Tag: | Advanced Mac'ing (code) |
SCSI Formatter Project, Part II (code)
By Tim Standing, University of Calif. at SF
#Tim Standing #2/24/87 #An automatic routine to build my output for me. # Unalias make {mpw}tools:make -f {tardir}makefile > {Scratch}makeout {Scratch}makeout exit # File: MakeFile # # MakeFile for Formatting program for MacTutor An Low # Level formatting of a SCSI hard disk drive. Note if # resource file has been changed, run first Rez then # Link. Rez sometimes trashes code resources. Format ƒƒFormat.r Rez -p Format.r -o Format Format ƒƒFormat.a.o BuffStuff.a.o SCSI.a.o Format.r Link -p Format.a.o BuffStuff.a.o SCSI.a.o -o Format -l > Format.Map Format.a.oƒ Format.a FormatEqu.a Asm -p -wb Format.a BuffStuff.a.o ƒ BuffStuff.a FormatEqu.a Asm -p -wb BuffStuff.a SCSI.a.oƒ SCSI.a FormatEqu.a Asm -p -wb SCSI.a ;*********** File FormatEqu.a **** ;********************************** ; ; A list of equates used in the SCSI formatting program. ; Menu Bar resource numbers AppleM EQU 128 FileM EQU 129 EditM EQU 130 SCSIM EQU 131 ; Dialog box resource numbers AboutRN EQU 128 SelAddrRN EQU 129 ParamRN EQU 130 EDefectsRNEQU 131 AboutItem EQU 1 DialWLenEQU 170 ;lenght of window storage for ;dialog box ; Event Processing Equates AllEvents EQU $0000FFFF EventMask EQU $FFFF ; Codes used in error dialog box for SCSI Manager traps. Reset EQU $A0 Get EQU $A1 Select EQU $A2 Command EQU $A3 CompleteEQU $A4 Read EQU $A5 Write EQU $A6 Install EQU $A7 Stat EQU $A10 ; Codes used in error dialog box for SCSI commands. RezeroUnitEQU 1 TestUReadyEQU 0 FormatUnitEQU 4 ModeSelectEQU $15 MaxTicksEQU 108000 ;max # of ticks to wait for ;a SCSI command to complete MaxAddr EQU 6 ;largest SCSI address MaxCylind EQU 2048;largest number of cylinders MaxHeadsEQU 16 ;largest number of heads MaxBFI EQU $10000000;bigest Bytes From Index value ; Offsets for input buffer used when inputting disk surface ; defect information (relative to the beginning of that record). CylBBuffEQU 0 ;offset for cylinder field HeadBBuff EQU 4 ;offset for head field BFIBBuffEQU 8 ;offset for bytes from index field ; Offset for each record in output buffer (relative ; to the beginning of that record). CylOut EQU 0 ;Offset for cylinder HeadOut EQU 3 ;Offset for head BFIOut EQU 4 ;Offset for byte from index ; Equates for buffer sizes. Note there are two buffers for ; defects, one for input, another reformated one for output ; during the Format Unit command. MaxBBs EQU 60 ;Maximum number of defects BadBRecLenEQU 16;size of each defect record for ;input buffer MaxBBListLenEQU MaxBBs*BadBRecLen ;length of defect buff ;for input MaxBBOutLen EQU (MaxBBs*8)+4 ;length of defect buffer ; for output during Format ;Unit command ModeSelBytesOut EQU 22 ;bytes transfered in ; Mode Select command ParamBuffLenEQU 30;length of SCSI command ;parameter buffer CBuffLenEQU 10 ;length of command buffer StrBuffLenEQU 256 ;length of string buffers CommLenSEQU 6 ;lenght of class 0 commands CommLenLEQU 10 ;length of class 1 commands PProgLenEQU 30 ;Pseudo program buffer length CylOut_LB EQU 2 ;Offset for cylinder low byte CylOut_MB EQU 1 ;Offset for cylinder middle byte CylOut_HB EQU 0 ;Offset for cylinder high byte ;***** File:BuffStuff.a ************* ;************************************ ; ; This file contains a couple of subroutines that are used for ; manipulating the buffers. BLANKS ON IMPORT (BadB_List,BadB_Num,BadB_Out):DATA INCLUDE‘FormatEqu.a’ ; PROC ClearBuff ; ; This is used to clear a buffer. It gets the address ; in A0 and the length in bytes in D0 (long word value). ClearBuff PROC EXPORT;subroutine for export to other ;moduals at link time. movem.lD0/A0,-(SP);save registers subq #1,D0 ;D0=count-1; branch after clr.b blt.s exit;exit if passed buffer length =0 buf_loop clr.b (A0,D0.L);clear the byte dbra D0,buf_loop;branch when D0>0 exit movem.l(SP)+,D0/A0;restore the registers rts ENDPROC ; PROC OutBuff ; OutBuff takes list of bad blocks and reformats so it can be ; used as the defect data block that gets transfered after the ; format command. First we place the size of the defect data ; block minus the 4 byte header in byte 3. Then we load ; in the bad block records. They must have aready been sorted ; in order of increasing cylinder number. The fields in each ; record are: 3 bytes for the cylinder, one byte for ; the head number and a long word for the bytes ; from index value. ; ; Registers: ; A0 Points to curent position in BadB_List ;(our sorted list of bad blocks used for input) ; A1 Points to current position in BadB_Out ;(our output buffer used in formatting) ; D1 Scratch ; D0 Loop index OutBuff PROCEXPORT ; Equates for offset at beginning of bad block output buffer: ByteCount EQU 3 movem.lD0-D1/A0-A1,-(A7) ;save registers on stack ; Clear the output buffer before we start transfering records. lea BadB_Out(A5),A0 move.l #MaxBBOutLen,D0 jsr ClearBuff move.l BadB_Num(A5),D0 ;get the number of defects beq exit;exit if it’s zero subq.l #1,D0 ;adjust D0 (dbra after transfer) lea BadB_List(A5),A0 ;initialize A0 lea BadB_Out(A5),A1 ;initialize A1 clr.l D1;clear scratch move.l BadB_Num(A5),D1 lsl.l #3,D1 ;multipy count by 8 bytes move.b D1,ByteCount(A1) ;place LSB of count in header adda.l #4,A1 ;incr output pointer to 1st rec. loop move.l CylBBuff(A0),D1 ;load cylinder# in scratch move.b D1,CylOut_LB(A1) ;move low byte into record lsr.l #8,D1 move.b D1,CylOut_MB(A1) ;move middle byte into record lsr.l #8,D1 move.b D1,CylOut_HB(A1) ;move high byte into record move.b 3+HeadBBuff(A0),HeadOut(A1) ;move head number into record, ;moving byte value of long move.l BFIBBuff(A0),BFIOut(A1) ;transfer Byte_From_Index value adda.l #16,A0 ;A0 points to next input record adda.l #8,A1 ;A1 points to next output record dbra D0,Loop ;decrement loop counter & repeat exit movem.l(A7)+,D0-D1/A0-A1 ;restore registers rts ENDPROC END ;********* File: SCSI.a *********** ;********************************** ; ; This file contains all the items in the SCSI menu. It also ; contains the SCSI primatives for comunication with SCSI hard ; disks. ; ; Note: All the symbols that are declared or imported before ; start of the first module (PROC, MAIN or RECORD) are defined ; for all modules in this file. Forward references to other ; modules in a file must be imported. INCLUDE‘FormatEqu.a’ LOAD ‘Traps.d’ LOAD ‘ToolEqu.d’ LOAD ‘SysEqu.d’ LOAD ‘QuickEqu.d’ LOAD ‘SCSIEqu.d’ EXPORT (Res,Last_Cmd,CompStat,CompMsg):DATA EXPORT (BadB_Num):DATA Addr ds.w1 ;address of hard disk Heads ds.l1 ;number of heads on hard disk Cyls ds.l1 ;number of cylinders on hard disk WrtPreCompds.l 1 ;cylinder to start write precomp RedWrtCur ds.l 1 ;cylinder to start reduced write cur Interleaveds.w 1 ;interleave number to be used StepCodeds.w1 ;type of step pulse used CompStatds.w1 ;status from _SCSIComplete CompMsg ds.w1 ;message from _SCSIComplete Resds.w 1 ;result from SCSI commands Last_Cmdds.w1 ;last command executed BadB_Numds.l1 ;total number of bad blocks IMPORT (DialogPort,ItemType,ItemHandle,ItemRect):DATA IMPORT (EventLoop,DefButton,RadioHiLite):CODE IMPORT (CountOff,ExtractNumb,SortBuff,OutBuff):CODE IMPORT (ErrorProc,ClearBuff):CODE IMPORT (DialogBuff,ButtonHit):DATA IMPORT (StrBuff1,StrBuff2,StrBuff3,StrBuff4):DATA Buffers RECORD EXPORT EXPORT (CommandBuff,PseudoProg,ParamBuff):DATA EXPORT (BadB_List,BadB_Out):DATA ALIGN 2 CommandBuff ds.b CBuffLen ;buffer for SCSI commands PseudoProgds.b PProgLen ;buffer for pseudo program ParamBuff ds.b ParamBuffLen ;buffer for parameters BadB_List ds.l MaxBBListLen ;buffer area for entering ; and sorting bad blocks BadB_Outds.lMaxBBOutLen ;buffer area for output to ;to the disk drive ENDR ; PROC SelAddrProc ; ; Gets the address of the device to be formatted ; from the user. Then tests to see if device is connected ; and if device is ready to be formatted. If either of these ; are false, it jumps to ErrorProc (error handling procedure). SelAddrProc PROC EXPORT IMPORT (Hd_Select,HD_TestUnit,HD_Discon):CODE IMPORT (UnHiLite,ResetDisk):CODE clr.l -(A7) ;space for pointer to dialog move.w #SelAddrRN,-(A7) ;select addr dialog resource# pea DialogBuff(A5) ;push pointer to dialog stor move.l #-1,-(A7);make dialog top most window _GetNewDialog move.l (A7),DialogPort(A5) ;save pointer to dialog ;leave a copy on the stack ;for call to _SetPort _SetPort lea DialogBuff(A5),A4 ;pass address of dialog record jsr DefButton;make default = “Select” move.w #10,D0 ;make Mac’s address grey jsr UnHiLite ; Register ussage: ;D0Radio button that was selected, now must be unselected ;D1Radio button that was just clicked on to be selected ; Initialize D0 and D3 for first call to RadioHiLite. D0=0 ; is flag for no radio button to deselect. D1=3 is to set the ; default SCSI address = 0. clr.l D0 moveq.l#3,D7 NotYet move.w D7,D1 ;update D1 (pass to RadioHiLite) jsr RadioHiLite;change radio button selected clr.l -(A7) ;use standard filter proc func pea ButtonHit(A5) _ModalDialog cmpi.w #2,ButtonHit(A5) ;was select or cancel hit ble EndDialog;yes, so close up dialog move.l D7,D0 ;D0 = id of old radio button move.w ButtonHit(A5),D7 ;D7 = id of new radio button bra.s NotYet ;loop back for next mouse down EndDialog move.l DialogPort(A5),-(A7);pass addr of Dialog Rec _CloseDialog ;close dialog cmpi.w #2,ButtonHit(A5) ;was ok button hit blt Continue ;yes jmp EventLoop;no, go get next event Continue subq.w #3,D7 ;D7 = last radio button hit so ;subtract item # for radio ;button “0” move.w D7,Addr(A5);D7 now equals address selected jsr ResetDisk;reset SCSI bus tst.w Res(A5) ;did we succeed bne ToErrorProc;no, go handle error ; This next block of code, through end of this procedure is ; an example of how all SCSI commands are done. You can ; replace the SCSI command Test Unit Ready with any other ; SCSI command. ; The Mode Select and Format Unit commands used in FormatProc ; differ only in that data is transfered after the command. ; First get control of the bus and select target device. jsr HD_Select;go get control of bus and ;select device tst.w Res(A5) ;did we succeed bne ToErrorProc;no, go handle error ; Second, we clear the buffer we use to construct the SCSI ; command in. lea CommandBuff(A5),A0 ;pass address of buffer in A0 move.l #CBuffLen,D0 ;pass buffer size in D0 jsr ClearBuff;clear buffer ; Third, we construct the command. This one has no parameters ; in the CDB (Command Desciption Block), so only have to move ; the SCSI OP code into the CDB. move.b #TestUReady,CommandBuff(A5); move OP code into byte ; 1 of the CDB clr.w -(A7) ; clear space for trap result pea CommandBuff(A5) ; pass address of CDB move.w #CommLenS,-(A7) ; pass length of command. ; This is a class 0 SCSI ; command, length is 6 bytes. ; Fourth, we send the command and test if it succeded. _SCSICmd ;send command to target move.w #TestUReady,Last_Cmd(A5) ;store command id move.w (A7)+,Res(A5);test result, an error? bne ToErrorProc;yes go to error proc ; Then, we wait for the target device send the Status and ; Message bytes. The target device will then release the Mac. move.l #10,D0 ;pass number of ticks to wait jsr HD_Discon;proc that handles _SCSIComplete tst.w Res(A5) ;SCSI Protocal Error? bne ToErrorProc;yes, go handle display error ; Lastly, we test the Status byte returned by _SCSIComplete. ; This is the SCSI command result code. It will tell us about ; the drives condition and if it is ready to be formated. move.w #TestUReady,Last_Cmd(A5) ;store command id for ;error handling proc cmp.w #3,CompStat(A5) ;is there a Write Fault? beq ToErrorProc;yes, go to error handler cmp.w #4,CompStat(A5) ;is the Drive Not Ready? beq ToErrorProc;yes, go to error handler cmp.w #20,CompStat(A5) ;SCSI command supported? beq ToErrorProc;no, go to error handling proc jmp EventLoop ToErrorProc jmp ErrorProc EndProc ; PROC ParamProc ; ; User is given dialog box to input parameters used ; for formatting drive. We then extract the values from the ; TextEdit fields and place the hex values in the appropiate ; global variables. All values are checked against maximum ; values to make sure they are reasonable. ParamProc PROC EXPORT clr.l -(A7) ;space for pointer to dialog move.w #ParamRN,-(A7) ;parameter dialog resource numb. pea DialogBuff(A5) ;push pointer to dialog storage move.l #-1,-(A7);dialog on top of all windows _GetNewDialog move.l (A7),DialogPort(A5) ;save pointer to dialog _SetPort lea DialogBuff(A5),A4 ;pass address of dialog record jsr DefButton;make “OK” button the default ; Now we set up our dialog box. First two calls to RadioHiLite ; have D0=0 as a flag for no radio button to unselect ; Register ussage: ;D0button that was selected, now must be unselected ;D1button that was just clicked on, to be selected ;D6Interleave Button that is currently selected ;D7Step Rate Button that is currently selected moveq.l#3,D6 ;default interleave = 1:1 moveq.l#7,D7 ;default step pulse = 3 msec ; High light default interleave and step pulse radio buttons. move.w D6,D1 clr.w D0 jsr RadioHiLite clr.w D0 move.w D7,D1 jsr RadioHiLite NotYet clr.l -(A7) ;use standard filter proc func pea ButtonHit(A5);push addr of storage for item# _ModalDialog move.w ButtonHit(A5),D1 ;move item number to D1 cmpi.w #2,D1 ;was OK or Cancel selected? ble EndDialog;yes, go close up dialog cmpi.w #9,D1 ;was text edit item hit bgt NotYet ;yes, loop back for next event cmpi.w #6,D1 ;was interleave radio button hit bgt StepRate ;no, must be step rate button ; Interleave radio button was selected. Update D6 and select ; the correct radio button. move.w D6,D0 move.w D1,D6 jsr RadioHiLite bra.s NotYet ; Step rate radio button was selected. Update D7 and select ; the correct radio button. StepRate move.w D7,D0 move.w D1,D7 jsr RadioHiLite bra NotYet EndDialog cmpi.w #2,ButtonHit(A5) ;was “Cancel” selected? blt Continue ;no bra exit;yes, so exit ; “OK” button selected so we can update the global variables ; that contain the step code and the interleave values. Continue subq.w #2,D6 move.w D6,Interleave(A5) subq.w #7,D7 move.w D7,StepCode(A5) ; For each of the TextEdit fields we place the address of the ; global variable in A3, the item # in D0 and maximum value ; in D3. We then call ExtractNumb to get the hex, insure that ; it is not over the maximum value, and place hex number in ; the global variable. lea Heads(A5),A3 ;extract number of heads from move.l #10,D0 ;item number 10 move.l #MaxHeads,D3 jsr ExtractNumb lea Cyls(A5),A3;extract number of cylinders move.l #11,D0 ;from item number 11 move.l #MaxCylind,D3 jsr ExtractNumb lea RedWrtCur(A5),A3 ;extract reduced write current move.l #12,D0 ;cylinder from item #12 move.l Cyls(A5),D3 jsr ExtractNumb lea WrtPreComp(A5),A3 ;extract write precompensation move.l #13,D0 ;cylinder from item #13 move.l Cyls(A5),D3 jsr ExtractNumb Exit move.l DialogPort(A5),-(A7);pass ptr to Dialog rec _CloseDialog ;close dialog box jmp EventLoop;return to event loop EndProc ; PROC EDefectProc ; ; Here we ask the user to enter the defects that are listed on ; the sheet that came with their disk drive. Can enter as ; many as 60 defects. Since dialog box only has room for 12 ; defects, we ask the user to hit “More” button if they have ; additional defects. We give the user a dialog box when they ; cannot enter any more defects. ; ; Register Ussage: ;D0Index for next item in dialog is EditText that ;we are going to remove a value from ;D6Index for record being input in defect list ;index is byte index of first field in record,
;not actual record number EDefectProc PROC EXPORT clr.l BadB_Num(A5) ;clear global for # of defects ; Clear our defect buffer. move.l #MaxBBListLen,D0 ;pass buffer size in D0 lea BadB_List(A5),A0 ;pass address in A0 jsr ClearBuff;go clear buffer LoopForMore clr.l D6;clear buffer record index clr.l -(A7) ;space for pointer to dialog rec move.w #EDefectsRN,-(A7) ;Enter Defects Dialog res# pea DialogBuff(A5) ;pass ptr to dialog storage move.l #-1,-(A7);make this window on top _GetNewDialog ;get dialog move.l (A7),DialogPort(A5) ;save pointer to dialog _SetPort lea DialogBuff(A5),A4 ;pass address of dialog record jsr DefButton;outline default button NotYet clr.l -(A7) ;use standard filter proc function pea ButtonHit(A5);place for button hit _ModalDialog ;handle dialog events cmpi.w #3,ButtonHit(A5) ;is “Cancel” button hit? blt EndDialog;no, “More” or “OK” buttons were hit bgt NotYet ;no, not a button, get another event ; User hit “Cancel” button, so clear defect count and return ; go close dialog box clr.l BadB_Num(A5) bra exit ; We want to close up the dialog, but first we must extract ; the defect data that the user has entered. EndDialog move.l BadB_Num(A5),D6 ;move number of defects to D6 lsl.l #4,D6 ;multiply by 16 to get byte ;index for first field in ;next empty record lea BadB_List(A5),A0 ;A0 points 1st byte of buffer AddToBuff move.l #3,D0 ;first item is 4, so put 3 in D0 LoopAddBuff ; First we extract the cylinder number. ; We use the ExtractNumb procedure ; which takes the address for the result in A3, the ; maximum value in D3 and the item number in D0. addq.l #1,D0 ;increment D0 by 1, next item lea CylBBuff(A0,D6.L),A3 move.l #MaxCylind,D3 jsr ExtractNumb ; Next we extract the head number addq.l #1,D0 lea HeadBBuff(A0,D6.L),A3 move.l #MaxHeads,D3 jsr ExtractNumb ; Lastly we extract the Bytes From Index value. addq.l #1,D0 lea BFIBBuff(A0,D6.L),A3 move.l #MaxBFI,D3 jsr ExtractNumb ; Test to see if the cylinder number was 0. If so ignore this ; record as it was either empty or invalid. Most controller ; cards cannot take defects in track 0. tst.l CylBBuff(A0,D6.L) ;is cylinder value valid bne IncrCounters ;yes increment counters ; Otherwise, don’t increment counters and next record will ; overwrite this one which was invalid. cmpi.l #39,D0 ;have we reached the last item beq Exit;yes, go close dialog box bra LoopAddBuff;no, go extract next record ; Cylinder number is not equal to 0 so this is a valid defect. IncrCounters add.l #BadBRecLen,D6 ;increment buffer index cmpi.l #(MaxBBs*BadBRecLen),D6 ;is our buffer full beq.s Exit;yes, so exit cmpi.l #39,D0 bne LoopAddBuff Exit lsr.l #4,D6 ;byte count/16 = number of defects move.l D6,BadB_Num(A5) ;store defect number in global move.l DialogPort(A5),-(A7);pass pointer to dialog _CloseDialog ;close up dialog cmpi.l #60,BadB_Num(A5) ;are there too many bad blocks beq.s NoMoreAlert;yes, go put up alert cmpi.w #2,ButtonHit(A5);did user hit “More” button (#2) bne ToEventLoop;no, so go to event loop bra LoopForMore;yes so put up dialog again ToEventLoop jmp EventLoop NoMoreAlert move.w #134,-(A7) ;get out of space alert clr.l -(A7) ;use standard filter proc function clr.w -(A7) _CautionAlert ;use a caution alert clr.w (A7)+ jmp EventLoop EndProc ; PROC FormatProc ; ; Formating procedure. This is where we sent the drive ; parameters in a Mode Select command. We then send the drive ; defects with the Format Unit command. And then the drive is ; ready to use with a driver installing program. FormatProcPROC EXPORT IMPORT (HD_Select,HD_Discon,WriteDisk):CODE ; First we put up a dialog box to make sure the user wants to ; format the drive. ; Make String buffer #1, 1 character long. Then move the hex ; value in the low order byte of Addr (the SCSI address of the ; target device) into the string buffer and convert to ascii. move.b #1,StrBuff1(A5) move.b 1+Addr(A5),1+StrBuff1(A5) add.b #$30,1+StrBuff1(A5) clr.b StrBuff2(A5) ;clear all other string buffers clr.b StrBuff3(A5) clr.b StrBuff4(A5) pea StrBuff1(A5) pea StrBuff2(A5) pea StrBuff3(A5) pea StrBuff4(A5) _ParamText ;substitute String buffer 1 for “^0” move.w #132,-(A7) ;get format alert dialog box clr.l -(A7) ;use standard filter proc function clr.w -(A7) _CautionAlert cmpi.w #1,(A7)+ ;did user hit cancel? bne Exit;yes, so exit ; User wants to Zap Drive, so let ‘em have it! ; Construct a data block to place parameters that we will send ; with the Mode Select command. ZapDrive ; First we clear the data buffer lea ParamBuff(A5),A0 ;pass address of buffer move.l #ParamBuffLen,D0 ;pass length of buffer jsr ClearBuff;clear buffer clr.l D0;clear D0, used for scratch ;header move.b #8,$3(A0);3rd byte = size of descriptor ;list ;descriptor list move.b #2,$A(A0);high byte of sector size ($200) ;Drive Parameter List move.b #1,$C(A0);List Format Code: (1 = soft ;sectored drives, 2= hard ;sectored drives or ;removable media drives) ; Place word value of cylinder count into buffer. We must do ; this as two bytes because the wored in the output buffer is ; at an odd address move.w 2+Cyls(A5),D0 move.b D0,$E(A0);make sure $E(A0) has LSB lsr.l #8,D0 ;now move in high byte of word move.b D0,$D(A0) ; Move byte value of head count into buffer. move.b 3+Heads(A5),$F(A0) ; Move word value of Reduced Write Current Cylinder into ; buffer. move.w 2+RedWrtCur(A5),$10(A0) ; Move word value of Write Precompensation Cylinder into ; buffer. move.w 2+WrtPreComp(A5),$12(A0) ; Move byte value of Step Pulse Code into Buffer. move.b 1+stepcode(A5),$15(A0) ; Now we send the Mode Select Command and the parameter ; buffer. jsr HD_Select;get SCSI bus and select drive tst.w Res(A5) ;did we succeed? bne ToErrorProc;no, so go to error handling proc ; Clear command buffer and construct command. lea CommandBuff(A5),A0 ;pass address of buffer move.l #CBuffLen,D0 ;pass length of buffer jsr ClearBuff;clear command buffer move.b #ModeSelect,(A0) ;move SCSI OP Code to byte #0 move.b #ModeSelBytesOut,4(A0);move # of bytes to ; transfer into byte #4 clr.w -(A7) ;space for trap error code move.l A0,-(A7) ;pass address of command buffer move.w #CommLenS,-(A7) ;pass length of CDB ;(Command Data Block), ;class 0 command, so it ;is 6 bytes long _SCSICmd ;send command move.w #ModeSelect,Last_Cmd(A5) ;store command id move.w (A7)+,Res(A5);did command work? bne ToErrorProc;no, go to error handling proc ; Now we clear the pseudo program buffer ; WriteDisk to send the data to the controller card. lea PseudoProg(A5),A0 ;pass address of buffer move.l #PProgLen,D0 ;pass size of buffer jsr ClearBuff;clear buffer lea ParamBuff(A5),A1 ;pass address of data buffer move.l #ModeSelBytesOut,D0 ;pass # of bytes to jsr WriteDisk; transfer & send data move.l #10,D0 ;pass number of ticks to wait jsr HD_Discon tst.w Res(A5) ;SCSI Protocal Error? bne ToErrorProc move.w #ModeSelect,Last_Cmd(A5) cmp.w #$4,CompStat(A5) ;is the Drive Not Ready? beq ToErrorProc;yes, go to error handler cmp.w #$24,CompStat(A5) ;Was a bad parameter passed to ;the controller? beq ToErrorProc;yes, go to error handler jsr HD_Select;go get control of bus and ;select device tst.w Res(A5) ;did we succeed? bne ToErrorProc;no, go to error proc ; Next, clear the buffer where we will assemble the CDB lea CommandBuff(A5),A0;pass address of buffer move.l #CBuffLen,D0 ;pass size of buffer jsr ClearBuff;clear buffer move.b #FormatUnit,(A0) ;move op code into first byte move.b 1+Interleave(A5),4(A0);move byte value of inter ;leave into byte 4 tst.l BadB_num(A5) ;see if there are disk defects beq NoDefects;no, skip ahead add.b #$1C,1(A0) ;place flags to tell controller ;that defects are coming ; Call OutBuff to format defects so they can be read by ; controller card. jsr OutBuff NoDefects clr.w -(A7) ;space for trap error code move.l A0,-(A7) ;pass address of CDB move.w #CommLenS,-(A7) ;class 0 command so pass ; length of 6 bytes _SCSICmd ;send command move.w #FormatUnit,Last_Cmd(A5) ;store command id move.w (A7)+,Res(A5);did _SCSICmd produce an error bne ToErrorProc;yes, go to error handler ; If we are sending any info on disk defects, prepare buffer ; for pseudo program and pass count bytes and address to ; sending procedure. tst.l BadB_num(A5) ;are we sending any defect data? beq NoPseudoProg ;no, skip the next section ; Clear pseudo program buffer. lea PseudoProg(A5),A0 ;pass address of buffer move.l #PProgLen,D0 ;pass length of buffer jsr ClearBuff;clear buffer ; Pass address of data, and number of bytes to be transfered. ; A0 still contains the address of the pseudo program buffer. lea BadB_Out(A5),A1 move.l BadB_Num(A5),D0 ;move number of defects to D0 lsl.l #3,D0 ;multiply by 8 (8 bytes/defect) addq.l #4,D0 ;add 4 bytes for header jsr WriteDisk;send info on defects NoPseudoProg move.l #MaxTicks,D0 ;pass number of ticks to wait jsr HD_Discon tst.w Res(A5) ;SCSI Protocal Error? bne ToErrorProc;yes, go to error handler ; Test Status byte from SCSI command to see if we passed a bad ; parameter or if there is a defect on track 0. move.w #FormatUnit,Last_Cmd(A5) ;update command id for ;error handling proc cmp.w #$24,CompStat(A5) ;Was a bad parameter passed to ;the controller? beq ToErrorProc;yes, go to error proc jmp EventLoop Exit jmp EventLoop ToErrorProc jmp ErrorProc EndProc ; PROC ResetProc ; ; This procedure is called from the menu handling routine. It ; acts as glue between the menu handling code and the reset ; subroutine and allows us to re-use the reset subroutine. ; (like in SelAddrProc). ResetProc PROC EXPORT IMPORT (ResetDisk):CODE jsr ResetDisk;go reset SCSI bus tst.w Res(A5) ;test result bne ToErrorProc;if error go to error handler jmp EventLoop;else return to event loop ToErrorProc jmp ErrorProc EndProc ; PROC WriteDisk ; ; Procedure wor transfering data to the controller. This ; routine uses a pseudo program which contains a simple loop ; of three instructions. This transfer could have been done ; using only the first and the last instruction if we had put ; the transfer count that is passed in D0 into the second ; parameter of the first command. It would look exactly the ; same if it were being used for a read, except the _SCSIWrite ; trap would be replaced with _SCSIRead. Refer to IM volume IV ; for info on the Pseudo instructions. Note each instruction ; is a word followed by two longword parameters. SCSTOP must ; be the last instruction. Address of pseudo buffer is ; passed in A0. Address of p_buff is passed in A1. ; Transfer count is passed in D0. WriteDisk PROC ENTRY movem.lD0-D2/A0-A1,-(SP) ;save registers ; First command moves bytes to given address move.w #SCINC,(A0) move.l A1,2(A0) ;address to be moved to or from move.l #1,6(A0) ;transfer count in bytes ; Second command is loop move.w #SCLOOP,10(A0) move.l #-10,12(A0);rel addr move.l d0,16(A0);loop count ; Third command = stop, no parameters move.w #SCSTOP,20(A0) clr.w -(SP) ;space for trap result code (OSErr) move.l A0,-(SP) ;address of pseudo program _SCSIWrite move.w (SP)+,Res(A5);store result move.w #Write,Last_Cmd(A5) ;store code for type ;of SCSI command movem.l(SP)+,D0-D2/A0-A1 ;restore registers rts ENDPROC ; PROC HD_Select ; ; HD_Select tries to get control of the SCSI bus. If it does, ; it then selects the device whose address is in Addr(A5). HD_Select PROC ENTRY IMPORT CountOff:CODE movem.lD0-D2/A0-A1,-(A7) ;save regs on stack clr.w -(A7) ;space for result code _SCSIGet ;reserve the bus for our use move.w (A7)+,Res(A5);store result code beq.s ok;go to ok2 if we succeeded move.w #Get,Last_Cmd(A5) ;else, store the last command bra.s exit;and return ok clr.w -(A7) ;space for result code move.w Addr(A5),-(A7) ;load SCSI address of hard disk _SCSISelect;select device move.w (A7)+,Res(A5);store result code move.w #Select,Last_Cmd(A5);store last command exit movem.l(A7)+,D0-D2/A0-A1 ;restore registers rts ENDPROC ; PROC HD_Discon ; ; Here we complete a SCSI command. We tell Mac how long to ; wait for target device on the SCSI bus to signal that the ; command has completed. This procedure receives number of ; ticks to wait in D0. HD_Discon PROC ENTRY movem.lD0-D2/A0-A1,-(A7) ;save regs on stack clr.w CompStat(A5) clr.w CompMsg(A5) clr.w -(A7) ;space for result code pea CompStat(A5) ;_SCSIComplete Status pea CompMsg(A5);_SCSIComplete Message move.l D0,-(A7) ;number of ticks to wait for ;completion of the last ;SCSI command _SCSIComplete move.w (A7)+,Res(A5);store result code move.w #Complete,Last_Cmd(A5);store last command movem.l(A7)+,D0-D2/A0-A1 ;restore registers rts ENDPROC ; PROC InitGlobals ; ; Initialize the globals used to store information about the ; hard disk. Set all values to zero InitGlobals PROC EXPORT clr.w Heads(A5) clr.w Cyls(A5) clr.w WrtPreComp(A5) clr.w RedWrtCur(A5) clr.w Interleave(A5) clr.w StepCode(A5) clr.l Addr(A5) clr.l BadB_Num(A5) rts ENDPROC ; PROC ResetDisk ; ; This procedure resets the SCSI bus. It has a ; one second delay after the ; reset because the CDC Wren III drives take more than ; 3/4 of a second to completely reset. Other drives may need ; longer delays after the reset. ResetDisk PROC EXPORT movem.lD0-D2/A0-A1,-(A7) ;save the registers clr.w -(A7) ;space for result code _SCSIReset ;reset the bus move.w (A7)+,Res(A5);store results beq.s ok1 ;go to ok1 if we succeeded move.w #Reset,Last_Cmd(A5) ;else cmd code for err Msg rts ok1 move.l #60,D0 ;D0 = number of ticks to wait jsr CountOff ;jst to delay routine movem.l(A7)+,D0-D2/A0-A1 ;restore the registers rts ENDPROC END ;******** File: Format.a *************** ;************************************** ; ; A formatting program for low level formatting of a SCSI ; hard disk. Allows user to enter disk drive parameters and ;information about defects. This file contains all ; the standard code for Menus etc. It also contains the ; procedures for error handling. BLANKS ON INCLUDE‘FormatEqu.a’ LOAD ‘Traps.d’ LOAD ‘ToolEqu.d’ LOAD ‘SysEqu.d’ LOAD ‘QuickEqu.d’ LOAD ‘SCSIEqu.d’ LOAD ‘PackMacs.d’ ; Note Export directives must be placed before a symbol is ; declared. EXPORT (DialogBuff,QuickDraw,ButtonHit,DialogPort):DATA EXPORT (ItemType,ItemHandle,ItemRect,MyPort):DATA EXPORT (StrBuff1,StrBuff2,StrBuff3,StrBuff4):DATA AppleMH ds.l1 ;handle for Apple menu FileMH ds.l1 ;handle for File menu EditMH ds.l1 ;handle for Edit menu SCSIMH ds.l1 ;handle for SCSI menu DAName ds.b255 ;space for DA’s name Window ds.l1 DialogBuffds.l DialWLen MyPort ds.l1 ButtonHit ds.w 1 DialogPortds.l 1 ItemTypeds.l1 ItemHandleds.l 1 ItemRectds.l2 StrBuff1ds.bStrBuffLen StrBuff2ds.bStrBuffLen StrBuff3ds.bStrBuffLen StrBuff4ds.bStrBuffLen EventRecRECORD 20, INCR what ds.w1 ; Message ds.l1 ; When ds.l1 ; Point ds.l1 ; Modify ds.w1 ; ENDR ; Note: The space for quickdraw globals must be declared. ; Unlike MDS, the MPW assembler does not do it for you. QuickDraw RECORD ,DECREMENT thePort ds.l1 ORG -grafSize ENDR ; This is our program. Begin MAIN ALIGN 2 IMPORT (InitManagers,LoadMenu,InitGlobals,EventLoop):CODE ;we must import these symbols as they are not ;yet defined in this file. jsr InitManagers jsr LoadMenu jsr InitGlobals jmp EventLoop ENDMAIN ; PROC InitManagers ; ; Initialize all managers that we might need. InitManagersPROC ENTRY IMPORT QuickDraw:DATA pea QuickDraw.thePort(A5) _InitGraf _InitFonts move.l #AllEvents,D0 _FlushEvents _InitWindows _InitMenus clr.l -(A7) _InitDialogs _TEInit _InitCursor rts ENDPROC ; PROC LoadMenus ; ; Subroutine called at start up time to load menus onto the ; heap, install them and then draw the menu bar. LoadMenuPROCENTRY clr.l -(A7) move.w #AppleM,-(A7);”About...” ID for Apple menu _GetRMenu;load it on heap move.l (A7),AppleMH(A5) ;copy handle for future ref. move.l (A7),-(A7) ;dup handle for next 2 traps clr.w -(A7) ; _InsertMenu move.l #’DRVR’,-(A7);get desk accessories _AddResMenu;add to Apple Menu clr.l -(A7) ;space for next handle move.w #FileM,-(A7) ;File menu ID _GetRMenu;load it on heap move.l (A7),FileMH(A5) ;copy handle for future ref. clr.w -(A7) ; _InsertMenu clr.l -(A7) ;space for next handle move.w #EditM,-(A7) ;Edit menu ID _GetRMenu;load it on heap move.l (A7),EditMH(A5) ;copy handle for future ref. clr.w -(A7) ; _InsertMenu clr.l -(A7) ;space for next handle move.w #SCSIM,-(A7) ;SCSI menu ID _GetRMenu;load it on heap move.l (A7),SCSIMH(A5) ;copy handle for future ref. clr.w -(A7) _InsertMenu _DrawMenuBar rts ENDPROC ; PROC EventLoop ; ; This is where we process our events. Not much happening ; except some jump tables. We only process mouse down events ; and system events. All others ignored. EventLoop PROC EXPORT IMPORT (DownMouse,DownKey,Update,Activate):CODE _SystemTask clr.w -(A7) move.w #EventMask,-(A7) pea EventRec(A5) ; Note we unhighlight the menu only in the event loop so that ; long opporations like formating leave the menu bar selected ; until they have completed. clr.w -(A7) _HiLiteMenu _GetNextEvent move.w (A7)+,D0 ;boolean for is there an event cmp.w #0,D0 ;if no event, loop back beq.s EventLoop movea.l#0,A0 ;clear register for jumptable move.w EventRec.What(A5),A0;get type of event adda.l A0,A0 ;multipy by 2 as offset table ;contains word length entries lea JumpTable,A1 adda.l A1,A0 ;add offset to table address adda.w (A0),A1 ;add offset for proc to table addr jmp (A1);jump to procedure JumpTable DATAREFS RELATIVE dc.w EventLoop-JumpTable ;event #0 - null event dc.w DownMouse-JumpTable ;event #1 - mouse-down dc.w EventLoop-JumpTable ;event #2 - mouse-up dc.w EventLoop-JumpTable ;event #3 - key-down dc.w EventLoop-JumpTable ;event #4 - key-up dc.w EventLoop-JumpTable ;event #5 - auto-key dc.w EventLoop-JumpTable ;event #6 - update dc.w EventLoop-JumpTable ;event #7 - disk-inserted dc.w EventLoop-JumpTable ;event #8 - activate dc.w EventLoop-JumpTable ;event #9 - n.a. dc.w EventLoop-JumpTable ;event #10 - network dc.w EventLoop-JumpTable ;event #11 - device driver dc.w EventLoop-JumpTable ;Appl event #1 dc.w EventLoop-JumpTable ;Appl event #2 dc.w EventLoop-JumpTable ;Appl event #3 dc.w EventLoop-JumpTable ;Appl event #4 ENDPROC ; PROC DownMouse ; ; Procedure for handling mousedown events and figuring ; out which routine is used to handle the event. DownMouse PROC Entry IMPORT (MenuBar,SysProc):CODE clr.w -(A7) move.l EventRec.Point(A5),-(A7) pea Window(A5) _FindWindow movea.l#0,A0 ;clear register move.w (A7)+,A0 ;pop region number for mouse down adda.l A0,A0 ;multiply by two as offset ;table has word entries lea MouseTable,A1 adda.l A1,A0 ;add address of table to offset adda.w (A0),A1 ;add offset of proc to table addr jmp (A1);jump to proc address ; Offset table for mouse down events. MouseTable DATAREFS RELATIVE dc.w EventLoop-MouseTable;in desk dc.w MenuBar-MouseTable ;in menu bar dc.w SysProc-MouseTable ;in system window dc.w EventLoop-MouseTable;in contents region dc.w EventLoop-MouseTable;in drag region dc.w EventLoop-MouseTable;in grow region dc.w EventLoop-MouseTable;in go away region ENDPROC ; PROC MenuBar ; ; Subroutine for jumping to the correct menu. Put the menu ; number in A1 and the item number in A2. This proc finds ; out which menu item was selected, calculated the correct ; address to jump to and jumps there. MenuBar PROCENTRY IMPORT (QuitProc,UndoMI,CutMI,CopyMI,PasteMI):CODE IMPORT (AppleMProc,SelAddrProc,ParamProc,EDefectProc):CODE IMPORT (FDefectProc,FormatProc,MSenseProc,ResetProc):CODE IMPORT (ClearMI):CODE clr.w -(A7) ;space for longint from menu move.l EventRec.Point(A5),-(A7) _MenuSelect;get menu selected movea.l#0,A1 ;clear A1 movea.l#0,A2 ;clear A2 move.w (A7)+,A1 ;menu id into A1 move.w (A7)+,A2 ;menu item into A2 cmpa.w #0,A1 ;movea doesn’t set cond. codes beq eventloop;if menu ID=0 get next event suba.l #AppleM+1,A1 ;convert menu id into menu # ;e.g. File menu becomes 0 cmpa.w #$FFFF,A1;was it the apple menu beq AppleMProc ;yes, so go handle event suba.l #1,A2 ;correct index (1st item = 0) adda.l A1,A1 ;A1*2; table has word entries adda.l A2,A2 ;A2*2; table has word entries ; Now calculate address to jump to by using the values in ; the offset tables. lea MenuTable,A0 adda.l A0,A1 ;this gives address of ;correct offset in MenuTable adda.w (A1),A0 ;A0 now contains the address ;of the correct ItemTable adda.l A0,A2 ;get entry in ItemTable adda.w (A2),A0 ;add offset at this entry to ;address of ItemTable jmp (A0);and jump to it! ; Offset table for each menu. MenuTable DATAREFS RELATIVE dc.w FileMTable-MenuTable dc.w EditMTable-MenuTable;in system window dc.w SCSIMTable-MenuTable;in contents region ; ItemTables ; Offset table for each item in each menu. FileMTable dc.w QuitProc-FileMTable EditMTable dc.w UndoMI-EditMTable dc.w 0 dc.w CutMI-EditMTable dc.w CopyMI-EditMTable dc.w PasteMI-EditMTable dc.w ClearMI-EditMTable SCSIMTable dc.w SelAddrProc-SCSIMTable dc.w ParamProc-SCSIMTable dc.w EDefectProc-SCSIMTable dc.w FormatProc-SCSIMTable dc.w ResetProc-SCSIMTable ENDPROC ;PROC SysProc ; ; Mouse down in a desk accessory. Go call _SystemClick. SysProc PROCENTRY IMPORT (EventLoop):CODE pea EventRec(A5) move.l Window(A5),-(A7) _SystemClick jmp EventLoop ENDPROC ;PROC QuitProc ; ; Procedure for quitting program. QuitProcPROCENTRY _ExitToShell ENDPROC ;PROC UndoMI ; ; Procedure to handle selection of the “Undo” menu item when a ; desk accessory controls the active window. UndoMI PROCENTRY clr.w -(A7) move.w #0,-(A7) _SysEdit adda.l #2,A7 jmp EventLoop ENDPROC ;PROC CutMI ; ; Procedure to handle selection of the “Cut” menu item when a ; desk accessory controls the active window. CutMI PROCENTRY clr.w -(A7) move.w #2,-(A7) _SysEdit move.w (A7)+,D0 jmp EventLoop ENDPROC ;PROC CopyMI ; ; Procedure to handle selection of “Copy” menu item when a ; desk accessory controls the active window. CopyMI PROCENTRY clr.w -(A7) move.w #3,-(A7) _SysEdit move.w (A7)+,D0 jmp EventLoop ENDPROC ;PROC PasteMI ; ; Procedure to handle selection of “Paste” menu item when ; desk accessory controls the active window. PasteMI PROCENTRY clr.w -(A7) move.w #4,-(A7) _SysEdit move.w (A7)+,D0 jmp EventLoop ENDPROC ;PROC ClearMI ; ; Procedure to handle selection of “Clear” menu item when ; desk accessory controls the active window. ClearMI PROCENTRY clr.w -(A7) move.w #5,-(A7) _SysEdit move.w (A7)+,D0 jmp EventLoop ENDPROC ; PROC AppleMProc ; ; This procedure handles menu events in the Apple menu. On ; entry, A2 has the item number of the menu item that was ; selected. AppleMProcPROC ENTRY IMPORT (DAName,QuickDraw):DATA IMPORT (AboutProc,EventLoop):CODE cmpa.l #AboutItem,A2;”About U2 Formatter” selected? beq AboutProc;yes, go put up dialog ; A desk accessory was selected, go install it. move.l QuickDraw.thePort(A5),MyPort(A5) ;save our port move.l AppleMH(A5),-(A7) ;push handle for apple menu move.w A2,-(A7) ;push item number hit pea DAName(A5) ;push space for DA name _GetItem ;get DA name clr.w -(A7) ;space for driver reference # pea DAName(A5) ;pass DA name _OpenDeskAcc ;open DA adda.l #2,A7 move.l MyPort(A5),-(A7) ;restore our port _SetPort jmp EventLoop ENDPROC ; PROC AboutProc ; ; This procedure puts up our about U2 Formatter dialog box. AboutProc PROC ENTRY IMPORT (ButtonHit,QuickDraw):DATA IMPORT (EventLoop,DefButton):CODE clr.l -(A7) ;space for pointer to dialog move.w #AboutRN,-(A7) ;About dialog resource number pea DialogBuff(A5) ;pass address of dialog buffer move.l #-1,-(A7);bring dialog window to front _GetNewDialog move.l (A7),DialogPort(A5) ;Save ptr to dialog rec ;note: pointer still on ;stack for _SetPort trap _SetPort lea DialogBuff(A5),A4 ;pass address of dialog buffer jsr DefButton NotYet clr.l -(A7) ;use standard filter proc pea ButtonHit(A5);addr of storage for item hit _ModalDialog cmpi.w #1,ButtonHit(A5) ;which item hit? is it “ok”? bne NotYet ;no so loop back move.l DialogPort(A5),-(A7);pass ptr to dialog rec _CloseDialog jmp EventLoop ENDPROC ; PROC DefButton ; ; This procedure draws a rounded rectangle around our default ; button in a modal dialog. It gets passed the address of our ; dialog. It then gets the rectangle for the first item and ; draws a rounded rectangle around it. DefButton PROC EXPORT movem.lD0-D2/A0-A4,-(A7) ;save registers ; First we get the rectangle for item 1. move.l A4,-(A7) ;push pointer for dialog move.w #1,-(A7) ;push item number pea ItemType(A5) ;addr of space for item type pea ItemHandle(A5) ;address of space for handle pea ItemRect(A5) ;address of space for rect _getDItem move.w #3,-(A7) ;change pensize to 3X3 move.w #3,-(A7) _PenSize pea ItemRect(A5) ;make button smaller; all edges move.w #-4,-(A7);moved in by 4 pixels move.w #-4,-(A7) _InsetRect pea ItemRect(A5) ;draw boarder for default button move.w #16,-(A7);curvature is 16 pixel in both move.w #16,-(A7);directions _FrameRoundRect Done movem.l(A7)+,D0-D2/A0-A4 ;restore registers rts ENDPROC ; PROC RadioHiLite ; ; This procedure selects the radio button that was chosen and ; deselects the radio button that was previously selected. It ; receives the old selected button in D0 and the new one in ; D1. It also receives the address of the dialog buffer in A4 ; If this is the first time the procedure is being called ; for a given dialog box, it should be passed zero in D0 ; so that it doesn’t try to deselect a radio button that ; is not selected. ; This procedure also checks to see that the button that was ; chosen was not the one that was already selected. If it is, ; it doesn’t deselect it. RadioHiLite PROC EXPORT movem.lD0-D2/D6-D7/A0-A4,-(A7) ;save registers move.w D0,D6 ;move radio button values to register move.w D1,D7 ;that wont be trashed by traps move.l A4,-(A7) ;get rectangle for radio button move.w D7,-(A7) pea ItemType(A5) pea ItemHandle(A5) pea ItemRect(A5) _getDItem ; Duplicate top left hand point of rectangle into bottom right ; point of rectangle. They are now the same. Now add a ; different constant to each so the circle in this rect ; is right in the middle of the radio button. Then invert ; this oval so it changes from white to black. move.l ItemRect(A5),ItemRect+4(A5) addi.l #$00070005,ItemRect(A5) addi.l #$000D000B,ItemRect+4(A5) pea ItemRect(A5) _InvertOval cmp.l D6,D7 ;is the radio button the same one that beq Done;was highlighted last time? If so, ;we are done. tst.l D6;is this the first time called? beq Done;if so, D6=0 and we must quit ; Repeat the opporation for the old radio button, the one that ; was previously highlighted. This time _InvertOval will ; change it from black to white. move.l A4,-(A7) move.w D6,-(A7) pea ItemType(A5) pea ItemHandle(A5) pea ItemRect(A5) _getDItem move.l ItemRect(A5),ItemRect+4(A5) addi.l #$00070005,ItemRect(A5) addi.l #$000D000B,ItemRect+4(A5) pea ItemRect(A5) _InvertOval Done movem.l(A7)+,D0-D2/D6-D7/A0-A4 ;restore registers rts ENDPROC ; PROC ErrorProc ; ; This procedure puts up an error dialog box. The dialog tells ; what the last SCSI command or trap was, what error occurred ; and a possible explanation. All text is in STR resources. ; Trap names are given the resource number: “AX00” where X is ; the routine selector used by the SCSI Manager (IM, IV-295). ; SCSI commands are given the resource number: “X00” where X ; is the SCSI op code. These values are all in the ; FormatEqu.a file. Resource number ; for errors are of the form “AAXX” for traps where XX is ; the OSErr returned from the trap, or “10XX” for SCSI ; commands, where XX is the SCSI error reported ; by _SCSIComplete in the ; status parameter. Resource numbers for the “Most Probable ; Cause” strings are “BAXX” for traps and “20XX” for SCSI ; commands. ErrorProc PROC EXPORT IMPORT (Last_Cmd,Res,CompStat,CompMsg):DATA ; Get res number for last command string and place a pointer ; to the string on the stack. clr.l D4;place no. for last command in D4 move.w Last_Cmd(A5),D4 lsl.l #8,D4 ;multipy by $100. clr.l -(A7) ;space for handle move.l #’STR ‘,-(A7);type of resource move.w D4,-(A7) ;resource number _GetResource ;get handle last command string move.l (A7)+,A0 ;pop handle into A0 move.l (A0),-(A7) ;dereference handle & push ptr ; Get resource number for error string and place a pointer ; to the string on the stack. clr.l D3 move.w Res(A5),D3 ;if there was no OSError, Res=0 bne ErrorInTrap;if Res = 0, error in SCSI cmd move.w CompStat(A5),D3 ;SCSI error into D3 add.w #$1000,D3;add offset for SCSI cmd error bra.s Continue ErrorInTrap add.w #$AA00,D3;add offset OS trap error Continue clr.l -(A7) ;space for handle move.l #’STR ‘,-(A7);type of resource move.w D3,-(A7) ;resource number _GetResource ;get resource handle move.l (A7)+,A0 ;pop handle into A0 move.l (A0),-(A7) ;dereference handle and push ptr ; Get resource number for probable cause string and place a ; pointer to the string on the stack. add.l #$1000,D3;add offset to error# to get cause# clr.l -(A7) ;space for handle move.l #’STR ‘,-(A7);type of resource move.w D3,-(A7) ;resource number _GetResource ;get resource handle move.l (A7)+,A0 ;pop handle into A0 move.l (A0),-(A7) ;dereference handle and push ptr clr.b StrBuff4(A5) ;make last string empty pea StrBuff4(A5) ;pass pointer to empty string _ParamText move.w #133,-(A7) ;resource number for dialog clr.l -(A7) ;use standard filter proc clr.w -(A7) _NoteAlert clr.w (A7)+ jmp EventLoop ;return to the event loop ENDPROC ; PROC CountOff ; ; This procedure produces a delay equal to the number of ticks ; in the long word passed in D0. CountOffPROCEXPORT movem.lD0-D3/A0-A1,-(A7) ;save the registers move.l D0,D3 ;move our value into a register that ;wont be trashed by the traps clr.l -(A7) ;space for the current tick count _TickCount add.l (A7)+,D3 ;add our delay to the current tick ;count, this is when we stop ;waiting. ; Our loop to see if we have gone past the tick count we are ; waiting for. NotYet clr.l -(A7) ;space for # of ticks since reboot _TickCount cmp.l (A7)+,D3 ;is this as big as the number we are ;waiting for? bgt NotYet;if not, then repeat loop movem.l(A7)+,D0-D3/A0-A1 ;restore registers rts ENDPROC ; PROC UnHiLite ; ; This procedure unhighlights an item in a dialog box to show ; that it cannot be selected. It receives the address of the ; dialog record in A4 and item number to be unhighlighted in ; D0. UnHiLitePROCEXPORT movem.lD0-D2/A0-A1,-(A7) ;save the registers move.l A4,-(A7) ;get the item rect for the item move.w D0,-(A7) ;number that we were passed pea ItemType(A5) pea ItemHandle(A5) pea ItemRect(A5) _getDItem move.l ItemHandle(A5),-(A7);pass the item handle move.w #255,-(A7) ;pass flag for unhighlighting _HiLiteControl movem.l(A7)+,D0-D2/A0-A1 ;restore registers rts ENDPROC ; PROC ExtractNumb ; ; This procedure extracts number value from edit text item ; in a dialog box. It gets passed item number in D0 and ; address of the Dialog box record in A4. A3 contains a ptr ; to the long where output is to be placed and D3 contains the ; maximum value allowable. ExtractNumb PROC EXPORT movem.lD0-D2/A0-A1,-(A7) ;save registers clr.l (A3);clear output long ; Get the handle for this item number. move.l A4,-(A7) ;pass dialog record pointer move.w D0,-(A7) ;pass item number pea ItemType(A5) pea ItemHandle(A5) pea ItemRect(A5) _getDItem ; Get the text from that dialog item. Move text into StrBuff1. move.l ItemHandle(A5),-(A7) pea StrBuff1(A5) _GetIText clr.l D0;get length of text out of first move.b StrBuff1(A5),D0 ;byte and place it in D0 ble Exit;if length is 0, we are done. ; Loop to remove all characters that aren’t numbers. D1 is the ; index for the position being tested. D2 is the index for the ; last valid position. lea StrBuff1(A5),A0 ;initialize A0 to point to string moveq.l#1,D1 moveq.l#1,D2 Loop: cmp.b #’0',(A0,D1.w) ;test next character blt NotNumb cmp.b #’9',(A0,D1.w) bgt NotNumb cmp.l D1,D2 ;if indeces are same, no move is beq NoMove ;necessary move.b (A0,D1.w),(A0,D2.w) ;move character NoMove: addq.l #1,D1 ;increment both pointers as last addq.l #1,D2 ;character was a number cmp.w D1,D0 ;reached the end of string? bge Loop;no bra Convert ;yes NotNumb addq.w #1,D1 ;only increment test pointer cmp.w D1,D0 ;reached the end of string? bge Loop;no bra Convert ;yes Convert: subq.l #1,D2 ;make D2 into count byte move.b D2,(A0) ;count byte into byte 1 of string _StringToNum ;convert to hex value move.l D0,(A3) ;move result to output Default cmp.l (A3),D3 ;is result greater than allowable bge Exit;no, we can exit move.l D3,(A3) ;yes so use default value instead Exit movem.l(A7)+,D0-D2/A0-A1 ;restore registers rts ENDPROC END /* Format.r3/1/87 Timothy Standing Resources for the SCSI formatting program.*/ #include “Types.r” /* These define’s used in MENU resources to disable specific menu items. */ #define AllItems 0b1111111111111111111111111111111 /* 31 flags */ #define MenuItem10b00001 #define MenuItem20b00010 #define MenuItem30b00100 #define MenuItem40b01000 #define MenuItem50b10000 resource ‘MENU’ (128, “Apple”, preload,nonpurgeable) { 128, textMenuProc, AllItems & ~MenuItem2, /* Disable item #2 */ enabled, apple, { “About Format”, noicon, nokey, nomark, plain; “-”, noicon, nokey, nomark, plain } }; resource ‘MENU’ (129, “File”, preload,nonpurgeable) { 129, textMenuProc, allEnabled, enabled, “File”, { “Quit”, noicon, nokey, nomark, plain } }; resource ‘MENU’ (130, “Edit”, preload,nonpurgeable) { 130, textMenuProc, AllItems & ~(MenuItem1 | MenuItem2),/* Disable items #1 & #2 */ enabled, “Edit”, { “Undo”, noicon, “Z”, nomark, plain; “-”, noicon, nokey, nomark, plain; “Cut”, noicon, “X”, nomark, plain; “Copy”, noicon, “C”, nomark, plain; “Paste”, noicon, “V”, nomark, plain; “Clear”, noicon, nokey, nomark, plain } }; resource ‘MENU’ (131, “SCSI”, preload,nonpurgeable) { 131, textMenuProc, allEnabled, enabled, “SCSI”, { “Select Address”, noicon, nokey, nomark, plain; “Parameters”, noicon, nokey, nomark, plain; “Enter Defects”, noicon, nokey, nomark, plain; “Format”, noicon, nokey, nomark, plain; “Reset”, noicon, nokey, nomark, plain } }; resource ‘DLOG’ (128,”About Format”,preload,nonpurgeable) { {106, 106, 290, 406}, dBoxProc, visible, noGoAway, 0x0, 128, “” }; resource ‘DITL’ (128,preload,nonpurgeable) { { /* 1 */ {141,202,161,273}, button { enabled, “OK” }; /* 2 */ {15,27,41,274}, staticText { enabled, “A formatting program for MacTutor™” }; /* 3 */ {46,97,69,204}, /* Author Item */ staticText { enabled, “by Tim Standing” }; /* 4 */ {81,46,101,260}, staticText { enabled, “For help with this program see” }; /* 5 */ {101,46,119,260}, staticText { disabled, “the June ’87 issue of MacTutor™” }; /* 6 */ {141,31,156,172}, staticText { enabled, “© 1987 Tim Standing” } } }; resource ‘DLOG’ (129,”Select Address”,preload,nonpurgeable) { {54,99,295,391}, dBoxProc, visible, noGoAway, 0x0, 129, “” }; resource ‘DITL’ (129,preload,nonpurgeable) { { /* 1 */ {195,75,215,135}, button { enabled, “Select” }; /* 2 */ {195,160,215,220}, button { enabled, “Cancel” }; /* 3 */ {55,75,75,115}, RadioButton { enabled, “#0” }; /* 4 */ {85,75,105,115}, RadioButton { enabled, “#1” }; /* 5 */ {115,75,135,115}, RadioButton { enabled, “#2” }; /* 6 */ {145,75,165,115}, RadioButton { enabled, “#3” }; /* 7 */ {55,175,75,215}, RadioButton { enabled, “#4” }; /* 8 */ {85,175,105,215}, RadioButton { enabled, “#5” }; /* 9 */ {115,175,135,215}, RadioButton { enabled, “#6” }; /* 10 */ {145,175,160,215}, RadioButton { disabled, “#7” }; /* 11 */ {160,175,175,285}, staticText { disabled, “(Mac’s Address)” }; /* 12 */ {12,35,30,258}, staticText { disabled, “Please select the SCSI address of” }; /* 13 */ {27,35,47,263}, staticText { disabled, “the hard disk you wish to format:” } } }; resource ‘DLOG’ (130,”Enter Parameters”,preload,nonpurgeable) { {33,43,330,469}, dBoxProc, visible, noGoAway, 0x0, 130, “” }; resource ‘DITL’ (130,preload,nonpurgeable) { { /* 1 */ {260,345,280,405}, button { enabled, “OK” }; /* 2 */ {230,345,250,405}, button { enabled, “Cancel” }; /* 3 */ {73,115,93,158}, RadioButton { enabled, “1:1” }; /* 4 */ {73,175,93,219}, RadioButton { enabled, “2:1” }; /* 5 */ {73,235,93,278}, RadioButton { enabled, “3:1” }; /* 6 */ {73,295,93,338}, RadioButton { enabled, “4:1” }; /* 7 */ {103,135,123,402}, RadioButton { enabled, “3.0 mSec (ST506-Nonbuffered Seeks)” }; /* 8 */ {123,135,143,375}, RadioButton { enabled, “28 uSec (ST412-Buffered Seeks)” }; /* 9 */ {143,135,163,415}, RadioButton { enabled, “12 uSec (Pseudo ST412-Buffered Seeks)” }; /* 10 */ {173,170,188,222}, EditText { disabled, “0” }; /* 11 */ {203,170,218,222}, EditText { disabled, “0” }; /* 12 */ {233,245,248,297}, EditText { disabled, “0” }; /* 13 */ {263,245,278,297}, EditText { disabled, “0” }; /* 14 */ {12,44,27,374}, StaticText { disabled, “Please enter the parameters for the disk that” }; /* 15 */ {27,44,43,390}, StaticText { disabled, “you wish to format. Enter a zero for each unused” }; /* 16 */ {43,43,58,303}, StaticText { disabled, “field. All numbers are decimal.” }; /* 17 */ {73,15,88,107}, StaticText { disabled, “Interleave:” }; /* 18 */ {103,15,118,130}, StaticText { disabled, “Step Pulse Rate:” }; /* 19 */ {173,15,188,144}, StaticText { disabled, “Number of Heads:” }; /* 20 */ {203,15,218,162}, StaticText { disabled, “Number of Cylinders:” }; /* 21 */ {233,15,248,239}, StaticText { disabled, “Reduced Write Current Cylinder:” }; /* 22 */ {263,15,278,240}, StaticText { disabled, “Write Precompensation Cylinder:” } } }; resource ‘DLOG’ (131,”Enter Defects”,preload,nonpurgeable) { {31,16,331,496}, dBoxProc, visible, noGoAway, 0x0, 131, “” }; resource ‘DITL’ (131,preload,nonpurgeable) { { /* 1 */ {10,400,30,460}, button { enabled, “OK” }; /* 2 */ {40,400,60,460}, button { enabled, “More” }; /* 3 */ {70,400,90,460}, button { enabled, “Cancel” }; /* 4 */ {120,50,135,85}, EditText { enabled, “0” }; /* 5 */ {120,115,135,140}, EditText { enabled, “0” }; /* 6 */ {120,160,135,210}, EditText { enabled, “0” }; /* 7 */ {150,50,165,85}, EditText { enabled, “0” }; /* 8 */ {150,115,165,140}, EditText { enabled, “0” }; /* 9 */ {150,160,165,210}, EditText { enabled, “0” }; /* 10 */ {180,50,195,85}, EditText { enabled, “0” }; /* 11 */ {180,115,195,140}, EditText { enabled, “0” }; /* 12 */ {180,160,195,210}, EditText { enabled, “0” }; /* 13 */ {210,50,225,85}, EditText { enabled, “0” }; /* 14 */ {210,115,225,140}, EditText { enabled, “0” }; /* 15 */ {210,160,225,210}, EditText { enabled, “0” }; /* 16 */ {240,50,255,85}, EditText { enabled, “0” }; /* 17 */ {240,115,255,140}, EditText { enabled, “0” }; /* 18 */ {240,160,255,210}, EditText { enabled, “0” }; /* 19 */ {270,50,285,85}, EditText { enabled, “0” }; /* 20 */ {270,115,285,140}, EditText { enabled, “0” }; /* 21 */ {270,160,285,210}, EditText { enabled, “0” }; /* 22 */ {120,290,135,325}, EditText { enabled, “0” }; /* 23 */ {120,355,135,380}, EditText { enabled, “0” }; /* 24 */ {120,400,135,450}, EditText { enabled, “0” }; /* 25 */ {150,290,165,325}, EditText { enabled, “0” }; /* 26 */ {150,355,165,380}, EditText { enabled, “0” }; /* 27 */ {150,400,165,450}, EditText { enabled, “0” }; /* 28 */ {180,290,195,325}, EditText { enabled, “0” }; /* 29 */ {180,355,195,380}, EditText { enabled, “0” }; /* 30 */ {180,400,195,450}, EditText { enabled, “0” }; /* 31 */ {210,290,225,325}, EditText { enabled, “0” }; /* 32 */ {210,355,225,380}, EditText { enabled, “0” }; /* 33 */ {210,400,225,450}, EditText { enabled, “0” }; /* 34 */ {240,290,255,325}, EditText { enabled, “0” }; /* 35 */ {240,355,255,380}, EditText { enabled, “0” }; /* 36 */ {240,400,255,450}, EditText { enabled, “0” }; /* 37 */ {270,290,285,325}, EditText { enabled, “0” }; /* 38 */ {270,355,285,380}, EditText { enabled, “0” }; /* 39 */ {270,400,285,450}, EditText { enabled, “0” }; /* 40 */ {10,15,25,375}, StaticText { disabled, “Please enter the list of defects that came with your” }; /* 41 */ {25,15,40,375}, StaticText { disabled, “disk drive. Enter the Head #, Cylinder #, and Bytes” }; /* 42 */ {40,15,55,375}, StaticText { disabled, “From Index (BFI) value for each defect. Enter” }; /* 43 */ {55,15,70,370}, StaticText { disabled, “defects in order of increasing cylinder #. If you” }; /* 44 */ {70,15,85,370}, StaticText { disabled, “have more than 12 defects, click on the More Button.” }; /* 45 */ {120,10,135,45}, StaticText { disabled, “#1:” }; /* 46 */ {150,10,165,45}, StaticText { disabled, “#2:” }; /* 47 */ {180,10,195,45}, StaticText { disabled, “#3:” }; /* 48 */ {210,10,225,45}, StaticText { disabled, “#4:” }; /* 49 */ {240,10,255,45}, StaticText { disabled, “#5:” }; /* 50 */ {270,10,285,45}, StaticText { disabled, “#6:” }; /* 51 */ {120,250,135,285}, StaticText { disabled, “#7:” }; /* 52 */ {150,250,165,285}, StaticText { disabled, “#8:” }; /* 53 */ {180,250,195,285}, StaticText { disabled, “#9:” }; /* 54 */ {210,250,225,285}, StaticText { disabled, “#10:” }; /* 55 */ {240,250,255,285}, StaticText { disabled, “#11:” }; /* 56 */ {270,250,285,285}, StaticText { disabled, “#12:” }; /* 57 */ {95,30,110,100}, StaticText { disabled, “Cylinder:” }; /* 58 */ {95,110,110,155}, StaticText { disabled, “Head:” }; /* 59 */ {95,165,110,200}, StaticText { disabled, “BFI:” }; /* 60 */ {95,270,110,340}, StaticText { disabled, “Cylinder:” }; /* 61 */ {95,350,110,395}, StaticText { disabled, “Head:” }; /* 62 */ {95,410,110,445}, StaticText { disabled, “BFI:” } } }; resource ‘ALRT’ (132,”Format Alert”,preload,nonpurgeable) { {100,100,250,400}, 132, { Cancel, visible,2; Cancel,visible,2; Cancel,visible,2; Cancel,Visible,2 } }; resource ‘DITL’ (132,preload,nonpurgeable) { { /* 1 */ {115,75,135,135}, button { enabled, “OK” }; /* 2 */ {115,165,135,225}, button { enabled, “Cancel” }; /* 3 */ {30,80,45,290}, StaticText { disabled, “Do you really want to format” }; /* 4 */ {45,80,60,290}, StaticText { disabled, “disk drive #^0? It will erase” }; /* 5 */ {60,80,75,290}, Statictext { disabled, “all the files on this volume.” } } }; resource ‘ALRT’ (133,preload,nonpurgeable) { {50, 40, 300, 460}, 133, { /* array: 4 elements */ /* [1] */ OK, visible, sound1; /* [2] */ OK, visible, sound1; /* [3] */ OK, visible, sound1; /* [4] */ OK, visible, sound1 } }; resource ‘DITL’ (133,preload,nonpurgeable) { { /* array DITLarray: 8 elements */ /* [1] */ {213, 180, 233, 240}, Button { enabled, “OK” }; /* [2] */ {19, 92, 34, 267}, StaticText { enabled, “A SCSI error has occurred!” }; /* [3] */ {70, 15, 85, 251}, StaticText { disabled, “SCSI command or trap that failed:” }; /* [4] */ {85, 35, 99, 290}, StaticText { disabled, “^0” }; /* [5] */ {115, 15, 130, 125}, StaticText { disabled, “Type of error:” }; /* [6] */ {130, 35, 145, 290}, StaticText { disabled, “^1” }; /* [7] */ {160, 15, 175, 200}, StaticText { disabled, “Most probable cause:” }; /* [8] */ {175, 35, 190, 405}, StaticText { disabled, “^2” } } }; resource‘STR ‘ ($A000,preload,nonpurgeable) { “_SCSIReset.” }; resource‘STR ‘ ($A100,preload,nonpurgeable) { “_SCSIGet.” }; resource‘STR ‘ ($A200,preload,nonpurgeable) { “_SCSISelect.” }; resource‘STR ‘ ($A300,preload,nonpurgeable) { “_SCSICommand.” }; resource‘STR ‘ ($A400,preload,nonpurgeable) { “_SCSIComplete.” }; resource‘STR ‘ ($A600,preload,nonpurgeable) { “_SCSIWrite.” }; resource‘STR ‘ ($000,preload,nonpurgeable) { “Test Unit Ready.”
}; resource‘STR ‘ ($400,preload,nonpurgeable) { “Format Unit.” }; resource‘STR ‘ ($1500,preload,nonpurgeable) { “Mode Select.” }; resource‘STR ‘ ($1003,preload,nonpurgeable) { “Write Fault.” }; resource‘STR ‘ ($1004,preload,nonpurgeable) { “Drive not ready.” }; resource‘STR ‘ ($1020,preload,nonpurgeable) { “Command not implemented.” }; resource‘STR ‘ ($1024,preload,nonpurgeable) { “Bad parameter passed to controller.” }; resource‘STR ‘ ($AA02,preload,nonpurgeable) { “SCSI communication error.” }; resource‘STR ‘ ($AA04,preload,nonpurgeable) { “Bad pseudo program.” }; resource‘STR ‘ ($AA05,preload,nonpurgeable) { “SCSI timing error.” }; resource‘STR ‘ ($2003,preload,nonpurgeable) { “Drive detects failure during power on diagnostic.” }; resource‘STR ‘ ($2004,preload,nonpurgeable) { “Drive not up to speed or hardware problem.” }; resource‘STR ‘ ($2020,preload,nonpurgeable) { “Controller does not support this command.” }; resource‘STR ‘ ($2024,preload,nonpurgeable) { “Defect in cylinder 0 or bad defect parameters.” }; resource‘STR ‘ ($BA02,preload,nonpurgeable) { “Wrong address or no termination.” }; resource‘STR ‘ ($BA04,preload,nonpurgeable) { “Programmer error; my fault.” }; resource‘STR ‘ ($BA05,preload,nonpurgeable) { “Program uses incorrect delay value for your controller.” }; resource ‘ALRT’ (134,preload,nonpurgeable) { {100, 100, 270, 400}, 134, { /* array: 4 elements */ /* [1] */ OK, visible, sound1; /* [2] */ OK, visible, sound1; /* [3] */ OK, visible, sound1; /* [4] */ OK, visible, sound1 } }; resource ‘DITL’ (134,preload,nonpurgeable) { { /* array DITLarray: 4 elements */ /* [1] */ {129, 120, 149, 180}, Button { enabled, “OK” }; /* [2] */ {20, 90, 35, 252}, StaticText { enabled, “No more defects can be” }; /* [3] */ {40, 90, 55, 263}, StaticText { disabled, “added to the defect list.” }; /* [4] */ {60, 90, 75, 263}, StaticText { disabled, “The buffer is full. The” }; /* [5] */ {80, 90, 95, 275}, StaticText { disabled, “first 60 defects were used.” } } }; resource ‘DLOG’ (135,preload,nonpurgeable) { {131, 131, 231, 381}, dBoxProc, visible, noGoAway, 0x0, 135, “” }; resource ‘DITL’ (135,preload,nonpurgeable) { { /* array DITLarray: 3 elements */ /* [1] */ {15, 20, 30, 230}, StaticText { enabled, “Now formatting SCSI disk #^0.” }; /* [2] */ {35, 20, 50, 224}, StaticText { disabled, “This will take between three” }; /* [3] */ {55, 20, 70, 166}, StaticText { disabled, “and fifteen minutes.” } } };
SCSI Formatter Insights
Steve Brecher
Sunnyvale, CA
(From Letters Column Vol. 3 No. 9)
There are some errors in the “SCSI Formatter Project, Part II” article in the June 1987 MacTutor, and I thought that corrections might be of help to readers who are implementing the project.
(1) The status byte which is returned to the initiator (the Mac) by a target device (disk controller) is not a target-specific error code. Rather, it is a generalized result. Bit 1 set indicates a “check condition,” or error. Hence, a status byte value of 2 indicates that an error occurred, but it does not indicate the nature of the error. To determine the cause of the error, a Request Sense command must be issued to the target. Then the target will send sense data which specifies the nature of the error that occurred.
This - not a bug in the SCSI Manager - is the reason that author Tim Standing observed a status value of 2 when an error occurred. This misunderstanding is reflected in the Formatter program code, which will not correctly diagnose or report error conditions.
In sum, there are three sources of error information: (a) the result code returned by a SCSI Manager function, e.g., phase error, which indicates whether there was some problem in communicating with the target device; (b) the status byte which is returned in the low half of the VAR “stat” integer parameter of the SCSIComplete call, which indicates whether or not an error occurred; and (c) the sense data that is returned by the target in response to a Request Sense command, e.g., unit not ready, write fault, etc., which indicates the exact nature of any error that occurred.
(2) Pseudo-programs, or Transfer Instruction Blocks (TIBs), are not part of the SCSI standard. Rather, they are an invention of Apple, and are found only in Apple’s SCSI Manager software. TIBs were implemented to permit the transfer of disk file sector tags along with each sector of disk data; the tags and the sector data are at different RAM addresses and must be read/written in an interleaved fashion (tags, sector data, tags, sector data...). TIBs permit multiple sectors with tags to be read or written with just one SCSICmd and one SCSIRead or SCSIWrite call. While some existing products implement file sector tags, Apple is no longer supporting tags for new products, so in that sense TIBs are implemented only for historical reasons.
Last, a few minor implementation notes:
The Mac OS provides a Delay routine which can be used in place of Tim’s CountOff subroutine.
The Control Manager provides a SetCtlValue routine which can be used to alter the setting of a radio button and redraw it to reflect the new setting.
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine