diff --git a/programs/network/sntp/Tupfile.lua b/programs/network/sntp/Tupfile.lua new file mode 100644 index 0000000000..1bb94d28b1 --- /dev/null +++ b/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") diff --git a/programs/network/sntp/build.bat b/programs/network/sntp/build.bat new file mode 100644 index 0000000000..476008cf9c --- /dev/null +++ b/programs/network/sntp/build.bat @@ -0,0 +1,2 @@ +fasm sntp.asm sntp +pause \ No newline at end of file diff --git a/programs/network/sntp/readme_en.txt b/programs/network/sntp/readme_en.txt new file mode 100644 index 0000000000..3979440f21 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/programs/network/sntp/readme_ru.txt b/programs/network/sntp/readme_ru.txt new file mode 100644 index 0000000000..4d996e7e6a --- /dev/null +++ b/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 + Первый релиз \ No newline at end of file diff --git a/programs/network/sntp/sntp.asm b/programs/network/sntp/sntp.asm new file mode 100644 index 0000000000..7fce62394f --- /dev/null +++ b/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: \ No newline at end of file diff --git a/programs/network/sntp/sntp.sh b/programs/network/sntp/sntp.sh new file mode 100644 index 0000000000..b7bae9e339 --- /dev/null +++ b/programs/network/sntp/sntp.sh @@ -0,0 +1,3 @@ +#SHS +sntp pool.ntp.org -tz 3 -s +exit \ No newline at end of file diff --git a/programs/network/sntp/time.inc b/programs/network/sntp/time.inc new file mode 100644 index 0000000000..211ff6d297 --- /dev/null +++ b/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 \ No newline at end of file