aubrey@rpp386.cactus.org (Aubrey McIntosh) (01/03/90)
This program works in MS-DOS under Logitech 3.x. It is hard coded to format a 3.5" drive in drive A: (/dev/fd0) to 720kb. The user must provide FAT and empty Directory (mkfs must also be run). There is no I/O, so there is no concern with different calling conventions between systems: this talks directly with DMA and the NEC765. NO SYSTEM CALLS are made. The ROM bios is used, but is intact during the Minix bootup menu period. I am looking for someone to correspond with as I translate it into C. I know Zilch about C, and am still trying to get 1.1 --> 1.2 upgrades from NoDak. Actually, I'm looking for help there too. If no one comes forth, I may cheat and post format.a in hand translation. ------------------------format.mod--------------------- (* * NOTICES * Aubrey McIntosh * December 4, 1989 * Austin, Texas * * world!cs.utexas.edu![ rpp386.Cactus.ORG | val.COM ]!aubrey * * Contract Programming Services available. * * Copyright 1989 by Aubrey McIntosh. * Permission granted to make derivative work, and to distribute * for any reasons, provided that this comment remains intact, * derivations are identified by author(s), and original works * can be identified. * * BACKGROUND * This file contains a study for a floppy disk format program * running under Minix. Unfortunately I haven't written C. However, * 'we' can translate a working Modula-2 program into C or .asm * with some confidence. Actually (12/28/89) this draft just * talks with the NEC765, with little regard for Minix. * * Minimal guidance for identifier names and structure taken from * the floppy.c driver under Minix 1.1 * * This program (shall) run(s) under MS-DOS with the Logitech 3.x * environment. * * USAGE * In MS-DOS, type * c:> format0 * Drive 0 will be formatted as a 720kB 3.5" floppy. (hard coded const) * use Norton to write dir stock and virgin fat. * --- This works. --- * * In Minix: * Not yet ported. See future work section. * * FUTURE WORK NEEDED * * Write a Modula-2 compiler for Minix. :-) * anyone who has worked on *nix .DEF files ~please~ drop me a line, * If I do it, everyone will cuss at ~me.~~ * * Add command line switches for: * 1) Drive type. * 2) Which Drive. * 3) Reformatting with data intact. * 4) Robustness, e.g. write protected drive * * January 2, 1990. * * Overview of future work and program environment. * * This program will need to be included by build with fsck and init. * I have not looked at that code to see what needs to be done. * My current idea is to compile this 'by hand' or else to obtain * the newly announced Modula-3 compiler which produces C output, * and end up with the format0.s and executable Minix files. * I further understand that the ROM bios interrupt tables and * hardware initialisation is still in the default state when the * boot menu is active. * * An option, d Diskette format, should be added to the startup menu. * mkfs will be called by the user from the same startup menu. * * A decode file, roughly compatible to 'format0.asm,' is available * for the intrepid. * * REFERENCES * IBM Technical Reference Manual, pn 6025008. * Minix 1.1 sources, floppy.c, Prentice Hall. * NEC uPD765 Data Sheet. * Logitech users manual. *) MODULE Format0; IMPORT DebugPMD, RTSMain, SYSTEM; MODULE NEC765; IMPORT SYSTEM, RTSMain, panic, Relinquish, seekStatus, STATUSport, DATAport, RATE; EXPORT DoRecalibrate, DoSeek, DoSenseInterruptStatus, DoFormatATrack, (* *) SR0Bits, HD; (* Page 6-4 of 1984 NEC data book. * Primitive I/O support for the 765. * * This module should support anything that the NEC765 will do, * and could be made into a separate module and submitted to a * library. *) TYPE MainStatus = ( D0B, D1B, D2B, D3B, CB, EXM, DIO, RQM ); SR0 = ( US0, US1, HD, NR, EC, SE, D6, D7 ); SR1 = ( MA, NW, ND, res13, or, DE, res16, EN ); SR2 = ( MD, BC, SN, SH, WC, DD, CM, res27); SR3 = ( US03, US13, HD3, TS, T0, RY, WP, FT); MainStatusBits = SET OF MainStatus; SR0Bits = SET OF SR0; SR1Bits = SET OF SR1; SR2Bits = SET OF SR2; SR3Bits = SET OF SR3; PROCEDURE DoRecalibrate( (* fdn : CARDINAL *) ); VAR results : SR0Bits; PCN : CARDINAL; BEGIN WriteDataRegisterFile( Recalibrate ); WriteDataRegisterFile( SR0Bits { } (* Drive 0 *) ); REPEAT UNTIL 7 IN seekStatus; EXCL( seekStatus, 7 ); DoSenseInterruptStatus( results, PCN ) END DoRecalibrate; PROCEDURE DoSeek( Head, NCN : CARDINAL ); VAR results : SR0Bits; PCN : CARDINAL; BEGIN WriteDataRegisterFile( Seek ); IF Head = 0 THEN WriteDataRegisterFile( SR0Bits { } (* Head 0 *) ) ELSE WriteDataRegisterFile( SR0Bits { HD } (* Head 1 *) ) END; WriteDataRegisterFile( NCN ); END DoSeek; PROCEDURE DoSenseInterruptStatus( VAR st0 : SR0Bits; VAR PCN : CARDINAL); BEGIN WriteDataRegisterFile( SenseInterruptStatus ); ReadDataRegisterFile( st0 ); ReadDataRegisterFile( PCN ) END DoSenseInterruptStatus; PROCEDURE DoFormatATrack( Head : CARDINAL; Bytes, Sectors : CARDINAL ); VAR aux : CARDINAL; (*The DMA should be armed before this is called.*) BEGIN WriteDataRegisterFile( MFM + FormatATrack ); IF Head = 0 THEN WriteDataRegisterFile( SR0Bits { } (* Head 0 *) ) ELSE WriteDataRegisterFile( SR0Bits { HD } (* Head 1 *) ) END; WriteDataRegisterFile( Bytes ); WriteDataRegisterFile( Sectors ); IF (Bytes = 3) AND (Sectors = 5) THEN (* 3.5" p. 6-11 in Nec Book for Sony drive *) WriteDataRegisterFile( 74H ) ELSE WriteDataRegisterFile( 54H ) END; WriteDataRegisterFile( 0H ); REPEAT (*relinquish*) UNTIL 7 IN seekStatus; EXCL( seekStatus, 7 ); ReadDataRegisterFile( aux ); ReadDataRegisterFile( aux ); ReadDataRegisterFile( aux ); (*Manditory read of meaningless (sic) results...*) ReadDataRegisterFile( aux ); ReadDataRegisterFile( aux ); ReadDataRegisterFile( aux ); ReadDataRegisterFile( aux ); END DoFormatATrack; PROCEDURE ReadMainStatus(): MainStatusBits; (* Wait 12 usec or longer for the chip to know what it did and update * the registers, then read the port. As I read the data sheets, * it will not do to loop poll the chip -- although that is what * Minix 1.1 floppy.c in fact does do. *) VAR result : MainStatusBits; BEGIN Relinquish( 12 ); SYSTEM.INBYTE ( STATUSport, result ); RETURN result END ReadMainStatus; PROCEDURE ReadDataRegisterFile( VAR file : ARRAY OF SYSTEM.WORD ); VAR count : CARDINAL; BEGIN REPEAT (* This should always conclude. *) (* Jan 2, 1990. * To be safe, I should actually do more testing on the * status bits to be sure I'm not here while the NEC765 is * waiting for the last byte of a command. But this works * if the next higher level is correct, and I'm gonna release it. *) UNTIL (ReadMainStatus () * MainStatusBits{ RQM, DIO }) = MainStatusBits{ RQM, DIO }; count := 0; WHILE (ReadMainStatus () * MainStatusBits{ RQM, DIO} = MainStatusBits { RQM, DIO }) AND ( count <= HIGH(file) ) DO SYSTEM.INBYTE( DATAport, file[ count ] ); INC( count ) END (* Jan 2, 1990 * This procedure was designed to be called with a packet parameter. * Then I broke the higher level up during debugging. * I stripped out all the packet data definitions. * After I put them back, I would * assert( count = HIGH(file)); *) END ReadDataRegisterFile; PROCEDURE WriteDataRegisterFile( file : ARRAY OF SYSTEM.WORD ); VAR count : CARDINAL; BEGIN REPEAT (*wait. This should always conclude. *) UNTIL (ReadMainStatus () * MainStatusBits{ RQM, DIO }) = MainStatusBits{ RQM }; count := 0; WHILE (ReadMainStatus () * MainStatusBits{ RQM, DIO} = MainStatusBits { RQM }) AND ( count <= HIGH(file) ) DO SYSTEM.OUTBYTE( DATAport, file[ count ] ); INC( count ) END END WriteDataRegisterFile; CONST MT = 80H; (* The multi track bit command (PD7265 only) *) MFM = 40H; SK = 20H; (* Skip Deleted sector, read next *) (*These are listed here in the order that they are introduced *on page 6-6 in the NEC literature. I don't see the pattern. *) ReadData = 06H; (* 00110 *) ReadDeleted = 0CH; (* 01100 *) WriteData = 05H; (* 00101 *) WriteDeleted = 09H; (* 01001 *) ReadATrack = 02H; (* 00010 *) ReadId = 0AH; (* 01010 *) FormatATrack = 0DH; (* 01101 *) ScanEqual = 11H; (* 10001 *) ScanLowerEqual = 19H; (* 11001 *) ScanHighEqual = 1DH; (* 11101 *) Recalibrate = 07H; (* 00111 *) SenseInterruptStatus = 08H; (* 01000 *) Specify = 03H; (* 00011 *) SenseDriveStatus = 04H; (* 00100 *) Seek = 0FH; (* 01111 *) END NEC765; (* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *) MODULE DiskAdapter; (* * This is board designer stuff, and the nec765 module (should be| * is purely) applicable to the NEC765 and has no board designer * modifications in it. * * I read the schematics in the IBM Technical Reference, * pn 6025008, Revised Edition, July 1982. The various values * agree with that schematic, the NEC 765 Data sheet, and floppy.c * * This is not intended to be complete enough for a separate * module in a library, but just 'nuf to get the code out the door. * *) IMPORT SYSTEM; EXPORT STATUSport, DATAport, RATE, seekStatus, MotorOn, MotorOff; CONST (* IBM-PC magic I/O addresses. * Resolved with floppy.c, BIOS, and schematic. *) DOR = 03F2H; STATUSport= 03F4H; DATAport = 03F5H; RATE = 03F7H; (* not on XT *) ENABLEINT = 0CH; MOTORMASK = 0F0H; TYPE ControlDOR = ( DriveSelect0, DriveSelect1, RST765, DMA2Enable, Motor1, Motor2, Motor3, Motor4 , res0, res1, res2, res3, (*A packed byte*) res4, res5, res6, res7 ); DORBits = SET OF ControlDOR; VAR (*ROM bios accomodation*) seekStatus [ 0040H:003EH ] : BITSET; status [ 0040H:003FH ] : DORBits; BitImage [ 0040H:003FH ] : SYSTEM.BYTE; PROCEDURE MotorOn; BEGIN status := status + DORBits { Motor1 } - DORBits { Motor2, Motor3, Motor4} (*Hard coded for drive 0*) - DORBits { DriveSelect0, DriveSelect1 } (*Set watchdog timer count at 40:40*) + DORBits { res0..res7 }; SYSTEM.OUTBYTE( DOR, BitImage ) END MotorOn; PROCEDURE MotorOff; BEGIN status := status - DORBits { DriveSelect0, DriveSelect1 } - DORBits { Motor1 .. Motor4 }; SYSTEM.OUTBYTE( DOR, status ) END MotorOff; BEGIN (* (*Hmm, if the machine booted, the controller has been *initialized ok! *) status := DORBits{ DMA2Enable }; SYSTEM.OUTBYTE( DOR, status ) INCL( status, RST765 ); *) status := DORBits{ RST765 , DMA2Enable }; SYSTEM.OUTBYTE( DOR, status ); END DiskAdapter; (* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *) MODULE OtherSysStuff; (*These are suggestions for other goodies in the library. *) IMPORT SYSTEM; EXPORT panic, Relinquish, SetupDMA; PROCEDURE panic( msg : ARRAY OF CHAR); BEGIN (* I set a breakpoint in the RTD. * Again, this is prototype activity. *) END panic; PROCEDURE Relinquish( time : CARDINAL ); (* On a blindingly fast machine, this would do a call * into the scheduler or message system, and let other * work be done while 12 micro seconds crawled by. *) CONST usec = 5; (* I don't know a correct value. *) overhead = 0 * usec; (* This'un either *) VAR ix : CARDINAL; BEGIN FOR ix := overhead TO time * usec DO (*nothing*) (*Can You say optimizer interference?*) END; END Relinquish; PROCEDURE SetupDMA( dmaout : BOOLEAN; VAR buffer : ARRAY OF SYSTEM.WORD); (*nb ************************************************** * * WARNING: ESCAPED HACKER IN YOUR VICINITY. * RESIDENTS ARE URGED TO ADOPT EXTREEM CAUTION. * HE IS REPORTED TO EXHIBIT CUTE CODE WITHOUT PROVOCATION. * ARTIFACTS MAY BECOME DANGEROUS WHEN MOVED TO NEW ENVIRONMENTS. * ************************************************** * Not Portable. Uses knowledge of compiler output. * Be wary of optimizers. * PROC is almost guaranteed to be the wrong size. * Be careful if you move variables, * or even change compiler switches. :-) ************************************************** * I guess folks do this all the time in C without warnings, and * call it 'close to the machine' huh? *) TYPE ParamStackPtr = POINTER TO ParamStack; ParamStack = RECORD itself : ParamStackPtr; BP : CARDINAL; return : PROC; Offset : CARDINAL; Segment: CARDINAL; Size : CARDINAL END; VAR top : CARDINAL; aux : CARDINAL; count: CARDINAL; hook : ParamStackPtr; CONST DMAWRITE = 04AH; DMAREAD = 046H; DMAStatus = 00BH; DMATOP = 081H; DMAADDR = 004H; DMACOUNT = 005H; DMAINIT = 00AH; BEGIN hook := SYSTEM.ADR( hook ); WITH hook^ DO IF Size # HIGH( buffer ) THEN panic( 'Stack structure error in SetupDMA.' ) END; aux := (Offset DIV 16) + Segment; top := aux DIV 1000H; Offset := (Offset MOD 16) + 16 * (aux MOD 1000H); IF 0FFFFH - SYSTEM.SIZE(buffer) <= Offset THEN panic ( "Can't set up DMA." ) END; IF dmaout (*For some reason Minix sends the status command to *two DMA channels. So does the ROM bios listing. *I don't comprehend why. I don't do it. The code works. * *What if I'm using DMA for some other device also? *) THEN SYSTEM.OUTBYTE( DMAStatus, DMAWRITE ) ELSE SYSTEM.OUTBYTE( DMAStatus, DMAREAD ) END; SYSTEM.OUTBYTE( DMAADDR, Offset MOD 256 ); SYSTEM.OUTBYTE( DMAADDR, Offset DIV 256 ); SYSTEM.OUTBYTE( DMATOP, top ); count := ((1 + HIGH( buffer )) * 2) - 1; (* Byte Count - 1 *) SYSTEM.OUTBYTE( DMACOUNT, count MOD 256 ); SYSTEM.OUTBYTE( DMACOUNT, count DIV 256 ); SYSTEM.OUTBYTE( DMAINIT, 2 ) END END SetupDMA; END OtherSysStuff; (* *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *) CONST (* Sectors = 5; Bytes = 3 (* 512 -- Note: this is ln2(size)-7 NOT size/128*); *) Sectors = 9; Bytes = 2 (* 512 -- Note: this is ln2(size)-7 NOT size/128*); Tracks = 80; Sides = 2; Out = TRUE; VAR track : ARRAY [ 0 .. 9 * 512-1 ] OF CARDINAL; continue : BOOLEAN; tracks : CARDINAL; sectors : CARDINAL; sides : CARDINAL; CHRN : (*This is byte packed -- 4 bytes long*) ARRAY [ 0..Sectors-1 ] OF RECORD CH, RN : CARDINAL END; hackTrack : PROCEDURE( BOOLEAN, VAR ARRAY OF SYSTEM.WORD ); results : SR0Bits; PCN : CARDINAL; BEGIN hackTrack := SetupDMA; (* ... defeat a compiler optimisation...*) WITH CHRN[0] DO CH := 0; RN := Bytes * 100H END; FOR sectors := 0 TO Sectors - 1 DO CHRN[ sectors ] := CHRN [0]; CHRN[ sectors ].RN := (sectors + 1) + 100H * Bytes END; continue := TRUE; (* I've got an RTD, 'continue' changes by magic; this is prototyping! *) WHILE continue DO MotorOn; DoRecalibrate; FOR tracks := 0 TO Tracks - 1 DO MotorOn; FOR sides := 0 TO Sides - 1 DO DoSeek( sides, tracks ); FOR sectors := 0 TO Sectors - 1 DO CHRN[ sectors ].CH := tracks + 100H * sides END; REPEAT (*wait*) UNTIL 7 IN seekStatus; EXCL( seekStatus, 7 ); DoSenseInterruptStatus( results, PCN ); SetupDMA( Out, CHRN ); DoFormatATrack( sides, Bytes, Sectors ) END END; continue := FALSE (*A breakpoint hook.*) END; MotorOff END Format0. -- Aubrey McIntosh Freelance using Modula-2 Real time, embedded, instruments. Austin, TX 78723 Enquiries welcome 1-(512)-452-1540 aubrey%rpp386.Cactus.org@cs.utexas.edu