There are a few Masm compatible assemblers (three of them actually hosted on this forum) that handle the stack automatically. What they do is basically to calculate the maximum number of arguments used within a PROC/ENDP frame and create a common stack frame for all calls. This sort of simplify things.
Example:
include stdio.inc
.code
main proc
printf("2 args: %d\n", 1)
printf("5 args: %d,%d,%d,%d\n", 1, 2, 3, 4)
xor eax,eax
ret
main endp
end main
Result:
main PROC
sub rsp, 56 ; 0000 _ 48: 83. EC, 38
mov edx, 1 ; 0004 _ BA, 00000001
lea rcx, [DS0000] ; 0009 _ 48: 8D. 0D, 00000000(rel)
call printf ; 0010 _ E8, 00000000(rel)
mov dword ptr [rsp+20H], 4 ; 0015 _ C7. 44 24, 20, 00000004
mov r9d, 3 ; 001D _ 41: B9, 00000003
mov r8d, 2 ; 0023 _ 41: B8, 00000002
mov edx, 1 ; 0029 _ BA, 00000001
lea rcx, [DS0001] ; 002E _ 48: 8D. 0D, 00000000(rel)
call printf ; 0035 _ E8, 00000000(rel)
xor eax, eax ; 003A _ 33. C0
add rsp, 56 ; 003C _ 48: 83. C4, 38
ret ; 0040 _ C3
main ENDP
However, to get an understanding of how the calling convention works you need to do some testing.