//msymb.bcpl get "mdecl.d" get "streams.d" external [ // OS CallSwat; DoubleAdd; MoveBlock; Zero Puts; Wss; Wns; ReadBlock; WriteBlock; OpenFile; Closes // MASM BinScan; SymbKeyComp; GetBodySize; FindBlock; @BlockTable Mag; Min; MoveUp; StrSize; SearchBlock // MIOC SimpleTexttoDVec // MPARSE ParseAddress // MMPRGN DoubleNeg // MCMD DisplayError // MINIT0 BlockRecFP // Overlay package IndexedPageIO // STATE package GetStorage // Machine dependent @MEMNAM // Defined here EvalAText; SearchBlocks; FindInTable; TVtoString; UpdateRcd SaveRcdFile; @BHsize; @StringVec // Defined here for init only MakeNewBlock; GetFreeBlock @HeadBlock; Map; BlockUpdateRcd; @LastBlockAddr; @NextM @LastM1; @LastM2; NextFreePage; PBCalls; GBCalls ] manifest [ IndexedPageRead = 0; IndexedPageWrite = -1 BlockSize = NPagesPerStandardBlock*PageSize ] static [ @StringVec // used by TVtoString to hold string @BHsize = size BH/16 @HeadBlock; BlockBreak = -(BlockSize/4) @LastBlockAddr; @NextM = (size BH/16)+1 @LastM2 = -(size BH/16); @LastM1 = -(size BH/16) NextFreePage = 1; PBCalls = 0; GBCalls = 0 Map DebugSym = false ChkBlockCount = 0 ] let EvalAText(TV, lvX, AVal, ifExpectMore) = valof [ if TV!0 < rv lvX then resultis false let PV = vec 72; PV!0 = 72 let Y = 1 unless ParseAddress(TV, lvX, PV, lv Y) then resultis false let NmTV = PV + 1 //TV for symbol let SgnTV = NmTV + NmTV!0 + 1 //TV for sign let OctTV = SgnTV + SgnTV!0 + 1 //TV for offset (octal) let SymbDef = vec size Symb/16 let SizeSymbDef = nil unless FindInTable((NmTV!0 eq 0 ? "IM",TVtoString(NmTV)),SymbDef, lv SizeSymbDef) do resultis false let Oct = vec 2; SimpleTexttoDVec(OctTV,32,Oct) if (SgnTV!0 eq 1) & (SgnTV!1 eq $-) then DoubleNeg(Oct) NmTV!0 = Min(maxsymlen,NmTV!0) let SName = TVtoString(NmTV) MoveBlock(lv AVal>>AVal.SName,SName,(maxsymlen/2)+1) AVal>>AVal.X = SymbDef>>Symb.A.X AVal>>AVal.TypeStorage = MemTypeStorage MoveBlock(lv AVal>>AVal.Offset,Oct,2) switchon SymbDef>>Symb.A.Type into [ case MemSymb: AVal>>AVal.Sign = 0 MoveBlock(lv AVal>>AVal.Addr,Oct,2) endcase case RegSymb: AVal>>AVal.TypeStorage = RegTypeStorage endcase case AddrSymb: AVal>>AVal.Sign = 1 (lv AVal>>AVal.Addr)!0 = 0 (lv AVal>>AVal.Addr)!1 = SymbDef>>Symb.A.A2 DoubleAdd(lv AVal>>AVal.Addr,Oct) endcase case LAddrSymb: AVal>>AVal.Sign = 1 MoveBlock(lv AVal>>AVal.Addr,Oct,2) DoubleAdd(lv AVal>>AVal.Addr,lv SymbDef>>Symb.LA.A1) endcase default: CallSwat() ] test ifExpectMore ifso if rv lvX le TV!0 then rv lvX = rv lvX + 1 // skip seperator ifnot if rv lvX < TV!0 then resultis false resultis true ] //Search all of the symbols to find the nearest address le a particular //location in a particular memory. Output the name and offset to the //given stream. and SearchBlocks(S,MemX,AVec,IMA; numargs NA) be [ let Addr = AVec!1 let BName = vec 20 MoveBlock(BName,MEMNAM!MemX,StrSize(MEMNAM!MemX)) let BOffset = vec 0; BOffset!0 = Addr //Construct table of already-checked blocks let CheckedBlocks = vec 40; Zero(CheckedBlocks,40) let BlockAddr = nil //Search in-core blocks first for I = 1 to BlockTable!0 do [ let Block = BlockTable!I BlockAddr = Block>>BH.BlockAddr if BlockAddr > 0 do [ SearchBlock(Block,BOffset,BName,Addr,MemX) CheckedBlocks!0 = CheckedBlocks!0 + 1 CheckedBlocks!(CheckedBlocks!0) = BlockAddr ] ] //Now do not-in-core blocks for I = BHsize to HeadBlock>>BH.LastPntr do [ let Sym = HeadBlock+(HeadBlock!I) BlockAddr = Sym!(StrSize(Sym)) for J = 1 to CheckedBlocks!0 do [ if CheckedBlocks!J eq BlockAddr then goto NoChk ] if FindBlock(BlockAddr) eq 0 do [ SearchBlock(GetBlock(BlockAddr),BOffset,BName,Addr,MemX) CheckedBlocks!0 = CheckedBlocks!0 + 1 CheckedBlocks!(CheckedBlocks!0) = BlockAddr ] NoChk: ] if (BOffset!0 ne 0) & (NA > 3) do [ let Disp = Addr-IMA if (Disp ge -3) & (Disp le 3) do [ test Disp eq 0 ifso [ Puts(S,$.); return ] ifnot [ test Disp > 0 ifso Wss(S,".+") ifnot [ Wss(S,".-"); Disp = -Disp ] Wns(S,Disp,0,8); return ] ] ] Wss(S,BName) if BOffset!0 ne 0 do [ Puts(S,$+); Wns(S,BOffset!0,0,8) ] ] //This has been hand-coded //and SearchBlock(Block,BOffset,BName,Addr,MemX) be //[ let LastPntr = Block>>BH.LastPntr // let Sym,SymSize,TypePtr,Offset = nil,nil,nil,nil // for I = BHsize to LastPntr do // [ Sym = Block+(Block!I) // SymSize = StrSize(Sym) // TypePtr = Sym+SymSize // if (TypePtr>>Symb.A.Type eq AddrSymb) & // (TypePtr>>Symb.A.X eq MemX) do // [ Offset = Addr - TypePtr>>Symb.A.A2 // if (Offset < BOffset!0) & (Offset ge 0) do // [ MoveBlock(BName,Sym,SymSize); BOffset!0 = Offset ] // ] // ] //] //**long address case deimplemented // let AVec = vec 1 // switchon TypePtr>>Symb.A.Type into // [ //default: loop //case AddrSymb: if TypePtr>>Symb.A.X ne MemX then loop // AVec!0 = 0; AVec!1 = TypePtr>>Symb.A.A2 // endcase //case LAddrSymb: if TypePtr>>Symb.LA.X ne MemX then loop // MoveBlock(AVec,lv TypePtr>>Symb.LA.A1,2) // endcase // ] // DoubleAdd(AVec,NAddr); DoubleNeg(AVec) // if (AVec!0 eq 0) & (AVec!1 < BOffset!1) do // [ MoveBlock(BOffset,AVec,2) // MoveBlock(BName,Sym,SymSize) // ] // ] //] // useful procedure and TVtoString(TV) = valof // can only form one string at a time [ let Sn = 255 if TV!0 < Sn then Sn = TV!0 StringVec>>lh = Sn; StringVec>>rh = TV!1 let X = 2; let Y = 1 while X le Sn do [ StringVec!Y = (TV!X lshift 8) % (TV!(X+1) & #377) Y = Y+1; X = X+2 ] if Sn & 1 eq 0 then StringVec!(Y-1) = StringVec!(Y-1) & #177400 resultis StringVec ] //These have been hand-coded // Returns index of greatest record in block with key le Key // if equal returns + index; if not equal returns - index // assumes SymbKeyComp(K1, K2) roughly is "K1 - K2" //and BinScan(Block, Key) = valof //[ let k,l = BHsize,Block>>BH.LastPntr // let C = SymbKeyComp(Key, Block+Block!l) // if C ge 0 then resultis (C > 0 ? -l, l) //Now know (symbol at k le Key) & (Key < symbol at l) // while k < l do // [ let m = (k+l) rshift 1 // C = SymbKeyComp(Key,Block+Block!m) // if C eq 0 then resultis m // test C < 0; ifso l = m; ifnot test k ne m // ifso k = m; ifnot resultis -k // ] // resultis -k //] //and SymbKeyComp(K1, K2) = valof // "K1" - "K2" //[ let N1, N2 = K1>>lh, K2>>lh // if N1 eq 0 then // resultis (N2 eq 0? 0, -1) // empty string preceeds all others // if N2 eq 0 then resultis +1 // empty string wins // let Dif = K1>>rh - K2>>rh // if Dif ne 0 then resultis Dif // let Nmin = (N1>BH.BlockAddr if Block>>BH.LastPntr < BHsize then CallSwat() unless BlockUpdateRcd(HeadBlock,LastM1,Block + Block!BHsize, lv Block>>BH.BlockAddr,1) do CallSwat("HeadBlock full") LastM2 = BinScan(Block,Key) unless BlockUpdateRcd(Block,LastM2,Key,Body,BodySize) do CallSwat() ] LastM2 = -(Mag(LastM2))-1 NextM = -LastM2 eq Block>>BH.LastPntr ? (-LastM1-1),(-LastM2+1) ] // Local Procedures // if M ge 0, updates record with index M // if M < 0 , inserts a record following index -M and BlockUpdateRcd(Block, M, Key, Body, BodySize) = valof [ let KeySize = StrSize(Key) let RcdSize = KeySize + BodySize let Plast = Block>>BH.LastPntr let FirstR = Block>>BH.FirstRcd - RcdSize test M ge 0 //Match existing Key? ifso [ let Lim = Block!M let OldSize = KeySize+(Block eq HeadBlock ? 1, GetBodySize(Block+Lim+KeySize)) BlUpT1: FirstR = FirstR + OldSize if OldSize eq RcdSize do [ MoveBlock(Block+Lim+KeySize,Body,BodySize) Block>>BH.Dirty = true; resultis true ] if OldSize < 0 then CallSwat() if FirstR le Plast+1 then resultis false DisplayError(Key,"Redefine") //Squeeze out old block MoveUp(Block+OldSize+Lim-1,Block+Lim-1,Lim-(FirstR+RcdSize-OldSize)) for I = BHsize to Plast do if Block!I < Lim then Block!I = (Block!I) + OldSize ] ifnot [ if FirstR le Plast+1 then resultis false MoveUp(Block+Plast+1,Block+Plast,Plast+M) M = -M + 1 Block>>BH.LastPntr = Plast+1 ] Block!M = FirstR let NewR = Block + FirstR MoveBlock(NewR, Key, KeySize) MoveBlock(NewR + KeySize, Body, BodySize) Block>>BH.FirstRcd = FirstR Block>>BH.Dirty = true // if DebugSym then ChkBlock(Block) resultis true ] //Split Block by first freeing another block (via PutBlock if busy & dirty) //and then copying part of the full block into the free one. The M2 arg is //the index of the greatest record le the one being inserted. //Returns a pointer to the new block. //**Note: Algorithm must never leave an empty block. //The block managing algorithm uses all the available blocks. When they //are all dirty it cycles through the first two blocks repeatedly. and BlockSplit(Block,M2) = valof [ let OtherBlock = GetFreeBlock(FindFreeBlock(Block)) //First sort Block into OtherBlock, so that the records will be in order, //and compute the record at which to split the block. //Then move the records above the split point back to Block, winding //up with low addresses in OtherBlock, high in Block. let LastPntr,FirstRcd = Block>>BH.LastPntr,Block>>BH.FirstRcd let OrigLast,OrigFirstRcd,PR = LastPntr,FirstRcd,BlockSize let V = vec size BH/16 MoveBlock(V,OtherBlock,BHsize) //Interchange block headers MoveBlock(OtherBlock,Block,BHsize) MoveBlock(Block,V,BHsize) for I = BHsize to OrigLast do [ let R = Block + (Block!I) //Pointer to next record let Size = StrSize(R) //Key size Size = Size+GetBodySize(R+Size) //Record size if Size < 0 then CallSwat() if ((I-PR) ge BlockBreak) & (I ge M2) then [ LastPntr,FirstRcd,M2 = I-1,PR,BlockSize ] PR = PR-Size MoveBlock(OtherBlock+PR,R,Size) //Copy block OtherBlock!I = PR //New pointer ] if PR ne OrigFirstRcd then CallSwat() //Copy stuff beyond the split point back into Block. let NPtrs = OrigLast - LastPntr let NRcdWds = FirstRcd - OrigFirstRcd if (NPtrs eq 0) % (NRcdWds eq 0) then CallSwat() MoveBlock(Block+BlockSize-NRcdWds,OtherBlock+OrigFirstRcd,NRcdWds) MoveBlock(Block+BHsize,OtherBlock+LastPntr+1,NPtrs) let X,Y = Block+NPtrs+(size BH/16 -1),BlockSize-FirstRcd for I = Block+BHsize to X do [ rv I = (rv I)+Y ] Block>>BH.FirstRcd = BlockSize-NRcdWds Block>>BH.LastPntr = NPtrs +(size BH/16 - 1) OtherBlock>>BH.FirstRcd = FirstRcd OtherBlock>>BH.LastPntr = LastPntr OtherBlock>>BH.Dirty = true // if DebugSym then [ ChkBlock(Block); ChkBlock(OtherBlock)] resultis Block ] //and ChkBlock(Block) be // for I = BHsize to (Block>>BH.LastPntr)-1 do // [ ChkBlockCount = ChkBlockCount + 1 // let R1 = (Block!I) + Block // let R2 = (Block!(I+1)) + Block // ChkOrder(R1, R2) // ] // and ChkOrder(R1, R2) be // unless // SymbKeyComp(R1, R2) < 0 // do CallSwat("Bad KeyComp in RcdStore") // ] // block file procedures and GetFreeBlock(Block) = valof [ if Block>>BH.Dirty then PutBlock(Block) let NPages = Block>>BH.NPages Block>>BH.BlockAddr = NextFreePage NextFreePage = NextFreePage + NPages if NextFreePage + 1 > MaxBlockPages then CallSwat("Midas.Symtab overflowed") Block>>BH.LastPntr = size BH/16 - 1 Block>>BH.FirstRcd = NPages * PageSize Block>>BH.Dirty = true resultis Block ] //GetBlock is only called when the required block is not in core and GetBlock(BlockAddr) = valof [ let Block = FindFreeBlock(0) if Block>>BH.Dirty then PutBlock(Block) IndexedPageIO(Map,BlockAddr,Block,Block>>BH.NPages,IndexedPageRead) if Block>>BH.BlockAddr ne BlockAddr then CallSwat() GBCalls = GBCalls+1 resultis Block ] and PutBlock(Block) be [ let NPages = Block>>BH.NPages if NPages ne NPagesPerStandardBlock then CallSwat() let BlockAddr = Block>>BH.BlockAddr if BlockAddr le 0 then CallSwat() Block>>BH.Dirty = false //Clear Dirty before writing IndexedPageIO(Map, BlockAddr, Block, NPages, IndexedPageWrite) PBCalls = PBCalls+1 ] and FindFreeBlock(Block) = valof [ for I = 1 to BlockTable!0 do [ let B = BlockTable!I if not B>>BH.Dirty then [ if B ne Block then resultis B ] ] //All blocks are dirty resultis BlockTable!(BlockTable!1 ne Block ? 1,2) ] //Used by initialization only and MakeNewBlock(NPages) = valof [ let Block = GetStorage(NPages*PageSize) Block>>BH.Dirty = false Block>>BH.NPages = NPages Block>>BH.BlockAddr = -1 resultis Block ] //Used before calling another subsystem to preserve the state of extra //symbol blocks by writing them onto "Midas.SymTab". The HeadBlock //and the first two symbol blocks are preserved by SaveState, so they //don't have to be preserved. and SaveRcdFile() be [ for I = 3 to BlockTable!0 do [ let Block = BlockTable!I if Block>>BH.Dirty then PutBlock(Block) ] BlockTable!0 = 2 //Undo initialization of extra blocks ]