jwbirdsa@amc.com (James Birdsall) (05/21/91)
Anyone writing interrupt handlers using TC++'s "interrupt" keyword should be aware that DS is not preserved into the function. DS is preserved through the function; the value of DS after the interrupt is the same as before. However, the value of DS while the function is executing is not the same as the value when it was called. The entry code pushes all the registers on the stack and then reloads DS with the value for the program's near data segment. This doesn't matter for most interrupt handlers, but if you're trying to revector an interrupt that uses DS as a parameter (for example, the DOS interrupt (0x21)), then it becomes a major obstacle. This behavior has been observed with Borland C++ 2.0. Presumably it is the same with TC++ 1.0, and probably plain TC. It is not mentioned as such in the documentation, although if you read it carefully it is implied. Anyway, having spent an hour or two tracing the errors caused by this behavior, I thought it worth posting a public notice to save other people the trouble. -- James W. Birdsall WORK: jwbirdsa@amc.com {uunet,uw-coco}!amc-gw!jwbirdsa HOME: {uunet,uw-coco}!amc-gw!picarefy!jwbirdsa OTHER: 71261.1731@compuserve.com ================== Kitten: a small homicidal muffin on legs. ================== =========== "For it is the doom of men that they forget." -- Merlin ===========
dmurdoch@watstat.waterloo.edu (Duncan Murdoch) (05/21/91)
In article <1991May20.171545.23591@amc.com> jwbirdsa@polaris.amc.com () writes: > > Anyone writing interrupt handlers using TC++'s "interrupt" keyword >should be aware that DS is not preserved into the function. DS is preserved >through the function; the value of DS after the interrupt is the same as >before. However, the value of DS while the function is executing is not the >same as the value when it was called. The entry code pushes all the >registers on the stack and then reloads DS with the value for the program's >near data segment. > > This doesn't matter for most interrupt handlers, but if you're trying to >revector an interrupt that uses DS as a parameter (for example, the DOS >interrupt (0x21)), then it becomes a major obstacle. > > This behavior has been observed with Borland C++ 2.0. Presumably it is >the same with TC++ 1.0, and probably plain TC. It is not mentioned as such >in the documentation, although if you read it carefully it is implied. >Anyway, having spent an hour or two tracing the errors caused by this >behavior, I thought it worth posting a public notice to save other people >the trouble. I don't have any of the C flavours, but I'm surprised it's not documented there. In TP, the prologue of an Interrupt procedure is explicitly given; it says that DS is changed to the ISR's data segment. If you want the old DS, you just read the 3rd argument from the end; presumably in BC++ that would be the 3rd from the beginning. Duncan Murdoch dmurdoch@watstat.waterloo.edu
ressler@CS.Cornell.EDU (Gene Ressler) (05/21/91)
In article <1991May20.230908.7178@maytag.waterloo.edu> dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes: >In article <1991May20.171545.23591@amc.com> jwbirdsa@polaris.amc.com () writes: >> >> Anyone writing interrupt handlers using TC++'s "interrupt" keyword >>should be aware that DS is not preserved into the function. DS is preserved >>through the function; the value of DS after the interrupt is the same as >>before. However, the value of DS while the function is executing is not the .... >I don't have any of the C flavours, but I'm surprised it's not documented >there. In TP, the prologue of an Interrupt procedure is explicitly given; .... >Duncan Murdoch >dmurdoch@watstat.waterloo.edu Both my TC++ manual (PG, pg 48) and my TC2.0 manual (UG, pg 319) state that DS is set. What else could be done to provide access to static data? This `behavior' is, practically speaking, the only one possible. (...that is, short of allowing statics in the code segment (ugh). I believe MSC provides this option.) Chalk up one more for the joys of segmented architectures. ;-> Gene
tjr@cbnewsc.att.com (thomas.j.roberts) (05/21/91)
From article <1991May20.171545.23591@amc.com>, by jwbirdsa@amc.com (James Birdsall): > Anyone writing interrupt handlers using TC++'s "interrupt" keyword > should be aware that DS is not preserved into the function. DS is preserved > through the function; the value of DS after the interrupt is the same as > before. However, the value of DS while the function is executing is not the > same as the value when it was called. The entry code pushes all the > registers on the stack and then reloads DS with the value for the program's > near data segment. > > This doesn't matter for most interrupt handlers, but if you're trying to > revector an interrupt that uses DS as a parameter (for example, the DOS > interrupt (0x21)), then it becomes a major obstacle. This is NECESSARY for Turbo C / Turbo C++ / Borland C++ functions to work correctly (they may reference static variables). It is well documented in the manuals. If you need the caller's DS (or any other register), it (they) are available as arguments to your interrupt function. This is not an obstacle, it is an essential feature. I am astounded that you are attempting to use this complicated CPU-dependent and compiler-dependent feature without CAREFULLY reading the manuals. Note that unlike normal C functions, modifying the parameters to an interrupt function within the function will change the values of registers used by the caller upon its return - great care is needed! READ THE MANUAL! The truly hard thing to do is to chain to the previous interrupt handler with the caller's registers, and arrange for the previous handler to return registers to the caller (this is usually what you want to do when "wrapping" int 21H, et al). Borland did not include an appropriate function in the library (I believe it would really need to be a "psuedo-function", as its semantics could not be those of a normal C function). Thus, to do this you must use assembly language to restore the stack and registers and then jump to the previous handler's address (without modifying ANY registers!). It can be done, but is subtle..... Tom Roberts att!ihlpl!tjrob TJROB@IHLPL.ATT.COM
sorrow@oak.circa.ufl.edu (05/21/91)
In article <1991May20.230908.7178@maytag.waterloo.edu>, dmurdoch@watstat.waterloo.edu (Duncan Murdoch) writes: |>In article <1991May20.171545.23591@amc.com> jwbirdsa@polaris.amc.com () writes: |>> |>> Anyone writing interrupt handlers using TC++'s "interrupt" keyword |>>should be aware that DS is not preserved into the function. DS is preserved |>>through the function; the value of DS after the interrupt is the same as |>>before. However, the value of DS while the function is executing is not the |>>same as the value when it was called. The entry code pushes all the |>>registers on the stack and then reloads DS with the value for the program's |>>near data segment. |>> |>> This doesn't matter for most interrupt handlers, but if you're trying to |>>revector an interrupt that uses DS as a parameter (for example, the DOS |>>interrupt (0x21)), then it becomes a major obstacle. |>> |>> This behavior has been observed with Borland C++ 2.0. Presumably it is |>>the same with TC++ 1.0, and probably plain TC. It is not mentioned as such |>>in the documentation, although if you read it carefully it is implied. |>>Anyway, having spent an hour or two tracing the errors caused by this |>>behavior, I thought it worth posting a public notice to save other people |>>the trouble. Actually, I found out that I must be doing something terribly wrong. When I installed my own mouse interrupt handler, if the function was declared as "void far interrupt" it would die. "void far" worked just fine. I just don't USE the interrupt keyword. The documentation is too skimpy on it for me to be able to use it decently. Brian
ekalenda@cup.portal.com (Edward John Kalenda) (05/22/91)
>Anyone writing interrupt handlers using TC++'s "interrupt" keyword >should be aware that DS is not preserved into the function. DS is preserved >through the function; the value of DS after the interrupt is the same as >before. However, the value of DS while the function is executing is not the >same as the value when it was called. The entry code pushes all the >registers on the stack and then reloads DS with the value for the program's >near data segment. > >This doesn't matter for most interrupt handlers, but if you're trying to >revector an interrupt that uses DS as a parameter (for example, the DOS >interrupt (0x21)), then it becomes a major obstacle. This is also the behaviour under Microsoft C. You can have access to all the registers pushed if you declare them as shorts in the formal parameter list of the function. The return address and flags are also available this way. I had to look at the code generated to see the order they push things so it is a bit version dependant but it worked for one project I did. Ed ekalenda@cup.portal.com
anicolao@watcgl.waterloo.edu (Alex Nicolaou) (05/22/91)
In article <00948EEF.C05FAA40@MAPLE.CIRCA.UFL.EDU> sorrow@oak.circa.ufl.edu writes: >Actually, I found out that I must be doing something terribly wrong. When >I installed my own mouse interrupt handler, if the function was declared >as "void far interrupt" it would die. "void far" worked just fine. ** Hmm. What do you mean you "installed my own mouse interrupt handler"? If you mean via the services provided by the mouse driver (12h or 24h, I think) which make the mouse driver call your function, then NO, you CANNOT use the interrupt keyword; for a very simple reason: you aren't the interrupt handler, you are being called by it, and you are supposed to be a routine. If you use an interrupt function, the BC++ compiler will put an iret instruction in for you, and that will terminate the interrupt handling prematurely - your Microsoft compatible driver will not have finished its work and your computer will dissappear into limbo. Incidentally, if you are doing this and then accessing data, you'd better be sure that you're restoring DS yourself, since the MS mouse driver won't do it for you - if you compile in Medium or Large models this is an absolute must, in smaller models you ought to do it for painless changes later. >I just >don't USE the interrupt keyword. The documentation is too skimpy on it >for me to be able to use it decently. ** Interrupts are a pain, but that is not a function of the documentation. Yes, I am another borland fan :-) alex
grimlok@hubcap.clemson.edu (Mike Percy) (05/22/91)
sorrow@oak.circa.ufl.edu writes: >Actually, I found out that I must be doing something terribly wrong. When >I installed my own mouse interrupt handler, if the function was declared >as "void far interrupt" it would die. "void far" worked just fine. I just >don't USE the interrupt keyword. The documentation is too skimpy on it >for me to be able to use it decently. If you are talking about mouse function 12(?), theone that lets you have the mouse driver call your function whenever certain mouse events happen, then you are on the right track. The "interrupt" handler you give to the mouse is not of type void interrupt. TurboC interrupt functions push regs to the stack, do your stuff, pop the regs and -- IRET. This is what screws you. The driver does a simple far CALL to your routine, expecting to have control come back to it with a far RET. The IRET is simply wrong here. Declaring your mouse function to be void far fixes this problem, but you still cannot access global/static data except by sheer accident, as DS will likely not be pointing to your DGROUP. Declaring it as void huge does this trick. But the mouse driver also apparantly assumes a called-saved register scheme, while the C compiler assumes a caller-saved scheme. Your routine can trash registers the mouse driver expected you to leave alone. I use #pragma savregs. This guarantees that a huge function will not change the value of any of the register when it is entered. I like the pragma rather than declaring the function as _saveregs. _saveregs pushes all register and pops them at the end. This is costly in time/space. The #pragma saveregs costs more at compile time, but is much smaller/faster at runtime. In short, when installing a mouse handler (function 12), I declare it #pragma saveregs void huge handler(void) { /* handler code */ } I spent a lot of time before getting this to work... "I don't know about your brain, but mine is really...bossy." Mike Percy grimlok@hubcap.clemson.edu ISD, Clemson University mspercy@clemson.BITNET (803)656-3780 mspercy@clemson.clemson.edu
sorrow@oak.circa.ufl.edu (05/22/91)
In article <1991May21.195106.1530@watcgl.waterloo.edu>, anicolao@watcgl.waterloo.edu (Alex Nicolaou) writes: |>In article <00948EEF.C05FAA40@MAPLE.CIRCA.UFL.EDU> sorrow@oak.circa.ufl.edu writes: |>>Actually, I found out that I must be doing something terribly wrong. When |>>I installed my own mouse interrupt handler, if the function was declared |>>as "void far interrupt" it would die. "void far" worked just fine. |> |> ** Hmm. What do you mean you "installed my own mouse interrupt handler"? If |>you mean via the services provided by the mouse driver (12h or 24h, I think) |>which make the mouse driver call your function, then NO, you CANNOT use the |>interrupt keyword; for a very simple reason: you aren't the interrupt handler, Thanks for the info. Right you are. I did use the functions, and didn't think to even think about the fact that INT0x33 was still in place and thus I wasn't the interrupt handler. Whoops. (:-> Brian
jwbirdsa@amc.com (James Birdsall) (05/22/91)
Allow me to clarify my earlier posting. Also please note that I was in a bad mood that day and not feeling kindly disposed toward the compiler in question. In article <1991May21.140557.16018@cbnewsc.att.com> tjr@cbnewsc.att.com (thomas.j.roberts) writes: >From article <1991May20.171545.23591@amc.com>, by jwbirdsa@amc.com (James Birdsall): >> Anyone writing interrupt handlers using TC++'s "interrupt" keyword >> should be aware that DS is not preserved into the function. DS is preserved >> through the function; the value of DS after the interrupt is the same as >> before. However, the value of DS while the function is executing is not the >> same as the value when it was called. The entry code pushes all the >> registers on the stack and then reloads DS with the value for the program's >> near data segment. >> >> This doesn't matter for most interrupt handlers, but if you're trying to >> revector an interrupt that uses DS as a parameter (for example, the DOS >> interrupt (0x21)), then it becomes a major obstacle. > >This is NECESSARY for Turbo C / Turbo C++ / Borland C++ functions to >work correctly (they may reference static variables). No. It is HANDY. There are other ways that interrupt functions could be allowed to address static data. They would require special code generation and run more slowly, so I can understand why they did it that way. Indeed, the fact that one can access global variables should have tipped me off, and as soon as I found out what was happening, I knew why. >It is well >documented in the manuals. Maybe. I took a fast look and didn't see it. Considering the experiences I've had with Borland documentation in the past, I wasn't hopeful. >If you need the caller's DS (or any other register), it (they) are >available as arguments to your interrupt function. This is not an obstacle, >it is an essential feature. I am astounded that you are attempting to use >this complicated CPU-dependent and compiler-dependent feature without >CAREFULLY reading the manuals. I was attempting to throw together a fast test program to determine whether setvect(), which I was trying to use in an entirely separate program to revector int 21h to an assembly-language handler, was corrupting DOS. It was based on their example under setvect(), which worked fine as given but crashed the system when revectoring 0x21 to a function which simply called the old handler. When I revectored manually, fprintf() would emit a line of junk but the system didn't crash, and suddenly I was enlightened. The original problem turned out to be a stack misalignment error aggravated by a confused debugger. The debugger would hang when the new handler chained back to the original handler, which was NOT the location of the bug that took out the system when the debugger was not used. Thus, it appeared that something was munging part of DOS and the only candidate I had was setvect(), because the program worked fine without it and the new handler was not writing to anyplace it wasn't supposed to... After a day like that (I didn't identify the original problem until after posting the article), you may understand why I was feeling betrayed by my compiler. :) -- James W. Birdsall WORK: jwbirdsa@amc.com {uunet,uw-coco}!amc-gw!jwbirdsa HOME: {uunet,uw-coco}!amc-gw!picarefy!jwbirdsa OTHER: 71261.1731@compuserve.com "The OS shouldn't die every time the controller drools on a sector." -- a sysop =========== "For it is the doom of men that they forget." -- Merlin ===========
jml@stekt.oulu.fi (Lepp{j{rvi Jouni) (05/22/91)
As stated earlier, this 'feature' is indeed needed. I however, come to think a side effect not mentioned in the previous postings (?) : DS is changed, but SS is still the one the interrupted program / a program which called an interrupt had. This means that pointers to any 'automatic' variables (in small data models) are, in fact, incorrect. Even if the programmer doesn't use these directly, some library function assumed to be safe (~no DOS calls) might. In most cases this is not an issue, since an interrupt handler performing anything stack intensive _should_ swap to a stack of its own (usually a static array) due to the fact that the stack size at call time is usually unknown. However, this might be the cause of *the mystery bug*. (You know, when there seems to be nothing wrong with the code after 100 or so checks etc ;-) (Swapping the stack, of course, takes the register arguments out of reach. (The reason (BTW) why function arguments (such as the registers) and automatic variables work from the foreign stack, is that they are accessed relative to BP, which by default is an offset relative to SS.)) -- - Jouni Lepp{j{rvi / jml@stekt.oulu.fi - - '.. but only maybe, life is a joy .. ' -
b-davis%cai.utah.edu@cs.utah.edu (Brad Davis) (05/22/91)
In article <1991May21.195106.1530@watcgl.waterloo.edu> anicolao@watcgl.waterloo.edu (Alex Nicolaou) writes: > ** Interrupts are a pain, but that is not a function of the documentation. As Alex said interrupts are a pain (any form of multi-tasking is) but you can always have the compiler generate assembler to read and check. If you can't read 8086 code and/or don't know what the hardware does you shouldn't be trying to write interrupt handlers. >Yes, I am another borland fan :-) Me too (and so is my fully interrupt driven, buffered, handshaking, with up calls to other routines serial port driver, all written in Wizard/Turbo/Borland C). -- Brad Davis ..!uunet.uu.net!cs.utah.edu!cai.utah.edu!b-davis b-davis@cs.utah.edu, b-davis@cai.utah.edu One drunk driver can ruin your whole day.
dave@tygra.Michigan.COM (David Conrad) (05/27/91)
In article <1991May21.195106.1530@watcgl.waterloo.edu> anicolao@watcgl.waterloo.edu (Alex Nicolaou) writes: >; for a very simple reason: you aren't the interrupt handler, >you are being called by it, and you are supposed to be a routine. If you use >an interrupt function, the BC++ compiler will put an iret instruction in >for you, and that will terminate the interrupt handling prematurely > >alex ??? You make it sound as if that iret is going to somehow magically return to the program which generated the interrupt - it'll return to the caller, i.e. the interrupt handler, but it will pop an extra word off the stack (looking for the flags), corrupting the caller's stack. (Of course, the previous message said something about, "I just DON'T use the interrupt keyword - the documentation is too skimpy." As if the interrupt keyword was simply a matter of style, having no consequence in terms of code generated. (Sorry, I deleted that part of the article.) Confusion abounds.) -- David Conrad, dave@michigan.com -- = CAT-TALK Conferencing Network, Computer Conferencing and File Archive = - 1-313-343-0800, 300/1200/2400/9600 baud, 8/N/1. New users use 'new' - = as a login id. AVAILABLE VIA PC-PURSUIT!!! (City code "MIDET") = E-MAIL Address: dave@Michigan.COM