--last changed August 13, 1982 4:28 PM by Glen Williams
--now return firstPage rather than lastPage when not proceeding to rewrite pages.

DIRECTORY
InlineDefs: FROM "inlinedefs" USING [BITAND, BITOR, BITSHIFT],
SystemDefs: FROM "systemdefs" USING [AllocateHeapNode, FreeHeapNode],
TridentDefs: FROM "tridentdefs";

TfsWrite: PROGRAM IMPORTS SystemDefs, TridentDefs, InlineDefs EXPORTS TridentDefs =

BEGIN OPEN SystemDefs, TridentDefs, InlineDefs;

TfsWritePages: PUBLIC PROCEDURE [disk: tfsdskPtr,
caS: DESCRIPTOR FOR ARRAY OF POINTER,
daS: DESCRIPTOR FOR ARRAY OF PAGE,
fp: FP, firstPage, lastPage: INTEGER, lastAction: WORD, numcharsPtr: POINTER, lastnumChars: CARDINAL, fixedCA: POINTER, dummy1: UNSPECIFIED, errorRtn: ErrorRoutType, dummy2: UNSPECIFIED, hintLastPage: INTEGER] RETURNS [resPage: INTEGER] =

-- This procedure acts similarly to TfsActOnPages (which reads or writes a specified range of pages). TfsWritePages only writes, however, and must be used if a filed will be extended. Note also that daS[firstPage-1] will be referenced (remembering that files have forward and backward links) except when the label of firstPage doesn’t need to be rewritten.


BEGIN
i, firstNewPage: INTEGER;
numChars: CARDINAL ← 0;
cb: cbPtr;
cbz: cbzPtr;
temp: POINTER;
prevPagePtr: POINTER TO PAGE;

-- The first six arguments should have valid values. The others may be NIL, zero, or some No-Op routine, as appropriate. Three of the others are forced to non-zero defaults. Also, a real procedure has to be passed through errorRtn.


--IF lastnumChars = 0 THEN lastnumChars ← TFSwordsPerPage*2;---- set defaults

IF lastAction = 0 THEN lastAction ← dcWriteD;
IF numcharsPtr = NIL THEN numcharsPtr ← @numChars;

-- Rewrite existing pages in the file before worrying about adding new ones.
-- The following is code to help rename a file. To do so TfsActOnPages is used to read several pages into memory and to build a daS array. Then this procedure is called with lastAction = -1, and all those pages (and labels) are rewritten.

