GCC Calling Convention
gcc uses the standard calling convention on whatever platform it's targeting. It sounds like you're describing the i386 System V calling convention / ABI used on Linux, and/or some Windows calling conventions. (some of which pass args differently, but make the same choice of registers that can be clobbered).
You're using 16-bit register names, but gcc barely supports 16-bit x86. It basically generates 32-bit code and then assembles it with .code16 so most instructions have operand-size and/or address-size prefixes.
Caller saves values of AX, CX and DX registers
No, the caller does that only if it had any data in them that it wants to preserve across the call. The normal case is that the caller lets those values die. "caller-saved" vs. "callee-saved" is bad terminology because it implies that all registers actually get saved somewhere.
Much easier to understand, IMO, is
- call-clobbered: EAX ECX EDX and condition codes (part of EFLAGS), all xmm regs
- call-preserved: EBX, ESI EDI, EBP, ESP.
DF must be 0 at call and return, so string instructions go upwards. (DF is another bit in EFLAGS). The x87 stack must be empty on call and ret, except for functions which return an FP value (in which case st0 has the return value, and the rest of the x87 stack is empty).
Call-clobbered means that after a call, the caller has to assume the register holds garbage, whether or not the callee actually used the register. If there was anything in that register the caller needed for later, it has to move it somewhere else. But if not, it's totally fine to let the value die. e.g. to compile something like rv = foo(a + b + c), the caller would calculate a+b+c in a register. But if it doesn't also need that value after the function call, it doesn't have any need to preserve it.
Call-preserved means the caller can assume the register value hasn't changed, whether the callee simply avoided touching that register, or the callee saved / restored it. (Or for ESP, it's common for the callee to restore it with an add esp, 28 or similar, to reverse whatever changes it made with push and sub. It doesn't matter how the callee manages to return with call-preserved registers still holding the caller's values, just that it does. This is why "callee-saved" is also not the clearest term: it implies the callee explicitly saves them.
But, what about Status register? Who saves it?
Nobody saves it, except in very rare cases. The caller could save it if desired, but usually it's far easier and cheaper to to just redo a compare (popf is slow, and the pushf to save EFLAGS in the first place isn't free).
Or more often there wasn't any useful data in the condition codes, just integer values in integer registers. Most instructions write EFLAGS, but most of the time you never read those results. You usually use add, imul, and so on for the integer result and ignore the flag result.
Fun fact: 64-bit OS X system calls set CF on error, otherwise they clear CF. No common 32 or 64-bit function-calling conventions return anything in EFLAGS; they're just clobbered. (For Linux system calls, EFLAGS / RFLAGS is preserved. It's typical for system calls to not clobber any registers, other than the return value, partly because this avoids leaking kernel information back to user space.)