Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 7637 → Rev 7638

/programs/network/sntp/Tupfile.lua
0,0 → 1,2
if tup.getconfig("NO_FASM") ~= "" then return end
tup.rule("sntp.asm", "fasm %f %o " .. tup.getconfig("KPACK_CMD"), "sntp")
/programs/network/sntp/build.bat
0,0 → 1,2
fasm sntp.asm sntp
pause
/programs/network/sntp/readme_en.txt
0,0 → 1,15
Command line:
sntp host [-tz [-[+]]hh[:ss]] [-s]|[-st]|[-ss]]
host Name of SNTP server
-tz - set time zone, default is GMT +0:00
 
Syncronization, defautl is disabled
-s - system date and time
-st - system time (hours, mitutes and seconds) only
-ss - safe current hour (syncronize mitutes and seconds only)
 
Eg:
sntp pool.ntp.org -tz 1 -s
sntp 88.147.254.227 -tz 1 -ss
 
History
/programs/network/sntp/readme_ru.txt
0,0 → 1,49
SNTP êëèåíò äëÿ KolibriOS
 
Êîìàíäíàÿ ñòðîêà:
sntp host [-tz [-[+]]hh[:ss]] [-s]|[-st]|[-ss]]
host Èìÿ èëè IP àäðåñ NTP/SNTP ñåðâåðà
Îïöèè:
-tz - óñòàíîâêà ÷àñîâîãî ïîÿñà, ïî óìîë÷àíèþ GMT +0:00
 
Ñèíõðîíèçàöèÿ, ïî óìîë÷àíèþ âûêëþ÷åíà
-s - ñèñòåìíàÿ äàòà è âðåìÿ
-st - òîëüêî ñèñòåìíîå âðåìÿ (÷àñû, ìèíóòû è ñåêóíäû)
-ss - òîëüêî ñèñòåìíîå âðåìÿ ñ ñîõðàíåíèåì òåêóùåãî ÷àñà (ñèíõðîíèçèðóþòñÿ òîëüêî ìèíóòû è ñåêóíäû)
 
Ïðèìåðû:
sntp ntp1.stratum1.ru -tz 3 -s
sntp 88.147.254.227 -tz 3 -ss
 
 
Èçâåñòíûå ïðîáëåìû:
Ïîêà íå çàìå÷åíû
 
Èñòîðèÿ âåðñèé:
0.5.3
+ Èñïðàâëåíà îøèáêà êîíâåðòàöèè âðåìåíè, â ÷àñòíîñòè, ïðîáëåìà ïîÿâëÿëàñü ïðè timestamp = 0x7fffffff
+ Äîðàáîòàíà êîððåêòèðîâêà ñ ó÷åòîì ÷àñîâîãî ïîÿñà
+ Äîðàáîòàíî îòîáðàæåíèå ÷àñîâîãî ïîÿñà, òåïåðü îí îòîáðàæàåòñÿ â òîé æå ñòîêå, ÷òî è äàòà è âðåìÿ
+ Èñïðàâëåíà îøèáêà îòîáðàæåíèÿ îòðèöàòåëüíîãî ÷àñîâîãî ïîÿñà
+ Äîáàâëåíà ïðîâåðêà ÷àñîâîãî ïîÿñà
+ Äîðàáîòàíî è óëó÷øåíî îòîáðàæåíèå áîëüøèíñòâà îøèáîê (êðîìå îøèáêè óñòàíîâêè äàòû è/èëè âðåìåíè)
+ Â äèñòèðáóòèâ äîáàâëåí shell-ñêðèïò (sntp.sh) êîòîðûé ìîæíî ïðîñòî íàñòðîèòü îäèí ðàç è çàïóñòèòü
íàïðÿìóþ (áåç êîìàíäíîé ñðîêè) èç ôàéëîâîãî ìåíåäæåðà
 
