Link to home
Start Free TrialLog in
Avatar of aktush
aktush

asked on

Getting HDD geometry information../ writing to a sector in C

[Ok I will be breef, I have not posted this article in C - Languages because it might not be possible to use pure C for this task, I have not placed it in ASM topics for the same reason.]

I need to write a customized software to override each sector on the hdd that uses 13h interrupt (aka IDE drives).

My question is:

1) How do I detect the drive geometry to calculate how many sectors and blocks there are to feed it to main loop that actually does erasure (writes to each sector).

2) Is it possible [and what functions] to use C to write to sector or do I have to resort to int 13h and use software interrupts for that matter. [This question would be then asked in ASM forum].

3) I am trying to make this software UNIX compatible what kinds of problems can I run into.

P.S

I know that Linux uses int80 does BSD or other UNIcies use the same? int80?

Thank you all for reading / answering.!
Avatar of makc
makc

1) There was 13th int subfunction for that, for sure. But... considering low points amount I will not bother to look for it :P My only suggestion is to loop-untill-no-error-encountered (coz BIOS will not let you to write to unexistent head/track/sector/whatever)

2) Sure they are. If you have proper C library :) If not, do ask where you were about to ask.

3) That depends upon answers you'll accept on 1) and 2)

P.S.> I hope I didn't helped you too much, did I ?
Avatar of aktush

ASKER

Thank you for you kindness I am upgrading points as soon as I get more, but surely you can not just answer for points where is your comaradory? :)

Upping to 25 points value when I will get more I will use more.
Here's a snippet from something I did a while back

// Get Disk parameters
InRegs.h.ah=0x08;
InRegs.h.dl=0x80;
_int86(0x13,&InRegs,&OutRegs);
NumDisks=OutRegs.h.dl;

for (DiskNo=0; DiskNo<NumDisks; DiskNo++)
    {
    Cylinders[DiskNo]=OutRegs.h.ch;
    TempInt=OutRegs.h.cl;
    TempInt=4*(TempInt & 0xc0);
    Cylinders[DiskNo]+=TempInt;
    Sectors[DiskNo]=OutRegs.h.cl & 0x3f;
    Heads[DiskNo]=OutRegs.h.dh;
    .....
    }



For a complete description of all interrupts in a standard Intel based BIOS, see Ralf Brown's list:

http://www-2.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html

Avatar of aktush

ASKER

Is this a code for Linux? Or for DOS implementation?
Cause you use here _int86 which is a dos routine and a x80 int which is Linux interrupt vector.

I believe also the type of the InRegs is REGS ? and you have to include <dos.h> ? or include <int86.h> something like that.
Avatar of aktush

ASKER

Yes I know the ralf brown interrupt list but can you use the interupts in C? like this ? In DOS/Windows ? or In linux? WIll the software be cross platform compatible if the hardware is x86?
That was using 16-bit Microsoft C, but the method would still be the same for whichever compiler you use.  Regardless of OS, as long as you have a standard BIOS and an Intel-like chip at or above a 386, that int13 function will get you the drive parameters in the specified registers.

Now, if your OS doesn't let you use those interrupts (e.g., Windows later than 9x), you'll have a problem.  However, in those cases (NT, 2K, XP), there are API calls that will get you what you want.  

Alas, I don't know about Linux.

Damn bifocals - I missed the part about writing.



Here's a larger chunk that contains what I posted above.  For writing to the high end of large drives, you have to use the int13 extensions.  These started showing up in BIOSes in the mid 90's.  This chunk shows the check for int13 extensions and the fetching of extended drive parameters.



// Get Disk parameters
InRegs.h.ah=0x08;
InRegs.h.dl=0x80;
_int86(0x13,&InRegs,&OutRegs);
NumDisks=OutRegs.h.dl;
if (NumDisks>9)
   {
   printf("Too many drives\n");
   exit(0);
   }

for (DiskNo=0; DiskNo<NumDisks; DiskNo++)
    {
    Cylinders[DiskNo]=OutRegs.h.ch;
    TempInt=OutRegs.h.cl;
    TempInt=4*(TempInt & 0xc0);
    Cylinders[DiskNo]+=TempInt;
    Sectors[DiskNo]=OutRegs.h.cl & 0x3f;
    Heads[DiskNo]=OutRegs.h.dh;
    SizesM[DiskNo]=((unsigned long)(1+Cylinders[DiskNo])) // +1?
                  *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                  *((unsigned long)(1+Heads[DiskNo]))     // +1?
                  /2L;
    SizesM[DiskNo]=SizesM[DiskNo]/1024L;
    Zapped[DiskNo]='N';

    //
    // Load partition table for this drive
    InRegs.h.ah=0x02; // Read
    InRegs.h.al=1;    // Num sectors
    InRegs.h.ch=0;    // Cylinder 0
    InRegs.h.cl=1;    // Sect 1
    InRegs.h.dh=0;    // Head 0
    InRegs.h.dl=0x80+(unsigned char) DiskNo;
    TempAddr=&SectorBuf[0];
    _segread(&SegRegs);
    SegRegs.es=_FP_SEG(TempAddr);
    InRegs.x.bx=_FP_OFF(TempAddr);
    _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
    for (TempIdx=0; TempIdx<64; TempIdx++)
        {
        Partitions[DiskNo][TempIdx]=SectorBuf[TempIdx+512-66];
        }
    // Check for int13 extensions installed
    InRegs.h.ah=0x41;
    InRegs.x.bx=0x55aa;
    InRegs.h.dl=0x80;
    _int86(0x13,&InRegs,&OutRegs);
    Int13Ext[DiskNo] = !OutRegs.x.cflag; // cflag set => int13 extensions not present
    if (Int13Ext[DiskNo])
       {
       // get extended drive parms
       InRegs.h.ah=0x48;
       InRegs.h.dl=0x80+DiskNo;
       ExtParmBuf[0]=0x42;
       ExtParmBuf[1]=0;
       TempAddr=&ExtParmBuf[0];
       _segread(&SegRegs);
       SegRegs.ds=_FP_SEG(TempAddr);
       InRegs.x.si=_FP_OFF(TempAddr);
       _int86(0x13,&InRegs,&OutRegs);
       // Save the extended info over the old stuff
       SectorSize[DiskNo]=ExtParmBuf[0x18]+256*((unsigned int)ExtParmBuf[0x19]);
       for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x08+TempIdx];
       Heads[DiskNo]=ldef.uulong;
       for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x0c+TempIdx];
       Sectors[DiskNo]=ldef.uulong;
       for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x04+TempIdx];
       Cylinders[DiskNo]=ldef.uulong;
       SizesM[DiskNo]=((unsigned long)(1+Cylinders[DiskNo])) // +1?
                     *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                     *((unsigned long)(1+Heads[DiskNo]))     // +1?
                     /2L;
       SizesM[DiskNo]=SizesM[DiskNo]/1024L;
       }
    //
    // Get next Disk parameters
    InRegs.h.ah=0x08;
    InRegs.h.dl=0x81+DiskNo;
    _int86(0x13,&InRegs,&OutRegs);
    }





How you write depends on whether you're writing to an older or newer drive/BIOS:
I've cut out a lot of un-related code

union  _REGS  InRegs, OutRegs;
struct _SREGS SegRegs;

long          Int32;
short         Int16;
struct Def64  {
              unsigned long Low32;
              unsigned long High32;
              };
struct Def64 Int64;

union {
      unsigned long uulong;
      unsigned char ucchar[4];
      } ldef;

unsigned int  DiskNo;            // Current disk #
unsigned int  NumDisks;          // Number of disks
unsigned char SectorBuf[512];    // Place to load a disk's sector 1 into
unsigned long Cylinders[9];      // High cylinder for each disk
unsigned long Heads[9];          // High head for each disk
unsigned long Sectors[9];        // High sector for each disk
unsigned int  SectorSize[9];     // int 13 ext - sector size
struct Def64  SectorsOnDisk[9];  // int 13 ext
unsigned long SizesM[9];         // Size in MB for each disk
unsigned char Zapped[9];         // Tells us if the disk has been zapped yet
unsigned char Partitions[9][64]; // Partition tables
unsigned char Int13Ext[9];       // int13 extensions?
unsigned char Pattern1[512];     // All zeros
unsigned char Pattern2[8192];    // All ones
unsigned char Pattern3[512];     // All zero one
unsigned char Pattern4[512];     // All one zero
unsigned char ExtParmBuf[0x42];
int           Removable[9];      // int 13 ext - removable drive

void     far *TempAddr;  // Place to get SEG and OFFs through
unsigned int  CylIdx;    // Just some subscripts
unsigned int  SecIdx;
unsigned int  HeadIdx;


unsigned int  HiWord;
unsigned int  LoWord;
unsigned long PartSize;

struct DefInt   {int  IntDef;};
struct DefBytes {unsigned char LoByte;
                 unsigned char HiByte;};
union  DefMap   {struct DefInt   DefWord;
                 struct DefBytes DefChars;};
union  DefMap   WordDef;



// Extended write packet
struct {
       unsigned char  PktSize;
       unsigned char  Reserved;
       unsigned short BlockCount;
       unsigned short OffBuff;
       unsigned short SegBuff;
       struct Def64   AbsBlockNum;
       } ExtPkt;
