After having considered some comments from Mr./Ms. G I optimized my assembly code a little bit.
Here a better approach:
Output
deadbeef
11011110101011011011111011101111
00000000111111111111111111111111
Code:
.equ STRING_LENGTH, 32 .equ ZERO_CHARACTER, 0x30 .equ ONE_CHARACTER, 0x31 .data Mask: .ascii "%x\n\n" .align 4 varToPrint: .long 0xdeadbeef .balign 8 varToUnspace: .long 0xdeadbeef .balign 8 unspacedVarToPrint: .long 0xdeadbeef .balign 8 stringToPrint: .ascii " \n\n" len = STRING_LENGTH .balign STRING_LENGTH unspacedStringToPrint: .ascii " \n\n" len = STRING_LENGTH .balign STRING_LENGTH .text .globl main main: stmfd sp!, {r0, r1, r2, r3, r4, r5, r6, r7, lr} @ save the registers we use to the stack ldr r3, AddrVarToPrint ldr r6, [r3] ldr r3, AddrVarToPrint str r6, [r3] /* Generating string of binary representation: Address of variable in r0 Address of target string in r1 */ ldr r0, AddrVarToPrint ldr r1, AddrStringToPrint bl make_binary_number /*------------------------------------------*/ /* Removing zeroes between the ones in binary value: Address of variable in r0 Address of target variable in r1 */ ldr r0, AddrVarToPrint ldr r1, AddrUnspacedVarToPrint bl remove_spaces /* Generating string of binary representation: Address of variable in r0 Address of target string in r1 */ ldr r0, AddrUnspacedVarToPrint ldr r1, AddrUnspacedStringToPrint bl make_binary_number /*------------------------------------------*/ bl print_out_result b exit /*-Fetching the values and printing them-----------------------*/ print_out_result: push {r0, r1, r2, r3, r4, lr} ldr r3, AddrMask movs r0, r3 ldr r3, AddrVarToPrint ldr r1, [r3] bl printf ldr r3, AddrStringToPrint movs r0, r3 bl printf ldr r3, AddrUnspacedStringToPrint movs r0, r3 bl printf pop {r0, r1, r2, r3, r4, pc} /*-Generate a binary representation string from integer--------*/ /* Address of variable in r0 */ /* Address of target string in r1 */ make_binary_number: push {r2, r3, r4, r5, r6, r7, lr} ldr r3, [r0] /* integer value of variable */ movs r4, $0 /* loop counter */ bin_loop: movs r2, $0x1 lsls r2, r4 /* r2 = (0x01 >> counter) */ movs r6, $STRING_LENGTH-1 subs r6, r6, r4 /* idx = reg_length-counter */ tst r3, r2 beq no_one movs r5, $ONE_CHARACTER strb r5, [r1,r6] b end_bin_loop no_one: movs r5, $ZERO_CHARACTER strb r5, [r1,r6] b end_bin_loop end_bin_loop: adds r4, r4,$1 cmp r4, $STRING_LENGTH bne bin_loop pop {r2, r3, r4, r5, r6, r7, pc} /*-Removes spaces between ones---------------------------------*/ /* Taken we have: 0xdeadbeef Binary representation: 11011110101011011011111011101111 Then the ones will be shifted together so that we get the following result: 00000000111111111111111111111111 */ /* Address of variable in r0 */ /* Address of target variable in r1 */ remove_spaces: push {r2, r3, r4, r5, r6, r7, lr} ldr r6, [r0] movs r4, $0 movs r5, $0 movs r7, $0 unspace_loop: movs r2, $0x1 lsls r2, r4 tst r6, r2 beq end_unspace_loop lsls r5, r5, $1 adds r5, r5, $1 end_unspace_loop: adds r4, r4,$1 cmp r4, $STRING_LENGTH bne unspace_loop str r5, [r1] pop {r2, r3, r4, r5, r6, r7, pc} /*-------------------------------------------------------------*/ /*--------EXIT main(void) function -> return 0-----------------*/ exit: ldmfd sp!, {r0, r1, r2, r3, r4, r5, r6, r7, pc} @ restore registers before exit movs r7, $1 @ set r7 to 1 - the syscall for exit swi 0 @ then invoke the syscall from linux /*-----Variable pointers---------------------------------------*/ AddrMask: .word Mask AddrVarToPrint: .long varToPrint AddrVarToUnspace: .long varToUnspace AddrStringToPrint: .word stringToPrint AddrUnspacedStringToPrint: .word unspacedStringToPrint AddrUnspacedVarToPrint: .long unspacedVarToPrint
Well, I ended up cheating and looking up what gcc -O3 would do with it. 🙂
~/arm-asm $ cat stripzero.c
int stripzero(int x) {
int count = 0;
while (x) {
count += x & 1;
x >>= 1;
}
return (1 <>= 1
bne _continue_counting
// Huh! Instead of return (1 << count) - 1,
// which I put into the C code,
// gcc does return ~(~0 << count).
// Mad optimizations.
mvn r0, #0 // r0 := ~0
mvn r0, r0, asl r3 // r0 := ~(r0 << r3)
bx lr
This time with code font...
~/arm-asm $ cat stripzero.c
int stripzero(int x) {
int count = 0;
while (x) {
count += x & 1;
x >>= 1;
}
return (1 << count) - 1;
}
~/arm-asm $ cc -S -o stripzero.S -O3 stripzero.c
stripzero:
// Count bits in r0, store in r3.
// Destroys r0 along the way.
mov r3, #0 // r3 := 0
_continue_counting:
and r2, r0, #1 // r2 := r0 & 1
add r3, r3, r2 // r3 += r2
movs r0, r0, asr #1 // r0 >>= 1
bne _continue_counting
// Huh! Instead of return (1 << count) - 1,
// which I put into the C code,
// gcc does return ~(~0 << count).
// Mad optimizations.
mvn r0, #0 // r0 := ~0
mvn r0, r0, asl r3 // r0 := ~(r0 << r3)
bx lr
you can use <pre lang="asm">
MVN... Have to try if this command exists with the proprietary Keil ASM dialect we use in our lecture...
I think all ALU instructions can mangle their last argument with a shift like that. It's part of the binary Instruction Format, which is the same for a broad range oft data processing instructions. Check this out: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489f/Chddgcje.html
It's the section "operand2 as register with optional shift".
So if your teaching dialect of ARM doesn't support it, it's giving away a few bits per instruction for free 🙂
Ah right, you're talking about the negation part. I'm not sure about that one
Doc is at http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489h/CJAEDJHJ.html