0.5.2
+ Èñïðàâëåíà îøèáêà ïðè êîíâåðòàöèè äàòû ïðè âû÷èñëåíèè ìåñÿöà
- Ïîëíîñòüþ íå èñïðàâëåíû îøèáêè êîíâåðòàöèè âðåìåíè (ïðîáëåìà
ïîÿâèòñÿ ñ îðåäåëåííîé äàòû, â ÷àñòíîñòè, îøèáêà ïðîÿâëÿåòñÿ ïðè timestamp = 0x7fffffff)
 
0.5.1
+ Èñïðàâëåíà îøèáêà íåâåðíîé êîíâåðòàöèè äíÿ
+ Äîáàâëåíà êîððåêòèðîâêà âðåìåíè â ñëó÷àå íàñòóïëåíèÿ íîâûõ ñóòîê
ïðè çàäàíèè ÷àñîâîãî ïîÿñà
+ Äîðàáîòàíà âîçìîæíîñòü çàäàíèÿ IP àäðåñà õîñòà
+ Äîáàâëåíà íà÷àëüíàÿ îáðàòîòêà îøèáîê
+ Äîáàâëåíà âîçìîæíîñòü îïðåäåëåíèÿ îòñóòñâèÿ îòâåòà õîñòà,
ïîëåçíî â ñëó÷àå îøèáî÷íîãî ââîäà
0.5
Ïåðâûé ðåëèç
/programs/network/sntp/sntp.asm
0,0 → 1,905
;
; SNTP client for KolibriOS
;
; (C) 2019 Coldy
; Thank's you for use this code and software based on it!
; I will glad if it's will be helpful.
;
; Distributed under terms of GPL
;
 
format binary as ""
 
use32
org 0x0
 
db 'MENUET01' ; signature
dd 1 ; header version
dd START ; entry point
dd I_END ; initialized size
dd MEM ; required memory
dd STACKTOP ; stack pointer
dd params ; parameters
dd 0 ; path
__DEBUG__ = 1
__DEBUG_LEVEL__ = 2
 
include '../../proc32.inc'
include '../../macros.inc'
include '../../dll.inc'
include '../../struct.inc'
include '../../network.inc'
include '../../debug-fdo.inc'
include 'time.inc'
 
 
START:
; init heap
mcall 68, 11
test eax, eax
; fatal error (not enough memory)
jz exit_now
; load libraries
stdcall dll.Load, @IMPORT
test eax, eax
; fatal error(imports not loaded)
jnz exit_now
; initialize console
; push 1
; call [con_start]
push str_title
push 250
push 80
push 25
push 80
call [con_init]
;test eax, eax
; fatal error(console error)
;jnz exit
 
; setup params
call parse_params
call tz_validate
; is TZ correct?
cmp ebx,0
je @f
mov eax, ebx
mov ebx, str_err11
jmp .tz_error
@@:
; is command line correct?
cmp eax, 10
jne @f
mov ebx, str_err10
jmp .error
@@:
; empty command line (need help)?
cmp eax, 0
je @f
cinvoke con_printf, str_help
jmp exit
 
