Message-Id: <199808080153.CAA17394@sable.ox.ac.uk> Comments: Authenticated sender is From: George Foot To: rylan AT inbtekom DOT co DOT za Date: Sat, 8 Aug 1998 02:51:55 +0000 MIME-Version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7BIT Subject: Re: How to generate a BIOS Int in inline AT&T Reply-to: george DOT foot AT merton DOT oxford DOT ac DOT uk CC: djgpp AT delorie DOT com Precedence: bulk On 7 Aug 98 at 20:08, Rylan wrote: > Hi, > > George Foot told me how to generate a BIOS int 16h (keyboard) in inline AT&T > assembler in DJGPP: > > >__dpmi_int is just a function, so you pass parameters in the usual > >way. For the parameters to __dpmi_int, which are nice parameters, > >you just push them onto the stack, four bytes each, right-to-left, > >and then do the (near) call: > > > movl _regs, %eax /* address of register block */ (note the mistake I pointed out before -- the above should use "$_regs") > > pushl %eax > > movl $0x10, %eax /* interrupt number (example) */ > > pushl %eax > > call ___dpmi_int /* (0x10, ®s) */ > > addl $8, %esp /* or popl twice */ > > /* now EAX is the return value */ > > >Since you're calling GCC-compiled code (or rather, code written to > >be called by GCC-compiled code) you must make sure the normal > >assumptions hold, e.g. ES=DS=CS=SS. Also note that it can clobber > >ECX, EDX, FS and GS without restoring them; if you care about their > >values, save them (perhaps on the stack before pushing parameters). > > My questions: > > 1) What are "nice parameters"? Integer numbers and pointers. Passing and returning structs is complicated and not necessary. I think floating point numbers are pushed onto the FPU stack. > 2) How do I "make sure the normal assumptions hold"? Restore ES, DS, CS and SS if you've changed them. Generally, avoid changing them; you can do whatever you like to FS and GS, so use those if possible. Things like lodsb, which you've used, obviously only work on DS or ES (depending which opcode we're talking about in particular), so changing away from those could have a performance hit, so you might not want to do this. > 3) What kind of entry and exit code is needed if the "assumptionss do not > hold" e.g. if ES NOT egual to ES=DS=CS=SS in the routine I want to call the > int in? As above, restore those four registers (perhaps pushing their values onto the stack first, in case you want them back again later). Also ensure that ESP is set sensibly before pushing parameters, so that when the parameters are pushed, the call is made, and the function itself pushes and pops things, nothing important is overwritten. In practice, so long as you never access anything below ESP you'll be fine. > My problem is that the code works 100% when in total isolation, but as soon > as I put it in my routine where I want to do the BIOS int (check for key > down during a loop to break the loop) it crashes the whole DOS box, dropping > me into W95. I think it's actually a problem with your code before calling __dpmi_int. It's hard to say though because you didn't include the code you used to call __dpmi_int. > void waitkey() Here it works fine - why?<-------------------------- > { > __asm__ __volatile__ > (" > movw $0x0,_r+28 > pushl $_r > pushl $0x16 > call ___dpmi_int > addl $8,%esp > "); > } Note that __dpmi_int is allowed to clobber ECX and EDX, and will return a value in EAX, yet you didn't put these on the clobber list. > int main(void) > { > > __asm__ __volatile__ > (" > pushw %%ds > pushl %%ebp > pushl %%esp Why push these, if you're going to save them into variables below anyway? > movw %%ds,_dssave > movl %%ebp,_ebpsave > movl %%esp,_espsave I'm not sure why you save them all, either; you don't change EBP and neither will __dpmi_int. __dpmi_int won't care what EBP is anyway. It will need ESP to be sensible, i.e. immediately underneath the parameters to the function. So the only thing worth saving is DS (and ES if you don't want to assume that it will equal DS). > movw _vid_descriptor,%%es > movw _vid_descriptor,%%ds --------->Is this the problem? What's `vid_descriptor'? I haven't seen you initialise it. I presume it's just 0? > xorw %%bx,%%bx > mainloop: > movw $320,%%si > movb $0x7d,%%ch > > pushw %%si > pushw %%cx You haven't initialisd CL, but you just pushed it onto the stack as part of CX. Note for later... > smoothloop: > lodsb You're dereferencing through ESI here, without having set its top word to anything. That's bad. > movb (%%esi),%%bl Note that BH might not be zero on later iterations of this loop. Perhaps you want to move the "xorw %%bx,%%bx" inside the loop. > addw %%bx,%%ax > movb 319(%%esi),%%bl > addw %%bx,%%ax > movb -2(%%esi),%%bl > addw %%bx,%%ax > > shrw $2,%%ax > movb %%al,-321(%%esi) > loop smoothloop > > popw %%di > popw %%cx Now you're popping off the CX value (with the low byte uninitialised) and storing it in DI, leaving the high word of EDI uninitialised. All through the next loop though you're dereferencing through EDI; effectively you're dereferencing through a random pointer. Also note that DI got the value that was in CX before, and CX got the value that was in SI before. Was this what you wanted? > randline: > mulw (%%edi) > incw %%ax > stosw > decw %%di I suggest "decl %%edi", having initialised the whole register. > loop randline > -------->If i put it here it crashes totally - what kind of entry and exit > code is needed to > -------->be able to push parameters for __dpmi_int HERE and call __dpmi_int > HERE > -------->in this stack register context? > -------->I've tried restoring DS,ES,FS,GS before calling but __dpmi_int > still crashes > -------->I want the loop to exit on a keypress (i. e. int 16h func 01h, then > jz mainloop) > -------->but I can't execute the int, no matter what. How about: movw _dssave, %%ds movw _dssave, %%es movw $1, _r+28 pushl $_r pushl $0x16 call ___dpmi_int addl $8, %%esp movw _vid_descriptor, %%ds movw _vid_descriptor, %%es testw $0x40, _r+32 jnz mainloop > finish: > popl %%esp > popl %%ebp > popw %%ds > " > : > : > : "ax","bx","cx","di","bp","sp","memory" You haven't clobbered BP or SP, because you just restored them. But you did clobber SI. > ); > > waitkey(); > unsetmode(); > exit(0); > } Of course, there may be other problems that I have missed. For this amount of code I'd be inclined to make it a function on its own, in a separate .S file. It's easier to debug, for one thing; you can get accurate information about where errors are, using `symify', in external assembler code, whereas in inline assembler it just points you to the start of the `asm' statement. Also, you can use the preprocessor in external code, to make macros, etc. "FLAGS(_r)" looks much nicer than "_r+32", for instance. -- george DOT foot AT merton DOT oxford DOT ac DOT uk