Note that indexes are always in bytes and starting at 0. How many bytes are there in 8*rdx?
Ah. eh. rdx is 8 bytes in size, so potentially a lot.
I made my index a byte, so I should have done 8*dl instead? Nah that won’t work either. I could make it a dq, so the reads are always right. But I don’t think that will solve it.
But I think what you’re saying is that I am not writing at the right location.
The index is reset when it reaches 7, so the max index is 6. So 8*rdx varies from 0 to 48. But your array has only 8 bytes (and you use only 7 of those).
Oh darn it. I don’t need 8*, because each thing is only taking one byte, not 8 bits (hence the 8).
Yeah okay. It’s obvious now. Is there a way to test for this behaviour earlier? I didn’t touch it because it felt correct :')
Save some meta (like an analyzer or such), there’s no way. If the access is out of bounds, things might segfault. But they might not, especially because memory is contiguous and every variable can be accessed from another declared in a previous position (plus an offset large enough):
section .data
var1 db 4
var2 db 8 ; this can be accessed at var1 + 1
Maybe I can add some hint for something like that. I’ll think of something.
Ah yes, and because I was consistent with my memory issue, I was consistently getting the same memory out of bounds. Okay, ignore me. Maybe a caution could help. I think that if you add it to this task, it would be good enough.
save_count (cont.)
With a bit of help from @oxe-b I realised I was using bits to index the memory offset instead of bytes. That won’t work! Removing the multiplication with 8 solved all my issues.
In the meanwhile I also updated the code and replaced add x, 1 with inc x and sub x, 1 with dec x. I don’t know if that is more idiomatic but it seemed like it!
update_week_counts
I think this is going to be easy.
- Copy
swap - Replace the
0x0with the input parameter - Forcefully unset the last byte
- Set the index to 7
mov r8, qword [rel next_bird_counts] ; get current week count as single number
mov qword [rel last_bird_counts], r8 ; store r8 as last week count
mov qword [rel next_bird_counts], rdi ; set array
mov byte [rel next_bird_counts + 7], 0 ; clear last byte
mov qword [rel day_index], 7 ; set day index
Takeaways
- Better error messages help a lot. This change has already been made.
- Showing the expected and actual numbers as bytes instead of a single integer would help.
- The test order and task order didn’t line up (see “That is a function I have to implement. I haven’t done that yet, because that is task 4! Will that doesn’t really work.”)
- Overall really fun exercise.
I checked out The PR and now I see that the content has updated everywhere.
Yes, overall this is a huuuuge improvement.
I did spot this:

(and you still may want to bold italic low level those headings, also here:

).
Shall I do another one?
Yes, please! I’ll keep rolling the PRs =)
I’ve been wondering… what is:
%ifidn __OUTPUT_FORMAT__,elf64 section .note.GNU-stack noalloc noexec nowrite progbits %endif
Perhaps could have a comment in lagana to explain and leave alone.
Bit-manipulation (Secrets)
Ooooh fun. I am a big fan of this and most of my solutions to secrets accross Exercism use bitwise operators. I start again with the work to define them functions so I can run the tests. I do notice some inconsistencies in the sub comments. Arguments are not listed as list, and the copy is slightly different in most functions.