@@:
; Prepare to do query time
; Convert host name to IP address
invoke inet_addr, params
; Host name by IP address was provided?
cmp eax, -1
jne .resolved
push esp ; reserve stack place
invoke getaddrinfo, params, 0, 0, esp
; Get ptr to result addrinfo struct
pop esi
; Test for error
test eax, eax
jz @f
mov eax, 30
mov ebx, str_err3
;ret ; Error: Name not resolved!
jmp .error
@@:
mov eax, [esi+addrinfo.ai_addr]
mov eax, [eax+sockaddr_in.sin_addr]
push eax ; Store IP to stack
invoke inet_ntoa, eax
; Store string of IP
mov edx, eax
pop eax ; Load IP from stack
jmp @f
.resolved:
clear edx ; mark host is IP format
@@:
mov [sockaddr1.ip], eax
clear ebx
mov bx, [port]
;cinvoke con_printf, str_query, params, eax, ebx
cinvoke con_printf, str_query, params
cmp edx,0
je @f ; Skip IP display
cinvoke con_printf, str_ip, edx
@@:
cinvoke con_printf, str_port, ebx
; free allocated memory
invoke freeaddrinfo, esi
; Now we ready to query server
call sntp_query_time
cmp eax,1
jne @f
mov eax, 59
jmp .warning
@@:
cmp eax,2
jne @f
mov eax, 61
.warning:
cinvoke con_printf, str_warn, eax
jmp .display
@@:
cmp eax, 41
jge .error
.display:
; Tell user results
;mov esi, datetime ; ?
; Display server date and time
xor eax, eax
mov al, [esi + DateTime.sec]
push eax
mov al, [esi + DateTime.min]
push eax
mov al, [esi + DateTime.hour]
push eax
mov ax, [esi + DateTime.year]
push eax
xor eax, eax
mov al, [esi + DateTime.month]
push eax
mov al, [esi + DateTime.day]
push eax
push str_dt
call [con_printf]
add esp, 7*4
; Display timezone
cmp [tz_h],0
jne @f
cmp [tz_m],0
jne @f
jmp .no_bias
@@:
clear eax, ebx
mov ecx, str_tz
mov al, [tz_h]
test al, al
jns @f
mov byte[ecx+1],'-' ; Change sign
sub bl, al
mov al, bl
@@:
cmp [tz_m],0
jne @f
mov word[ecx+4],10 ; \n\0
@@:
mov bl, [tz_m]
cinvoke con_printf, str_tz, eax, ebx
.no_bias:
cmp [sync],0
je exit
cmp [sync], SYNC_S
jne @f
mov eax, str_s
jmp .sync_ok
@@:
cmp [sync], SYNC_ST
jne @f
mov eax, str_st
jmp .sync_ok
@@:
cmp [sync], SYNC_ST
jne .sync_ok
mov eax, str_ss
.sync_ok:
cinvoke con_printf, str_sync, eax
jmp exit
.error:
mov esi, params
.tz_error:
; Note: esi assign by parse_params
 
; cmp [notify],0
; jne @f
cinvoke con_printf, str_err, eax, esi, ebx
;jmp exit
;@@:
 
; Do call @NOTIFY
 
 
; Finally... exit!
exit:
push 0
call [con_exit]
 
exit_now:
mcall -1
; End of program
 
;notify:
 
; Time zone check helper
tz_validate:
;jmp .exit
cmp [tz_h],-12
jl .fail
cmp [tz_h],14
jg .fail
cmp [tz_m],0
je .exit
cmp [tz_m],30
jne .tz_45m
cmp [tz_h],-9
je .exit
cmp [tz_h],-3
je .exit
cmp [tz_h],3
jl .fail
je .exit
cmp [tz_h],6
jg @f
jle .exit
@@:
cmp [tz_h],9
je .exit
cmp [tz_h],10
jne .fail
je .exit
.tz_45m:
cmp [tz_m],45
jne .fail
cmp [tz_h],5
je .exit
cmp [tz_h],8
je .exit
cmp [tz_h],12
jne .fail
.exit:
clear ebx
ret
.fail:
mov ebx,11
ret
; Sycronization constants
SYNC_S = 1
SYNC_SS = 2
SYNC_ST = 3
 
parse_params:
mov esi, params
mov ebx, esi
.f00:
lodsb
cmp al, 0
jne .f01
dec esi
cmp esi, ebx
jne @f
;no params
mov eax, -1
ret
.exit:
; mark end of TZ
mov byte [ecx+1], 0
; now esi = start of TZ
mov esi,ebp
 
@@:
mov eax, 0
ret
 
.f01:
cmp al, ' '
jne .f00
; Save end of host position
mov edi, esi
dec edi
;mov byte [esi-1], 0
jmp .param
 
.param_loop:
lodsb
cmp al, 0
je @f
cmp al, ' '
jne .invalid
jmp .param
@@:
mov byte [edi], 0
jmp .exit ;ret
.param:
lodsb
cmp al, '-'
jne .invalid
 
