SCSI Formatter 2
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 Apples 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 Apples 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. Thats 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 EPROMs 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 its 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 havent 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 drives address cannot be 7 as this is the address of the Macintosh. If you use DIP switches or jumpers to set the drives 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 Apples 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 Standings 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 Standings 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
INCLUDEFormatEqu.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 its 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.
INCLUDEFormatEqu.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 Macs 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, dont 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
INCLUDEFormatEqu.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 DAs 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 doesnt 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 doesnt 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 doesnt 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 arent 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 defines 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,
(Macs 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
}
}
};
resourceSTR ($A000,preload,nonpurgeable) {
_SCSIReset.
};
resourceSTR ($A100,preload,nonpurgeable) {
_SCSIGet.
};
resourceSTR ($A200,preload,nonpurgeable) {
_SCSISelect.
};
resourceSTR ($A300,preload,nonpurgeable) {
_SCSICommand.
};
resourceSTR ($A400,preload,nonpurgeable) {
_SCSIComplete.
};
resourceSTR ($A600,preload,nonpurgeable) {
_SCSIWrite.
};
resourceSTR ($000,preload,nonpurgeable) {
Test Unit Ready.
};
resourceSTR ($400,preload,nonpurgeable) {
Format Unit.
};
resourceSTR ($1500,preload,nonpurgeable) {
Mode Select.
};
resourceSTR ($1003,preload,nonpurgeable) {
Write Fault.
};
resourceSTR ($1004,preload,nonpurgeable) {
Drive not ready.
};
resourceSTR ($1020,preload,nonpurgeable) {
Command not implemented.
};
resourceSTR ($1024,preload,nonpurgeable) {
Bad parameter passed to controller.
};
resourceSTR ($AA02,preload,nonpurgeable) {
SCSI communication error.
};
resourceSTR ($AA04,preload,nonpurgeable) {
Bad pseudo program.
};
resourceSTR ($AA05,preload,nonpurgeable) {
SCSI timing error.
};
resourceSTR ($2003,preload,nonpurgeable) {
Drive detects failure during power on diagnostic.
};
resourceSTR ($2004,preload,nonpurgeable) {
Drive not up to speed or hardware problem.
};
resourceSTR ($2020,preload,nonpurgeable) {
Controller does not support this command.
};
resourceSTR ($2024,preload,nonpurgeable) {
Defect in cylinder 0 or bad defect parameters.
};
resourceSTR ($BA02,preload,nonpurgeable) {
Wrong address or no termination.
};
resourceSTR ($BA04,preload,nonpurgeable) {
Programmer error; my fault.
};
resourceSTR ($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 Apples 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 Tims 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.