IF lastAction # 177777B THEN
BEGIN
IF daS[firstPage] = fillInDA-- 1st page is to be appended
THEN firstNewPage ← firstPage-- else rewrite some of the pages
ELSE
{firstPage ← TfsActOnPages[disk, caS, daS, fp, firstPage, lastPage, dcWriteD, numcharsPtr, lastAction, fixedCA,
DefaultTfsCleanupRtn, errorRtn, FALSE, hintLastPage];
IF firstPage = lastPage AND (lastAction # dcWriteD OR numcharsPtr↑ = lastnumChars) THEN RETURN [firstPage];
firstNewPage ← firstPage + 1;
};

-- Here’s the code to assign more pages to the file. Three things are done here:
-- 1. TfsAssignDisk page is called to allocate the pages using the BitMap.
-- 2.TfsActOnPages is used to read the labels, which are checked by CheckFreePage to see if they’re free.
-- 3.We go through a loop to see if all the assigned pages are free (CheckFreePage stuffs fillInDA if not). For any not free, we start
--again at 1.

DO --Assign any new pages
FOR i IN [firstNewPage..lastPage]
DO prevPagePtr ← @daS[i]-1;
daS[i] ← TfsAssignDiskPage[disk, prevPagePtr↑, FALSE];
IF daS[i] = eofDA THEN ERROR NoMorePages;
ENDLOOP;

-- Read the labels of the assigned pages and check
[] ← TfsActOnPages[disk, caS, daS, freePageFP, firstNewPage, lastPage, dcReadLnD, NIL, dcReadLnD, NIL, CheckFreePage, errorRtn, FALSE, 0];

--Assign new pages if not free
FOR i IN [firstNewPage..lastPage]
DO daS[firstNewPage] ← daS[i];
IF daS[i] # fillInDA
THEN firstNewPage ← firstNewPage + 1;
ENDLOOP;

IF firstNewPage - lastPage > 0 THEN EXIT;
ENDLOOP;-- end of 3-phase assignment loop
END;
-- of lastAction # -1 block

-- All pages have been checked. Write labels and data, including possibly revising lastnumChars.

cbz ← AllocateHeapNode[cbZoneLength];
TfsInitializeCBStorage[disk,cbz,firstPage,cbZoneLength,Wretry,errorRtn];

BEGIN ENABLE Wretry => RETRY;
--start this block over when TfsGetCb raises this SIGNAL
FOR i IN [cbz.currentPage..lastPage]
DO
cb ← TfsGetCb[disk,cbz,FALSE,FALSE];

-- set up eofDA as the page after the End of File
IF ((i = lastPage AND lastnumChars # TFSwordsPerPage*2) OR (daS[i+1] = fillInDA))
THEN daS[i+1] ← eofDA;

-- Set up the label to be written on this page
prevPagePtr ← @daS[i]-1;
cb.label.next ← TfsRealDA[disk, daS[i+1]];
cb.label.previous ← TfsRealDA[disk, prevPagePtr↑];
cb.label.numChars ← (IF i = lastPage THEN lastnumChars ELSE TFSwordsPerPage*2);

-- Perform the I/O
IF fixedCA # NIL THEN temp ← fixedCA
ELSE temp ← caS[i];
TfsDoDiskCommand[disk,cb,temp,daS[i],fp,i,dcWriteLD,NIL];
ENDLOOP;--of FOR i IN [cbz.currentPage..lastPage]

WHILE cbz.head # NIL
DO
[] ← TfsGetCb[disk,cbz,FALSE,FALSE];-- wait
ENDLOOP;-- End of wait loop
END;
--of BEGIN ENABLE Wretry => RETRY
FreeHeapNode[cbz];
RETURN[lastPage];
END;
-- of TfsWritePages

TfsAssignDiskPage: PUBLIC PROCEDURE [disk: tfsdskPtr, diskAddr: PAGE, test: BOOLEAN]
RETURNS [resPage: PAGE] =

-- Assigns in a sequential manner, in order of increasing virtual disk addresses. Second argument is a VDA previously assigned; the code tries to assign pages sequentially in this case. However, for a new file, the VDA passed is eofDA; in this case, the code resumes looking in the bit table where it last left off trying to allocate a file. The idea is to reduce bit table scanning time and also page in/outs.
--
--Test is a special argument to determine if a page is available without
--
assigning it. Result is vda if next page is available, else 0.
--
BEGIN
ddMgr: ddMgrPtr;-- pointer to DD mgr object
diskBTaddr: POINTER;
buffer: DESCRIPTOR FOR ARRAY [0..TFSwordsPerPage]OF WORD;
free: BOOLEAN;-- is this page free?
wa: WordNumInPage;--word address
pa: PageNum;-- page address
vda: VDA;-- virtual disk address
bitMask: WORD;-- 1 bit on
i, loopsize: CARDINAL;



ddMgr ← disk.tfskd.ddMgr;-- get the pointer to the DD manager
TfsLockDD[ddMgr, disk];-- lock the disk descriptor

IF diskAddr = eofDA THEN-- this means start where we left off
diskAddr ← disk.tfskd.lastPageAlloc-- get last page allocated
ELSE diskAddr ← diskAddr + 1;

vda ← LOOPHOLE[diskAddr];-- data convert

--We may have to do (bit-table pages) + 2 iterations

loopsize ← BITSHIFT[disk.tfskd.kdh.diskBTsize, Right*TFSlnWordsPerPage]+3;
FOR i IN [1..loopsize]
DO-- At top of loop vda
IF vda.pageAndWord >= disk.tfskd.kdh.diskBTsize-- = vda to be examined next
THEN {vda.pageAndWord ←0; vda.bitNum ←0};
pa ← vda.pageNum;-- get the right bitmap page and read it
diskBTaddr ← TfsReadDDPage[ddMgr,disk,pa+lTfsDDPreamble];
buffer ← DESCRIPTOR[diskBTaddr, TFSwordsPerPage];

-- Test the bit corresponding to vda. FAIL => test the remainder of the word 1 bit at a time.

wa ← vda.wordNumInPage;-- get the right word in the page
bitMask ← oneBits[vda.bitNum];-- get the bit in the word
DO-- search from here down for a free page
free ← BITAND[buffer[wa],bitMask]=0;-- is this page free?

IF test THEN
BEGIN-- we just wanted to look
TfsUnlockDD[ddMgr,disk,FALSE];-- don’t set dirty flag
IF free THEN resPage ← LOOPHOLE[vda]
ELSE resPage ← 0;
RETURN [resPage];-- return from test
END;

IF free THEN-- assign the page if it’s free
BEGIN
buffer[wa] ← BITOR[buffer[wa], bitMask];
disk.tfskd.kdh.freePages ← disk.tfskd.kdh.freePages - 1;
disk.tfskd.lastPageAlloc ← LOOPHOLE[vda];
TfsUnlockDD[ddMgr,disk,TRUE];
RETURN[LOOPHOLE[vda]];-- return page number assigned
END;-- if not free continue looking thru the bitmap word
bitMask ← BITSHIFT[bitMask, Right*1];
IF bitMask = 0 THEN EXIT;-- No mre bits

diskAddr ← diskAddr + 1;-- get next address
vda ← LOOPHOLE[diskAddr];
ENDLOOP;-- of search in pageAndWord

DO-- look through rest of page
wa ← wa + 1;
IF wa >= TFSwordsPerPage OR buffer[wa] # 177777B THEN EXIT;
ENDLOOP;

--Now wa addresses a word # -1 or wa = TfsWordsPerPage
IF wa >= TFSwordsPerPage THEN
BEGIN-- Finished with this page of bitmap
pa ← pa + 1;-- Set to look at next page
--vda ← VDA[pageNum: pa, wordNumInPage: 0, bitPos: 0];---- Construct a vda
vda.pageNum ← pa; vda.wordNumInPage ← 0; vda.bitPos ← 0;
LOOP;-- Go get next page
END;

--buffer[wa] has one or more 0 bits
FOR j: INTEGER IN [0..16) DO-- A zero bit means free page
IF 0=BITAND[oneBits[j],buffer[wa]]
THEN BEGIN
vda.pageNum ← pa; vda.wordNumInPage ← wa; vda.bitPos ← j;--Found, construct vda
EXIT;-- Drop out since search is done
END;
ENDLOOP;-- Look for first free bit in buffer[wa]

ENDLOOP;--of FOR i IN [1..loopsize]; Go assign page or read bitmap

TfsUnlockDD[ddMgr,disk,FALSE];-- no pages available
RETURN[eofDA];
END;
-- of TfsAssignDiskPage

CheckFreePage: PROCEDURE [disk: tfsdskPtr, cb: cbPtr, cbz: cbzPtr] =
-- This procedure is used as a cleanup routine in TfsWritePages in a call
-- to TfsActOnPages. fillInDA is stored in the daS array for pages which
-- are not free.

BEGIN
fid: DESCRIPTOR FOR ARRAY [0..lFID) OF WORD;
pageindex,i: INTEGER;

fid ← DESCRIPTOR[cb.kcb.blockL.addr, lFID];-- Describe the fileid

FOR i IN [0..lFID) DO
IF fid[i] # 177777B THEN-- Used to be freePageFID[i]
BEGIN
pageindex ← cb.truePageNumber;
cbz.daS[pageindex] ← fillInDA;
disk.tfskd.nBTErrors ← disk.tfskd.nBTErrors + 1;
EXIT;-- leave the DO-loop
END;
ENDLOOP;
END;
-- of CheckFreePage

NoMorePages: PUBLIC ERROR = CODE;-- disk full
Wretry: SIGNAL = CODE;

END.
-- of TfsWrite.mesa
--last changed September 25, 1981 2:01 PM by Glen Williams
--dropped an arg to TfsInitializeCBStorage

--last changed July 17, 1981 11:49 AM by Glen Williams
--added ’j’ to TfsAssignDiskPage
--
changed call on TfsRealDA in TfsWritePage
--changed Wretry scoping to include a wait for the command block at the end of TfWritePages. Also made it a BEGIN..END rather
-- than a DO..ENDLOOP.

--last changed August 4, 1982 6:00 PM by Glen Williams
--no longer default lastnumChars to 2048 if came in as a zero (as stated in BFS/TFS documentation).