* scottdisk.asm Copyright 1987 by Scott Turner ALL RIGHTS RESERVED * * This is a MOUNTable device driver for the MicroForge SCSI buss interface. * It may not chase yer mouse pointer around the screen, but I think someone * will find it useful :-). The strict license below is not because I'm an * asshole, but because there ARE assholes out there in the world. I've put * alot of effort into the driver and it is my sincere wish that it be made * available to ANYONE who wants it so long as the person or entity providing * it to them doesn't make a profit off it. BE WARNED, THIS IS NOT A 'PLAY' * LICENSE, I MEAN IT. (grrrr) * * This device driver supports 1 (ONE) SCSI buss device. This device can have * 2 (TWO) hard disk units attached to it. This driver is currently tuned for * use with an OMTI 5100 SCSI buss device. * * Please note that the OMTI needs a little breathing space after completion * of one command before beginning another. This driver is tuned to work on a * MC68000 or MC68010 running at the factory clock rate. If you jack the clock * rate or use an MC68020 you may need to re-tune this driver. * * Please note that this device driver uses the power LED as a disk activity * indicator. * * Formatting the hard disk seems to be a touchy area for most disk drivers * I've seen or heard about. This driver uses the SCSI command #4 to format * the drive if it is issued a format command for IO_OFFSET 0. I tried using * the OMTI format track command but it didn't really seem to format the * track because when I hit the un-formatted part of my test hard disk it * didn't format it. (Reads would return error $94) So I fell back to my * less than ideal solution of issuing the format command at offset 0. This * allows you to format the hard disk with the CLI FORMAT command, but none * of the other 'nifty' uses of a track by track formatter are available. * This may seem like a total bummer but then again more than a few controllers * can't format a single track. :) PLEASE NOTE: Unlike the MessyDOS FORMAT * command, there IS NO CHANCE of recovering data from the drive! So let's be * careful out there. ;) * * The MOUNT command can be used to partition the two hard disk drives into * smaller pieces. (See hints below) * * Here is a sample mountlist entry for a Seagate ST-4051: * DH0: Device = scottdisk.device * Unit = 0 * Flags = 1 * Surfaces = 5 * BlocksPerTrack = 17 * Reserved = 2 * Interleave = 0 * LowCyl = 0 ; HighCyl = 976 * Buffers = 30 * BufMemType = 0 * # * Don't forget that the numbers used in the mountlist and used at the end of * this driver DO NOT HAVE TO MATCH. HOWEVER, the total SIZE given in the * mountlist MUST NOT exceed the size encoded at the end of this driver. * * To setup a system for operation: * 1. Configure the driver for your hard disk drive(s). * 2. Construct this driver and place the result in df0:devs/scottdisk.device * 3. Insert entry(s) into df0:devs/mountlist * 4. For each drive: * A. mount * B. format drive name * C. Insert 'mount ' into your df0:s/startup-sequence * 5. Enjoy! :-) * * Hints: * If like me you hate using a slow 2nd floppy drive to copy disks, then setup * a partition on your hard disk using mountlist. Make it the same as an * amiga floppy: 11 sectors per track, 2 sides, 80 tracks. Then mount it and * you can diskcopy to/from a floppy to/from it! You can even call it DF1! :) * Rather than use the CLI AddBuffers command just tweak the 'Buffers =' line in * the mountlist entry. * * Please note that this driver could have tons of things done to it to improve * performance. And I am currently continuing work to make all sorts of changes * to this driver to improve performance. This version is being released at this * time so that others can get some use from it while I labor on. * * If you have questions/comments/requests concerning this source code, please * direct them in WRITING (no I don't mean via phone!) to: * * Scott Turner * L5 Computing * 12311 Maplewood Avenue * Edmonds, WA. 98020-1115 USA * * I may also be reached via: * * JST on GEnie * scotty@l5comp.UUCP or stride!l5comp!scotty * * I am NOT releasing this source code into the public domain. However, I here * by grant a license for distribution via AmigaDOS format 3.5" diskette or via * an online telecommunications medium provided that the party charges less than * the following to do so: * * USA $10 for a copy on an AmigaDOS 3.5" disk. * USA $10 an hour for 1200 baud connection at 18:00 PST from Seattle, WA USA. * * You may not use this source code to make object code if you intend to charge * anything above the above limits for the object code alone. ie you may not * sell the object code for more than the above stated limits for the source. * * I also hereby grant a license for converting this source code for the * following purposes: * 1. To change the hard disk drive parameters. * 2. To work with other SCSI buss interface cards. * 3. To work with other SCSI buss devices. * 4. Re-tune time delays. * * HOWEVER, under this license you may NOT: * 1. Remove my copyright. * 2. Modify or make additions to this license. * 3. Change the name of the device from 'scottdisk.device'. * 4. Change the limitation on charges for object code. * * (If you wonder if I'm egotistical, please note that 'scott' is the same * length as 'track'. This is handy for patching disk editors to use this device * driver rather than trackdisk.device.) * * I reserve all other rights. This source code is made available "AS IS" and I * make no warranties for it's fitness for any purpose. * *------------------------------------------------------------------------------ * * I hate assembling 3,000 lines of include files. How about you? * * Definition of our base variables * This first part is cast in concrete LibNegSize EQU 16 LibPosSize EQU 18 OpenCount EQU 32 * * End of pre-defined lib-base structure, we can now do as we damn well please. CurUnit EQU 34 CurIOReq EQU 36 CurBlock EQU 40 CurBuffer EQU 44 NotifyIRQ EQU 48 NeedNotify EQU 52 DosBase EQU 56 BaseVarSize EQU 60 * * External references to EXEC _MakeLibrary EQU -6*14 _Forbid EQU -6*22 _Permit EQU -6*23 _Cause EQU -6*30 _AddPort EQU -6*59 _PutMsg EQU -6*61 _GetMsg EQU -6*62 _ReplyMsg EQU -6*63 _WaitPort EQU -6*64 _OpenLibrary EQU -6*68 _CloseLibrary EQU -6*69 _AddDevice EQU -6*72 * * External AmigaDOG reference _CreateProc EQU -6*23 * * Address of SCSI port CtrlAddr EQU $EF7000 * * Device version MajorVersion EQU 33 MinorVersion EQU 1 * * Number of blocks per track for BOTH DRIVES!!! BlocksPerTrack EQU 17 * * Driver header DriverIDString DC.B 'ScottDisk 33.1 (23 May 1987)',13,10,0 MatchTag DC.W $4AFC ; RT_MATCHWORD DC.L MatchTag ; RT_MATCHTAG DC.L EndOfDriver ; RT_ENDSKIP DC.B 1 ; RT_FLAGS DC.B MajorVersion ; RT_VERSION DC.B 3 ; RT_TYPE DC.B 30 ; RT_PRI DC.L DeviceName ; RT_NAME DC.L DriverIDString ; RT_IDSTRING DC.L DriverInit ; RT_INIT * * NOTE: This table is up here so the entries are positive in value. I don't * know if this is important or not. VectorTable DC.W -1 ; Entries are relative to VectorTable DC.W DeviceOpen-VectorTable ; Open DC.W DeviceClose-VectorTable ; Close DC.W DeviceNull-VectorTable ; Expunge DC.W DeviceNull-VectorTable ; NULL DC.W DeviceBeginIO-VectorTable ; BeginIO DC.W DeviceNull-VectorTable ; AbortIO DC.W -1 ; End of the table DeviceOpen CMPI.L #1,D0 ; Unit number = 0 or 1? BHI.S 0$ MOVE.L D0,24(A1) ; Then AOK CLR.B 31(A1) ; IO_ERR := 0 ADDQ.W #1,32(A6) ; Bump open count RTS * * Illegal unit # requested 0$ MOVE.B #32,31(A1) ; Return bad unit number error RTS DeviceClose MOVEQ #-1,D0 MOVE.L D0,24(A1) ; Kill unit # MOVE.L D0,20(A1) ; Kill device pointer * * Dec open counter, but don't let it go negative MOVE.W OpenCount(A6),D0 BEQ.S 0$ SUBQ.W #1,D0 MOVE.W D0,OpenCount(A6) 0$ MOVEQ #0,D0 RTS DeviceBeginIO MOVEM.L D1/A0-A1,-(A7) CMPI.L #1,24(A1) ; Legal unit #? BHI.S 3$ CLR.B 31(A1) ; IO_ERR := 0 MOVE.B 29(A1),D0 ; Get command SUBI.B #12,D0 ; Standard command? BLT.S 2$ CMPI.B #4,D0 ; Special command? BGE.S 2$ TST.B D0 BNE.S 0$ * * Record notify info MOVE.L 40(A1),NotifyIRQ(A6) SNE NeedNotify(A6) BRA.S 1$ * * Return command packet now 3$ MOVE.B #32,31(A1) ; IO_ERR := bad unit you nerd! 0$ CLR.L 32(A1) 1$ BSET #7,30(A1) MOVE.B #5,8(A1) MOVEM.L (A7)+,D1/A0-A1 MOVEQ #0,D0 RTS * * Post command to the handler 2$ ANDI.B #$7E,30(A1) ; Clear status flags LEA OurPort,A0 MOVE.L A6,-(A7) MOVEA.L 4,A6 JSR _PutMsg(A6) MOVEA.L (A7)+,A6 MOVEM.L (A7)+,D1/A0-A1 MOVEQ #0,D0 DeviceNull RTS DriverInitStructure DC.B %11100000 DC.B 0 DC.W $8 ; LN_TYPE DC.B 3 ; Device DC.B 0 ; Pad DC.B %11000000 DC.B 0 DC.W $A ; LN_NAME DC.L DeviceName DC.B %11100000 DC.B 0 DC.W $E ; LIB_FLAGS DC.B 6 ; Value for LIB_FLAGS, JustChanged, and please sum DC.B 0 ; Padding DC.B %11000000 DC.B 0 DC.W 20 ; LIB_VERSION DC.W MajorVersion DC.W MinorVersion DC.L 0 DogName DC.B 'dos.library',0 NewProcRecord DC.L DeviceName DC.L 5 DC.L SegList DC.L 256 DriverInit MOVEM.L A2/A6,-(SP) MOVEA.L 4,A6 * * Build library structure LEA VectorTable,A0 ; Vectors LEA DriverInitStructure,A1 ; InitStructure SUBA.L A2,A2 ; InitRoutine, NULL we're already running! MOVEQ #BaseVarSize,D0 ; Our data seg size JSR _MakeLibrary(A6) TST.L D0 BEQ.S TwilightZone ; Go there if we didn't get created * * We were created * Save library base for the new process MOVE.L D0,LibraryBase MOVEA.L D0,A2 * * Create a process for our handler LEA DogName(PC),A1 MOVEQ #0,D0 JSR _OpenLibrary(A6) MOVE.L D0,DosBase(A2) MOVEA.L D0,A2 * * Create the handler process MOVEM.L NewProcRecord(PC),D1-D4 LSR.L #2,D3 ; Linker won't do this :) JSR _CreateProc(A2) SUBI.L #$5C,D0 ; Convert DOS process to Exec process MOVE.L D0,PortTask ; Patch message port * * Add our communications port to the system list LEA OurPort,A1 JSR _AddPort(A6) * * Add us to the system device list MOVEA.L LibraryBase,A1 ; Position parameter for call JSR _AddDevice(A6) MOVEQ #1,D0 ; Indicate that we opened MOVEM.L (SP)+,A2/A6 RTS TwilightZone MOVEQ #0,D0 ; Indicate that we didn't open MOVEM.L (SP)+,A2/A6 RTS * * Here begins the code for the handler process CNOP 0,4 ; MUST be on longword boundary DC.L 16 ; Segment length, value doesn't matter SegList DC.L 0 ; Pointer to next segment (ie none) * * Initialize some registers with some handy values MOVEA.L 4,A6 MOVEA.L LibraryBase,A5 MOVEA.L #CtrlAddr,A4 LEA 42(A4),A2 ; Status port-READ ONLY LEA 44(A4),A3 ; Status port-WRITE ONLY LEA 40(A4),A4 ; Data port-WRITE ONLY * * Offset 38 is the Data port-READ ONLY, but I ran out of regs... * * MicroForge SCSI status port bits * Read 0 Reads as ONE Write 0 ACK* * 1 MSG* 1 ??? * 2 CD* 2 SEL* * 3 IO* 3 ??? * 4 REQ* 4 ??? * 5 Reads as ONE 5 ??? * 6 Reads as ONE 6 ??? * 7 Reads as ONE 7 ??? MOVEQ #1,D3 ; Used for ACKing MOVEQ #4,D4 ; Used checking REQ MOVEQ #0,D5 ; Used for ACKing * * Configure the SCSI hard disk controller JSR _Forbid(A6) BSR InitController JSR _Permit(A6) * * Start waiting for commands on our port CLR.W NeedNotify(A5) LoopTop TST.W NeedNotify(A5) BEQ.S 0$ MOVEA.L NotifyIRQ(A5),A1 JSR _Cause(A6) CLR.W NeedNotify(A5) 0$ LEA OurPort,A0 JSR _WaitPort(A6) MOVE.L D0,CurIOReq(A5) LEA OurPort,A0 ; Delink the message JSR _GetMsg(A6) TST.L CurIOReq(A5) BEQ LoopTop * * We have a packet, process it BCHG #1,$BFE001 ; Dim the LED MOVEA.L CurIOReq(A5),A0 MOVE.W 24+2(A0),D1 ; Get unit # LSL.W #5,D1 ; Convert to LUN MOVE.B D1,CurUnit(A5) ; Store for use later MOVE.L 44(A0),D0 ; Get IO_OFFSET MOVEQ #9,D1 LSR.L D1,D0 MOVE.L D0,CurBlock(A5) MOVE.L 36(A0),D7 ; Get IO_LENGTH LSR.L D1,D7 MOVE.L 40(A0),CurBuffer(A5) ; Move IO_BUFFER MOVEQ #0,D1 ; Get IO_COMMAND MOVE.B 29(A0),D1 CMPI.W #12,D1 ; In range? BCC.S CmdDone ADD.W D1,D1 JMP DispatchTable(PC,D1.W) MotorCmd MOVE.L D3,32(A0) ;Return motor as ON BRA.S CmdDone ReturnAOK MOVE.L D5,32(A0) CmdDone BCHG #1,$BFE001 ; Bring LED back up MOVEA.L CurIOReq(A5),A1 ; Send request back JSR _ReplyMsg(A6) BRA LoopTop ; Back to loop top DispatchTable BRA ReturnAOK ;Invalid BRA ReturnAOK ;Reset BRA.S ReadCmd ;Read BRA.S WriteCmd ;Write BRA ReturnAOK ;Update buffers BRA ReturnAOK ;Clear buffers BRA ReturnAOK ;Stop BRA ReturnAOK ;Start BRA ReturnAOK ;Abort BRA MotorCmd ;Motor BRA ReturnAOK ;Seek * * Save one BRA by placing this here (Can I shave code or what? :) FormatCmd TST.L CurBlock(A5) BNE.S WriteCmd SUBQ.W #1,D7 BLT.S 1$ 0$ MOVE.B #4,CurrentCmd ; Format track command MOVE.B #1,CurrentCmd+4 BSR SendCmd BSR FinishUp BPL.S WriteCmd 1$ MOVEA.L CurIOReq(A5),A0 MOVE.L 36(A0),32(A0) BRA CmdDone ReadCmd MOVE.B #8,CurrentCmd MOVE.B D7,CurrentCmd+4 BEQ.S 1$ SUBQ.W #1,D7 BSR.S SendCmd 0$ BSR ReadData DBPL D7,0$ BSR FinishUp 1$ MOVEA.L CurIOReq(A5),A0 MOVE.L 36(A0),32(A0) BRA CmdDone WriteCmd MOVE.B #$A,CurrentCmd MOVE.B D7,CurrentCmd+4 BEQ.S 1$ SUBQ.W #1,D7 BSR.S SendCmd 0$ BSR.S WriteData DBPL D7,0$ BSR FinishUp 1$ MOVEA.L CurIOReq(A5),A0 MOVE.L 36(A0),32(A0) BRA CmdDone SendCmd MOVE.L CurBlock(A5),D0 MOVE.W D0,CurrentCmd+2 SWAP.W D0 OR.B CurUnit(A5),D0 MOVE.B D0,CurrentCmd+1 * * Select SCSI device MOVE.B D3,(A4) MOVE.B D4,(A3) * * Wait for BSY 0$ MOVEQ #%11111,D0 AND.B (A2),D0 BEQ 0$ * * Clear our out going bits (get off buss) MOVE.B D5,(A4) MOVE.B D5,(A3) * * Wait for SEL 1$ BTST #2,(A2) BEQ 1$ MOVEQ #6-1,D0 LEA CurrentCmd,A0 * * Wait for REQ 2$ BTST D4,(A2) BEQ 2$ * * Send command bytes 3$ MOVE.B (A0)+,(A4) MOVE.B D3,(A3) MOVE.B D5,(A3) DBF D0,3$ MOVE.B D5,(A4) RTS WriteData MOVEA.L CurBuffer(A5),A0 MOVEQ #64-1,D0 * * Wait for REQ 0$ BTST D4,(A2) BEQ 0$ * * Check C/D if it's clear then AOK, else ERROR. BTST #2,(A2) BNE.S 2$ * * Write data to SCSI buss * NOTE: Un-winding past 8 doesn't return alot for the extra size. 1$ MOVE.B (A0)+,(A4) ;1 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;2 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;3 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;4 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;5 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;6 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;7 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A0)+,(A4) ;8 MOVE.B D3,(A3) MOVE.B D5,(A3) DBF D0,1$ MOVE.B D5,(A4) 2$ MOVE.L A0,CurBuffer(A5) TST.W D0 RTS ReadData MOVEA.L CurBuffer(A5),A0 MOVEQ #64-1,D0 MOVEA.L #CtrlAddr+38,A1 * * Wait for REQ 0$ BTST D4,(A2) BEQ 0$ * * Check C/D if it's clear then AOK, else ERROR. BTST #2,(A2) BNE.S 2$ * * Read data from SCSI buss * NOTE: Un-winding past 8 doesn't return alot for the extra size. 1$ MOVE.B (A1),(A0)+ ;1 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;2 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;3 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;4 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;5 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;6 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;7 MOVE.B D3,(A3) MOVE.B D5,(A3) MOVE.B (A1),(A0)+ ;8 MOVE.B D3,(A3) MOVE.B D5,(A3) DBF D0,1$ 2$ MOVE.L A0,CurBuffer(A5) TST.W D0 RTS FinishUp * * Wait for C/D 0$ BTST #3,(A2) BEQ 0$ * * Wait for REQ 1$ BTST D4,(A2) BEQ 1$ * * Read completion status byte MOVE.B CtrlAddr+38,D0 MOVE.B D3,(A3) MOVE.B D5,(A3) * * Wait for MSG 2$ BTST.B D3,(A2) BEQ 2$ * * Wait for REQ 3$ BTST D4,(A2) BEQ 3$ * * ACK the completion message byte MOVE.B D3,(A3) MOVE.B D5,(A3) * * Did an error occur? BTST D3,D0 BNE.S Error * * A little delay for when we're good... MOVEQ #$10,D0 9$ DBF D0,9$ * * Return normal completion MOVEQ #0,D0 RTS * * Error occured in command * Send command #3 to get reason for error Error MOVE.B #3,CurrentCmd CLR.B CurrentCmd+4 CLR.L CurBlock(A5) * * OMTI needs time to think before we ask for error sense data. MOVEQ #$7F,D0 9$ DBF D0,9$ BSR SendCmd MOVEQ #4-1,D0 LEA ErrBuffer,A0 MOVEA.L #CtrlAddr+38,A1 * * Wait for REQ 0$ BTST D4,(A2) BEQ 0$ * * Check C/D if it's clear then AOK, else ERROR BTST #2,(A2) BNE.S 2$ * * Read data from SCSI buss 1$ MOVE.B (A1),(A0)+ MOVE.B D3,(A3) MOVE.B D5,(A3) DBF D0,1$ MOVE.B D5,(A4) BSR.S TossStatus * * Take the data returned and feed it to the user MOVEA.L CurIOReq(A5),A0 MOVE.B ErrBuffer,D0 MOVE.B D0,$CF ; "back door" to get I/O result MOVE.B D0,31(A0) ; Return SCSI error code MOVEQ #-1,D0 RTS * * Ooops, we had an error getting the error info. * BTW, this is most likely because the delay above needs tweaking. 2$ BSR.S TossStatus MOVE.B #20,31(A0) ; Return some kind of error :) MOVEQ #-1,D0 RTS TossStatus * * Wait for C/D 0$ BTST #3,(A2) BEQ 0$ * * Wait for REQ 1$ BTST D4,(A2) BEQ 1$ * * Read completion status byte MOVE.B D3,(A3) MOVE.B D5,(A3) * * Wait for MSG 2$ BTST.B D3,(A2) BEQ 2$ * * Wait for REQ 3$ BTST D4,(A2) BEQ 3$ * * ACK the completion message byte MOVE.B D3,(A3) MOVE.B D5,(A3) RTS InitController LEA InitCmd,A0 BSR.S SendICmd LEA InitData,A0 BSR.S SendInitData * * Wait for OMTI to get with it MOVEQ #$10,D0 0$ DBF D0,0$ LEA InitCmd1,A0 BSR.S SendICmd LEA InitData1,A0 BRA.S SendInitData SendICmd MOVE.B D3,(A4) MOVE.B D4,(A3) 0$ MOVEQ #%11111,D0 AND.B (A2),D0 BEQ 0$ MOVE.B D5,(A4) MOVE.B D5,(A3) 1$ BTST #2,(A2) BEQ 1$ MOVEQ #6-1,D0 2$ BTST D4,(A2) BEQ 2$ 3$ MOVE.B (A0)+,(A4) MOVE.B D3,(A3) MOVE.B D5,(A3) DBF D0,3$ MOVE.B D5,(A4) RTS SendInitData MOVEQ #10-1,D0 0$ BTST D4,(A2) BEQ 0$ 1$ MOVE.B (A0)+,(A4) MOVE.B D3,(A3) MOVE.B D5,(A3) DBF D0,1$ MOVE.B D5,(A4) BRA TossStatus DATA * * NOTE: I use a DATA section so that I can reach some of these variables via * absolute addressing. I COULD hang them off (A5) but it was just simpler to * hack them in down here as I needed them... DeviceName DC.B 'scottdisk.device',0 CNOP 0,4 LibraryBase DC.L 0 OurPort DC.L 0 DC.L 0 DC.B 4 DC.B 0 DC.L DeviceName DC.B 0 DC.B 24 PortTask DC.L 0 ;TaskHandle LH1 DC.L LH2 LH2 DC.L 0 DC.L LH1 DC.B 5 DC.B 0 CurrentCmd DC.B 0,0,0,0,1,0 ErrBuffer DC.L 0 InitCmd DC.B $C2,0,0,0,0,0 InitCmd1 DC.B $C2,$20,0,0,0,0 * * The tables below are sent to the controller to configure it for the drive * attached to it. Do not change the sector size from 512 bytes!!!! * * Some global data about these values for the OMTI 5100 controller: * Step pulse width: * 00 thru 05 5us * 06 thru 08 7.8us * 09 thru 0B 10.6us * 0C thru 0E 13.4us * etc.... * * Step period: * Value * 50us with value 0 yielding 9us rather than 0us :). * * Number of heads: * This value must be one less than the number of heads. If the drive has 5 * heads then enter 4. * * Number of cylinders: * This value must be one less than the number of cylinders. If the drive has * 977 cylinders then enter 976. * * Bytes 7 and 8: * Are used to set the precomp and reduced write current starting points. * Reduced write is based on the 8 bits in byte 7. If byte 7 equals 0 or 1 then * reduced write current is disabled. Otherwise it starts at the cylinder * indicated by the value of byte 7. * Write precompensation is based on a 10 bit value made up from the 8 bits in * byte 7 and the lower 2 bits in byte 8. The two bits from byte 8 are tacked * onto the left of the bits from byte 7 to make the 10 bit value. If this 10 * bit value is 0 then precomp is disabled. If this 10 bit value is 1 then * precomp is applied to ALL cylinders. Any other value indicates the cylinder * to start precomp on. * * Init data for drive 0 InitData DC.B 0 ; Step pulse width DC.B 0 ; Step period DC.B 0 ; Step mode DC.B 5 ; Number of heads DC.W 627 ; Number of cylinders DC.W 0 ; Bytes 7 and 8 (see above) DC.B BlocksPerTrack DC.B 0 ; Reserved * * Init data for drive 1 InitData1 DC.B 0 ; Step pulse width DC.B 0 ; Step period DC.B 0 ; Step mode DC.B 3 ; Number of heads DC.W 611 ; Number of cylinders DC.W 0 ; Bytes 7 and 8 (see above) DC.B BlocksPerTrack DC.B 0 ; Reserved * * End driver on a long word boundary CNOP 0,4 EndOfDriver END