How to reverse endianness? - c

Can someone help me understand this code?
int reverse_endianess(int value) {
int resultat = 0;
char *source, *destination;
int i;
source = (char *) &value;
destination = ((char *) &resultat) + sizeof(int);
for (i = 0; i < sizeof(int); i++)
*(--destination) = *(source++);
return resultat;
}
I can't understand this part of code:
destination = ((char *) &resultat) + sizeof(int);
for (i = 0; i < sizeof(int); i++)
*(--destination) = *(source++);

The following causes destination to point to the byte that follows resultat (as long as resultat is an int):
destination = ((char *) &resultat) + sizeof(int);
It could also have been written as follows:
destination = (char *)(&resultat + 1);
The following is just a simple memory copy loop:
for (i = 0; i < sizeof(int); i++)
*(--destination) = *(source++);
It's equivalent to the following:
for (i = 0; i < sizeof(int); i++) {
--destination; // Point to the one byte earlier.
*destination = *source; // Copy one byte.
source++; // Point to one byte later.
}
Program flow (assuming 32-bit int and 8-bit char)
After setup:
source value
+----------+ +---+---+---+---+
| -------+ | a | b | c | d |
+----------+ | +---+---+---+---+
| ^
+------+
destination resultat
+----------+ +---+---+---+---+
| -------+ | 0 | 0 | 0 | 0 |
+----------+ | +---+---+---+---+
| ^
+----------------------+
After one pass of the loop:
source value
+----------+ +---+---+---+---+
| -------+ | a | b | c | d |
+----------+ | +---+---+---+---+
| ^
+----------+
destination resultat
+----------+ +---+---+---+---+
| -------+ | 0 | 0 | 0 | a |
+----------+ | +---+---+---+---+
| ^
+------------------+
When it's done:
source value
+----------+ +---+---+---+---+
| -------+ | a | b | c | d |
+----------+ | +---+---+---+---+
| ^
+----------------------+
destination resultat
+----------+ +---+---+---+---+
| -------+ | d | c | b | a |
+----------+ | +---+---+---+---+
| ^
+------+

Lets say sizeof(int) = 4 so 32 bit int.
A char is sizeof 1, 1 byte.
A normal int* looks like this:
aabbccdd // the int in hexadecimal
^ pointer points to start
If we cast it to a char*, we get aa. Thats whats done with source.
If we now add sizeof 4, we jump 4 bytes to the right:
aabbccdd??
^
We are now one byte behind the value, accessing this may segfault the program or just read garbage. This does not happen due to the use of --destination instead of destination--. It is decremented first.
Now we just read the integer passed in from the front, while writing it from the back:
a1b2c3d4 // original int
->
d4c3b2a1 // destination
<-
Note that two hexadecimal digits are one byte, which is why we dont get
4d3c2b1a. We leave the bytes in the correct way, but put the first bytes last.

