TweetFollow Us on Twitter

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 Apple’s implementation of the protocol has made writing SCSI software much easier. The second section describes in detail how a SCSI command is sent to a SCSI hard disk and how data transfer is accomplished. I use U2 Formatter as an example. In the last section, I give step by step instructions on how to use this program including comments as to what may go wrong at each step and how to fix it.

During low level formatting, the Macintosh sends information to the controller card describing the disk drive. This information includes both the physical characteristics (e.g. number or cylinders and number of heads) and the type of sectors that are to be used (sector size and interleaving). The controller card uses this information to write track markings on the disk surface so that information can be stored there. It also stores this information on a dedicated track on the disk surface so that it can be read into the controller card RAM when the power is turned on. Once the low level formatting has been completed, a program such as Apple’s Generic SCSI Installer (available from Apple Software Support) can be used to construct a files system, install a driver and write the boot blocks. Although the drive can store information after low level formatting, it cannot be recognized as a volume by the Macintosh until a file system has been constructed on it. (A SCSI driver will be the subject of a future article in this series)

The program Format should work with any hard disk that uses the Adaptec ACB-4000A controller card or the Adaptec ACB-4070 which is their RLL controller card. In addition, it should work with any of the Seagate disk drives that have built in controllers. These drives all have an “N” at the end of the model number (e.g. “ST225N”). I have referred to the documentation for a Seagate ST225N while writing this program.

I have developed this program using a 40 megabyte Seagate (ST4051) hard disk connected to an Adaptec ACB-4000A controller card. I have also tested it with a 155 megabyte CDC Wren III (see side bar article on the Wren drive.) It should work with other drives and controller cards, but I cannot guarantee it. There are subtle differences in the way different manufactures implement the SCSI commands that make writing a universal formatting program difficult.

The Wren III is the fastest drive I have used on the Macintosh. Using the time needed to assemble and link the Format program as a benchmark, the Wren III is more than 40% faster than a Data Frame XP-40. Disk Timer II rates the 155 Mbyte drive as 118 for write, 108 for read and 9 for access time with an interleave of 3:1. The average access time on the drive is 15 msec. That’s more than twice as fast as any other drive available for the Mac. In addition, the drive can transfer data at 1.25 Mbytes/second, which is as fast as the Mac II can read it in. The Wren III comes in three sizes: 86, 121, and 155 Mbytes which range in price from $1510 to $1710. They are available from Arrow Electronics 521 Weddell Drive, Sunnyvale, CA 94089, (800-325-3329).

The only problem with the Wren III is that the biege colored Macintosh Plus will not boot properly from them. If you buy a platinum Macintosh Plus, or get a copy of the ROMs from one, the Wren III will work fine. The EPROM’s you need to do this are 27C512, 150 nsec. They are made by Atmel and are available from Insight Electronics, San Diego, (619)-587-0471. (The ROMS in the platinum Mac Plus have a modified boot code that does a reset on power on and keeps polling until a hard disk responds. In the standard Mac Plus, they did a reset and poll in a loop of 1 second, which is shorter than the recovery tme of the Wren III to respond to a reset command. Hence, the Wren III will never come up! The new ROMS re-poll the driver when they get a drive not ready signal rather than resetting every second. This is not strict SCSI protocol and the people at CDC pointed this out to Apple, which then changed it’s ROMS. To get the ROMS, you need a friend in an Apple dealership who can work with you since the dealers are not allowed to swap the ROMS unless you fry your board.)

MPW

Before I get into the details of SCSI commands, I will say a few things about MPW. I switched from the MDS system to MPW because I got tired of waiting for a good linker to show up for MDS. MPW is powerful and allows you to develop in PASCAL, C or assembly language and link the results together. More importantly, the MPW environment provides a lot of tools that make development easier. In this section, I will describe both a modification that I have made to the “Make utility” to make it entirely automatic and a few bugs that I have found in the assembler that you should look out for. This program was developed using MPW version 1.0.1. I have included all the files that you will need for this project including the Makefile which establishes the dependency rules used for automatic assembly and linking. You will notice that the dependencies in the Makefile specify that the program is relinked anytime after the Rez tool is used. This is because Rez tends to trash the code resources in the program being built.

I have modified the Make command so that it automatically executes its output. By doing this, you can build your application by simply typing the command “Make”. The modification requires that you create a new folder inside the “MPW” folder called “Scratch”. The enclosed file “MakeIt” must then be placed in the “Tools” folder. Lastly, the line “Alias Make Makeit” must be placed in the UserStartup file.

Although I have not uncovered any bugs in the linker, the MPW assembler does not always create the instruction that you specify! On three occasions it assembled instructions incorrectly. In all three instances, I got the assembler to correctly assemble the instructions by either changing the instruction order or adding a label to the line above. If it looks like something funny is happening with your code, check that the executing code is what you think it is by using the disassemble feature in a debugger like TMON. Lets hope the 2.0 version will fix this bug as an assembler that lies is not much good for anything.

Lastly, I use the “dump” assembly directive to load symbols into a file. You will see that all the files like “Traps.d” are loaded in at the beginning of each source file. To use this you must place the following lines:

Dump ‘FileName.d’ 
end

at the end of each of the files in the AIncludes folder and then assemble these files. This will create a file called “Filename.d” for each file which is a lot like the packed symbol files in MDS and will make your assembly runs a lot faster.

SCSI Bus Phases

There are seven different states that the SCSI bus can be in. These are called Bus Phases and are determined by which device has control of the bus and what information is being transfered on the bus. The bus phases that the SCSI bus goes through during a SCSI command are: arbitration, selection, command, data, status, message and lastly bus free. During the arbitration phase, the Mac (the initiator) tries to gain control of the bus. If it is successful, the initiator tries to select your hard disk (target device) during a phase called the selection phase. Once a target device has been selected, the target determines what information will be transferred down the bus and when. The target also determines which direction the information travels down the bus. The target device then requests a SCSI command from the initiator in the command phase. If the SCSI command that was transfered during the command phase indicated that there was data to be transferred between the target and the initiator, the bus will then enter the Data phase. An example of this would be a read or write command where blocks of data are transfered down the bus. Once the target has transfered the number of bytes specified by the command, the bus enters the status phase. Here the target sends a single byte of information to the initiator. This status byte is an error code which describes any errors that may have occured. The status byte is cleared if the command was completed successfully. The message phase is used to transfer additional information between the target and the initiator. The Adaptec ACB-4000A always sends a clear byte in the message phase which is the command complete message. I have therefore ignored the message byte in my program. At the end of the message phase, the target determines that there is no more information to transfer down the bus and disconnects from the initiator. The bus then enters the Bus Free Phase where no device has control of the bus.

During all of these phases, timing is critical. After each bus phase change, there is a required amount of time necessary to let the signal settle. There is also a maximum time delay after which the target device will time out. There are also timing specifications for the arbitration phase that describe how long an initiator device should wait to determine if he has gained control of the bus. Apple decided to use the NCR 5380 chip to connect to the SCSI bus. This has several advantages including the fact that the chip performs many SCSI bus functions in hardware. It arbitrates for use of the bus, including performing retries, can select a target, send commands and transfer data. All of these actions are done in hardware. The timing is determined on the chip using a gate delay, rather than a clock pulse, which allows Apple to use the same chip in any computer regardless of the clock speed. (Of course, a vendor change or manufacturing quirk could change the average gate delay time, rendering some Macs less reliable than others.)

This SCSI code is collectively known as the SCSI Manager and includes traps for all types of SCSI operations. The _SCSIGet trap arbitrates for control of the SCSI bus. The _SCSISelect trap selects the target device. The _SCSICmd trap sends the command in the Command Phase. It includes a count for the size of the command which is either 6 or 10 bytes. The format and size of the various SCSI commands are described in the next section. Data is transfered by sending the correct SCSI read or write command using the _SCSIcmd trap. The Mac must then be instructed to transfer the data using one of the SCSI read or write traps. These traps use a pseudo program that describes the number of bytes to be transfered and where they are to be transfered to or from. I will discuss pseudo programs in detail later.

Lastly, the _SCSIComplete trap instructs the Mac to wait for the target to complete the command. It includes the number of ticks to wait for completion and returns the status byte and message byte. I have found that the Status byte is often returned with a value of 2. This should signify that the drive was unable to finish seeking to the desired track. A status byte equal to 2 is returned even for commands which cannot return this error code, or for commands which completed successfully (status byte should equal 0). Accurate status byte reporting was obviously not a prioirty for the software engineer at Apple who wrote the SCSI manager! This bug is in the Mac Plus, but I haven’t checked if it is still in the Platinum Mac Plus ROMS. Obviously, the Mac Plus ROMS have been changed considerably in the Platinum Macs and it is inexcusable that Apple does not at least offer the opportunity for Mac Plus owners to purchase new ROM sets.

SCSI Commands

SCSI commands are divided into two types: class 0 commands which are 6 bytes long and class 1 commands which are 10 bytes long. The data structure that includes all the command information (6 or 10 bytes) is called a command descriptor block (CDB). SCSI commands often contain fields that are less than 1 byte long. For example, bytes 0 and 1 of all SCSI commands contain four fields: the class code, the OP code, the logical unit number (for devices that have more than one disk drive connected to a single controller card) and 5 bits of command specific information. The format of these four fields is the same for all SCSI commands used in this program.

Some commands require more information than there is room for in the command descriptor block. In these commands, the command descriptor block includes a count byte which tells the SCSI hard disk how many bytes it should request from the Macintosh. The information is then transfered in the same way as in a read or write command, using a pseudo program and a _SCSIRead or _SCSIWrite trap. An example of this is the Mode Select command which is sent just prior to a Format Unit command, when formatting a SCSI hard disk. This information is included in a 24 byte parameter list and includes information such as write pre-compensation cylinder, reduced write current cylinder, cylinder count and head count.

Pseudo Programs

So what is this pseudo program and why not use a real program instead? The Pseudo program (Apple refers to them as Transfer Instruction Blocks) is a short subroutine of interpreted instructions. The ANSI standard for SCSI specifies the OP codes for pseudo instructions and the parameters used with them. Why were interpreted instructions specified? By specifying a set of OP codes for data transfer subroutines, the transfer functions can be performed by an I/O channel controller. This dedicated hardware would have these OP codes as its instruction set and would reduce the load on the CPU during I/O. This is important for multitasking systems.

While Apple adheres to this part of the ANSI SCSI standard, they have not used hardware to perform the transfer instructions. Instead, all Macintoshs interpret these instructions. This adherence to the standard comes at a price; lower transfer speed. The alternative method of writing transfer subroutines is to use instructions that can be executed directly by the MC 68000. Some SCSI hard disk manufactures (e.g. Super Mac Technologies and Micha) have used this method. This allows them to use a faster transfer rate and therefore use a smaller interleave. The driver in the Data Frame intializer version 2.1 uses a block of 256 “move.b (A0),(A2)+” instructions for blind reads and a block of 256 “move.b (A2)+,(A1)” instructions for blind writes. Although this method of writing data routines is faster than that employed by Apple, it is very dependent on timing to work properly. Any change in the transfer rate of the drive or the CPU speed (e.g. as in a Macintosh II) would cripple this technique, and require a modification by the manufacturer.

The SCSI Manager section of IM volume IV includes a description of each of the pseudo instructions and their parameters. All instructions are 10 bytes long; a word for the pseudo instruction OP code followed by two long word parameters. For a read or write operation, we simply create a loop with a transfer instruction and a branch with a loop counter. I have heard that the code which interprets the SCSI pseudo instructions is not terribly robust, so it is better to not try and do anything too fancy with it.

You will notice that the pseudo program that I have put in the WriteDisk routine could have been written with two instructions rather than the three that I used, by using the transfer byte count as a parameter in the SCInc instruction. Instead, I placed 1 here and made a loop which transferes only 1 byte at a time. I did this solely to demonstrate how one constructs a loop in the pseudo program.

Examples of SCSI commands

The simplest example of a SCSI command is found in the SelAddrProc procedure. Here I send a Test Unit Ready command to see if the target device is ready. I then test the Status byte to see if the target device is connected and ready to be formatted.

More complex examples of SCSI commands can be found in the FormatProc procedure. This procedure sends two commands including a Mode Select command which must be sent imediately before formatting the disk. This command tells the controller card what type of hard disk is connected to it and how to format it. As I explain in the source code comments, the mode select command is followed by a call to WriteDisk which is a subroutine that writes to the hard disk using the _SCSI write trap and a pseudo program. I then use the Format Unit command to format the hard disk. This command includes the interleave value and is followed by data which describes where the disk media defects are found.

A Few comments about error handling

I have included in the error dialog box, not only the cause of the error but its most probable cause. This is not always the reason you got this error, but it is 90% of the time. I check for all the SCSI command errors that you are likely to get at any part of the program. If you are getting Phase errors, the most likely problem is that the timing loop in the ResetDisk procedure is too short. Try increasing the delay from 60 ticks to 120 or 180 ticks. This will give your controller more time to complete a reset. Selection errors are always cabling or termination problems.

Step-by-Step

1). Connect your hard disk to the Macintosh and turn both devices on. Make sure that the hard disk is correctly terminated and note what SCSI address it has. The drive’s address cannot be 7 as this is the address of the Macintosh. If you use DIP switches or jumpers to set the drive’s address, you are setting it in binary, with closed switches signifying 1 and open switches signifying 0. The switch that closes the connection between pins A and B on the Adaptec ACB-4000A is the low bit on the SCSI Address.

