INT 21h AH=2Dh sets the time of day from the same registers INT 21h AH=2Ch returns it in: the hour in CH, the minute in CL, the second in DH and hundredths in DL, all binary. DOS turns the time into timer ticks and sets the BIOS tick count; DOS 3.3 and later also set the CMOS real-time clock of an AT, so the time survives a reboot.
Values out of range are refused: AL = FFh comes back and the clock is untouched. The runnable example relies on exactly that, so it changes nothing:
MOV CH, 25 ; hour 25: there is noneMOV CL, 0MOV DX, 0 ; 0 seconds, 0 hundredthsMOV AH, 2Dh ; DOS: set the timeINT 21h ; AL = FFh: refused, the clock is untouchedPUSH AXMOV DX, OFFSET MSG1MOV AH, 09h ; DOS: print the string at DS:DXINT 21hPOP AXCALL HEX2MOV DL, "h"MOV AH, 02h ; DOS: print the character in DLINT 21hMSG1 DB "AL = $"HEX2 PROC NEAR ; print AL as two hex digitsPUSH AXMOV CL, 4SHR AL, CL ; the high digit firstCALL NIB1POP AXAND AL, 0Fh ; then the low oneCALL NIB1RETHEX2 ENDPNIB1 PROC NEAR ; print AL (0-15) as one hex digitADD AL, "0"CMP AL, "9"JBE NIB2ADD AL, 7 ; 10-15 become "A"-"F"NIB2:MOV DL, ALMOV AH, 02hINT 21hRETNIB1 ENDP
A real setting looks like this, with a check of AL afterwards:
MOV CH, 12 ; 12:30:00.00MOV CL, 30MOV DH, 0MOV DL, 0MOV AH, 2Dh ; DOS: set the timeINT 21hCMP AL, 0 ; 00h: set; FFh: refusedJNE BADTIME1
Pitfalls
Binary, not BCD: 12:30 is CH = 12 (0Ch) and CL = 30 (1Eh), whereas the BIOS clock service INT 1Ah AH=03h wants 12h and 30h. The tick count has a resolution of 55 ms, so the hundredths read back a moment later need not match those set. The date is separate (INT 21h AH=2Bh): setting the time does not touch it, and a time set just before midnight rolls the date over normally when midnight comes. In DOSBox-X a successful set writes the CMOS clock and the tick count, and switches off the "synchronize time" option (following the host's clock) if it was on.