x86 Assembly Heap Memory Allocation

Greetings everyone!
As we all know, in C, which is a low-level programming language, there's a concept of a stack and a heap for doing memory allocation.
Now, if I want to go even lower than C and use Assembly, I notice that it's lacking one of the aforementioned concepts!
Is there really no instruction in x86 Assembly for putting stuff on the heap? Is such concept even exist in Assembly?
And if not, does this mean that when you allocate memory on the heap using C it actually (down at the very very bottom) allocates it on the stack (since C code is translated to assembly when compiled)?

Edited by Bits Please on Reason: Initial post
Heap and stack are not really concepts for language. It is more runtime thing. If you have pointer in C and are putting value into it, you don't really know if this is stack or heap. All that matters is where you got the pointer. So it is not language thing - it is runtime thing. Heap/stack exists in assembly same way as it exists in C. Putting values into stack or heap is done with "mov". Heap vs stack is different here only in a way where you get address to use.

For stack you get address from rsp register. You "allocated" space there by subtracting necessary amount from rsp register.

For heap you get address from heap allocation functions - malloc, HeapAlloc or similar. Or just skip the heap and allocate directly from OS, same how HandmadeHero does allocations.
The stack is part of the calling convention between functions. In most cases it is a area of memory pointed to by the stack pointer where some of the parameters get stored and the called function is allowed to grow it downwards to store its own values on it.

However there is no requirement that the stack is continuous. A function can allocate a big chunk of memory and start a new stack and call functions with it, freeing it when it returns.

The heap is a bit more nebulous. Most old diagrams show the heap implemented with a bump allocator with heap and stack growing towards each other. However modern allocators that implement heaps get memory pages from the operating system and parcel out chunks of it to the user code.

so all you'll see in assembly from a heap allocation is a function call to the allocator function, which itself does some data-structure magic and possibly a system call to get more pages (or release them)