Package Partition

Uses
    KbdCRT,Error

Interface

Sub ReadSect(Buf as Reference; Drive,Face as Byte;
                               Track as Word;
                               Sector,N as Byte)
Sub ReadPSect
Sub PrintPSect

Sub ReadBSH(Drive as Byte)
Sub PrintBSH

Var
   RootDirBeg,NbEntriesRootDir,CurDirBeg as Word
   DataBeg,SizeSector,SizeCluster,SectPerClus as Word

Const
     DriveA,DriveB,HardDisk=&H80

Sub SetDrive(No as Byte)
Sub ReadAbsSect(Buf as Reference,N as LongWord,Nb as Word)
Sub ReadCluster(Buf as Reference,No as Word)
Sub WriteAbsSect(Buf as Reference,N as LongWord,Nb as Word)
Sub WriteCluster(Buf as Reference,No as Word)

Var
   FATMask as Word

Sub FlushDirtyFATPiece
Def FATNext(N as Word) as Word
Def FATAlloc(N as Word) as Word
Sub ReWriteFATString(N as Word)

Sub PrintSector(L0,C0 as Int; Buf as Pointer)

Implementation

\\\
    AccŠs disque bas niveau
                            \\\
Sub ResetDrive(Drive as Byte)
  Nb,Err as Word
Enter
  Nb=0
  Always
    DX=Drive
    AX=0
    InLine &H13CD
    Err=High(AX)
  AWhile
    Err<>0 And Nb<&H10
  Do
    Nb+=1
  Wend
  If Err<>0 Then Error("ResetDrive");
Leave

Sub ReadSect(Buf as Reference; Drive,Face as Byte;
                               Track as Word;
                               Sector,N as Byte)
  A,C,D as Word
Enter
\ Lecture \
  A=&H0200|N
  D=(Face,Drive)
  If Drive=HardDisk Then
    C=(Low(Track),(High(Track)<<6)|Sector)
  Else
    C=(Low(Track),Sector)
  End
  AX=A:DX=D:CX=C
\ LES BX,[@Buf] \
  InLine &HC4,&H9E,Buf
  InLine &H13CD
  If High(AX)<>0 Then
  \ DeuxiŠme essai \
    AX=A:DX=D:CX=C
    InLine &HC4,&H9E,Buf
    InLine &H13CD
    If High(AX)<>0 Then Error("ReadSect");
  End
Leave

Sub WriteSect(Buf as Reference; Drive,Face as Byte;
                                Track as Word;
                                Sector,N as Byte)
  A,C,D as Word
Enter
\ Ecriture \
  A=&H0300|N
  D=(Face,Drive)
  If Drive=HardDisk Then
    C=(Low(Track),(High(Track)<<6)|Sector)
  Else
    C=(Low(Track),Sector)
  End
  AX=A:DX=D:CX=C
\ LES BX,[@Buf] \
  InLine &HC4,&H9E,Buf
  InLine &H13CD
  If High(AX)<>0 Then
  \ DeuxiŠme essai \
    AX=A:DX=D:CX=C
    InLine &HC4,&H9E,Buf
    InLine &H13CD
    If High(AX)<>0 Then Error("WriteSect");
  End
Leave

\\\
    Gestion PSect (HD)
                       \\\
Const
   \ State \
     NonBootable,Bootable=&H80
   \ Types \
     NotUsed,DOSFAT12,XENIX1,XENIX2,DOSFAT16,DOSExtended,DOSUnder32Mo
Type
    Record PartEntry is
      State as Byte
      BegHead as Byte
      BegSectTrack as Word
      PType as Byte
      EndHead as Byte
      EndSectTrack as Word
      Distance,Length as LongWord
    End

Sub PrintPartEntry(P as @PartEntry)
Enter
  PrintS "  State=":PrintX P.State:PrintCR
  PrintS "  BegHead=":PrintX P.BegHead:PrintCR
  PrintS "  BegSectTrack=":PrintX P.BegSectTrack:PrintCR
  PrintS "  PType=":PrintX P.PType:PrintCR
  PrintS "  EndHead=":PrintX P.EndHead:PrintCR
  PrintS "  EndSectTrack=":PrintX P.EndSectTrack:PrintCR
  PrintS "  Distance=":PrintLX P.Distance:PrintCR
  PrintS "  Length=":PrintLX P.Length:PrintCR