lodsb
; cmp al, 'n'
; jne @f
; mov [notify],1
; jmp .param_loop
;@@:
; cmp al, 'p'
; je .p
cmp al, 't'
jne @f
lodsb
cmp al, 'z'
jne .invalid
je .tz
@@:
cmp al, 's'
je .sync
;.p:
; port setup
; lodsb
; cmp al, ' '
; jne .invalid
; call c2n
; test ebx, ebx
; jz .invalid
; mov [port], bx
; jmp .param_loop
.tz:
; tz setup
lodsb
cmp al, ' '
jne .invalid
; save start of TZ
;push esi
mov ebp, esi
call c2n
;cmp ebx, ebx ; 0 is possible
;jz .invalid
mov [tz_h], bl
cmp al, ':'
je .tz_m
;dec esi
jmp @f;.param_loop
 
.tz_m:
call c2n
;test ebx, ebx ; 0 is possible
;jz .invalid
mov [tz_m], bl
@@:
; save end of TZ
;push esi
mov ecx, esi
jmp .param_loop
 
.sync:
; sync setup
lodsb
cmp al, 's'
jne .st
mov [sync], SYNC_SS
jmp .param_loop
.st:
cmp al, 't'
jne .s
mov [sync], SYNC_ST
jmp .param_loop
.s:
mov [sync], SYNC_S
dec esi
jmp .param_loop
 
.invalid:
mov eax, 10
ret
 
; Helper to convert char to number
; Input:
; esi - ptr to char of digit
; Output:
; ebx - number
; Use registers (not restore):
; eax, edx
;
c2n:
xor eax, eax
xor ebx, ebx
xor edx, edx
.loop:
lodsb
test al, al
jz .done
cmp al, ' '
je .done
 
cmp al, ':'
;jne .f0
je .done1
 
;.f0:
cmp al, '+'
je .f00
cmp al, '-'
jne .f01
mov dl,1
.f00:
lodsb
.f01:
sub al, '0'
jb .fail
cmp al, 9
ja .fail
lea ebx, [ebx*4+ebx]
lea ebx, [ebx*2+eax]
jmp .loop
.fail:
xor ebx, ebx
.done:
dec esi
.done1:
cmp dl, 1
jne .ret
neg ebx
.ret:
ret
; Sync worker
; Input:
; eax - in_addr (IPv4)
; Output:
; eax - error_code
; ebx - error_string
; esi - ptr to DateTime
; Use registers (not restore):
; eax, edx, ecx, edx, esi,...
;
;sntp_sync_time:
;mov edx, eax
sntp_query_time:
 
; jmp .test
 
; Create socket
mcall socket, AF_INET4, SOCK_DGRAM, IPPROTO_IP
cmp eax, -1
jne @f
mov eax, 41
mov ebx, str_err4
ret ; Connection error (1)
@@:
;mov [socketnum], eax
;mcall connect, [socketnum], sockaddr1, 18
mov ebp, eax ; Store socket
mcall connect, ebp, sockaddr1, 18
cmp eax, -1
jne @f
mov edx, 42
mov edi, str_err4
jmp .error ; Connection error (2)
;DEBUGF 1, "Socket connected.\n"
@@:
; Query system time
;mcall 3
;mov [SystemTime], eax
mcall send, ebp, sntp_packet, SIZEOF_SNTP_PACKET, 0
cmp eax, -1
jne @f
mov edx, 43
mov edi, str_err4
jmp .error ; Connection error (3)
;DEBUGF 1, "send done.\n"
@@:
; Wait 300 msec.
mcall 5, 30
;do_recv:
mcall recv, ebp, sntp_packet, SIZEOF_SNTP_PACKET, MSG_DONTWAIT
cmp eax, -1
jne @f
mov edx, 50
mov edi, str_err5
jmp .error ; no response
@@:
test eax, eax
jnz @f
mov edx, 44
mov edi, str_err4
jmp .error ; ; Connection error (4)
 
