This is the past DOS era of the compilation of source code, although has passed, but for the study of the assembly is still helpful, assembly language is just a basic programmer language, most people can grasp, not necessarily in-depth research.
Cgroup GROUP vector,codeseg
VECTOR SEGMENT at 0H
DB 6CH DUP (?) ; Filler
Time_lo DW? ;D OS Time
Time_hi DW? ;D OS Time
Vec_ip DW; CLOCK UPDATE VECTOR IP
Vec_cs DW; CLOCK UPDATE VECTOR CS
VECTOR ENDS
Codeseg SEGMENT PARA
Assume Cs:codeseg,ds:cgroup
ORG 100H
CLK PROC FAR
JMP SETUP; ATTACH to DOS
Intrpt LABEL DWORD
Int_ip DW 0; old UPDATE VECTOR IP
Int_cs DW 0; old UPDATE vecror CS
TICKS DW 0; TICK COUNTER
Scr_off DB 0,0; Screen OFFSET in BUFFER
Crt_port DW 0; Screen STATUS PORT
Flag db 0
Time DB 8 DUP (': ', 0BH); Time SAVE Area
Clk_int LABEL NEAR
PUSH AX; SAVE Registers
PUSH CX
PUSH DI
PUSH SI
PUSH DS
PUSH ES
PUSHF; and FLAGS
Call Cs:[intrpt];D o-old UPDATE INTERRUPT
MOV cx,0040h; Get SEGMENT of DOS TABLE
MOV ds,cx; Put in DS
MOV cx,cs:ticks; Get TICK COUNT
INC CX; INCREMENT IT
CMP cx,20; 01f4h; Has A MINUTE GONE by?
JB No_minute; NO, move on
Call UPDATE; YES, UPDATE CLOCK and
MOV cx,0; RESET TICK COUNTER
No_minute:
MOV cs:ticks,cx; SAVE UPDATED TICK COUNT
MOV cx,0b000h; Get Video SEGMENT
MOV es,cx; Put in ES
MOV Dx,cs:crt_port; Get CRT STATUS PORT ADDR
MOV Di,word PTR Cs:scr_off; Get screen BUFFER OFFSET
LEA Si,cs:time; Get DOS Time
MOV cx,16; SET up to move BYTES
CLI;D isable Other interrupts
Wait1:in AL,DX; READ CRT STATUS
TEST al,1; CHECK for VERTICAL Retrace
JNZ WAIT1; Wait for retrace low
MOV Ah,cs:[si]; Get-I-move
Wait2:in AL,DX; Get CRT STATUS
TEST al,1; CHECK for VERTICAL Retrace
JZ WAIT2; Wait for retrace
MOV Es:[di],ah; Move BYTE to Screen
INC DI INCREMENT INDEX
INC SI
LOOP WAIT1; Move NEXT BYTE
STI; ENABLE interrupts
POP ES; RESTORE Registers
POP DS
POP SI
POP DI
POP CX
POP AX
IRET; Return from INTERRUPT
CLK ENDP
UPDATE PROC NEAR
PUSH AX; SAVE Registers
PUSH BX
PUSH CX
PUSH DX
PUSH DS
MOV ax,0040h; Get address of DOS TABLE
MOV Ds,ax; Put in DS
MOV Ax,time_hi; Get high BYTE to DOS time
MOV flag,0; AM Flag
HOUR:CMP ax,0ch; CONVERT to HOURS
Jle H1
mov flag,1; set to PM
SUB ax,0ch
JMP HOUR
H1:aam; CONVERT to ASCII
ADD ax,3030h
LEA Bx,cs:time; Get Address of
MOV Cs:[bx],ah; SAVE HOURS-DIGIT
MOV Cs:[bx+2],al; SAVE HOURS SECOND DIGIT
MOV Ax,time_lo; Get DOS Time Low BYTE
MOV cx,8h; CONVERT to MINUTES
SHR AX,CL
MOV dx,3ch
MUL DL
SHR AX,CL
AAM; CONVERT to ASCII
ADD ax,3030h
MOV Cs:[bx+6],ah; SAVE MINUTES-DIGIT
MOV Cs:[bx+8],al; SAVE MINUTES SECOND DIGIT
mov byte ptr cs:[bx+12], ' a '
CMP flag,0 is it am?
JZ Goahead
mov byte ptr cs:[bx+12], ' P '
Goahead:
mov byte ptr cs:[bx+14], ' m '
POP DS; RESTORE Registers
POP DX
POP CX
POP BX
POP AX
Ret
UPDATE ENDP
Setup:mov ax,0; Get address of VECTOR TABLE
MOV Ds,ax; Put in DS
CLI;D Isable further interrupts
MOV AX,[VEC_IP]; Get address of the old UPDATE IP
MOV Cs:[int_ip],ax; SAVE IT
MOV Ax,[vec_cs]; Get address of the old UPDATE CS
MOV Cs:[int_cs],ax; SAVE IT
MOV Vec_ip,offset Clk_int; Put the CLK in VECTOR IP
MOV Vec_cs,cs; Put CS-CLK in VECTOR CS
STI; ENABLE interrupts
MOV ah,0fh; READ Video STATUS
INT 10H
SUB ah,8; Subtract 8 CHAR time from NCOLS
SHL ah,1; MULTIPLY by 2 for ATTRIBUTE
MOV Cs:scr_off,ah; SAVE screen Time LOCATION
MOV WORD PTR Cs:crt_port,03bah; SAVE MONO STATUS PORT ADDR
TEST al,4; CHECK for COLOR MONITOR
JNZ MONO; IF MONO, move on
ADD WORD PTR cs:scr_off,8000h; ADD COLOR offset to time offset
MOV WORD PTR Cs:crt_port,03dah; SAVE COLOR STATUS PORT ADDR
Mono:call Update;D o-I update & PRINT time
MOV Dx,offset SETUP; Get end address of NEW INTERRUPT
INT 27H; TERMINATE and remain resident
DB 117 DUP (0); Filler
Codeseg ENDS
End CLK