2). Open the Format program and choose Select Address from the SCSI menu. Choose the address that you have given the disk drive and then hit the select button. The program then goes and resets the SCSI bus and tries to select the device. (If it did work, you will not see an error message.) If you get a drive selection error, the drive is either the wrong address, is not correctly terminated or you have incorrectly attached one of the connectors on the ribbon cable.

3). Next choose the Enter Parameters from the SCSI menu. Enter all the values for your drive. If your drive does not need a parameter, use the default value as this will send a 0 in this field and will work properly. The CDC Wren III only needs to be told which interleave to use, so this is the only thing you need to enter in this dialog box. If you are uncertain of which value to use for the step rate, use the 25 msec value. All but the most ancient drives will work correctly with this value. To determine which interleave to use, you must use trial and error. The value that is most likely the best is 3:1. To determine the correct value, you must format your drive using different interleave values. Then install a driver and copy a large file or folder onto the disk. I use a 1.2 Mbyte system file for this purpose. Now determine the amount of time it takes to duplicate the file. Do this with each interleave value. The value that gives you the shortest time for duplicating the file is the one you should use.

4). Now choose Enter Defects from the SCSI menu. You can enter up to 60 defects. You should have received a list of defects when you purchased the drive. Some of the better drives contain this information in firmware on the controller card and you can skip this step. Drives of this type include the CDC Wren III and the Seagate “N” drives (e.g. the ST225N). Note that you must enter the defects in order of increasing cylinder number and you are not permited to have any defects on cylinder 0.

5). Next choose Format from the SCSI menu. You should see the drive activity light (that little red LED) light up on your drive and it will probably stay on for between 3 and 15 minutes (the Wren III takes 18 minutes and my Seagate ST4051 takes 2). Your drive is now being formatted. You should also be able to hear the head postioning motor or voice coil moving the head as it formats the drive. If the light stays on for more than 20 minutes, or if you cannot hear the head moving, something is probably wrong. This is not always the case as I cannot hear the head moving on the Wren III when it is formatting. Be patient the first time.

6). Now use a driver installer program to install a driver on your hard disk. You can use Apple’s Generic SCSI Installer for this purpose, but be forwarned that this driver has bugs and the data on your drive will eventually become corrupted and lost after about 20 hours of use. There are a few public domain programs that will also do this. Or you can wait for my next article!

My next MacTutor article will include a driver that works on all three Macintoshes (Mac Plus, Mac SE and Mac II). I will include subroutines that can be added to this program to allow you to install this driver.

Build you own Macintosh Hard Disk using the Control Data Wren III

Paul L. Derby, Control Data Corporation

P.O. Box 0,Minneapolis, MN 55420-2028

612-853-5986

With the introduction of the Macintosh Plus, connection of computer peripheral devices that attach to the Small Computer Standard Interface bus (SCSI connection) is fairly straightforward. Quite a few SCSI hard disk drives are available on the market with capacity and prices running from 20MB for around $600 to 172MB for around $6,000. There is also quite a variance in drive quality and reliability. If you are willing to put together a few pieces to make your own disk drive, you can have a top quality Wren III 156MB drive made by MPI, Inc. for around $2,000. (This is the same drive Sony, a disk manufacturer, chose to place in their commercial workstation hardware.)

Here are some of the technical specifications on the Wren III: 16.5 ms average seek time with a 10 Mbits/second data transfer rate. A SCSI controller is integral to the drive as well as termination resistors. Typical average seek time is 18.5ms with controller overhead. If the head is already positioned single track seek time is 6 ms. Seek time with full stroke of the read/write head is 42ms. It takes about 15 minutes to format the drive. Sector interleave is 1 to 1, although the speed of the Mac SCSI bus cannot keep up with the data transfer rate of the Wren. The SCSI controller takes care of transferring the data to the Mac at the speed the Mac can handle.

The Wren is very reliable with a service life rating of 30,000 hours (5 years).

If you want to put together you own Wren disk system, there are 4 pieces to acquire: (1) the actual disk unit with the integral controller, (2) a cabinet with power, fuse, fan, and mounting rails, (3) the connectors and cables to attach the drive to the Macintosh, and (4) software to format, install, and access the disk.

The disk is available from a number of electronics distribution companies such as Arrow Electronics, Kierulff Electronics, MTI, and Duccomun. The disk is an MPI Wren III (or a CDC Wren III), model number 94161-155 and sells for around $1850.

An enclosure for the disk that includes a 75 watt switching power supply, fuse, fan, on/off switch, power cord and mounting rails, plus the cut outs to accommodate the two SCSI connectors is available from Microware Inc. for $185.

Two cables are needed to connect the Wren to the Mac. One cable is needed to connect the controller on the Wren III to the connector on the rear of the enclosure. The other cable goes from the disk enclosure case to the Macintosh. Apple sells the second cable that goes from the DB25 connector on the rear of the Mac to the back panel of the disk enclosure. The first cable you build as follows:

Buy two industrial Amphenol 50 pin F57 connectors (or equivalent) and one 50 pin IDC connector. Use a bench vise to press the connectors onto 50 conductor flat ribbon cable. Thread the ribbon cable through the cutouts on the back panel of the disk enclosure before pressing the connectors onto the cable.

Assembly of the unit is very straightforward. Remove the power supply by unscrewing the two screws that secure it from the bottom of the enclosure. Slide the Wren into the hole in the front of the disk enclosure and secure it with four 6-32 screws and washers. Connect the ground wire from the power supply to the ground spade on the Wren. Plug the power supply cable into the Wren power socket. At this time it is a good idea to power up the drive and make sure the voltage from the power supply is within specification for the Wren. The 5 volt output should be between 4.75 and 5.25 volts with the drive running. If it is not within these voltage limits then you gently rotate the trim potentiometer located at the bottom center of the power supply. Set the 5 volt line as close to 5 volts as you can and then check the 12 volt side to make sure it is 12 volts ±5%. When both the 5 and 12 volt supplies are within tolerance, power down the drive and secure the power supply board. Install the cable between the Wren and the rear panel of the enclosure. Attach the Mac and you are ready to use software to prepare and use the drive.

There are 3 pieces of software needed to use the Wren on the Mac Plus. A disk format program is needed to format the disk into 512 byte sectors (See Tim Standing’s Format program in this issue). After formatting, an installer program is run to place the correct SCSI driver information on the disk. The last piece of software is a special file called an INIT file that is placed in the system folder on the floppy disk used to boot the Macintosh. This file loads the driver during boot time. On the Mac SE, the INIT file is not needed and the Wren drive can be booted directly. These pieces of software are available from an independent software development company, Carl Nelson & Associates, for a very reasonable licensing fee. You can also use Tim Standing’s software that he is presenting in a series of articles in MacTutor.

After the disk is initialized and the install process completed, using the drive is fairly straightforward. You use a floppy with the special INIT file in the system folder to boot up the Mac Plus. All the Wren drives that are on line will then come up. To run from the Wren double click on the FINDER icon in the system folder on the Wren to switch-launch to the Wren. The disk icon for the Wren will then be in the upper left position on the desktop and you can eject the floppy that was used for booting. The reason a floppy has to be used to bring the drive up is because Apple does not follow the SCSI protocol in the code located in the Mac Plus Roms. If you try to boot the Wren directly, the Apple SCSI routine in the ROM causes the Wren to hang in a loop. The Wren SCSI implementation strictly follows the ANSI specification. The MAC II and MAC SE ROMS fix this problem.

Even with the hassle of booting the Mac from a floppy on the older Macintosh systems, the price, speed and capacity of the Wren are well worth the slower boot up process. This drive is especially suited as a file server an a Mac running Apple Share.

Here are some more details on where to obtain parts:

Wren III disk, Model 94161-155, SCSI interface, 512 byte sectors. Distributors are Arrow, MTI, Duccomun Data, and Kierulff. You can call Dave Johnson at Kierulff in Minneapolis at 612-941-7500 to find out which Kierulff office has the drives in stock and the closest outlet to you. Kierulff sells the drive for around $1850.

The disk case and power supply are available from Microware Inc., 41711 Joy Road, Canton, MI 48187, telephone 313-459-3557. You need a Beige tape case with a 75 watt power supply, part number 900014. This unit includes a fuse, fan, line cord and switch. It will run either 110V or 220V by moving a jumper on the power supply card. The power supply also runs fine on a 220V to 110V transformer if you want to use it temporarily on European trips. The enclosure costs around $185.

Amphenol 50 pin F57 type receptor connectors. Part number 850-57F-40500-20. These connectors cost about $8.27. They are solderless and not reusable so you may want to buy an extra just to practice on. In Minneapolis the only industrial Amphenol Distributor is Newark Electronics at 612-331-6350.

50 conductor flat ribbon cable is available most anywhere in 100 foot rolls. Gopher Electronics, 222 E. Little Canada Rd, St. Paul, telephone 612-483-3322, will sell this stuff by the foot. All you need i s about 2 feet to make the cable with some left over for mistakes.

The 50 pin IDC connector to the Wren is a Belden 8S0J050 or an A P Products 925110-50-R. These cost around $7.00 each and you need one. Both Newark and Gopher Electronics sell these.

The software is available from Carl Nelson & Associates, 4007Nassau Place, Everett, WA, 98201-4855 telephone 206-252-6897.

You will need four 6-32 x 3/8" pan head screws and washers to mount the disk in the enclosure. You will need four 4-40 x 3/8" screws, nuts, washers, and lock washers to mount the two connectors in the disk enclosure back panel.

Continued in next frame

Volume Number:3
Issue Number:6
Column Tag:Advanced Mac'ing (code)

SCSI Formatter Project, Part II (code)

By Tim Standing, University of Calif. at SF

#Tim Standing
#2/24/87
#An automatic routine to build my output for me.
#
Unalias make
{mpw}tools:make -f {tardir}makefile > {Scratch}makeout
{Scratch}makeout
exit

# File: MakeFile
#
# MakeFile for Formatting program for MacTutor An Low 
# Level formatting of a SCSI hard disk drive. Note if  
# resource file has been changed, run first Rez then 
# Link.  Rez sometimes trashes code resources.

Format  ƒƒFormat.r
 Rez -p Format.r -o Format
Format  ƒƒFormat.a.o BuffStuff.a.o SCSI.a.o Format.r
 Link -p Format.a.o BuffStuff.a.o SCSI.a.o 
 -o Format -l > Format.Map
Format.a.oƒ Format.a FormatEqu.a
 Asm -p -wb Format.a
BuffStuff.a.o  ƒ BuffStuff.a FormatEqu.a
 Asm -p -wb BuffStuff.a
SCSI.a.oƒ SCSI.a FormatEqu.a
 Asm -p -wb SCSI.a


;*********** File FormatEqu.a ****
;**********************************
;
; A list of equates used in the SCSI formatting program.

; Menu Bar resource numbers

AppleM  EQU 128
FileM   EQU 129
EditM   EQU 130
SCSIM   EQU 131

; Dialog box resource numbers
AboutRN EQU 128
SelAddrRN EQU    129
ParamRN EQU 130
EDefectsRNEQU    131
AboutItem EQU    1

DialWLenEQU 170  ;lenght of window storage for
 ;dialog box

; Event Processing Equates

AllEvents EQU    $0000FFFF
EventMask EQU    $FFFF

; Codes used in error dialog box for SCSI Manager traps.
Reset   EQU $A0
Get     EQU $A1
Select    EQU    $A2
Command   EQU    $A3
CompleteEQU $A4
Read    EQU $A5
Write   EQU $A6
Install   EQU    $A7
Stat    EQU $A10

; Codes used in error dialog box for SCSI commands.
RezeroUnitEQU    1
TestUReadyEQU    0
FormatUnitEQU    4
ModeSelectEQU    $15

MaxTicksEQU 108000 ;max # of ticks to wait for
 ;a SCSI command to complete
MaxAddr EQU 6    ;largest SCSI address
MaxCylind EQU    2048;largest number of cylinders 
MaxHeadsEQU 16   ;largest number of heads 
MaxBFI  EQU $10000000;bigest Bytes From Index value

; Offsets for input buffer used when inputting disk surface 
; defect information (relative to the beginning of that record).
CylBBuffEQU 0    ;offset for cylinder field
HeadBBuff EQU  4 ;offset for head field
BFIBBuffEQU 8    ;offset for bytes from index field

; Offset for each record in output buffer (relative
; to the beginning of that record).
CylOut  EQU 0    ;Offset for cylinder 
HeadOut EQU 3    ;Offset for head 
BFIOut  EQU 4    ;Offset for byte from index

; Equates for buffer sizes.  Note there are two buffers for 
; defects, one for input, another reformated one for output
; during the Format Unit command.
MaxBBs  EQU 60   ;Maximum number of defects
BadBRecLenEQU    16;size of each defect record for
 ;input buffer
MaxBBListLenEQU  MaxBBs*BadBRecLen ;length of defect buff
 ;for input
MaxBBOutLen EQU  (MaxBBs*8)+4 ;length of defect buffer
 ; for output during Format
 ;Unit command

ModeSelBytesOut  EQU 22 ;bytes transfered in
 ; Mode Select command

ParamBuffLenEQU  30;length of SCSI command
 ;parameter buffer
CBuffLenEQU 10   ;length of command buffer
StrBuffLenEQU    256 ;length of string buffers
CommLenSEQU 6    ;lenght of class 0 commands
CommLenLEQU 10   ;length of class 1 commands
PProgLenEQU 30   ;Pseudo program buffer length
CylOut_LB EQU    2 ;Offset for cylinder low byte
CylOut_MB EQU    1 ;Offset for cylinder middle byte
CylOut_HB EQU    0 ;Offset for cylinder high byte