Leave

Type
    Record PartitionSector is
      BootCode as Array[0..&H1BD] Of Byte
      Entry as Array[1..4] Of PartEntry
      Signature as Word
    End
Var
   PSect as PartitionSector

Sub ReadPSect
Enter
  ReadSect PSect,HardDisk,0,0,1,1
Leave

Sub PrintPSect
  W as Word
Enter
  For W=1 To 4 Do
    If PSect.Entry[W].PType<>NotUsed Then
      PrintS "Partition No "
      PrintX W
      PrintCR
      PrintPartEntry PSect.Entry[W]
    End
  Next
Leave

\\\
    Gestion BSH
                \\\
Type
    Sector is Array [0..511] Of Byte
  \ BIOS Parm Block (BPB) \
    Record BootSectorHead is
      Jump as Array[1..3] Of Byte
      SysName as Array[1..8] Of Byte
      BytesPerSector as Word
      SectorsPerCluster as Byte
      NbBootSectors as Word
      NbFATs as Byte
      NbEntriesRootDir as Word
      NbSectors as Word
      VolumeDescriptor as Byte
      SectorsPerFAT as Word
      SectorsPerTrack as Word
      NbHeads as Word
      Distance as Word
    End
Var
   BSH as @BootSectorHead
   Buffer as Sector
   EntryNum as Word

Sub ReadBSH(Drive as Byte)
  W as Word
Enter
  If Drive=HardDisk Then
    EntryNum=0
    For W=1 To 4 Do
      If PSect.Entry[W].PType<>NotUsed Then
        EntryNum=W
        W=4
      End
    Next
    ReadSect(Buffer,Drive,
                    PSect.Entry[EntryNum].BegHead,
                    PSect.Entry[EntryNum].BegSectTrack>>6,
                    PSect.Entry[EntryNum].BegSectTrack&&H3F,1)
  Else
    ReadSect Buffer,Drive,0,0,1,1
  End
  @BSH=Pointer(@Buffer)
Leave

Sub PrintBSH
Var
   I as Int
Enter
  PrintS("  SysName : ")
  For I=1 To 8 Do PutCar(BSH.SysName[I]);
  PrintCR
  PrintS("  BytesPerSector : ")
  PrintX(BSH.BytesPerSector)
  PrintCR
  PrintS("  SectorsPerCluster : ")
  PrintX(BSH.SectorsPerCluster)
  PrintCR
  PrintS("  NbBootSectors : ")
  PrintX(BSH.NbBootSectors)
  PrintCR
  PrintS("  NbFATs : ")
  PrintX(BSH.NbFATs)
  PrintCR
  PrintS("  NbEntriesRootDir : ")
  PrintX(BSH.NbEntriesRootDir)
  PrintCR
  PrintS("  NbSectors : ")
  PrintX(BSH.NbSectors)
  PrintCR
  PrintS("  VolumeDescriptor : ")
  PrintX(BSH.VolumeDescriptor)
  PrintCR
  PrintS("  SectorsPerFAT : ")
  PrintX(BSH.SectorsPerFAT)
  PrintCR
  PrintS("  SectorsPerTrack : ")
  PrintX(BSH.SectorsPerTrack)
  PrintCR
  PrintS("  NbHeads : ")
  PrintX(BSH.NbHeads)
  PrintCR
  PrintS("  Distance : ")
  PrintX(BSH.Distance)
  PrintCR
Leave

\\\
    SetDrive
             \\\
Var
   DriveNo as Byte
   FATPiece as Array[0..1023] Of Byte
   FATBeg,NoFATPieceSect as Word
   FatPieceDirty as Byte
   IsFAT12 as Byte

