Path: utzoo!attcan!uunet!lll-winken!ames!pasteur!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!oakhill!dover!fullmer From: fullmer@dover.azsps.mot.com (Glen Fullmer) Newsgroups: comp.sys.amiga.tech Subject: Re: CBM, Why did you make it so hard? Keywords: Graphics Libraries, reentrancy Message-ID: <933@dover.azsps.mot.com> Date: 22 Mar 89 23:20:51 GMT References: <913@dover.uucp> <3623@amiga.UUCP> Reply-To: fullmer@dover.UUCP (Glen Fullmer) Organization: Motorola CAD, Mesa Lines: 178 In article <3623@amiga.UUCP> jimm@cloyd.UUCP (Jim Mackraz) writes: >)3. How does one restore the environment, for example, after an interrupt? > >I don't follow. Unless you are using interrupts as part of the package, >the system will restore you without your ever knowing. All registers must >... >)4. Is making a package that must retain context both over the life of a >) call and the life of the session different than "pure" residentable >) code? If so, how are they different, and what are the coding >) differences? > >Most system libraries don't have a notion of "session," that is, there is >typically no preserved state. To do this, you would just introduce >a "handle" that is returned by InitSession() and passed to every pertinent >call (including EndSession()). I guess you could say the OpenWindow() is >doing just that. Think also about the model of a RastPort: it isn't >session-based, but it is a handle. It has an Init() (although no Create()), >and no End(). >Jim Mackraz, I and I Computing "Like you said when we crawled down >{cbmvax,well,oliveb}!amiga!jimm from the trees: We're in transition." > - Gang of Four I want to thank all those who responded to my original inquiry about Amiga run-time libraries. It was very helpful and enlightening. Howard Anderson, whose generic graphics system originally elicited the questions, also appreciated the comments. There was some misunderstanding concerning my reference to interrupts. No, he is not using interrupts as part of the package. The essential question was: How does one maintain session context (reentrancy) in an Amiga run-time library when multiple sessions use a set of changing globals? There was a couple of suggestions: (1) To require the user to create a session context pointer (like Raster-Pointer in Intuition) or (2) to maintain a session context table within the library. He had some additional comments about his preference to (2) and questions about methods to achieve it. Any other methods that I missed? As it is more convenient for me to post news than he, I am including his comments and questions: OK, let's try a concrete example. I have a graphics system that is a collection of library routines that can be called by the user. The user must call "gstart" first. He must call "gend" when finished using the library. There is a function called "gdraw" that the user can call to draw a line. There is a function called "gset" that allows one to change modes, such as the drawing color, that will be used for subsequent "gdraw" calls. There are of course a lot of other callable functions in the library. I don't want the user to have to bother with setting up a graphics structure for the graphics system. I don't want the user to have to bother with passing some structure pointer that is irrelevant to doing graphics. Since the graphics system library is the object that NEEDS the structure for saving mode data, it should do its own allocation, pointer-finding, and deallocation of its task-dependent global/persistent storage. The purpose of putting a generic graphics system up on the Amiga is to make it easier for the user to use. So the problem on the Amiga is how to set up a library with task-dependent global/persistent storage that, after a time-share interrupt or other interrupt, remembers where its particular task-dependent global/persistent storage area is when it again receives control from the scheduler. I so far have placed all of the global/persistent storage items in one structure. I can use MemAlloc to allocate storage for the area. The problem is how to preserve that pointer through a time-share interrupt (or any other sort of asynchronous interrupt that takes control away from me, runs something else, then returns to me.) During an interrupt, 70 bytes are pushed onto the stack representing all of the data registers, address registers, status register, and the program counter. When you find yourself back in your code after an interrupt, those 70 bytes are all you really know. (You wouldn't even know where the stack was unless one of the registers was dedicated to holding a pointer to it.) Clearly, the best way to save a pointer to a task-dependent global/persistent storage area is to DEDICATE one of the registers, say A3, to always holding the pointer to the task-dependent global/persistent storage area. This is how Multics achieved what I consider to be true reentrancy - reentrancy with per process global/persistent data - and spoiled me beyond words... (See page 68 of "Timesharing System Design Concepts", Richard Watson, McGraw-Hill, 1970. The Multics operating system, by convention, dedicates a hardware register called a "link pointer" to the "linkage information", i.e., the task-dependent storage. This is always restored after an interrupt so the task ALWAYS knows where its own storage is. All programs are shared. New storage for task-dependent data of a called function is always allocated whenever that function is first called by a new process. On Multics, the operating system takes care of all reentrancy/data mechanics.) If I had written the graphics system in assembly language, I could have dedicated a register to point to the task-dependent storage. This register would have to be reserved and untouched in the user program that calls the library function of course. There does not appear to be a way to do that if the calling program is written in C. My best guess at present is that to fake reentrancy with per-process global/persistent data, for a program written in C, where the shared code is responsible for allocating and deallocating its own global/persistent storage automatically, is to use FindTask with a null argument to get a pointer to the current task, then use tc_UserData as a storage location for the pointer to the task-dependent global/persistent storage area -- IF the Amiga designers meant for tc_UserData to be used by ME. Did they? My documentation says "per task data" but doesn't say WHOSE. (I see that it is NULL from within a CLI task.) Now even if I have that, I don't exactly like it. My "gstart" routine would have to call Forbid(), then FindTask so that the pointer could be stored in tc_UserData. Then I would have to store the pointer in a local automatic variable for further use within "gstart" then I'd have to call Permit(). That would be OK but EACH routine in the graphics library, subsequently called by the user program, would have to call FindTask to locate tc_UserData and store the pointer in a local automatic variable so that graphics mode data could be referenced through that structure pointer. What is the overhead for a FindTask? Now assuming the Commodore/Amiga people really don't want me mucking around with tc_UserData, I could define a 2xn vector in static storage that would contain the address of the current task block as determined by a FindTask call paired with the address of the corresponding global/persistent storage area. Then EACH routine in the graphics library would have to call FindTask, to get the task block pointer, then search the vector for that pointer which would yield the index of the corresponding address of global/persistent storage so that it could be placed in a local automatic variable. I don't like the idea of having to search that vector even though I know some great hash algorithms. So I really don't like any of the alternatives very well. Am I missing something? I figured out how to link to a library and pass floating point variables. I know how to DO reentrant code with per-process global/persistent data. I'm just not sure if there is an efficient way to do it in C on the Amiga. I definitely don't like the idea of the user having to pass a pointer to each routine he calls as is done with RPort. All of this began of course with the fact that the Manx and Lattice compilers cannot link each other's object segments. I had originally intended to create a library of object segments for linking in with the user program. Most of the larger machines I've worked with such as the Philco 2000, IBM 360/85, 3094-Q, Honeywell 6080, Multics and even Sun and Apollo have standard linkers on the system provided as part of the system. (At least I've always thought they were provided as part of the system.) On smaller machines I've worked on such as the Apple II and C-64, the linkers appear to have been specified by third-party software suppliers. The shared library method however seems to be a better answer than a link library if I can get it to work the way I want it to. I like the Amiga a lot. Don't misconstrue any of this as criticism of the design. Commodore/Amiga did a rather brilliant job. I am just trying to understand the Amiga conventions while attempting to implement some software whose interface I want to conform to some of my conventions. Is there an easier way to do what I want to do? _____ _ "For a successful technology, reality must take precedence" {____/ // "over public relations, for Nature cannot be fooled." \ // _ __Richard P. Feynman, Appendix F of Shuttle Disaster Report {____/