Three different approaches. The first one is most efficient on the systems having byte reversing instructions.
#define SWAPUC(a,b) do{unsigned char temp = (a); (a) = (b); (b) = temp;}while(0)
int reverse(int i)
{
unsigned int val = i;
if(sizeof(val) == 4)
val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24);
if(sizeof(val) == 8)
val = ((val & 0x00000000000000ffULL) << 56) | ((val & 0xff00000000000000ULL) >> 56) |
((val & 0x000000000000ff00ULL) << 40) | ((val & 0x00ff000000000000ULL) >> 40) |
((val & 0x0000000000ff0000ULL) << 24) | ((val & 0x0000ff0000000000ULL) >> 24) |
((val & 0x00000000ff000000ULL) << 8) | ((val & 0x000000ff00000000ULL) >> 8);
return val;
}
int reverse1(int val)
{
union
{
unsigned i;
unsigned char uc[sizeof(val)];
}uni = {.i = val};
if(sizeof(val) == 8)
{
SWAPUC(uni.uc[7], uni.uc[0]);
SWAPUC(uni.uc[6], uni.uc[1]);
SWAPUC(uni.uc[5], uni.uc[2]);
SWAPUC(uni.uc[4], uni.uc[3]);
}
if(sizeof(val) == 4)
{
SWAPUC(uni.uc[3], uni.uc[0]);
SWAPUC(uni.uc[2], uni.uc[1]);
}
return uni.i;
}
int reverse2(int val)
{
unsigned char uc[sizeof(val)];
memcpy(uc, &val, sizeof(uc));
if(sizeof(val) == 8)
{
SWAPUC(uc[7], uc[0]);
SWAPUC(uc[6], uc[1]);
SWAPUC(uc[5], uc[2]);
SWAPUC(uc[4], uc[3]);
}
if(sizeof(val) == 4)
{
SWAPUC(uc[3], uc[0]);
SWAPUC(uc[2], uc[1]);
}
memcpy(&val, uc, sizeof(uc));
return val;
}
int main(void)
{
printf("%x\n", reverse2(0xaabbccdd));
}
The generated code (x86):
reverse:
mov eax, edi
bswap eax
ret
reverse1:
mov eax, edi
xor edx, edx
mov ecx, edi
shr eax, 24
movzx esi, ch
sal ecx, 24
mov dl, al
mov eax, edi
sal esi, 16
shr eax, 16
mov dh, al
movzx eax, dx
or eax, esi
or eax, ecx
ret
reverse2:
mov eax, edi
xor edx, edx
mov ecx, edi
shr eax, 24
movzx esi, ch
sal ecx, 24
mov dl, al
mov eax, edi
sal esi, 16
shr eax, 16
mov dh, al
movzx eax, dx
or eax, esi
or eax, ecx
ret
.LC0:
.string "%x\n"
Or cortex M4 (this one has byte swapping instruction)
reverse:
rev r0, r0
bx lr
reverse1:
mov r3, r0
lsrs r2, r3, #24
movs r0, #0
bfi r0, r2, #0, #8
ubfx r2, r3, #16, #8
bfi r0, r2, #8, #8
ubfx r2, r3, #8, #8
bfi r0, r2, #16, #8
bfi r0, r3, #24, #8
bx lr
reverse2:
mov r3, r0
lsrs r2, r3, #24
movs r0, #0
bfi r0, r2, #0, #8
ubfx r2, r3, #16, #8
bfi r0, r2, #8, #8
ubfx r2, r3, #8, #8
bfi r0, r2, #16, #8
bfi r0, r3, #24, #8
bx lr
.LC0:
So the winner is the first function using only the bitwise arithmetics.

I was using this for a very long time.
data is a pointer to value to be reversed
n is the number of char to be reversed; usually 2, 4, 8 for short, int, long long. But this can be different on various architectures/OS
void SwapEndianN(char *data, unsigned short n) {
unsigned short k; char c;
for ( k=0 ; k < (n/2) ;k++ ) {
c = *(data+((n-1)-k));
*(data+((n-1)-k)) = *(data+k);
*(data+k) = c;
}
}

Related

enabling paging leads to triple fault

In case this helps somebody in the future:
check if the global descriptor table is working
check if the elements of page table entry struct are ordered correctly
This code worked for me:
gdt.s
section .data
gdt:
.null:
dq 0
.code:
dw 0xFFFF
dw 0x0000
db 0x00
db 0x9A
db 0xCF
db 0x00
.data:
dw 0xFFFF
dw 0x0000
db 0x00
db 0x92
db 0xCF
db 0x00
gdtr:
dw $-gdt-1
dd gdt
section .text
global init_global_descriptor_table
init_global_descriptor_table:
lgdt [gdtr]
jmp 0x08:.reload_cs
.reload_cs:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
ret
paging.c
enum flags {
Present = 1 << 0,
ReadWrite = 1 << 1,
AccessAll = 1 << 2,
WriteThroughCashing = 1 << 3,
DisableCashing = 1 << 4,
Accessed = 1 << 5,
Dirty = 1 << 6, // only for page-table-entries
MPages = 1 << 7,
Global = 1 << 8, // only for page-table-entries
};
struct entry {
unsigned int flags : 9;
unsigned int available : 3;
unsigned int addr : 20;
};
extern void load_paging_directory(int *ptr);
void init_paging() {
struct entry *dir = (struct entry *)0x00105000;
struct entry *t1 = (struct entry *)0x00106000;
for (int i = 0; i < 1024; i++) {
dir[i] = (struct entry){0};
if (i <= 262) t1[i] = (struct entry){Present | ReadWrite, 0, i};
}
dir[0] = (struct entry){Present, 0, (int)t1 >> 12};
load_paging_directory((int *)dir);
}
paging_asm.s
global load_paging_directory
load_paging_directory:
push ebp,
mov ebp, esp
mov eax, [ebp + 8]
mov cr3, eax
mov eax, cr0
or eax, 0x80000001
mov cr0, eax
mov esp, ebp
pop ebp
ret
The question has been solved using this struct:
struct entry {
unsigned int flags : 9;
unsigned int available : 3;
unsigned int addr : 20;
};

