delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1998/08/07/21:54:42

Message-Id: <199808080153.CAA17394@sable.ox.ac.uk>
Comments: Authenticated sender is <mert0407 AT sable DOT ox DOT ac DOT uk>
From: George Foot <george DOT foot AT merton DOT oxford DOT ac DOT uk>
To: rylan AT inbtekom DOT co DOT za
Date: Sat, 8 Aug 1998 02:51:55 +0000
MIME-Version: 1.0
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

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, &regs) */
> >    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 -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019