minar@reed.UUCP (Nelson Minar) (11/07/90)
Summary: getting the environment (various segment registers, etc) right for C++ class-member-functions that are called via callback or interrupt. Exactly how is the memory reference to static members of objects implemented in TC++ 1.01? How does one write an assembly program that can interface cleanly with objects of classes (name demangling aside). I am trying to be clever in C++, and write a class Mouse. The specific model I am interested in adopting is somewhat similar to a Macintosh: there is a mouse object who has a member function getNextEvent(). When that function is called, the first event on the queue (button down or button up events, specifically) is returned. In order to accomplish that, I am hooking the mouse driver to call a function I specify (via INT 0x33, service 0x0c) upon mouse events (button presses). This function should examine the mouse event the driver has notified me of and store it on the queue. Currently I am working in the small model. The problem is, when my function 'static void far Mouse::handler()' is called by the mouse callback mechanism, the CPU is a little confused. (Note that this call itself is NOT a software interrupt, it is a plain old far call) The confusion is a result of the data segment being set to the DS of the mouse driver, and not the DS of the object which I am trying to queue mouse events into (I assume, other environmental aspects besides the DS are munged, too). I can get around the data segment problem by being really clever, storing the data segment of the mouse object into global far memory (upon initialization of the Mouse object), and having my handler() load the data segment explicitly (_DS = globalSavedDS). Doing this gets me pretty far: global near variables, as well as instance variables of the class Mouse can be diddled with. (so, for example, the instance variable Mouse::count can be incremented with success within the handler()). However, when I go to call my (debugged) enque() to store the event I have caught into the queue, all hell breaks loose. I need to use the pointer to the queue that is stored as a member variable of my class Mouse. I can find it, by whacking at the data segment. However, the first call used off of this pointer and the stack overflow logic kicks in and reports "Stack Overflow!" (disabling the SO checking just makes my program crash the machine) I can't really tell how the SO logic is supposed to work, but I dont think its the stack pointer that is triggering the error, but rather the number in memory it is comparing it to (which is no doubt in the wrong segment). But I can't be sure. Regardless, this is a symptom of a larger problem. I've tried to be less clever. I tried just compiling the dumb thing under the large model, with no monkeying of the data segment. I tried converting all the pointer references to far pointer references. No luck. I would love to declare the whole Mouse object to be far ('Mouse far myMouse'). That's illegal, according to TC++ 1.01. I even tried making my handler function not actually be a member function of the class, and to get at the data via a globally-stored 'this' pointer. This got confusing quickly, and seemed to be the wrong answer. I need to know more about the internals of the machine and compiler, even though I don't want to. I want to be able to set up the same ENVIRONMENT upon mouse-driver-callback that would be there if the function were called through more normal means. Finding the correct DS is a long way towards the goal, but other things are wrong. I have considered junking the whole C++ handler function, and writing the code in assembly. Borland's documentation concerning assembly interfacing to C++ code (not C code) is nonexistant. Furthermore, the same problem exists: how do I create that environment? Has anyone done something like this? I assume the problem is the same for member functions of classes that are called within interrupts (as opposed to callbacks, like my application), and as such, should be fairly common in some systems programming. The whole point of C++ is that one should still be able to get to the bare metal if needed. This one isn't working. (some code, pared down) // the actual Mouse. Then handler() function is called asynchrously, with // an environment that is all messed up.. class Mouse { public: enum mStatus {ok, error}; mStatus reset(); // reset mouse driver, hook // the mouse driver MouseEvent getNextEvent(); private: static void far handler(); // mouse driver callback func static MEventQueue * eq; // queue of events }; // the Queue of events. The definiton of class Node is left out for brevity class MEventQueue { public: void enq(MouseEvent); // add a new data item private: Node * q; }; // drop a data item at the end of the linked list void MEventQueue::enq(MouseEvent d) { if (q == NULL) { // first element q = new Node(d); // all thats needed } else { Node * p = q; while (p->nextNode != NULL) // find the end of the list p = p->nextNode; p->nextNode = new Node(d); // add the new node } return; } // the handler code itself. This is called from anywhere in the address // space, and God only knows what the various registers (DS, SS included) // are going to look like. unsigned globalDS; // initialized to the data // segment storing the mouse // object (this works) void far Mouse::handler() { unsigned tempDS = _DS; // diddle with the segs so the _DS = globalDS; // small model works ok eq->enq(MouseEvent(buttonDown, leftB, 0, 0)); // the constructor call here // fails. So does any call of // a function, with // 'Stack Overflow!' count++; // this will work, simply // incrementing a member // variable. Grr! _DS = tempDS; // restore the old seg regs } Thank you for reading this far. Not many have, no doubt. __ \/ minar@reed.bitnet You know in your heart its flat. -- __ \/ minar@reed.bitnet You know in your heart its flat.