Integers
I’ll write down my exact thought process as I complete this so you can see if something is not how you expected that to be.
When I saw the stub, I knew I first had to do “the boilerplate work” to make it runnable:
; Inventory Management
section .text
global get_box_weight
get_box_weight:
; This function takes the following parameters:
; - The number of items for the first product in the box
; - The weight of each item of the first product, in grams
; - The number of items for the second product in the box
; - The weight of each item of the second product, in grams
; The function must return the total weight of a box, in grams
ret
global max_number_of_boxes
max_number_of_boxes:
; This function takes the height of the box, in centimeters, as parameter
; It must return how many boxes can be stacked vertically
ret
global items_to_be_moved
items_to_be_moved:
; This function takes the following parameters:
; - The number of items still unaccounted for a product
; - The number of items for the product in a box
; The function must return how many items remain to be moved, after counting those in the box
ret
global calculate_payment
calculate_payment:
; This function takes the following parameters:
; - The upfront payment
; - The total number of boxes moved
; - The number of truck trips made
; - The number of lost items
; - The value of each lost item
; - The number of other workers to split the payment/debt with you
; The function must return how much you should be paid, or pay, at the end
ret
%ifidn __OUTPUT_FORMAT__,elf64
section .note.GNU-stack noalloc noexec nowrite progbits
%endif
I then started reading the instructions (temporarily editing the style of h4 to be h3).
Even if you fix the format, this will not look correct in the UI. I recommend adding *_italicbold_* to all the level 5, then level 4 headings (in markdown level 3 after you fix it).
| Now |
Proposed |
 |
 |
