I2C communication using MSP432G2553 and HMC5883L - sensors

I am trying to interface my HMC5883L magnetometer with my MSP432G2553 to get continuous readings on x,y and z axis to provide orientation for a device.
I am having problem getting data using the i2c communication. I am trying to get the x,y and z data but I am not getting anything from my code. I would appreciate any feedback, I am new to the I2C communication.
I found a code online and have been using it and modifying it. Here is the code:
main code:
#include <msp430g2553.h>
#include "my_types.h"
#include "i2c.h"
#define M_PI 3.14159265358979323846264338327950288
int TXByteCtr;
unsigned char PRxData;
int Rx = 0;
char WHO_AM_I = 0x1E;
char itgAddress = 0x69;
const uchar setup_hmc5883[] = {0x00, 0x70};
const uchar gain_hmc5883[] = {0x01, 0xA0};
const uchar continuous_hmc5883[] = {0x02, 0x00};
const uchar base_hmc5883[] = {0x03};
char readBuffer [6];
void init_I2C(void);
void Transmit(void);
void Receive(void);
int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
int x, y, z ;
float heading ;
float headingDegrees ;
P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0
P1SEL2|= BIT6 + BIT7; // Assign I2C pins to USCI_B0
BCSCTL1 = CALBC1_16MHZ ;
DCOCTL = CALDCO_16MHZ ;
init_I2C();
Rx = 0;
TXByteCtr = 1;
writei2c(0x3C,setup_hmc5883,2);
TXByteCtr = 1;
writei2c(0x3C,gain_hmc5883,2);
TXByteCtr = 1;
writei2c(0x3C,continuous_hmc5883,2);
while(1){
writei2c(0x3C, base_hmc5883, 1);
__delay_cycles(100000);
readi2c(0X3D, readBuffer, 6);
x = readBuffer[0] ;
x |= readBuffer[1] << 8 ;
z = readBuffer[2] ;
z |= readBuffer[3] << 8 ;
y = readBuffer[4] ;
y |= readBuffer[5] << 8 ;
heading = atan2(y, x);
if(heading < 0) heading += 2*M_PI;
headingDegrees = heading * 180/M_PI;
}
}
//-------------------------------------------------------------------------------
// The USCI_B0 data ISR is used to move received data from the I2C slave
// to the MSP430 memory. It is structured such that it can be used to receive
//-------------------------------------------------------------------------------
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
if(Rx == 1){ // Master Recieve?
PRxData = UCB0RXBUF; // Get RX data
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
else{ // Master Transmit
if (TXByteCtr) // Check TX byte counter
{
if ((IFG2 & UCB0TXIFG) || (IFG2 & UCB0RXIFG))
i2cDataInterruptService();
TXByteCtr--; // Decrement TX byte counter
if(!TXByteCtr)
{
UCB0CTL1 |= UCTXSTP; // I2C stop condition
IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(CPUOFF); // Exit LPM0
}
}
}
}
void init_I2C(void) {
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz
UCB0BR1 = 0;
UCB0I2CSA = itgAddress; // Slave Address is 069h
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
IE2 |= UCB0RXIE + UCB0TXIE; //Enable RX and TX interrupt
}
i2c.c:
#include "i2c.h"
volatile char i2cBusy = 0;
volatile unsigned char i2cBufferLength = 0 ;
unsigned char * i2cBufferPtr ;
volatile char txDone = 1 ;
volatile char rxDone = 1 ;
void initi2c(unsigned int divider){
P1SEL |= BIT6 + BIT7 ;
P1SEL2 |= BIT6 + BIT7 ;
UCB0CTL1 |= UCSWRST ;
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC ;
UCB0CTL1 = UCSSEL_2 + UCSWRST ;
UCB0BR0 = divider & 0x00FF ;
UCB0BR1 = ((divider & 0xFF00) >> 8) ;
UCB0I2CSA = 0x069 ;
UCB0I2CIE = UCNACKIE | UCALIE;
UCB0CTL1 &= ~UCSWRST ;
IE2 |= UCB0TXIE | UCB0RXIE ;
}
char writei2c(unsigned char addr, unsigned char * data, unsigned char nbData){
UCB0I2CSA = addr ;
i2cBusy = 0 ;
i2cBufferPtr = data ;
i2cBufferLength = nbData ;
txDone = 0 ;
UCB0CTL1 |= UCTR + UCTXSTT;
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
//while(txDone == 0) ;
//return txDone ;
}
char readi2c(unsigned char addr, unsigned char * data, unsigned char nbData){
UCB0I2CSA = addr ;
i2cBusy = 0 ;
i2cBufferPtr = data ;
i2cBufferLength = nbData ;
rxDone = 0 ;
UCB0CTL1 &= ~UCTR;
UCB0CTL1 |= UCTXSTT;
while(UCB0CTL1 & UCTXSTT);
UCB0CTL1 |= UCTXSTP ;
__bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
while(rxDone == 0) ;
return rxDone ;
}
inline void i2cDataInterruptService(void){
if ((IFG2 & UCB0TXIFG) != 0 || (IFG2 & UCB0RXIFG) != 0){
if((UCB0CTL1 & UCTR) != 0){
if(i2cBusy >= i2cBufferLength){
UCB0CTL1 |= UCTXSTP ;
i2cBusy = 0 ;
txDone = 1 ;
IFG2 &= ~UCB0TXIFG;
}else{
UCB0TXBUF = i2cBufferPtr[i2cBusy] ;
i2cBusy ++ ;
}
}else{
if((i2cBufferLength - i2cBusy) == 1){ // may generate a repeated stop condition
UCB0CTL1 |= UCTXSTP ;
}
i2cBufferPtr[i2cBusy] = UCB0RXBUF;
i2cBusy ++ ;
if(i2cBusy >= i2cBufferLength){
i2cBusy = 0 ;
rxDone = 1 ;
}
}
}
}
inline void i2cErrorInterruptService(void){
if ((UCB0STAT & UCNACKIFG) != 0) {
UCB0CTL1 |= UCTXSTP;
UCB0STAT &= ~UCNACKIFG;
i2cBusy = -1;
txDone = -1 ;
rxDone = -1 ;
}else if ((UCB0STAT & UCALIE) != 0) {
UCB0CTL1 |= UCTXSTP;
UCB0STAT &= ~UCALIE;
i2cBusy = -1;
txDone = -1 ;
rxDone = -1 ;
}
}