unsigned long BlockNum;      


//
// Drain keyboard
while (_kbhit()) getch();

BrokeOut=0;
time(&StartTime);
BlockNum=0;
for (CylIdx=0; CylIdx<Cylinders[DiskToWipe]; CylIdx++)
    {
    if (_kbhit()) {BrokeOut=1; break;}
    for (HeadIdx=0; HeadIdx<Heads[DiskToWipe]; HeadIdx++)
        {
        if (_kbhit()) {BrokeOut=1; break;}
        for (SecIdx=1; SecIdx<=Sectors[DiskToWipe]; SecIdx+=16)
            {
            if (_kbhit()) {BrokeOut=1; break;}
            if (Int13Ext[DiskToWipe])
               {
               // Use extended write
               ExtPkt.PktSize=0x10;
               ExtPkt.Reserved=0;
               ExtPkt.BlockCount=16;
               TempAddr=&Pattern2[0];
               ExtPkt.SegBuff=_FP_SEG(TempAddr);
               ExtPkt.OffBuff=_FP_OFF(TempAddr);
               ExtPkt.AbsBlockNum.High32=0;
               ExtPkt.AbsBlockNum.Low32=BlockNum;
               InRegs.h.ah=0x43;
               InRegs.h.al=0;
               InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
               TempAddr=&ExtPkt;
               _segread(&SegRegs);
               SegRegs.ds=_FP_SEG(TempAddr);
               InRegs.x.si=_FP_OFF(TempAddr);
               _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
               if (OutRegs.x.cflag)
                  {
                  printf("\nI/O error (%02x) at block (%08lx).  (%08lx) transferred\n",OutRegs.h.ah,BlockNum,ExtPkt.AbsBlockNum.Low32);
                  exit(1);
                  }
               BlockNum+=16;
               }
            else
               {
               // Use old fasioned write
               InRegs.h.ah=0x03;                     // Write function
               InRegs.h.al=16;                       // Number of sectors
               WordDef.DefWord.IntDef=CylIdx;        // CH gets low 8 bits of cylinder
               InRegs.h.ch=WordDef.DefChars.LoByte;  // CL gets sector and top 2 bits of cyl
               InRegs.h.cl=(WordDef.DefChars.HiByte<<6)+(unsigned char)SecIdx;
               InRegs.h.dh=HeadIdx;                  // Head
               InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
               TempAddr=&Pattern2[0];
               _segread(&SegRegs);
               SegRegs.es=_FP_SEG(TempAddr);
               InRegs.x.bx=_FP_OFF(TempAddr);
               _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
               }
            }
  ..... unrelated stuff snipped out
        }
    }
Avatar of aktush

ASKER

I will try to compile when I come home today thanx! I hope either VC7.1++ or MingW 3.0 will compile it.
Avatar of aktush

ASKER

I understand the jist of it but I need to compile this section after modifying to see HOW exactly it works and to step in the debugger etc.. but I have a good feeling it will do the job

P.S

I am not trying to run it form windows its designed to be run under DOS or something like DOS eg FreeDOS ... So no protected modes..
You'll need a 16-bit compiler - something that generates real mode code.
Avatar of aktush

ASKER

Such as? Borland C++ 2.01?
DJGPP>?
Watcom C?
Digital Mars?
Micro C?
Pacific C?

Which one will do the trick of compiling real mode ? exe's I mean can you simulate 16-bit mode in 32-bit mode ?
And why can not you run 32bit code in real mode? under DOS , I though it would be perfectly fine?
Avatar of aktush

ASKER

And also does this page provides what I need?

http://www.mirzoyan.com/lib/
32-bit code implies an address space that excedes real mode.  Moreover, the BIOS presumes real mode.

As far as libraries are concerned, all you'll need are those to support:

1) Any stdio you may want to do
2) Register defs


Don't know about Borland
DJGPP ought to  (you can probably find a gcc for DOS somewhere)
Watcom don't know
Digital Mars looks like it does
Micro C would, but you have to diddle with structs and unions to get longs et al for extended mode
Pacific C looks like it will work


Avatar of aktush

ASKER

I think Digital Mars is good for me I looked at the specs it looks like it is ok look:

Digital Mars C/C++ Compiler Version 8.34 (2,966,000 bytes) (NEW!)
     Contains:
          Win32 console versions of the tools
          C and C++ compilers
          Optlink Linker
          Librarian
          Standard and Win32 header files
          Runtime linkable libraries
          Does not come with a debugger, but works with the Symantec or Microsoft debuggers
          SGI's Standard Template Library 3.3
          MicroEmacs for Win32
          Works with STLport 4.5.3
     DOS 16 libraries (646,000 bytes)
     Add on to above compiler to enable development of 16 bit DOS programs.
     Contains:
          Runtime linkable libraries for 16 bit DOS
          .COM generation tools
          MicroEmacs for DOS


Especially the latter part that says 16-bit DOS development is this it? is this what I want? Is this that will allow me to include <dos.h> and use union _REGS InRegs, OutRegs?
ASKER CERTIFIED SOLUTION
Avatar of cookre
cookre
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of aktush

ASKER

I see what you are saying ... yes I have been working in MASM for quite number of years and shifting and putting offsets into segments etc.. so this is all have been done in 32bit mode ... then oohh ohh i see .... I see now but you can only issue 13h or whichever int's to access physical devices etc.. from the BIOS only from the real mode... I see...

Well thank you all guys for help especially the cookre !! you are the best now I know a little more about this topic since I am relatively young still in college :) (computer science major) and have not been programming since 197x but its great to get some feedback from cookre!
Avatar of aktush

ASKER

I was not able to write to sectors using this method althought it compiled there is nothing that happened I edited a code a little to see what effect will it give:

This is my code:

#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <time.h>


void disk_format();


int main ( void )
{
  disk_format();
  return 0;
}


void disk_format()
{

  union  _REGS  InRegs, OutRegs;
  struct _SREGS SegRegs;

  long          Int32;
  short         Int16;

  struct Def64
  {
    unsigned long Low32;
    unsigned long High32;
  };


  struct Def64 Int64;

  union
  {
     unsigned long uulong;
     unsigned char ucchar[4];
  }  ldef;


  unsigned int  DiskNo;            // Current disk #
  unsigned int  NumDisks;          // Number of disks
  unsigned char SectorBuf[512];    // Place to load a disk's sector 1 into
  unsigned long Cylinders[9];      // High cylinder for each disk
  unsigned long Heads[9];          // High head for each disk
  unsigned long Sectors[9];        // High sector for each disk
  unsigned int  SectorSize[9];     // int 13 ext - sector size
  struct Def64  SectorsOnDisk[9];  // int 13 ext
  unsigned long SizesM[9];         // Size in MB for each disk
  unsigned char Zapped[9];         // Tells us if the disk has been zapped yet
  unsigned char Partitions[9][64]; // Partition tables
  unsigned char Int13Ext[9];       // int13 extensions?
  unsigned char Pattern1[512];     // All zeros
  unsigned char Pattern2[8192];    // All ones
  unsigned char Pattern3[512];     // All zero one
  unsigned char Pattern4[512];     // All one zero
  unsigned char ExtParmBuf[0x42];
  int           Removable[9];      // int 13 ext - removable drive



  void     far *TempAddr;  // Place to get SEG and OFFs through
  unsigned int  CylIdx;    // Just some subscripts
  unsigned int  SecIdx;
  unsigned int  HeadIdx;


  unsigned int  HiWord;
  unsigned int  LoWord;
  unsigned long PartSize;

 
  struct DefInt
  {
    int  IntDef;
  };

  struct DefBytes
  {
    unsigned char LoByte;
    unsigned char HiByte;
  };

  union  DefMap
  {
    struct DefInt   DefWord;
    struct DefBytes DefChars;
  };


  union  DefMap   WordDef;


  // Extended write packet
  struct
  {
    unsigned char  PktSize;
    unsigned char  Reserved;
    unsigned short BlockCount;
    unsigned short OffBuff;
    unsigned short SegBuff;
    struct Def64   AbsBlockNum;
  } ExtPkt;


  unsigned long BlockNum;      
  time_t StartTime;
  int BrokeOut=0;
  int DiskToWipe = 0;

  // Drain keyboard
  while (_kbhit())
         getch();


 
  time (&StartTime);
  BlockNum=0;

  for (int i = 0; i < 9; i++, DiskToWipe++)
  for (CylIdx=0; CylIdx < Cylinders[DiskToWipe]; CylIdx++)
  {
    if (_kbhit()) {BrokeOut=1; break;}

    for (HeadIdx=0; HeadIdx<Heads[DiskToWipe]; HeadIdx++)
    {
       if (_kbhit()) {BrokeOut=1; break;}
       
       for (SecIdx=1; SecIdx<=Sectors[DiskToWipe]; SecIdx+=16)
       {
           if (_kbhit()) {BrokeOut=1; break;}

           if (Int13Ext[DiskToWipe])
           {
              // Use extended write
              ExtPkt.PktSize=0x10;
              ExtPkt.Reserved=0;
              ExtPkt.BlockCount=16;
              TempAddr=&Pattern2[0];
              ExtPkt.SegBuff=_FP_SEG(TempAddr);
              ExtPkt.OffBuff=_FP_OFF(TempAddr);
              ExtPkt.AbsBlockNum.High32=0;
              ExtPkt.AbsBlockNum.Low32=BlockNum;
              InRegs.h.ah=0x43;
              InRegs.h.al=0;
              InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
              TempAddr=&ExtPkt;
              _segread(&SegRegs);
              SegRegs.ds=_FP_SEG(TempAddr);
              InRegs.x.si=_FP_OFF(TempAddr);
              _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
             
              if (OutRegs.x.cflag)
              {
                 printf("\nI/O error (%02x) at block (%08lx).  (%08lx)transferred\n",OutRegs.h.ah,BlockNum,ExtPkt.AbsBlockNum.Low32);
                 exit(1);
              }
              BlockNum+=16;
           }
           else
           {
             // Use old fasioned write
           
             InRegs.h.ah=0x03;                                                   // Write function
             InRegs.h.al=16;                                                     // Number of sectors
             WordDef.DefWord.IntDef=CylIdx;                                      // CH gets low 8 bits of cylinder
             InRegs.h.ch=WordDef.DefChars.LoByte;                                // CL gets sector and top 2 bits of cyl
             InRegs.h.cl=(WordDef.DefChars.HiByte<<6)+(unsigned char)SecIdx;
             InRegs.h.dh=HeadIdx;                                                // Head
             InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
             TempAddr=&Pattern2[0];
             _segread(&SegRegs);
             SegRegs.es=_FP_SEG(TempAddr);
             InRegs.x.bx=_FP_OFF(TempAddr);
             _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
           }
         }
      }
   }
}
Avatar of aktush