;***** File:BuffStuff.a *************
;************************************
;
; This file contains a couple of subroutines that are used for 
; manipulating the buffers.

 BLANKS ON
 
 IMPORT (BadB_List,BadB_Num,BadB_Out):DATA
 INCLUDE‘FormatEqu.a’
 

; PROC ClearBuff 
;
; This is used to clear a buffer.  It gets the address
; in A0 and the length in bytes in D0 (long word value).

ClearBuff PROC EXPORT;subroutine for export to other
 ;moduals at link time.
 
 movem.lD0/A0,-(SP);save registers
 subq #1,D0 ;D0=count-1; branch after clr.b
 blt.s  exit;exit if passed buffer length =0
 
buf_loop
 clr.b  (A0,D0.L);clear the byte
 dbra D0,buf_loop;branch when D0>0
exit
 movem.l(SP)+,D0/A0;restore the registers
 rts

 ENDPROC

; PROC OutBuff
; OutBuff takes list of bad blocks and reformats so it can be 
; used as the defect data block that gets transfered after the 
; format command. First we place the size of the defect data 
; block minus the 4 byte header in byte 3. Then we load
; in the bad block records.  They must have aready been sorted 
; in order of increasing cylinder number.  The fields in each
; record are: 3 bytes for the cylinder, one byte for
; the head number and a long word for the bytes 
; from index value. 
;
; Registers:
; A0  Points to curent position in BadB_List 
;(our sorted list of bad blocks used for input)
; A1  Points to current position in BadB_Out
;(our output buffer used in formatting)
; D1  Scratch
; D0  Loop index

OutBuff PROCEXPORT 
; Equates for offset at beginning of bad block output buffer:
ByteCount EQU    3

 movem.lD0-D1/A0-A1,-(A7) ;save registers on stack
; Clear the output buffer before we start transfering records.
 lea    BadB_Out(A5),A0   
 move.l #MaxBBOutLen,D0
 jsr    ClearBuff
 
 move.l BadB_Num(A5),D0 ;get the number of defects
 beq    exit;exit if it’s zero
 subq.l #1,D0    ;adjust D0 (dbra after transfer)

 lea    BadB_List(A5),A0  ;initialize A0
 lea    BadB_Out(A5),A1 ;initialize A1
 clr.l  D1;clear scratch
 move.l BadB_Num(A5),D1
 lsl.l  #3,D1    ;multipy count by 8 bytes
 move.b D1,ByteCount(A1)  ;place LSB of count in header
 adda.l #4,A1    ;incr output pointer to 1st rec.
 
loop
 move.l CylBBuff(A0),D1 ;load cylinder# in scratch 
 move.b D1,CylOut_LB(A1)  ;move low byte into record
 lsr.l  #8,D1    
 move.b D1,CylOut_MB(A1)  ;move middle byte into record
 lsr.l  #8,D1
 move.b D1,CylOut_HB(A1)  ;move high byte into record
 move.b 3+HeadBBuff(A0),HeadOut(A1)
 ;move head number into record, 
 ;moving byte value of long
 move.l BFIBBuff(A0),BFIOut(A1)
 ;transfer Byte_From_Index value
 adda.l #16,A0   ;A0 points to next input record
 adda.l #8,A1    ;A1 points to next output record
 dbra D0,Loop    ;decrement loop counter & repeat
 
exit  
 movem.l(A7)+,D0-D1/A0-A1 ;restore registers
 rts
 ENDPROC
 END

;********* File: SCSI.a ***********
;**********************************
;
; This file contains all the items in the SCSI menu.  It also
; contains the SCSI primatives for comunication with SCSI hard 
; disks.  
;
; Note: All the symbols that are declared or imported before 
; start of the first module (PROC, MAIN or RECORD) are defined
; for all modules in this file.  Forward references to other
; modules in a file must be imported. 

 INCLUDE‘FormatEqu.a’
 LOAD   ‘Traps.d’
 LOAD   ‘ToolEqu.d’
 LOAD   ‘SysEqu.d’
 LOAD   ‘QuickEqu.d’
 LOAD   ‘SCSIEqu.d’

 EXPORT (Res,Last_Cmd,CompStat,CompMsg):DATA
 EXPORT (BadB_Num):DATA
 
Addr    ds.w1  ;address of hard disk
Heads   ds.l1  ;number of heads on hard disk
Cyls    ds.l1  ;number of cylinders on hard disk
WrtPreCompds.l 1 ;cylinder to start write precomp
RedWrtCur ds.l 1 ;cylinder to start reduced write cur
Interleaveds.w 1 ;interleave number to be used
StepCodeds.w1  ;type of step pulse used
 
CompStatds.w1  ;status from _SCSIComplete
CompMsg ds.w1  ;message from _SCSIComplete
 
Resds.w 1 ;result from SCSI commands 
Last_Cmdds.w1  ;last command executed

BadB_Numds.l1  ;total number of bad blocks
 IMPORT (DialogPort,ItemType,ItemHandle,ItemRect):DATA
 IMPORT (EventLoop,DefButton,RadioHiLite):CODE
 IMPORT (CountOff,ExtractNumb,SortBuff,OutBuff):CODE
 IMPORT (ErrorProc,ClearBuff):CODE
 IMPORT (DialogBuff,ButtonHit):DATA
 IMPORT (StrBuff1,StrBuff2,StrBuff3,StrBuff4):DATA
 
Buffers RECORD EXPORT
 EXPORT (CommandBuff,PseudoProg,ParamBuff):DATA
 EXPORT (BadB_List,BadB_Out):DATA
 ALIGN  2
 
CommandBuff ds.b CBuffLen ;buffer for SCSI commands
PseudoProgds.b PProgLen ;buffer for pseudo program
ParamBuff ds.b ParamBuffLen ;buffer for parameters
BadB_List ds.l MaxBBListLen ;buffer area for entering
 ; and sorting bad blocks
BadB_Outds.lMaxBBOutLen   ;buffer area for output to
 ;to the disk drive

 ENDR
 
; PROC  SelAddrProc
;
; Gets the address of the device to be formatted
; from the user. Then tests to see if device is connected
; and if device is ready to be formatted. If either of these
; are false, it jumps to ErrorProc (error handling procedure).

SelAddrProc PROC EXPORT
 IMPORT (Hd_Select,HD_TestUnit,HD_Discon):CODE
 IMPORT (UnHiLite,ResetDisk):CODE
 
 clr.l  -(A7)    ;space for pointer to dialog
 move.w #SelAddrRN,-(A7)  ;select addr dialog resource#
 pea    DialogBuff(A5)    ;push pointer to dialog stor
 move.l #-1,-(A7);make dialog top most window
 _GetNewDialog
 
 move.l (A7),DialogPort(A5) ;save pointer to dialog
 ;leave a copy on the stack  
 ;for call to _SetPort
 _SetPort
 lea    DialogBuff(A5),A4 ;pass address of dialog record
 jsr    DefButton;make default = “Select”
 move.w #10,D0   ;make Mac’s address grey
 jsr    UnHiLite
 
; Register ussage:
;D0Radio button that was selected, now must be unselected
;D1Radio button that was just clicked on to be selected

; Initialize D0 and D3 for first call to RadioHiLite.  D0=0 
; is flag for no radio button to deselect.  D1=3 is to set the
; default SCSI address = 0.
 clr.l  D0
 moveq.l#3,D7    
   
NotYet
 move.w D7,D1    ;update D1 (pass to RadioHiLite)
 jsr    RadioHiLite;change radio button selected
 clr.l  -(A7)    ;use standard filter proc func
 pea    ButtonHit(A5)
 _ModalDialog
 cmpi.w #2,ButtonHit(A5)  ;was select or cancel hit
 ble    EndDialog;yes, so close up dialog
 move.l D7,D0    ;D0 = id of old radio button
 move.w ButtonHit(A5),D7  ;D7 = id of new radio button
 bra.s  NotYet   ;loop back for next mouse down
 
EndDialog
 move.l DialogPort(A5),-(A7);pass addr of Dialog Rec
 _CloseDialog    ;close dialog
 cmpi.w #2,ButtonHit(A5)  ;was ok button hit
 blt    Continue ;yes
 jmp    EventLoop;no, go get next event

Continue
 subq.w #3,D7    ;D7 = last radio button hit so
 ;subtract item # for radio
 ;button “0”
 move.w D7,Addr(A5);D7 now equals address selected
 
 jsr    ResetDisk;reset SCSI bus
 tst.w  Res(A5)  ;did we succeed
 bne    ToErrorProc;no, go handle error

; This next block of code, through end of this procedure is
; an example of how all SCSI commands are done.  You can
; replace the SCSI command Test Unit Ready with any other 
; SCSI command.
; The Mode Select and Format Unit commands used in FormatProc 
; differ only in that data is transfered after the command.


; First get control of the bus and select target device.
 jsr    HD_Select;go get control of bus and
 ;select device
 tst.w  Res(A5)  ;did we succeed
 bne    ToErrorProc;no, go handle error

; Second, we clear the buffer we use to construct the SCSI
; command in.
 lea  CommandBuff(A5),A0  ;pass address of buffer in A0
 move.l #CBuffLen,D0 ;pass buffer size in D0
 jsr    ClearBuff;clear buffer

; Third, we construct the command.  This one has no parameters
; in the CDB (Command Desciption Block), so only have to move
; the SCSI OP code into the CDB.

 move.b #TestUReady,CommandBuff(A5); move OP code into byte
 ; 1 of the CDB
 clr.w  -(A7)    ; clear space for trap result
 pea    CommandBuff(A5) ; pass address of CDB
 move.w #CommLenS,-(A7) ; pass length of command.
 ; This is a class 0 SCSI
 ; command, length is 6 bytes.

; Fourth, we send the command and test if it succeded.
 _SCSICmd ;send command to target
 
 move.w #TestUReady,Last_Cmd(A5) ;store command id
 move.w (A7)+,Res(A5);test result, an error?
 bne    ToErrorProc;yes go to error proc

; Then, we wait for the target device send the Status and
; Message bytes.  The target device will then release the Mac.
 move.l #10,D0   ;pass number of ticks to wait
 jsr    HD_Discon;proc that handles _SCSIComplete
 tst.w  Res(A5)  ;SCSI Protocal Error?
 bne    ToErrorProc;yes, go handle display error

; Lastly, we test the Status byte returned by _SCSIComplete.  
; This is the SCSI command result code.  It will tell us about
; the drives condition and if it is ready to be formated.

 move.w #TestUReady,Last_Cmd(A5) ;store command id for 
 ;error handling proc
 cmp.w  #3,CompStat(A5) ;is there a Write Fault?
 beq    ToErrorProc;yes, go to error handler
 cmp.w  #4,CompStat(A5) ;is the Drive Not Ready?
 beq    ToErrorProc;yes, go to error handler
 cmp.w  #20,CompStat(A5)  ;SCSI command supported?
 beq    ToErrorProc;no, go to error handling proc
 jmp    EventLoop

ToErrorProc
 jmp    ErrorProc
 
 EndProc

; PROC ParamProc
;
; User is given dialog box to input parameters used
; for  formatting drive. We then extract the values from the
; TextEdit fields and place the hex values in the appropiate
; global variables.  All values are checked against maximum 
; values to make sure they are reasonable.

ParamProc PROC EXPORT

 clr.l  -(A7)    ;space for pointer to dialog
 move.w #ParamRN,-(A7)  ;parameter dialog resource numb.
 pea    DialogBuff(A5)  ;push pointer to dialog storage
 move.l #-1,-(A7);dialog on top of all windows
 _GetNewDialog
 move.l (A7),DialogPort(A5) ;save pointer to dialog 
 _SetPort
 lea    DialogBuff(A5),A4 ;pass address of dialog record
 jsr    DefButton;make “OK” button the default

; Now we set up our dialog box. First two calls to RadioHiLite
; have D0=0 as a flag for no radio button to unselect
; Register ussage:
;D0button that was selected, now must be unselected
;D1button that was just clicked on, to be selected
;D6Interleave Button that is currently selected
;D7Step Rate Button that is currently selected
 moveq.l#3,D6    ;default interleave = 1:1
 moveq.l#7,D7    ;default step pulse = 3 msec

; High light default interleave and step pulse radio buttons.
 move.w D6,D1    
 clr.w  D0
 jsr    RadioHiLite
 clr.w  D0
 move.w D7,D1
 jsr    RadioHiLite 
  
NotYet
 clr.l  -(A7)    ;use standard filter proc func
 pea    ButtonHit(A5);push addr of storage for item#
 _ModalDialog
 move.w ButtonHit(A5),D1  ;move item number to D1
 cmpi.w #2,D1    ;was OK or Cancel selected?
 ble    EndDialog;yes, go close up dialog
 cmpi.w #9,D1    ;was text edit item hit
 bgt    NotYet   ;yes, loop back for next event
 cmpi.w #6,D1    ;was interleave radio button hit
 bgt    StepRate ;no, must be step rate button

; Interleave radio button was selected. Update D6 and select
; the correct radio button.
 move.w D6,D0    
 move.w D1,D6    
 jsr    RadioHiLite
 bra.s  NotYet
 
; Step rate radio button was selected. Update D7 and select
; the correct radio button.
StepRate
 move.w D7,D0
 move.w D1,D7
 jsr    RadioHiLite
 bra    NotYet
 
EndDialog
 cmpi.w #2,ButtonHit(A5)  ;was “Cancel” selected?
 blt    Continue ;no
 bra    exit;yes, so exit