Problems with integer promotion in C

I'm developing a C code for an embedded application for ARM processor (LPC54628) using Keil software. There's a strange behavior that I am unable to resolve. I tried running this on the software simulator as well as on the microcontroller and the behavior is the same. The problem is with the execution of the second 'else if' condition.
Working code:
uint8_t a; uint8_t b ; uint8_t temp1; uint8_t temp2; uint8_t c;
a = 0x1; b = 0x80; temp1 = 0; temp2 = 0; c = 10U;
temp1 = (b << 1); // after execution, temp1 is 0x00
temp2 = (b >> 7); // after execution, temp2 is 0x01
__NOP();
temp1 = ((b << 1) | (b >> 7)); // after execution, temp1 is 0x00 | 0x01 = 0x01
if (a == b) { }
else if ( a == ((b >> 1) | (b << 7)) ) {c += 1; }
else if ( a == temp1 ) {c -= 1; } // this 'else if' executes since a= 0x01 and temp1 = 0x01
else if ( a == ((b >> 2) | (b << 6)) ) {c += 2; }
else if ( a == ((b << 2) | (b >> 6)) ) {c -= 2; }
else if ( a == ((b >> 3) | (b << 5)) ) {c += 3; }
else if ( a == ((b << 3) | (b >> 5)) ) {c -= 3; }
However, the 'else if' that worked in the code above fails to execute in the following code. Note that the only change I have done is to replace temp1 with the actual expression inside the 'else if' condition. No other change.
Non-working code:
a = 0x1; b = 0x80; temp1 = 0; temp2 = 0; c = 10U;
temp1 = (b << 1); // after execution, temp1 is 0x00
temp2 = (b >> 7); // after execution, temp2 is 0x01
__NOP();
temp1 = ((b << 1) | (b >> 7)); // after execution, temp1 is 0x00 | 0x01 = 0x01
if (a == b) { }
else if ( a == ((b >> 1) | (b << 7)) ) {c += 1; }
else if ( a == ((b << 1) | (b >> 7)) ) {c -= 1; } // this 'else if' DOES NOT execute.
else if ( a == ((b >> 2) | (b << 6)) ) {c += 2; }
else if ( a == ((b << 2) | (b >> 6)) ) {c -= 2; }
else if ( a == ((b >> 3) | (b << 5)) ) {c += 3; }
else if ( a == ((b << 3) | (b >> 5)) ) {c -= 3; }
Can you point out what I am doing wrong?
Integer promotion is annoying. You're fundamentally doing:
else if ( (int) a == (((int)(b << 1)) | ((int)(b >> 7))) ) {
c -= 1;
}
Which means that you're testing if 0x01 == 0x101, which it doesn't.
When you do something like:
uint8_t x = 3;
uint8_t y = x + 4;
You're really doing something like:
uint8_t x = 3;
uint8_t y = (uint8_t)((int) x) + 4)
In the expression ((b << 1) | (b >> 7)), the value b is first promoted to type int because its type is smaller than int. So this expression ends up being:
((0x80 << 1) | (0x80 >> 7)) == (0x100 | 0x1) == 0x101
When you assign this value to temp1, it is converted to a value that fits and you're left with 0x1. When you instead compare the result of this expression directly against a, you're comparing the value 0x1 with 0x101.
If you want the result of this expression to be 8 bit, you need to cast it to uint8_t to truncate the higher bits.
if (a == b) { }
else if ( a == (uint8_t)((b >> 1) | (b << 7)) ) {c += 1; }
else if ( a == (uint8_t)((b << 1) | (b >> 7)) ) {c -= 1; }
else if ( a == (uint8_t)((b >> 2) | (b << 6)) ) {c += 2; }
else if ( a == (uint8_t)((b << 2) | (b >> 6)) ) {c -= 2; }
else if ( a == (uint8_t)((b >> 3) | (b << 5)) ) {c += 3; }
else if ( a == (uint8_t)((b << 3) | (b >> 5)) ) {c -= 3; }
C compilers did NOT used to do this, I do not know exactly when it changed.
unsigned int fun0 ( unsigned char a, unsigned char b )
{
return((a<<1)|(b>>1));
}
unsigned int fun1 ( unsigned char a, unsigned char b )
{
return(unsigned char)((a<<1)|(b>>1));
}
00000000 <fun0>:
0: e1a010a1 lsr r1, r1, #1
4: e1810080 orr r0, r1, r0, lsl #1
8: e12fff1e bx lr
0000000c <fun1>:
c: e1a010a1 lsr r1, r1, #1
10: e1810080 orr r0, r1, r0, lsl #1
14: e20000ff and r0, r0, #255 ; 0xff
18: e12fff1e bx lr
The first one the operation is on 8 bit values before it is combined to be returned. The second is clipped.
I specifically had a day of year problem many many years ago now, the bug would appear late in the year (just so happened to be day 256) and fixed itself January first... day = (high_byte<<8)|(low_byte); (fixed with ...((unsigned int)high_byte)<<8...)
unsigned int fun ( unsigned char a, unsigned char b )
{
return((a<<8)|b);
}
00000000 <fun>:
0: e1810400 orr r0, r1, r0, lsl #8
4: e12fff1e bx lr
Would not have broken today...at least with gcc 10.x.x...I also want to say at some point it was implementation defined, but seems that from many of the various quotes on the net it has been this way since C99...
Note disassembly is your friend...But then always understand that sometimes it is implementation defined (does not seem so in this case) and that just because your compiler did it one way does not mean that is the standard and is true for all compilers. (you are using Kiel I am using gnu for example).
Folks run into this a lot with floating point
float fun0 ( float a, float b )
{
return(a*(b+2.0));
}
float fun1 ( float a, float b )
{
return(a*(b+2.0F));
}
00000000 <fun0>:
0: e92d4070 push {r4, r5, r6, lr}
4: e1a06000 mov r6, r0
8: e1a00001 mov r0, r1
c: ebfffffe bl 0 <__aeabi_f2d>
10: e3a02000 mov r2, #0
14: e3a03101 mov r3, #1073741824 ; 0x40000000
18: ebfffffe bl 0 <__aeabi_dadd>
1c: e1a04000 mov r4, r0
20: e1a00006 mov r0, r6
24: e1a05001 mov r5, r1
28: ebfffffe bl 0 <__aeabi_f2d>
2c: e1a02000 mov r2, r0
30: e1a03001 mov r3, r1
34: e1a00004 mov r0, r4
38: e1a01005 mov r1, r5
3c: ebfffffe bl 0 <__aeabi_dmul>
40: ebfffffe bl 0 <__aeabi_d2f>
44: e8bd4070 pop {r4, r5, r6, lr}
48: e12fff1e bx lr
0000004c <fun1>:
4c: e92d4010 push {r4, lr}
50: e1a04000 mov r4, r0
54: e1a00001 mov r0, r1
58: e3a01101 mov r1, #1073741824 ; 0x40000000
5c: ebfffffe bl 0 <__aeabi_fadd>
60: e1a01004 mov r1, r4
64: ebfffffe bl 0 <__aeabi_fmul>
68: e8bd4010 pop {r4, lr}
6c: e12fff1e bx lr
2.0 is a double in the eyes of the compiler but 2.0F is single. And a double plus a single gets promoted to a double operation. Not an integer promotion but constants have an implied type (integer or floating point) and that plays into promotion.

