I have read many books and instructions on this, but one thing that is never specified is what happens to a register after you push it on the stack. For example if you write in
CPUs almost always copy, not copy + zero-the-src. Despite instruction mnemonics like mov for "move", the actual operation is "copy" (between registers), or load or store from/to memory.
A register can't be "empty", it always has some value. (Except the legacy x87 FP stack registers; there is a separate status register that tracks which x87 registers being empty / having a value).
If an instruction destroys its source operand, that's always noted in the documentation. Just like in high-level programming languages where reading a variable doesn't modify it unless something super weird is happening.
You can easily try this and find the answer yourself by using push with 2 different register values. Single-step it in your debugger and notice that the register values don't change (only ESP changes, and memory).
Also you can look at (an HTML extract of) the ISA reference manual, https://www.felixcloutier.com/x86/push .Z E Nir quoted the "Operation" section from it.
The register keeps its value after push instruction.
push effects two places: the stack memory that get new value, and the value of esp which now points to the new top.
You can deduce that from the description of push operation:
if(StackAddressSize == 32) {
if(OperandSize == 32) {
ESP = ESP - 4;
SS:ESP = Source //push doubleword
}
else { //OperandSize == 16
ESP = ESP - 2;
SS:ESP = Source; //push word
}
}
else { //StackAddressSize == 16
if(OperandSize == 16) {
SP = SP - 2;
SS:ESP = Source //push word
}
else { //OperandSize == 32
SP = SP - 4;
SS:ESP = Source; //push doubleword
}
}
source: https://c9x.me/x86/html/file_module_x86_id_269.html
https://c9x.me/x86/ is a great pocket reference for x86 instructions, use it at will!