; “OK” button selected so we can update the global variables
; that contain the step code and the interleave values.
Continue
 subq.w #2,D6
 move.w D6,Interleave(A5)
 subq.w #7,D7
 move.w D7,StepCode(A5)

; For each of the TextEdit fields we place the address of the
; global variable in A3, the item # in D0 and maximum value
; in D3.  We then call ExtractNumb to get the hex, insure that
; it is not over the maximum value, and place hex number in
; the global variable.
 lea    Heads(A5),A3 ;extract number of heads from 
 move.l #10,D0   ;item number 10
 move.l #MaxHeads,D3
 jsr    ExtractNumb

 lea    Cyls(A5),A3;extract number of cylinders
 move.l #11,D0   ;from item number 11
 move.l #MaxCylind,D3
 jsr    ExtractNumb

 lea    RedWrtCur(A5),A3  ;extract reduced write current
 move.l #12,D0   ;cylinder from item #12
 move.l Cyls(A5),D3
 jsr    ExtractNumb

 lea    WrtPreComp(A5),A3 ;extract write precompensation
 move.l #13,D0   ;cylinder from item #13
 move.l Cyls(A5),D3
 jsr    ExtractNumb
 
Exit
 move.l DialogPort(A5),-(A7);pass ptr to Dialog rec
 _CloseDialog    ;close dialog box
 jmp    EventLoop;return to event loop
 EndProc

; PROC EDefectProc
;
; Here we ask the user to enter the defects that are listed on
; the sheet that came with their disk drive. Can enter as
; many as 60 defects.  Since dialog box only has room for 12
; defects, we ask the user to hit “More” button if they have
; additional defects.  We give the user a dialog box when they
; cannot enter any more defects.
;
; Register Ussage:
;D0Index for next item in dialog is EditText that
;we are going to remove a value from
;D6Index for record being input in defect list
;index is byte index of first field in record,
;not actual record number
EDefectProc PROC EXPORT

 clr.l  BadB_Num(A5) ;clear global for # of defects
 ; Clear our defect buffer.
 move.l #MaxBBListLen,D0  ;pass buffer size in D0
 lea    BadB_List(A5),A0  ;pass address in A0
 jsr    ClearBuff;go clear buffer
 
LoopForMore
 clr.l  D6;clear buffer record index
 clr.l  -(A7)    ;space for pointer to dialog rec
 move.w #EDefectsRN,-(A7) ;Enter Defects Dialog res#
 pea    DialogBuff(A5)    ;pass ptr to dialog storage
 move.l #-1,-(A7);make this window on top
 _GetNewDialog   ;get dialog
 move.l (A7),DialogPort(A5) ;save pointer to dialog 
 _SetPort
 lea    DialogBuff(A5),A4 ;pass address of dialog record
 jsr    DefButton;outline default button
    
NotYet
 clr.l  -(A7)    ;use standard filter proc function
 pea    ButtonHit(A5);place for button hit
 _ModalDialog    ;handle dialog events
 cmpi.w #3,ButtonHit(A5)  ;is “Cancel” button hit?
 blt    EndDialog;no, “More” or “OK” buttons were hit
 bgt    NotYet   ;no, not a button, get another event

; User hit “Cancel” button, so clear defect count and return
; go close dialog box
 clr.l  BadB_Num(A5) 
 bra    exit

; We want to close up the dialog, but first we must extract
; the defect data that the user has entered.
EndDialog
 move.l BadB_Num(A5),D6 ;move number of defects to D6
 lsl.l  #4,D6    ;multiply by 16 to get byte 
 ;index for first field in
 ;next empty record
 lea    BadB_List(A5),A0  ;A0 points 1st byte of buffer
 
AddToBuff
 move.l #3,D0    ;first item is 4, so put 3 in D0
 
LoopAddBuff
; First we extract the cylinder number.  
; We use the ExtractNumb procedure 
; which takes the address for the result in A3, the 
; maximum value in D3 and the item number in D0.
 
 addq.l #1,D0    ;increment D0 by 1, next item
 lea    CylBBuff(A0,D6.L),A3
 move.l #MaxCylind,D3
 jsr    ExtractNumb
 
; Next we extract the head number
 addq.l #1,D0
 lea    HeadBBuff(A0,D6.L),A3
 move.l #MaxHeads,D3
 jsr    ExtractNumb
 
; Lastly we extract the Bytes From Index value.
 addq.l #1,D0
 lea    BFIBBuff(A0,D6.L),A3
 move.l #MaxBFI,D3
 jsr    ExtractNumb

; Test to see if the cylinder number was 0.  If so ignore this
; record as it was either empty or invalid.  Most controller
; cards cannot take defects in track 0.
 tst.l  CylBBuff(A0,D6.L) ;is cylinder value valid
 bne    IncrCounters ;yes increment counters

; Otherwise, don’t increment counters and next record will 
; overwrite this one which was invalid.
 cmpi.l #39,D0   ;have we reached the last item
 beq    Exit;yes, go close dialog box
 bra    LoopAddBuff;no, go extract next record
 
; Cylinder number is not equal to 0 so this is a valid defect.

IncrCounters
 add.l  #BadBRecLen,D6    ;increment buffer index
 cmpi.l #(MaxBBs*BadBRecLen),D6  ;is our buffer full
 beq.s  Exit;yes, so exit
 
 
 cmpi.l #39,D0
 bne    LoopAddBuff
 
Exit
 lsr.l  #4,D6    ;byte count/16 = number of defects
 move.l D6,BadB_Num(A5) ;store defect number in global
 move.l DialogPort(A5),-(A7);pass pointer to dialog
 _CloseDialog    ;close up dialog
 cmpi.l #60,BadB_Num(A5)  ;are there too many bad blocks
 beq.s  NoMoreAlert;yes, go put up alert
 cmpi.w #2,ButtonHit(A5);did user hit “More” button (#2)
 bne    ToEventLoop;no, so go to event loop
 bra    LoopForMore;yes so put up dialog again

ToEventLoop
 jmp    EventLoop

NoMoreAlert
 move.w #134,-(A7) ;get out of space alert
 clr.l  -(A7)    ;use standard filter proc function
 clr.w  -(A7)
 _CautionAlert   ;use a caution alert
 clr.w  (A7)+
 jmp    EventLoop
 
 EndProc

; PROC FormatProc
;
; Formating procedure.  This is where we sent the drive 
; parameters in a Mode Select command.  We then send the drive
; defects with the Format Unit command.  And then the drive is
; ready to use with a driver installing program.
FormatProcPROC EXPORT
 IMPORT (HD_Select,HD_Discon,WriteDisk):CODE

; First we put up a dialog box to make sure the user wants to 
; format the drive.

; Make String buffer #1, 1 character long.  Then move the hex 
; value in the low order byte of Addr (the SCSI address of the
; target device) into the string buffer and convert to ascii.
 move.b #1,StrBuff1(A5) 
 move.b 1+Addr(A5),1+StrBuff1(A5)  
 add.b  #$30,1+StrBuff1(A5) 
 
 clr.b  StrBuff2(A5) ;clear all other string buffers
 clr.b  StrBuff3(A5)
 clr.b  StrBuff4(A5)
 pea    StrBuff1(A5)
 pea    StrBuff2(A5)
 pea    StrBuff3(A5)
 pea    StrBuff4(A5)
 _ParamText ;substitute String buffer 1 for “^0”
 
 move.w #132,-(A7) ;get format alert dialog box
 clr.l  -(A7)    ;use standard filter proc function
 clr.w  -(A7)
 _CautionAlert
 cmpi.w #1,(A7)+ ;did user hit cancel?
 bne    Exit;yes, so exit

; User wants to Zap Drive, so let ‘em have it!
; Construct a data block to place parameters that we will send
; with the Mode Select command.

ZapDrive

; First we clear the data buffer
 lea    ParamBuff(A5),A0  ;pass address of buffer
 move.l #ParamBuffLen,D0  ;pass length of buffer
 jsr    ClearBuff;clear buffer
 
 clr.l  D0;clear D0, used for scratch
 ;header
 move.b #8,$3(A0);3rd byte = size of descriptor
 ;list
 ;descriptor list
 move.b #2,$A(A0);high byte of sector size ($200)
 ;Drive Parameter List
 move.b #1,$C(A0);List Format Code: (1 = soft
 ;sectored drives, 2= hard
 ;sectored drives or 
 ;removable media drives)
 
; Place word value of cylinder count into buffer.  We must do 
; this as two bytes because the wored in the output buffer is
; at an odd address

 move.w 2+Cyls(A5),D0

 move.b D0,$E(A0);make sure $E(A0) has LSB
 lsr.l  #8,D0    ;now move in high byte of word
 move.b D0,$D(A0)

; Move byte value of head count into buffer.
 move.b 3+Heads(A5),$F(A0)
 
; Move word value of Reduced Write Current Cylinder into
; buffer.
 move.w 2+RedWrtCur(A5),$10(A0)
 
; Move word value of Write Precompensation Cylinder into
; buffer.
 move.w 2+WrtPreComp(A5),$12(A0)
 
; Move byte value of Step Pulse Code into Buffer.
 move.b 1+stepcode(A5),$15(A0)
 
; Now we send the Mode Select Command and the parameter
; buffer.
 jsr    HD_Select;get SCSI bus and select drive
 tst.w  Res(A5)  ;did we succeed?
 bne    ToErrorProc;no, so go to error handling proc
 
; Clear command buffer and construct command.
 lea  CommandBuff(A5),A0  ;pass address of buffer
 move.l #CBuffLen,D0 ;pass length of buffer
 jsr    ClearBuff;clear command buffer
 
 move.b #ModeSelect,(A0)  ;move SCSI OP Code to byte #0
 move.b #ModeSelBytesOut,4(A0);move # of bytes to
 ; transfer into byte #4
 clr.w  -(A7)    ;space for trap error code
 move.l A0,-(A7) ;pass address of command buffer
 move.w #CommLenS,-(A7) ;pass length of CDB 
 ;(Command Data Block),
 ;class 0 command, so it
 ;is 6 bytes long
 _SCSICmd ;send command
 
 move.w #ModeSelect,Last_Cmd(A5) ;store command id 
 
 move.w (A7)+,Res(A5);did command work?
 bne    ToErrorProc;no, go to error handling proc

; Now we clear the pseudo program buffer
; WriteDisk to send the data to the controller card.
 lea    PseudoProg(A5),A0 ;pass address of buffer
 move.l #PProgLen,D0 ;pass size of buffer
 jsr    ClearBuff;clear buffer
 
 lea    ParamBuff(A5),A1  ;pass address of data buffer
 move.l #ModeSelBytesOut,D0 ;pass # of bytes to
 jsr    WriteDisk; transfer & send data

 move.l #10,D0   ;pass number of ticks to wait
 jsr    HD_Discon
 tst.w  Res(A5)  ;SCSI Protocal Error?
 bne    ToErrorProc
 move.w #ModeSelect,Last_Cmd(A5)
 cmp.w  #$4,CompStat(A5)  ;is the Drive Not Ready?
 beq    ToErrorProc;yes, go to error handler
 cmp.w  #$24,CompStat(A5) ;Was a bad parameter passed to
 ;the controller?
 beq    ToErrorProc;yes, go to error handler
 
 jsr    HD_Select;go get control of bus and 
 ;select device
 tst.w  Res(A5)  ;did we succeed?
 bne    ToErrorProc;no, go to error proc

; Next, clear the buffer where we will assemble the CDB
 lea    CommandBuff(A5),A0;pass address of buffer
 move.l #CBuffLen,D0 ;pass size of buffer
 jsr    ClearBuff;clear buffer
 move.b #FormatUnit,(A0)  ;move op code into first byte
 move.b 1+Interleave(A5),4(A0);move byte value of inter
 ;leave into byte 4
 tst.l  BadB_num(A5) ;see if there are disk defects
 beq    NoDefects;no, skip ahead
 add.b  #$1C,1(A0) ;place flags to tell controller
 ;that defects are coming
; Call OutBuff to format defects so they can be read by
; controller card.
 jsr    OutBuff  
 
NoDefects
 clr.w  -(A7)    ;space for trap error code
 move.l A0,-(A7) ;pass address of CDB
 move.w #CommLenS,-(A7) ;class 0 command so pass
 ; length of 6 bytes
 _SCSICmd ;send command
 
 move.w #FormatUnit,Last_Cmd(A5) ;store command id
 
 move.w (A7)+,Res(A5);did _SCSICmd produce an error
 bne    ToErrorProc;yes, go to error handler
 
; If we are sending any info on disk defects, prepare buffer
; for pseudo program and pass count bytes and address to
; sending procedure.
 tst.l  BadB_num(A5) ;are we sending any defect data?
 beq    NoPseudoProg ;no, skip the next section

; Clear pseudo program buffer.
 lea    PseudoProg(A5),A0 ;pass address of buffer
 move.l #PProgLen,D0 ;pass length of buffer
 jsr    ClearBuff;clear buffer

; Pass address of data, and number of bytes to be transfered.
; A0 still contains the address of the pseudo program buffer.
 lea    BadB_Out(A5),A1
 move.l BadB_Num(A5),D0 ;move number of defects to D0
 lsl.l  #3,D0    ;multiply by 8 (8 bytes/defect)
 addq.l #4,D0    ;add 4 bytes for header
 jsr    WriteDisk;send info on defects

NoPseudoProg
 move.l #MaxTicks,D0 ;pass number of ticks to wait
 jsr    HD_Discon
 tst.w  Res(A5)  ;SCSI Protocal Error?
 bne    ToErrorProc;yes, go to error handler