Lots of interesting information about different forms of multiplication, but I am wondering if I’ll need it in this exercise. I’ll keep reading though. I like the caution admonitions. Very clear stuff, in very little text. Thanks!
Would love the summary table of instructions at the bottom like mentioned before.
The actual exercise
Okay, so I have a function with 4 parameters. I do not think it’s reasonable to expect I recalled which register is used for which parameter. I now have to go back to the previous exercise to get this information.
This track uses the System V AMD64 ABI calling convention and the six first integers arguments are passed to a function in registers. They are passed in the following order: rdi , rsi , rdx , rcx , r8 , and r9 .
It could be helpful to link back to the previous concept in the structions, using a concept link (see docs on exercise how to do that, I do not know of the top of my head, but we sometimes use it in JS).
Trying that.
I see in the instructions
All arguments are 16-bit non-negative integers, and the return value is a 32-bit non-negative integer.
So that means that I likely need to use imul, and I likely need to “pad” those numbers (movezx?). The warning about this was relevant for 32bit numbers, but I am dealing with 16bits, right? But if I multiply, it may overflow into 32 bits…
Using imul with unsigned numbers […]
Okay so maybe I use the example from the instructions about extending to 32 bits first, and then multiplying.
movzx eax, rdi ; lower 16 bits of rdi, upper bits are cleared
movzx ecx, rsi ; lower 16 bits of rsi, upper bits are cleared
mul ecx ; result should now be in eax
That doesn’t work. Compiler error: inventory_management.asm:18: error: invalid combination of opcode and operands. Perhaps I need to use the 16-bit registers di and si. I got this from the table from the first exercise, but there is zero explanation about this.
movzx eax, di ; lower 16 bits of di, upper bits are cleared
movzx ecx, si ; lower 16 bits of si, upper bits are cleared
mul ecx ; result should now be in eax
That doesn’t error. I will move ecx into the return value. My guess is that rax would not work (size?), but let me just move it into esp so I can do the other multiplication as well.
mov esp, eax
Yeah no. That doesn’t work and it doesn’t tell me why.
Segmentation fault (core dumped)
Okay, then I guess I’ll try to just move eax into rax:
mov rax, eax
And I get the original error again: inventory_management.asm:23: error: invalid combination of opcode and operands, despite the instruction saying that this should be possible:
A 32-bit source operand is always zero-extended to all 64 bits of the destination operand with a simple mov.
Wait, perhaps rax, eax, ax are the same thing, but just accessing it differently. Back to the original instructions
Illustration of how the bits are accessed for the rax register:
Yes! Okay great. So the result is already in the right register. I just need to move it out so I can do the second multiplication, then sum them. Golden!
mov ebx, eax
movzx eax, dx ; lower 16 bits of dx, upper bits are cleared
movzx ecx, cx ; lower 16 bits of cx, upper bits are cleared
mul ecx ; result should now be in eax
add eax, ebx
But that won’t work because the first multiplication “used” the registers that I now need. I didn’t find a way to make mul use a different second register, but I can inverse the operations so that I “clear out” parameter 3 for multiplication usage.
I am off by 500g , but that is solved because the empty box is 500g.
max_number_of_boxes
I see that the comment about the parameter is structured differently here. Would be good to change for consistency of the other stubs.
; This function takes the following parameters:
; - This function takes the height of the box, in centimeters, as parameter
; The function must return how many boxes can be stacked vertically
I see I need to do a division, namely:
Divide truck height by the first parameter.
I read that idiv will divide whatever is in rdx:rax with the given argument.
@oxe-b told me that the constant literals are 32 bits, so I should mov it into a 64-bit register if I want everything to work on 64 bits:
mov rax, TRUCK_INTERIOR_HEIGHT
idiv rdi; first parameter
And I hit an error:
make: *** [Makefile:29: all] Arithmetic exception (core dumped)
Maybe it’s because I ignored this part in the instructions:
So, whenever working with 64-bit integers, all bits in the rdx register must be cleared for a non-negative number in rax , and set for a negative number.
I mean, I am, I guess, working with 64 bit integers, but maybe I should just limit myself to 32bits? All the content that follows below this warning do not tell me how to clear the bits, and are all about sign extending, but I don’t want that. I will take the following approach:
move <> 0 into both registers to clear it (??)
- If that doesn’t work, maybe try
movzx <> 0 (??)
- If that doesn’t work, don’t use
r registers, but use the 32 bit equivalents so I can ignore all this (??)
mov rax, 0
mov rdx, 0
mov rax, TRUCK_INTERIOR_HEIGHT
idiv rdi; first parameter
Okay, that worked. Maybe it was just rdx not being set, because I read that TRUCK_INTERIOR_HEIGHT would be zero extended. I retry without the first mov rax, 0.
items_to_be_moved
I find the instructions hard to follow, but I think it wants me to subtract the second argument from the first.
The second argument is in rsi, the first one in rdi.
sub rdi, rsi
That doesn’t work. It only gives me negative numbers. The instructions point that this may be an error:
The arguments are 32-bit non-negative integers. The return value is a 32-bit integer. In case of an error in the process, it is possible that the result is a negative number.
Perhaps I should use the 32 bit registers instead. edi and esi it is.
sub edi, esi
I am still getting negative numbers.
I tried to figure out what the tests are using, so I tried sub edi, 0 and sub esi, 0 and sub rdi, 0, and then realised: the result is in the first argument, so I still need to move it to rax.
sub edi, esi
mov eax, edi
Yay. That worked. Now I am wondering: is it idiomatic to move the argument into eax and end with sub eax, esi, or is it idiomatic to move the result explicitly into eax. And should I have used the 64 bit integer registers?
Do you want me to continue this? Does that help?
Key takeaways solving the first task:
- Lack of locality of knowledge. Link back to previous content would help.
- I recommend adding a “info” admonition: remember you can access the same register with different sizes using the correct register name, eg.
rax for 64 bits, eax for 32 bits, ax for 16 bits.
- I recommend adding parameter comment for
max_number_of_boxes.
- I recommend adding a “caution” at the first instruction that
mul uses ecx, and that the third argument is also in (e)cx. I don’t think it was fun or helpful to have to figure this out. You still need to think about the solution anyway (am I wrong here?)
- It is unclear how to clear a register. I guessed (correctly? I have no way to check)
- It is not obvious when you should use one register type over another, except that usually both should be the same size.
- I will report back when I did the other tasks.