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 > Attempting to copy memory using asm

#177037 - Timofiend - Fri Dec 02, 2011 2:58 am

So I am attempting to copy some character data from a header file into the character base block, but I have gone wrong somewhere. It compiles, but when linking I am told that in another function there is an undefined reference to "r7" and "ip"

This is how I am trying to copy the data:
Code:

   asm volatile(
   ".arm\n\t"
   "mov r3, #0\n\t"
   "ldr r0, =%[fontcbb]\n\t"
   "ldr r1, =%[fontdata]\n"
   "for_loop:\n\t"
   "ldr r2, [r1, r3, lsl #2]\n\t"
   "str r2, [r0, r3, lsl #2]\n\t"
   "add r3, #1\n\t"
   "cmp r3, #7\n\t"
   "blt for_loop\n\t"
   ".code 16"
   : [fontcbb] "+r" (fontCBB2)
   : [fontdata] "r" (fontData)
   : "cc", "r0", "r1", "r2", "r3"
   );


FontCBB2 is a 32 bit pointer to the location in the character base block I am trying to write to, and fontData is a 32 bit pointer to my array full of character data.

I tried removing the function that was referenced, but it just caused the same problem with a different function, so I assume I have done something wrong in the asm code.

Any ideas? I am fairly new at this so if theres anything else I haven't included that would help locate the problem just ask. I am trying to integrate this into my c code.


On a different note while I am here I had another question - When using labels for branches in asm code that is in a function, I have no troubles as long as the function is being called once, but if I call the function more than once I am told the error message that my labels are already define, is there something I should be doing to avoid that?

Here is an example of what I am doing, this is in a print text function I have written to avoid the text going off the screen

Code:
      asm volatile(
      "cmp %[tempOut], #32\n\t"
      "blt end\n"
      "loop1:\n\t"
      "sub %[tempOut], %[tempIn], #32\n\t"
      "cmp %[tempOut], #31\n\t"
      "bgt loop1\n\t"
      "cmp %[tempOut], #31\n\t"
      "bne skip\n\t"
      "add %[cursorPosOut], %[cursorPosIn], #1\n"
      "skip:"
      "cmp %[tempOut], #32\n\t"
      "bne end\n\t"
      "add %[cursorPosOut], %[cursorPosIn], #2\n"      
      "end:\n\t"
      :[tempOut] "+r" (temp), [cursorPosOut] "+r" (cursorPos)
      :[tempIn] "r" (temp), [cursorPosIn] "r" (cursorPos)
      );


We have been asked to demonstrate knowledge of how to integrate asm into our code - if you were wondering why I am using this.

Thanks

#177038 - Dwedit - Fri Dec 02, 2011 7:54 am

When you do ARM inline assembly, you need to specify input, output, and clobber lists. See the "ARM GCC Inline Assembler Cookbook" for more details.

And you can't just do a .code32 as your first line, you need to force a mode change from THUMB to ARM by using a "bx" instruction, like this:
Code:

adr r0,SomeLabel
bx r0
.code32
SomeLabel:
 nop
.code16



For the labels problem, this is because of function inlining. You can use unnamed numeric labels instead, so instead of code like this:
Code:
loop1:
 nop
 subs r0,r0,#1
 bne loop1

you would use this type of label:
Code:
0:
 nop
 subs r0,r0,#1
 bne 0b

You use just the number to define the label, then when you use the label, you specify the direction, "b" for backwards, and "f" for forwards.

There is no limit to how many times you can define a numeric label. Only problem is that if you copy-paste code, you may be adding a conflicting numeric label where it didn't exist before, and that would make your branches go to the wrong place.

If you don't want to use the unnamed numeric labels, you can also set of the attributes of the function to force it to never be inlined.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#177042 - Timofiend - Fri Dec 02, 2011 1:03 pm

Thanks for the reply. I have looked through the cookbook, and quite a few other bits of documentation. I was under the impression that when using multiple lines of code I could specify the input/output and clobber lists at the end of the whole code snippet rather than on a per-line basis? That what I was doing previously with other snippets and it seemed to work fine.

