INT 21h AH=56h changes a file's name. The old name is at DS:DX and the new one at ES:DI, both ASCIIZ. In a .COM program ES = DS from the start; in an .EXE, ES points to the PSP until you load it, a classic reason for this call to fail with a garbage name.

If the new name has a different directory, the file moves there: its directory entry is taken out of one directory and put into the other, and the data is not copied, so a move is instant whatever the size. Both names must be on the same drive; a different drive is error 11h, not the same device. To move a file to another drive, copy it and delete the original.

AXMeaning
02hthe old file does not exist
03ha directory in either name does not exist
05haccess denied: something already has the new name, or the target directory is full
11hthe names are on different drives
prints three lines: "renamed OLD.TXT to NEW.TXT", "open OLD.TXT: error 02" and "rename NEW.TXT to OLD.TXT: error 05"
MOV AH, 3Ch ; create OLD.TXT
XOR CX, CX
MOV DX, OFFSET OLDN
INT 21h
CALL CHECK
MOV BX, AX
MOV AH, 3Eh ; and close it
INT 21h
MOV AH, 56h ; DOS: rename
MOV DX, OFFSET OLDN ; DS:DX = the old name
MOV DI, OFFSET NEWN ; ES:DI = the new name (ES = DS here)
INT 21h
CALL CHECK
MOV DX, OFFSET M1
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV DX, OFFSET M2
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV AX, 3D00h ; is the old name still there?
MOV DX, OFFSET OLDN
INT 21h
CALL REPORT ; error 02
MOV AH, 3Ch ; make a new OLD.TXT
XOR CX, CX
MOV DX, OFFSET OLDN
INT 21h
CALL CHECK
MOV BX, AX
MOV AH, 3Eh
INT 21h
MOV DX, OFFSET M3
MOV AH, 09h ; DOS: print the string at DS:DX
INT 21h
MOV AH, 56h ; rename onto a name that is taken
MOV DX, OFFSET NEWN
MOV DI, OFFSET OLDN
INT 21h
CALL REPORT ; error 05
MOV AH, 41h ; delete both files
MOV DX, OFFSET OLDN
INT 21h
MOV AH, 41h
MOV DX, OFFSET NEWN
INT 21h
OLDN DB "OLD.TXT", 0
NEWN DB "NEW.TXT", 0
M1 DB "renamed OLD.TXT to NEW.TXT", 13, 10, "$"
M2 DB "open OLD.TXT: $"
M3 DB "rename NEW.TXT to OLD.TXT: $"
CRLF DB 13, 10, "$"
CHECK PROC NEAR ; CF = 1: print the error code in AL and exit
JNC CHECK1
PUSH AX
MOV DX, OFFSET FAILMSG
MOV AH, 09h
INT 21h
POP AX
CALL PHEXB
MOV AX, 4C01h ; exit with code 1
INT 21h
CHECK1:
RET
CHECK ENDP
FAILMSG DB "failed: error $"
REPORT PROC NEAR ; CF = 0: "ok"; CF = 1: "error" and AL
JC REPORT1
MOV DX, OFFSET OKMSG
MOV AH, 09h
INT 21h
RET
REPORT1:
PUSH AX
MOV DX, OFFSET ERRMSG
MOV AH, 09h
INT 21h
POP AX
CALL PHEXB ; the error code
MOV DX, OFFSET CRLF
MOV AH, 09h
INT 21h
RET
REPORT ENDP
OKMSG DB "ok", 13, 10, "$"
ERRMSG DB "error $"
PHEXB PROC NEAR ; print AL as two hex digits
PUSH CX
PUSH DX
MOV DH, AL ; keep the byte
MOV CL, 4
SHR AL, CL ; the high digit first
MOV CX, 2
PHEXB1:
AND AL, 0Fh
ADD AL, "0"
CMP AL, "9"
JBE PHEXB2
ADD AL, 7 ; 10..15 -> "A".."F"
PHEXB2:
MOV DL, AL
MOV AH, 02h ; DOS: print DL
INT 21h
MOV AL, DH ; then the low digit
LOOP PHEXB1
POP DX
POP CX
RET
PHEXB ENDP

Pitfalls

An existing file with the new name is never replaced: delete it first with INT 21h AH=41h if that is what you want. Wildcards are not allowed; rename a group of files one by one, found with INT 21h AH=4Eh. Renaming a file that is open is asking for trouble on MS-DOS: close it first.