;DEBUGF 1, "recv done.\n"
@@:
; Kiss of death?
cmp [sntp_packet.Stratum], 0
jne @f
mov edx, 60
mov edi, str_err6
jmp .error
@@:
; cmp eax, SIZEOF_SNTP_PACKET
; jne do_recv
;push sntp_packet.ReferenceID
;call [con_write_asciiz]
;invoke con_write_asciiz, str_refid
; TODO: calñ roudtrip
mov eax, [sntp_packet.TransmitTime]
bswap eax
; Bias between epoch
sub eax, 0x83AA7E80
;push eax
;cinvoke con_printf, str_t, eax
;cinvoke con_printf, str_tt, [sntp_packet.TransmitTime], [sntp_packet.TransmitTime + 32]
;pop eax
;.test:
mov ebx, datetime
; mov eax, 7fffffffh ; max timestamp
call timestamp2DateTime
; mov esi, datetime
; Calc time zone (hour only)
; mov eax, [SystemTime]
; clear ecx
; mov cl, al
; clear eax
; mov al, [esi + DateTime.hour]
; push ecx
; b2bcd
; pop ecx
; sub cl, al
;sub bh, [esi + DateTime.min]
; correct minutes
clear eax, ebx, ecx
mov al, [tz_m]
test al, al ; tz_m = 0 ?
jz .tz_h
mov bl, [tz_h] ; tz_m < 0 ? This not work with tz_h = 0!
test bl,bl
jns @f
sub [esi + DateTime.min], al
cmp [esi + DateTime.min], 0
jge @f
mov al, 60
mov cl, -1
add [esi + DateTime.min], al;bh
;mov [TimeZone], cx
;@@:
@@:
.tz_h:
; correct hour
cmp [sync], SYNC_SS
; if -ss ignore timezone for hour
je .tz_done
clear eax
mov al, [tz_h]
add al, cl
add [esi + DateTime.hour], al ;3 ; MSK = GMT +3
; Correct day & hour if prev/new day
; hour < 0 ?
mov al,[esi + DateTime.hour]
test al, al
jns @f
add [esi + DateTime.hour],24
dec [esi + DateTime.day]
jmp .tz_done
@@:
; hour >= 24 ?
cmp [esi + DateTime.hour], 24
jl .tz_done
inc [esi + DateTime.day]
mov bl, [esi + DateTime.hour]
sub ebx, 24
mov [esi + DateTime.hour],bl
.tz_done:
 
; {{
; Removed block 1.1
;}}
;clear eax, ebx, ecx
;mov al, '+'
;mov ax, [TimeZone]
;mov bl, al
;mov cl, ah
; {{
; Removed block 1.2
;}}
; FIXED: do sync before display!!!
; It's need to do sync fast ASAP
; Take out any printf from sntp_query_time!
; sync > 0 ?
cmp [sync], 0
je .nosync
;{{
; Removed block 2
; FIXME: Go it from sntp_query_time!
;}}
; Convert time to BCD
clear eax, edx
mov al, [esi + DateTime.sec]
b2bcd
mov ecx, eax
shl ecx, 16
mov al, [esi + DateTime.min]
b2bcd
mov ch, al
mov al, [esi + DateTime.hour]
b2bcd
mov cl, al
; Display BCD time
;cinvoke con_printf, str_t, ecx
; mov ecx, [ebx + DateTime.date]
; push ecx
; push str_d
; call [con_printf]
; add esp, 2*4
 
; Set time
mov eax, 22
mov ebx, 0
int 0x40
; ? error
cmp [sync], SYNC_S
jne .nosync
; Convert date to BCD
mov esi, datetime
clear eax, edx
mov al, [esi + DateTime.day]
b2bcd
mov ecx, eax
shl ecx, 16
mov al, [esi + DateTime.month]
b2bcd
mov ch, al
mov ax, [esi + DateTime.year]
sub ax, 2000
b2bcd
mov cl, al
; Display BCD date
;cinvoke con_printf, str_d, ecx
; Set date
mov eax, 22
mov ebx, 1
; mov ecx, [edx + DateTime.date]
int 0x40
; ? error
.nosync:
; Check LI field
; mov al, byte [sntp_packet]
; bt al,0
; jnc @f
; mov edx,1
;@@:
; bt ax, 1
; jnc @f
; mov edx,2
; jmp .error
@@:
clear edx, edi ; no error
.error:
; Close socket
mcall close, ebp
 
