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