Sub SetDrive(No as Byte)
Enter
  FlushDirtyFATPiece
  ResetDrive No
  If No=HardDisk Then ReadPSect;
  ReadBSH(No)
  DriveNo=No
  FATBeg=BSH.NbBootSectors+BSH.Distance
  RootDirBeg=FATBeg+BSH.NbFATs*BSH.SectorsPerFAT
  NbEntriesRootDir=BSH.NbEntriesRootDir
  DataBeg=FATBeg+(
                    BSH.NbFATs*BSH.SectorsPerFAT+
                    BSH.NbEntriesRootDir/(BSH.BytesPerSector>>5)
                 )
  SectPerClus=BSH.SectorsPerCluster
  SizeSector=BSH.BytesPerSector
  SizeCluster=BSH.BytesPerSector*BSH.SectorsPerCluster
  If BSH.NbSectors=0 Then IsFat12=0
  Else
    If BSH.NbSectors/BSH.SectorsPerCluster>4096 Then
      IsFAT12=0
    Else
      IsFAT12=1
    End
  End
  If IsFAT12=1 Then FATMask=&H0FF0 Else FATMask=&HFFF0;
  NoFATPieceSect=&HFFFF
  CurDirBeg=0
Leave

\\\
    Lecture et ‚criture absolues
                                 \\\
Sub ReadAbsSect(Buf as Reference,N as LongWord,Nb as Word)
  Face,Track,Sector as Word
  No,TTL as Word \ Total Track Length \
Enter
  TTL=BSH.SectorsPerTrack*BSH.NbHeads
\ LES AX,[N]; MOV DX,ES \
  InLine &H86C4,N,&HC28C
\ DIV [TTL] \
  InLine &HB6F7,TTL
  Track=AX:No=DX
  Face=No/BSH.SectorsPerTrack
  Sector=No%BSH.SectorsPerTrack+1
  ReadSect(Buf,DriveNo,Face,Track,Sector,Nb)
Leave

Sub ReadCluster(Buf as Reference,No as Word)
  I as Word
Enter
\ Il doit y avoir mieux … faire que ‡a, mais c'est chiant.
  Le problŠme vient du fait que qd le #SectsPerTrack est
  impair et qu'on essaye de lire 1 cluster (2 secteurs,
  au moment o— j'ai trouv‚ le bug) … partir du dernier
  secteur, la fn BIOS merde pour la lecture du 2nd
  secteur (il faudrait qu'elle soit capable de
  changer de piste). \
  If BSH.SectorsPerTrack&1<>0 Then
    For I=0 To BSH.SectorsPerCluster-1 Do
       ReadAbsSect(Buf+I*BSH.BytesPerSector,
                   DataBeg+(DX,No*BSH.SectorsPerCluster)+I,
                   1
                  )
    Next
  Else
    ReadAbsSect(Buf,
                DataBeg+No*BSH.SectorsPerCluster,
                (DX,BSH.SectorsPerCluster)
               );
Leave

Sub WriteAbsSect(Buf as Reference,N as LongWord,Nb as Word)
  Face,Track,Sector as Word
  No,TTL as Word \ Total Track Length \
Enter
  TTL=BSH.SectorsPerTrack*BSH.NbHeads
\ LES AX,[N]; MOV DX,ES \
  InLine &H86C4,N,&HC28C
\ DIV [TTL] \
  InLine &HB6F7,TTL
  Track=AX:No=DX
  Face=No/BSH.SectorsPerTrack
  Sector=No%BSH.SectorsPerTrack+1
  WriteSect(Buf,DriveNo,Face,Track,Sector,Nb)
Leave

Sub WriteCluster(Buf as Reference,No as Word)
  I as Word
Enter
  If BSH.SectorsPerTrack&1<>0 Then
    For I=0 To BSH.SectorsPerCluster-1 Do
       WriteAbsSect(Buf+I*BSH.BytesPerSector,
                    DataBeg+(DX,No*BSH.SectorsPerCluster)+I,
                    1
                   )
    Next
  Else
    WriteAbsSect(Buf,
                 DataBeg+No*BSH.SectorsPerCluster,
                 (DX,BSH.SectorsPerCluster)
                );
Leave

\\\
    Gestion FATs
                 \\\
Type
    WordPtr is ^Word

Sub FlushDirtyFATPiece
Enter
  If IsFAT12=1 Then
    If FATPieceDirty=1 Then
      WriteAbsSect(FATPiece,NoFATPieceSect,1)
      WriteAbsSect(WordPtr(@FATPiece[BSH.BytesPerSector])^,NoFATPieceSect+1,1)
      FATPieceDirty=0
    End
  Else
    If FATPieceDirty=1 Then
      WriteAbsSect(FATPiece,NoFATPieceSect,1)
      FATPieceDirty=0
    End;