ASKER

I get the code I studyed it before actually using it makes sence, you write 16 sectors at a time and then increment it but nothing seems to change the partition table it there, the data is there ... do you have any suggestions?
If that's all of the code, then, nope, it won't do much of anythying.  At least it was probably quite fast.

You copied over the code to do the writes, but left out the code to determine the drive geometry and to check for int13 extensions.

It's probably just as well.  If you had other physical drive in the box you're testing on, it would have wiped them, too.

Avatar of aktush

ASKER

there was a cracking of the hdd i have heared it but nothing has been done to the drive it was very fast like 1 sec.
doh! I need to of course give it the sectors and heads and etc...

I have a Toshiba 440CDX Laptop with 1.3G drive and a floppy.

I booted into dos, and run the program, that was the idea.

I will add geometry info and try it.
Avatar of aktush

ASKER

By the way the:

unsigned char Pattern1[512];     // All zeros
unsigned char Pattern2[8192];    // All ones
unsigned char Pattern3[512];     // All zero one

arrays are not filled in ? how can they be anything? like 1s and 0s?
Or this is a directive for  future filling?
Avatar of aktush

ASKER

Yessss!!! I think I got all the information into the temp vars and its working!!!!!!!!!!!!!!!! Thank you! I have not checked it yet with HEX viewer but !! it takes 7- 10 min to erase drive 1.3 G and ! the drive cracks!
This code was part of a program I wrote to wipe hard drives. What I left out here was all of the screen displays showing the various drives in a given box, their associated geometry, partition info and wipe progress.

The purpose of the patterns was to perform a more secure wipe according to certain government regulations.  Because of the ultimate disposition of the drives we would wipe, the requirement for the multi-pattern wipe was waived, thereby saving considerable time, considering that thousands of drive were/are wipe targets.
Avatar of aktush

ASKER

Ok I see, thats ectually all I need to display information about the cylinders and sectors on the screen nothing fancy just simple stuff but printf() gave up on me for some reason it works in the main() but stops working in the loop like those for loops when i do
printf("%s%i", "Cylinders: ", CylIndx);

but i dont get any responce at all, the getch works so thats good but the printf ...

I was thinking using 10h and for displaying the cylinders and sectors (maybe some more info) but that would be the last resort... You have any suggestions?
Avatar of aktush

ASKER

Also I have done step by step in code view debugging and I understand more about the program now it essentially the same as I would do in MASM you initialize segments and push values to the registers and execute interrupts but in a weired way a little. I have created a fat32 partition loaded with data and launched the software overriden it. it worked fine.
Avatar of aktush

ASKER

>>The purpose of the patterns was to perform a more secure wipe according to certain government regulations.  Because of >>the ultimate disposition of the drives we would wipe, the requirement for the multi-pattern wipe was waived, thereby >>saving considerable time, considering that thousands of drive were/are wipe targets

I see so thats exactly what i needed i will initialize the arrays to what i need and write 0000's 1111's and random numbers.

But I need to have some kind of a display mechanism that would be cool.
I'll post all the code tomorrow night.
Avatar of aktush

ASKER

Ok great, I will look at it but will try to make my own attempts at solving problem. Thanks!
Avatar of aktush

ASKER

I dont want to just copy code from you I want to understand kind of that was the purpose not only to make a working program but to have me contribute something also..
Here's the whole thing so you can see how displays are done.  You'll also note it writes a signature sector when it's done.  If the profram is run against an already wiped drive with that signature, the display says as much so the user need not spend the time wiping it again.

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <conio.h>
#include <graph.h>

union  _REGS  InRegs, OutRegs;
struct _SREGS SegRegs;

long          Int32;
short         Int16;
struct Def64  {
              unsigned long Low32;
              unsigned long High32;
              };
struct Def64 Int64;

union {
      unsigned long uulong;
      unsigned char ucchar[4];
      } ldef;

unsigned int  DiskNo;            // Current disk #
unsigned int  NumDisks;          // Number of disks
unsigned char SectorBuf[512];    // Place to load a disk's sector 1 into
unsigned long Cylinders[9];      // High cylinder for each disk
unsigned long Heads[9];          // High head for each disk
unsigned long Sectors[9];        // High sector for each disk
unsigned int  SectorSize[9];     // int 13 ext - sector size
struct Def64  SectorsOnDisk[9];  // int 13 ext
unsigned long SizesM[9];         // Size in MB for each disk
unsigned char Zapped[9];         // Tells us if the disk has been zapped yet
unsigned char Partitions[9][64]; // Partition tables
unsigned char Int13Ext[9];       // int13 extensions?
unsigned char Pattern1[512];     // All zeros
unsigned char Pattern2[8192];    // All ones
unsigned char Pattern3[512];     // All zero one
unsigned char Pattern4[512];     // All one zero
unsigned char SigSec[512];
unsigned char ReadSig[512];
unsigned char ExtParmBuf[0x42];
int           Removable[9];      // int 13 ext - removable drive

void     far *TempAddr;  // Place to get SEG and OFFs through
unsigned int  CylIdx;    // Just some subscripts
unsigned int  SecIdx;
unsigned int  HeadIdx;

int           TempIdx;      // Hmm?
unsigned int  TempInt;      // Guess
char          TempStr[128]; // Damfino
unsigned int  CharIn;       // Keystroke

unsigned int  HiWord;
unsigned int  LoWord;
unsigned long PartSize;

int           DidPtn;     // Tells us if a partition's info has been displayed
int           Quit;       // Just a quit flag
int           DiskToWipe; // Which disk is getting zapped
int           LineCtr;    // Used for _settextposition

unsigned char MiddleLine[80]; // Place for a graphics line
short         MainTextColor;  // Color we usually display in

struct DefInt   {int  IntDef;};
struct DefBytes {unsigned char LoByte;
                 unsigned char HiByte;};
union  DefMap   {struct DefInt   DefWord;
                 struct DefBytes DefChars;};
union  DefMap   WordDef;

int BrokeOut; // Tells us if the zap code was terminated prematurely

long   CylHeadTot;
long   CylHeadDone;
float  PctComplete;
time_t StartTime;
time_t CurrentTime;
long   ElapsedSeconds;
long   PrevElapsed;
long   EstTotSecs;
long   TotMin;
long   RemMin;
long   RemSec;

// Extended write packet
struct {
       unsigned char  PktSize;
       unsigned char  Reserved;
       unsigned short BlockCount;
       unsigned short OffBuff;
       unsigned short SegBuff;
       struct Def64   AbsBlockNum;
       } ExtPkt;
unsigned long BlockIdx;


// (DrawBox 'borrowed' from FS_FLIMG )
//                                                                    DrawBox
/////////////////////////////////////////////////////////////////////////////
// TLCX,Y - Column and row of TLC (0,0 is top left of screen)
// Width & Height are exterior dimensions
// Color of foreground
// Style - 0->single, ~0->double
// Fill - 0->no fill, ~0->fill interior
void DrawBox(TLCX,TLCY,Width,Height,Color,Style,Fill)
int          TLCX,TLCY,Width,Height,Color,Style,Fill;
{
char BoxChars[7];
int  idx;
char LineBuf[81];

if (Style==0) strcpy(BoxChars,"Ú¿ÀÙij"); // TLC, TRC, BLC, BRC, H, V
else          strcpy(BoxChars,"ɻȼͺ");

_settextcolor((short)Color);

//
// Draw top line
for (idx=0; idx<Width; idx++) LineBuf[idx]=BoxChars[4];
LineBuf[0]=BoxChars[0];
LineBuf[Width-1]=BoxChars[1];
LineBuf[Width]='\0';
_settextposition(TLCY,TLCX);
_outtext(LineBuf);

//
// Draw bottom line
LineBuf[0]=BoxChars[2];
LineBuf[Width-1]=BoxChars[3];
_settextposition(TLCY+Height,TLCX);
_outtext(LineBuf);

//
// Draw middle lines
for (idx=0; idx<Width; idx++) LineBuf[idx]=' ';
LineBuf[0]=BoxChars[5];
LineBuf[Width-1]=BoxChars[5];
LineBuf[Width]='\0';
for (idx=0; idx<(Height-1); idx++)
    {
    _settextposition(TLCY+1+idx,TLCX);
    _outtext(LineBuf);
    }
return;
}