Let’s get into it!
I read about immediate. I guess this is the “literal” we know from other languages. I do not recall if this was mentioned in an earlier concept.
extract_higher_bits
I am wondering what the canonical / idiomatic way to do this is. My intuition says:
- get the parameter
rdi(but then the 16bit size) andwith0b1111111100000000
I see in the tests that I can write it with underscores:
0b1111_1111_0000_0000
About the and operator I read:
Most of them take two operands, perform a bitwise operation on both and store the result in the destination operand. The exception is not, which takes just one destination operand.
Yeah okay but which one is the destination operand? I would assume the first because add x, y has x as destination. I recall from computer science (which I studied long ago) that this was the difference between the two types. One had source before dest and one had dest before source.
Anyway. It’s easy to test:
mov ax, 0xFF
and di, ax
if this error with the result being 255 and the inverse errors with non-255, then and ax, di means and dest, src.
mov ax, 0xF0
and ax, di
Unfortunately this gives me Expected 164 Was 192.. I think that’s because I am now setting the 16bit register and it expects 8bits, which are ah/al. I don’t know how to do the conversion, but I re-read lasagna and see:
When using less than 64-bits, the bits accessed are usually from the lower portion of the register. The exception to this rule are
ah,bh,chanddh, which access the upper 8-bits from the 16-bits portion of the register.
So that means that if I can write 0000_0000_value to the 16bit register, and the test uses al, then I am golden.
| 0 - 8 | 9 - 15 | |
|---|---|---|
| di | ||
| ax | 0 |
What I need to accomplish is move the bits from di to the right, fill with zeros, and return that.
mov ax, di
shr ax, 8
extract_lower_bits
I think I can use the same trick here:
| 0 - 8 | 9 - 15 | |
|---|---|---|
| di | ||
| ax | 0 |
So either I and with 0x0F, or I shift left, then call extract_higher_bits. Since I don’t know yet how to make the and work here, I take the second approach:
shl di, 8
call extract_higher_bits
extract_redundant_bits
I think what I need to do is:
| 0 - 8 | 9 - 15 | |
|---|---|---|
| di | ||
| ax | 0 | 0-8 & 9-15 |
I already know how to get the higher and lower bits, and I know how to get only the 8 bits so I am in the right size right away (al instead of ax)
call extract_higher_bits
mov bl, al
call extract_lower_bits
and al, bl
Yay.
set_message_bits
From the instructions it’s not completely clear if high or low is the mask or message. However, the goal is: return 1 if mask is 1, otherwise keep the original bit in the message. That means: if either bit is 1, have the bit be 1, if neither bit is 1, have the bit be 0. That sounds like or
1100 0100 1100 0101
1100 0100
1100 0101
1100 0101 OR
1110 0101 EXAMPLE
Yeah I don’t think the example is correct. I am just going to try it.
call extract_higher_bits
mov bl, al
call extract_lower_bits
or al, bl
It worked! @oxe-b you probably want to update the example.
rotate_private_key
I believe I should start with extracting the redundant bits, and then:
- count the number of 1s
- take the private key
- rotate it left by that count
I’ll need popcnt which only works on 16-bit values;
I’ll need rol which only works with the second value being cl (or a literal).
That means I must zero-extend the redundant bits answer:
call extract_redundant_bits ; find redundant bits
movzx ax, al ; zero-extend answer to 16bits
popcnt cx, ax ; count number of 1s
The number in cx must live in cl as well, because there are only 16 bits to count. I don’t quite understand why this operation only takes the 16-bit arguments on both sides, but that’s besides the point.
At this moment I can still run the tests. Let me try to read the private key
section .data
PRIVATE_KEY equ 0b1011_0011_0011_1100
If it turns out I never update this, it can become .odata.
Then I try tor move it into the result (and then rol the result):
mov ax, word [PRIVATE_KEY] ; get private key
rol ax, cl ; rotate left by stored count
ret
That gives me a segmentation fault. Meh.
mov ax, 0b1011_0011_0011_1100 ; get private key
rol ax, cl ; rotate left by stored count
ret
This passes the tests. Okay. I supposed that LABEL equ value doesn’t give me memory addresses to play with, and the dq things do. I think I forgot what the concepts say about that, but I can try to inline the label directly:
mov ax, PRIVATE_KEY ; get private key
rol ax, cl ; rotate left by stored count
ret
Superb. Two more to go!
format_private_key
I think this is called after the key is already rotated, so no need to rotate the key.
- Isolate the lowest 8-bit portion of the rotated private key, which is the base value. That means extract_lowest_bits
- Isolate the highest 8-bit portion of the rotated private key, which is a mask to be applied to the base value. That means extract_highest_bits
- Flip set bits in the base value that are also set in the mask. That is exclusive or
- Flip all bits in the result.: That is not
call extract_higher_bits ; get the mask
mov bl, al
call extract_lower_bits ; get the base value
xor al, bl ; flip what's set in both
not al ; flip it
That does not pass any tests, but I am pretty certain it’s correct.
Perhaps my interpretation that it was already rotated (please add a note to the instructions) is wrong. Easy peasy lemon squeezy:
call rotate_private_key
mov di, ax
One more to go ![]()
decrypt_message
I think it’s a bit hidden that encoding both the message and a mask is the input argument. I found the same text for other instructions, so I know I need to pass it into format_private_key, but I do think the stub can be improved to clearly indicate what is being passed in where.
I have to start with generating that private key, because that needs to be returned in the result.
Now wondering: are the registers for rdi preserved across calls or no? In other words, if I am in function A, call function B which changes rdi, does it get changes in function A? I remember reading something about preserving but I do not recall. Let me look it up.
Some of those registers must be preserved across function calls:
rbp,rsp,rbx,r12,r13,r14andr15. Failing to preserve them may lead to an error or to undefined behaviour.The others are not preserved and may be used freely:
rax,rcx,rdx,rdi,rsi,r8,r9,r10andr11.
That doesn’t answer my question. I’ll play it save and:
- explicitly store
rdi - explicitly set
rdibefore a call if needed
mov r8w, di ; store original input
call format_private_key ; get private key
mov r9b, al ; store private key
The instructions are not really clear on what needs to be happening, but reading back to set_message_bits I can see that that takes a message-mask combination and “set the relevant bits”. Let’s use that:
mov di, r8w ; prepare set bits
call set_message_bits
I do not understand how I am decrypting the message, or what that means, but if I ignore that gap, I take the instructions at face value and prepare the following:
- expand the message with 0s on the left
- expand the private key with 0s on the right
OR both, valuesto get the result
The first I can do with movzx ax, al.
The second I can do with shl <>, 8.
; Combine the two
movzx ax, al ; zero-fill output on the left (high)
shl r9w, 8 ; shift left private key (left fill)
or ax, r9w ; combine
And that concludes the exercise!
Takeaways
By far the exercise where I got stuck the least. A few things you already know about, and some more:
- Stub could be more consistent with the others, more explicit about the contents of the arguments
- It is (still) unclear to me, how you can “make smaller” a register, but I have a feeling it’s clearing the higher bits and that’s it. It was easier for me to use shift left and right in the first two tasks. Don’t know if that was intended