; Ignore error from close,
; but it write result to eax & ebx
; so we need wtire result here
mov eax,edx
mov ebx,edi
ret
;fail:
; cinvoke con_printf, str_err, eax, ebx
; ret
 
; data
str_title db 'SNTP client',0
str_help db 'sntp host [-tz [-[+]]hh[:ss]] [-s]|[-st]|[-ss]',10
;str_help db 'sntp host [-c][[-p] [-tz [-[+]]hh[:ss]] [-s]|[-st]|[-ss]]',10
;'-ñ Load config from Time.ini',10
;'-p Set port, default is 123',10
db 'host Name or IP address of NTP/SNTP server',10
db 'Options:',10
db '-tz Set time zone, default is GMT',10
;'-qm Try query time from master SNTP server (if stratum > 1), defautl is disabled',10
db 10
db 'Syncronization, default is disabled',10
db '-s System date and time',10
db '-st System time (hours, mitutes and seconds) only',10
db '-ss Save current hour (syncronize minutes and seconds only)',10
db 10
;-dr Display SNTP server reserence ID (if stratum = 0)
;-dt Display accurate time from SNTP request, default is enabled
;-da Display all SNTP server information
db 'Examples:',10
db 'sntp pool.ntp.org -tz 1 -s',10
db 'sntp 88.147.254.227 -tz 1 -ss',10,0
;str_badoption db 'Warning: unknown option %s, ignored',10, 0
;str_query db 'Query - %s [%s] :%i',10,0
str_query db 'Query: %s',0
str_ip db ' [%s]',0
str_port db ' :%i',10,0
;str_tt db 'Transmit time - 0x%X.%X',10,0
;str_t db 'Timestamp - %i',10,0
str_dt db 'Date & time: %i.%02i.%02i %i:%02i:%02i GMT',0 ; ' UTC %%i:%02i'
str_tz db ' +%i:%02i',10,0 ;Time zone: GMT +%i:%02i
;str_d db 'BCD date - 0x%08X',10,0
;str_t db 'BCD time - 0x%08X',10,0
str_err db 'Error: #%i, %s => %s',0
str_err10 db 'Bad command line, type ',39,'sntp',39,' for help.',10,0
str_err11 db 'Incorrect time zone! Visit https://www.timeanddate.com/time/map for details.',10,0
;str_err2 db 'Can',39,'t run @NOTIFY!',10,0
str_err3 db 'Host name not resolved!',0 ; @notify => ntp.example.com \nError 30: Host name not resolved!
str_err4 db 'Connection failed!',0 ; @notify => ntp.example.com:100 \nError 4x: Connection failed!
str_err5 db 'Host not responce!',0 ; @notify => ntp.example.com:100 \nError 50: Host not responce!
str_err6 db 'Received Kiss-o',39,'-Death (KoD) packet, repeat later or try another server!',0
 
;str_err7 db 'Server clock not syncronizing!',0 Not needed, see RFC 4330 about LI field
str_err7 db 'Clock syncronizing failed!',10,0
;str_err62 db 'Date syncronizing failed',0
str_warn db 'Warning: Last minute will have %i seconds!',10,0
str_s db 'Date & time',0
str_st db 'Time',0
str_ss db 'Mitutes & seconds',0
str_sync db '%s syncronized',10,0
datetime DateTime ?
;SystemTime dd ?
;TimeZone dw 180 ; GMT + 3
;Flags db 0
 
port dw 123
tz_h db 0
tz_m db 0
sync db 0
;notify db 0
 
sockaddr1:
dw AF_INET4
.port dw 0x7b00 ; 123 in network order (big endian)
.ip dd 0
rb 10
 