; Test Status byte from SCSI command to see if we passed a bad
; parameter or if there is a defect on track 0.
 move.w #FormatUnit,Last_Cmd(A5) ;update command id for
 ;error handling proc
 cmp.w  #$24,CompStat(A5) ;Was a bad parameter passed to 
 ;the controller?
 beq    ToErrorProc;yes, go to error proc
 jmp    EventLoop

Exit
 jmp    EventLoop
 
ToErrorProc
 jmp    ErrorProc
 
 EndProc

; PROC ResetProc
;
; This procedure is called from the menu handling routine.  It
; acts as glue between the menu handling code and the reset 
; subroutine and allows us to re-use the reset subroutine.
; (like in SelAddrProc).
ResetProc PROC EXPORT
 IMPORT (ResetDisk):CODE
 
 jsr    ResetDisk;go reset SCSI bus
 tst.w  Res(A5)  ;test result
 bne    ToErrorProc;if error go to error handler
 jmp    EventLoop;else return to event loop
ToErrorProc
 jmp    ErrorProc
 EndProc

; PROC WriteDisk
;
; Procedure wor transfering data to the controller. This 
; routine uses a pseudo program which contains a simple loop
; of three instructions. This transfer could have been done
; using only the first and the last instruction if we had put 
; the transfer count that is passed in D0 into the second 
; parameter of the first command. It would look exactly the 
; same if it were being used for a read, except the _SCSIWrite 
; trap would be replaced with _SCSIRead. Refer to IM volume IV 
; for info on the Pseudo instructions. Note each instruction
; is a  word followed by two longword parameters.  SCSTOP must
; be the last instruction. Address of pseudo buffer is
; passed in A0.  Address of p_buff is passed in A1.
; Transfer count is passed in D0.
WriteDisk PROC ENTRY
 movem.lD0-D2/A0-A1,-(SP) ;save registers

; First command  moves bytes to given address
 move.w #SCINC,(A0)
 move.l A1,2(A0) ;address to be moved to or from
 move.l #1,6(A0) ;transfer count in bytes

; Second command is loop  
 move.w #SCLOOP,10(A0)  
 move.l #-10,12(A0);rel addr
 move.l d0,16(A0);loop count

; Third command = stop, no parameters
 move.w #SCSTOP,20(A0)  
 
 clr.w  -(SP)    ;space for trap result code (OSErr)
 move.l A0,-(SP) ;address of pseudo program
 _SCSIWrite
 move.w (SP)+,Res(A5);store result
 move.w #Write,Last_Cmd(A5) ;store code for type 
 ;of SCSI command
 movem.l(SP)+,D0-D2/A0-A1 ;restore registers
 rts
 ENDPROC
 
; PROC HD_Select
;
; HD_Select tries to get control of the SCSI bus.  If it does,
; it then selects the device whose address is in Addr(A5).

HD_Select PROC ENTRY
 IMPORT CountOff:CODE
 movem.lD0-D2/A0-A1,-(A7) ;save regs on stack
 clr.w  -(A7)    ;space for result code
 _SCSIGet ;reserve the bus for our use
 move.w (A7)+,Res(A5);store result code
 beq.s  ok;go to ok2 if we succeeded
 move.w #Get,Last_Cmd(A5) ;else, store the last command
 bra.s  exit;and return
 
ok
 clr.w  -(A7)    ;space for result code
 move.w Addr(A5),-(A7)    ;load SCSI address of hard disk
 _SCSISelect;select device
 
 move.w (A7)+,Res(A5);store result code
 move.w #Select,Last_Cmd(A5);store last command

exit
 movem.l(A7)+,D0-D2/A0-A1 ;restore registers
 rts
 ENDPROC


; PROC HD_Discon
;
; Here we complete a SCSI command.  We tell Mac how long to
; wait for target device on the SCSI bus to signal that the
; command has completed.  This procedure receives number of
; ticks to wait in D0.
HD_Discon PROC ENTRY
 
 movem.lD0-D2/A0-A1,-(A7) ;save regs on stack
 
 clr.w  CompStat(A5)
 clr.w  CompMsg(A5)
 
 clr.w  -(A7)    ;space for result code
 pea    CompStat(A5) ;_SCSIComplete Status
 pea    CompMsg(A5);_SCSIComplete Message
 move.l D0,-(A7) ;number of ticks to wait for
 ;completion of the last
 ;SCSI command
 _SCSIComplete   
 move.w (A7)+,Res(A5);store result code
 move.w #Complete,Last_Cmd(A5);store last command 
 movem.l(A7)+,D0-D2/A0-A1 ;restore registers
 rts
 ENDPROC

; PROC InitGlobals
;
; Initialize the globals used to store information about the 
; hard disk.  Set all values to zero
InitGlobals PROC EXPORT
 
 clr.w  Heads(A5)
 clr.w  Cyls(A5)
 clr.w  WrtPreComp(A5)
 clr.w  RedWrtCur(A5)
 clr.w  Interleave(A5)
 clr.w  StepCode(A5)
 clr.l  Addr(A5)

 clr.l  BadB_Num(A5)
 
 rts
 ENDPROC

; PROC ResetDisk
;
; This procedure resets the SCSI bus.  It has a 
; one second delay after the
; reset because the CDC Wren III drives take more than
; 3/4 of a second to completely reset.  Other drives may need 
; longer delays after the reset.
ResetDisk PROC EXPORT

 movem.lD0-D2/A0-A1,-(A7) ;save the registers
 clr.w  -(A7)    ;space for result code
 _SCSIReset ;reset the bus
 move.w (A7)+,Res(A5);store results
 beq.s  ok1 ;go to ok1 if we succeeded
 move.w #Reset,Last_Cmd(A5) ;else cmd code for err Msg
 rts
 
ok1
 move.l #60,D0   ;D0 = number of ticks to wait
 jsr    CountOff ;jst to delay routine
 movem.l(A7)+,D0-D2/A0-A1 ;restore the registers
 rts
 ENDPROC
 
 END

;******** File: Format.a ***************
;**************************************
;
; A formatting program for low level formatting of a SCSI
; hard disk.  Allows user to enter disk drive parameters and 
;information about defects. This file contains all
; the standard code for Menus etc.  It also contains the
; procedures for error handling.

 BLANKS ON
 INCLUDE‘FormatEqu.a’
 LOAD   ‘Traps.d’
 LOAD   ‘ToolEqu.d’
 LOAD   ‘SysEqu.d’
 LOAD   ‘QuickEqu.d’
 LOAD   ‘SCSIEqu.d’
 LOAD   ‘PackMacs.d’

; Note Export directives must be placed before a symbol is 
; declared.

 EXPORT (DialogBuff,QuickDraw,ButtonHit,DialogPort):DATA
 EXPORT (ItemType,ItemHandle,ItemRect,MyPort):DATA
 EXPORT (StrBuff1,StrBuff2,StrBuff3,StrBuff4):DATA
 
AppleMH ds.l1  ;handle for Apple menu
FileMH  ds.l1  ;handle for File menu
EditMH  ds.l1  ;handle for Edit menu
SCSIMH  ds.l1  ;handle for SCSI menu

DAName  ds.b255  ;space for DA’s name
Window  ds.l1
DialogBuffds.l DialWLen
MyPort  ds.l1
ButtonHit ds.w 1
DialogPortds.l 1
ItemTypeds.l1
ItemHandleds.l 1
ItemRectds.l2
StrBuff1ds.bStrBuffLen
StrBuff2ds.bStrBuffLen
StrBuff3ds.bStrBuffLen
StrBuff4ds.bStrBuffLen

EventRecRECORD 20, INCR
what    ds.w1  ;
Message ds.l1  ;
When    ds.l1  ;
Point   ds.l1  ;
Modify  ds.w1  ;
 ENDR

; Note: The space for quickdraw globals must be declared.  
; Unlike MDS, the MPW assembler does not do it for you.

QuickDraw RECORD ,DECREMENT
thePort ds.l1
 ORG  -grafSize
 ENDR

; This is our program.  
Begin MAIN
 ALIGN  2
 IMPORT (InitManagers,LoadMenu,InitGlobals,EventLoop):CODE
 ;we must import these symbols as they are not
 ;yet defined in this file.
 
 jsr    InitManagers
 jsr    LoadMenu
 jsr    InitGlobals
 jmp    EventLoop
 
 ENDMAIN


; PROC  InitManagers
;
; Initialize all managers that we might need.

InitManagersPROC ENTRY
 IMPORT QuickDraw:DATA
 
 pea    QuickDraw.thePort(A5)
 _InitGraf
 _InitFonts
 move.l #AllEvents,D0
 _FlushEvents
 _InitWindows
 _InitMenus
 clr.l  -(A7)
 _InitDialogs
 _TEInit
 _InitCursor
 rts
 
 ENDPROC
 
 
; PROC  LoadMenus
;
; Subroutine called at start up time to load menus onto the
; heap, install them and then draw the menu bar.

LoadMenuPROCENTRY

 clr.l  -(A7)
 move.w #AppleM,-(A7);”About...” ID for Apple menu
 _GetRMenu;load it on heap
 move.l (A7),AppleMH(A5)  ;copy handle for future ref.
 move.l (A7),-(A7) ;dup handle for next 2 traps
 clr.w  -(A7)    ;
 _InsertMenu
 move.l #’DRVR’,-(A7);get desk accessories
 _AddResMenu;add to Apple Menu
 
 clr.l  -(A7)    ;space for next handle
 move.w #FileM,-(A7) ;File menu ID
 _GetRMenu;load it on heap
 move.l (A7),FileMH(A5) ;copy handle for future ref.
 clr.w  -(A7)    ;
 _InsertMenu
 
 clr.l  -(A7)    ;space for next handle
 move.w #EditM,-(A7) ;Edit menu ID
 _GetRMenu;load it on heap
 move.l (A7),EditMH(A5) ;copy handle for future ref.
 clr.w  -(A7)    ;
 _InsertMenu
 
 clr.l  -(A7)    ;space for next handle
 move.w #SCSIM,-(A7) ;SCSI menu ID
 _GetRMenu;load it on heap
 move.l (A7),SCSIMH(A5) ;copy handle for future ref.
 clr.w  -(A7)
 _InsertMenu
 
 _DrawMenuBar
 rts
 
 ENDPROC


; PROC  EventLoop
;
; This is where we process our events.  Not much happening
; except some jump tables.  We only process mouse down events
; and system events.  All others ignored.

EventLoop PROC EXPORT
 IMPORT (DownMouse,DownKey,Update,Activate):CODE
 
 _SystemTask
 clr.w  -(A7)
 move.w #EventMask,-(A7)
 pea    EventRec(A5)
 
; Note we unhighlight the menu only in the event loop so that 
; long opporations like formating leave the menu bar selected 
; until they have completed.
 clr.w  -(A7)  
 _HiLiteMenu
 _GetNextEvent
 
 move.w (A7)+,D0 ;boolean for is there an event
 cmp.w  #0,D0    ;if no event, loop back
 beq.s  EventLoop

 movea.l#0,A0    ;clear register for jumptable
 move.w EventRec.What(A5),A0;get type of event
 adda.l A0,A0    ;multipy by 2 as offset table 
 ;contains word length entries
 lea    JumpTable,A1 
 adda.l A1,A0    ;add offset to table address
 adda.w (A0),A1  ;add offset for proc to table addr
 jmp    (A1);jump to procedure

JumpTable 
 DATAREFS RELATIVE
 dc.w EventLoop-JumpTable ;event #0 - null event
 dc.w DownMouse-JumpTable ;event #1 - mouse-down
 dc.w EventLoop-JumpTable ;event #2 - mouse-up
 dc.w EventLoop-JumpTable ;event #3 - key-down
 dc.w EventLoop-JumpTable ;event #4 - key-up
 dc.w EventLoop-JumpTable ;event #5 - auto-key
 dc.w EventLoop-JumpTable ;event #6 - update
 dc.w EventLoop-JumpTable ;event #7 - disk-inserted
 dc.w EventLoop-JumpTable ;event #8 - activate
 dc.w EventLoop-JumpTable ;event #9 - n.a.
 dc.w EventLoop-JumpTable ;event #10 - network
 dc.w EventLoop-JumpTable ;event #11 - device driver
 dc.w EventLoop-JumpTable ;Appl event #1
 dc.w EventLoop-JumpTable ;Appl event #2
 dc.w EventLoop-JumpTable ;Appl event #3
 dc.w EventLoop-JumpTable ;Appl event #4
 ENDPROC
 
; PROC  DownMouse
;
; Procedure for handling mousedown events and figuring
; out which routine is used to handle the event.

DownMouse PROC Entry
 IMPORT (MenuBar,SysProc):CODE
 
 clr.w  -(A7)
 move.l EventRec.Point(A5),-(A7)
 pea    Window(A5)
 _FindWindow
 movea.l#0,A0    ;clear register 
 move.w (A7)+,A0 ;pop region number for mouse down
 adda.l A0,A0    ;multiply by two as offset
 ;table has word entries
 lea    MouseTable,A1
 adda.l A1,A0    ;add address of table to offset
 adda.w (A0),A1  ;add offset of proc to table addr
 jmp    (A1);jump to proc address

; Offset table for mouse down events.
MouseTable
 DATAREFS RELATIVE
 dc.w EventLoop-MouseTable;in desk
 dc.w MenuBar-MouseTable  ;in menu bar
 dc.w SysProc-MouseTable  ;in system window
 dc.w EventLoop-MouseTable;in contents region
 dc.w EventLoop-MouseTable;in drag region
 dc.w EventLoop-MouseTable;in grow region
 dc.w EventLoop-MouseTable;in go away region
 ENDPROC
 
