program dumpdisk; imports Dynamic from Dynamic; imports io from io; imports ioerrors from ioerrors; imports rs232baud from rs232baud; imports system from system; imports FileSystem from FileSystem; imports IOErrMessages from IOErrMessages; { This is a dead simple program to do a raw disk dump of the hard drive over the RS232 port. It's also my first Pascal program as well as my first PERQ program, so it's not great. But it seems to work ok... This is currently hard-coded for a 24mb Shugart 14" drive, but it should be adaptable to any other drive by tweaking some parameters. This program reads the raw data in the following format, on a sector by sector basis (from cyl 0, head 0, sector 0 on out) and dumps it over RS232 port A at 9600 baud. - 8 words of header (16 bytes) - 256 words of data (512 bytes) The 8 header words are returned from IODiagRead and according to the documentation these represent physical data on the disk; I'm not entirely sure it's necessary to save, but i erred on the side of saving more data rather than less. Bad sectors are retried up to 10 times; if that fails the data sent over the RS232 port will be 8 words of header (which may be bogus) and 512 bytes of '0101010101'. Yes, this is rather lame. J. Dersch, 8/14/06. } type CharArray = array [0..512] of char; ChrArrayPtr = ^CharArray; var i : integer; c : char; buf : IOBufPtr; Stat : DevStatusBlock; addr : double; header : IOHeadPtr; status : IOStatPtr; Head : integer; Sector : integer; Cylinder : integer; CylStart : integer; CylEnd : integer; seg : SegmentNumber; success : boolean; RetryCount : integer; { Writes a char to the RS232 port } procedure WriteChar( c : char ); begin if iocwrite(RS232Out, c) <> IOEIOC then writeln( ' -- error writing to serial port -- ' ); end; { Writes an integer to the RS232 port } procedure WriteInt( i : integer ); var high : char; low : char; begin high := chr(shift(i,-8)); low := chr(land(i,255)); WriteChar( low ); WriteChar( high ); end; { Writes a double to the RS232 port } procedure WriteDbl( d : double ); begin WriteInt( d[0] ); WriteInt( d[1] ); end; { Start of the main program } begin writeln ('Start at what cylinder? '); readln ( CylStart ); writeln ('End at what cylinder? (Max 201)'); readln ( CylEnd ); writeln ('Here we go...'); { Configure the RS232 port } with Stat do begin ByteCnt := 1; RSRcvEnable := false; end; IOPutStatus( RS232In, Stat ); SetBaud('9600', true); writeln ('Set RS232 port to 9600 baud...'); writeln ('Allocating memory...'); { Allocate memory for status and header } NEW(0,4,status); NEW(0,4,header); { Allocate a segment for the disk data buffer } CreateSegment( seg, 1, 1, 1 ); buf := MakePtr(seg,0, IOBufPtr); { Loop over the disk from the start cylinder to the end } for Cylinder := CylStart to CylEnd do begin for Head := 0 to 7 do begin for Sector := 0 to 29 do begin addr[0] := Cylinder * 256 + Head*32 + Sector; writeln( 'reading cylinder ', cylinder, ' head ', head, ' sector ', sector ); success := false; RetryCount := 0; { Read the block from the disk; retry 10 times on a read error } while (not success) and (RetryCount < 10) do begin { Read 512 bytes from the disk. IODiagRead reads the block and fills in the header info for us. } UnitIO( HardDisk, buf, IODiagRead, 512, addr, header, status ); {writeln( 'serial ', header^.SerialNum[0], header^.SerialNum[1], ' logblock ', header^.LogBlock, ' filler ', header^.Filler, ' NextAdr ', header^.NextAdr[0], header^.NextAdr[1], ' PrevAdr ', header^.PrevAdr[0], header^.PrevAdr[1] );} if status^.SoftStatus = IOEIOC then success := true; {Yes, this is stupid, got fed up trying to get 'else' to compile without errors. Grumble.} if status^.SoftStatus <> IOEIOC then begin writeln('error ', status^.SoftStatus, ' while reading disk.'); RetryCount := RetryCount + 1; end; end; { We didn't read the block, the data in the block will be filled with binary '01010...' } if (not success) then begin writeln('retries unsuccessful, data is unusable for this sector'); for i := 0 to 255 do begin {$R-} buf^[i] := 21845; {$R+} end; end; { Send header block } WriteDbl( header^.SerialNum ); WriteInt( header^.LogBlock ); WriteInt( header^.Filler ); WriteDbl( header^.NextAdr ); WriteDbl( header^.PrevAdr ); { Send data block } for i := 0 to 255 do begin {$R-} WriteInt( buf^[i] ); {$R+} {write( low, high ); } end; end; end; end; end.