I am new here and I don't know the current state of things in the world. I have an algorithm I may want to share, if it is not already been invented elsewhere. I came up with the algorithm in the late 70s.
What is the current state of the art in converting binary to a null terminated string of any base, ie binary to hex, to decimal, or binary?
For example, given a number in rax, the base in rbx, the address of the target string in edi, and a table of digits, ie "0123456789abcdef". Where can I find the best example of assembler code that would convert the value in rax into displayable text.
I am not looking for code that will simply do the function, I already know how to do that. I want to see if I know a better way of doing it. Remember this is converting binary to any base, lets say base 2 to base 60. Base 60 is what the Sumerian's used.
Hi ahset,
It depends on your expectations, well-optimized routines will require extra work. As a beginning, you can start by studying the functions from masm32.lib :
\masm32\help\masmlib.chm :
QuoteConversions
atodw Convert ascii number to DWORD
htodw Convert HEX string to DWORD
dwtoa Convert DWORD to ascii string
.
.
.
Quote from: ahsat on March 31, 2024, 04:37:00 AMWhat is the current state of the art in converting binary to a null terminated string of any base, ie binary to hex, to decimal, or binary?
In addition to what Vortex wrote, there are these
number to string macros, part of the Masm32 SDK:
ubyte$() Unsigned BYTE input
sbyte$() Signed BYTE input
xbyte$() Hex notation output BYTE
uword$() Unsigned WORD input
sword$() Signed WORD input
xword$() Hex notation output WORD
udword$() Unsigned DWORD input
sdword$() Signed DWORD input
xdword$() Hex notation output DWORD
uqword$() Unsigned QWORD input
sqword$() Signed QWORD input
xqword$() Hex notation output QWORD
real4$() 32 bit FLOAT input
real8$() 64 bit FLOAT input
real10$() 80 bit FLOAT input
MasmBasic offers
Str$ (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1186)() input automatically detected (reg32, xmm*, dword, word, byte, ST(0))
Hex$ (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1198)() input automatically detected (...)
Bin$ (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1199)() input reg32, dword variable, immediate
Quote from: Vortex on March 31, 2024, 05:53:00 AMatodw Convert ascii number to DWORD
htodw Convert HEX string to DWORD
dwtoa Convert DWORD to ascii string
I am sorry at being so new here, but where can I locate those?
Hi ahsat,
QuoteI am sorry at being so new here, but where can I locate those?
The location of the source code :
\masm32\m32lib
Remember, those (most of them) are all 32-bit code.
I'm sure your binary-to-ASCII routine could easily be rewritten in X86/X64 assembly language. Care to post it here?
Quote from: Vortex on March 31, 2024, 07:47:37 AMThe location of the source code :
I have \masm64\m64lib and it seems not to be there. I will get the 32 bit version.
Quote from: NoCforMe on March 31, 2024, 08:21:39 AMI'm sure your binary-to-ASCII routine could easily be rewritten in X86/X64 assembly language. Care to post it here?
If my code is a good as I think it is, I will. But I have been out of the coding world for so long, I don't want to look foolish. I want to see the best that is out there first.
Quote from: ahsat on March 31, 2024, 08:30:04 AMI want to see the best that is out there first
Go to forum search (http://masm32.com/board/index.php?action=search;advanced;search=) and try e.g.
fast hexBtw instead of chasing the fastest bintostring algo, you might simply
use them for some more ambitious project of yours.
Quote from: jj2007 on March 31, 2024, 08:43:46 AMsome more ambitious project of yours
I'm too old for anything ambitious.
Quote from: ahsat on March 31, 2024, 09:20:49 AMI'm too old for anything ambitious.
Beating the algos in the Laboratory is
extremely ambitious :biggrin:
Quote from: ahsat on March 31, 2024, 08:30:04 AMQuote from: NoCforMe on March 31, 2024, 08:21:39 AMI'm sure your binary-to-ASCII routine could easily be rewritten in X86/X64 assembly language. Care to post it here?
If my code is a good as I think it is, I will. But I have been out of the coding world for so long, I don't want to look foolish. I want to see the best that is out there first.
I, for one, would be curious to see what you have. No need to worry about looking foolish: we're not judgemental like that here. (If there are obvious flaws in your code someone might point them out.) Always interesting to see someone's take on how to perform some function, especially if it was conceived some number of years ago.
And so far as performance goes, I've been driving the same nail here for some time:
it really doesn't make any difference how fast such routines are, at least in most applications. Let's say you're using such a routine to convert display user output in a Windows console program: the time it takes to actually display the text completely swamps whatever time it takes to do the conversion.
Now if your code is super-adaptable, can produce output in any radix (base), then that would be something ...
Quote from: NoCforMe on March 31, 2024, 10:10:38 AMoutput in any radix (base), then that would be something
The code can produce output in any base, I currently have a test program that supports base 2 to base 60. The only thing that limits the base is the characters available to represent the digits. I am currently using
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
as the digits. Does that meet your definition of a radix (base)?
I also understand you to be saying that you know of nothing like this existing now, is that correct.
Quote from: jj2007 on March 31, 2024, 09:45:05 AMBeating the algos in the Laboratory is extremely ambitious
I think I have been doing that all my life. :biggrin:
Yes, radix == base.
Base 60: how would that even work? Wouldn't you need 60 unique "digits" for representation? Never dealt with any higher base than 16 (hex).
I don't know of any routine that does such universal conversion, but that certainly doesn't mean it doesn't exist.
I'd still like to see your code if possible.
Quote from: NoCforMe on March 31, 2024, 11:18:43 AMWouldn't you need 60 unique "digits" for representation?
I gave you the digits I am using. Again they are:
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
Quote from: NoCforMe on March 31, 2024, 11:18:43 AMWouldn't you need 60 unique "digits" for representation?
I gave you the digits I am using. Again they are:
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
Quote from: NoCforMe on March 31, 2024, 11:18:43 AMWouldn't you need 60 unique "digits" for representation?
As an alternative to the 0...z scheme (10+2*26=62 digits), you could use A0...A9, B0...B9, ... F0...F9: 60 "digits". The Unicode universe is also quite big, but a base-60 number composed of lots of emojis would be difficult to read.
I posted the code, now it seems to have disappeared.
This algorithm is not the fastest power of 2 bases like binary, octal and hex. But from looking around a bit, I think AnyToAny and ctAnyToAny are unique. Few programmers ever think about recursion.
I would like to know if any of you will use the routines. The routines can be converted to macros.
The program was linked using:
polink.exe /SUBSYSTEM:CONSOLE /LARGEADDRESSAWARE:NO /ENTRY:main /OUT:AnyBase.exe AnyBase.obj
TITLE AnyBase
OPTION PROC:PRIVATE ;Don't automatically make procs public
include \masm64\include64\masm64rt.inc
.data
result db 65 dup (?) ;max should be 64, plus a null
newLine db 0dh, 0ah, 0
numb qword ? ;holds the number being converted
.CODE
;Got to keep the Sumerian happy, base 60 is the max here.
digits db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
maxBase equ $-digits ;Max base number, should be 60
AnyToAny proc
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any number to any base.
; Parameters:
; eax The number to convert
; ebx The desired base
; edi Where to place the converted string
;----------------------------------------------------------------------------
;
push rdx ;save rdx, or a digit
xor rdx, rdx ;zero rdx
div rbx ;generate next digit in rdx
test rax, rax ;test if done
jz @f ;br if done
invoke AnyToAny ;generate next digit
@@: mov al, digits[rdx] ;get the ascii value
stosb ;save it
pop rdx ;restore rdx, or get next digit
ret
AnyToAny endp
ctAnyToAny proc
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any number to any base with a specified digit length, will pad leading zeros.
; Parameters:
; eax The number to convert
; ebx The desired base
; ecx The desired digit count
; edi Where to place the converted string
;----------------------------------------------------------------------------
;
push rdx ;save rdx, or a digit
xor rdx, rdx ;zero rdx
div rbx ;generate next digit in rdx
dec ecx ;decrement digit count
jecxz @f ;br if done
invoke ctAnyToAny ;generate next digit
@@: mov al, digits[rdx] ;get the ascii value
stosb ;save it
pop rdx ;restore rdx, or get next digit
ret
ctAnyToAny endp
main proc Public
;----------------------------------------------------------------------------
; Program to demo any number to any base, bases 2 to 60.
;----------------------------------------------------------------------------
;
mov numb, 123456789 ;convert this number, over and over
jmp @f
m00 db 13, 10, 'This is the number we will be converting', 13, 10, 0
@@: mov rax, numb ;the number to rax
mov ebx, 10 ;the base to rbx
mov edi, offset result ;the destination addr to edi
invoke AnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m00
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m01 db 13, 10, 'This is the low byte of the number in hex', 13, 10, 0
@@: mov ecx,2
mov rax, numb ;the number to rax
mov ebx, 16 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m01
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m02 db 13, 10, 'This is the 16 bit number, in hex', 13, 10, 0
@@: mov ecx,4
mov rax, numb ;the number to rax
mov ebx, 16 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m02
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m03 db 13, 10, 'This is the 32 bit number, in hex', 13, 10, 0
@@: mov ecx,8
mov rax, numb ;the number to rax
mov ebx, 16 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m03
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m04 db 13, 10, 'This is the 64 bit number, in hex', 13, 10, 0
@@: mov ecx,16
mov rax, numb ;the number to rax
mov ebx, 16 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m04
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m05 db 13, 10, 'This is the 64 bit number, in binary', 13, 10, 0
@@: mov ecx,64
mov rax, numb ;the number to rax
mov ebx, 2 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m05
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m06 db 13, 10, 'And in Sumerian Sexagesimal, base 60', 13, 10, 0
@@: mov ecx,10
mov rax, numb ;the number to rax
mov ebx, 60 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m06
invoke StdOut, addr result
invoke StdOut, addr newLine
invoke ExitProcess, 0 ;Error code 0
ret
main ENDP
end
It works :thumbsup:
This is the number we will be converting
123456789
This is the low byte of the number in hex
15
This is the 16 bit number, in hex
CD15
This is the 32 bit number, in hex
075BCD15
This is the 64 bit number, in hex
00000000075BCD15
This is the 64 bit number, in binary
0000000000000000000000000000000000000111010110111100110100010101
And in Sumerian Sexagesimal, base 60
000009VXX9
Quote from: ahsat on March 31, 2024, 11:33:25 AMQuote from: NoCforMe on March 31, 2024, 11:18:43 AMWouldn't you need 60 unique "digits" for representation?
I gave you the digits I am using. Again they are:
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
Yes, sorry, I miscounted (I missed that you put
kyl in there). Pretty kewl ... like the recursion, too.
Quote from: jj2007 on March 31, 2024, 11:54:45 AMIt works :thumbsup:
Not bad for an 81 year old, right?
Quote from: NoCforMe on March 31, 2024, 12:13:07 PMlike the recursion, too.
My first program was a plug board. Recursion was common then.
Quote from: ahsat on March 31, 2024, 12:23:44 PMQuote from: NoCforMe on March 31, 2024, 12:13:07 PMlike the recursion, too.
My first program was a plug board.
You mean like this (http://www.righto.com/2017/04/1950s-tax-preparation-plugboard.html)? Wow.
Quote from: NoCforMe on March 31, 2024, 12:36:13 PMYou mean like this (http://www.righto.com/2017/04/1950s-tax-preparation-plugboard.html)?
No, but you had to use recursion.
Quote from: ahsat on March 31, 2024, 11:44:31 AMThis algorithm is not the fastest power of 2 bases like binary, octal and hex. But from looking around a bit, I think AnyToAny and ctAnyToAny are unique. Few programmers ever think about recursion.
I like the idea. I made some quick tests: 10 Million numbertohex conversions run in about 900 milliseconds on my AMD Athlon Gold 3150U. For comparison, Hex$ does it in less than a quarter of the time, but since it's 32-bit code, it's limited to DWORDs.
Compliments, that's really good, even for somebody who is only 14 years older than I am ;-)
Quote from: ahsat on March 31, 2024, 12:48:39 PMQuote from: NoCforMe on March 31, 2024, 12:36:13 PMYou mean like this (http://www.righto.com/2017/04/1950s-tax-preparation-plugboard.html)?
No, but you had to use recursion.
So what is "plug board" programming then?
Quote from: NoCforMe on March 31, 2024, 01:00:44 PMSo what is "plug board" programming then?
Some of the original computers used wiring boards. Instructions were plugging wires from one hole in the board to another hole. You then added wires to tell that instruction what registers to use. The one I programmed had only registers, no memory. I can't remember all the details, but the registers were very large, probably 128 bits or more. I also can't remember how many registers it had, probably 16 or 32.
Quote from: jj2007 on March 31, 2024, 12:58:43 PM10 Million numbertohex conversions run in about 900 milliseconds
I think the algorithms will hold their own, speed wise, on bases like base 10. You are obviously faster than I am. You are probably active, and I have been inactive since around 2000.
I would be very interested in a speed test of base 10. Also, it should be simple and quick to convert to 32 bit. Since coming alive again, I have only been fooling around with 64 bit. It would probably take me a day or so to setup a 32 bit version.
Quote from: ahsat on March 31, 2024, 01:38:32 PMI would be very interested in a speed test of base 10. Also, it should be simple and quick to convert to 32 bit.
Here are timings for your algo, base 16 and base 10:
This program was assembled with UAsm64 in 64-bit format.
AMD Athlon Gold 3150U with Radeon Graphics
500 milliseconds
485 milliseconds
484 milliseconds
484 milliseconds
485 milliseconds
The number is 075BCD15
500 milliseconds
516 milliseconds
516 milliseconds
484 milliseconds
500 milliseconds
The number is 123456789
With base 10, it's 30% faster than my Str$() algo. I attach both sources and executables.
Note I have modified your algo a bit, not in its content but instead of xx
proc I used xx:, i.e. a label, to make sure the assembler doesn't create any overhead.
AnyToAnyRay.zip > AnyToAny.exe > oops
(1f00.30e0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=000000b4 ebx=00000010 ecx=89ab5db4 edx=000000b5 esi=000000b4 edi=000000b4
eip=7758343a esp=0019ff3c ebp=0019ff64 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
KERNELBASE!lstrlenA+0x1a:
7758343a 8a08 mov cl,byte ptr [eax] ds:002b:000000b4=??
This program was assembled with UAsm64 in 32-bit format.
This is the 64 bit number, in hex:
bye
Quote from: sinsi on March 31, 2024, 09:26:54 PMAnyToAnyRay.zip > AnyToAny.exe > oops
Oops indeed - wrong exe attached, sorry. ^^^ Corrected ^^^
Nice
This program was assembled with UAsm64 in 64-bit format.
13th Gen Intel(R) Core(TM) i9-13900KF
156 milliseconds
140 milliseconds
141 milliseconds
141 milliseconds
140 milliseconds
The number is 075BCD15
157 milliseconds
156 milliseconds
172 milliseconds
156 milliseconds
156 milliseconds
The number is 123456789
Quote from: jj2007 on March 31, 2024, 09:01:26 PMWith base 10, it's 30% faster than my Str$() algo.
I don't understand the code or the results you posted, but I think you are saying that base 10, it did beat the algos in the Laboratory.
How did you attach files to your post?
Quote from: ahsat on April 01, 2024, 12:30:35 AMI don't understand the code or the results you posted, but I think you are saying that base 10, it did beat the algos in the Laboratory.
The code is basically some wrappers for measuring time with a GetTickCount pair, plus your original two algos. The only modification was to use
AnyToAny: instead of
AnyToAny proc, i.e. a simple label instead of the
proc macro. The latter may trigger certain things that could slow down your code, such as creating a stack frame.
Quote from: ahsat on April 01, 2024, 12:30:35 AMHow did you attach files to your post?
There are two modes to respond:
1. Quick edit
2. Reply
Both show
Add image to post at the bottom.
Only the Reply mode shows
Click or drag files here to attach them.Zip your code and attach the archive, using the Reply mode.
If you accidentally already posted in Quick edit mode, use
More... Modify in the lower right corner.
Quote from: jj2007 on April 01, 2024, 01:15:21 AMIf you accidentally already posted in Quick edit mode, use
I think the last part of the text in your post was cut off.
Quote from: ahsat on April 01, 2024, 01:18:49 AMI think the last part of the text in your post was cut off.
Just hit F5 or Ctrl F5.
I posted a testbed (https://masm32.com/board/index.php?topic=11820.0) in the Laboratory. It's 32-bit code, so if you can convert your algo to 32-bit, I can add it to the testbed.
Quote from: jj2007 on April 01, 2024, 03:46:04 AMif you can convert your algo to 32-bit, I can add it to the testbed.
Do you mean to convert just the to ten instruction routines (AnyToAny and ctAnyToAny) or the whole program Anybase?
Remember, I am very new in the modern development environments. The MASM32 STK does not like something on my computer and will not install. I will have to research why, so converting the whole program may take me awhile.
Quote from: ahsat on April 01, 2024, 04:57:06 AMDo you mean to convert just the to ten instruction routines
Have a look (https://masm32.com/board/index.php?msg=128381) :cool:
Quote from: jj2007 on April 01, 2024, 06:56:37 AMHave a look
I did, and made a comment.
Perhaps you would be so kind as to help me with the MASM32 SDK. I am getting an error that has me stumped. I have reduced the code to this:
.386
.model flat
.data
db 0dh, 0ah, 0
end
I then get the following:
d:\masm32\AnyBase32>D:\masm32\bin\ml.exe /c AnyBase.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: AnyBase.asm
d:\masm32\AnyBase32>D:\masm32\bin\polink.exe /SUBSYSTEM:CONSOLE /OUT:AnyBase32.exe AnyBase.obj
POLINK: fatal error: Invalid machine type in object 'AnyBase.obj'.
Note that I am using egerything from the SDK, but still getting the POLINK error.
ml /c /coff
You might want to create a batch file to do your assembling and linking, rather than typing it every time at the command line as you apparently did. Here's mine, lifted from [somewhere in] the MASM32 SDK and modified:
@echo off
ml /c /coff /Fl /Sg /Sn %1.asm
:ml /c /coff /Fl /Sn /Zi %1.asm
if errorlevel 1 goto errasm
link /SUBSYSTEM:WINDOWS %1
:link /SUBSYSTEM:WINDOWS /debug %1
if errorlevel 1 goto errlink
goto TheEnd
:errlink
echo _
echo Link error
goto TheEnd
:errasm
echo _
echo Assembly Error
goto TheEnd
:TheEnd
It's got all those command-line switches that are needed, plus there is a "debug" flavor (commented out here) for when you need to look at your code using OllyDbug or some other debugger.
This version creates a regular Windows GUI application. To create a console app, replace SUBSYSTEM:WINDOWS with SUBSYSTEM:CONSOLE.
Invoke it without the ".asm" in the filename (or you can modify it to accept the full root filename).
I have the program entry named entry_point.
Now the linker or assembler is adding a leading underscore to my public procs. Can anyone help with this one?
d:\masm32\AnyBase32>D:\masm32\bin\ml.exe /c /coff /Fl /Sg /Sn AnyBase.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: AnyBase.asm
***********
ASCII build
***********
d:\masm32\AnyBase32>D:\masm32\bin\polink.exe /SUBSYSTEM:CONSOLE /ENTRY:entry_point /OUT:AnyBase32.exe AnyBase.obj
POLINK: error: Unresolved external symbol '_entry_point'.
You might need this at the end of your source code for 32-bit
end entry_point
Quote from: sinsi on April 01, 2024, 09:37:54 AMYou might need this at the end of your source code for 32-bit
end entry_point
Yes! You definitely do need that. (But I know nothing about polink; I just use good old
ml.exe.)
Quote from: sinsi on April 01, 2024, 09:37:54 AMend entry_point
The entry_point on the end statement did it. Forever, that has been the way the entry point was defined, but they changed that for the 64bit assembled.
Thank you guys so much for this help. The 32 bit version now links and seems to work. I will see if it needs any clean up.
Below is the 32 bit version of Anybase32.exe. It was linked using:
polink.exe /SUBSYSTEM:CONSOLE /OUT:AnyBase32.exe AnyBase.obj
In my day, an algorithms worth was based on how powerful it was, not just its speed. All the routines you guys are comparing it to, will only convert a single base. The algorithm I released will do any base, and is faster for most bases.
So, which is better, code a routine for each base that you will ever use, or have one routine that will do any base you need? With this algorithm you only need one choice, leading zeors or not.
TITLE AnyBase
OPTION PROC:PRIVATE ;Don't automatically make procs public
include \masm32\include\masm32rt.inc
.data
result db 33 dup (?) ;max should be 32, plus a null
newLine db 0dh, 0ah, 0
numb dword ? ;holds the number being converted
.CODE
;Got to keep the Sumerian happy, base 60 is the max here.
digits db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
maxBase equ $-digits ;Max base number, should be 60
AnyToAny proc
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any number to any base.
; Parameters:
; eax The number to convert
; ebx The desired base
; edi Where to place the converted string
;----------------------------------------------------------------------------
;
push edx ;save edx, or a digit
xor edx, edx ;zero edx
div ebx ;generate next digit in edx
test eax, eax ;test if done
jz @f ;br if done
invoke AnyToAny ;generate next digit
@@: mov al, digits[edx] ;get the ascii value
stosb ;save it
pop edx ;restore edx, or get next digit
ret
AnyToAny endp
ctAnyToAny proc
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any number to any base with a specified digit length, will pad leading zeros.
; Parameters:
; eax The number to convert
; ebx The desired base
; ecx The desired digit count
; edi Where to place the converted string
;----------------------------------------------------------------------------
;
push edx ;save edx, or a digit
xor edx, edx ;zero edx
div ebx ;generate next digit in edx
dec ecx ;decrement digit count
jcxz @f ;br if done
invoke ctAnyToAny ;generate next digit
@@: mov al, digits[edx] ;get the ascii value
stosb ;save it
pop edx ;restore edx, or get next digit
ret
ctAnyToAny endp
entry_point proc public
;----------------------------------------------------------------------------
; Program to demo any number to any base, bases 2 to 60.
;----------------------------------------------------------------------------
;
mov numb, 123456789 ;convert this number, over and over
jmp @f
m00 db 13, 10, 'This is the number we will be converting', 13, 10, 0
@@: mov eax, numb ;the number to eax
mov ebx, 10 ;the base to ebx
mov edi, offset result ;the destination addr to edi
invoke AnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m00
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m01 db 13, 10, 'This is the low byte of the number in hex', 13, 10, 0
@@: mov ecx,2
mov eax, numb ;the number to eax
mov ebx, 16 ;the base to ebx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m01
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m02 db 13, 10, 'This is the 16 bit number, in hex', 13, 10, 0
@@: mov ecx,4
mov eax, numb ;the number to eax
mov ebx, 16 ;the base to ebx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m02
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m03 db 13, 10, 'This is the 32 bit number, in hex', 13, 10, 0
@@: mov ecx,8
mov eax, numb ;the number to eax
mov ebx, 16 ;the base to ebx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m03
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m05 db 13, 10, 'This is the 32 bit number, in binary', 13, 10, 0
@@: mov ecx,32
mov eax, numb ;the number to eax
mov ebx, 2 ;the base to ebx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m05
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m06 db 13, 10, 'And in Sumerian Sexagesimal, base 60', 13, 10, 0
@@: mov ecx,10
mov eax, numb ;the number to eax
mov ebx, 60 ;the base to ebx
mov edi, offset result ;the destination to edi
invoke ctAnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m06
invoke StdOut, addr result
invoke StdOut, addr newLine
invoke ExitProcess, 0 ;Error code 0
ret
entry_point ENDP
end entry_point
Welll, so far in my assembly-coding career I've only ever needed 2 bases: decimal and hex. Well, maybe binary once or twice. So the "any base" thing is a meh in my book; just one more annoying parameter to forget to place properly.
And really, when in the world would you ever use base 60? or really anything except 2, 10 or 16? (If you're a really old Unix coot I guess you could add octal.)
Still, kewl algo.
Quote from: ahsat on April 02, 2024, 05:46:03 AMBelow is the 32 bit version of Anybase32.exe
Thanks, Ray. I've added it to the testbed as "Ray II", see attachment. Timings:
AMD Athlon Gold 3150U with Radeon Graphics (SSE4)
...
Averages:
5348 cycles for dwtoa
3646 cycles for dw2str
22575 cycles for MasmBasic Str$()
16554 cycles for Ray's algo I
15138 cycles for Ray's algo II
20 bytes for dwtoa
74 bytes for dw2str
16 bytes for MasmBasic Str$()
110 bytes for Ray's algo I
129 bytes for Ray's algo II
dwtoa 12345678
dw2a 12345678
dw2str 12345678
MasmBasic Str$() 12345678
Ray's algo I 012345678
Ray's algo II 123456788
It is certainly faster than my own Str$(), but dw2str is a tick faster. You may also need to care for a zero delimiter: 123456788
Quote from: jj2007 on April 02, 2024, 06:23:33 AMIt is certainly faster than my own Str$(), but dw2str is a tick faster
Yes, but my point is that you need two routines to do that.
Even in your timing test you had to use three different programs to compare to one. The one can do what takes three others to do, just for you to test it.
If one assembler can do the combined things that three other assemblers can do, which one are you going to use on a normal basis? And I suspect you would not care if it was a little slower.
Ray,
Your solution is certainly very elegant, and I think we all agree that you got an extremely good start as an "old newbie" in this forum. It's nice to have you with us :thup:
Jochen
I got the mult version working, at least partially:
Averages:
4372 cycles for dwtoa
2816 cycles for dw2str
16560 cycles for MasmBasic Str$()
12337 cycles for Ray's algo I
4289 cycles for Ray+mul
12338 cycles for Ray's algo II
20 bytes for dwtoa
82 bytes for dw2str
16 bytes for MasmBasic Str$()
110 bytes for Ray's algo I
64 bytes for Ray+mul
129 bytes for Ray's algo II
dwtoa 123456789
dw2a 123456789
dw2str 123456789
MasmBasic Str$() 123456789
Ray's algo I 123456789
Ray+mul 123456789
Ray's algo II 123456789
.DATA?
result2 db 40 dup(?)
.CODE
_xmul dd 0CCCCCCCDh
ctAnyToAnyMul:
push ecx ; save edx, or a digit
mov ecx, eax
mul _xmul ; something is wrong here
shr edx, 3
mov eax, edx
add edx, edx
lea edx, [edx*4+edx]
sub ecx, edx
neg edx
test eax, eax
.if !Zero? ; br if done
call ctAnyToAnyMul ; generate next digit
.endif
mov al, @digits[edx] ; get the ascii value
stosb ; save it
pop edx ; restore edx, or get next digit
ret
NameF equ Ray+mul
TestF proc
mov esi, AlgoLoops-1 ; loop e.g. 100x
align 4
.Repeat
mov eax, TheNumber
mov edi, offset result2
call ctAnyToAnyMul
dec esi
.Until Sign?
mov eax, offset result
ret
TestF endp
A forum member, jimg, managed to eliminate the recursion and I have incorporated his changes.
I will do the 32 bit version tomorrow, a new 64 bit version of AnyToAny is below. It can be linked with:
polink.exe /SUBSYSTEM:CONSOLE /LARGEADDRESSAWARE:NO /ENTRY:main /OUT:AnyBase.exe AnyBase.obj
TITLE AnyBase
OPTION PROC:PRIVATE ;Don't automatically make procs public
include \masm64\include64\masm64rt.inc
.data
result db 65 dup (?) ;max should be 64, plus a null
newLine db 0dh, 0ah, 0
numb qword ? ;holds the number being converted
.CODE
;Got to keep the Sumerian happy, base 60 is the max here.
digits db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkylmnopqrstuvw'
maxBase equ $-digits ;Max base number, should be 60
AnyToAny proc
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any number to any base.
; Parameters:
; eax The number to convert
; ebx The desired base
; edi Where to place the converted string
;----------------------------------------------------------------------------
;
xor rcx, rcx ;zero rcx
agn:
push rdx ;save rdx, or a digit
inc rcx ;bump counts in loop
xor rdx, rdx ;zero rdx
div rbx ;generate next digit in rdx
test rax, rax ;test if done
jnz agn ;br if done
@@: mov al, digits[rdx] ;get the ascii value
stosb ;save it
pop rdx ;restore rdx, or get next digit
loop @b ;loop till all digits processed
ret
AnyToAny endp
main proc Public
;----------------------------------------------------------------------------
; Program to demo any number to any base, bases 2 to 60.
;----------------------------------------------------------------------------
;
xor rsi, rsi
mov rax, 123456789 ;convert this number, over and over
mov numb, rax ;save it in numb
jmp @f
m00 db 13, 10, 'This is the number we will be converting', 13, 10, 0
@@: mov rax, numb ;the number to rax
mov ebx, 10 ;the base to rbx
mov edi, offset result ;the destination addr to edi
invoke AnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m00
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m04 db 13, 10, 'This is the 64 bit number, in hex', 13, 10, 0
@@: mov rax, numb ;the number to rax
mov ebx, 16 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke AnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m04
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m05 db 13, 10, 'This is the 64 bit number, in binary', 13, 10, 0
@@: mov rax, numb ;the number to rax
mov ebx, 2 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke AnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m05
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m06 db 13, 10, 'And in Sumerian Sexagesimal, base 60', 13, 10, 0
@@: mov rax, numb ;the number to rax
mov ebx, 60 ;the base to rbx
mov edi, offset result ;the destination to edi
invoke AnyToAny
mov byte ptr [edi], 0 ;null terminate the result string
invoke StdOut, addr m06
invoke StdOut, addr result
invoke StdOut, addr newLine
done:
invoke ExitProcess, 0 ;Error code 0
ret
main ENDP
end
mov edi, offset result
Unless you want a write access violation, that should be RDI
mov al, digits[rdx]
You might get a link error about fixups, try this
lea r9,digits
...
mov al,[r9][rdx]
If you use lea instead of offset then ML uses a RIP-relative instruction which has two benefits - code is smaller by 4(?) bytes and you will have proper position-independent code
One last niggle
xor rsi,rsi
;the same but 1 byte smaller
xor esi,esi
An instruction (all? not sure) that changes the low 32-bit part of a 64-bit register will zero the top 32-bits.
Quote from: sinsi on April 04, 2024, 05:14:09 PMOne last niggle
xor rsi,rsi
;the same but 1 byte smaller
xor esi,esi
An instruction (all? not sure) that changes the low 32-bit part of a 64-bit register will zero the top 32-bits.
Interesting. Is that a totally reliable (IOW, documented and all) side effect?
Quote from: NoCforMe on April 04, 2024, 06:28:40 PMQuote from: sinsi on April 04, 2024, 05:14:09 PMOne last niggle
xor rsi,rsi
;the same but 1 byte smaller
xor esi,esi
An instruction (all? not sure) that changes the low 32-bit part of a 64-bit register will zero the top 32-bits.
Interesting. Is that a totally reliable (IOW, documented and all) side effect?
It's in the Intel docs
Quote from: sinsi on April 04, 2024, 05:14:09 PMmov edi, offset result
One last niggle
xor rsi,rsi
;the same but 1 byte smaller
xor esi,esi
An instruction (all? not sure) that changes the low 32-bit part of a 64-bit register will zero the top 32-bits.
That is really good information. However, I am not going to use it until I become more comfortable with 64 bit code.
I am going to post yet another 64 bit version, then I will get to work on a 32 bit version.
Quote from: ahsat on March 31, 2024, 11:44:31 AMThe program was linked using:
polink.exe /SUBSYSTEM:CONSOLE /LARGEADDRESSAWARE:NO
With that option, you can use mov edi, offset somestring. /LARGEADDRESSAWARE:NO means "stick to the 32-bit world, even in 64-bit code". A dangerous road, because you lose the only real advantage that the 64-bit world offers: a large address space.
Quote from: jj2007 on April 05, 2024, 05:55:38 AMWith that option, you can use mov edi, offset somestring. /LARGEADDRESSAWARE:NO means "stick to the 32-bit world, even in 64-bit code".
I don't know. But I came here to learn 64 bit, so I am stuck with whatever exists.
Thanks to jimg, I have a new routine, that is universally callable. It now checks the paramaters, saves and restores registers. The program now uses tabs instead of spaces.
I used the following link command to create the program:
polink.exe /SUBSYSTEM:CONSOLE /LARGEADDRESSAWARE:NO /ENTRY:main /OUT:AnyBase.exe AnyBase.obj
TITLE AnyBase
OPTION PROC:PRIVATE ;Don't automatically make procs public
include \masm64\include64\masm64rt.inc
cr equ 13
lf equ 10
.data
result db 65 dup (?) ;max should be 64, plus a null
newLine db cr, lf, 0 ;cr and lf
numb qword ? ;holds the number being converted
.CODE
;Got to keep the Sumerian happy, base 60 is the max here.
digits db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx'
maxBase equ $-digits ;Max base number, should be 60
bin2any proc theNumber:qword,
theBase:qword,
theDestination:qword
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any binary number to any base.
;----------------------------------------------------------------------------
;
push rbx ;save regs, still can't get uses to work
push rcx
push rdi
mov rdi, theDestination ;destination addr to rdi
mov rbx, theBase ;the desired base to rbx
mov rax, 1 ;rax=1
cmp rbx, rax ;test if valid base
jbe short bin30 ;br if invalid base
cmp rbx, maxBase ;test if base too large
ja short bin30 ;br if base too large
mov rax, theNumber ;get the number to rax
xor ecx, ecx ;zero rcx
@@: push rdx ;save rdx, or a digit
inc rcx ;bump count of loops
xor edx, edx ;zero rdx
div rbx ;generate next digit in rdx
test rax, rax ;test if done
jnz @b ;loop if not done
@@: mov al, digits[rdx] ;get the ascii value
stosb ;save it
pop rdx ;restore rdx, or get next digit
loop @b ;loop till all digits processed
xor eax, eax ;no error
bin30: mov byte ptr [rdi], 0 ;null terminate the string
pop rdi ;restore regs, need for uses to work
pop rcx
pop rbx
ret
bin2any endp
main proc Public
;----------------------------------------------------------------------------
; Program to demo any number to any base, bases 2 to 60.
;----------------------------------------------------------------------------
;
mov rax, 123456789 ;the number to display
mov numb, rax ;save it in numb
jmp @f
m00 db cr, lf, 'This is the number we will be converting', cr, lf, 0
@@: invoke bin2any, numb, 10, addr result
invoke StdOut, addr m00
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m04 db cr, lf, 'This is the 64 bit number, in hex', cr, lf, 0
@@: invoke bin2any, numb, 16, addr result
invoke StdOut, addr m04
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m05 db cr, lf, 'This is the 64 bit number, in binary', cr, lf, 0
@@: invoke bin2any, numb, 2, addr result
invoke StdOut, addr m05
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m06 db cr, lf, 'In Sumerian Sexagesimal, base 60', cr, lf, 0
@@: invoke bin2any, numb, 60, addr result
invoke StdOut, addr m06
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m07 db cr, lf, 'In Aztec, Myan and Kaktovik, base 20', cr, lf, 0
@@: invoke bin2any, numb, 20, addr result
invoke StdOut, addr m07
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m08 db cr, lf, 'In Kaktovik, base 5', cr, lf, 0
@@: invoke bin2any, numb, 5, addr result
invoke StdOut, addr m08
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m09 db cr, lf, 'In Ternary, base 3', cr, lf, 0
@@: invoke bin2any, numb, 3, addr result
invoke StdOut, addr m09
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m10 db cr, lf, 'In Quaternary, base 4', cr, lf, 0
@@: invoke bin2any, numb, 4, addr result
invoke StdOut, addr m10
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m11 db cr, lf, 'In Undecimal, base 11', cr, lf, 0
@@: invoke bin2any, numb, 11, addr result
invoke StdOut, addr m11
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
@@: invoke ExitProcess, 0 ;Error code 0
ret
main endp
end
Nice.
Still curious why you chose to use "abcdefghijkylmnop" as your Sumerian digits. Wouldn't that be confusing to any Sumerians trying to interpret your results?
Quote from: NoCforMe on April 05, 2024, 08:10:02 AMStill curious why you chose to use "abcdefghijkylmnop"
Its a typo, I will correct it. It has been corrected in the post. Thank you, sometimes its hard to get my attention.
The 32 bit version of Anybase, and uses works there.
I used the following link command:
polink.exe /SUBSYSTEM:CONSOLE /OUT:AnyBase32.exe AnyBase.obj
TITLE AnyBase
OPTION PROC:PRIVATE ;Don't automatically make procs public
include \masm32\include\masm32rt.inc
cr equ 13
lf equ 10
.data
result db 33 dup (?) ;max should be 32, plus a null
newLine db cr, lf, 0 ;cr and lf
numb dword ? ;holds the number being converted
.CODE
;Got to keep the Sumerian happy, base 60 is the max here.
digits db '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx'
maxBase equ $-digits ;Max base number, should be 60
bin2any proc public uses ebx ecx edi,
theNumber:dword,
theBase:dword,
theDestination:dword
;----------------------------------------------------------------------------
; Original by Ray Gwinn
; Any binary number to any base.
;----------------------------------------------------------------------------
;
mov edi, theDestination ;destination addr to edi
mov ebx, theBase ;the desired base to ebx
mov eax, 1 ;eax=1
cmp ebx, eax ;test if valid base
jbe short bin30 ;br if invalid base
cmp ebx, maxBase ;test if base too large
ja short bin30 ;br if base too large
mov eax, theNumber ;get the number to eax
xor ecx, ecx ;zero ecx
@@: push edx ;save edx, or a digit
inc ecx ;bump count of loops
xor edx, edx ;zero edx
div ebx ;generate next digit in edx
test eax, eax ;test if done
jnz @b ;loop if not done
@@: mov al, digits[edx] ;get the ascii value
stosb ;save it
pop edx ;restore edx, or get next digit
loop @b ;loop till all digits processed
xor eax, eax ;no error
bin30: mov byte ptr [edi], 0 ;null terminate the string
ret
bin2any endp
entry_point proc public
;----------------------------------------------------------------------------
; Program to demo any number to any base, bases 2 to 60.
;----------------------------------------------------------------------------
;
mov eax, 123456789 ;the number to display
mov numb, eax ;save it in numb
jmp @f
m00 db cr, lf, 'This is the number we will be converting', cr, lf, 0
@@: invoke bin2any, numb, 10, addr result
invoke StdOut, addr m00
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m04 db cr, lf, 'This is the 32 bit number, in hex', cr, lf, 0
@@: invoke bin2any, numb, 16, addr result
invoke StdOut, addr m04
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m06 db cr, lf, 'In Sumerian Sexagesimal, base 60', cr, lf, 0
@@: invoke bin2any, numb, 60, addr result
invoke StdOut, addr m06
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m07 db cr, lf, 'In Aztec, Myan and Kaktovik, base 20', cr, lf, 0
@@: invoke bin2any, numb, 20, addr result
invoke StdOut, addr m07
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m08 db cr, lf, 'In Kaktovik, base 5', cr, lf, 0
@@: invoke bin2any, numb, 5, addr result
invoke StdOut, addr m08
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m09 db cr, lf, 'In Ternary, base 3', cr, lf, 0
@@: invoke bin2any, numb, 3, addr result
invoke StdOut, addr m09
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m10 db cr, lf, 'In Quaternary, base 4', cr, lf, 0
@@: invoke bin2any, numb, 4, addr result
invoke StdOut, addr m10
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
m11 db cr, lf, 'In Undecimal, base 11', cr, lf, 0
@@: invoke bin2any, numb, 11, addr result
invoke StdOut, addr m11
invoke StdOut, addr result
invoke StdOut, addr newLine
jmp @f
@@: invoke ExitProcess, 0 ;Error code 0
ret
entry_point endp
end entry_point
USES still works with ML64 but only with the default prologue/epilogue. I guess that MASM64's prologue doesn't bother.
Quote from: sinsi on April 05, 2024, 09:16:02 AMuses still works with ML64 but only with the default prologue/epilogue.
Can you show me in an example with code? I am sorry to be so behind and slow.
Quote from: ahsat on April 05, 2024, 10:00:10 AMI am sorry to be so behind and slow
You are remarkably fast for your age, Ray. Truth is that 64-bit code is a can of worms. The PROLOGUE macro determines what happens when you write e.g.
MyAlgo proc param1, the EPILOGUE macro specifies what the "instruction"
ret does (it is a macro, yeah).
In 32-bit land,
uses esi simply pushes esi on entry into the proc and pops it before the
ret.
I bet that 90% of our members here (sinsi excluded) don't know what
uses rsi does in 64-bit code. I had to set up a 64-bit test with ml64 and launch the debugger to find out. Here is the code:
include \masm64\include64\masm64rt.inc
.code
MyAlgo proc uses rsi arg1, arg2
ret
MyAlgo endp
entry_point proc
Local pContent:QWORD, ticks:QWORD ; OPT_Assembler ml64
lea rax, entry_point
conout "The entry point is at ", hex$(rax), lf
int 3
invoke MyAlgo, 123h, 456h
invoke ExitProcess, 0 ; terminate process
entry_point endp
end
The
int 3 makes the debugger stop there. When hitting F7, you see the following on entry to MyAlgo:
0000000140001069 | 48:C7C1 23010000 | mov rcx,123 |
0000000140001070 | 48:C7C2 56040000 | mov rdx,456 |
0000000140001077 | E8 84FFFFFF | call <sub_140001000> |
...
0000000140001000 <su | C8 8000 00 | enter 80,0 |
0000000140001004 | 48:83EC 60 | sub rsp,60 |
0000000140001008 | 48:894D 10 | mov [rbp+10],rcx |
000000014000100C | 48:8955 18 | mov [rbp+18],rdx |
0000000140001010 | C9 | leave |
0000000140001011 | C3 | ret |
Do you see any attempt to save rsi? I don't.
Quote from: jj2007 on April 05, 2024, 10:21:25 AMI would have to set up a 64-bit test with ml64 and launch the debugger to find out.
I have, and it does nothing, zip. No pushes or pops of anything with "uses esi".
I understand what you guys are saying about 32 bit is still better to write programs with. But I want to know what Intel is selling in their 64 bit products, as well as possibility developing a few programs. So, I want to use 64 bit, if for nothing else, to learn what it does.
Previously... (https://masm32.com/board/index.php?msg=128262)
I'm pretty sure that include \masm64\include64\masm64rt.inc also includes a custom prologue.
Quote from: jj2007 on April 05, 2024, 10:21:25 AMinclude \masm64\include64\masm64rt.inc
.code
MyAlgo proc uses rsi arg1, arg2
ret
MyAlgo endp
entry_point proc
Local pContent:QWORD, ticks:QWORD ; OPT_Assembler ml64
lea rax, entry_point
conout "The entry point is at ", hex$(rax), lf
int 3
invoke MyAlgo, 123h, 456h
invoke ExitProcess, 0 ; terminate process
entry_point endp
end
0000000140001069 | 48:C7C1 23010000 | mov rcx,123 |
0000000140001070 | 48:C7C2 56040000 | mov rdx,456 |
0000000140001077 | E8 84FFFFFF | call <sub_140001000> |
...
0000000140001000 <su | C8 8000 00 | enter 80,0 |
0000000140001004 | 48:83EC 60 | sub rsp,60 |
0000000140001008 | 48:894D 10 | mov [rbp+10],rcx |
000000014000100C | 48:8955 18 | mov [rbp+18],rdx |
0000000140001010 | C9 | leave |
0000000140001011 | C3 | ret |
Do you see any attempt to save rsi? I don't.
It's AI,man; the assembler is so smart it knows you haven't touched RSI so it doesn't bother saving and restoring it. It's a mind reader.
masm64 uses "USING" (not uses) reg... and the SaveRegs/RestoreRegs macros for saving and restoring registers.
64 bit Stackframe register preservation macros (https://masm32.com/board/index.php?topic=7285.0)
Quote from: sinsi on April 05, 2024, 10:45:21 AMI'm pretty sure that include \masm64\include64\masm64rt.inc also includes a custom prologue.
Okay, that's great. Can you show me how to use it with code? It does not seem to work for me in Anybase.
Quote from: NoCforMe on April 05, 2024, 11:08:33 AMIt's AI,man; the assembler is so smart it knows you haven't touched RSI so it doesn't bother saving and restoring it. It's a mind reader.
I just made sure for myself, and that is not true.
Quote from: sudoku on April 05, 2024, 11:21:35 AMmasm64 uses "USING" (not uses)
Using seems to be an in line macro.
Quote from: ahsat on April 05, 2024, 11:50:31 AMQuote from: NoCforMe on April 05, 2024, 11:08:33 AMIt's AI,man; the assembler is so smart it knows you haven't touched RSI so it doesn't bother saving and restoring it. It's a mind reader.
I just made sure for myself, and that is not true.
Are you sure?
Hint: I don't use smileys or emojis. Context, man, context.
Quote from: NoCforMe on April 05, 2024, 11:08:33 AMthe assembler is so smart it knows you haven't touched RSI so it doesn't bother saving and restoring it. It's a mind reader
I know that was irony, but being a curious guy, I tried it:
option prologue:prologuedef
option epilogue:epiloguedef
MyAlgo proc uses rsi arg1, arg2
mov rsi, 789h
invoke GetTickCount
ret
MyAlgo endp
0000000140001000 <su | 55 | push rbp |
0000000140001001 | 48:8BEC | mov rbp,rsp |
0000000140001004 | 56 | push rsi |
0000000140001005 | 48:C7C6 89070000 | mov rsi,789 |
000000014000100C | FF15 BE110000 | call [<GetTickCount>] |
0000000140001012 | 5E | pop rsi |
0000000140001013 | C9 | leave |
0000000140001014 | C3 | ret |
That's lovely: ML64.exe performs an API call with a misaligned stack! Compliments, Microsoft :thumbsup:
So ML64 is indeed a mind reader, but a very dumb one :cool:
ML64 does what you tell it to do.
It doesn't know about the MS x64 ABI, or the Intel x64 ABI, or even the Linux x64 ABI :biggrin:
Nothing to get in your way of pure ASM code.
What version of ml64.exe are you using? I get the following with your code. No "push esi" or "pop esi".
22: .CODE
23:
C8 80 00 00 enter 80h,0
48 83 EC 60 sub rsp,60h
48 89 4D 10 mov qword ptr [arg1],rcx
48 89 55 18 mov qword ptr [arg2],rdx
25: mov rsi, 789h
48 C7 C6 89 07 00 00 mov rsi,789h
26: invoke GetTickCount
FF 15 B3 3F 00 00 call qword ptr [__imp_GetTickCount (0D95000h)]
C9 leave
C3 ret
27: ret
28: MyAlgo endp
Quote from: ahsat on April 06, 2024, 01:00:09 AMWhat version of ml64.exe are you using?
Microsoft (R) Macro Assembler (x64) Version 14.30.30705.0
Quote from: jj2007 on April 06, 2024, 01:14:06 AMMicrosoft (R) Macro Assembler (x64) Version 14.30.30705.0
Microsoft (R) Macro Assembler (x64) Version 14.39.33522.0
The above is my version of my version of ML64.exe. There is obviously a big difference. Mine never produces any code for "uses" no matter what I do. I will look for an older copy of ml64.
Quote from: jj2007 on April 05, 2024, 07:29:38 PMThat's lovely: ML64.exe performs an API call with a misaligned stack!
What are you talking about here? What is wrong with the stack?
Quote from: ahsat on April 06, 2024, 03:20:25 AMWhat are you talking about here? What is wrong with the stack?
The stack must be aligned to 16 for ml64. Pushing a single 64 bit register misaligns the stack by 8. Two 64 bit registers may be pushed to keep the 16 byte alignment, but don't forget to pop two in that case.
In my book, yet another reason to just avoid 64-bit programming altogether ...
Quote from: sudoku on April 06, 2024, 04:11:49 AMQuote from: ahsat on April 06, 2024, 03:20:25 AMWhat are you talking about here? What is wrong with the stack?
The stack must be aligned to 16 for ml64. Pushing a single 64 bit register misaligns the stack by 8. Two 64 bit registers may be pushed to keep the 16 byte alignment, but don't forget to pop two in that case.
Thank you. That seems odd, do you know why?
It's a requirement for the intel win64 abi.
When you call a function, it requires the 16 byte stack alignment.
Do a google search for "win64 abi stack alignment" for better explanation... I can get a link later, I'm on my iPad at the moment. (Hard to copy and paste links from the iPad)
Later:
info on 64 bit stack alignment from Microsoft (https://learn.microsoft.com/en-us/cpp/build/stack-usage?view=msvc-170)
Quote from: ahsat on April 06, 2024, 07:32:22 AMThat seems odd, do you know why?
The 16-byte alignment is required by Windows. Why exactly is not well documented (to my knowledge), but there are many SIMD instructions like
movaps which fail miserably if the address is not aligned to 16 bytes; that could be the underlying reason.
Quote from: ahsat on April 06, 2024, 01:00:09 AMWhat version of ml64.exe are you using? I get the following with your code. No "push esi" or "pop esi".
22: .CODE
23:
C8 80 00 00 enter 80h,0
48 83 EC 60 sub rsp,60h
48 89 4D 10 mov qword ptr [arg1],rcx
48 89 55 18 mov qword ptr [arg2],rdx
25: mov rsi, 789h
48 C7 C6 89 07 00 00 mov rsi,789h
26: invoke GetTickCount
FF 15 B3 3F 00 00 call qword ptr [__imp_GetTickCount (0D95000h)]
C9 leave
C3 ret
27: ret
28: MyAlgo endp
You can tell that's hutch's prologue, and as I've said numerous times, his prologue doesn't use
USES.
To see how ML64 does it, use the default prologue
option prologue:prologuedef
option epilogue:epiloguedef
Quote from: sinsi on April 06, 2024, 08:35:10 AMQuote from: ahsat on April 06, 2024, 01:00:09 AMWhat version of ml64.exe are you using? I get the following with your code. No "push esi" or "pop esi".
22: .CODE
23:
C8 80 00 00 enter 80h,0
48 83 EC 60 sub rsp,60h
48 89 4D 10 mov qword ptr [arg1],rcx
48 89 55 18 mov qword ptr [arg2],rdx
25: mov rsi, 789h
48 C7 C6 89 07 00 00 mov rsi,789h
26: invoke GetTickCount
FF 15 B3 3F 00 00 call qword ptr [__imp_GetTickCount (0D95000h)]
C9 leave
C3 ret
27: ret
28: MyAlgo endp
You can tell that's hutch's prologue, and as I've said numerous times, his prologue doesn't use USES.
To see how ML64 does it, use the default prologue
option prologue:prologuedef
option epilogue:epiloguedef
Finally, something that I understand, thank you. I am sorry I didn't understand if/when you told me previously. I will try to find the masm64 SDK docs to see what else hutch is doing for me.
Quote from: ahsat on April 06, 2024, 12:12:01 PMI will try to find the masm64 SDK docs to see what else hutch is doing for me.
:badgrin: :badgrin: :badgrin:
Good luck with that. I've been forced to write my own prologue/epilogue and invoke macros.
Maybe I'll clean them up and post them here. They don't play well with hutch's masm64 stuff though...
Quote from: sinsi on April 06, 2024, 01:18:29 PMQuote from: ahsat on April 06, 2024, 12:12:01 PMI will try to find the masm64 SDK docs to see what else hutch is doing for me.
:badgrin: :badgrin: :badgrin:
Good luck with that. I've been forced to write my own prologue/epilogue and invoke macros.
Maybe I'll clean them up and post them here. They don't play well with hutch's masm64 stuff though...
I thought the masm 64 SDK was largely just the inc files, defining the equates and prototypes needed to call the Windows APIs. I didn't realize he was trying to make ml64 as complete an assembler as the 32 bit version. I now understand why Microsoft tries to stop people from downloading the SDK. I assume that Hutch has moved on, or passed away, and that development on the SDK has slowed.
I would like to see your prologue/epilogue macros if you ever decide to release them. I haven't done much macro coding, so I wouldn't even try.
Go to ebay, search "masm assembler", then hit the end key. The last Masm, 6.11 I think, sells for $300+. One guy is asking for $600 for a copy still in shrink wrap.
Quote from: ahsat on April 06, 2024, 02:54:27 PMI assume that Hutch has moved on, or passed away [...]
The latter. And yes, MASM64 development has basically stalled here.
Quote from: NoCforMe on April 07, 2024, 06:48:02 AMThe latter. And yes, MASM64 development has basically stalled here.
What country did Hutch live in?
Australia.