i want to translate this upcode in assembler programming keil uVision 5

(a , b can be any number, between -20 and 20.. Find final values of
i, j, and k for three pairs of a and b having the relationships a > b, a < b, and a = b)
i =1
j=0
k = -1
while (i > j) {
i = i + a – 2 * j;
if (j >= k) {
i = i + 2;
k = k – b + 2 * j;
}
j++;
Keil ( this my version but why it end up infinity loop )
MOV r0, #1
MOV r1, #0
MOV r2, #0
SUB r2,r2,#1; k = -1
MOV r4, #4 ;a =4
MOV r5, #6
MOV r8, #2
B whileLoop
whileLoop
CMP r0,r1
BLE stop
MUL r3,r1,r8 ; r3 = 2*j
ADD r0, r0,r4
SUB r0, r0, r 3; i = i + a -2*j
B ifloop
ifloop
CMP r1,r2 ;j>=k?
BLT A
ADD r0,r0,#2
MUL r3,r1,r8 ;r3 = 2*j
SUB r2,r2,r5 ;k = k -b
ADD r2,r2,r3 ; k = k-b+2j
B A
A
ADD r1,r1,#1 ;j++
B whileLoop
stop B stop
ENDP
END

Bare metal audio output on Raspberry Pi3 working in AARCH64 asm but not the C version

I have been trying to write a bare metal kernel for over a year now and I am up to the point where I am ready to start working on audio output. I have written the code in asm however since I'm not great at it I'm not sure how I can pass audio samples as arguments to a asm function. I tried to rewrite it in C however it isn't working. This problem is really a spot the difference. I know my asm version works but the audio sample is written into the play_audio function. My goal is to have a init function for the audio with no arguments and a play_audio function that takes the pointer to the start of the audio function and a pointer to the end of the audio file. The audio file to be played is a 16 bit unsigned int pcm file. The same file that I'm trying to use for the C audio part is used successfully in the asm version. Since I set the hardware pwm to expect 13bit audio at 41400Hz there is a shift to convert the sample from 16bit to 13 bit so this isn't a mistake.
Not_working_audio.c
void init_audio_jack_c()//ERROR IN HERE
{
//Set phone jack to pwm output
uint32_t *gpio_addr = (uint32_t *)(PERIPHERAL_BASE + GPIO_BASE);
uint32_t *gpio_gpfsel4_addr = gpio_addr + GPIO_GPFSEL4;
*gpio_gpfsel4_addr = GPIO_FSEL0_ALT0 | GPIO_FSEL5_ALT0;
//Set clock
uint32_t *clock_manager_addr = (uint32_t *)(((PERIPHERAL_BASE + CM_BASE) & 0x0000FFFF) | ((PERIPHERAL_BASE + CM_BASE) & 0xFFFF0000));
*(clock_manager_addr + CM_PWMDIV) = (CM_PASSWORD | 0x2000);
*(clock_manager_addr + CM_PWMCTL) = ((CM_PASSWORD | CM_ENAB) | (CM_SRC_OSCILLATOR + CM_SRC_PLLCPER));
//Set PWM
uint32_t *pwm_manager_addr = (uint32_t *)(((PERIPHERAL_BASE + PWM_BASE) & 0x0000FFFF) | ((PERIPHERAL_BASE + PWM_BASE) & 0xFFFF0000));
*(pwm_manager_addr + PWM_RNG1) = 0x1624;
*(pwm_manager_addr + PWM_RNG2) = 0x1624;
*(pwm_manager_addr + PWM_CTL) = PWM_USEF2 + PWM_PWEN2 + PWM_USEF1 + PWM_PWEN1 + PWM_CLRF1;
printf("[INFO] Audio Init Finished");
}
int32_t play_16bit_unsigned_audio(uint16_t *start, uint16_t *end)
{
if(end < start)
{
printf("[ERROR] End is less than start.");
return 1;
}
if((start - end) % 2 == 0)
{
printf("[ERROR] Isn't a multiple of two so it isn't 16bit");
return 2;
}
uint16_t *end_of_file = (uint16_t *)(uint64_t)(((uint32_t)(uintptr_t)end & 0x0000FFFF) | ((uint32_t)(uintptr_t)end & 0xFFFF0000));
//FIFO write
while(start != end_of_file)
{
uint16_t sample = start[0];
sample >>= 3;
*(uint32_t *)((((uint32_t)(PERIPHERAL_BASE + PWM_BASE) & 0x0000FFFF) | ((uint32_t)(PERIPHERAL_BASE + PWM_BASE) & 0xFFFF0000)) + PWM_FIF1) = sample;
start++;
sample = start[0];
sample >>= 3;
*(uint32_t *)((((uint32_t)(PERIPHERAL_BASE + PWM_BASE) & 0x0000FFFF) | ((uint32_t)(PERIPHERAL_BASE + PWM_BASE) & 0xFFFF0000)) + PWM_FIF1) = sample;
//FIFO wait
while(*(uint32_t *)((((uint32_t)(PERIPHERAL_BASE + PWM_BASE) & 0x0000FFFF) | ((uint32_t)(PERIPHERAL_BASE + PWM_BASE) & 0xFFFF0000)) + PWM_STA) != PWM_FULL1);
start++;
}
printf("[INFO] Completed Audio");
return 0;
}
Working_audio.s
.section .text.init_audio_jack, "ax", %progbits
.balign 4
.globl init_audio_jack;
.type init_audio_jack, %function
init_audio_jack:
mov w0,PERIPHERAL_BASE + GPIO_BASE
mov w1,GPIO_FSEL0_ALT0
orr w1,w1,GPIO_FSEL5_ALT0
str w1,[x0,GPIO_GPFSEL4]
// Set Clock
mov w0, PERIPHERAL_BASE
add w0, w0, CM_BASE
and w0, w0, 0x0000FFFF
mov w1, PERIPHERAL_BASE
add w1, w1, CM_BASE
and w1, w1, 0xFFFF0000
orr w0,w0,w1
mov w1,CM_PASSWORD
orr w1,w1,0x2000 // Bits 0..11 Fractional Part Of Divisor = 0, Bits 12..23 Integer Part Of Divisor = 2
brk #0
str w1,[x0,CM_PWMDIV]
mov w1,CM_PASSWORD
orr w1,w1,CM_ENAB
orr w1,w1,CM_SRC_OSCILLATOR + CM_SRC_PLLCPER // Use 650MHz PLLC Clock
str w1,[x0,CM_PWMCTL]
// Set PWM
mov w0, PERIPHERAL_BASE
add w0, w0, PWM_BASE
and w0, w0, 0x0000FFFF
mov w1,PERIPHERAL_BASE
add w1, w1, PWM_BASE
and w1, w1, 0xFFFF0000
orr w0,w0,w1
mov w1,0x1624 // Range = 13bit 44100Hz Mono
str w1,[x0,PWM_RNG1]
str w1,[x0,PWM_RNG2]
mov w1,PWM_USEF2 + PWM_PWEN2 + PWM_USEF1 + PWM_PWEN1 + PWM_CLRF1
str w1,[x0,PWM_CTL]
.section .text.play_audio, "ax", %progbits
.balign 4
.globl play_audio;
.type play_audio, %function
play_audio:
Loop:
adr x1, _binary_src_audio_Interlude_bin_start // X1 = Sound Sample
ldr w2, =_binary_src_audio_Interlude_bin_end
and w2, w2, 0x0000FFFF // W2 = End Of Sound Sample
ldr w3, =_binary_src_audio_Interlude_bin_end
and w3, w3, 0xFFFF0000
orr w2,w2,w3
FIFO_Write:
ldrh w3,[x1],2 // Write 2 Bytes To FIFO
lsr w3,w3,3 // Convert 16bit To 13bit
str w3,[x0,PWM_FIF1] // FIFO Address
ldrh w3, [x1], 2
lsr w3, w3, 3
str w3, [x0, PWM_FIF1]
FIFO_Wait:
ldr w3,[x0,PWM_STA]
tst w3,PWM_FULL1 // Test Bit 1 FIFO Full
b.ne FIFO_Wait
cmp w1,w2 // Check End Of Sound Sample
b.ne FIFO_Write
b Loop // Play Sample Again
Thanks in advance to anyone that can help!

Operator * and + produces wrong result in digital mars

I am trying to calculate a number which produce Longest Collatz sequence. But here is a strange problem. 3n+1 become 38654705674 when n is 3. I do not see an error. here is the full code:
/* 6.c -- calculates Longest Collatz sequence */
#include <stdio.h>
long long get_collatz_length(long long);
int main(void)
{
long long i;
long long current, current_count, count;
current_count = 1;
current = 1;
for(i=2;i<1000000;i++)
{
// works fine when i is 2 the next line take eternity when i is 3;
count = get_collatz_length(i);
if(current_count <= count)
{
current = i;
current_count = count;
}
}
printf("%lld %lld\n", current, current_count);
return 0;
}
long long get_collatz_length(long long num)
{
long long count;
count = 1;
while(num != 1)
{
printf("%lld\n", num);
if(num%2)
{
num = num*3+1; // here it is;
}
else
{
num/=2;
}
count++;
}
puts("");
return count;
}
It's seems to be bug in dmc compiler, that fails to handle long long type correctly. Here is narrowed test-case:
#include <stdio.h>
int main(void)
{
long long num = 3LL;
/*printf("%lld\n", num);*/
num = num * 3LL;
char *t = (char *) &num;
for (int i = 0; i < 8; i++)
printf("%x\t", t[i]);
putchar('\n');
/*printf("%lld\n", num);*/
return 0;
}
It produces (little endian, so 0x900000009 == 38 654 705 673):
9 0 0 0 9 0 0 0
From dissasembly it looks that it stores 64-bit integer as two 32-bit registers:
.data:0x000000be 6bd203 imul edx,edx,0x3
.data:0x000000c1 6bc803 imul ecx,eax,0x3
.data:0x000000c4 03ca add ecx,edx
.data:0x000000c6 ba03000000 mov edx,0x3
.data:0x000000cb f7e2 mul edx
.data:0x000000cd 03d1 add edx,ecx
.data:0x000000cf 31c0 xor eax,eax
I additionaly tested it with objconv tool, that just confirms my initial diagnose:
#include <stdio.h>
void mul(void)
{
long long a;
long long c;
a = 5LL;
c = a * 3LL;
printf("%llx\n", c);
}
int main(void)
{
mul();
return 0;
}
disassembly (single section):
>objconv.exe -fmasm ..\dm\bin\check.obj
_mul PROC NEAR
mov eax, 5 ; 0000 _ B8, 00000005
cdq ; 0005 _ 99
imul edx, edx, 3 ; 0006 _ 6B. D2, 03
imul ecx, eax, 3 ; 0009 _ 6B. C8, 03
add ecx, edx ; 000C _ 03. CA
mov edx, 3 ; 000E _ BA, 00000003
mul edx ; 0013 _ F7. E2
add edx, ecx ; 0015 _ 03. D1
push edx ; 0017 _ 52
push eax ; 0018 _ 50
push offset FLAT:?_001 ; 0019 _ 68, 00000000(segrel)
call _printf ; 001E _ E8, 00000000(rel)
add esp, 12 ; 0023 _ 83. C4, 0C
ret ; 0026 _ C3
_mul ENDP
Note that mul edx operates implicitely on eax. The result is stored in both registers, higher part (in this case 0) in stored in edx, while lower in eax.

Resources