; PROC  MenuBar  
;
; Subroutine for jumping to the correct menu.  Put the menu 
; number in A1 and the item number in A2.  This proc finds
; out which menu item was selected, calculated the correct 
; address to jump to and jumps there.

MenuBar PROCENTRY
 IMPORT (QuitProc,UndoMI,CutMI,CopyMI,PasteMI):CODE
 IMPORT (AppleMProc,SelAddrProc,ParamProc,EDefectProc):CODE
 IMPORT (FDefectProc,FormatProc,MSenseProc,ResetProc):CODE
 IMPORT (ClearMI):CODE

 clr.w  -(A7)    ;space for longint from menu
 move.l EventRec.Point(A5),-(A7)
 _MenuSelect;get menu selected
 
 movea.l#0,A1    ;clear A1
 movea.l#0,A2    ;clear A2
 move.w (A7)+,A1 ;menu id into A1
 move.w (A7)+,A2 ;menu item into A2
 
 cmpa.w #0,A1    ;movea doesn’t set cond. codes
 beq    eventloop;if menu ID=0 get next event
 suba.l #AppleM+1,A1 ;convert menu id into menu #
 ;e.g. File menu becomes 0
 cmpa.w #$FFFF,A1;was it the apple menu
 beq    AppleMProc ;yes, so go handle event

 suba.l #1,A2    ;correct index (1st item = 0)
 adda.l A1,A1    ;A1*2; table has word entries
 adda.l A2,A2    ;A2*2; table has word entries
 
; Now calculate address to jump to by using the values in
; the offset tables.
 lea    MenuTable,A0
 adda.l A0,A1    ;this gives address of 
 ;correct offset in MenuTable
 adda.w (A1),A0  ;A0 now contains the address 
 ;of the correct ItemTable
 adda.l A0,A2    ;get entry in ItemTable
 adda.w (A2),A0  ;add offset at this entry to 
 ;address of ItemTable
 jmp    (A0);and jump to it!
 
; Offset table for each menu. 
MenuTable 
 DATAREFS RELATIVE
 dc.w FileMTable-MenuTable
 dc.w EditMTable-MenuTable;in system window
 dc.w SCSIMTable-MenuTable;in contents region

; ItemTables
; Offset table for each item in each menu.
FileMTable
 dc.w QuitProc-FileMTable
EditMTable
 dc.w UndoMI-EditMTable
 dc.w 0
 dc.w CutMI-EditMTable
 dc.w CopyMI-EditMTable
 dc.w PasteMI-EditMTable
 dc.w ClearMI-EditMTable

SCSIMTable
 dc.w SelAddrProc-SCSIMTable
 dc.w ParamProc-SCSIMTable
 dc.w EDefectProc-SCSIMTable
 dc.w FormatProc-SCSIMTable
 dc.w ResetProc-SCSIMTable
 
 ENDPROC

;PROC SysProc
;
; Mouse down in a desk accessory.  Go call _SystemClick.
SysProc PROCENTRY
 IMPORT (EventLoop):CODE
 
 pea    EventRec(A5) 
 move.l Window(A5),-(A7)
 _SystemClick
 jmp    EventLoop
 ENDPROC
 
;PROC QuitProc
;
; Procedure for quitting program.
QuitProcPROCENTRY

 _ExitToShell
 ENDPROC

;PROC UndoMI
;
; Procedure to handle selection of the “Undo” menu item when a
; desk accessory controls the active window.
UndoMI  PROCENTRY
 clr.w  -(A7)
 move.w #0,-(A7)
 _SysEdit
 adda.l #2,A7
 jmp    EventLoop
 ENDPROC

;PROC CutMI
;
; Procedure to handle selection of the “Cut” menu item when a
; desk accessory controls the active window.
CutMI PROCENTRY
 clr.w  -(A7)
 move.w #2,-(A7)
 _SysEdit
 move.w (A7)+,D0
 jmp    EventLoop
 ENDPROC

;PROC CopyMI
;
; Procedure to handle selection of “Copy” menu item when a
; desk accessory controls the active window.
CopyMI  PROCENTRY
 clr.w  -(A7)
 move.w #3,-(A7)
 _SysEdit
 move.w (A7)+,D0
 jmp    EventLoop
 ENDPROC

;PROC PasteMI
;
; Procedure to handle selection of “Paste” menu item when
; desk accessory controls the active window.
PasteMI PROCENTRY
 clr.w  -(A7)
 move.w #4,-(A7)
 _SysEdit
 move.w (A7)+,D0
 jmp    EventLoop
 ENDPROC

;PROC ClearMI
;
; Procedure to handle selection of “Clear” menu item when
; desk accessory controls the active window.
ClearMI PROCENTRY
 clr.w  -(A7)
 move.w #5,-(A7)
 _SysEdit
 move.w (A7)+,D0
 jmp    EventLoop
 ENDPROC

; PROC AppleMProc
;
; This procedure handles menu events in the Apple menu. On 
; entry, A2 has the item number of the menu item that was 
; selected.

AppleMProcPROC ENTRY
 IMPORT (DAName,QuickDraw):DATA
 IMPORT (AboutProc,EventLoop):CODE
 
 cmpa.l #AboutItem,A2;”About U2 Formatter” selected?
 beq    AboutProc;yes, go put up dialog

; A desk accessory was selected, go install it.    
 move.l QuickDraw.thePort(A5),MyPort(A5)     ;save our port
 move.l AppleMH(A5),-(A7) ;push handle for apple menu
 move.w A2,-(A7) ;push item number hit
 pea    DAName(A5) ;push space for DA name
 _GetItem ;get DA name

 clr.w  -(A7)    ;space for driver reference #
 pea    DAName(A5) ;pass DA name
 _OpenDeskAcc    ;open DA
 adda.l #2,A7
 
 move.l MyPort(A5),-(A7)  ;restore our port
 _SetPort
 jmp    EventLoop
 ENDPROC

; PROC  AboutProc
;
; This procedure puts up our about U2 Formatter dialog box.
AboutProc PROC ENTRY
 IMPORT (ButtonHit,QuickDraw):DATA
 IMPORT (EventLoop,DefButton):CODE
 
 clr.l  -(A7)    ;space for pointer to dialog
 move.w #AboutRN,-(A7)    ;About dialog resource number
 pea    DialogBuff(A5)    ;pass address of dialog buffer
 move.l #-1,-(A7);bring dialog window to front
 _GetNewDialog

 move.l (A7),DialogPort(A5) ;Save ptr to dialog rec
 ;note: pointer still on
 ;stack for _SetPort trap
 _SetPort
 lea    DialogBuff(A5),A4 ;pass address of dialog buffer
 jsr    DefButton

NotYet
 clr.l  -(A7)    ;use standard filter proc
 pea    ButtonHit(A5);addr of storage for item hit
 _ModalDialog    
 cmpi.w #1,ButtonHit(A5)  ;which item hit? is it “ok”?
 bne    NotYet   ;no so loop back
 
 move.l DialogPort(A5),-(A7);pass ptr to dialog rec
 _CloseDialog    
 jmp    EventLoop
 
 ENDPROC
 
; PROC DefButton
;
; This procedure draws a rounded rectangle around our default 
; button in a modal dialog.  It gets passed the address of our 
; dialog.  It then gets the rectangle for the first item and 
; draws a rounded rectangle around it.

DefButton PROC EXPORT
 movem.lD0-D2/A0-A4,-(A7) ;save registers

; First we get the rectangle for item 1.     
 move.l A4,-(A7) ;push pointer for dialog
 move.w #1,-(A7) ;push item number
 pea    ItemType(A5) ;addr of space for item type
 pea    ItemHandle(A5)    ;address of space for handle
 pea    ItemRect(A5) ;address of space for rect
 _getDItem

 move.w #3,-(A7) ;change pensize to 3X3
 move.w #3,-(A7)
 _PenSize
 
 pea    ItemRect(A5) ;make button smaller; all edges 
 move.w #-4,-(A7);moved in by 4 pixels
 move.w #-4,-(A7)
 _InsetRect
 
 pea    ItemRect(A5) ;draw boarder for default button
 move.w #16,-(A7);curvature is 16 pixel in both 
 move.w #16,-(A7);directions
 _FrameRoundRect
 
Done
 movem.l(A7)+,D0-D2/A0-A4 ;restore registers
 rts
 
 ENDPROC
 
; PROC RadioHiLite
;
; This procedure selects the radio button that was chosen and 
; deselects the radio button that was previously selected.  It 
; receives the old selected button in D0 and the new one in
; D1.  It also receives the address of the dialog buffer in A4
; If this is the first time the procedure is being called 
; for a given dialog box, it should be passed zero in D0
; so that it doesn’t try to deselect a radio button that 
; is not selected.
; This procedure also checks to see that the button that was 
; chosen was not the one that was already selected.  If it is,
; it doesn’t deselect it.

RadioHiLite PROC EXPORT
 movem.lD0-D2/D6-D7/A0-A4,-(A7)  ;save registers
 
 move.w D0,D6    ;move radio button values to register
 move.w D1,D7    ;that wont be trashed by traps
 
 move.l A4,-(A7) ;get rectangle for radio button
 move.w D7,-(A7)
 pea    ItemType(A5)
 pea    ItemHandle(A5)
 pea    ItemRect(A5)
 _getDItem

; Duplicate top left hand point of rectangle into bottom right
; point of rectangle.  They are now the same.  Now add a 
; different constant to each so the circle in this rect
; is right in the middle of the radio button.  Then invert
; this oval so it changes from white to black.
 move.l ItemRect(A5),ItemRect+4(A5)
 addi.l #$00070005,ItemRect(A5)
 addi.l #$000D000B,ItemRect+4(A5)
 pea    ItemRect(A5)
 _InvertOval
 
 cmp.l  D6,D7  ;is the radio button the same one that
 beq    Done;was highlighted last time? If so,
 ;we are done.
 
 tst.l  D6;is this the first time called?
 beq    Done;if so, D6=0 and we must quit

; Repeat the opporation for the old radio button, the one that 
; was previously highlighted.  This time _InvertOval will
; change it from black to white.
 move.l A4,-(A7)
 move.w D6,-(A7)
 pea    ItemType(A5)
 pea    ItemHandle(A5)
 pea    ItemRect(A5)
 _getDItem
 move.l ItemRect(A5),ItemRect+4(A5)
 addi.l #$00070005,ItemRect(A5)
 addi.l #$000D000B,ItemRect+4(A5)
 pea    ItemRect(A5)
 _InvertOval

Done
 movem.l(A7)+,D0-D2/D6-D7/A0-A4  ;restore registers
 rts
 
 ENDPROC

; PROC ErrorProc
;
; This procedure puts up an error dialog box. The dialog tells
; what the last SCSI command or trap was, what error occurred
; and a possible explanation.  All text is in STR resources.
; Trap names are given the resource number: “AX00” where X is
; the routine selector used by the SCSI Manager (IM, IV-295).
; SCSI commands are given the resource number: “X00” where X
; is the SCSI op code.  These values are all in the 
; FormatEqu.a file. Resource number 
; for errors are of the form “AAXX” for traps where XX is 
; the OSErr returned from the trap, or “10XX” for SCSI
; commands, where XX is the SCSI error reported
; by _SCSIComplete in the 
; status parameter. Resource numbers for the “Most Probable
; Cause” strings are “BAXX” for traps and “20XX” for SCSI 
; commands.

ErrorProc PROC EXPORT
 IMPORT (Last_Cmd,Res,CompStat,CompMsg):DATA
 
; Get res number for last command string and place a pointer
; to the string on the stack.
 clr.l  D4;place no. for last command in D4
 move.w Last_Cmd(A5),D4
 lsl.l  #8,D4    ;multipy by $100.
 clr.l  -(A7)    ;space for handle
 move.l #’STR ‘,-(A7);type of resource
 move.w D4,-(A7) ;resource number
 _GetResource    ;get handle last command string
 move.l (A7)+,A0 ;pop handle into A0
 move.l (A0),-(A7) ;dereference handle & push ptr

; Get resource number for error string and place a pointer
; to the string on the stack. 
 clr.l  D3
 move.w Res(A5),D3 ;if there was no OSError, Res=0
 bne    ErrorInTrap;if Res = 0, error in SCSI cmd
 move.w CompStat(A5),D3 ;SCSI error into D3
 add.w  #$1000,D3;add offset for SCSI cmd error
 bra.s  Continue
ErrorInTrap
 add.w  #$AA00,D3;add offset OS trap error
Continue
 clr.l  -(A7)    ;space for handle
 move.l #’STR ‘,-(A7);type of resource
 move.w D3,-(A7) ;resource number
 _GetResource    ;get resource handle
 move.l (A7)+,A0 ;pop handle into A0
 move.l (A0),-(A7) ;dereference handle and push ptr

; Get resource number for probable cause string and place a 
; pointer to the string on the stack.
 add.l  #$1000,D3;add offset to error# to get cause#
 clr.l  -(A7)    ;space for handle
 move.l #’STR ‘,-(A7);type of resource
 move.w D3,-(A7) ;resource number
 _GetResource    ;get resource handle
 move.l (A7)+,A0 ;pop handle into A0
 move.l (A0),-(A7) ;dereference handle and push ptr
 
 clr.b  StrBuff4(A5) ;make last string empty
 pea    StrBuff4(A5) ;pass pointer to empty string
 _ParamText 
 move.w #133,-(A7) ;resource number for dialog
 clr.l  -(A7)    ;use standard filter proc
 clr.w  -(A7)
 _NoteAlert
 clr.w  (A7)+
 
 jmp  EventLoop  ;return to the event loop
 ENDPROC

