gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

ASM > Help with some inline ASM

#1275 - animension - Thu Jan 16, 2003 4:00 am

I'm trying to access the BIOS divide utility (SWI 6) and I'm having trouble inlining the ASM calls. Here's the source code:

Code:

void BIOSDIV(
   signed long numerator,
   signed long denominator,
   signed long *div,
   signed long *mod,
   signed long *abs
){

   asm volatile(
      "
   @ SUBSTITUTION LIST
   @ 0: %0
   @ 1: %1
   @ 2: %2
   @ 3: %3
   @ 4: %4

   ldr      r0,=%3   @ Load the value of %3(numerator) into r0
   ldr      r1,=%4   @ Load the value of %4(denominator) into r1
   swi      6      @ Call BIOS SWI 6 (DIV)
   str      r0,[%0]   @ Store value of r0 (numerator DIV denominator) into &div
   str      r1,[%1]   @ Store value of r1 (numerator MOD denominator) into &mod
   str      r2,[%2]   @ Store value of r2 (abs of numerator DIV denominator) into &abs
      "
      : "=r"(div), "=r"(mod),"=r"(abs)   // OUTPUT LIST
      : "r"(numerator), "r"(denominator)   // INPUT LIST
      : "r0","r1","r2","r3","r4","r5"      // CLOBBER LIST
   );
}


What I get from the compiler is:
Code:

test.o: In function `BIOSDIV(long, long, long*, long*, long*)':
test.o(.text+0x88): undefined reference to `lr'
test.o(.text+0x8c): undefined reference to `ip'


A look at the asm file that is generated by the compiler shows:
Code:

_Z7BIOSDIVllPlS_S_:
   @ Function supports interworking.
   @ args = 4, pretend = 0, frame = 0
   @ frame_needed = 0, current_function_anonymous_args = 0
   stmfd   sp!, {r4, r5, r6, lr}
   mov   lr, r0
   mov   ip, r1
   
   @ SUBSTITUTION LIST
   @ 0: lr
   @ 1: r6
   @ 2: ip
   @ 3: lr
   @ 4: ip

   ldr      r0,=lr   @ Load the value of lr(numerator) into r0
   ldr      r1,=ip   @ Load the value of ip(denominator) into r1
   swi      6      @ Call BIOS SWI 6 (DIV)
   str      r0,[lr]   @ Store value of r0 (numerator DIV denominator) into &div
   str      r1,[r6]   @ Store value of r1 (numerator MOD denominator) into &mod
   str      r2,[ip]   @ Store value of r2 (abs of numerator DIV denominator) into &abs
      
   ldmfd   sp!, {r4, r5, r6, lr}
   bx   lr


Why is this in C++ inline ASM:
Code:

   @ SUBSTITUTION LIST
   @ 0: %0
   @ 1: %1
   @ 2: %2
   @ 3: %3
   @ 4: %4


Turning into this in generated ASM:
Code:

   @ SUBSTITUTION LIST
   @ 0: lr
   @ 1: r6
   @ 2: ip
   @ 3: lr
   @ 4: ip


Does anyone have any idea why the substitions aren't working? I'm totally new at inlining ASM so any help would be appreciated.

Thanks!

#1305 - DekuTree64 - Thu Jan 16, 2003 5:22 pm

Well, appearently it's using all sorts of registers for the inputs, but I always thought it used the stack instead.
Well I use this, and I know it works, so try it instead:

void divfull(s32 numerator, s32 denomenator,
s32 *quotient, s32 *remainder, s32 *abs)
{
asm volatile
("
mov r0, %3
mov r1, %4
swi 0x60000
ldr r2, %0
str r0, [r2]
ldr r2, %1
str r1, [r2]
ldr r2, %2
str r3, [r2]
"
: "=m" (quotient), "=m" (remainder), "=m" (abs)
: "r" (numerator), "r" (denomenator)
: "r0", "r1", "r2", "r3"
);
}

#1306 - DekuTree64 - Thu Jan 16, 2003 5:26 pm

Ah, there was the problem, you were putting "=r"(output) instead of "=m"(output), so it was just picking registers to put those in. No idea why it would pick so many important ones, especially ip, but oh well^_^

#1326 - animension - Thu Jan 16, 2003 7:48 pm

DekuTree64 wrote:
Ah, there was the problem, you were putting "=r"(output) instead of "=m"(output), so it was just picking registers to put those in. No idea why it would pick so many important ones, especially ip, but oh well^_^


I don't seem to grasp the difference... the documentation says that "r" simply means a generic register but that "m" means a memory address.
Quote:

r
A register operand is allowed provided that it is in a general register.

m
A memory operand is allowed, with any kind of address that the machine supports in general.


Isn't a general register able to handle a memory address? I'm not sure I understand the black magic that's going on behind the scenes...
_________________
"Beer is proof that God loves us and wants us to be happy."
-- Benjamin Franklin

#1343 - animension - Thu Jan 16, 2003 9:23 pm

Also another question: is the BIOS divide utility more efficient than using the "/" and "%" operators in C?
_________________
"Beer is proof that God loves us and wants us to be happy."
-- Benjamin Franklin

#1344 - Splam - Thu Jan 16, 2003 9:28 pm

The answer to that is a resounding and very big YES ;)

#1346 - animension - Thu Jan 16, 2003 10:17 pm

Another question: is it possible with C++ to make an operator use a different function, somewhat like operator overloading, but with primitive types? Essentially, is it possible to replace the "/" and "%" operators to call a function that retreives their respective values after a BIOS divide instead? Like, make the "/" operator point to and call a function that does a BIOS divide and return the quotient and "%" do a BIOS divide and return the modulus?
_________________
"Beer is proof that God loves us and wants us to be happy."
-- Benjamin Franklin

#1355 - tepples - Thu Jan 16, 2003 11:56 pm

animension wrote:
Another question: is it possible with C++ to make an operator use a different function, somewhat like operator overloading, but with primitive types?

C++ does not support replacing the primitive types' operator actions. You'll need to make a class.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#1358 - animension - Fri Jan 17, 2003 12:06 am

DekuTree64 wrote:
Well, appearently it's using all sorts of registers for the inputs, but I always thought it used the stack instead.
Well I use this, and I know it works, so try it instead:

void divfull(s32 numerator, s32 denomenator,
s32 *quotient, s32 *remainder, s32 *abs)
{
asm volatile
("
mov r0, %3
mov r1, %4
swi 0x60000
ldr r2, %0
str r0, [r2]
ldr r2, %1
str r1, [r2]
ldr r2, %2
str r3, [r2]
"
: "=m" (quotient), "=m" (remainder), "=m" (abs)
: "r" (numerator), "r" (denomenator)
: "r0", "r1", "r2", "r3"
);
}


I cut and pasted this and I get the following compiler errors:
Code:


---------- Compile ROM ----------
test.cpp: In function `void divfull(int, int, s32*, s32*, s32*)':
test.cpp:34: output number 1 not directly addressable
test.cpp:34: output number 2 not directly addressable
test.cpp:50: confused by earlier errors, bailing out
make: *** [test.o] Error 1
Output completed (0 sec consumed) - Normal Termination