Leave

Sub ReadFATPiece12b(No as Word)
Enter
  If NoFATPieceSect<>No Then
    FlushDirtyFATPiece
    ReadAbsSect(FATPiece,No,1)
    ReadAbsSect(WordPtr(@FATPiece[BSH.BytesPerSector])^,No+1,1)
  End
  NoFATPieceSect=No
Leave

Sub ReadFATPiece16b(No as Word)
Enter
  If NoFATPieceSect<>No Then
    FlushDirtyFATPiece
    ReadAbsSect(FATPiece,No,1)
  End
  NoFATPieceSect=No
Leave

Def FATNext12b(N as Word) as Word
  NoSectFAT,OfsInTheFAT,OfsInTheBuf as Word
Enter
  OfsInTheFAT=N+N>>1
  NoSectFAT=FATBeg+OfsInTheFAT/BSH.BytesPerSector
  ReadFATPiece12b(NoSectFAT)
  OfsInTheBuf=OfsInTheFAT%BSH.BytesPerSector
  If N&1=0 Then
    Result=WordPtr(@FATPiece[OfsInTheBuf])^&&H0FFF
  Else
    Result=WordPtr(@FATPiece[OfsInTheBuf])^>>4
  End
Leave

Def FATNext16b(N as Word) as Word
  NoSectFAT,OfsInTheFAT,OfsInTheBuf as Word
Enter
  OfsInTheFAT=N<<1
  NoSectFAT=FATBeg+OfsInTheFAT/BSH.BytesPerSector
  ReadFATPiece16b(NoSectFAT)
  OfsInTheBuf=OfsInTheFAT%BSH.BytesPerSector
  Result=WordPtr(@FATPiece[OfsInTheBuf])^
Leave

Def FATNext(N as Word) as Word
Enter
  If IsFat12=1 Then Result=FATNext12b(N) Else Result=FATNext16b(N);
Leave