; PROC CountOff
;
; This procedure produces a delay equal to the number of ticks
; in the long word passed in D0.

CountOffPROCEXPORT
 movem.lD0-D3/A0-A1,-(A7) ;save the registers
 
 move.l D0,D3    ;move our value into a register that
 ;wont be trashed by the traps
 clr.l  -(A7)    ;space for the current tick count
 _TickCount 
 add.l  (A7)+,D3 ;add our delay to the current tick
 ;count, this is when we stop 
 ;waiting.
 
; Our loop to see if we have gone past the tick count we are 
; waiting for.
NotYet
 clr.l  -(A7)    ;space for # of ticks since reboot
 _TickCount
 cmp.l  (A7)+,D3 ;is this as big as the number we are 
 ;waiting for?
 bgt  NotYet;if not, then repeat loop
 
 movem.l(A7)+,D0-D3/A0-A1 ;restore registers
 rts
 ENDPROC
 
 
; PROC UnHiLite
;
; This procedure unhighlights an item in a dialog box to show
; that it cannot be selected.  It receives the address of the 
; dialog record in A4 and item number to be unhighlighted in
; D0.

UnHiLitePROCEXPORT
 movem.lD0-D2/A0-A1,-(A7) ;save the registers
 
 move.l A4,-(A7) ;get the item rect for the item
 move.w D0,-(A7) ;number that we were passed
 pea    ItemType(A5)
 pea    ItemHandle(A5)
 pea    ItemRect(A5)
 _getDItem
 
 move.l ItemHandle(A5),-(A7);pass the item handle
 move.w #255,-(A7) ;pass flag for unhighlighting
 _HiLiteControl
 
 movem.l(A7)+,D0-D2/A0-A1 ;restore registers
 rts
 ENDPROC

; PROC ExtractNumb
;
; This procedure extracts number value from edit text item
; in a dialog box.  It gets passed item number in D0 and 
; address of the Dialog box record in A4.  A3 contains a ptr
; to the long where output is to be placed and D3 contains the 
; maximum value allowable.

ExtractNumb PROC EXPORT
 movem.lD0-D2/A0-A1,-(A7) ;save registers
 
 clr.l  (A3);clear output long

; Get the handle for this item number.
 move.l A4,-(A7) ;pass dialog record pointer
 move.w D0,-(A7) ;pass item number
 pea    ItemType(A5)
 pea    ItemHandle(A5)
 pea    ItemRect(A5)
 _getDItem
 
; Get the text from that dialog item.  Move text into StrBuff1.
 move.l ItemHandle(A5),-(A7)
 pea    StrBuff1(A5)
 _GetIText
 
 clr.l  D0;get length of text out of first
 move.b StrBuff1(A5),D0 ;byte and place it in D0
 ble    Exit;if length is 0, we are done.

; Loop to remove all characters that aren’t numbers. D1 is the
; index for the position being tested. D2 is the index for the 
; last valid position.
 lea    StrBuff1(A5),A0   ;initialize A0 to point to string
 moveq.l#1,D1
 moveq.l#1,D2
 
Loop: 
 cmp.b  #’0',(A0,D1.w)    ;test next character
 blt    NotNumb  
 cmp.b  #’9',(A0,D1.w)
 bgt    NotNumb
 
 cmp.l  D1,D2    ;if indeces are same, no move is
 beq    NoMove   ;necessary
 move.b (A0,D1.w),(A0,D2.w) ;move character
 
NoMove:
 addq.l #1,D1    ;increment both pointers as last
 addq.l #1,D2    ;character was a number
 cmp.w  D1,D0    ;reached the end of string?
 bge    Loop;no
 bra    Convert  ;yes
 
NotNumb
 addq.w #1,D1    ;only increment test pointer
 cmp.w  D1,D0    ;reached the end of string?
 bge    Loop;no
 bra    Convert  ;yes
 
Convert:
 subq.l #1,D2    ;make D2 into count byte 
 move.b D2,(A0)  ;count byte into byte 1 of string
 _StringToNum    ;convert to hex value
 move.l D0,(A3)  ;move result to output
 
Default
 cmp.l  (A3),D3  ;is result greater than allowable
 bge    Exit;no, we can exit
 move.l D3,(A3)  ;yes so use default value instead

Exit
 movem.l(A7)+,D0-D2/A0-A1 ;restore registers
 rts
 ENDPROC
 
 END


/*  Format.r3/1/87 Timothy Standing
    Resources for the SCSI formatting program.*/
#include “Types.r”

/* These define’s used in MENU resources to disable specific menu items. 
*/

#define AllItems 0b1111111111111111111111111111111 /* 31 flags */
#define MenuItem10b00001
#define MenuItem20b00010
#define MenuItem30b00100
#define MenuItem40b01000
#define MenuItem50b10000

resource ‘MENU’ (128, “Apple”, preload,nonpurgeable) {
 128, textMenuProc,
 AllItems & ~MenuItem2, /* Disable item #2 */
 enabled, apple,
 {
 “About Format”,
 noicon, nokey, nomark, plain;
 “-”,
 noicon, nokey, nomark, plain
 }
};
resource ‘MENU’ (129, “File”, preload,nonpurgeable) {
 129, textMenuProc,
 allEnabled,
 enabled, “File”,
 {
 “Quit”,
 noicon, nokey, nomark, plain
 }
};
resource ‘MENU’ (130, “Edit”, preload,nonpurgeable) {
 130, textMenuProc,
 AllItems & ~(MenuItem1 | MenuItem2),/* Disable items #1 & #2 */
 enabled, “Edit”,
  {
 “Undo”,
 noicon, “Z”, nomark, plain;
 “-”,
 noicon, nokey, nomark, plain;
 “Cut”,
 noicon, “X”, nomark, plain;
 “Copy”,
 noicon, “C”, nomark, plain;
 “Paste”,
 noicon, “V”, nomark, plain;
 “Clear”,
 noicon, nokey, nomark, plain
 }
};
resource ‘MENU’ (131, “SCSI”, preload,nonpurgeable) {
 131, textMenuProc,
 allEnabled,
 enabled, “SCSI”,
  {
 “Select Address”,
 noicon, nokey, nomark, plain;
 “Parameters”,
 noicon, nokey, nomark, plain;
 “Enter Defects”,
 noicon, nokey, nomark, plain;
 “Format”,
 noicon, nokey, nomark, plain;
 “Reset”,
 noicon, nokey, nomark, plain
 }
};
resource ‘DLOG’ (128,”About Format”,preload,nonpurgeable) {
 {106, 106, 290, 406},
 dBoxProc, visible, noGoAway, 0x0, 128, “”
};
resource ‘DITL’ (128,preload,nonpurgeable) {
  {
/* 1 */ {141,202,161,273},
 button {
 enabled,
 “OK”
 };
/* 2 */ {15,27,41,274},   
 staticText {
 enabled,
 “A formatting program for MacTutor™”
 };
/* 3 */ {46,97,69,204},   /* Author Item */
 staticText {
 enabled,
 “by Tim Standing”
 };
/* 4 */ {81,46,101,260},
 staticText {
 enabled,
 “For help with this program see”
 };
/* 5 */ {101,46,119,260},
 staticText {
 disabled,
 “the June ’87 issue of MacTutor™”
 };
/* 6 */ {141,31,156,172},
 staticText {
 enabled, 
 “© 1987 Tim Standing”
 }
 }
};
resource ‘DLOG’ (129,”Select Address”,preload,nonpurgeable) {
 {54,99,295,391},
 dBoxProc, visible, noGoAway, 0x0, 129, “”
};
resource ‘DITL’ (129,preload,nonpurgeable) {
  {
/* 1 */ {195,75,215,135},
 button {
 enabled,
 “Select”
 };
/* 2 */ {195,160,215,220},
 button {
 enabled,
 “Cancel”
 };
/* 3 */ {55,75,75,115},   
 RadioButton {
 enabled,
 “#0”
 };
/* 4 */ {85,75,105,115},  
 RadioButton {
 enabled,
 “#1”
 };
/* 5 */ {115,75,135,115},
 RadioButton {
 enabled,
 “#2”
 };
/* 6 */ {145,75,165,115},
 RadioButton {
 enabled,
 “#3”
 };
/* 7 */ {55,175,75,215},
 RadioButton {
 enabled, 
 “#4”
 };
/* 8 */ {85,175,105,215},
 RadioButton {
 enabled,
 “#5”
 };
/* 9 */ {115,175,135,215},
 RadioButton {
 enabled,
 “#6”
 };
/* 10 */ {145,175,160,215},
 RadioButton {
 disabled,
 “#7”
 };
/* 11 */ {160,175,175,285},
 staticText {
 disabled,
 “(Mac’s Address)”
 };
/* 12 */ {12,35,30,258},
 staticText {
 disabled,
 “Please select the SCSI address of”
 };
/* 13 */ {27,35,47,263},
 staticText {
 disabled,
 “the hard disk you wish to format:”
 }
 }
};
resource ‘DLOG’ (130,”Enter Parameters”,preload,nonpurgeable) {
 {33,43,330,469},
 dBoxProc, visible, noGoAway, 0x0, 130, “”
};
resource ‘DITL’ (130,preload,nonpurgeable) {
  {
/* 1 */ {260,345,280,405},
 button {
 enabled,
 “OK”
 };
/* 2 */ {230,345,250,405},
 button {
 enabled,
 “Cancel”
 };
/* 3 */ {73,115,93,158},  
 RadioButton {
 enabled,
 “1:1”
 };
/* 4 */ {73,175,93,219},  
 RadioButton {
 enabled,
 “2:1”
 };
/* 5 */ {73,235,93,278},
 RadioButton {
 enabled,
 “3:1”
 };
/* 6 */ {73,295,93,338},
 RadioButton {
 enabled,
 “4:1”
 };
/* 7 */ {103,135,123,402},
 RadioButton {
 enabled, 
 “3.0 mSec (ST506-Nonbuffered Seeks)”
 };
/* 8 */ {123,135,143,375},
 RadioButton {
 enabled,
 “28 uSec (ST412-Buffered Seeks)”
 };
/* 9 */ {143,135,163,415},
 RadioButton {
 enabled,
 “12 uSec (Pseudo ST412-Buffered Seeks)”
 };
/* 10 */ {173,170,188,222},
 EditText {
 disabled,
 “0”
 };
/* 11 */ {203,170,218,222},
 EditText {
 disabled,
 “0”
 };
/* 12 */ {233,245,248,297},
 EditText {
 disabled,
 “0”
 };
/* 13 */ {263,245,278,297},
 EditText {
 disabled,
 “0”
 };
/* 14 */ {12,44,27,374},
 StaticText {
 disabled,
 “Please enter the parameters for the disk that”
 };
/* 15 */ {27,44,43,390},
 StaticText {
 disabled,
 “you wish to format.  Enter a zero for each unused”
 };
/* 16 */ {43,43,58,303},
 StaticText {
 disabled,
 “field.  All numbers are decimal.”
 };
/* 17 */ {73,15,88,107},
 StaticText {
 disabled,
 “Interleave:”
 };
/* 18 */ {103,15,118,130},
 StaticText {
 disabled,
 “Step Pulse Rate:”
 };
/* 19 */ {173,15,188,144},
 StaticText {
 disabled,
 “Number of Heads:”
 };
/* 20 */ {203,15,218,162},
 StaticText {
 disabled,
 “Number of Cylinders:”
 };
/* 21 */ {233,15,248,239},
 StaticText {
 disabled,
 “Reduced Write Current Cylinder:”
 };
/* 22 */ {263,15,278,240},
 StaticText {
 disabled,
 “Write Precompensation Cylinder:”
 }
 }
};
resource ‘DLOG’ (131,”Enter Defects”,preload,nonpurgeable) {
 {31,16,331,496},
 dBoxProc, visible, noGoAway, 0x0, 131, “”
};

