Mail Archives: djgpp/1998/08/07/21:54:42
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
- Raw text -