- The
equthing you know about - Final exercise could explicitly state that you don’t need to “understand decryption” or something similar
- Fixing the example for
OR
How did I do?
This is for the assembler to add some parameters in the executable, so that the OS separates stack space in a certain way. The bottom line is that it is a security measure.
This was not clear at the beginning, but after you’ve pointed it out, I added an observation for it in the basics concept and an entry in the integers concept.
This is from the basics concept, it is instruction dest, src for a two-operand instruction. At least in the syntax we use in the track (Intel’s).
But I think that I should discuss this a bit more in the integers concept when I talk about one-operand and three-operand imul.
Yes, I think you’re right. I’ll look into it.
Thank you very much!
Another thing to explain, this time in the memory concept.
Yes, equ is for assembler-time constants. They are just placeholders for the assembler (basically, you are creating a readable version of a literal). So you can use them whenever you’d use a literal and they do not occupy space in memory.
In case someone wants to read my solutions, I left all the commentary in. I do think mine are more readible than most XD.
I have time for another one, sooooo Mixed Juices!… here I come ![]()
Loops (Mixed Juices)
After preparing the stub I can run the tests. A few things spring to mind:
- Sometimes an array is passed in which means a memory address is passed in, which is 64bit (
rdi/rsietc.). - Sometimes a 32-bit number is passed in (
edi/esi) - It returns 32-bit numbers (
eax)
Reading the content now.
Found a missing ref here.
Whilst reading I saw that once again rcx is special (rotate also had this). It seems that so far built-ins use rcx whenever they need outside config / state.
Finally I think that loop decrements for me, does not do the comparison, but does exit based on the rflag. I’ll see what I can do with that.
time_to_make_juice
In JavaScript/TypeScript I would have done this with an object lookup or case statement. With 8 items, I think I’ll store this in an array instead, and then lookup the time in the array
| id | name | time | array index |
|---|---|---|---|
| 1 | Pure Strawberry Joy | 1 | 0 |
| 2 | Energizer | 3 | 1 |
| 3 | Green Garden | 3 | 2 |
| 4 | Tropical Island | 4 | 3 |
| 5 | All or Nothing | 5 | 4 |
| 6 | Feel Good | 4 | 5 |
| 7 | Today’s Special | 7 | 6 |
| 8 | Client’s Choice | 10 | 7 |
That gives me
section .odata
time_to_juice db 1, 3, 3, 4, 5, 4, 7, 10
Getting to the correct memory address I can use lea plus math
lea r8, [rel time_to_juice] ; get memory of first item
add r8, rdi ; move to next byte * id
dec r8 ; ID is 1-indexed, array is 0-indexed
Finally I can get the time-to-juice and ensure zero-fill
mov al, byte [r8] ; retrieve time to juice
movzx eax, al ; ensure 32-bit number
That passes the first 8 tests.
time_to_prepare
I need to map-reduce the drinks to the total juicing time, effectively summing all the time to juice. I do not know how to mutate the array length (probably not really a thing), so recursive will be hard. As this is the loop exercise, probably need to loop.
time_to_prepare({2, 3, 8}, 3)
This probably means I need to loop 3 times (so set rcx) and then for the index 0, 1, 2 collect the time to juice and sum it. My guess is that, because the time to prepare is order-independent, I can use the rcx index to my advantage and count from the right, or I need to math the input variable - loop iterations left. I could also store the index register and increment it by 1 each iteration.
Many options.
mov ecx, esi ; loop this many times
mov r9d, 0 ; store the sum (0 initially)
lea r10, [rdi] ; store the array start (input ordered juices)
Now if I understood correctly, I can now add a local (jump) label, and it will be able to GOTO that label when the loop instruction is hit, until ecx is 0.
Getting the ID of the current juice is using the effective address thing where I take the base address, add size * index +/- displacement.
| iteration | rcx | array index | expected rel |
|---|---|---|---|
| 0 | 3 | 2 | base + 4 * 3 - 4 |
| 1 | 2 | 1 | base + 4 * 2 - 4 |
| 2 | 1 | 0 | base + 4 * 1 - 4 |
After iteration 2, rcx will hit 0 and it will not jump back to the top.
.juice:
mov edi, dword [r10 + 4*rcx - 4] ; get the ID of the current juice
call time_to_make_juice ; get the time to juice
add r9d, eax ; add to sum
loop .juice ; repeat until rcx is 0
mov eax, r9d ; return the sum
ret
Tests pass, so I continue
limes_to_cut
wedges […] each represented by a 8-bit number
I see 'characters' in the example input. I wonder if those are turned into an 8-bit number. I have not learnt about characters yet. I think the solution for that is extern LIME_SMALL etc, but alas. I’ll try to equ the character and see what happens.
LIME_S equ 'S'
LIME_M equ 'M'
LIME_L equ 'L'
This compiles, so I’ll stick with it. I don’t know how to make mappings or anything like that, so I’l pick the easy route and define lime_wedges that takes a lime and returns a 32-bit number for the amount of wedges.
No clue if equ makes these 32-bits instead of 8-bits, but I’ll know very fast if this works.
lime_wedges:
; - Takes one input parameter: 8-bit number ; dil
; Returns the number of wedges as 32-bit number
cmp dil, LIME_S
je .small
cmp dil, LIME_M
je .medium
mov eax, 10
ret
.small:
mov eax, 6
ret
.medium:
mov eax, 8
ret
I can then implement limes_to_cut to quickly test my function:
lea r10, [rsi] ; get the memory address of first lime
mov dil, byte [r10] ; deref first lime character into arg1
call lime_wedges
If this works, I will see something like
Expected 1 Was 6. Number of wedges needed: 6. Limes: ‘S’.
If it doesn’t work I will get an error, or “Was 10”.
Great. Now I can build the limes_to_cut function. I’ll set-up the structure first, then implement it.
mov ecx, edx ; loop this many times (at most amount of lime times)
lea r8, [rsi] ; store index of first lime to cut
mov r9b, 0 ; track lime index
mov r10d, 0 ; track cut count
mov r11d, edi ; track count still needed
cmp ecx, 0 ; if there is nothing to cut, break
je .break
.loop:
cmp r11d, 0 ; if there is nothing left to cut, break
jbe .break
; TODO: cut lime, add wedges to count
; increment lime index
loop .loop
.break:
mov eax, r10d ; return the count
ret
It still compiles and runs, so it’s probably correct.
I now realise I do not need to track both cut count and index. They are the same thing.
mov ecx, edx ; loop this many times (at most amount of lime times)
lea r8, [rsi] ; store index of first lime to cut
mov r9, 0 ; track lime index (cut count)
mov r10d, edi ; track count still needed
cmp ecx, 0 ; if there is nothing to cut, break
je .break
.loop:
cmp r10d, 0 ; if there is nothing left to cut, break
jle .break
mov dil, byte [r8 + 1*r9] ; get lime at index
call lime_wedges ; get wedges for lime
sub r10d, eax ; subtract cut wedges
inc r9b ; move lime index +1
loop .loop
.break:
mov eax, r9d ; return the count (lime index)
ret
I think I did that right!
remaining_orders
Looks like more of the same (which is good).
I should loop as long as there is time left in shift;
I should loop at most the length of the array (which I do not know);
I should loop until the time in the shift is done;
I should return how many juices have been made;
I probably can use the jmp with exit condition to make this loop work:
lea r8, [rsi] ; store index of first drink to make
mov r9, 0 ; track drink index
mov r10d, edi ; track time left
.loop:
; TODO prepare a drink
; TODO subtract from r10
cmp r10d, 0
jle .handover
jump .loop
.handover:
mov eax, r9d ; return the count (drink index)
ret
Unfortunately time_to_make_juice uses r8, so I’ll shift everything by 1. (r9, r10, r11)
I should not run this code, because there is nothing to decrease r10d right now, so it won’t work.
.loop:
mov edi, dword [r9 + 4*r10] ; prepare juicing
call time_to_make_juice
sub r11d, eax ; subtract time passed
inc r10 ; move drink index +1
cmp r11d, 0 ; if out of time
jle .handover ; ...handover
jmp .loop
.handover:
mov eax, r10d ; return the count (drink index)
ret
Yoooooooo I got this on the first try
Takeaways
- The character literal thing was weird
- I think that’s it!
Yeah, this was my bad. This concept was planned to depend on strings, but things changed and I forgot to update this task. I’ll change this to use constant values.
Despite that, I didn’t need any help! So the concept exercise ported really well.
Very well!
I’ve noticed that you are getting less stuck as you advance. This is in part something I expected, because one of the hardest thing in assembly is how “strange” things are in the beginning, without named arguments and with lots of manual stuff handling bytes and such. Once you get the hang of this, most things follow more or less.
About your question on “reducing” a register: you don’t usually need to do it. A register is a 64-bit memory slot in the CPU. The 32-bit, 16-bit, 8-bit and any byte in between are all inside this slot. When you return a 8-bit value, only the first byte of rax is used, the rest doesn’t matter. Same thing if you have, say, a ecx and do a mov dl, cl, you copy the first byte. The logic is the same for other sizes.
Even though we’ve learned that in a 32-bit positive number the upper bits are cleared, you shouldn’t think of registers are numbers. Registers are bytes, memory is bytes, even code is bytes. Integers and everything else are just abstractions/views over bytes, that are defined according to the rules we set. This is an overarching theme on the syllabus.
My aim is that once you go solve the practice exercises, after a while, you are able to create data structures such as queues, pqueues, etc by saying “those 19 bytes are a node, with one byte integer with meaning X, one 8-byte address with meaning Y and 10-bytes stuff that sometimes has meaning Z and sometimes has meaning K”. And then you can reinterpret things as you see fit.
By the way, thank you again for all the feedback! I’ll make a second large PR (less large than the first) tonight or tomorrow to address lots of things based on what you shared.
You’ve already reached half of the syllabus! With what you know, you are able to solve lots of practice exercises already if you have some basic knowledge of C types.
Yeah that’s where I hope to get.
I also hope to learn more about when to use internal memory, and when to keep abusing the registers etc.
p.s.

Yeah, but I do need to clear it, because the tests often check the entire thing
. I guess that’s expected!
I also want to know if it’s idiomatic to clear things often or not. I don’t see it in your or kieraville’s solutions, but my feeling is it could prevent a lot of bugs! I suppose it does take CPU cycles…
If you need, say, 4 bytes and you have 1 or 2 bytes, then you may need to clear upper bits if they are to be interpreter as integers (or set them for negative numbers).
This happens exactly because each byte is its own thing and the calling function (which called your code) only guarantees that you get the bytes that you get. The rest have undefined values. So, you need to manually ensure the other bytes have the value you need in order to interpret the sequence in the way you want.
To reduce, this is usually not necessary because if the calling function expects the result to fit, say, 1 byte, then it can not expect any result in any byte after the first. And to have the value in the entire sequence, you need to have the partial value in each byte, so things are already set.
As for what is idiomatic, it depends on your use case. There are some situations when it is good to clear bytes for security reasons, to “hide” data, but other than that, if it fits the contract between calling and called function, without undefined behavior, then it’s usually right.
The most common and “idiomatic” way to clear a register is using xor with itself. For example, xor rax, rax.





