Ive modified Paul Dixon's orignal atou_ex function for use with 64bit JWasm64. Return value in rax. Done some basic tests and it seemed to work - so fingers crossed that it continues to. If anything looks out of place let me know.
.686
.MMX
.XMM
.x64
option casemap : none
option win64 : 11
option frame : auto
option stackbase : rsp
atou_ex PROTO :QWORD
.code
;--------------------------------------------------------------------------------------------------------------------
; Convert ascii string pointed to by String param to unsigned qword value. Returns qword value in rax.
; Original code for atou_ex by Paul Dixon. Converted to 64bit support by fearless 2015
;--------------------------------------------------------------------------------------------------------------------
atou_ex PROC FRAME USES RCX RDX String:QWORD
; ------------------------------------------------
; Convert decimal string into UNSIGNED QWORD value
; ------------------------------------------------
mov rdx, String
xor rcx, rcx
movzx rax, BYTE PTR [rdx]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+1]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+2]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+3]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+4]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+5]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+6]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+7]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+8]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+9]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+10]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+11]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+12]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+13]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+14]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+15]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+16]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+17]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+18]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+19]
test rax, rax
jz quit
lea rcx, [rcx+rcx*4]
lea rcx, [rax+rcx*2-48]
movzx rax, BYTE PTR [rdx+20] ; if > 20 ascii chars in length will fall out.
test rax, rax
jnz out_of_range
quit:
lea rax, [rcx] ; return value in RAX
or rcx, -1 ; non zero in RCX for success
ret
out_of_range:
xor rax, rax ; zero return value on error
xor rcx, rcx ; zero in RCX is out of range error
ret
atou_ex endp
END
Hi fearless,
why do you use a stack frame? Is it necessary?
Gunther
Hi Gunther,
Not sure if it is necessary, tbh i just added it in to most of the functions i created and seemed to compile ok, i just kept my fingers crossed. Maybe others will know if its needed or if there is other optional (maybe more optimal?) ways of calling/defining functions to use. Im still new to the 64bit stuff, so my fingers have been crossed a lot lately ;-)
Hi fearless,
Quote from: fearless on April 26, 2015, 07:19:59 AM
Not sure if it is necessary, tbh i just added it in to most of the functions i created and seemed to compile ok, i just kept my fingers crossed. Maybe others will know if its needed or if there is other optional (maybe more optimal?) ways of calling/defining functions to use. Im still new to the 64bit stuff, so my fingers have been crossed a lot lately ;-)
I think that by following the calling conventions, a stack frame isn't necessary. The details can be found here (http://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention). Please notice the differences between MS calling convention and the System V AMD64 ABI. Also new is the MS __vectorcall (http://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_vectorcall). It supports homogeneous vector aggregate (HVA) values. I haven't used that call until now, because it wasn't necessary. But in some situations it could be quite well.
Gunther
Since fearless has no local variables, no stack frame will be generated anyway. Habran can correct me if wrong; certainly, it shouldn't be generated. So you can leave that option on always, will only have effect when it's needed. It's also involved with SEH, but I don't think that's relevant here.
In general stack frame is not necessary for fastcall convention. Arguments are (well, can be) pushed on stack, like 32-bit; main difference is first four are also in registers RCX etc. Stack frame, as I say, is for local variables and isn't directly concerned with calling conventions.
MS "vectorcall" - in assembler of course we can use SIMD's to pass args without Bill's gracious permission; I've been doing it as a matter of course.
U know, fearless, u don't have to "uses" RCX and RDX since they're volatile. In fact since rcx is used for (the first) argument, I don't think "uses" is of any use. And, since rcx already contains "String" it would make more sense to switch the roles of rcx and rdx in your code.
BTW fearless, (answering the other thread) no I don't have vKim 64-bit macros handy! Someday when I have spare time it would be an interesting project
local variables OR passed arguments will cause generation of a prologue/epilogue
deleted
I think i was mainly using the FRAME as a just in case measure, not really understanding its inner working, but hoping that including it would be ok. I used it primarily when doing stuff for the x64dbg plugins, the callback functions crashed without the FRAME, so when i put it back in, i also ended up putting it everywhere else in other functions as well. Probably a habit of declaring USES X Y Z now as well, just in case - even tho as you say the rcx rdx r8 and r9 are volitile and are used anyhow
When i was converting the orignal code for atou_ex, it used the edx and ecx registers, and so i didnt want to modify too heavily or change the flow of the code use of registers that much, i was hoping to just expand the registers to their full 64bit width, and adjust the start of the code that had the push ebp stuff (commented them out) to use the params directly. Seemed to work so just left it as listed above.
Ive had a look at the utoa_ex function as well, but so far its proved a little more troublesome - i can get it to work sort of, but only for the dword equivalent length of a number is changed to its ascii representation so far.
It's a little confusing ...
To me the word "stack frame" means using rbp (or, ebp) to reserve space below the entry point on the stack for local variables. (Or other purposes, see SEH). However I found an MSN reference (https://msdn.microsoft.com/en-us/library/ms235286.aspx) which uses the word "frame" in the context of merely putting arguments on the stack - like dedndave (who's implying that just having a prologue/eliplogue constitutes a "frame"). So, let's ignore the language.
What JWASM does when option frame : auto is used - OR, not used - is this. If there are no local variables (or, other complications like RtlAddFunctionTable() - not applicable here; or non-leaf, see below) then it doesn't use rbp to create what I've traditionally called a frame. Instead it puts the first 4 args in rcx, rdx, r8, r9, reserve 20h byte on stack for them, any other args on stack above (ignoring other complications which aren't applicable here). When you do have locals JWasm creates an "rbp frame" whether the option is on or not. In other words, the option is ignored.
But when the function is not a leaf function, the option causes an "rbp frame", due to Habran's latest work. Again - NAH (not applicable here). I believe that's the only difference with the "auto" option. See Habran's posts in the JWasm development forum for examples.
Habran of course knows exactly so if I'm wrong pls correct me. We mortals can simply check the code generated and see what I mean. AFAIK fearless's code will compile the same with or without that option; and so will the invoke used to call it. I should check it myself but I'll take a chance on being wrong, I'm used to it.
nidud, not just C-proc, an assembler proc also needs to use the stack for arguments if you're using JWasm invoke, and following new ABI.
p.s. just checked JWasm, seems it also puts first args on stack, which is not required by ABI but makes sense.
@fearless, I posted the above b4 seeing your post - sure your code will work as it is. Just makes more sense to use rcx for String since it's there already.
p.s. the frame has something to do with SEH (not sure what exactly) so makes sense that when working with a debugger it's necessary.
deleted
Listen to rrr314159, he is a teacher ;)
Hi nidud,
Quote from: nidud on April 27, 2015, 05:12:37 AM
Is this correct ?
it's all described here (http://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention) and in post #3 above.
Gunther
Listen to Habran, he is a doer, much better than a teacher
@nidud, no that's not quite it.
mov rcx,1 ; load regs with 4 args
mov rdx,2
mov r8,3
mov r9,4
sub rsp,8*8 ; reserve shadow space for first four, plus "real" space for last 4
mov [rsp+24],5 ; load stack with 4 args, 5 thru 8
mov [rsp+16],6
mov [rsp+8],7
mov [rsp+0],8
call proc8 ; make the call
add rsp,8*8 ; release stack
It could be done like this, modifying your example minimally. Although JWasm doesn't do it exactly that way, the effect is the same. Of course this assumes "5", "6" etc are registers else the mov's won't work ...
And the called proc doesn't allocate stack, that's already been done. But it may spill rcx, rdx, r8 r9 to their (already allocated) space on the stack - that's what it's there for. That's as you show, except you have them in upside-down order. And in this case we wouldn't call them "locals" they're saved ("spilled") args.
JWasm doesn't put rcx etc on the stack when calling. But when they're used in the proc, it spills them first. Me, I write my own invoke, and always put them on the stack when calling, except when speed is really critical.
Anyway this means that even if fearless xor's rcx before putting String in rdx, it will work OK, because rcx gets saved before xor'ing. Seems strange but if JWasm does it that way it must be good!
If there were locals - which you don't show - then space would be used on the stack, but it goes below the entry point, not above (as for args), and uses rbp to reference them.
There are more details, some args are pointed to not passed, stack alignment is critical (above discussion assumes it's already aligned) and then there's SEH which I haven't really gotten to yet
BTW Gunther's reference (Wikipedia) is incomplete and has the standard mistake re. floating point (which I've mentioned often) - seems to be required for any write-up on the net. Use MSDN for info, the only place I've seen that gets this one right
Here is one way to do that:
atou_ex PROC FRAME String:QWORD
xor eax, eax
.repeat
movzx rdx, BYTE PTR [rcx]
test rdx, rdx
.break .if (ZERO?)
lea rax, [rax+rax*4]
lea rax, [rdx+rax*2-48]
inc rcx
.until FALSE
mov rcx, -1 ; non zero in RCX for success
ret ; rax contains the number
atou_ex endp
atou_ex:
0000000001041147 33 C0 xor eax,eax
0000000001041149 48 0F B6 11 movzx rdx,byte ptr [rcx]
000000000104114D 48 85 D2 test rdx,rdx
0000000001041150 74 0E je atou_ex+19h (01041160h)
0000000001041152 48 8D 04 80 lea rax,[rax+rax*4]
0000000001041156 48 8D 44 42 D0 lea rax,[rdx+rax*2-30h]
000000000104115B 48 FF C1 inc rcx
000000000104115E EB E9 jmp atou_ex+2h (01041149h)
0000000001041160 48 C7 C1 FF FF FF FF mov rcx,0FFFFFFFFFFFFFFFFh
0000000001041167 C3 ret
You can try to add a counter :biggrin:
Here is one with the counter:
atou_ex PROC FRAME String:QWORD
xor eax,eax
xor edx,edx
.repeat
movzx r8, BYTE PTR [rcx+rdx]
.if (!r8)
mov rcx, -1 ; non zero in RCX for success
jmp done
.endif
lea rax, [rax+rax*4]
lea rax, [r8+rax*2-48]
inc edx
.until (edx==20)
xor ecx,ecx ;zero means out of range error
done:
ret ; rax contains the number
atou_ex endp
atou_ex:
0000000000981147 33 C0 xor eax,eax
0000000000981149 33 D2 xor edx,edx
000000000098114B 4C 0F B6 04 0A movzx r8,byte ptr [rdx+rcx]
0000000000981150 4D 85 C0 test r8,r8
0000000000981153 75 09 jne atou_ex+17h (098115Eh)
0000000000981155 48 C7 C1 FF FF FF FF mov rcx,0FFFFFFFFFFFFFFFFh
000000000098115C EB 12 jmp atou_ex+29h (0981170h)
000000000098115E 48 8D 04 80 lea rax,[rax+rax*4]
0000000000981162 49 8D 44 40 D0 lea rax,[r8+rax*2-30h]
0000000000981167 FF C2 inc edx
0000000000981169 83 FA 14 cmp edx,14h
000000000098116C 75 DD jne atou_ex+4h (098114Bh)
000000000098116E 33 C9 xor ecx,ecx
0000000000981170 C3 ret
Here is one more complex:
;this function can convert up to 128 bit:
;binary: b or y base = 2
;octal: o or q base = 8
;decimal: d or t base = 10
;hexadecimal: h base = 16
;hsize contains number of chars
;dst contains converted number in little endian
;that means you can load register directly from memory
;like mov rax,dst
hatoi128 PROC FRAME USES rbx src:QWORD,dst:QWORD,base:SDWORD,hsize:SDWORD
movsxd r9,r9d
mov r11d,r8d
mov rbx,rdx
add r9,rcx
xor eax,eax
mov r10,rcx
mov [rdx],rax
mov [rdx+8],rax
.repeat
movzx rax,BYTE PTR [r10]
.if (al <= '9')
sub eax,'0'
.else
or eax,20h
sub eax,57h
.endif
mov rdx,rbx
.for (r8d=8¦r8d¦r8d--)
movzx ecx,WORD PTR [rdx]
lea rdx,QWORD PTR [rdx+2]
imul ecx,r11d
add eax,ecx
mov [rdx-2],ax
shr eax,16
.endfor
inc r10
.until (r10==r9)
ret
hatoi128 ENDP
Quote from: rrr314159 on April 27, 2015, 07:53:37 AM
Listen to Habran, he is a doer
He's overdoing it :P
No, seriously: compliments to Habran, you are doing a great job :t
It's a great pleasure to receive flowers from you jj2007 :biggrin:
Hi rrr,
Quote from: rrr314159 on April 27, 2015, 07:53:37 AM
BTW Gunther's reference (Wikipedia) is incomplete and has the standard mistake re. floating point (which I've mentioned often) - seems to be required for any write-up on the net. Use MSDN for info, the only place I've seen that gets this one right
that might be, but the following sources are complete: source 1 (https://msdn.microsoft.com/en-us/library/9z1stfyw.aspx), source 2 (https://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx), source 3 (https://msdn.microsoft.com/en-us/library/dn375768.aspx). Interesting is the following overall view (http://yankeerino.com/windowsx64callingconvention.bhs), too. A drawback is that __vectorcall is not mentioned. But the comparison between Windows and Unix is interesting.
By the way, good job Habran. :t
Gunther
gunther, I know MSDN has the right info somewhere, when I get around to it I'll search my notes. The Visual Studio 2013 pages you give 1) may be correct for Visual Studio for all I know, 2) may have the right info somewhere if I read all the other pages, but, the specific pages on Register Usage are wrong for MASM and our environment. They have the same old error re floating point. - and it's not trivial, you will soon run into it if you do much 64-bitting
[edit] actually, come to think of it, as long as you use invoke in the normal way you don't have to worry about it. If you write an invoke macro for ML64, as I did, then it's inescapable; or if you load up registers then call a function, expecting those ref's to be correct. But JWasm invoke does it right, so u don't really have to deal with it
Hi rrr,
Quote from: rrr314159 on April 28, 2015, 04:38:46 AM
[edit] actually, come to think of it, as long as you use invoke in the normal way you don't have to worry about it. If you write an invoke macro for ML64, as I did, then it's inescapable; or if you load up registers then call a function, expecting those ref's to be correct. But JWasm invoke does it right, so u don't really have to deal with it
all the invoke and other fancy hll stuff isn't my way. I've another point of view.
Gunther
Quote from: Guntherall the invoke and other fancy hll stuff isn't my way. I've another point of view.
- We're on the same page! I too want to get down to the nitty-gritty - know exactly how things work - like mabdelouahab, whose attitude I admire. Like him, if I want to know, and someone else has the answer - I ask
Quote from: rrr314159 on April 30, 2015, 08:10:17 AM
- We're on the same page! I too want to get down to the nitty-gritty - know exactly how things work - like mabdelouahab, whose attitude I admire. Like him, if I want to know, and someone else has the answer - I ask
Yes, of course. The tricky point with all that fancy macro stuff is that: Do you have the full control over the code? If not, you can do the same with HLL.
Gunther
Tricky macro stuff: I have to admit I don't have "full control". That would mean knowing what macros will do before trying them. No, I have to run it: don't always know whether, for instance, a nested macro argument will be expanded, or how far, until I do. When it doesn't work, I put a lot of %'s and &'s in. Since it still doesn't work, I take them out; usually then, it works ;) I honestly don't think it's entirely consistent, the rules vary according to factors only qWord understands. However I'm getting better at it .. the key thing is, once it works it keeps working; without that, forget it; but that is the case. And, macros are so powerful they're worth some hair torn out.
HLL: I finally realized, when people refer to MASM HLL they mean those .if, .else statements! I thought it meant languages like C or Pascal - and sometimes, it does - or Randall Hyde's HLA, or something. Now that I know what goes on "under the hood" I use those HLL constructs and think they're great. Habran's example convinced me ... also an old thread from a couple years ago where nidud is fixing them in JWasm and argues convincingly for his project
Hey rrr314159 :biggrin:
I am happy to hear that you now understand the beauty of HLL :t
Easy to read and understand what's going on
BTW did you read your PM?
Just wondering if anyone has a working 64bit code version of the utoa_ex function? ive had no success converting the existing code.
:biggrin:
Humerously enough, I forgot how it works as I wrote it years ago but just converted it and it works OK. If you want it as a library module, just put it into a bare procedure in ML64 and assemble it. I have added the NOSTACKFRAME to turn off the prologue/epilogue code that I am using with ML64.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include64\masm64rt.inc
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
LOCAL decstr :QWORD
mrm decstr, "12345678"
mov rcx, decstr
call atou_ex
conout str$(rax),lf
waitkey
invoke ExitProcess,0
ret
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
NOSTACKFRAME
atou_ex proc
; ------------------------------------------------
; Convert decimal string into UNSIGNED QWORD value
; ------------------------------------------------
; argument in RCX
xor r11, r11
movzx rax, BYTE PTR [rcx]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+1]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+2]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+3]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+4]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+5]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+6]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+7]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+8]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+9]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+10]
test rax, rax
jnz out_of_range
quit:
lea rax, [r11] ; return value in RAX
or rcx, -1 ; non zero in RCX for success
ret
out_of_range:
xor rax, rax ; zero return value on error
xor rcx, rcx ; zero in RCX is out of range error
ret
atou_ex endp
STACKFRAME
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
This one will do 19 digits.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
NOSTACKFRAME
atou_ex proc
; ------------------------------------------------
; Convert decimal string into UNSIGNED QWORD value
; ------------------------------------------------
; argument in RCX
xor r11, r11
movzx rax, BYTE PTR [rcx]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+1]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+2]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+3]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+4]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+5]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+6]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+7]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+8]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+9]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+10]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+11]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+12]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+13]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+14]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+15]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+16]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+17]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+18]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+19]
test rax, rax
jz quit
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx+20]
test rax, rax
jnz out_of_range
quit:
lea rax, [r11] ; return value in RAX
or rcx, -1 ; non zero in RCX for success
ret
out_of_range:
xor rax, rax ; zero return value on error
xor rcx, rcx ; zero in RCX is out of range error
ret
atou_ex endp
STACKFRAME
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Quote from: fearless on September 06, 2016, 12:26:15 PM
Just wondering if anyone has a working 64bit code version of the utoa_ex function? ive had no success converting the existing code.
And using loop ?option epilogue:none
option prologue:none
.code
atou_ex proc
; ------------------------------------------------
; Convert decimal string into UNSIGNED QWORD value
; ------------------------------------------------
; argument in RCX
xor r11, r11
movzx rax, BYTE PTR [rcx]
test rax, rax
jz quit
xor r10, r10
@@:
inc r10
inc rcx
lea r11, [r11+r11*4]
lea r11, [rax+r11*2-48]
movzx rax, BYTE PTR [rcx]
test rax, rax
jz quit
cmp r10, 20
jl @B
test rax, rax
jnz out_of_range
quit:
lea rax, [r11] ; return value in RAX
or rcx, -1 ; non zero in RCX for success
ret
out_of_range:
xor rax, rax ; zero return value on error
xor rcx, rcx ; zero in RCX is out of range error
ret
atou_ex endp
end
option casemap :none
option epilogue:none
option prologue:none
ifdef __HJWASM__
OPTION STACKBASE:rsp
OPTION WIN64:2
endif
includelib msvcrt64.lib
exit proto :dword
printf proto args:vararg
extern atou_ex :proc
.data
fmt db "%s %d",13,10,0
qstr db "12345678",0
.code
mainCRTStartup proc
sub rsp, 30h
mov rcx, offset qstr
call atou_ex
invoke printf,addr fmt, addr qstr, rax
invoke exit, 0
mainCRTStartup endp
end
EDIT fix as habran suggested
You can use r10 instead of r12 it is a volatile register, r12 is nonvolatile register and should be saved before use.
It is OK in your case, however, if you plan to use this function from other functions you may get in trouble :biggrin:
So, I am not sure were you looking for utoa_ex function or atou_ex function or both :dazzled:
I was looking for utoa_ex, i used your version of the atou_ex for atou_ex. Both would be useful for others id imagine.
The only version of utoa_ex I have was one of Lingo's from memory and it has magic numbers in it that I don't understand how it works. I will see what I can find.
Here is one that I converted from HJWasm:
It can be used for signed and unsigned qword and can convert to binary, octal, decimal and hex characters
you can call it something like :
mynum = 738h
invoke myqtoa, mynum,ADDR buff, 2, FALSE, FALSE ;binary
invoke myqtoa, mynum,ADDR buff, 8, FALSE, FALSE ;octal
invoke myqtoa, mynum,ADDR buff, 10, FALSE, FALSE ;decimal
invoke myqtoa, mynum,ADDR buff, 16, FALSE, FALSE ;hexadecimal
.data
hextbl db '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
.code
myqtoa PROC FRAME USES rbx value:qword, buffer:QWORD,radix:DWORD,sign:DWORD,addzero:DWORD
local tmpbuf[34] :BYTE
mov rbx,rdx ;buffer
mov r10,rdx ;buffer
.if (!rcx)
mov rax,rdx
mov byte ptr[rax],'0'
jmp done
.endif
.if (r9b)
mov byte ptr [rdx],'-'
lea r10,[rdx+1]
neg rcx
.endif
lea r9,tmpbuf[33]
mov byte ptr tmpbuf[33],0
lea r11,hextbl
.repeat
xor edx,edx ;clear rdx
mov rax,rcx ;value into rax
dec r9 ;make space for next char
div r8 ;div value with radix (2, 8, 10, 16)
mov rcx,rax ;mod is in rdx, save result back in rcx
movzx eax,byte ptr [rdx+r11] ;put char from hextbl pointed by rdx
mov byte ptr [r9],al ;store char from al to tmpbuf pointed by r9
.until (!rcx) ;repeat if rcx not clear
.if (addzero && al > '9') ;add a leading '0' if first digit is alpha
mov byte ptr[r10],'0'
inc r10
.endif
lea r8,tmpbuf[34] ;end of the buffer in r8
sub r8,r9 ;that will give a count of chars to be copied
invoke memcpy,r10,r9,r8 ;call routine to copy
mov rax,rbx ;return the address of the buffer in rax
done: ret
myqtoa ENDP
Thanks Habran, that works fine. I noticed that the addzero param if set to true displays as the ascii numeric value, and false displays as the hex value which is pretty neat as well. Also, I used RtlMoveMemory in place of the memcpy function, and it works fine with this as well.
No worries fearless :biggrin:
I have modified explanation so that is more clear how it works and how to use it
It was very late last night when I posted it and I've had no time to clarify properly
Because HJWasm accepts '0x' for hexadecimal numbers it can be written:
.if (addzero && al > '9') ;add a leading '0x' if first digit is alpha
mov word ptr[r10],'x0'
add r10,2
.endif
Then you should use : invoke myqtoa, mynum,ADDR buff, 16, FALSE, TRUE ;hexadecimal
Or if you prefer asm style you can ad this here:
lea r9,tmpbuf[33]
mov byte ptr tmpbuf[33],0
lea r11,hextbl
.f (radix == 16)
dec r9
mov byte ptr[r9],'h'
.endif
.repeat
If you prefer lower case letters in hexadecimal numbers you can change:
hextbl db '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
to:
hextbl db '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
I have optimized myqtoa function so that it doesn't need to call memcpy and it makes it much faster :
Quote
;value is in rcx and contains the number to be printed
;buffer is in rdx and contains the destiny address where to store the string
;radix is in r8 and it can contain 2 for bin, 8 for octal, 10 for decimal or 16 for hex
;sign is TRUE for negative signed numbers, otherwise FALSE
;set addzero TRUE if you want hex numbers to be used by assembler
myqtoa PROC FRAME USES rbx rdi rsi value:qword, buffer:QWORD,radix:DWORD,sign:DWORD,addzero:DWORD
local tmpbuf[34] :BYTE
mov rbx,rdx ;keep the original address of the buffer in rbx
mov rdi,rdx ;this is working buffer address
.if (!rcx) ;number could be zero
mov word ptr[rdx],30h ;print '0',0
jmp done ;the job done
.endif
.if (r9d) ;if sign
mov byte ptr [rdx],'-' ;print it out
lea rdi,[rdx+1] ;increase pointer
neg rcx ;make it negative number
.endif
lea rsi,tmpbuf[33] ;load last location of tmpbuf
mov byte ptr tmpbuf[33],0 ;mark the end of string
lea r11,hextbl ;r11 points to hextable
;if you want 'h' at the end, uncomment next 4 lines
;.if (radix == 16) ;if hex number
; dec rsi ;make space for next char
; mov byte ptr[rsi], 'h' ;end it with 'h'
;.endif
.repeat
xor edx,edx ;clear rdx
mov rax,rcx ;value into rax
dec rsi ;make space for next char
div r8 ;div value with radix (2, 8, 10, 16)
mov rcx,rax ;mod is in rdx, save result back in rcx
movzx eax,byte ptr [rdx+r11] ;put char from hextbl pointed by rdx
mov byte ptr [rsi],al ;store char from al to tmpbuf pointed by rsi
.until (!rcx) ;repeat if rcx not clear
.if (addzero && al > '9') ;add a leading '0' if first digit is alpha
mov byte ptr[rdi],'0'
inc rdi
.endif
lea rcx,tmpbuf[34] ;start of the buffer in r8
sub rcx,rsi ;that will give a count of chars to be copied
rep movs byte ptr [rdi],byte ptr [rsi];copy bytes to the buffer
mov rax,rbx ;return the address of the buffer in rax
done: ret
myqtoa ENDP
Quote from: rrr314159 on April 27, 2015, 04:26:04 AM
Instead it puts the first 4 args in rcx, rdx, r8, r9...
I don't know what JWASM, or any of the assemblers, actually do, and not to be picking nits, but your description leaves out essential details. For the first four arguments, the integer and FP argument registers are matched by position, RCX:XMM0L, RDX:XMM1L, R8:XMM2L, R9:XMM3L, with the register that does not match the argument type ignored. And IIRC, for unprototyped or vararg functions, the FP values should be duplicated in the matching integer registers.