Saving rcx is usually not necessary, it is not callee-save in common calling conventions. On Linux (and similar) rdi also does not need to be saved, I guess you're using that since the Win64 calling convention does not pass an argument in rdi. You can save them anyway if you want, which can be useful if you're using custom calling conventions. Saving an even ...
Correctness (Return Value): You are violating the convention for the strlen function, which is documented as returning the number of characters between the beginning of the string and the terminating null character without including the terminating NUL character. Your code includes the terminating NUL, given the position of the inc ebx instruction.
A big danger.
When the len parameter happens to be a muliple of 8 and all of the full qwords have been processed, there will be no remaining bytes to process.
Your code calculates the number of remaining bytes using
mov 16(%ebp),%edx # length
and $7,%edx # loop tail
but forgets to exit if this produces zero. The loop that follows is ...
feedback on how clear my comments were and how easy the code was to follow
I'm not a big fan of line comments. I think they make the code a bit harder to follow. That's why I prefer tail comments along with a nice tabular layout of the labels, instructions, operands, and tail comments.
And of course don't write redundant comments like "Update the ...
The code looks straight-forward and really optimized for size and simplicity.
There's a small detail that I would change, though: replace cmove with cmovz, to make the code more expressive. It's not that "being equal" would be of any interest here, it's the zeroness of %ecx that is interesting.
I like the omitted second jmp in memmove. It's obvious after ...
I was thinking if I should add labels like write even if I wouldn't jmp to them, so code would be cleaner.
Writing labels that aren't jumped to is fine. Provided they have meaningful names, it can help to understand the program.
My comments (not in a particular order)
mov esi, 1
This is a redundant comment! I already can see what the instruction ...
Your loop uses 2 jumps (je/jmp) on every iteration! Jumping is expensive, so a solution that needs only 1 jump (jne) will be more effective.
xor rdx, rdx
dec rdx ; This compensates for the INC that is happening first.
cmp byte [rsi + rdx], 0
Do keep things logically together. There's no point in ...
I'll just add that CharToHex assumes that the high bytes of EAX and EBX are zero. In particular, the AH value will be non-zero for an input string length greater than 255 characters. When you look up the digit with [Digits+eax] you can access out of bounds memory.
I see a number of things that may help you improve your program.
Keep writing good comments
The comments for the macro and procedure headers are quite good. They convey, in a systematic way, just what the reader needs to know. My custom is also to add a "Trashed" section that says which registers are used and not restored. More on that later. One area ...
It’s been a good many years since I wrote significant code in x86 assembly language, but I’ll wade in here.
Yes, div is a fat pig of an instruction. It not only takes a ridiculous number of clock cycles, but it uses many of the dispatch ports, clogging the CPU so it can’t do much else while waiting on the result. See the links in this S.O. answer.
Now I would like to know, can it be done in some better way? It contains 13 bytes.
From this I conclude that by better you mean shorter.
You can easily bring this down to 11 bytes if you duplicate the method that was used to fill RSI for the setup of RAX.
The instruction dec rdi depends on the ...
If you execute this code with n DB 0, it returns without setting ch to any value, leaving the result as undefined.
If n happens to be between 128 and 255, the jge will treat the comparison as if it was a signed comparison, and since 0 is not greater than or equal to any value between ...
mov SS, AX ;
mov BP, 0x7BFF
mov SP, BP
This is a dangerous construct! To maintain a consistent SS:SP pair of registers you should always set these back to back (No instruction(s) in between).
The code uses pusha, popa, shr bx, 12, ...
So your code is clearly targetting x86 (and not the infamous 8086). Then you don't need to use ...
Ooooh... MS-DOS Assembly programming! That was a long time ago for me, but here goes.
There's a lack of error handling with the number entered, since a user can enter 99999 for Z and your program does not complain. The SUB, CMP, JB sequence can lose the CMP instruction since the flags will already be set by the subtraction. You can also get rid of the ...
We generally prefer smaller instructions. To zero the EBX register we normally use xor ebx, ebx. This is a 2-byte instruction where mov ebx, 0 is a 5-byte instruction.
Loops get faster if they include less jumping. Your strlen_loop has 2 jump-instructions per iteration. You can easily do this with a single conditional jump-instruction.
Next code ...
Is the implementation complete?
From the manual pages of strcmp()
The strcmp() and strncmp() functions return an integer less than,
equal to, or greater than zero if s1 (or the first n bytes thereof)
is found, respectively, to be less than, to match, or be greater
Now if we take your strcmp function we have
In pseudocode, your program looks roughly like this:
z = |z|
for x = 0..1000
for y = 0..1000
if x² - y² = z -> return true
It can be made faster by several orders of magnitude if you got rid of the nested loops:
x = y = 0
z = |z|
while x <= 1000:
r = x² - y² - z
if r = 0: return true
if r < 0: x += 1
if r > 0: y += 1
I'm wondering how this is normally done
Yours is as good a solution as anyone else's.
That said, a solution that pushes the elements would not need to address these elements relative to EBP at all.
; new f of size n + 2
lea edi, [ebx + 2] ; save size of array in edi
shl edi, 2 ; mul by 4, size of DWORD int
Some perf thoughts:
Using xchg reg,reg is more expensive than you might expect.
pushad isn't great either.
I'm not sure how useful it might be here, but you might also take a look at this. This has to do with the issues related to chains of addc.
And while people are dubious about repe scas, it probably isn't an issue here.
It might also be interesting to ...
Use a multiplication to perform several shifts in a single instruction.
Trim the input to seven bits to avoid overlap in the second step.
Shift by 0, 7, 14, 21, 28, 35, 42 bits and aggregate the results in a 64-bit integer.
Keep only bits 0, 8, 16, 24, 32, 40, 48.
Handle the 8th bit of the input separately. Shift by 49, then add it to the others.
Documentation for this flag reads:
If a software procedure can set and clear this flag, the processor executing the procedure supports the CPUID instruction
This clearly means that the flag does not have a defined value. It could be OFF or ON at any time!
Where your comment says that you "Toggle bit 21 (ID)", the instruction actually sets the bit. I don'...
I'd expect that most of the performance problem comes from the indirect branch (jmp qword ptr [r15 + r10 * 8]) - it's relatively unpredictable and should cause lots of branch mispredictions, bad speculations and pipeline stalls.
There's only really 2 ways to deal with this:
1) Amortise the cost. For example, if you packed 4 instructions into r10 and had a ...
I see a number of things that may help you improve your program, but first I wanted to say that you've definitely improved the program greatly from the previous version. Nice work!
Fix the (minor) bug
The previous version correctly showed blanks at the end of the lines if there were fewer actual bytes in the file than the line length called for. ...
Why are you so keen on zeroing the registers at the completion of the program? Drop those and you'll have made your first optimization already.
Don't use mov si, 0 to zero the register. Use xor si, si. It's shorter (codesize) and faster (execution speed).
Instead of using lea bx, array use mov bx, array (NASM style) or mov bx, offset array (...
There's a bug in your code if memchr finds %sil in the last byte of %rdi; if %rcx tests to be zero and yet the byte has been found, it will incorrectly return zero.
To fix that, do something like this:
mov %rdx, %rcx
movzbl %sil, %eax
lea -1(%rdi), %rax
test %cl, %cl
A code-size optimization
If you move the mov edi, Metrics instruction to just below the FindMatch label and thus have it repeat with each iteration, you can remove 4 instructions from the code. I've marked these with an exclamation mark:
xor edx, edx
mov ecx, edx
mov edi, Metrics ;Restore it from here
Here are some things that may help you improve your program
Use consistent formatting
The code as posted has irregular indentation, making it not so easy to read. Assembly language programs are typically very linear and neat. Also, I personally don't use tab characters in my code so that it looks the same everywhere (including printing), but that's a ...
My equivalent to your nice quine is a bit nicer, and differs in other ways.
It is nicer in that the payload at the end of the source is split into several lines. It still uses over-wide lines because the indentation and the blanks before comments are repeated verbatim, and each line is prefixed with a db " directive (the prefix) that is itself indented.
Written for NASM
Your program is a little hard to follow, mostly due to poor formatting. There is a lot of unnecessary duplication too and the user has no opportunity for correcting mistakes. The following prologue will address these initial criteria;
Prompt operator for what is required.
Use a function that allows the user to correct mistakes.
Test that ...