Sub FATSet12b(N,N' as Word)
  NoSectFAT,OfsInTheFAT,OfsInTheBuf as Word
Enter
  If N<2 Then Error("FATSet12b");
  OfsInTheFAT=N+N>>1
  NoSectFAT=FATBeg+OfsInTheFAT/BSH.BytesPerSector
  ReadFATPiece12b(NoSectFAT)
  OfsInTheBuf=OfsInTheFAT%BSH.BytesPerSector
  If N&1=0 Then
    WordPtr(@FATPiece[OfsInTheBuf])^&=&HF000
    WordPtr(@FATPiece[OfsInTheBuf])^|=N'
  Else
    WordPtr(@FATPiece[OfsInTheBuf])^&=&H000F
    WordPtr(@FATPiece[OfsInTheBuf])^|=N'<<4
  End
  FATPieceDirty=1
Leave

Sub FATSet16b(N,N' as Word)
  NoSectFAT,OfsInTheFAT,OfsInTheBuf as Word
Enter
  If N<2 Then Error("FATSet16b");
  OfsInTheFAT=N<<1
  NoSectFAT=FATBeg+OfsInTheFAT/BSH.BytesPerSector
  ReadFATPiece16b(NoSectFAT)
  OfsInTheBuf=OfsInTheFAT%BSH.BytesPerSector
  WordPtr(@FATPiece[OfsInTheBuf])^=N'
  FATPieceDirty=1
Leave

Sub FATSet(N,N' as Word)
Enter
  If IsFat12=1 Then FATSet12b(N,N') Else FATSet16b(N,N');
Leave

Def FATAlloc12b(N as Word) as Word
  OfsInTheBuf,EntriesPerFATMoins1,N',Val,BytesPerSector as Word
Enter
  Result=0
  EntriesPerFATMoins1=((BSH.SectorsPerFAT*BSH.BytesPerSector)/3)<<1-1
  OfsInTheBuf=1
  BytesPerSector=BSH.BytesPerSector
  ReadFATPiece12b(FATBeg)
  For N'=2 To EntriesPerFATMoins1 Do
    If N'&1=0 Then
      OfsInTheBuf+=2
      If OfsInTheBuf>=BytesPerSector Then
        ReadFATPiece12b(FATBeg+(N'+N'>>1)/BSH.BytesPerSector)
        OfsInTheBuf-=BytesPerSector
        ;
      Val=WordPtr(@FATPiece[OfsInTheBuf])^&&H0FFF
    Else
      OfsInTheBuf+=1
      If OfsInTheBuf>BytesPerSector Then
        ReadFATPiece12b(FATBeg+(N'+N'>>1)/BSH.BytesPerSector)
        OfsInTheBuf-=BytesPerSector
        ;
      Val=WordPtr(@FATPiece[OfsInTheBuf])^>>4
    End
    If Val=0 Then
      If N'&1=0 Then
        WordPtr(@FATPiece[OfsInTheBuf])^|=&H0FFF
      Else
        WordPtr(@FATPiece[OfsInTheBuf])^|=&HFFF0
      End
      FATPieceDirty=1
      If N<>0 Then FATSet12b(N,N');
      Result=N'
      N'=EntriesPerFATMoins1
    End
  Next
Leave

Def FATAlloc16b(N as Word) as Word
  OfsInTheBuf,EntriesPerFATMoins1,N',Val,BytesPerSector as Word
Enter
  Result=0
  EntriesPerFATMoins1=(BSH.SectorsPerFAT*BSH.BytesPerSector)>>1-1
  OfsInTheBuf=2
  BytesPerSector=BSH.BytesPerSector
  ReadFATPiece16b(FATBeg)
  For N'=2 To EntriesPerFATMoins1 Do
    OfsInTheBuf+=2
    If OfsInTheBuf>=BytesPerSector Then
      ReadFATPiece16b(FATBeg+(N'<<1)/BSH.BytesPerSector)
      OfsInTheBuf=0
      ;
    Val=WordPtr(@FATPiece[OfsInTheBuf])^
    If Val=0 Then
      WordPtr(@FATPiece[OfsInTheBuf])^=&HFFFF
      FATPieceDirty=1
      If N<>0 Then FATSet16b(N,N');
      Result=N'
      N'=EntriesPerFATMoins1
    End
  Next
Leave

Def FATAlloc(N as Word) as Word
Enter
  If IsFat12=1 Then Result=FATAlloc12b(N) Else Result=FATAlloc16b(N);
Leave

Sub ReWriteFATString(N as Word)
  N' as Word
Enter
  If N>=2 Then While N&FATMask<>FATMask Do
                 N'=FATNext(N)
                 FATSet(N,0)
                 N=N'
               Wend;
Leave

\\\
    Divers
           \\\
Type
    IntPtr is ^Int
    AccessPtr is Array[0..1] Of Int

Sub LCNPrintS(L,C,Color as Int; Str as IntPtr,Len as Int)
Var
   Scr as IntPtr
   ScrPtr,StrPtr as ^AccessPtr
Enter
  ScrPtr=Pointer(@Scr)
  ScrPtr^[0]=(L-1)*160+(C-1)*2
  ScrPtr^[1]=Int(&HB800)
  StrPtr=Pointer(@Str)
  While Len<>0 Do
    Scr^=Color*&H100+(Str^ & &HFF)
    ScrPtr^[0]=ScrPtr^[0]+2
    StrPtr^[0]=StrPtr^[0]+1
    Len=Len-1
  Wend
Leave

Const
     LARGEUR=32,HAUTEUR=16

Sub PrintSector(L0,C0 as Int; Buf as Pointer)
Var
   L as Int
   IP as IntPtr
Enter
  IP=Pointer(@Buf)
  For L=0 To HAUTEUR-1 Do
    LCNPrintS(L0+L,C0,7,Buf,LARGEUR)
    IP^=IP^+LARGEUR
  Next
Leave

Sub PrintStats
Enter
  ClearScreen
  PrintPSect
  PrintCR
  PrintS "BSH de la 1st partition active"
  PrintCR
  PrintBSH
Leave

Enter
\ Init DriveNo \
  DriveNo=&HFF
\ Init FAT cache \
  NoFATPieceSect=&HFFFF
  FATPieceDirty=0
  PrintS "Partition start done":PrintCR
Leave