resource ‘DITL’ (131,preload,nonpurgeable) {
  {
/* 1 */ {10,400,30,460},
 button {
 enabled,
 “OK”
 };
/* 2 */ {40,400,60,460},
 button {
 enabled,
 “More”
 };
/* 3 */ {70,400,90,460},
 button {
 enabled,
 “Cancel”
 };
/* 4 */ {120,50,135,85},
 EditText {
 enabled,
 “0”
 };
/* 5 */ {120,115,135,140},
 EditText {
 enabled,
 “0”
 };
/* 6 */ {120,160,135,210},
 EditText {
 enabled,
 “0”
 };
/* 7 */ {150,50,165,85},
 EditText {
 enabled,
 “0”
 };
/* 8 */ {150,115,165,140},
 EditText {
 enabled,
 “0”
 };
/* 9 */ {150,160,165,210},
 EditText {
 enabled,
 “0”
 };
/* 10 */ {180,50,195,85},
 EditText {
 enabled,
 “0”
 };
/* 11 */ {180,115,195,140},
 EditText {
 enabled,
 “0”
 };
/* 12 */ {180,160,195,210},
 EditText {
 enabled,
 “0”
 };
/* 13 */ {210,50,225,85},
 EditText {
 enabled,
 “0”
 };
/* 14 */ {210,115,225,140},
 EditText {
 enabled,
 “0”
 };
/* 15 */ {210,160,225,210},
 EditText {
 enabled,
 “0”
 };
/* 16 */ {240,50,255,85},
 EditText {
 enabled,
 “0”
 };
/* 17 */ {240,115,255,140},
 EditText {
 enabled,
 “0”
 };
/* 18 */ {240,160,255,210},
 EditText {
 enabled,
 “0”
 };
/* 19 */ {270,50,285,85},
 EditText {
 enabled,
 “0”
 };
/* 20 */ {270,115,285,140},
 EditText {
 enabled,
 “0”
 };
/* 21 */ {270,160,285,210},
 EditText {
 enabled,
 “0”
 };
/* 22 */ {120,290,135,325},
 EditText {
 enabled,
 “0”
 };
/* 23 */ {120,355,135,380},
 EditText {
 enabled,
 “0”
 };
/* 24 */ {120,400,135,450},
 EditText {
 enabled,
 “0”
 };
/* 25 */ {150,290,165,325},
 EditText {
 enabled,
 “0”
 };
/* 26 */ {150,355,165,380},
 EditText {
 enabled,
 “0”
 };
/* 27 */ {150,400,165,450},
 EditText {
 enabled,
 “0”
 };
/* 28 */ {180,290,195,325},
 EditText {
 enabled,
 “0”
 };
/* 29 */ {180,355,195,380},
 EditText {
 enabled,
 “0”
 };
/* 30 */ {180,400,195,450},
 EditText {
 enabled,
 “0”
 };
/* 31 */ {210,290,225,325},
 EditText {
 enabled,
 “0”
 };
/* 32 */ {210,355,225,380},
 EditText {
 enabled,
 “0”
 };
/* 33 */ {210,400,225,450},
 EditText {
 enabled,
 “0”
 };
/* 34 */ {240,290,255,325},
 EditText {
 enabled,
 “0”
 };
/* 35 */ {240,355,255,380},
 EditText {
 enabled,
 “0”
 };
/* 36 */ {240,400,255,450},
 EditText {
 enabled,
 “0”
 };
/* 37 */ {270,290,285,325},
 EditText {
 enabled,
 “0”
 };
/* 38 */ {270,355,285,380},
 EditText {
 enabled,
 “0”
 };
/* 39 */ {270,400,285,450},
 EditText {
 enabled,
 “0”
 };
/* 40 */ {10,15,25,375},
 StaticText {
 disabled,
 “Please enter the list of defects that came with your”
 };
/* 41 */ {25,15,40,375},
 StaticText {
 disabled,
 “disk drive.  Enter the Head #, Cylinder #, and Bytes”
 };
/* 42 */ {40,15,55,375},
 StaticText {
 disabled,
 “From Index (BFI) value for each defect.  Enter” 
 };
/* 43 */ {55,15,70,370},
 StaticText {
 disabled,
 “defects in order of increasing cylinder #.  If you”
 };
/* 44 */ {70,15,85,370},
 StaticText {
 disabled,
 “have more than 12 defects, click on the More Button.”
 };
/* 45 */ {120,10,135,45},
 StaticText {
 disabled,
 “#1:”
 };
/* 46 */ {150,10,165,45},
 StaticText {
 disabled,
 “#2:”
 };
/* 47 */ {180,10,195,45},
 StaticText {
 disabled,
 “#3:”
 };
/* 48 */ {210,10,225,45},
 StaticText {
 disabled,
 “#4:”
 };
/* 49 */ {240,10,255,45},
 StaticText {
 disabled,
 “#5:”
 };
/* 50 */ {270,10,285,45},
 StaticText {
 disabled,
 “#6:”
 };
/* 51 */ {120,250,135,285},
 StaticText {
 disabled,
 “#7:”
 };
/* 52 */ {150,250,165,285},
 StaticText {
 disabled,
 “#8:”
 };
/* 53 */ {180,250,195,285},
 StaticText {
 disabled,
 “#9:”
 };
/* 54 */ {210,250,225,285},
 StaticText {
 disabled,
 “#10:”
 };
/* 55 */ {240,250,255,285},
 StaticText {
 disabled,
 “#11:”
 };
/* 56 */ {270,250,285,285},
 StaticText {
 disabled,
 “#12:”
 };
/* 57 */ {95,30,110,100},
 StaticText {
 disabled,
 “Cylinder:”
 };
/* 58 */ {95,110,110,155},
 StaticText {
 disabled,
 “Head:”
 };
/* 59 */ {95,165,110,200},
 StaticText {
 disabled,
 “BFI:”
 };
/* 60 */ {95,270,110,340},
 StaticText {
 disabled,
 “Cylinder:”
 };
/* 61 */ {95,350,110,395},
 StaticText {
 disabled,
 “Head:”
 };
/* 62 */ {95,410,110,445},
 StaticText {
 disabled,
 “BFI:”
 }
 }
};
resource ‘ALRT’ (132,”Format Alert”,preload,nonpurgeable) {
 {100,100,250,400},
 132, 
 {
 Cancel, visible,2;
 Cancel,visible,2;
 Cancel,visible,2;
 Cancel,Visible,2
 }
};
resource ‘DITL’ (132,preload,nonpurgeable) {
  {
/* 1 */ {115,75,135,135},
 button {
 enabled,
 “OK”
 };
/* 2 */ {115,165,135,225},
 button {
 enabled,
 “Cancel”
 };
/* 3 */ {30,80,45,290},
 StaticText {
 disabled,
 “Do you really want to format”
 };

/* 4 */ {45,80,60,290},
 StaticText {
 disabled,
 “disk drive #^0?  It will erase”
 };
/* 5 */ {60,80,75,290},
 Statictext {
 disabled,
 “all the files on this volume.”
 }
 }
};
resource ‘ALRT’ (133,preload,nonpurgeable) {
 {50, 40, 300, 460},
 133,
 { /* array: 4 elements */
 /* [1] */
 OK, visible, sound1;
 /* [2] */
 OK, visible, sound1;
 /* [3] */
 OK, visible, sound1;
 /* [4] */
 OK, visible, sound1
 }
};

resource ‘DITL’ (133,preload,nonpurgeable) {
 { /* array DITLarray: 8 elements */
 /* [1] */
 {213, 180, 233, 240},
 Button {
 enabled,
 “OK”
 };
 /* [2] */
 {19, 92, 34, 267},
 StaticText {
 enabled,
 “A SCSI error has occurred!”
 };
 /* [3] */
 {70, 15, 85, 251},
 StaticText {
 disabled,
 “SCSI command or trap that failed:”
 };
 /* [4] */
 {85, 35, 99, 290},
 StaticText {
 disabled,
 “^0”
 };
 /* [5] */
 {115, 15, 130, 125},
 StaticText {
 disabled,
 “Type of error:”
 };
 /* [6] */
 {130, 35, 145, 290},
 StaticText {
 disabled,
 “^1”
 };
 /* [7] */
 {160, 15, 175, 200},
 StaticText {
 disabled,
 “Most probable cause:”
 };
 /* [8] */
 {175, 35, 190, 405},
 StaticText {
 disabled,
 “^2”
 }
 }
};
resource‘STR ‘ ($A000,preload,nonpurgeable) {
 “_SCSIReset.”
 };
resource‘STR ‘ ($A100,preload,nonpurgeable) {
 “_SCSIGet.”
 };
resource‘STR ‘ ($A200,preload,nonpurgeable) {
 “_SCSISelect.”
 };
resource‘STR ‘ ($A300,preload,nonpurgeable) {
 “_SCSICommand.”
 };
resource‘STR ‘ ($A400,preload,nonpurgeable) {
 “_SCSIComplete.”
 };
resource‘STR ‘ ($A600,preload,nonpurgeable) {
 “_SCSIWrite.”
 };
resource‘STR ‘ ($000,preload,nonpurgeable) {
 “Test Unit Ready.”
 };
resource‘STR ‘ ($400,preload,nonpurgeable) {
 “Format Unit.”
 };
resource‘STR ‘ ($1500,preload,nonpurgeable) {
 “Mode Select.”
 };
resource‘STR ‘ ($1003,preload,nonpurgeable) {
 “Write Fault.”
 };
resource‘STR ‘ ($1004,preload,nonpurgeable) {
 “Drive not ready.”
 };
resource‘STR ‘ ($1020,preload,nonpurgeable) {
 “Command not implemented.”
 };
resource‘STR ‘ ($1024,preload,nonpurgeable) {
 “Bad parameter passed to controller.”
 };
resource‘STR ‘ ($AA02,preload,nonpurgeable) {
 “SCSI communication error.”
 };
resource‘STR ‘ ($AA04,preload,nonpurgeable) {
 “Bad pseudo program.”
 };
resource‘STR ‘ ($AA05,preload,nonpurgeable) {
 “SCSI timing error.”
 };
resource‘STR ‘ ($2003,preload,nonpurgeable) {
 “Drive detects failure during power on diagnostic.”
 };
resource‘STR ‘ ($2004,preload,nonpurgeable) {
 “Drive not up to speed or hardware problem.”
 };
resource‘STR ‘ ($2020,preload,nonpurgeable) {
 “Controller does not support this command.”
 };
resource‘STR ‘ ($2024,preload,nonpurgeable) {
 “Defect in cylinder 0 or bad defect parameters.”
 };
resource‘STR ‘ ($BA02,preload,nonpurgeable) {
 “Wrong address or no termination.”
 };
resource‘STR ‘ ($BA04,preload,nonpurgeable) {
 “Programmer error; my fault.”
 };
resource‘STR ‘ ($BA05,preload,nonpurgeable) {
 “Program uses incorrect delay value for your controller.”
 };
resource ‘ALRT’ (134,preload,nonpurgeable) {
 {100, 100, 270, 400},
 134,
 { /* array: 4 elements */
 /* [1] */
 OK, visible, sound1;
 /* [2] */
 OK, visible, sound1;
 /* [3] */
 OK, visible, sound1;
 /* [4] */
 OK, visible, sound1
 }
};

resource ‘DITL’ (134,preload,nonpurgeable) {
 { /* array DITLarray: 4 elements */
 /* [1] */
 {129, 120, 149, 180},
 Button {
 enabled,
 “OK”
 };
 /* [2] */
 {20, 90, 35, 252},
 StaticText {
 enabled,
 “No more defects can be”
 };
 /* [3] */
 {40, 90, 55, 263},
 StaticText {
 disabled,
 “added to the defect list.”
 };
 /* [4] */
 {60, 90, 75, 263},
 StaticText {
 disabled,
 “The buffer is full.  The”
 };
 /* [5] */
 {80, 90, 95, 275},
 StaticText {
 disabled,
 “first 60 defects were used.”
 }
 }
};
resource ‘DLOG’ (135,preload,nonpurgeable) {
 {131, 131, 231, 381},
 dBoxProc,
 visible,
 noGoAway,
 0x0,
 135,
 “”
};
resource ‘DITL’ (135,preload,nonpurgeable) {
 { /* array DITLarray: 3 elements */
 /* [1] */
 {15, 20, 30, 230},
 StaticText {
 enabled,
 “Now formatting SCSI disk #^0.”
 };
 /* [2] */
 {35, 20, 50, 224},
 StaticText {
 disabled,
 “This will take between three”
 };
 /* [3] */
 {55, 20, 70, 166},
 StaticText {
 disabled,
 “and fifteen minutes.”
 }
 }
};

SCSI Formatter Insights

Steve Brecher

Sunnyvale, CA

(From Letters Column Vol. 3 No. 9)

There are some errors in the “SCSI Formatter Project, Part II” article in the June 1987 MacTutor, and I thought that corrections might be of help to readers who are implementing the project.

(1) The status byte which is returned to the initiator (the Mac) by a target device (disk controller) is not a target-specific error code. Rather, it is a generalized result. Bit 1 set indicates a “check condition,” or error. Hence, a status byte value of 2 indicates that an error occurred, but it does not indicate the nature of the error. To determine the cause of the error, a Request Sense command must be issued to the target. Then the target will send sense data which specifies the nature of the error that occurred.

This - not a bug in the SCSI Manager - is the reason that author Tim Standing observed a status value of 2 when an error occurred. This misunderstanding is reflected in the Formatter program code, which will not correctly diagnose or report error conditions.

In sum, there are three sources of error information: (a) the result code returned by a SCSI Manager function, e.g., phase error, which indicates whether there was some problem in communicating with the target device; (b) the status byte which is returned in the low half of the VAR “stat” integer parameter of the SCSIComplete call, which indicates whether or not an error occurred; and (c) the sense data that is returned by the target in response to a Request Sense command, e.g., unit not ready, write fault, etc., which indicates the exact nature of any error that occurred.

(2) Pseudo-programs, or Transfer Instruction Blocks (TIBs), are not part of the SCSI standard. Rather, they are an invention of Apple, and are found only in Apple’s SCSI Manager software. TIBs were implemented to permit the transfer of disk file sector tags along with each sector of disk data; the tags and the sector data are at different RAM addresses and must be read/written in an interleaved fashion (tags, sector data, tags, sector data...). TIBs permit multiple sectors with tags to be read or written with just one SCSICmd and one SCSIRead or SCSIWrite call. While some existing products implement file sector tags, Apple is no longer supporting tags for new products, so in that sense TIBs are implemented only for historical reasons.

Last, a few minor implementation notes:

The Mac OS provides a Delay routine which can be used in place of Tim’s CountOff subroutine.

The Control Manager provides a SetCtlValue routine which can be used to alter the setting of a radio button and redraw it to reflect the new setting.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more

Jobs Board

*Apple* Systems Administrator - JAMF - Syste...
Title: Apple Systems Administrator - JAMF ALTA is supporting a direct hire opportunity. This position is 100% Onsite for initial 3-6 months and then remote 1-2 Read more
Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.