//                                                                     Zappit
/////////////////////////////////////////////////////////////////////////////
// Wipes disk DiskToWipe
void Zappit()
{
//
// Blank part of display
_settextposition(22,3);
_outtext("                                                                          ");
_settextposition(23,3);
_outtext("                                                                          ");
_settextposition(24,3);
_outtext("                                                                          ");

//
// Setup progress display
_setbkcolor(4l);
DrawBox(4,22,73,2,MainTextColor,1,4);
_settextcolor(0);

//
// Paint black smudge for progress bar background
for (TempIdx=0; TempIdx<69; TempIdx++)
    {
    _settextposition(23,6+TempIdx);
    _outtext("\xdb");
    }

//
// Show which disk is getting zapped
// (As an extra added feature (yaaaah) each disk gets it's own color
//  and every reference to that disk is displayed in that color (15-DiskNo))
_setbkcolor(1l);
_settextcolor(MainTextColor);
_settextposition(21,29);
_outtext("Progress Wipeing Disk");
sprintf(TempStr,"%1d",DiskToWipe);
_settextposition(21,51);
_settextcolor(15-DiskToWipe);
_outtext(TempStr);
_settextcolor(MainTextColor);

//
// Progress is based on how many cylinder/head clumps have been done
CylHeadTot=((long)Cylinders[DiskToWipe])
          *((long)Heads[DiskToWipe]);
CylHeadDone=0;

//
// Drain keyboard
while (_kbhit()) getch();

BrokeOut=0;
time(&StartTime);
if (Int13Ext[DiskToWipe])
   {
   for (BlockIdx=0; BlockIdx<SectorsOnDisk[DiskToWipe].Low32; BlockIdx+=16)
       {
       ExtPkt.PktSize=0x10;
       ExtPkt.Reserved=0;
       ExtPkt.BlockCount=16;
       TempAddr=&Pattern2[0];
       ExtPkt.SegBuff=_FP_SEG(TempAddr);
       ExtPkt.OffBuff=_FP_OFF(TempAddr);
       ExtPkt.AbsBlockNum.High32=0;
       ExtPkt.AbsBlockNum.Low32=BlockIdx;
       InRegs.h.ah=0x43;
       InRegs.h.al=0;
       InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
       TempAddr=&ExtPkt;
       _segread(&SegRegs);
       SegRegs.ds=_FP_SEG(TempAddr);
       InRegs.x.si=_FP_OFF(TempAddr);
       _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
       if (OutRegs.x.cflag)
          {
          printf("\nI/O error (%02x) at block (%08lx).  (%08lx) transferred\n",OutRegs.h.ah,BlockIdx,ExtPkt.AbsBlockNum.Low32);
          exit(1);
          }
       PctComplete=(1.0e2) * (((float)BlockIdx)
                             /((float)SectorsOnDisk[DiskToWipe].Low32));
       time(&CurrentTime);
       ElapsedSeconds=CurrentTime-StartTime;
       if (ElapsedSeconds==PrevElapsed) continue;
       if (ElapsedSeconds>0 && PctComplete>1.0e-4)
          {
          EstTotSecs=(long)(((float)ElapsedSeconds) * ((1.0e2)/PctComplete));
          }
       else EstTotSecs=0l;
       TotMin=ElapsedSeconds/60;
       RemSec=EstTotSecs-ElapsedSeconds;
       RemMin=RemSec/60;
       _settextposition(21,5);
       sprintf(TempStr,"Elapsed:%4ld:%02ld"
                      ,TotMin
                      ,ElapsedSeconds-(60*TotMin));
       _outtext(TempStr);
       _settextposition(21,65);
       sprintf(TempStr,"Rem:%4ld:%02ld"
                      ,RemMin
                      ,RemSec-(60*RemMin));
       _outtext(TempStr);
   
       _settextcolor(15-DiskToWipe);
       for (TempIdx=0; TempIdx<((int) (PctComplete*6.9e-1)); TempIdx++)
           {
           _settextposition(23,6+TempIdx);
           _outtext("\xdb");
           }
       _settextcolor(MainTextColor);
       PrevElapsed=ElapsedSeconds;
       }
   }
else
   {  
   for (CylIdx=0; CylIdx<Cylinders[DiskToWipe]; CylIdx++)
       {
       if (_kbhit()) {BrokeOut=1; break;}
       for (HeadIdx=0; HeadIdx<Heads[DiskToWipe]; HeadIdx++)
           {
           if (_kbhit()) {BrokeOut=1; break;}
           for (SecIdx=1; SecIdx<=Sectors[DiskToWipe]; SecIdx+=1)
               {
               if (_kbhit()) {BrokeOut=1; break;}
               InRegs.h.ah=0x03;                     // Write function
               InRegs.h.al=1;                        // Number of sectors
               WordDef.DefWord.IntDef=CylIdx;        // CH gets low 8 bits of cylinder
               InRegs.h.ch=WordDef.DefChars.LoByte;  // CL gets sector and top 2 bits of cyl
               InRegs.h.cl=(WordDef.DefChars.HiByte<<6)+(unsigned char)SecIdx;
               InRegs.h.dh=HeadIdx;                  // Head
               InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
               TempAddr=&Pattern2[0];
               _segread(&SegRegs);
               SegRegs.es=_FP_SEG(TempAddr);
               InRegs.x.bx=_FP_OFF(TempAddr);
               _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
               }
           //
           // Display progress

           CylHeadDone+=1;
           PctComplete=(1.0e2) * (((float)CylHeadDone)
                                 /((float)CylHeadTot));
           time(&CurrentTime);
           ElapsedSeconds=CurrentTime-StartTime;
           if (ElapsedSeconds==PrevElapsed) continue;
           if (ElapsedSeconds>0 && PctComplete>1.0e-4)
              {
              EstTotSecs=(long)(((float)ElapsedSeconds) * ((1.0e2)/PctComplete));
              }
           else EstTotSecs=0l;
           TotMin=ElapsedSeconds/60;
           RemSec=EstTotSecs-ElapsedSeconds;
           RemMin=RemSec/60;
           _settextposition(21,5);
           sprintf(TempStr,"Elapsed:%4ld:%02ld"
                          ,TotMin
                          ,ElapsedSeconds-(60*TotMin));
           _outtext(TempStr);
           _settextposition(21,65);
           sprintf(TempStr,"Rem:%4ld:%02ld"
                          ,RemMin
                          ,RemSec-(60*RemMin));
           _outtext(TempStr);
   
           _settextcolor(15-DiskToWipe);
           for (TempIdx=0; TempIdx<((int) (PctComplete*6.9e-1)); TempIdx++)
               {
               _settextposition(23,6+TempIdx);
               _outtext("\xdb");
               }
           _settextcolor(MainTextColor);
           PrevElapsed=ElapsedSeconds;
           }
       }
   }      
if (BrokeOut==0)
   {
   // Put our signature on the drive
   CylIdx=6;
   HeadIdx=6;
   SecIdx=6;
   InRegs.h.ah=0x03;                     // Write function
   InRegs.h.al=1;                        // Number of sectors
   WordDef.DefWord.IntDef=CylIdx;        // CH gets low 8 bits of cylinder
   InRegs.h.ch=WordDef.DefChars.LoByte;  // CL gets sector and top 2 bits of cyl
   InRegs.h.cl=(WordDef.DefChars.HiByte<<6)+(unsigned char)SecIdx;
   InRegs.h.dh=HeadIdx;                  // Head
   InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
   TempAddr=&SigSec[0];
   _segread(&SegRegs);
   SegRegs.es=_FP_SEG(TempAddr);
   InRegs.x.bx=_FP_OFF(TempAddr);
   _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
   Zapped[DiskToWipe]='Y';
   }
while (_kbhit()) getch();
return;
}



