Virtual_segments Virtual Segments
virtual_segments virtual_segments
User Manual: virtual_segments
Open the PDF directly: View PDF .
Page Count: 6
Download | |
Open PDF In Browser | View PDF |
)Jr~~ ";1 I' ";tl ••.1:1: ' ''', ;..::,:,:~. . )\ ~.I1.l .' / , I.r"':',~. ../ .' ;: ~ ;l.~ . : ',,~~ f; '~.). ~:.I tl I I "; " Froml Sub) : JOCN)V .( Vlrtu6. Ser,nenta Article Since I wrote the S/W for Version I.S, I have dlacovered how to get It wo r kin 9 0 n Ve r II ('0 n I I I • 0 (9 n d I V • n Iss Im I I art 0 I I I • n I nth I s r e q a r d ) • On the Mlcroenglne version IV.O there are no lIer,nent tahles to load, so I played wIth the Seoment InformAtion Alocks (SIRs). These are described In the IV.O manual on page 34, and the format for the Mlcroenqlne Is qlven bel OWl = record Seqbase I Seqlenq I xxx Segaddr r SeQunlt Segref SeQSP end, I work for a companv (Energy Data Systems) which has heen trylnQ to do some fairly complex applicatIons In UCSD Pascal, and we ran Into the ·se~nt barrier' In the spring of this year. We considered modlfylnq the lCSr> operating system, since we had source for It, hut decided to try to sLick to solutions which required as few modifications to operatlnQ syst~ code 8S possible. After s~ thouQht I c~e up with an Interim meRna of ame I lor a tin 9 the pro b I em, wh I chI . w I I Ide &c rib e I nth I SAt tic Ie. 518 "Integeq integer; Inteqer; Intefler; Inteqer, Inteqer; Integer, ·f.T There· Is a p-machlne regIster on the Mlcroenqlne which III a pointer to 9n "arrayrseqranqel of ·SIA", and the value of thIs register may be loaded Into the variable "ptr" with }~.. where LPR = 157 and 5TO = 196. One can then mess with the SIA for seqnent 'I' with ptr·rll.fleldn~e, and this Is how I mede Virtual Segments work on the Mlcroenglne; I ass~e that ~klng It work under IV.O would be s Imll sr • {~::;' . ~."!. . !l :~: Un for tun 8 tel y, u:( Pas cal a I I ow s the pro q r emne r a c c e s s ·t 0 0 n I y s I x S f! (fne n t procedurea under nvrmal clrcunstances. This Is fine for small proQroms (under )000 linea), but when one starts to get serlous About an application, one really needs more segment procedures. On~ reason for this Is that tHe UCSD Pascal sepArate compilation construct {lNIT:d uses one of these alx s8cp'nent procedure 'slots' "ven If It Is not loaded Into memory dynamically, wasting this scarce resource. Thla situation should lmorove very soon (perhaps by thB time .thls article Is printed) becRusf! Softech Is plannlnq to announce features In UCSD Pascal Version IV.O to solve some of these problems. ~INE("ptr,(-2),LPRfSTO) When a segment procedure Is called by the UCSD Pascal p-machlne, R soeclal op-code la used to do so. This op-code flrlt looka In an operatlnq syst~ 'segment table' to see If the Indicated procedure has been called more than zero times (reference count, In case It Is a .recurslve call . and thp. codp. need not' be reloaded). If It Is already In memory (reference count) n), It Is executed like any other procedurel If not, the op-code loo'1 Thl& article Is copyrighted hy Mlcrosystems (Aox 1192, NJ, 070Q2) and Is . reprinted with their permission. ~untalnslde, One of the nl~e features of UCSD Pascal Is Its support of seqnent procedures. A seqment procedure Is I Ike any other Poscal orocedure except that whenever It Is called (except for recursive calls) It Is loaded from disk Into memory prior to being e)(ecuted, and after It exits, the mernory Is reclaimed. In fact, the seqnent procedure Is loarled onto the stack, since the pattern of memory use Is nested In 8 very 'stack-like' mAnner. Seqnent procerlures al low the proqr~er to manaqe m~nory resources explicitly and conveniently, and really 8re a form of overloy. In IRroe programs, It is not uncomnon to dedicate a seqment procedlJrp. to Initialization, since that code need not reslrle in memory after the proqr~ starts. Seqment procedures ~y have Internal procerlures anrl functions, All of which are loaded with the seqnent procedure code, allowlno functional groups of routines to be hrouqht Into mfflnory In A slnfJle operation, anrl executed. :v i-* ~ for the segment procedure, and the n~ber of bytes In the code. It thp.n loads the code onto the stack and cal Is the procedure, Incr~entln~ th~ reference count to one. Upon exit, the seqnent procedure return on-corie decrements the reference count by one, and clears the atAck hAck un to where It was prior to the call If the count has hec~ zero. The Idea which I had was to somehow write data Into the sp.qnent tAhlps p r lor t o e a c 1'1 s e!JYl6 n t pro c e d u r e c a I I Ins u c h a wa y t hAt wh e nth eo A h 0 V t' op-code was Invoked It would find data descrlblnQ different st'fJ'TlPnt procedures each time; It would In fact be faked Into loadlnqdlffprent corle se9'flents Into memory for each call, even thouqh the same seO'Tlent nrocf'rillre was being called each time. In order to do this, I first had to find where the Beqnent tRhles wer" located In memory. The first fllobal variable declared In the lrc;O PASCfll operating system code III a pointer to R special recorc1, callec1 the syst~ c~nlC8tlon record, or SYSonMREC, And the seqnent tahles Are a nRrt of t h I II r e cor d . The rei s asp e c I R I ' s wit c 1'1' I nth P. I CSO P R S Ca I c omn I I P. r ( t 1'1 f' 'U' switch), end when Its value Is '-', proqrAms function very c1lffp.rpntly than usual. The 'main proqram' does not execute at all. hut rAther thE! fir 8 t 9 e qme n t pro c e d u r e d e c I are d i n the 0 roC) r Aln e l( e c II t e s . I nil t p a c1 • A Iso, the variable definitions are 'al lased' on too of thp. definitions for the operatlnq system (like an EaIIVALF:N':f: In rffiTRAN), allowln,! the nrol)rAm to read and write those varlahles. Usually, one uses exactly the SWTle varlahle definitions as were ulled when the operatlnll sy't~n was compllpc1. In order that one's proQrAm aqree with the operatlnll systrm rlerlnltlons. In this CAse, however, I simply made my own definitions. since All I wanterl was to determine the VAlue of the oolnter to SYSonMRfr.. Slnr~ l.S stores pointers AS Rctual fllP.fYIory addrellscs. hv prlntlnll the VAlue of ttlf' pointer I COli I d de t e rml ne whe rei n I11P.fTlO r v SYS('"'('l.-Rfr. WA II at 0 r pd. ~~l ~ N ~. ~., ( 1 wrote the following proqr9m and was able to locate SYSOOVREC. It was part trial and error, since after I thought I had 'found' SYSCOMREC the first time, I was forced to I,ook through dtmps to flqure out why I had been wrong ••• Anyway, the program below wll I tell you where In memory any SYSCOMREC 19 under Version 1.5 (It Is at location 718 rdeclmall for my Z-AO verllon of 1.5), and probably under Version 11.01 I have not tried It with lit. O. f su-I progr9m find; var I 1 I n t e 9 e r; f a I I as edt 0 ~ s YBC omr e c !l t: YJ.ll '" I > I • t.: u u t: u t: II C • C U U tl I u I I l J . UII C C IJ U I U c:; L II U I I lJ" that pointer with the fol lowing record definition: L II '" LUI I "l. ~ \lit"'''\'''; til I. 1'" ' ..•. I." f.,I, ",' var ',le r·~.i.'.\.' alias: record case boolean of true: (I Integer); falser (p : "segtab); end; This record definition states that one will either use the storaqe for the record 'alias' a8 an Inteqer (denoted 'allss.I') or as a pointer to a variable of type 'segtab' (denoted 'allas.p'). Since the same storaqe Is used for both values, If one were to write Into the Inteqer part, one could then use that value as a pointer; one could 'fake' Pascal Into thlnklnq that a variable of type 'segtype' waB being pointed to. scqnent procedure flndsyscom; begin wrlteln('Syscomrec Is located at address ',1,' decimal.'); end; .j}~-.." (, ,"· .' I Ifr rII'; •..'•.' ...•.... r.' . I l~:il i:' By stating 'aIl8s.1 1= 710 + 9(;', I could access the actual operating system segment table es 'allas.p"'. I could then write .Into the 8eqTIent table entries of any se~nt which I chose, forcing the syst~n to load the code I wanted to when I called the appropriate procedure. beqln end. then needed to flqure out how far from the start of SYSOOMREC the Begment lAhles started. fortunately, the UCSD Pascal operating ByBten variable definitions, found In 8 file called GLOAALS.TEXT, were distributed with lCSO Pascal Versions 1.4 and 1.5, so t had th~ at my disposal. fThe Suppose, for Instance, that I knew that I wanted to exeGute each of a sorlos of ten segment procedures which were located on 'unltr\l', had length 'Iengthrll' bytes, and otarted at disk block nunber 'block f \l'. could write a procedure to perform a 'call' to the 'I_th' such segnent • . I• • • . ,',:,1..:... ' :~ : " 'I ". ~~';:~,:, I· , !+:::.' ~~ . Copyright 1981 by Mlcrosystens ~\ ~; Copyrlqht 1981 by Mlcrosyatens i ~. Virtual Segment Procedures under UCSO Pascal Page J n_OMl.S.TEXT file, along with all other UCSD Pascal source code, 'Is cooyrlght by the University of California at. San Olegol some of the (".LOMLS.TEXT file Is presented In this article (the portions of Pascal !lource In capltel letter9) with the permission of Softech Mlcrosysterns Inc, their licensee. 1 I om Infor~d that the SYSOOMREC data layout has not chanqed with the varlou9 UCSD Pascal Versions, so you should be able to locate the se~nt tables at 96 bytes past the slart of your SYSCOMREC. ThIs ~ans that If one were to write to location RlA tn my memory, one would b, wrltlnq on the first byte of the selJl1ent tables, The se~nt tables are defined as followsr type seQdesc = record dlskaddr inteqerJ codelenq Integerl endl absolute block number on disk I In bytes 1 seqtab = array rse9ranqel of record unit: unltnum; , disk unit number (an Integer) I codedesc : segdesc; f 8S above l . end; where 'seQrange' Is the numher of seqnent procedures defined for the UCSO system belnQ used ('0 .. 15' In the case of Version 1.,). If one declared a pointer 'segptr",whlch oolnted to a record of type 'scqtab", one could refer to that record as 'seqptr", to the unit (disk drive) on which the code for the third seqrnent procedure was loc8tnd a9 'setlplr"r11.unlt', and to the nU'llber of hytes In the code for that oror.erfllre fill Virtual 5eqTIent Procedures under lCSD Pascal PaQe 4 procedure as folloW81 fh III <'" ~" . !W;;.' . ..·..(ri.,,· program test; type eeqdesc = record dlskaddr Inteqerl absolute block ntmber on disk I codelenq : InteQor; In bytes 1 end; seqtab = array fsegranqel of record unit: unltnun; I disk unIt number codedeec : segdesc, f 00 above 1 end; var al Iss: record csse boolean of true: (I : InteQer); falsel (p : Aaegtab); end; unit. : arrayfl •. lOl of Inteqer; lenQ(h : arrayfl .• 101 of Inteqerl hlock I ar~ayrl •• JOl of lnteqer; I : Inteqer; sef1T1ent procecfure 'vl rtual; beQin f need not have any code, since It wi I I never exccute -the other ten scr,nr.nt procedurcs will execute Instead' enOl f virtUAl 1 procedure rlovlrtunl(i If; intetler); ~~r . ! jj r,.:f , ,~ ", f. ,. ii~ ~y ,;,' "~; ~. [i'i;'.~. " ~ t r:;.. ~,; .' . f. ~ i .• ' •' ...... l. ':~Q,,::: , j?', . rr . :;' I." ":i... '~ ( begin r set up to call vi rluat segment allas.p~rlO1.codeunit := unitfilj alias.p~r101.codedesc.codelenq := alias.p~r101.codedesc.diskaddr := f call virtual seqment vi rtual; endj dovirtual At 1 Virtual lengthrilj blockf'ilj loaded above'l CO\!ST /V1I\XLN IT = 1 2 i /V1I\XO I R = 77; YIDLEI'-G = 7; TIDLEI'G = 15; FRlKSIZE 512; OIR8LK 2; /V1I\XSEG = lS; r begin f ~ e t up poi n t e r al ias. i := 718 + f call the ten vi for i := 1 to 10 end. r test l tor e a I s e qrne n t t a hie = 9f>j rtual procedures do dovirtuat(i); Segment Procedures under LCSO Pascal Paqe h (lI-/V1I\XIIVLM PHYS I CAL LNI T 1/ Fffi LREAIJ*) (*/V1I\?< N...MAER q: ENTRIES IN A DIRECTffiY*) (1IN..Mf3ER CF Q-ll\RS I N A VOLUv£ 10*) (*N...MRER CF OHARS IN TITLE ID*) (*STANJARD 01 SI< AUXJ.< LEf'.GTH*) (*01 SK ACCR CF oIRECTffiY*) (*M4X a::I)E SEOvF.NT N..MAER*) '~t TYPE I :1:'1 'j .~~ The above program will actually work, but it has a few problems which make it a bit awkward to use. First off, procedure cal Is which previously looked I ike a nice nrune now are reduced to a cryptic statement I ike 'dovirtual(:n< This can be taken care of by cre~tinq constants at the start of the program with values from 1. to 10, and callinq 'dovirtual' with those constant values; a pro~edure to clear the screen mlqht then be cal led as 'dovirtual(clrscreen)', a significant improvement. The other problem is that it is I\OT EASY to find out some of the information which I so casually stated would be found in the 'length' and " ~' ".~".' Copyright 1981 by Microsystems ';1 "'~ ":~ (*0 IMJL IES DATE !\oT MEANIWFUL'*) (*oAY CF M)\,JTHIt) (*100 IS TEMP DISK FLAr,*) Virtual Seqrnent Procedures under LCSo Pascal Page 5 'block' arrays. To do so involves reading the 'segnent tahle' of the code f iIe I n wh I c h 0 ne 0 f the v I r t u a I s e cpne n t sex i s t s , ' I nor de r tad e term i n e Un for tun ate I y, the dis k add res s i n forma t Ion s tor e d i n t hat in forma t ion. the segment tables of a code file is slightly different than that stored in the operatinq system's seqrnent tables. The normal code file disk a~dresses are relative to the start of the entire code file; the system addresses are the a b sol ute dis k bI 0 ck mmb e r • T his me an s t hat i nor de r t 0 can v e r t the data In the code file's seqment table into 'useful' Information, we must also know the absolute address of the start of the code file. And in order to determine this, we must read (and understand) the directory of the disk. Whew!... . i t a s t epa t a t i me, the forma t 0 f a l.£So Pas c a I dis k d ire c tor y i s T a kin q given below. It is a Dortion of the UCSO GLoAAlS.TEXT file mentioned earlier (and is copyrlqht by UCSo). LN ITN...M = 0 •• Mo.XLN IT ; Vlo STRIf'.GfVIDLEf'.Gl; = O.. ~IR; = STRIf'.GfTIDLEl'-Glj FILEKII'D = (LNTYPEDFllE,XDSKFILE,OOO£FILE,TEXTFILE. olru~E oIRENTRY = RECORD DFIRSTALK: INTEGER; DLASTALK: INTECERj CASE DFKII'D: FILEKIN) OF SEaREoIR, LNTYPEDFILE: (OVID: Vlo; DEOVAU<: INTEGER; CN.JvF I LE S: 0 I RRJ\I'-GE ; OLOADTIME: INTEGER; DLASTACDT: oATEREC) j XOSKFllE,COOEFILE,TEXTFILE,INFOFILE, DATAFILE,GRAFFILE,FOTOFILE: (oTlo: TID; DLASTBYTE: 1., FAU;: It;, .~: !,~ ~ = var directory ·~~ ";i INFOFILE,DATAFILE,GRAFFILE,FOTOFILE,SECUREoIR); :.1 .,~ '1:.{. TID ....•. '~ :~ = oATEREC PACKED RECORD M:NTH: 0 ...\2; DAY: 0 •• 31 j YEAR: 0 .. 100 EI'D (lIoATEREC*) 1I'J01 hy Mic['osvstems ~ I ~, I ~;i~~. ~I ~ ~ t ( r· ~. i' t·~ Virtual Seqnent Procedures under UCSO Pascal Virtual Segnent Procedures under UCSO Pascal of the last modification to the file. The ahove definitions also tell us where to find the directory, n~ely at 'dlrblk', or block two. We can read the directory Into memory with the statenent ~ unltread(unitnun,dlrectory,slzeof{dlrectory),dlrblk); This statement uses two UCSO Intrlnslcs, the 'slzeof' function and the '"nltread' procedure. The former returns the ntrnber of bytes In a Qlven data structure; In our case, It Is the nunber of bytes In the 'directory'. The 'unltread' procedure reads from unit (disk drive) 'unltnun' Into ~ry At the address of the 'directory' structure for 'slzeof(dlrectory)' bytes slartlng at absolute disk block '~Irblk' VI I t h t h I II I n forma t Ion I n memo r y, we can sea r c h the d \ r e c tor y for an en try for a given file. Suppose that we wanted to see If the file 's~ple.code' were In the directory. We could say nlrnflles 1= dlrectoryrOl.dnuniTI~B; f nunber of valid entries I := 0; , will Index Into directory 1 done := false; f will become 'true' when we are done 1 while (I < nunflles) and not done do beqln I : = I + 1; If (dlrectoryfll.dtld = 's9mple.code') then done end; := truel If we COllie out of this loop with 'done' having a value of 'true',' then we have Indeed found an entry for the required fllel If not, the file was not present. If It was found, then the absolute address of the first block of the file on disk Is simply 'dlrectoryrll.dflratblk', We have then found the first thing we needed. the Absolute disk address of' the code file. We now nee d tog e t the cod e len g tho n d rei a t I ve dis ked d r e as I n forma t Ion from the code file's seqnent tables. The format of the first bloc~ of a code file Is as shown below. previous type definitions hold a8 before. segtable where the • ~l Page 7 Paqe A find; var codeflle I fllel . gegteble : array f segranqel of seqde9cr , 'seqdeac' defined as hefore 1 I ! Integer; begin un It: = s; r read In segnent table -- AS9t.rne 'olrectory' read In as ahove first, And 'dlrectoryr 11'. Is data for file which Is of Interest to U9 1 unltread(unlt. segtable, slzeof(seqtalJle), dlrectorfll.dflrstblk); length 1= seqtablefl0 1 .codelenq; block := seqtableflOl.dlskaddr; , relative block nunher I block 1= block + dlrectoryfll.dflrstblk; f absolute block ntrober I end. f find 1 ':,'1" progr~ So, we finally have the entire ball of wax. We can r.ead a l.cSD disk directory to find out where on disk a file Is storedl we can read and understand lhe Information In the seqnent table of a code file; we ca~ convert the relative block nunhers In the code, file seO'l1ent tahles to absolute block ntrobers; and we can use that InformatIon to Invoke virtual s e gne n t pro c e d u res • Tot lee v e r y t h I n 9 t 0 q e the r , you w \ \I fin d he I OW 8 sin g Ie' pro gram wh I c h doe s i t B I I. The pro c e d u r e 'v I r tin I t i n I t I 9 I I z e san I n t ern a I tab J e 0 fun 1 t, len 9 t h, and b lac kln.f 0 rma t Ion be for e the 0 r 0 Q r am really gets going. It does this by readlnq In the disk directory and c a II I n g , v I r t II n k' for e 8 c h v I r t u a I s e erne n t pro c e d u r e wh I c h i t w I II c a I I later. The 'vlrtllnk' procedure looks In the directory for the code file requested and puts the required Information In the Internal table. The main program then Invokes the virtual seqnents as 8 teat. Following thIs program Ie 8 sample of one of the programs which defines a virtual eeqnent pro c e d u r e • No t e t hat I n bot h the p r oq r am wh I c h c a I 1st h e v I r t u a I s e qne n t s and the program which defines one of th~. the virtual seQments Are defIned 08 the first executable code In the file. This Is necessary. alnce the technique which I have shown requires that both seqnent procedures have the Bame 'segnent nunbers'. [ J"'./ .. ;:'·, Ii· t·:···· r:·· I • "'j , , I, ••••••••. :.:.:•.: •.•••.••: ~ t 2t·· -; ~, ,,! , .,' ~ 1~ ,.~;/ 1 ".J . ;'.: ..." ' Wij ". i\'i;~ i.~'\l~.';. ' !:':,. arrayfseqranqel of aegdesc; It·!.l~' :' ·~ !-·i:'. we ~9nted to find the necessary Information about a particular code Ie, we could use the UCSD Pasca' syst~ Intrinsic orocedure 'unltread' to road the segment table oata from that file Into the above data structure. The n • If we wan ted the I n forma tl 0 nab 0 u t the ten t h 8 e gne n t pro c e d u rei n a code file, we could sl~ly use the tenth el~ent of that array. For ex~le. to determine the 'length' and 'block' data for segnent procedure n IInb e r ten I n a f I lee a II ed's amp Ie. cod e' 0 nun I t f I v e. one c 0 u I d dot h e followlnq: If ::,~!·Il~r ~l 1\ ':. ;~. .' .- , '1_ ;." I '~'·-·' ~!}.: ';~ , . .... ) ~'. ' .... ;:f . '~" ~ " CopyrlQht lqnl hy Mlcrosyst~n9 ~. ~ , .':,~:[d~ .\~ ':~~f /;-. ~<".: ; ~~ ~'I' aj,~ ( ,~~ ~ , \';I II ~~ rt ;i VI rlua) Sec,nent Procenures under U::::SO Pascal Paqe 9 proqrwn vlJeqleatl ~odeunlt aNST codedesc end; tv1t\>egln If (vsegsfvaegnuml.codeunlt = 0) then begin wr I tel n ( • At t emp t toe l( e cut e un I Ink e d v I r t u a I 8 e g numb e r " v 9 e (J n tro, ' . ' ); exlt(dovlrtual)j r not linked I end; load segment register 10 with secrnent data fron I'th sec,nent proceciure allas.p'rIOl := vaegsfvseqnt.rolj vIrtual; end; r dovlrtual procedure vlrtlnlt; r Initialize tables for call1nq v seqnent procedures \ const unum = 5; I unit number where v segnent proca are found var I : Integer; r temp var dIrectory I array rdlrrangel of dlrentry; f holds disk directory numflles I dlrrange; f number of flies In dIsk dlr , procedure vlrtllnk(fname atrlng; vseqnum vseoranQe); var I I Integeq clone I b~olean; fir 8 t b I k I I n t e 9 e r; r b I k numb e r 0 f r Irs t b I k 0 f f a k e s e 0 cod e f I I e I Isegtable I array rseqranqel of seqdescl f holds sPq th\ fr~ disk I begin f find file In directory 1 I := 0; done := false: wh I I e (I <:: n lro f I I e s) 8 n dna t don e dab e q I n I ; = I + 1; If (dlrectory f ll.dtld:: fnwne) then done:= true: end; If not done then heqln wrlteln('unable to find flle ',fnRffiE'!,'.'); exlt(vlrtllnk}; endj wr I tel n ( . F lie " f nAme " f 0 un (1. • ); 1901 by Micro9yst~ns v.:> C.11 \':' .~ I Se~nt Copyrloht l<)nl hy MicrosystP.rl1S "~ d Virtual ..i; 1 ,:1 ~; •.'~.;.,:, , __ "V"'? &' ( V-' ·""·; t';' rl;.·:;.' C1.) I; I ~,., Virtual Seqment Procedures under t.x:::SD Pascal Paqe 11 r They looked for rrre. The wondered where I was, f read in seqnent table 1 flrstblk != directo ry fi 1 .dfirstblk; un i tread (unum, I s eqt ab Ie, s I z eo f ( I s eqt ah Ie) , fir s l b 11< ) ; f enter data' from seqment 10 In found fi Ie into v seq table vsegsrvsegnuml.codeunit := unum; vsegsfvsegnum1.codeclesc.codelenq := Iseqtable f lOl.codelenq; vseqsrvsegnuml~codedesc.diskaddr := Iseqtable r l0 1 .diskaddr + firstblk; wrlteln('Finished with association of file ',fname,'.'); end; f virtllnk ~ begin f initialize vseq table so that all units are zero (vseg undefined) for i := n to J) do vseqsfil.codeunit :=0; f set up pointer to syscomrec's seq table located by 'find' program al ias. I := 71.A + 9';; I 9(' bytes in syscom ree before seq tables 1 f read directory into memory 1 unitread(unllm,dlrectory,slzeof(directory),dirblk,O); nlmfiles := directoryrnl.d n umfiles; writeln('Dlrectory of unit ',unum,' read in.'); , lin k f i len ame s wit h v s e g numb e r s 1 vir t lin k ( , F AI
Source Exif Data:File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.4 Linearized : No XMP Toolkit : Adobe XMP Core 4.2.1-c041 52.342996, 2008/05/07-21:37:19 Format : application/pdf Create Date : 2015:11:18 16:34:37-08:00 Creator Tool : Hewlett-Packard MFP Modify Date : 2015:11:18 19:46:07-08:00 Metadata Date : 2015:11:18 19:46:07-08:00 Producer : Adobe Acrobat 9.0 Paper Capture Plug-in Document ID : uuid:757d8dd2-8578-b34e-8adc-75eb1fa6c52f Instance ID : uuid:a986ce6c-8120-6948-93e6-2b6a2b2fdc53 Page Layout : SinglePage Page Count : 6 Creator : Hewlett-Packard MFPEXIF Metadata provided by EXIF.tools