January 20, 2020, 11:59:33 am

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

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Andy

August 10, 2017, 12:58:19 am Last 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&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&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&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 & 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

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

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 ISTRING
DEF outstr as POINTER
DEF count,i as INT
DEF remain as INT
DEF div as UINT64:div = 16
count = 0
outstr = 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 + 1
until num = 0q
strrev[count] = chr$(0)

for i = 0 to count-1
#<STRING>outstr[i] = strrev[count-1-i]
next i
#<STRING>outstr[i] = 0
RETURN #<STRING>outstr
ENDSUB
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
openconsole
UINT64 numin = 12345678901234567890
'AB54A98CEB1F0AD2 which is different from FFFFFFFFEB1F0AD2.
'FFFFFFFFEB1F0AD2
? hex$2(NUMIN)


waitcon



SUB HEX$2(num as UINT64),string
string outstr
uint64 num2

NUM2=NUM
FOR i = 1 TO 15
  outstr = Hex$(num2 & 0xF) +outstr
? outstr
num2=num2 >> 4
if num2=0 then break
NEXT i
RETURN outstr
ENDSUB

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

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

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
_endasm

print hexnum


fasecero

Try using the q modifier literal

Code Select

UINT64 numin = 12345678901234567890q
print HEX$(NUMIN)
waitcon

jalih

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

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

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 = 0xFF00FF00
uint64 num2 = 12345678901234567890q

print hex32$(num1)
print hex64$(num2)

waitcon
end


$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 hexnum

endsub


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 [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 hexnum

endsub
$option "/p 0"


Andy

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

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

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: 2147483647
INT b = 12772387
INT c = 9090

INT64 a64 = MakeINT64(a)
INT64 b64 = MakeINT64(b)
INT64 c64 = MakeINT64(c)

INT64 numin = a64 * b64 * c64

print HEX$(numin)

PRINT HEX$(12345678901234567890q)

WAITCON

SUB MakeINT64(INT value), INT64
double a = FLT(value)
INT64 b = INT(a)
RETURN b
ENDSUB


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

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

The biggest problem is getting the number into a 64 bit variable.  Using double, like fascero said is the easiest way.

Code Select
openconsole

int64 bignumber
string hex
double smallnumber = 1234567

bignumber = smallnumber * smallnumber * smallnumber

hex = makehex(bignumber)

print bignumber,hex

do
until inkey$=chr$(27)   'press ESC to exit

closeconsole

end

sub 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 ans
endsub


jalih

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, num4
double num3

num1 = 9223372036854775807uq
num3 = 9223372036854775807uq
num2 = Uint64(num3)

num4 = val("9223372036854775807")

print hex64$(num1)
print hex64$(num2)
print hex64$(num4)

waitcon
end


$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 hexnum

endsub


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 hexnum

endsub
$option "/p 0"

ckoehn

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
openconsole

uint64 bignumber
string hex
double smallnumber

smallnumber = 1234567

bignumber = smallnumber ^ 3

print "converting: ", smallnumber, "^3 = ",  bignumber,

hex = makehex(bignumber)

print "to hex: ",hex

DO
until inkey$=chr$(27)

closeconsole

end

sub 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 ans
endsub

jalih

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(),int

int i, start, finish
UINT64 num =9223372036854775807uq
string result

print "Execution times in millisecs for 1000000 iterations:"

start = GetTickCount()
for i = 1 to 1000000
result = hex$(num)
next i
finish = GetTickCount()
print "IWBasic HEX$() command: ",  finish-start

start = GetTickCount()
for i = 1 to 1000000
result = hex64$(num)
next i
finish = GetTickCount()
print "hex64$ function written in inline asm: ", finish-start

waitcon
end


$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 hexnum

endsub
$option "/p 0"

Andy

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

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.