SIZEOF_SNTP_PACKET = 48
sntp_packet db 0x23 ; Li = 0 Vn = 4 Mode = 3 (client) FIX: Why 0x0b?
.Stratum db 0
.Pool db 0
.Precision db 0
.RootDelay dd 0
.RootDispersion dd 0
.ReferenceID dd 0
.ReferenceTime dq 0
.OriginateTime dq 0
.ReceiveTime dq 0
.TransmitTime dd 0
 
; import
align 4
@IMPORT:
 
library network, 'network.obj', console, 'console.obj'
;library network, 'network.obj', console, 'console.obj'
import network, \
inet_addr, 'inet_addr', \
getaddrinfo, 'getaddrinfo', \
freeaddrinfo, 'freeaddrinfo', \
inet_ntoa, 'inet_ntoa'
 
; con_start, 'START', \
import console, \
con_init, 'con_init', \
con_write_asciiz, 'con_write_asciiz', \
con_printf, 'con_printf', \
con_exit, 'con_exit', \
con_gets, 'con_gets',\
con_cls, 'con_cls',\
con_getch2, 'con_getch2',\
con_set_cursor_pos, 'con_set_cursor_pos',\
con_get_flags, 'con_get_flags'
;, \
;con_set_flags, 'con_set_flags'
 
;socketnum dd ?
 
I_END:
rb 4096
align 16
;buffer_ptr: rb BUFFERSIZE
STACKTOP:
 
MEM:
params rb 1024
;buffer_ptr: rb BUFFERSIZE
IM_END:
/programs/network/sntp/sntp.sh
0,0 → 1,3
#SHS
sntp pool.ntp.org -tz 3 -s
exit
/programs/network/sntp/time.inc
0,0 → 1,256
;
; SNTP library
;
; (C) 2019 Coldy
; Thank's you for use this code and software based on it!
; I will glad if it's will be helpful.
;
; Distributed under terms of GPL
;
 
; Inline clearing register(s)
; E.g. uses:
; clear eax ; - clearing single register
; or
; clear eax, ebx ; ... and so on - clearing multiple register
macro clear [reg] { xor reg, reg }
 
macro b2bcd
; Convert hex byte to BCD byte
; Input:
; al = number 0...99 (not checking)
; Output:
; al = number in BCD
; Algorithm:
; al = (al > 9) ? ((al / 10) * 6 + al) : al
; Use registers (not restore):
; eax, ebx, edx
{
clear ebx, edx
cmp al, 9 ; (al <= 9 ?)
jle @f
mov bl, al
mov dl, 10
div dl ; al = al/10
mov dl, 6
mul dl ; al = al*6
add al, bl ; al = al + bl
@@:
}
 
struct DateTime
struct
day db ?
month db ?
year dw ?
ends
struct
hour db ?
min db ?
sec db ?
ends
ends
 
 
proc DateTime2bcd
;
; Input:
; eax => pointer to DateTime (UNIX time since 1.1.1970 00:00:00 GMT )
;
; Output:
; eax => time in ÂÑD format
; edx => date in BCD format
locals
date dd 0
time dd 0
endl
mov esi, eax
clear eax, ebx, ecx
mov al, [esi + DateTime.day]
cmp al, 9
jle @f
mov bl, al
mov cl, 10
div cl
mul bl, cl
add al, bl
@@:
endp
 
 
 
