From: ivie@cc.usu.edu (CP/M lives!)
Newsgroups: comp.os.cpm
Subject: PX-8 ROM generator 3/3: PXFS.PAS
Date: 23 Jul 92 11:59:07 MDT
Organization: Utah State University

{****************************************************************************
*
* This file implements just enough of a CP/M compatible file-system to
* create files in a disk image of a ROM. In particular, it supports only
* the following operations:
*
*       - Make File
*       - Write Sequential
*       - Close File
*
* These routines assume that they can call:
*
*       - ReadRom( Block_Number : Integer, Var Buffer );
*       - WriteRom( Block_Number : Integer, Buffer );
*
* They also assume that Buffer_Type is predefined as a buffer that can be
* written.
*
***************************************************************************}

Type
  FCB_Name = Array[ 0..10 ] of Char;      { File name and extension }
  Allocation_Map = Array[ 0..15 ] of Byte;{ Disk allocation map }
  Memory_FCB = Record
    DRV : Byte;             { Drive number; high bit writes a deleted FCB
                              when closed. }
    NAME : FCB_Name;        { File name and extension }
    EX : Byte;              { Extent number }
    FIX : Byte;             { Position of this FCB on disk; first FCB is #0 }
    CR : Byte;              { Current record position; high nybble =
                              index into DM, low nybble = record in that one }
    NR : Byte;              { Number of records owned by this FCB }
    DM : Allocation_Map;    { Disk allocation map }
  End;

  Disk_FCB = Record
    ET : Byte;              { Entry type: $E5 = deleted file }
    NAME : FCB_Name;        { File name and extension }
    EX : Byte;              { Extent number }
    Reserved1,
    Reserved2 : Byte;       { Reserved; not used }
    NR : Byte;              { Number of records owned by this FCB }
    DM : Allocation_Map;    { Disk allocation map }
  End;

Const
  Vector_Valid : Boolean = False; { If true, Vector contains a valid
                                    representation of the owned clusters on
                                    the disk. }

Var
  NumClusters : Byte;       { Number of clusters on the drive. }
  FCBBuffer : Array [ 0..3 ] of Disk_FCB;
  Vector : Array [ 0..127 ] of Boolean; { Bit map for drive. }

Procedure Zero_Directory( Blocks, FCBs : Integer );
Label 1, 2, 999;
Var Block : Integer;
Begin

1:
  Vector_Valid := False;
  Block := 0;
  FillChar( FCBBuffer, 128, $E5 );
  FCBBuffer[ 0 ].Name[ 0 ] := Chr( $37 );
  FCBBuffer[ 0 ].Name[ 1 ] := Chr( Blocks shr 3 );
  FCBBuffer[ 0 ].DM[ 6 ] := FCBs;
  Goto 2;

2:
  Write_Rom( Block, FCBBuffer );
  FCBs := FCBs - 4;
  Block := Block + 1;
  If( FCBs > 0 ) Then Goto 2;
  Goto 999;

999:
End;

Procedure Get_FCB( Var FCB : Memory_FCB );
Label 1, 2, 3, 4, 999;
Var
  Block : Integer;
  FIX : Byte;
Begin

1:
  Block := FCB.FIX shr 2;
  Read_ROM( Block, FCBBuffer );
  FIX := FCB.FIX and 3;
  If( FCBBuffer[ FIX ].ET = $E5 ) Then Goto 3;
  Goto 2;

2:
  FCB.DRV := FCB.DRV and $7f;
  Goto 4;

3:
  FCB.DRV := FCB.DRV or $80;
  Goto 4;

4:
  FCB.NAME := FCBBuffer[ FIX ].NAME;
  FCB.EX := FCBBuffer[ FIX ].EX;
  FCB.NR := FCBBuffer[ FIX ].NR;
  FCB.DM := FCBBuffer[ FIX ].DM;
  Goto 999;

999:
End;

Procedure Close_File( FCB : Memory_FCB );
Label 1, 2, 3, 4, 999;
Var
  Block : Integer;
  FIX : Byte;
Begin

1:
  Block := FCB.FIX shr 2;
  Read_Rom( Block, FCBBuffer );
  FIX := FCB.FIX and 3;
  if( ( FCB.DRV and $80 ) <> 0 ) Then Goto 3;
  Goto 2;

2:
  FCBBuffer[ FIX ].ET := 0;
  Goto 4;

3:
  FCBBuffer[ FIX ].ET := $E5;
  Goto 4;

4:
  FCBBuffer[ FIX ].Reserved1 := 0;
  FCBBuffer[ FIX ].Reserved2 := 0;
  FCBBuffer[ FIX ].NAME := FCB.NAME;
  FCBBuffer[ FIX ].EX := FCB.EX;
  FCBBuffer[ FIX ].NR := FCB.NR;
  FCBBuffer[ FIX ].DM := FCB.DM;
  Write_Rom( Block, FCBBuffer );
  Goto 999;

999:
End;

Procedure Init_FCB( Var FCB : Memory_FCB );
Begin
  FCB.DRV := FCB.DRV and $7f;
  FCB.FIX := 0;
  FCB.CR := 0;
  FCB.NR := 0;
  FillChar( FCB.DM, 16, 0 );
End;

Function Make_File( Var FCB : Memory_FCB ) : Boolean;
Label 1, 2, 3, 4, 5, 6, 7, 999;
Var
  FIX : Byte;
  Block : Integer;
  FCBs : Integer;
Begin

1:
  Init_FCB( FCB );
  FIX := 1;
  Block := 0;
  Read_Rom( Block, FCBBuffer );
  FCBs := FCBBuffer[ 0 ].DM[ 6 ];
  Goto 2;

2:
  If( ( FIX and 3 ) = 0 ) Then Goto 3;
  Goto 4;

3:
  Block := Block + 1;
  Read_Rom( Block, FCBBuffer );
  Goto 4;

4:
  If( FCBBuffer[ FIX and 3 ].ET = $E5 ) Then Goto 5;
  Goto 6;

5:
  FCB.FIX := FIX;
  Close_File( FCB );
  Make_File := True;
  Goto 999;

6:
  FIX := FIX + 1;
  If( FIX < FCBs ) Then Goto 2;
  Goto 7;

7:
  Make_File := False;
  Goto 999;

999:
End;

Procedure Build_Vector;
Label 1, 2, 3, 4, 5, 6, 7, 8, 9, 999;
Var
  FIX : Byte;
  Block : Integer;
  FCBs : Byte;
  I, DMIX : Integer;
Begin

1:
  FIX := 1;
  Block := 0;
  For I := 0 to 127 do
    Vector[ I ] := False;
  Read_Rom( Block, FCBBuffer );
  NumClusters := Ord( FCBBuffer[ 0 ].Name[ 1 ] );
  FCBs := FCBBuffer[ 0 ].DM[ 6 ];
  Goto 2;

2:
  If( ( FIX and 3 ) = 0 ) Then Goto 3;
  Goto 4;

3:
  Block := Block + 1;
  Read_Rom( Block, FCBBuffer );
  Goto 4;

4:
  If( FIX < FCBs ) Then Goto 6;
  Goto 5;

5:
  Vector[ 0 ] := True;
  Vector_Valid := True;
  Goto 999;

6:
  If( FCBBuffer[ FIX and 3 ].ET = $E5 ) Then Goto 9;
  Goto 7;

7:
  DMIX := 0;
  Goto 8;

8:
  Vector[ FCBBuffer[ FIX and 3 ].DM[ DMIX ] ] := True;
  DMIX := DMIX + 1;
  If( DMIX < 16 ) Then Goto 8;
  Goto 9;

9:
  FIX := FIX + 1;
  Goto 2;

999:
End;

Function Allocate( Var FCB: Memory_FCB ) : Boolean;

Label 1, 2, 3, 4, 5, 6, 7, 999;
Var
  CurCluster : Byte;
Begin

1:
  If( Vector_Valid ) Then Goto 3;
  Goto 2;

2:
  Build_Vector;
  Goto 3;

3:
  CurCluster := 0;
  Goto 4;

4:
  If( Vector[ CurCluster ] ) Then Goto 5;
  Goto 6;

5:
  CurCluster := CurCluster + 1;
  If( CurCluster < NumClusters ) Then Goto 4;
  Goto 7;

6:
  FCB.DM[ FCB.CR shr 4 ] := CurCluster;
  Vector[ CurCluster ] := True;
  Allocate := True;
  Goto 999;

7:
  Allocate := False;
  Goto 999;

999:
End;

Function Write_Sequential( Var FCB : Memory_FCB; Var Buffer ) :
  Boolean;

Label 1, 2, 3, 4, 5, 6, 7, 8, 999;
Var
  Block : Integer;
Begin

1:
  If( FCB.NR > 127 ) Then Goto 2;
  Goto 3;

2:
  Close_File( FCB );
  FCB.EX := FCB.EX + 1;
  If( Make_File( FCB ) ) Then Goto 3;
  Goto 7;

3:
  Block := FCB.DM[ FCB.CR SHR 4 ] * 8;
  If( Block = 0 ) Then Goto 4;
  Goto 5;

4:
  If( Allocate( FCB ) ) Then Goto 3;
  Goto 8;

5:
  Write_Rom( Block + ( FCB.CR and 7 ), Buffer );
  FCB.NR := FCB.NR + 1;
  FCB.CR := FCB.CR + 1;
  Write_Sequential := True;
  If( ( FCB.CR and 7 ) = 0 ) Then Goto 6;
  Goto 999;

6:
  FCB.CR := ( FCB.CR + $10 ) and $F0;
  Goto 999;

7:
  Writeln( '%Write_Sequential: Directory full.' );
  Write_Sequential := False;
  Goto 999;

8:
  Writeln( '%Write_Sequential: ROM full.' );
  Write_Sequential := False;
  Goto 999;

999:
End;