Arm Assembler In Gcc


Ziz

Advanced Member
Joined
Jan 15, 2006
Messages
3,583
Hello,

First of all i'am German and my english is bad.
But I think, here are the people to help me. :rolleyes:

my problem:
I would like to learn something about arm-assembler especially in gcc.
So I use the gcc inline assembler.
my references are
GBA-ARM-Tutorial (german)
ARM GCC Inline (english)

That is my code:
CODE
void hline(SDL_Surface* screen,Uint16 x,Uint16 y,Uint16 l,Uint16 f)
{
SDL_LockSurface(screen);
Uint16* pixel=(Uint16*)screen->pixels;
#ifdef GP2X
Uint32 pos=(x+y*320)*2+Uint32(pixel);

asm volatile(".myloop: \n\t"
"strh %2,[%0],#2 \n\t"
"subs %1,%1,#1 \n\t"
"bne .myloop"
: "=&r" (pos), "=&r"(l), "=&r"(f)
:
: "cc", "r3");
#endif
#ifndef GP2X
Uint32 pos=(x+y*320);
for (int a=pos;a<pos+l;a++)
pixel[a]=f;
#endif
SDL_UnlockSurface(screen);
}

The "#ifndef GP2X"-Part is not necessary. It works.
The problem is in the "#ifdef GP2X" Part.
I should draw a horizontal line from (x,y) to (x+l,y) in color f.
But if I start this function (with x=10;y=10;l=10;f=65535) the program stops and I land in the menu.

So where is my failure?

Thx for reading and sorry for my baaaad english again. :rolleyes:
Cyberpuer
 
It's probably segfaulting, in which case you should use gdb to debug it and it'll tell you exactly where and the status of all registers/variables/etc so you can fix the code.

The 'strh' doesn't seem right to me tho'. Also, for a loop like that, you may find gcc outputs better (or same performance) code. Did you compare the assembler output or profile it?
 
I'am an idiot.
I mistake the position of output and input variables in gcc inline assembler. First output then input. Now it works:
CODE
void hline(SDL_Surface* screen,Uint16 x,Uint16 y,Uint16 l,Uint16 f)
{
SDL_LockSurface(screen);
Uint16* pixel=(Uint16*)screen->pixels;
#ifdef GP2X
Uint32 pos=(x+y*320)*2+Uint32(pixel);

asm volatile(".myloop: \n\t"
"strh %2,[%0],#2 \n\t"
"subs %1,%1,#1 \n\t"
"bne .myloop"
:
: "r" (pos), "r"(l), "r"(f)

: "cc");
#endif
#ifndef GP2X
Uint32 pos=(x+y*320);
for (int a=pos;a<pos+l;a++)
pixel[a]=f;
#endif
SDL_UnlockSurface(screen);
}

And btw.: the "r3" after the "cc" is not necessary... It was a senseless relic from an old idea. ;)
Incidentally must it be "strh" in case of a 16bit-screen-surface.

Anyway thanks for help, Squidge. :lol:

bye, Cyberpuer
:gp2x :gp2x :gp2x
 
Also remember that the GBA is ARM7 while the GP2X is ARM9 so there are more instructions and such you can use.
 
Thanks for the tip @Blah.

Another question: What is faster (for "r3=r2*320)?
CODE

mov r3,r2 ,lsl #8
add r3,r3,r2, lsl #6


or
CODE

mov r4,#320
mul r3,r2,r4



Both works and I don't see a difference in fps, but that says nothing. ;-)
Theoretical the GP2X has a ARM920T. Incace of no "M" after "ARM920T" there is no multiplier, isn't it?
 
Yes, I thought you were using indexing in your strh, not addition back to base reg.

To work what sequence of instructions is fastest, read the cycle timings in the datasheet.

'mul' is hardware multiply on the gp2x, but barrel shifts are free... check the datasheet!
 
Hm, I looked in the datasheet from the wiki, but I didn't understand it. For what I must locking on?
 
Don't know what the docs are on the wiki (and can't be bothered to check :p), so I'll just tell you to read the ARM920T TRM from arm.com :)
 
Ah, thanks. :rolleyes:
So...
http://infocenter.arm.com/help/index.jsp?t...c/Chdfcdji.html
and
http://infocenter.arm.com/help/index.jsp?t...c/Chdedhfg.html
says, that the first variant needs 4 cycles and the second 5 cycles I think.
Reason:
Variant 1:
A "Data Operation" (AND and MOV are "Data Operations", aren't it?) needs 1 cycle and a "Data Operation with register controlled shift" needs 2 cycles.
I have two "Data Operations with register controlled shift, so it needs 4 cyclings.
Variant 2:
In basis of the thinks written at Variant 1, the "mov" needs 1 cycling times. The MUL instruction needs 2+m cyclings times, depending on the multiplier operand. In my case m should be 2, so it needs 5 cyclings.

Sounds that logical?

Cyberpuer
 
Hi Cyberpuer, its looks like your using assembler in your code because you've assumed it will be faster. This is an assumption that most people start off with (including me) when they first add assembler to their code.

The ASM you are currently using will be no faster than the ASM outputted by the C compiler. You are outputting 1 pixel at a time, you are decrementing a variable in each loop and then checking the variable in each loop to see if you are finished. This is probably exactly how the C compiler would do it.

When using ASM you need to think about shortcuts, take for example your line drawing code. At the moment if someone called it to fill a whole line it would have to loop 320 times. What we could do is plan for someone to call it to fill the whole line. Some basic code below

r0 = pointer to screen
r1 = colour (16 bit value)
stmfd sp!,{r2-r11}
orr r1,r1,r1,lsl #16 ;@ copy lower 16 bits to upper 16 bits
mov r2,r1 ;@ copy color to another register
mov r3,r1 ;@ copy color to another register
mov r4,r1 ;@ copy color to another register
mov r5,r1 ;@ copy color to another register
mov r6,r1 ;@ copy color to another register
mov r7,r1 ;@ copy color to another register
mov r8,r1 ;@ copy color to another register
mov r9,r1 ;@ copy color to another register
mov r10,r1 ;@ copy color to another register
mov r11,r1 ;@ copy color to another register
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
stmia r0!,{r1-r11} ;@ store 20 pixels in 1 hit
ldmfd sp!,{r2-r11}

This works out about 29 instructions, your old way would have been about 960 instructions. So you can see that if we are able to predict what the *big picture* is for a function we are able to improve the performance in assembler.

You could extend the line drawing code to run different bits of code depending on the line length and the alignment of the first pixel. So you could have 640 different bits of assembler, the size of your program would be massive but it would very optimised.

Hope that helps
Reesy
 
Hey, thanks for this big tip.
I will try to copy your strategy (possibly a bit other) and will tell you my result. :)

Cyberpuer
 
Like I said above, profile, profile and profile again before writing a single line of assembler, and even then check the gcc output to see how you could optimise it.

Oh, and don't forget about the cache - loops too small drop performance because it spends too much time comparing and subtracting values, make them too big and it all slows to a crawl due to cache inefficiency.
 
So, after 2 weeks, i'am back in forum to thank you very much.
Now my filled polygon isnt the slowest part of my program. ^^
You helped me enormous.
Thanks squidge & Reesy.

Cyberpuer
 
Blah said:
Also remember that the GBA is ARM7 while the GP2X is ARM9 so there are more instructions and such you can use.
They're both ARMv4. GP2X doesn't have any instructions over GBA.
 
Last edited by a moderator:
Back
Top