Also I thought the .code32 was forcing the mode change? Do I specifically need to use arm as I am trying to load a 32bit address? I only changed it because I couldnt logical shift left within my ldr statement using just thumb, but if I were to be loading 32bit addresses I would need to be doing it in arm anyway wouldnt I?

Thanks for the help, and I shall try using the unnamed numeric labels instead thanks for that as well :)

#177048 - Miked0801 - Fri Dec 02, 2011 4:32 pm

Thumb allows 32-bit addressing just fine. You are just limited to the number of registers that you can access, you get a few less op-codes, and you don't get built in shifting and conditional compiler per op-code.

#177060 - Cearn - Sat Dec 03, 2011 11:23 am

Did the assignment specifically ask for inline assembly? If not, using a separate assembly file might be easier as you won't have to worry about its particular syntax and quotes and \ns and such. If you're using the template makefiles, you don't even have to do anything extra to assemble it.

The basic framework would be
Code:

Basic rules for assembly:
* r0-r3 are the first 4 parameters of the function, the rest goes onto the stack.
* r0-r3, r12 are free to use as you will. When using other registers, push them onto the stack first and pop them when you're done.
* Return value goes in r0.
* Return from the function via bx lr


// --- in assembly file (say, foo.s) ---
@ int asm_add(int a, int b);
@ Add two numbers and return them.

@ Standard boiler plate:
    .text                  @ Put in into the code section
    .align  2              @ Align function to 1<<2
    .thumb                 @ Use Thumb code
    .thumb_func            @ This will be thumb function (seems redundant, but really isn't)
    .global asm_add        @ Make function accessible to outside world.
asm_add:
    add     r0, r1         @ r0 = r0 + r1
    bx      lr


// --- In C file ---

int asm_add(int a, int b);

int c= asm_add(5, 4);


More complicated example of ARM function: :

memcpy32.s:
Code:

@ === void memcpy32(void *dst, const void *src, uint nwords) IWRAM_CODE; =============
@ r0, r1: dst, src
@ r2: nwords, then nwords>>3
@ r3-r10: data buffer
@ r12: nwords&7
    .section .iwram,"ax", %progbits
    .align  2
    .code   32
    .global memcpy32
memcpy32:
    and     r12, r2, #7     @ r12: residual word count
    movs    r2, r2, lsr #3  @ r2: block count
    beq     .Lres_cpy32
    push    {r4-r10}

    @ Copy 32byte chunks with 8fold xxmia
    @ r2 range: [1, inf)
.Lmain_cpy32:
        ldmia   r1!, {r3-r10}   
        stmia   r0!, {r3-r10}
        subs    r2, #1
        bne     .Lmain_cpy32
    pop     {r4-r10}

    @ And the residual 0-7 words.
    @ r12 range: [0,7]
.Lres_cpy32:
        subs    r12, #1
        ldrcs   r3, [r1], #4
        strcs   r3, [r0], #4
        bcs     .Lres_cpy32

    @ return
    bx  lr


in C file:
Code:

// declaration:
extern "C" void memcpy32(void *dst, const void *src, nwords) IWRAM_CODE;

// Usage. Copy gfx for someSprite to OBJ VRAM.
#define someSpriteTilesLen 256         // Length of someSpriteTiles, in bytes.
u32 someSpriteTiles[64];               // sprite gfx array.

memcpy32(SPRITE_GFX, someSpriteTiles, someSpriteTilesLen/4);

#177098 - Timofiend - Sat Dec 10, 2011 5:55 pm

Thanks very much for the reply. Sorry it took me a while to get back I have been working on some other aspects of the project for now.

I do not think he specifically asked for it to be inline, although when giving us resources the inline assembly method was mentioned. I have sent off an email to double check this, and if it is allowed then I will definitely try your method thank you for the info.

So is there anything special I would have to do to include the separate assembly file in my project? Would I need to place it somewhere in particular in the project folder? **edit** I just read in the other thread that it would go in /sources. Would I need to tell the compiler I have put this here? Or would it automatically recognise and use it?

#177099 - Miked0801 - Sat Dec 10, 2011 6:42 pm

Depends on your makefile. Many will scan for .s files and automatically call the assembler on them instead of the compiler.