| !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
| ! IMem An Inform 6 library to implement Run-time allocation | |
| ! of dynamic memory to objects. By L. Ross Raszewski | |
| ! Version 2 <rraszews@acm.org> | |
| ! | |
| ! New in this version: | |
| ! *It is now possible to swap out objects other than | |
| ! the last one swapped in. | |
| ! *Big news: Support for array properties. | |
| ! Debugging verb "showheap" will show the current status of the heap | |
| ! This should be just about compliant with inform 6.20's strict | |
| ! error checking. If it isn't, TURN STRICT ERROR CHECKING OFF. | |
| ! A bug stemming from inform's lack of unsigned arithmatic was removed. | |
| ! | |
| ! | |
| ! Things to look out for: | |
| ! A number of people have asked me about problems that have crept in | |
| ! when using imem. | |
| ! The biggest problem seems to be that, for one reason or another, | |
| ! objects were being swapped out more than once, or without | |
| ! first being swapped in. Imem now silently rejects attempts to | |
| ! swap out of the heap objects that have the swapped_out flag set. | |
| ! Make sure you know EXACTLY when the code to swap an object in or | |
| ! out will be called. Remember, there's lots of ways to move the player, | |
| ! So trapping the before rule for "go" in an adjacent room isn't sufficient | |
| ! to guarantee that a room's contents will be swapped in when the player | |
| ! enters. | |
| ! | |
| ! | |
| ! First, I would like to apologize if I use terms that are not standard | |
| ! for refering to the workings of this library. I will endeavor to be | |
| ! consistant with myself, if not with the rest of the world. | |
| ! | |
| ! Recently, it seems that more than a few people have run into a rather | |
| ! unfortunate limitation of the Z-machine, the maximum 64K dynamic ("low") | |
| ! memory. Ideas have been batted around to discard Z and target inform | |
| ! to something else, or whatnot, but so far, no one's been willing to | |
| ! actually DO anything about it. I wrote IMem to give authors a way around | |
| ! the limitations of Z until someone comes up with something better. I'm sure | |
| ! that even if we do get a new format, some people will still want to stick | |
| ! to the Z-machine, and this library can help maximize the amount that Z | |
| ! can be extended. | |
| ! | |
| ! IMem uses a system which I call "Pooled dynamic memory". It does have | |
| ! several limitations compared to normal low memory, which will be discussed | |
| ! in depth. IMem's usefullness relies on several assumptions which I have | |
| ! made: | |
| ! 1. In a game large enough to break 64k low memory, there will be a | |
| ! large number of objects which are only accessible during certain | |
| ! sections of the game; | |
| ! ex: Jigsaw, Time: All things come to an end, So Far | |
| ! 2. Most of these objects are scenery, and do not change much over the | |
| ! course of the game. | |
| ! ex: Curses, etc. | |
| ! 3. Any changes which occur to this sort of object is recored in the | |
| ! attribute flags, not in the object's properties. | |
| ! ex: My own coding practices. Counterexamples: any object which | |
| ! updates itself by overwriting one of its properties | |
| ! 4. Every object does not need to be abvailable at the same time | |
| ! ex. Lots of games | |
| ! | |
| ! Now, if a large number of objects in your game meet these qualifications, | |
| ! IMem may be the thing for you. IMem-managed objects reside almost entirely | |
| ! in high-memory, except when they are swapped in. As a result, these objects | |
| ! are partially static -- the sections contained in high-memory cannot change | |
| ! during gameplay (sort of. I'll explain later). For the purposes of this | |
| ! document, "High-memory objects", "semi-static objects" and | |
| ! "IMem-Managed Objects (IMOs)" all refer to the same thing -- objects managed | |
| ! by IMem. | |
| ! | |
| ! | |
| ! Compatability issues: | |
| ! IMem is based on the current architecture of the v5/8 Z-machine. | |
| ! The constants WORD_SIZE, NUM_PROPS, OBJECT_LOCATION, OBJECT_SIZE | |
| ! and PROPERTIES_TABLE are based on values given in the specification | |
| ! of the Z-machine, section 12, and may need to be adjusted for any | |
| ! new and mysterious Z-machine version. | |
| ! | |
| ! IMem does not work with version 3 games. | |
| ! Well... I don't THINK it works for version 3 games. You might be able to | |
| ! Alter the symbolic constants and coerce it into operation. | |
| ! | |
| ! Usage: | |
| ! before using a high-memory object, you must swap it into low memory. | |
| ! this library gives the attribute "swapped_out" to IMOs | |
| ! which are currently not residing in low memory. Before performing an | |
| ! action on an object which may potentially be an IMO, | |
| ! always check that it is not "swapped_out" (there are some exceptions | |
| ! which I will discuss later.) | |
| ! | |
| ! To understand how IMOs are swapped in, it is nessecary | |
| ! to first understand how they are stored... | |
| ! HOW IMEM-MANAGED OBJECTS (IMOs) ARE STORED: | |
| ! In the Z-machine, an object consists of two (sometimes three) major | |
| ! parts: | |
| ! Part 1: The Object Header | |
| ! The object header is an array of size OBJECT_SIZE bytes. This | |
| ! array contains the attribute flags, the location of the object | |
| ! within the object tree, and the location of the property | |
| ! table for the object. | |
| ! In IMem: | |
| ! The object header for an IMO resides in low memory all the time. | |
| ! This means that even IMOs leave an OBJECT_SIZE "footprint" in | |
| ! the game file. | |
| ! Consequentially, the information in the header is fully dynamic, | |
| ! and may be both read and written to even when the object is | |
| ! swapped out. Thus, attributes may be set or tested even when the | |
| ! object is swapped out. The object may also be moved within the object | |
| ! tree, via the "move" command. Any IMO you create should | |
| ! start with its "swapped_out" attribute set. | |
| ! Part 2: The Property Table | |
| ! This is a variable-sized table, which contains the hardware short | |
| ! name of an object, and the common property values. | |
| ! In IMem: | |
| ! When an object is swapped into low memory, IMem creates a property | |
| ! table for the object based on a function in high memory. While | |
| ! the property table is in low memory, you can treat it just like a | |
| ! normal object. It is legal to reassign a property | |
| ! (ie. obj.prop=constant), however, this reassignment will not persist | |
| ! between swapping out and back in (in fact, this can be beneficial, | |
| ! as swapping an object out, then back in will "reset" the object's | |
| ! properties (but not the attributes or location). | |
| ! IMem DOES NOT give objects a hardware short name. Instead, provide | |
| ! the object with a short_name property. | |
| !!!!!!!!!!!!!!! | |
| ! (This block of text is obsolete, but is left in for reverse compatability) | |
| ! IMem also does not currently support array properties. | |
| ! The most common place where this will be noticable is the | |
| ! name property. If an object has more than one word as its typable | |
| ! name, use a parse_name function instead. | |
| !!!!!!!!!!!!!!! | |
| ! | |
| ! The property builder function which creates the property table has the | |
| ! following format: | |
| ! The function accepts two arguments. The first argument contains | |
| ! a common property number. If the second argument is 0, the function | |
| ! should return true is the object should provide the property listed | |
| ! in the first argument. | |
| ! | |
| !NEW!! To generate an array in a property, return the number of entries | |
| ! to be placed in the array. When the second argument is 2, the FOURTH | |
| ! argument to the function will hold an array. Information written to | |
| ! that array becomes the array data for the object. Imem will happily | |
| ! allow you to write past the end of dynamic memory, or write more | |
| ! words than you asked for. In the latter case, the extra entries | |
| ! will be happily chopped off by imem. In the former, you will | |
| ! probably crash the interpreter. Be careful. | |
| ! Entires not initialized by the object builder will still be allocated | |
| ! but will contain garbage and NOT zeroes. (at least, they may not be 0) | |
| ! There is a limit of about 30 Word-length array entries per property | |
| ! NOTE TO ADVANCED USERS: | |
| ! This can be used to kick imem into a sort of "fully manual | |
| ! operation". An object builder can return the total size of the | |
| ! property table (less two bytes) as an array request. Subtract 2 | |
| ! from the fourth argument to recieve an array which describes | |
| ! the entire property block for the object, which can be | |
| ! constructed "by hand" according to the specification of the Z-machine | |
| ! | |
| ! | |
| ! For non-array properties, this happens: | |
| ! Then the second argument is 1, it should return the value of the | |
| ! property given by the first argument. | |
| ! | |
| ! An example: | |
| ! object foo "" has swapped_out; | |
| ! [ FooProps prop code obj array; | |
| ! if (code==0) | |
| ! if (prop==description or before or initial or short_name) rtrue; | |
| ! else if (prop==name) return 4; | |
| ! else rfalse; | |
| ! else switch (prop) | |
| ! { description: return "It's a bar of Foo"; | |
| ! before: return Foo_Before; | |
| ! initial: return "There is a Foo bar here."; | |
| ! short_name: return "FooBar"; | |
| ! name: if (code==2) | |
| ! array-->0='foo'; array-->1='bar'; | |
| ! array-->2='foobar'; array-->3='baz'; | |
| ! } | |
| ! ]; | |
| ! will create the following object: | |
| ! object foo "" with | |
| ! name 'foo' 'bar' 'foobar' 'baz', | |
| ! short_name "FooBar", | |
| ! initial "There is a Foo bar here.", | |
| ! before Foo_Before, | |
| ! description "It's a bar of Foo"; | |
| ! NOTE: In inform, functions attached to objects return FALSE | |
| ! be default, and all other functions return TRUE. | |
| ! Because functions for IMOs are not attached to objects | |
| ! at compile-time, you need to add a explicit rfalse; to the | |
| ! end of the function. | |
| ! NOTE: An optional third argument holds the object beign created | |
| ! IMEM AND CLASSES: The class of an object is stored in property 2 | |
| ! of the object. returning the name of a class object for property 2 | |
| ! will make "ofclass" evaluate to true, however, it does not copy the | |
| ! class properties to the object. | |
| ! | |
| ! (Part 3: The individual property table | |
| ! Property 3 of an object with individual properties containes a pointer | |
| ! to the individual property table of that object if it provides any | |
| ! individual properties. | |
| ! In IMem: | |
| ! IMem does not provide any direct support for individual properties. | |
| ! A user CAN create an individual property table according to the | |
| ! specification given in the Inform Technical Manual (9.5), and then | |
| ! return that array for property 3) | |
| ! | |
| ! Now that the technical stuff is out of the way, on to using IMem: | |
| ! To Swap An Object into Memory: | |
| ! Call SwapIn(Object,Function); where Object is a swapped-out IMO, | |
| ! and Function is the property builder function for the object. | |
| ! NOTE: There is nothing to stop you from using a property builder | |
| ! function for another object. | |
| ! SwapIn(Foo,Foo_props); SwapIn(Bar,Foo_props); will produce two objects | |
| ! with identical property tables. | |
| ! SwapIn will return the object if it is successful. If the object | |
| ! is already swapped in, it will not reload the property table, | |
| ! and returns FALSE. | |
| ! IMem does no run-time range checking. If you load more objects than | |
| ! will fit into the IMem heap, you risk corrupting memory. SwapIn | |
| ! will assign the property table to an out of range variable and | |
| ! crash the interpreter. It is the responsibility of the author to | |
| ! make sure the heap is large enough. Define the constant HEAP_SIZE | |
| ! before inclusion to set the heap size (default is 1024 bytes, which | |
| ! isn't much really.) A general rule of thumb is that each property | |
| ! of an object is 3 bytes, and each object has an overhead of 6 bytes. | |
| ! Array properties have a length equal to twice the number of entries | |
| ! plus two. | |
| ! The Heap is always (Heap_Pointer-Heap) bytes long, and has | |
| ! (HEAP_SIZE*WORD_SIZE)-(Heap_Pointer-Heap) bytes remaining | |
| ! | |
| ! To swap an object out of memory: | |
| ! Call SwapOut(Object); to take the object out of low memory. | |
| ! NOTE: It is MUCH faster to treat the heap as a stack, and | |
| ! always swap out from the top -- that is, try to swap items | |
| ! out in the reverse order as they were swapped in. | |
| ! | |
| ! In normal usage, it is advisable ot swap in objects a "page" or set at | |
| ! a time. For example, when a player enters a particular section of a game, | |
| ! a function may be called to swap in all the IMOs for that section, | |
| ! and a second function could swap out all the items when the player leaves | |
| ! (Note that items must be swapped out in the reverse order as they were | |
| ! swapped in) | |
| ! | |
| ! CONDITIONS FOR USE | |
| ! This library is free for use in any game released as public domain, | |
| ! freeware, or free software, on the condition that the name of the author | |
| ! and of the library appears in the game credits. | |
| ! | |
| ! For commercial, shareware, and other for-profit games, this library may | |
| ! be used, provided that the name of the author appears in the game credits, | |
| ! in exchange for one copy/registration of the game. Contact | |
| ! L. Ross Raszewski <rraszews@acm.org> for details. | |
| ! | |
| system_file; | |
| ifndef IMEM_LIBRARY; | |
| ifndef temp_obj; | |
| object temp_obj "IMEM temp"; | |
| endif; | |
| Constant IMEM_LIBRARY 10; | |
| Default WORD_SIZE 2; | |
| Default NUM_PROPS 63; | |
| Default OBJECT_LOCATION $0a; | |
| Default PROPERTIES_TABLE 6; | |
| Default OBJECT_SIZE 14; | |
| Default HEAP_SIZE 512; | |
| Attribute swapped_out; | |
| Array Heap-->HEAP_SIZE; | |
| Global Heap_pointer=Heap; | |
| [ Obj_Offset obj; | |
| return (OBJECT_LOCATION-->0)+(NUM_PROPS*WORD_SIZE)+((obj-1)*OBJECT_SIZE); | |
| ]; | |
| [ SetPropertyTable obj a; | |
| obj=Obj_offset(obj); | |
| @storew obj PROPERTIES_TABLE a; | |
| ! Obj_Offset(obj)-->PROPERTIES_TABLE=a; | |
| ]; | |
| [ CreatePropTable fun obj i j; | |
| j=heap_pointer+WORD_SIZE; | |
| j-->0=Obj_Offset(obj)+(PROPERTIES_TABLE*WORD_SIZE); | |
| j=j+WORD_SIZE; | |
| i=objbuilder(j,fun,obj); | |
| j-->i=i; | |
| Heap_Pointer=j+(i*WORD_SIZE); | |
| if ((Heap_Pointer-Heap)>(WORD_SIZE*HEAP_SIZE)) | |
| { print "^***IMEM ERROR: HEAP OVERFLOW***^"; return -1;} | |
| return j; | |
| ]; | |
| [ DeletePropTable i; | |
| i=Heap_pointer-->0; | |
| Heap_pointer=Heap_pointer-((i+2)*word_size); | |
| if ((Heap_pointer-Heap)<0) { Heap_pointer=heap; return -1; } | |
| ]; | |
| [ ObjBuilder Hp fun obj i j k size; | |
| Hp->0=0; | |
| k=1; | |
| for (i=Num_props:i>0:i--) | |
| { | |
| size=fun(i,0,obj); | |
| if (size) | |
| { | |
| if (size==1) | |
| { | |
| Hp->k=i+64; | |
| k++; | |
| j=fun(i,1,obj); | |
| (Hp+k)-->0=j; | |
| } | |
| else | |
| { | |
| Hp->k=i+128; | |
| Hp->(k+1)=(size*WORD_SIZE)+128; | |
| k=k+2; | |
| fun(i,2,obj,Hp+k); | |
| } | |
| k=k+(size*(WORD_SIZE)); | |
| } | |
| } | |
| Hp->k=0; | |
| k=k/WORD_SIZE; | |
| k++; | |
| return k; | |
| ]; | |
| [ SwapIn Obj Builder; | |
| if (obj hasnt swapped_out) rfalse; | |
| SetPropertyTable(Obj,CreatePropTable(Builder,obj)); | |
| give obj ~swapped_out; | |
| return obj; | |
| ]; | |
| [ PopOut Obj i; | |
| if (Obj_Offset(obj)+(PROPERTIES_TABLE*WORD_SIZE)==(Heap_Pointer-((Heap_pointer-->0+1)*WORD_SIZE))-->0) | |
| { | |
| DeletePropTable(); | |
| i=Obj_Offset(temp_obj); | |
| @loadw i PROPERTIES_TABLE i; | |
| SetPropertyTable(obj,i); | |
| give obj swapped_out; | |
| rtrue; | |
| } | |
| else rfalse; | |
| ]; | |
| [ SwapOut Obj prop_addr i prop_tab_addr j k l; | |
| if (obj has swapped_out) rfalse; | |
| if (PopOut(Obj)) rtrue; | |
| prop_tab_addr=Obj_Offset(Obj)+(PROPERTIES_TABLE*WORD_SIZE); | |
| @loadw prop_tab_addr 0 Prop_addr; | |
| i=(Heap_Pointer-((Heap_pointer-->0+1)*WORD_SIZE)); | |
| k=Heap_pointer; | |
| j=Heap_Pointer+WORD_SIZE; | |
| while(i-->0~=prop_tab_addr && (i-Heap)>=0) | |
| { | |
| j=i; ! j gets the address byte of the last object | |
| i=i-WORD_SIZE; ! i gets the size byte of the next object | |
| k=i; | |
| i=i-((i-->0+1)*WORD_SIZE); !i gets the address byte of the next object | |
| } | |
| if (i==heap) rfalse; | |
| l=Heap_Pointer-k; | |
| l=-l; | |
| k=(k-->0+2)*WORD_SIZE; | |
| Heap_Pointer=Heap_Pointer-k; | |
| if (l) { | |
| @copy_table j i l; | |
| j=Heap_pointer-((Heap_Pointer-->0+1)*word_size); | |
| while((j-i)>=(0)) | |
| { | |
| (j-->0)-->0=(j-->0)-->0-k; | |
| j=j-WORD_SIZE; ! i gets the size byte of the next object | |
| j=j-((j-->0+1)*WORD_SIZE); !i gets the address byte of the next object | |
| } | |
| } | |
| j=Obj_Offset(temp_obj); | |
| @loadw j PROPERTIES_TABLE j; | |
| SetPropertyTable(obj,j); | |
| give obj swapped_out; | |
| ]; | |
| ifdef debug; | |
| [ ShowHeapSub; | |
| print "Heap: ", Heap; | |
| print "^Heap Pointer: ", Heap_pointer; | |
| print "^Heap Size: ", (Heap_pointer-heap), " bytes"; | |
| print "^Heap remaining: ", (HEAP_SIZE*WORD_SIZE)-(Heap_Pointer-Heap), | |
| " bytes^"; | |
| ]; | |
| verb meta 'showheap' * -> ShowHeap; | |
| endif; | |
Xet Storage Details
- Size:
- 16.9 kB
- Xet hash:
- 2a38e0e7d5ddf9e71082c1fcd293b41c6025373acd07a705a1df61b77cdcc931
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.