The sequence push cs
, pop ds
is simply a way to set your data segment to the same value as your code segment.
It's similar to using push ax
, pop bx
instead of mov bx, ax
, other than the fact that it uses memory and may have a different effect on certain flags, something I couldn't be bothered checking when my intent is only to provide an example :-)
One reason you would do this dates back to the old days of x86 segmented architecture (as opposed to the more modern selectors), which is rarely used nowadays. The x86 had various memory models like, tiny, small, compact, medium, large and huge.
These were basically variations on the sizes and quantities of code and data segments that you could use and, from memory, tiny meant that you had one segment that contained both code and data.
Hence cs
and ds
should be set to the same value so that all instructions operated on that segment by default.
In your particular case, you're saving ds
, setting it to the same value as cs
then restoring it. See below for a more likely explanation of why.
As to the workings of movsw
, it simply copies a single word value from the memory at ds:si
to address es:di
, updating the pointers afterward (increment or decrement, depending on the setting of the direction flag).
The rep
prefix does that in a loop, decrementing cx
until it reached zero.
Hence it's just a bulk memory copy.
Now, since the source of repsw
is specified in terms of the ds
segment, the real reason why you're seeing the push/pop
to set ds
temporarily becomes clear - it's because the source of the data obviously lies in the code segment.