//                                                                       main
/////////////////////////////////////////////////////////////////////////////
void main(argc,argv)
int argc;
char * argv[];
{

//
// Setup patterns
for (TempIdx=0; TempIdx<8192; TempIdx++)
    {
    Pattern2[TempIdx]=0;
    }
for (TempIdx=0; TempIdx<512; TempIdx++)
    {
    SigSec[TempIdx]=0;
    }
SigSec[236]='R';    

// Get Disk parameters
InRegs.h.ah=0x08;
InRegs.h.dl=0x80;
_int86(0x13,&InRegs,&OutRegs);
NumDisks=OutRegs.h.dl;
if (NumDisks>9)
   {
   printf("Too many drives\n");
   exit(0);
   }

for (DiskNo=0; DiskNo<NumDisks; DiskNo++)
    {
    Cylinders[DiskNo]=OutRegs.h.ch;
    TempInt=OutRegs.h.cl;
    TempInt=4*(TempInt & 0xc0);
    Cylinders[DiskNo]+=TempInt;
    Sectors[DiskNo]=OutRegs.h.cl & 0x3f;
    Heads[DiskNo]=OutRegs.h.dh;
    SizesM[DiskNo]=((unsigned long)(1+Cylinders[DiskNo])) // +1?
                  *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                  *((unsigned long)(1+Heads[DiskNo]))     // +1?
                  /2L;
    SizesM[DiskNo]=SizesM[DiskNo]/1024L;

    //
    // Load partition table for this drive
    InRegs.h.ah=0x02; // Read
    InRegs.h.al=1;    // Num sectors
    InRegs.h.ch=0;    // Cylinder 0
    InRegs.h.cl=1;    // Sect 1
    InRegs.h.dh=0;    // Head 0
    InRegs.h.dl=0x80+(unsigned char) DiskNo;
    TempAddr=&SectorBuf[0];
    _segread(&SegRegs);
    SegRegs.es=_FP_SEG(TempAddr);
    InRegs.x.bx=_FP_OFF(TempAddr);
    _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
    for (TempIdx=0; TempIdx<64; TempIdx++)
        {
        Partitions[DiskNo][TempIdx]=SectorBuf[TempIdx+512-66];
        }
    // Check for int13 extensions installed
    InRegs.h.ah=0x41;
    InRegs.x.bx=0x55aa;
    InRegs.h.dl=0x80;
    _int86(0x13,&InRegs,&OutRegs);
    Int13Ext[DiskNo] = !OutRegs.x.cflag; // cflag set => int13 extensions not present
    if (Int13Ext[DiskNo])
       {
       // get extended drive parms
       InRegs.h.ah=0x48;
       InRegs.h.dl=0x80+DiskNo;
       ExtParmBuf[0]=0x42;
       ExtParmBuf[1]=0;
       TempAddr=&ExtParmBuf[0];
       _segread(&SegRegs);
       SegRegs.ds=_FP_SEG(TempAddr);
       InRegs.x.si=_FP_OFF(TempAddr);
       _int86(0x13,&InRegs,&OutRegs);
       // Save the extended info over the old stuff
       SectorSize[DiskNo]=ExtParmBuf[0x18]+256*((unsigned int)ExtParmBuf[0x19]);
       for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x08+TempIdx];
       Heads[DiskNo]=ldef.uulong;
       for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x0c+TempIdx];
       Sectors[DiskNo]=ldef.uulong;
       for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x04+TempIdx];
       Cylinders[DiskNo]=ldef.uulong;
       SizesM[DiskNo]=((unsigned long)(1+Cylinders[DiskNo])) // +1?
                     *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                     *((unsigned long)(1+Heads[DiskNo]))     // +1?
                     /2L;
       SizesM[DiskNo]=SizesM[DiskNo]/1024L;
       for (TempIdx=0; TempIdx<8; TempIdx++) *(TempIdx+(unsigned char *)&SectorsOnDisk[DiskNo])=ExtParmBuf[0x10+TempIdx];
       }
    // Check for signature
    CylIdx=6;
    HeadIdx=6;
    SecIdx=6;
    InRegs.h.ah=0x02;                     // Read function
    InRegs.h.al=1;                        // Number of sectors
    WordDef.DefWord.IntDef=CylIdx;        // CH gets low 8 bits of cylinder
    InRegs.h.ch=WordDef.DefChars.LoByte;  // CL gets sector and top 2 bits of cyl
    InRegs.h.cl=(WordDef.DefChars.HiByte<<6)+(unsigned char)SecIdx;
    InRegs.h.dh=HeadIdx;                  // Head
    InRegs.h.dl=0x80+(unsigned char) DiskNo;
    TempAddr=&ReadSig[0];
    _segread(&SegRegs);
    SegRegs.es=_FP_SEG(TempAddr);
    InRegs.x.bx=_FP_OFF(TempAddr);
    _int86x(0x13,&InRegs,&OutRegs,&SegRegs);
    if (ReadSig[236]=='R') Zapped[DiskNo]='Y';
    else                   Zapped[DiskNo]='N';
    //
    // Get next Disk parameters
    InRegs.h.ah=0x08;
    InRegs.h.dl=0x81+DiskNo;
    _int86(0x13,&InRegs,&OutRegs);
    }

MainTextColor=11;
Quit=0;
while (Quit==0)
      {
      //
      // Showem what they have
      _setbkcolor(1l);
      _clearscreen(_GCLEARSCREEN);
      DrawBox(2,1,77,24,15,1,0);
      _settextcolor(MainTextColor);
      for (TempIdx=0; TempIdx<77; TempIdx++) MiddleLine[TempIdx]=(unsigned char)'Ä';
      MiddleLine[77]='\0';
      MiddleLine[0]=(unsigned char)199;
      MiddleLine[76]=(unsigned char)182;
      _settextposition(3,2);
      printf("%s",MiddleLine);
      _settextposition(2,20);
      _outtext("FS_WIPE v2.0 - The Secure Disk Wiper");
      _settextposition(4,6);
      _outtext("Disk#  Cyls  Heads  Sects   Wiped?     Ptn#  Boot?  i13Ext?  Size(Mb)");
      LineCtr=4;
      for (DiskNo=0; DiskNo<NumDisks; DiskNo++)
          {
          _settextposition(++LineCtr,8);
          _settextcolor(15-DiskNo);
          sprintf(TempStr,"%1x  %6ld    %3ld   %4ld     %1c",DiskNo
                                                            ,Cylinders[DiskNo]
                                                            ,Heads[DiskNo]
                                                            ,Sectors[DiskNo]
                                                            ,Zapped[DiskNo]);
          _outtext(TempStr);
          DidPtn=0;
          for (TempIdx=0; TempIdx<4; TempIdx++)
              {
              if (Partitions[DiskNo][16*TempIdx+4]==0) continue;
              HiWord=256*Partitions[DiskNo][16*TempIdx+15] + Partitions[DiskNo][16*TempIdx+14];
              LoWord=256*Partitions[DiskNo][16*TempIdx+13] + Partitions[DiskNo][16*TempIdx+12];
              PartSize=(((65536l)*((unsigned long)HiWord)) + (unsigned long) LoWord)/2048l;
              DidPtn=1;
              _settextposition(LineCtr++,47);
              sprintf(TempStr,"%1d     %1c       %1c       %5ld",TempIdx
                                                                ,(Partitions[DiskNo][TempIdx]==0x80)?'Y':'N'
                                                                ,(Int13Ext[DiskNo])?'Y':'N'
                                                                ,PartSize);
              _outtext(TempStr);
              }
          if (DidPtn!=0) LineCtr-=1;;
          }

      _settextcolor(MainTextColor);
      _settextposition(24,4);
      _outtext("Enter the number of a disk to wipe, or press ESC to quit:");
      while(!_kbhit());
      CharIn=getch();
      if (CharIn==27)
         {
         Quit=1;
         continue;
         }
      if (CharIn<'0' || CharIn >'9') continue;
      CharIn-='0';
      if (CharIn>=NumDisks) continue;
      DiskToWipe=CharIn;
      _settextposition(22,3);
      _outtext("You have selected disk ");
      sprintf(TempStr,"%1d",DiskToWipe);
      _settextcolor(15-DiskToWipe);
      _outtext(TempStr);
      _settextcolor(11);
      _outtext(" for wiping.");
      _settextposition(23,3);
      _outtext("All data will be irrevocably destroyed with no possibility of recovery!");
      _settextposition(24,3);
      _outtext("To procede with the wipe, press @.  Any other key will abort without loss.");
      while (_kbhit()) getch();
      while (!_kbhit());
      CharIn=getch();
      if (CharIn!='@') continue;
      //
      // Golly! They really wanna zap it!
      Zappit();
      }
exit(0);
}
Avatar of aktush

ASKER

Which compiler did you used because when I try to compile your _outtext() it displays garbage characters. I could use prontf() but it does not display anything I have talked to some people and figured out that it might be a thread creation that makes it act funny. When an interrut is called a thread is created in Digital Mars and when you do printf after the _int86() it does not do anything because the thread is still running. The compiled program does not wait for the _int86 to finish and goes right through it messing up printf(). After I removed the _int86() [the one detecting HDD] the printf() worked OK.

So I am just woundering what compiler did you use to compile the program, to be able to use <graph.h>?
Avatar of aktush

ASKER

Also every time you try to printf() even your printf in the error block


printf("\nI/O error (%02x) at block (%08lx).  (%08lx)transferred\n",OutRegs.h.ah,BlockNum,ExtPkt.AbsBlockNum.Low32);

it gives "stack overflow" message.
Link with a larger stack.
(Notifs for you last two posts came in reverse order, so I just now saw your earlier message)

MSVC 1.52 - their last 16-bit compiler.  The garbage characters charcters probably come from not running under pure DOS with ANSI.SYS (where you get block and line graphic characters instead of extended alphabetics).