_________________
"Beer is proof that God loves us and wants us to be happy."
-- Benjamin Franklin

#1367 - DekuTree64 - Fri Jan 17, 2003 5:30 am

Hmm, well I copy/pasted it straight from my game, so I don't know why it wouldn't work. Well, try using your code, but just replacing the "=r" with "=m" and see if that fixes it.
And about your question on the difference between r and m, at least as far as I know, you need to use m when you'll be storing something back to memory, and r when you just want to put it in a register and leave it there. I'm not too clear on inline ASM myself though, I pretty much copy/pasted and guessed my way into getting it working. One of these days I'll get around to making a pure ASM div function instead though, so you might want to just do that to begin with too.

#1399 - tepples - Sat Jan 18, 2003 5:23 am

This worked for me, but it's not the most efficient:
Code:

/* bios_divmod() ***********************
   Convert n/d to a mixed fraction q+r/d
   Returns Q.
*/
int bios_divmod (int num, int den, int *rem)
{
  int quo;

  asm volatile (
    " mov   r0,%2  \n"
    " mov   r1,%3  \n"
    " swi   6      \n"  /* in ARM, s/6/0x60000/ */
    " mov   %0,r0  \n"
    " ldr   r2,%1  \n"
    " str   r1,[r2]\n"
    : "=r" (quo), "=m" (rem) // Outputs
    : "r" (num), "r" (den)   // Inputs
    : "r0","r1","r2","r3"    // clobber
  );
  return quo;
}


Normally, I use this external asm subroutine, which even handles divide-by-zero in a rational manner (important for some projection code):
Code:

@ int dv(int num, int den)
@ Divide two signed integers.

.THUMB
.THUMB_FUNC
.ALIGN
.GLOBL  dv

dv:
  cmp r1, #0
  beq 0f
  swi 6
  bx lr
0:
  ldr r0, =0x7fffffff
  bx lr
[/code]
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.