September 23, 2020, 12:22:02 pm

## News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!

## Converting C to IWB

Started by Andy, August 10, 2017, 12:58:19 am

0 Members and 1 Guest are viewing this topic.

#### Andy

##### August 10, 2017, 12:58:19 amLast Edit: August 10, 2017, 01:18:03 am by Andy
Here is an example in C of how to convert a uint64 number to hex:

Code Select
`static char hex [] = { '0', '1', '2', '3', '4', '5', '6', '7',                        '8', '9' ,'A', 'B', 'C', 'D', 'E', 'F' }; int uintToHexStr(unsigned int num,char* buff){    int len=0,k=0;    do//for every 4 bits    {        //get the equivalent hex digit        buff[len] = hex[num&amp;0xF];        len++;        num>>=4;  //shift right 4 chars    }while(num!=0);`

buff receives the corresponding hex (in reverse order - but that's easy enough to sort out).

I'm having trouble converting the above to IWB - can anyone help please?

On the face of it, it looks simple enough - but how do I translate the line:
buff[len] = hex[num&amp;0xF];

I'm quite happy if the number to be converted is a string - if that makes it easier.

OR:

Could this code be placed in an IWB sub routine and called? - never attempted that before.

Thanks,
Andy.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### Andy

##### August 10, 2017, 04:16:42 am #1 Last Edit: August 10, 2017, 04:19:17 am by Andy
As far as I can work out (and I could be wrong as usual), the line:

buff[len] = hex[num&amp;0xF]; is getting the binary XOR value by comparing the binary number against 1111 (0xF), which then gets the corresponding hex value stored in the hex[] Array.

Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### Egil

##### August 10, 2017, 04:44:24 am #2 Last Edit: August 10, 2017, 04:54:51 am by Egil
Hi Andy,

I think the characters &amp; is the result of conversion errors when copying the code. And should read just & instead.

Then for  the decimal to hex conversion... why not use hex\$(nnnn), where nnnn is the decimal value to be converted, and  then arrange the hex expression in 8 bit chunks so the result is presented in the wanted order (reverse or normal)?

As far as I can see (whithout remebering much C coding), that's what your code example does.

EDIT:  Forgot to say that by using  8 bit chunks, it is easy to see if you need to insert a leading zero into the expressions, e.g. 07 instead of just 7.

Support Amateur Radio  -  Have a ham  for dinner!

#### LarryMc

##### August 10, 2017, 07:07:14 am #3
I guess I'm not understanding something.
Why are you trying to create a function that converts a Uint64 to HEX when we already have one called HEX\$
What am I missing?

LarryMc
Larry McCaughn
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

#### Andy

##### August 10, 2017, 08:17:50 am #4 Last Edit: August 10, 2017, 08:21:01 am by Andy
Thanks Egil I will have a look now, been out all day.

Larry,

If I take a uint64 number for example.....    12345678901234567890
and use HEX\$ I get:   FFFFFFFFEB1F0AD2

Now when a registry qword displays the hex version of the above decimal number it gives:
AB54A98CEB1F0AD2 which is different from FFFFFFFFEB1F0AD2 (not displaying the AB54A98C part but replacing them with F's).

Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### LarryMc

##### August 10, 2017, 11:07:28 am #5
This is the HEX\$ code. See if you can see why you get a different answer.
Code Select
`SUB HEX\$(num as UINT64)DEF strrev[17] as ISTRINGDEF outstr as POINTERDEF count,i as INTDEF remain as INTDEF div as UINT64:div = 16count = 0outstr = AllocHeap(17)do remain = num % div num = num / div if remain > 9 strrev[count] = (remain-10) + asc("A") else strrev[count] = remain + asc("0") endif count = count + 1until num = 0qstrrev[count] = chr\$(0)for i = 0 to count-1 #<STRING>outstr[i] = strrev[count-1-i]next i#<STRING>outstr[i] = 0RETURN #<STRING>outstrENDSUB`
LarryMc
Larry McCaughn
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

#### LarryMc

##### August 10, 2017, 12:39:30 pm #6 Last Edit: August 10, 2017, 12:46:32 pm by LarryMc
OK, we can fix this but we need someone smarter than me to sort it out.
I know why it's not working and I know HOW to fix it but I just don't know how to write the code to do it.
First I wrote this simple little sub that right shifts 4 bits at a time and then uses the existing HEX\$ function to get the hex value and add it on to a string and then return it.
Code Select
`openconsoleUINT64 numin = 12345678901234567890'AB54A98CEB1F0AD2 which is different from FFFFFFFFEB1F0AD2.'FFFFFFFFEB1F0AD2? hex\$2(NUMIN)waitconSUB HEX\$2(num as UINT64),stringstring outstruint64 num2NUM2=NUMFOR i = 1 TO 15  outstr = Hex\$(num2 & 0xF) +outstr? outstr num2=num2 >> 4 if num2=0 then breakNEXT iRETURN outstrENDSUB`
When you run the program with Andy's input you get the same wrong result as you do with the HEX\$ FUNCTION.
It demonstrates the problem with the HEX\$ function: after 32 bits there is a problem with shifting.

Now, I found a discussion about a problem with shifting 64 bits numbers past 32 bits.  The discussion, with Turley's reasoning for using his implementation, and Sapero's workaround fix, is all in assembly and in terms of LEFT shifting.   To solve our problem, we need a version of my little sub(or a variation thereof) to be built around RIGHT shifting and with Sapero's assembly workaround converted over to right shifting.

Like I implied, the info is there. We just need someone smart enough to take it and run. I'm not the who on this one.

http://www.ionicwind.com/forums/index.php?topic=1707.msg15766#msg15766
LarryMc
Larry McCaughn
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

#### Andy

##### August 10, 2017, 12:53:38 pm #7
Larry, I think that's the answer, wether I'm clever enough to do it remains to be seen, I will have a look at it in the morning as I really need to get some sleep now.

Thanks Larry.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### jalih

##### August 10, 2017, 01:03:11 pm #8
Hi all,

You could probably use this simple asm trick to convert one byte to ascii hexadecimal and process INT64 one byte at a time:

Code Select
`istring hexnum[2]char num = 255_asm mov al, [\$num] aam 16 @digit2: add al, 090h daa adc al, 040h daa @digit1: xchg al, ah add al, 090h daa adc al, 040h daa mov word [\$hexnum], ax_endasmprint hexnum`

#### fasecero

##### August 10, 2017, 10:12:38 pm #9
Try using the q modifier literal

Code Select
`UINT64 numin = 12345678901234567890qprint HEX\$(NUMIN)waitcon`

#### jalih

##### August 11, 2017, 12:59:29 am #10
I wrote a fast 32-bit hex32\$() subroutine in asm for quick ascii hex conversion. Fast 64-bit version should be possible by just calling routine twice to handle hi -and low-dwords separately.

I will post some code later today...

#### Andy

##### August 11, 2017, 03:38:40 am #11
Fasecero,

I forgot about adding the Q to the end of the uint64 - that works, but brings back the old Problem of adding a Q to a number you don't know.

Larry,

I copied the Hex\$ Code, renamed it - that's all, added a Q to the unit64 number and that didn't work, but the built in IWB Hex\$ function does.

Jalih,
That would be great if you could post a copy and explain how to use it.

Thanks guys.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### jalih

##### August 11, 2017, 09:04:46 am #12
Quote from: Andy on August 11, 2017, 03:38:40 am
Jalih,
That would be great if you could post a copy and explain how to use it.

Here are hex32\$() and hex64\$() functions:

Code Select
`uint num1 = 0xFF00FF00uint64 num2 = 12345678901234567890qprint hex32\$(num1)print hex64\$(num2)waitconend\$option "/p 1"sub hex32\$(uint num), string    istring hexnum[9]_asm    lea esi, [num]    lea edi, [hexnum]    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov [edi+4], edx    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov byte [edi+8], 0_endasm    return hexnumendsubsub hex64\$(uint64 num), string    istring hexnum[17]_asm    lea esi, [num+4]    lea edi, [hexnum]    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov [edi+4], edx    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    sub esi, 4    add edi, 8    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov [edi+4], edx    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov byte [edi+8], 0_endasm    return hexnumendsub\$option "/p 0"`

#### Andy

##### August 11, 2017, 09:15:52 am #13
Jalih,

I will try that, thanks for posting it, the only problem is you have added the letter q to the uint64 number, and I don't know what the number will be and cannot hard code it.

Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### jalih

##### August 11, 2017, 09:51:08 am #14
Quote from: Andy on August 11, 2017, 09:15:52 am
I will try that, thanks for posting it, the only problem is you have added the letter q to the uint64 number, and I don't know what the number will be and cannot hard code it.

You don't need to hard code a number. The q modifier is  only needed when you enter a numeric literal (constant) in code. Without the q modifier, compiler treats it as INT.

#### srvaldez

##### August 11, 2017, 02:43:09 pm #15
Andy, maybe make a union with a Uint64 and two ints, then take the hex of the two ints.

#### fasecero

##### August 11, 2017, 04:32:10 pm #16 Last Edit: August 11, 2017, 04:55:39 pm by fasecero
Quote
I forgot about adding the Q to the end of the uint64 - that works, but brings back the old Problem of adding a Q to a number you don't know.

Wow, I didn't know that.

It seems that INT() return an INT64 from a double, so theoretically you can use this to "attach" the q to a non literal integer.

Code Select
`INT a = 106335683 ' MAX: 2147483647INT b = 12772387INT c = 9090INT64 a64 = MakeINT64(a)INT64 b64 = MakeINT64(b)INT64 c64 = MakeINT64(c)INT64 numin = a64 * b64 * c64print HEX\$(numin)PRINT HEX\$(12345678901234567890q)WAITCONSUB MakeINT64(INT value), INT64 double a = FLT(value) INT64 b = INT(a) RETURN bENDSUB`

If this works for you then you should stick to INT64 in your whole program, and when some external func gives you an INT you should cast it to INT64 right away.

#### jalih

##### August 12, 2017, 12:05:49 am #17
Quote from: fasecero on August 11, 2017, 04:32:10 pm
It seems that INT() return an INT64 from a double, so theoretically you can use this to "attach" the q to a non literal integer.

On Andy's application, it's not probably a good idea. With double you get 52-bits of precision, not the full range of INT64.

#### fasecero

##### August 12, 2017, 02:07:43 am #18 Last Edit: August 12, 2017, 12:10:05 pm by fasecero
And you are right. A double has 64 bits but can represent an "int55" (assuming such a thing existed), so the funcion I made will not work, thank you very much for pointing this out. I was trying to avoid this suggestion, but the solution I can think of would be to make a c dll that does the casting from int to int64. Can't find another "native" way to do it.

EDIT: Now that I think about it better, I think it will work just fine. The MakeINT64() function is used to pass a number from 32 to 64 bits, a number that you already know it will be 32 bits, only then you use this function. If you have a number greater than 32 bits you just use an INT64 variable from the beginning, and forget about that function. Can you guys think of an example where something doesn't work fine?

#### ckoehn

##### August 12, 2017, 08:47:22 pm #19
The biggest problem is getting the number into a 64 bit variable.  Using double, like fascero said is the easiest way.

Code Select
`openconsoleint64 bignumberstring hexdouble smallnumber = 1234567bignumber = smallnumber * smallnumber * smallnumberhex = makehex(bignumber)print bignumber,hexdountil inkey\$=chr\$(27)   'press ESC to exitcloseconsoleendsub makehex(int64 bnum),string istring num[17] = "0123456789ABCDEF\0" istring ans[17] = "----------------\0" int ptr=1 for i=15 to 0 step -1 ans[i] = mid\$(num,(bnum % 16) + 1,1) bnum = bnum >> 4 next i while mid\$(ans,ptr,1)="0" 'remove leading 0's ptr++ endwhile ans = mid\$(ans,ptr) return ansendsub`

#### jalih

##### August 13, 2017, 12:08:36 am #20
Quote from: fasecero on August 12, 2017, 02:07:43 am
EDIT: Now that I think about it better, I think it will work just fine. The MakeINT64() function is used to pass a number from 32 to 64 bits, a number that you already know it will be 32 bits, only then you use this function. If you have a number greater than 32 bits you just use an INT64 variable from the beginning, and forget about that function. Can you guys think of an example where something doesn't work fine?

You can't use double to convert input to 64-bit INT and use the full number range. IWBasic uses VAL() for conversion and it's limited to DOUBLE precision. Easiest way is to use the C-library or you can write your own function.

With binary data there is no problem, as any 8-byte buffer for data will suffice.

Code below demonstrates limited range of DOUBLE:

Code Select
`Uint64 num1, num2, num4double num3 num1 = 9223372036854775807uqnum3 = 9223372036854775807uqnum2 = Uint64(num3)num4 = val("9223372036854775807")print hex64\$(num1)print hex64\$(num2)print hex64\$(num4)waitconend\$option "/p 1"sub hex32\$(uint num), string    istring hexnum[9]_asm    lea esi, [num]    lea edi, [hexnum]    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov [edi+4], edx    mov byte [edi+8], 0_endasm    return hexnumendsubsub hex64\$(uint64 num), string    istring hexnum[17]_asm    lea esi, [num+4]    lea edi, [hexnum]    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov [edi+4], edx    sub esi, 4    add edi, 8    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov [edi+4], edx    mov byte [edi+8], 0_endasm    return hexnumendsub\$option "/p 0"`

#### ckoehn

##### August 13, 2017, 06:50:52 am #21
I agree with Jalih.  Using double to convert to large of numbers results in an inaccurate conversion.  I tried it and wondered why my routine wasn't working.  Printing out the int64 number showed that it wasn't what i expected it to be.

This routine works too.

Code Select
`openconsoleuint64 bignumberstring hexdouble smallnumbersmallnumber = 1234567bignumber = smallnumber ^ 3print "converting: ", smallnumber, "^3 = ",  bignumber,hex = makehex(bignumber)print "to hex: ",hexDOuntil inkey\$=chr\$(27)closeconsoleendsub makehex(uint64 bnum),string String num = "0123456789ABCDEF" string ans ="" int ptr=1 for i=15 to 0 step -1 ans = mid\$(num,(bnum % 16) + 1,1) + ans bnum = bnum >> 4 next i while (mid\$(ans,ptr,1)="0") and (ptr<16) 'remove leading 0's ptr++ endwhile ans = mid\$(ans,ptr) return ansendsub`

#### jalih

##### August 13, 2017, 08:29:17 am #22
Quote from: ckoehn on August 13, 2017, 06:50:52 am
This routine works too.

I timed IWBasic's HEX\$() and my hex64\$() functions and found that my routine is over five times faster. Inline asm really makes the difference:

Code Select
`DECLARE "kernel32.dll", GetTickCount(),intint i, start, finishUINT64 num =9223372036854775807uqstring resultprint "Execution times in millisecs for 1000000 iterations:"start = GetTickCount()for i = 1 to 1000000 result = hex\$(num)next ifinish = GetTickCount()print "IWBasic HEX\$() command: ",  finish-startstart = GetTickCount()for i = 1 to 1000000 result = hex64\$(num)next ifinish = GetTickCount()print "hex64\$ function written in inline asm: ", finish-startwaitconend\$option "/p 1"sub hex64\$(uint64 num), string    istring hexnum[17]_asm    lea esi, [num+4]    lea edi, [hexnum]    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov [edi+4], edx    sub esi, 4    add edi, 8    mov al, byte [esi]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov edx, eax    shl edx, 16    mov al, byte [esi+1]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov dx, ax    mov al, byte [esi+2]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov ecx, eax    shl ecx, 16    mov al, byte[esi+3]    aam 16    add al, 090h    daa    adc al, 040h    daa    xchg al, ah    add al, 090h    daa    adc al, 040h    daa    mov cx, ax    mov [edi], ecx    mov [edi+4], edx    mov byte [edi+8], 0_endasm    return hexnumendsub\$option "/p 0"`

#### Andy

##### August 13, 2017, 08:49:39 am #23
This is all good stuff guys,

Clint I found you routine works, but if I just say bignumber = 12345678901234567890 directly it doesn't.

Jalih, I tried to assign a number like this:
result = hex64\$(9223372036854775806)

It came back with FFFFFFFFFFFFFFFE which is 18446744073709551614 decimal.

Am I missing something?

Thanks guys - please keep trying.
Day after day, day after day, we struck nor breath nor motion, as idle as a painted ship upon a painted ocean.

#### jalih

##### August 13, 2017, 09:10:33 am #24
Quote from: Andy on August 13, 2017, 08:49:39 am
Jalih, I tried to assign a number like this:
result = hex64\$(9223372036854775806)

It came back with FFFFFFFFFFFFFFFE which is 18446744073709551614 decimal.

Am I missing something?

You need to add q or uq modifier for numeric literal. It's IWBasic limitation not fault in my code.