graph.h is used for the definitions of the various screen positioning calls.


<After I removed the _int86() [the one detecting HDD] the printf() worked OK>
I can't imagine why a compiler would generate code to spawn a thread for interrupt processing.  Unless the documentation explicitly says that it does so, I would look for a more prosaic cause.  If it really does do that, does it let you embed assembly code (_asm directive) that might get around that problem.



You do run the program in pure DOS don't you?
Avatar of aktush

ASKER

It was my guess that the compiler does that spawns a thread noithing like that though in the documentation.

Also I have tried increasing stack size in C by using

unsigned __cdecl _stack = nnn;
where nnn is a stack size (Default = 8192 bytes).

This did not help, I will try using /STACK when linking.

I figured that MSVC would have characters set up in a special way so that was not a surprise, I dont need really a 'GUI' but just to display cylinders and sectors currently formating (beeing overriden) thats all and all i need is for printf to work properly thats all.

Why did you used _outtext() ? instead of puts or printf() ?
<noithing like that though in the documentation...>
I would have been surprised had there been.  If you're confident that the code is good, double check again.  If you want to check for a less likely cause, check the generated code.  Over the years I've seen numerous instances of compilers generating bad code.  Many such compiler failures can be worked around by adding do-nothing code around the otherwise good code that induced the error.

The most likely cause, however, is invariably a typo or misunderstanding of what's going on.



<I figured that MSVC...>
It's not MSVC, but DOS.  Loading ANSI.SYS in CONFIG.SYS remapped the top half of the ASCII set onto a set of graphic characters.  I used them to draw lines and borders to make the screen easier to read.


<_outtext()>
_outtext() writes  text to the screen coordinates specified by _settextposition().
Avatar of aktush

ASKER

I see .. but in theory printf() would work? right ? I dont know what happens but even after I increased the stack size to be 1048576 bytes instead of 8192 by default it gives a "stack overflow" again and if you look at the Digital Mars documentation it says for a DOS 16 bit programs do not use /STACK link option use the constant declaration:

unsigned __cdecl _stack = nnn;

at the beginning of the program.

--------------------------------------------

This is a part of DM docs:

The program stack

Win32 programs generally do not need to worry about stack overflows, but other memory model options require some attention to it.

In all memory models, the stack grows downward toward statically allocated data. If the stack grows larger than its allocated size, statically allocated data are
destroyed, and the program performs unpredictably. To check if the stack grows past the allocated limit, use the special function called _chkstack.

Call _chkstack from functions that potentially use large amounts of stack space, such as recursive functions. If the stack exceeds the allocated size, _chkstack aborts
the program with the message:

        Stack overflow

Use the -s compiler option (see below) to generate stack-overflow checking code on entry to every function, although this increases program size and reduces
speed. Use stack-overflow checking if you suspect stack-overflow errors (a symptom is a mysterious crash).

Controlling stack size for DOS programs

Control a DOS program's stack size in the following ways:

     Use the =nnnn command line option (see below) when running the program.
     Include the declaration

             unsigned __cdecl _stack = nnnn;
             

     in the program source code, where nnnn is the size in bytes that you want the stack to be. For C++ code, use:

             extern "C" unsigned __cdecl _stack = nnnn;
             

     Note that the declaration must appear just as described above to work correctly.
     Modify the c.asm startup module.

For Win16 compilations, use the linker's /STACK switch to set the stack size.

For a DOS program, do not use the linker /STACK parameter to set the stack size; use one of the methods described above. The default stack size is 8192 bytes
for 16-bit compilations and 16KB for 32-bit compilations.

     -s Check for stack overflow


Avatar of aktush

ASKER

So I added unsigned __cdecl _stack = nnnn; right at the beginning of the program declaration... I dont know why the stack grows that big I guess is the loops, but the program just starts and then exits with stack overflow WITHOUT actually doing anything.

P.S

If you are simpsons fan in one of the episodes there was a Krusty camp where Mr. Black was their leader and Burney (the alcoholic) was playing Krustry, and Mr. Black:

"So kids Krusty is a bit off today to he wont be doing anything OR saaaying anything..."

:)
Just like __cdscl _stack
Avatar of aktush

ASKER

I nerrowed the problem with printf down to the interrupt inside the for loops the one that writes to the disk.

When you comment it out everything works perfect.

I guess I will do some more debuging inside the loops.
Avatar of aktush

ASKER

            // Use extended write
                                ExtPkt.PktSize=0x10;
                                ExtPkt.Reserved=0;
                                ExtPkt.BlockCount=16;
                                TempAddr=&Pattern2[0];
                                ExtPkt.SegBuff=_FP_SEG(TempAddr);
                                ExtPkt.OffBuff=_FP_OFF(TempAddr);
                                ExtPkt.AbsBlockNum.High32=0;
                                ExtPkt.AbsBlockNum.Low32=BlockNum;
                                InRegs.h.ah=0x43;
                                InRegs.h.al=0;
                                InRegs.h.dl=0x80+(unsigned char) DiskToWipe;
                                TempAddr=&ExtPkt;
                                _segread(&SegRegs);
                                SegRegs.ds=_FP_SEG(TempAddr);
                                InRegs.x.si=_FP_OFF(TempAddr);
                                _int86x(0x13,&InRegs,&OutRegs,&SegRegs);

What can be wrong in this piece of code I wonder .... hmmm
I finally looked at DigitalMars.

Do you use -ml when compiling to specify the large memory model?

Avatar of aktush

ASKER

No I used -ms

I also found that when I am trying to print the structures are not filled in for some reason?

#include <stdio.h>
#include <dos.h>


void disk_format();


int main ( void )
{
  disk_format();
  return 0;
}


void disk_format()
{

  union  _REGS  InRegs, OutRegs;
  struct _SREGS SegRegs;


  long          Int32;
  short         Int16;

  struct Def64
  {
    unsigned long Low32;
    unsigned long High32;
  };


  struct Def64 Int64;

  union
  {
     unsigned long uulong;
     unsigned char ucchar[4];
  }  ldef;



  unsigned int  DiskNo   = 0;            // Current disk #
  unsigned int  NumDisks = 0;            // Number of disks
  unsigned char SectorBuf[512] = {0};    // Place to load a disk's sector 1 into

  unsigned long Tracks [9] = {0};        // High tracks for each disk
  unsigned long Heads  [9] = {0};        // High heads for each disk
  unsigned long Sectors[9] = {0};        // High sectors for each disk

  unsigned int  SectorSize[9] = {0};     // int 13 ext - sector size
  struct Def64  SectorsOnDisk[9] = {0};  // int 13 ext
  unsigned long SizesM[9] = {0};         // Size in MB for each disk
  unsigned char Partitions[9][64];       // Partition tables
  unsigned char Int13Ext[9] = {0};       // int13 extensions?


  unsigned char ExtParmBuf[0x42];

  int           Removable[9] = {0};      // int 13 ext - removable drive



  void     far *TempAddr;  // Place to get SEG and OFFs through
  unsigned int  CylIdx;    // Just some subscripts
  unsigned int  SecIdx;
  unsigned int  HeadIdx;

 
  struct DefInt
  {
    int  IntDef;
  };

  struct DefBytes
  {
    unsigned char LoByte;
    unsigned char HiByte;
  };

  union  DefMap
  {
    struct DefInt   DefWord;
    struct DefBytes DefChars;
  };


  union  DefMap WordDef;
  unsigned long BlockNum;      
  unsigned int  TempInt, TempIdx;

//-------------------------------------------------------




// Get Disk parameters
InRegs.h.ah=0x08;
InRegs.h.dl=0x80;
_int86(0x13,&InRegs,&OutRegs);

NumDisks=OutRegs.h.dl;




if (NumDisks > 9)
{
  printf("You have %i%s", NumDisks, " drives which is too many drives\n");
  exit(0);
}
else
  printf("You have: %i%s", NumDisks, " drive[s]\n");




for (DiskNo=0; DiskNo < NumDisks; DiskNo++)
{
   Tracks[DiskNo]=OutRegs.h.ch;
   TempInt=OutRegs.h.cl;
   TempInt=4*(TempInt & 0xc0);
   Tracks[DiskNo]+=TempInt;
   Sectors[DiskNo]=OutRegs.h.cl & 0x3f;
   Heads[DiskNo]=OutRegs.h.dh;
   SizesM[DiskNo]=((unsigned long)(1+Tracks[DiskNo]))    // +1?
                 *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                 *((unsigned long)(1+Heads[DiskNo]))     // +1?
                 /2L;
   SizesM[DiskNo]=SizesM[DiskNo]/1024L;

   //
   // Load partition table for this drive

   InRegs.h.ah=0x02; // Read
   InRegs.h.al=1;    // Num sectors
   InRegs.h.ch=0;    // Cylinder 0
   InRegs.h.cl=1;    // Sect 1
   InRegs.h.dh=0;    // Head 0
   InRegs.h.dl=0x80+(unsigned char) DiskNo;
   TempAddr=&SectorBuf[0];
   _segread(&SegRegs);
   SegRegs.es=_FP_SEG(TempAddr);
   InRegs.x.bx=_FP_OFF(TempAddr);
   _int86x(0x13,&InRegs,&OutRegs,&SegRegs);

   for (TempIdx=0; TempIdx < 64; TempIdx++)
   {
     Partitions[DiskNo][TempIdx]=SectorBuf[TempIdx+512-66];
   }

   // Check for int13 extensions installed
   InRegs.h.ah=0x41;
   InRegs.x.bx=0x55aa;
   InRegs.h.dl=0x80;
   _int86(0x13,&InRegs,&OutRegs);
   Int13Ext[DiskNo] = !OutRegs.x.cflag; // cflag set => int13 extensions not present
   
   if (Int13Ext[DiskNo])
   {
      // get extended drive parms

      InRegs.h.ah=0x48;                                    // Extended information about HDD
      InRegs.h.dl=0x80+DiskNo;                             // Loop through possible disks
      ExtParmBuf[0]=0x42;                                  // Function extended disk info
      ExtParmBuf[1]=0;                                    
      TempAddr=&ExtParmBuf[0];                             // Temporary address of HDD
      _segread(&SegRegs);                                  // Read the current memory segment
      SegRegs.ds=_FP_SEG(TempAddr);                        // Point to ds segment
      InRegs.x.si=_FP_OFF(TempAddr);                       // Point to si index
      _int86(0x13,&InRegs,&OutRegs);                       // Call the interupt 13h disk information


      // Save the extended information over the old information

      SectorSize[DiskNo]=ExtParmBuf[0x18]+256*((unsigned int)ExtParmBuf[0x19]);
      for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x08+TempIdx];
           Heads[DiskNo]=ldef.uulong;

      for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x0c+TempIdx];
           Sectors[DiskNo]=ldef.uulong;

      for (TempIdx=0; TempIdx<4; TempIdx++) ldef.ucchar[TempIdx]=ExtParmBuf[0x04+TempIdx];
           Tracks[DiskNo]=ldef.uulong;

      SizesM[DiskNo]=((unsigned long)(1+Tracks[DiskNo]))    // +1?
                    *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                    *((unsigned long)(1+Heads[DiskNo]))     // +1?
                    /2L;

      SizesM[DiskNo]=SizesM[DiskNo]/1024L;
   }


   // Get next Disk parameters

   InRegs.h.ah=0x08;
   InRegs.h.dl=0x81+DiskNo;
   _int86(0x13,&InRegs,&OutRegs);
 }






  // ------------------------------------
  // Print drives info



  for (unsigned int current_drive = 0; current_drive <= NumDisks; current_drive++)
  {
    printf("Drive: %i Tracks: %i Sectors: %i SectorSize: %i Heads: %i Size[MB]: %i\n",
                              current_drive,
                              Tracks[current_drive],
                              Sectors[current_drive],
                              SectorSize[current_drive],
                              Heads[current_drive],
                              SizesM[current_drive]);
  }
}
Avatar of aktush