Related

Is fdisk divides address space for created partition?

On my system, it has one mem node created already in /dev (/dev/pmem). then i use fdisk command for creating a partition
After creating partition I apply lsblk command and my result is
pmem1 259:4 0 15.8G 0 disk
└─pmem1p2 259:7 0 14.3G 0 part
After that, I write mmap code (Refer devmam library) just like
off_t address = 0x00;
int width = 10;
void *virtladdr, *phyaddr;
int a = 5;
int *lendata = &a;
int main(void)
{
int mode = 0x0777;
char *filename = "/dev/pmem0";
int fdout;
if ((fdout = open (filename, O_RDWR | O_CREAT | O_TRUNC, mode )) < 0)//edited here
{
printf ("can't create file");
return 0;
}
mapped_size = page_size = getpagesize();
offset_in_page = (uint64_t)address & (page_size - 1);
if (offset_in_page + (width * 8) > page_size) {
mapped_size *= 2;
}
printf("map size %ld\n", mapped_size);
phyaddr = mmap(0, mapped_size, (PROT_READ | PROT_WRITE), MAP_SHARED, fdout, address & ~(off_t)(page_size - 1));
1 = I write value 10 on 0x00 address for /dev/pmem1
2 = I write value 20 on 0x00 address for /dev/pmem1p2
Then after I Read mmap
off_t address = 0x00;
int width = 10;
int main(void)
{
int retVal = -1;
int fd;
unsigned long mapped_size, page_size, offset_in_page;
void *virtladdr, *phyaddr;
fd = open("/dev/pmem0", O_RDWR | O_SYNC);
if(fd < 0)
{
printf("file not open \n");
}
mapped_size = page_size = getpagesize();
offset_in_page = (uint64_t)address & (page_size - 1);
if (offset_in_page + (width * 8) > page_size) {
mapped_size *= 2;
}
/* Map one page */
phyaddr = mmap(0, mapped_size, PROT_READ,MAP_PRIVATE , fd, address & ~(off_t)(page_size - 1));
1 = Read 0x00 address from /dev/pmem1 My output is 10
2 = Read 0x00 address from /dev/pmem1p2 My output is 20
So what happened here why 0th address value is different? Is fdisk create two partitions which address starts from 0x00? Or I am in the wrong way?

CS50 pset4 filter reflection

#include "helpers.h"
#include <math.h>
// Convert image to grayscale
void grayscale(int height, int width, RGBTRIPLE image[height][width])
{
for ( int h=0 ; h<height ; h++)
{
for (int w=0 ; w<width ; w++)
{
int i = image[h][w].rgbtBlue + image[h][w].rgbtGreen + image[h][w].rgbtRed ;
float j = i/3 ;
int n = round(j) ;
image[h][w].rgbtBlue = n ;
image[h][w].rgbtGreen = n ;
image[h][w].rgbtRed = n ;
}
}
return;
}
// Convert image to sepia
void sepia(int height, int width, RGBTRIPLE image[height][width])
{
for ( int h=0 ; h<height ; h++)
{
for (int w=0 ; w<width ; w++)
{
float sr = .393 * image[h][w].rgbtRed + .769 * image[h][w].rgbtGreen + .189 * image[h][w].rgbtBlue ;
float sg = .349 * image[h][w].rgbtRed + .686 * image[h][w].rgbtGreen + .168 * image[h][w].rgbtBlue ;
float sb = .272 * image[h][w].rgbtRed + .534 * image[h][w].rgbtGreen + .131 * image[h][w].rgbtBlue ;
int SR = round(sr);
int SG = round(sg);
int SB = round(sb);
if ( SR>255)
{
SR = 255 ;
}
if (SG>255)
{
SG = 255 ;
}
if (SB>255)
{
SB = 255 ;
}
image[h][w].rgbtBlue = SB ;
image[h][w].rgbtGreen = SG ;
image[h][w].rgbtRed = SR ;
}
}
return;
}
// Reflect image horizontally
void swap (int *a , int *b) ;
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
for (int h=0 ; h<height ; h++)
{
for (int w=0 ; w<width/2 ; w++)
{
for (int p=width-1 ; p>=round(width/2) ; p--)
{
int blue = image[h][w].rgbtBlue ;
int B = image[h][p].rgbtBlue ;
swap(&blue , &B);
int g = image[h][w].rgbtGreen ;
int G = image[h][p].rgbtGreen ;
swap(&g , &G);
int r = image[h][w].rgbtRed ;
int R = image[h][p].rgbtRed ;
swap(&r , &R );
}
}
}
return;
}
void swap (int *a , int *b)
{
int tmp = *a ;
*a = *b ;
*b = tmp ;
}
// Blur image
void blur(int height, int width, RGBTRIPLE image[height][width])
{
return;
}
I have written this code for helper.c. It compiles ok and works for grayscale and sepia but fails to reflect the image.
I am stuck in it . Please tell me where have i made the mistake.
// Reflect image horizontally
void reflect(int height, int width, RGBTRIPLE image[height][width])
{
int tmp[3];
for (int h = 0; h < height; h++)
{
for (int w = 0; w < width / 2; w++)
{
// Place the first pixel in a temporary variable
tmp[0] = image[h][w].rgbtRed;
tmp[1] = image[h][w].rgbtGreen;
tmp[2] = image[h][w].rgbtBlue;
// Place the opposite pixel one into the first pixel
image[h][w].rgbtRed = image[h][width - w - 1].rgbtRed;
image[h][w].rgbtGreen = image[h][width - w - 1].rgbtGreen;
image[h][w].rgbtBlue = image[h][width - w - 1].rgbtBlue;
// From the temporary variable extract the first pixel and place it into
the opposite pixel
image[h][width - w - 1].rgbtRed = tmp[0];
image[h][width - w - 1].rgbtGreen = tmp[1];
image[h][width - w - 1].rgbtBlue = tmp[2];
}
}
return;
}
You don't need the third for loop
for (int p=width-1 ; p>=round(width/2) ; p--)
instead, you can assign the opposite pixel inside the second loop as follows:
for (int w=0 ; w<width/2 ; w++)
{
p = width - w - 1;
...
}

How to use vector acsess element through function

This code is made for the multiplication of two arrays given by the user
typedef vector<vector<int> > arr ;
void multiply (arr &arr1 ,arr &arr2 )
{
arr res ;
unsigned new_row = arr1.size() ;
unsigned new_col = arr2.at(0).size();
for(int i = 0 ; i < new_row ; i++)
{
vector <int> vec ;
res.push_back(vec ) ;
for(int j = 0 ; j<new_col ;j++)
{
int x = 0 ;
res.at(i).push_back(x);
for(unsigned k =0 ; k <arr2.size();k++)
{
res.at(i).at(j) += arr1.at(i).at(k)*arr2.at(k).at(j);
}
cout<< res.at(i).at(j) ;
}
}
}
int main()
{
unsigned rows_number1 = 0 , columns_number1 = 0 ;
arr arr1 ;
cout<<"MATRIX A "<<endl<<endl ;
cout << "The Rows : " ;
cin >> rows_number1 ;
cout << "The Columns :" ;
cin>> columns_number1 ;
for(int i = 0 ; i<rows_number1;i++)
{
vector<int> newr ;
arr1.push_back(newr);
for(int j = 0; j<columns_number1 ;j++)
{
int x ;
cout<<"The Member ("<<i+1<<","<<j+1 <<") :" ;
cin>>x ;
arr1.at(i).push_back(x);
}
}
unsigned rows_number2 = 0 , columns_number2 = 0 ;
arr arr2 ;
cout<<"MATRIX B "<<endl<<endl ;
cout << "The Rows : " ;
cin >> rows_number2 ;
cout << "The Columns :" ;
cin>> columns_number2 ;
for(int i = 0 ; i<rows_number2;i++)
{
vector<int> newr ;
arr1.push_back(newr);
for(int j = 0; j<columns_number2 ;j++)
{
int x ;
cout<<"The Member ("<<i+1<<","<<j+1 <<") :" ;
cin>>x ;
arr1.at(i).push_back(x);
}
}
system("cls");
if(columns_number1!=rows_number2)
{
cout<<"Error Multiplication Dimensions" <<endl ;
}
else
{
cout << "A * B ="<<endl;
multiply(arr1,arr2);
}
}
why there is an error and what is the other way ??
how i can improve the code to multiplicate two arrays
Edited : i tried with two 2*2 arrays using console input and output and this is my full code
The problem is out_of_range exception but i don't know why
When you mulitply to matrices, there are strict constraints on the input.
The number of columns of the first matrix must be the same as the number of rows of the second matrix.
When your matrix is represented by a std::vector<std::vector<int>>, you'll have to make sure that ALL the nested std::vectors are of the same size.
Also, don't assume that arr2 is non-empty. When it is empty, arr2.at(0) will thrown an exception.
Here's a more robust version of your function.
void multiply (arr &arr1 ,arr &arr2 )
{
arr res;
unsigned num_rows1 = arr1.size();
if ( num_rows1 == 0 )
{
// Can't do much.
// Return.
return res;
}
unsigned num_cols1 = arr1[0].size();
if ( num_cols1 == 0 )
{
// Can't do much.
// Return.
return res;
}
// Inner vector size check of arr1.
for(unsigned int i = 1 ; i < num_rows1 ; i++)
{
if ( num_cols1 != arr1[i].size() )
{
throw std::runtime_error("Bad input");
}
}
// Make sure the number of columns in arr1 is the same as
// number of rows in arr2.
unsigned num_rows2 = arr2.size();
if ( num_cols1 != num_rows2 )
{
throw std::runtime_error("Bad input");
}
unsigned num_cols2 = arr2[0].size();
if ( num_cols2 == 0 )
{
// Can't do much.
// Return.
return res;
}
// Inner vector size check of arr2.
for(unsigned int i = 1 ; i < num_rows2 ; i++)
{
if ( num_cols2 != arr2[i].size() )
{
throw std::runtime_error("Bad input");
}
}
// All inputs appear to be valid.
// Now, do the multiplication.
unsigned new_row = num_rows1;
unsigned new_col = num_cols2;
res.resize(num_rows1);
for(unsigned i = 0 ; i < new_row ; i++)
{
for(unsigned j = 0 ; j<new_col ;j++)
{
int x = 0 ;
res.at(i).push_back(x);
for(unsigned k =0 ; k < num_cols1; k++)
{
res.at(i).at(j) += arr1.at(i).at(k)*arr2.at(k).at(j);
}
cout<< res.at(i).at(j) ;
}
}
}

Working with labels in OpenMP: How to or is it possible to jump in Threads?

I am trying to get some code paralleled, and algorithm must be remained. Here is the original Code:
#include <stdio.h>
#define N 50
main()
{
int prime[N] ;
int j ;
int k ;
int n ;
int quo,rem ;
P1: prime[0] = 2 ;
n = 3 ;
j = 0 ;
P2: j = j+1 ;
prime[j] = n ;
P3: if (j == (N-1)) goto P9 ;
P4: n = n + 2 ;
P5: k = 1 ;
P6: quo = n / prime[k] ;
rem = n % prime[k] ;
if (rem == 0) goto P4 ;
P7: if (quo <= prime[k]) goto P2 ;
P8: k = k+1 ;
goto P6 ;
P9: for(j=0 ; j < N ; j++) printf("%d ",prime[j]) ;
}
And I changed it into this:
#include <stdio.h>
#include <omp.h>
#include <array>
#include <vector>
#define N 50
int tChecked = 0;
std::vector<int> tempArr;
std::array<int, 4> storeArr;
int main()
{
int prime[N];
int j;
int k;
int n;
int quo, rem;
int test = 0;
P1: prime[0] = 2;
n = 3;
j = 0;
P2:
if (tempArr.empty())
{
j = j + 1;
prime[j] = n;
}
else
{
std::sort(std::begin(tempArr), std::end(tempArr));
for (int i = 0; i < tempArr.size(); i++)
{
j = j + 1;
prime[j] = tempArr[i];
}
}
tChecked = 0;
tempArr.clear();
P3: if (j == (N - 1)) goto P9;
//P4: n = n + 2;
PX:
#pragma omp parallel num_threads(4)
{
int ID = omp_get_thread_num();
if (tChecked = 1)
{
n = storeArr[ID];
goto P6;
}
P4:
n = n + 2 * (ID + 1);
storeArr[ID] = n;
P5: k = 1;
P6: quo = n / prime[k];
rem = n % prime[k];
if (rem == 0) goto P4;
P7:
if (quo <= prime[k])
{
tempArr.push_back(n);
}
}
if (!tempArr.empty()) goto P2;
P8: k = k + 1;
tChecked = 1;
goto PX;
P9: for (j = 0; j < N; j++) printf("%d ", prime[j]);
getchar();
return 0;
}
I am using Visual Studio 2015 and OpenMP suport is on, when I run the code it crashes like this:
Exception thrown at 0x00ED9D29 in HW1.exe: 0xC0000005: Access
violation reading location 0x33612FA8.
When I look at the local variables, I see absurd values like:
prime[2] to prime[49] = -858993460
rem, quo, k = -858993460
What is this -858993460 anyway? I assume this is related to jumping in threads because original code works very fine.
So can you explain where this '-858993460' comes from and why? is it related to jumping or any other thing? And is it possible to jump in parallel threads?
Note: I am sharing the question also maybe it helps:
Implement an OpenMP program that generates prime numbers in a given
interval. You should use the prime generation method given in the next
page ( Do NOT use other method !). Your program should generate a csv
le called results.csv that reports the timing results in the
following format.
table

Convert extended precision float (80-bit) to double (64-bit) in MSVC

What is the most portable and "right" way to do conversion from extended precision float (80-bit value, also known as long double in some compilers) to double (64-bit) in MSVC win32/win64?
MSVC currently (as of 2010) assumes that long double is double synonym.
I could probably write fld/fstp assembler pair in inline asm, but inline asm is not available for win64 code in MSVC. Do I need to move this assembler code to separate .asm file? Is that really so there are no good solution?
Just did this in x86 code...
.686P
.XMM
_TEXT SEGMENT
EXTRN __fltused:DWORD
PUBLIC _cvt80to64
PUBLIC _cvt64to80
_cvt80to64 PROC
mov eax, dword ptr [esp+4]
fld TBYTE PTR [eax]
ret 0
_cvt80to64 ENDP
_cvt64to80 PROC
mov eax, DWORD PTR [esp+12]
fld QWORD PTR [esp+4]
fstp TBYTE PTR [eax]
ret 0
_cvt64to80 ENDP
ENDIF
_TEXT ENDS
END
If your compiler / platform doesn't have native support for 80 bit floating point values, you have to decode the value yourself.
Assuming that the 80 bit float is stored within a byte buffer, located at a specific offset, you can do it like this:
float64 C_IOHandler::readFloat80(IColl<uint8> buffer, uint32 *ref_offset)
{
uint32 &offset = *ref_offset;
//80 bit floating point value according to the IEEE-754 specification and the Standard Apple Numeric Environment specification:
//1 bit sign, 15 bit exponent, 1 bit normalization indication, 63 bit mantissa
float64 sign;
if ((buffer[offset] & 0x80) == 0x00)
sign = 1;
else
sign = -1;
uint32 exponent = (((uint32)buffer[offset] & 0x7F) << 8) | (uint32)buffer[offset + 1];
uint64 mantissa = readUInt64BE(buffer, offset + 2);
//If the highest bit of the mantissa is set, then this is a normalized number.
float64 normalizeCorrection;
if ((mantissa & 0x8000000000000000) != 0x00)
normalizeCorrection = 1;
else
normalizeCorrection = 0;
mantissa &= 0x7FFFFFFFFFFFFFFF;
offset += 10;
//value = (-1) ^ s * (normalizeCorrection + m / 2 ^ 63) * 2 ^ (e - 16383)
return (sign * (normalizeCorrection + (float64)mantissa / ((uint64)1 << 63)) * g_Math->toPower(2, (int32)exponent - 16383));
}
This is how I did it, and it compiles fine with g++ 4.5.0. It of course isn't a very fast solution, but at least a functional one. This code should also be portable to different platforms, though I didn't try.
I've just written this one. It constructs an IEEE double number from IEEE extended precision number using bit operations. It takes the 10 byte extended precision number in little endian format:
typedef unsigned long long uint64;
double makeDoubleFromExtended(const unsigned char x[10])
{
int exponent = (((x[9] << 8) | x[8]) & 0x7FFF);
uint64 mantissa =
((uint64)x[7] << 56) | ((uint64)x[6] << 48) | ((uint64)x[5] << 40) | ((uint64)x[4] << 32) |
((uint64)x[3] << 24) | ((uint64)x[2] << 16) | ((uint64)x[1] << 8) | (uint64)x[0];
unsigned char d[8] = {0};
double result;
d[7] = x[9] & 0x80; /* Set sign. */
if ((exponent == 0x7FFF) || (exponent == 0))
{
/* Infinite, NaN or denormal */
if (exponent == 0x7FFF)
{
/* Infinite or NaN */
d[7] |= 0x7F;
d[6] = 0xF0;
}
else
{
/* Otherwise it's denormal. It cannot be represented as double. Translate as singed zero. */
memcpy(&result, d, 8);
return result;
}
}
else
{
/* Normal number. */
exponent = exponent - 0x3FFF + 0x03FF; /*< exponent for double precision. */
if (exponent <= -52) /*< Too small to represent. Translate as (signed) zero. */
{
memcpy(&result, d, 8);
return result;
}
else if (exponent < 0)
{
/* Denormal, exponent bits are already zero here. */
}
else if (exponent >= 0x7FF) /*< Too large to represent. Translate as infinite. */
{
d[7] |= 0x7F;
d[6] = 0xF0;
memset(d, 0x00, 6);
memcpy(&result, d, 8);
return result;
}
else
{
/* Representable number */
d[7] |= (exponent & 0x7F0) >> 4;
d[6] |= (exponent & 0xF) << 4;
}
}
/* Translate mantissa. */
mantissa >>= 11;
if (exponent < 0)
{
/* Denormal, further shifting is required here. */
mantissa >>= (-exponent + 1);
}
d[0] = mantissa & 0xFF;
d[1] = (mantissa >> 8) & 0xFF;
d[2] = (mantissa >> 16) & 0xFF;
d[3] = (mantissa >> 24) & 0xFF;
d[4] = (mantissa >> 32) & 0xFF;
d[5] = (mantissa >> 40) & 0xFF;
d[6] |= (mantissa >> 48) & 0x0F;
memcpy(&result, d, 8);
printf("Result: 0x%016llx", *(uint64*)(&result) );
return result;
}
Played with the given answers and ended up with this.
#include <cmath>
#include <limits>
#include <cassert>
#ifndef _M_X64
__inline __declspec(naked) double _cvt80to64(void* ) {
__asm {
// PUBLIC _cvt80to64 PROC
mov eax, dword ptr [esp+4]
fld TBYTE PTR [eax]
ret 0
// _cvt80to64 ENDP
}
}
#endif
#pragma pack(push)
#pragma pack(2)
typedef unsigned char tDouble80[10];
#pragma pack(pop)
typedef struct {
unsigned __int64 mantissa:64;
unsigned int exponent:15;
unsigned int sign:1;
} tDouble80Struct;
inline double convertDouble80(const tDouble80& val)
{
assert(10 == sizeof(tDouble80));
const tDouble80Struct* valStruct = reinterpret_cast<const tDouble80Struct*>(&val);
const unsigned int mask_exponent = (1 << 15) - 1;
const unsigned __int64 mantissa_high_highestbit = unsigned __int64(1) << 63;
const unsigned __int64 mask_mantissa = (unsigned __int64(1) << 63) - 1;
if (mask_exponent == valStruct->exponent) {
if(0 == valStruct->mantissa) {
return (0 != valStruct->sign) ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
}
// highest mantissa bit set means quiet NaN
return (0 != (mantissa_high_highestbit & valStruct->mantissa)) ? std::numeric_limits<double>::quiet_NaN() : std::numeric_limits<double>::signaling_NaN();
}
// 80 bit floating point value according to the IEEE-754 specification and
// the Standard Apple Numeric Environment specification:
// 1 bit sign, 15 bit exponent, 1 bit normalization indication, 63 bit mantissa
const double sign(valStruct->sign ? -1 : 1);
//If the highest bit of the mantissa is set, then this is a normalized number.
unsigned __int64 mantissa = valStruct->mantissa;
double normalizeCorrection = (mantissa & mantissa_high_highestbit) != 0 ? 1 : 0;
mantissa &= mask_mantissa;
//value = (-1) ^ s * (normalizeCorrection + m / 2 ^ 63) * 2 ^ (e - 16383)
return (sign * (normalizeCorrection + double(mantissa) / mantissa_high_highestbit) * pow(2.0, int(valStruct->exponent) - 16383));
}

Resources