YEAR_EPOCH = 1970
YEAR_FIRST_LEAP_YEAR = 1972
SEC_IN_MINUTE = 60
SEC_IN_HOUR = (SEC_IN_MINUTE * 60)
SEC_IN_DAY = (SEC_IN_HOUR * 24)
SEC_IN_YEAR = (SEC_IN_DAY * 365)
proc timestamp2DateTime
;
; Input:
; eax => timestamp (UNIX time since 1.1.1970 00:00:00 GMT )
; ebx => pointer to DateTime
;
; Output:
; none
;
; Use registers (not restore):
;
; History:
; 14.04.2019 Bug fixed: Incorrect output day (-1 day error)!!!
; 19.04.2019 Bug fixed: Incorrect convert of time with maximum UNIX time (0x7ffffff)
;
; Known isuues:
; Not yet seen :)
;
locals
timestamp dd ?
years dw ?
lyears dw ?
;year dw ?
;_ts dd 0
;ts dd 0
MonthDays db 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
;month db ?
endl
;pusha
mov [timestamp], eax
mov esi, ebx ; ESI => pointer to DateTime
; Calculate total years since year epoch
cdq
mov ebx, SEC_IN_YEAR + SEC_IN_DAY/4
idiv ebx
mov [years], ax ; (AX => years)
; Calculate current year
add ax, YEAR_EPOCH
mov [esi + DateTime.year], ax ; (AX => year)
; Calculate leap years since year epoch
;xor ecx, ecx ; Clear
;mov cx, bx
;Fix: -1 day error
dec ax
sub ax, YEAR_FIRST_LEAP_YEAR ; - 1
clear ebx, edx
mov bx, 4
idiv bx
cmp [esi + DateTime.year], YEAR_FIRST_LEAP_YEAR + 1
js @f
inc al
@@:
mov [lyears], ax
; Drop years seconds
mov eax, [timestamp]
clear ebx
mov bx, [years]
sub bx, [lyears]
mov ecx, SEC_IN_YEAR
imul ebx, ecx ; ebx => (years - lyears) * SEC_IN_YEAR
clear ecx
mov cx, [lyears]
mov edx, SEC_IN_YEAR + SEC_IN_DAY
imul ecx, edx ; cx => lyears * (SEC_IN_YEAR + SEC_IN_DAY)
add ebx, ecx ; bx => (years - lyears) * SEC_IN_YEAR + lyears * (SEC_IN_YEAR + SEC_IN_DAY)
sub eax, ebx
mov [timestamp], eax
;Is leap year?
clear ecx, edx
mov cx, [esi + DateTime.year]
mov eax, ecx
mov ebx, 4
div bx
cmp dx, 0
je leap
mov ax, cx
mov bx, 100
div bx
cmp dx, 0
je leap
mov ax, cx
mov bx, 400
clear dx
div bx
cmp dx, 0
jmp @f
leap:
; Add +1 day in february if leap year
inc [MonthDays + 1]
@@:
; Calculate current month
clear eax, ecx, edi ; _ts, ts, month (Bug? => ecx != 0)
caclmonth:
clear ebx
mov bl, [MonthDays + edi]
imul ebx, SEC_IN_DAY
inc edi
add eax, ebx
 
; {{ 19.4.2019 Bug was somewhere here
cmp edi, 12
je @f
; TODO: if edi >= 12 then error!
;mov eax, -1
;mov edx, 0
;ret
;@@:
cmp eax, [timestamp]
jge @f ; Bug? => jg @f
mov ecx, eax
jmp caclmonth
; }}
@@:
; Drop months seconds
sub [timestamp], ecx
mov eax, edi
mov [esi + DateTime.month], al
; Calculate elapsed day
mov eax, [timestamp]
clear edx
mov ebx, SEC_IN_DAY
idiv ebx ; eax => day
; Drop days seconds
imul ebx, eax, SEC_IN_DAY
sub [timestamp],ebx
; Correct current day
add eax, 1 ; eax => current day
mov [esi + DateTime.day], al
; Calculate current hour
mov eax, [timestamp]
clear edx
mov ebx, SEC_IN_HOUR
idiv ebx
mov [esi + DateTime.hour], al
; Drop hours seconds
imul ebx, eax, SEC_IN_HOUR
sub [timestamp],ebx
mov eax, [timestamp]
clear edx
mov ebx, SEC_IN_MINUTE
idiv ebx
mov [esi + DateTime.min], al
; Drop minutes seconds
imul ebx, eax, SEC_IN_MINUTE
sub [timestamp],ebx
mov eax, [timestamp]
mov [esi + DateTime.sec], al
;popa
ret
endp