ASKER

Actually its not the model mode that was in the way it was the interrupt that wrote to a  disk itself. So that part I rewriten
in Digital Mars:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <disp.h>



#define _SECTOR_SIZE 512
#define _SECOTRS_AT_A_TIME 8
#define _CURRENT_DRIVE 0
#define RAND_MAX 127     // A byte can store = 127


typedef struct
{
  unsigned int tracks_detected;
  unsigned int heads_detected;
  unsigned int sectors_detected;   // 512 * 36 = 18
} disk_info;



void start_program();
void init_ones(char *);
void init_zeros(char *);
void init_random(char *);
void overwrite_disk(char *, disk_info *);
unsigned int get_random_digit();
void overwrite_disk_random(disk_info *);
void clear_screen();




int main (void)
{
  start_program();
  return 0;
}



void start_program()
{
  char buffer[_SECTOR_SIZE * _SECOTRS_AT_A_TIME];

  disk_info current_drive;
  current_drive.tracks_detected  = 80;
  current_drive.heads_detected   = 2;
  current_drive.sectors_detected = 18;    // 512 * 36 = 18



  init_ones(buffer);
  overwrite_disk(buffer, &current_drive);

  init_zeros(buffer);
  overwrite_disk(buffer, &current_drive);

  overwrite_disk_random(&current_drive);
}


void init_ones(char * buffer)
{
  for (int i = 0; i < _SECTOR_SIZE * _SECOTRS_AT_A_TIME; i++)
       buffer[i] = 1;
}


void init_zeros(char * buffer)
{
  for (int i = 0; i < _SECTOR_SIZE * _SECOTRS_AT_A_TIME; i++)
       buffer[i] = 0;
}


void init_random(char * buffer)
{
  for (int i = 0; i < _SECTOR_SIZE * _SECOTRS_AT_A_TIME; i++)
       buffer[i] = get_random_digit();
}


void overwrite_disk(char * buffer, disk_info * current_drive)
{
  time_t start_time , finish_time;
  time (&start_time);

  for (int track = 0; track < current_drive->tracks_detected; track++)
  {
    for (int sector = 0; sector < current_drive->sectors_detected; sector += _SECOTRS_AT_A_TIME)
    {
      for (int head = 0; head < current_drive->heads_detected; head++)
      {
        biosdisk(3, _CURRENT_DRIVE, head, track, sector, _SECOTRS_AT_A_TIME, buffer);

        clear_screen();
        time(&finish_time);
        printf("Overwriting with %i's onto the disk\n", buffer[0]);
        printf("Writing to head: %i track: %i sector: %i\n\n", head, track, sector);
        printf("Time elapsed: %i%s", (int)(finish_time-start_time), " seconds");
      }
    }
  }
}


void overwrite_disk_random(disk_info * current_drive)
{
  char buffer[_SECTOR_SIZE];
  time_t start_time , finish_time;
  time (&start_time);

  for (int track = 0; track < current_drive->tracks_detected; track++)
  {
    for (int sector = 0; sector < current_drive->sectors_detected; sector += _SECOTRS_AT_A_TIME)
    {
      for (int head = 0; head < current_drive->heads_detected; head++)
      {
        init_random(buffer);
        clear_screen();
        biosdisk(3, _CURRENT_DRIVE, head, track, sector, _SECOTRS_AT_A_TIME, buffer);

        clear_screen();
        time(&finish_time);
        printf("Overwriting with random numbers\n");
        printf("Writing to head: %i track: %i sector: %i\n\n", head, track, sector);
        printf("Time elapsed: %i%s", (int)(finish_time-start_time), " seconds");
      }
    }
  }
}



unsigned int get_random_digit()
{
  srand(time(NULL));
  return(rand() % RAND_MAX);
}


void clear_screen()
{
  disp_open();
  disp_move(0,0);
  disp_eeop();
  disp_close();
}
Avatar of aktush

ASKER

So all I need is the information extended information about the drives and I can not get a proper info. Try to compile this code 1 from above, the one printing information about the drives and compile see what you get cause i get some ridiculous info:

You have: 1 drive[s]
Drive: 0 Tracks: 962 Sectors: 0 SectorSize: 4 Heads: 0 Size[MB]: 0
Drive: 1 Tracks: 0 Sectors: 0 SectorSize: 0 Heads: 0 Size[MB]: 0

Drive 0 is floppy and 1 is HDD 4Gb!
When I took out the graphic stuff and ran a DM produced exe, I got good disk geometry info.  I didn't go beyond that because I had no spare drive available for trashing.


1) The original code uses far pointers.  It must be compiled with the large memory model.

2) I'd be real surprised if DM's biosdisk() uses the int13 extensions.


Avatar of aktush

ASKER

I dont know .. I did check you the contents of the arrays after the execution of the code that detects drive parameters:

.................. from the beginning..............................

      SizesM[DiskNo]=((unsigned long)(1+Cylinders[DiskNo])) // +1?
                    *((unsigned long)(1+Sectors[DiskNo]))   // +1?
                    *((unsigned long)(1+Heads[DiskNo]))     // +1?
                    /2L;
      SizesM[DiskNo]=SizesM[DiskNo]/1024L;
      }
   //
   // Get next Disk parameters
   InRegs.h.ah=0x08;
   InRegs.h.dl=0x81+DiskNo;
   _int86(0x13,&InRegs,&OutRegs);
   }


..........................My added code..................................................

   for (int i = 0; i < NumDisks; i++)
   {
     printf("Tracks: %i Heads: %i Sectors: %i SizesM: %i SectorSize: %i Int13Ext: %i SectorsOnDisk: %i\n",
     Cylinders[i], Heads[i], Sectors[i], SizesM[i], SectorSize[i], Int13Ext[i], SectorsOnDisk[i]);
   }


This is what i get:

Tracks: 962 Heads: 0 Sectors: 1 SizesM: 0 SectorSize: 4 Int13Ext: 0 SectorsOnDisk: 4

The Int13ext is correct because 0 is success meaning system has extentions.
The rest of the stuff are you shuuure that this is what I supposed to get?
Avatar of aktush

ASKER

May be the Tracks (Cyllinders[]) are the sectors?? or something and the Sectors are the drive number ..I dont know I mean sectors on disk can not be 4?
Avatar of aktush

ASKER

WOW Q! but when I run the program in the real mode on the other machine under DOS it gives:

You have 1 drives
Tracks: 1023 Heads: 0 Sectors: 15 SizesM: 0 SectorSize: 63 Int13Ext: 0 SectorsOnDisk: 512

Is this ok? but Size of the disk is still 0?
Avatar of aktush

ASKER

Ok ok ! I see I ran the comercial program a tool to detect the numbers the geometry and this is what they gave me:

Cylinders: 1024
Heads: 16
Sectors per table: 63
Bytes per sector: 512

Now I calculate the dirve size myself 1024 x 16 x 63 x 512 bytes / table = 528.482304 BUT! this is only in bytes
by dividing the result by 1,048,576 ! in  meg! we get exactly ! 504 MB just like detected my the OS!!! yahoo!

So now the program crashed because these values were reversed:

Cylinders = Cilinders + 1
Sectors = Heads + 1
Sector size = sectors per table
SectorsOnDisk = Bytes per sector!

Now all is clear! I will rewrite the loop and woala!
Avatar of aktush

ASKER

No I am still getting the same error messages about the stack overflow ... darn whenever I am calling  interrupt 13h with write  after that if you call something it crashed with stack overflow. Now the arrays are they supposed to be empty before you write them onto the disk because you do not initialize them to ={0/1}?
Are you now compiling tp large memory model?  If not, do so.

---

Bear in mind that the CHS numbers you get may vary from those stamped on the drive.  It depends on the BIOS and what addressing mode it uses on the drive.  Moreover, some BIOSes will report different CHS figures for the same non-SCSI drive when it's moved from channel 0 to 1.

Perhaps it would prove instructional for you to do a web search on: +chs +lba
(Google returned almost 14,000 hits)
Avatar of aktush

ASKER

I have read quite a number of articles over the last month or so I am ready to write a book the problem is it works in practice and DOES not in reality so many problems .. there is gotta be a better easier way to access extneded write to an HDD using some C functions like standard functions. I dont know I have almost memorized by now the Ralf Brown int lists and all those params that go into high and low registers ... its hard nothing seems to work and I do exactly like it says.. Now I have ASM experience but not like that doing __FSEG() and OF IF xx.xx.xx.long is weird. I dont know I have learned a lot about C & ASM and DOS int lists over the last little while and I am very greatfull to you for caring .. its just I guess some things are ment not to work..

P.S

I compiled in the -ml mode and -ms mode both producing the same results.

LBA is a little easier you just need a struct with the DWORDS and WORDS in it make offset point into SI segment and then call you write procedure with 13h thats all but it does not work. .. your program works but when I compile with DMc -ml crashes with stack overflow even sometimes when there is no output...

when I commented out once the TempAdr line inside the write loops it worked ok ?? did not crash but did not do anything,
I can not even get right values for CYLS HEADS SECS. ?

I am thinking about all this .. just what will i do next cause quote Simpsons: "I have tried nothing maaan and I am all out of ideas"... :)) I ahve tried EVEYTHING and I am all out of ideas for now ...
Post all of the code and I'll look at tomorrow.

Avatar of aktush

ASKER

I am posting the code:

http://www.echosoft.ca/~aktush/df.c

This is a url of the code it works I have checked, so that we do not create a huge lengthy branch or at least do not expand it as it is big filled with pieces of code already. :)


P.S

Another way is, do you have a Visual C++ 1.5 ? MSVC 1.5 that you compiled the final program with I can try to use that ? hey and maybe the cut out version will not crash with stack overflow? I dont know you probably dont have such old soft but just an idea.
Yes, I do use MCVC 1.52


See what happens if you initialize DiskToWipe before using it in
for (..., DiskToWipe++)

If your linker doesn't create EXEs with an automatic clear of memory upon initial load, then one is liable to get interesting results when using it as a subscript.




Note that Microsoft doesn't like declaring variables in a for() definition, e.g.,:
for(int i=0; ...



When initializing the various Patterns, what's the purpose of the final stores of '\0' (and where are they REALLY going)?



Checking for terminating keystrokes is nice, but a bit pointless when there's no oppotunity for the user to abort before the writes begin.  Woe betide the poor soul who mistakenly runs it on a box not meant to have all of its' drives wiped.  

Can you figure out why I included the abilty to abort after wiping has begun?







Avatar of aktush

ASKER

Can you upload the MSVC to my friends' OpenBSD server where I posted the *.c file.
I will give you a password and you can log in as FTP or type in the browser user:pass@ftp.ca/~aktush
I will send you the password and details by e-mail.

I really just want to try to compile with YOUR compiler may be this is the root of the problem.
And see what results do I get. I have a lot of space there, although I am not an admin.

Plz I appreciate you effort.
If you need any soft that I might have tell me.

P.S

I am building and am almost finished my OWN FreeBSD server so if you need a good account there with space tell me too.
Avatar of aktush

ASKER

The address is ftp.echosoft.ca/~aktush just as the file name you have gotten from.
Avatar of aktush

ASKER

* I am not sure about the linker I will check it out. But most certainly there migh be an option for that

* Also the purpose of the '\0' null "terminator 3" is that I am very carefull programmer (will be) and try to minimize chances of trouble even though they might never occur like buffer overrun. Just in case is the answer.

* Yes I noticed you have a preemptive functions to terminate when a key is pressed which is nice! But unfortunatly when you have a stack overflow it quits right away not even a second after you run the program. But when I had program running before without output to the screen it worked when you press a key program exited almost instantly. Its good I like the idea.

If only I had MSVC to try to compile with clear screen etc with printf(). That would be awsome. A friend of mine has MSVC v2 but thats too new. Its already 32bit.

So yeah if you could post it upload it to an FTP cause that would be great

quote Simpsons: Old Gill "So how many calecas should I put the order on cmonn order a lot please say a lot cause that would be graeeeat..".
MSVC 1.52 is still under copyright, so I won't upload it.

If your friend has a real MSVC 2 CD, 1.52 should be on it.

In any case, I'll be gone this weekend, and will start fiddling with df under DM when I get back.

---

Since we're not dealing with strings, the zero byte store doesn't really do anything.  Moreover, you're not even putting them at the end of the arrays, but in the next byte after the array (the last byte of pattern1 is pattern1[511].  pattern1[512] is the first byte of pattern2.

---

The reason for the key check after wiping has begun is to allow the program to be used as a quick MBR and partition table zapper.  Letting the program run to completion is done only when all data is to be cleared with a reasonable degree of security.


Avatar of aktush

ASKER

"MSVC 1.52 is still under copyright, so I won't upload it." -

No problem I will try to see if I can get MSVC from somewhere, I am not sure why would they include MSVC 1,5 into 2 nd edition? I dont see the logic behind this.

"In any case, I'll be gone this weekend, and will start fiddling with df under DM when I get back." -

Sure, I will try on my side things on other machines but doubt it will give results... will see


pattern1[511] -

Sorry mistaken :)) Usually I do [SIZE - 1]

"The reason for the key check after wiping has begun is to allow the program to be used as a quick MBR and partition table zapper.  Letting the program run to completion is done only when all data is to be cleared with a reasonable degree of security." -

Its also convinient for exiting in the middle of write if its too slow.. :) when testing.
Avatar of aktush

ASKER

     for (TempIdx=0; TempIdx < 4; TempIdx++)
      {
        if (Partitions[DiskNo][16*TempIdx+4] == 0)
            continue;

        HiWord=256*Partitions[DiskNo][16*TempIdx+15] + Partitions[DiskNo][16*TempIdx+14];
        LoWord=256*Partitions[DiskNo][16*TempIdx+13] + Partitions[DiskNo][16*TempIdx+12];
        PartSize=(((65536l)*((unsigned long)HiWord)) + (unsigned long) LoWord)/2048l;
        DidPtn=1;
        _settextposition(LineCtr++,47);
        sprintf(TempStr,  "%1d     %1c       %1c       %5ld", TempIdx, (Partitions[DiskNo][TempIdx]==0x80)?'Y':'N', (Int13Ext[DiskNo])?'Y':'N', PartSize);
        _outtext(TempStr);
      }


I found an error in your code trying to debug but otherwise after a little redesign and adding random and 0's and 1's patterns the program is working!
Avatar of aktush

ASKER

Ok one question for you did you test the 13h extensions on an older machine? I think your Int13ext does not work.

AH = 41h
BX = 55AAh
DL = drive (80h-FFh)

It always gives the extensions are present, however when I tried to use a program (your original version, also my heavily modified) on an older machine that does not support extensions it crashed with error 0000000 omething, after I hard coded no extensions! it worked like a charm on that machine.

Then I isolated the module into a separate program and found out the detection of extensions does not work, of course all the tests are done in pure DOS.

What I will end up doing is creating then 2 programs one with hard coded int13ext to 1 another to 0.
Do you have better ideas?

P.S

I have added a writing patterns 0s 1s and random letters to the program.
It's been such a long time, I don't recall what I tested it on.

Avatar of aktush

ASKER

Ok good ! :)) I have figured a way around @!! yeeah! when it crashes with 00000 is test for it and reinitialize the procedure to a CHS mode and do the erase! 3 times the old way! cool.


Anyways I dont need my other points can I give you some more points because you helped me so muuch!!!
I can give you 35 points thats all I have for now.