;**************************************************************************************** ; ; Kalender- u. Zeitfunktionen ; ; H.Gröger, 2008 ; ;********************* Funktionen ******************************************************* ; ;------------------------- Datum -------------------------------------------------------- ; ; DayOftheWeek ; Isleapyear ; IncDay ; NextDay ; NextWeek ; NextMonth ; NextYear ; EndOfTheWeek ; GetEasternSunday ; ;------------------------- Zeit --------------------------------------------------------- ; ; IncSecond ; ;----------------------Unterprogramme --------------------------------------------------- ; ; Mod_Seven ; DivMod19 ; DivMod100 ; DivMod100s ; MulAdd100 ; DivMod400 ;********************* Compiler ********************************************************* ; ; Mit den .EQU-Anweisungen können einzelne Funktionen oder ; Speicher aktiviert oder deaktiviert werden. ; .equ use_DayOftheWeek = 1 .equ use_Isleapyear = 1 .equ use_IncDay = 1 .equ use_NextDay = 1 .equ use_Next_Week = 1 .equ use_NextMonth = 1 .equ use_NextYear = 1 .equ use_EndOfTheWeek = 1 .equ use_GetEasternSunday = 1 .equ use_IncSecond = 1 .equ use_DayNames = 1 .equ use_Fix_Days = 1 .equ use_MulAdd100 = 1 .equ use_dtram = 1 ;---------------------------------------------------------------------------------------- .equ _Isleapyear =(use_IncDay==1)|(use_NextDay==1)|(use_Isleapyear==1)|(use_Next_Week==1)|(use_NextMonth) .equ _IncDay =(use_IncDay==1)|(use_NextDay==1) .equ _GetMonthTab =(use_IncDay==1)|(_IsLeapYear==1)|(use_Next_Week==1) ;********************* RAM ************************************************************** .dseg .if use_dtram holidays: .byte 30 dt_day: .byte 1 dt_month: .byte 1 dt_year: .byte 2 dt_res_day: .byte 1 dt_res_month: .byte 1 dt_res_year: .byte 2 dt_res_dayow: .byte 1 dt_second: .byte 1 dt_minute: .byte 1 dt_hour: .byte 1 .endif ;********************* Programm ********************************************************* .cseg ;---------------------------------------------------------------------------------------- .if (use_DayOftheWeek == 1)|(use_GetEasternSunday) ; ; DayOfTheWeek gibt den Wochentag zu einem Datum zurück ; ; in: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jahr ; ; out: r16 Tag (Mo=0,Di=1...So=6) ; DayOftheWeek: push r14 push r15 push r17 push ZL push ZH lds r16,dt_day rcall mod_seven mov r14,r16 ; r14 -> Tageszahl (Tag mod 7) clr r17 lds r16,dt_month ldi ZL,Low((monthnum<<1)-1) ldi ZH,High((monthnum<<1)-1) add ZL,r16 adc ZH,r17 lpm r15,Z ; r15 -> Monatszahl (aus Tabelle) lds r16,dt_year+1 lds r17,dt_year ; Jahr lds ZL,dt_month ; Monat clr ZH cpi ZL,3 ; wenn Monat<3 -> Jahr-1 brcc DayOfTheWeek10 ; -> Jan/Feb letzte Monate des Vorjahrs subi r16,1 sbc r17,ZH DayOfTheWeek10: rcall divmod100 ; r16=Jahr(0..99), r17=Jahrhundert andi r17,0b00000011 ; JH mod 4 ldi ZL,3 sub ZL,r17 ; 2*(3-(JH mod 4)) lsl ZL ; ZL -> Jahrhundertzahl mov r17,r16 ; Jahr merken lsr r16 lsr r16 ; Jahr div 4 add r16,r17 ; (JR+(JR div 4)) mod 7 rcall mod_seven ; r16 -> Jahreszahl ldi r17,6 add r16,r17 ; wegen ISO8601 (1WT=Montag) add r16,r14 adc r16,r15 adc r16,ZL rcall mod_seven ; WT=(TZ+MZ+JZ+JHZ) mod 7 pop ZH pop ZL pop r17 pop r15 pop r14 ret ; Tabelle für Wochentag ; J F M A M J J A S O N D monthnum: .db 1,4,3,6,1,4,6,2,5,0,3,5 .endif ;---------------------------------------------------------------------------------------- .if (use_Isleapyear==1)|(use_IncDay==1)|(use_NextDay==1) ; IsLeapYear gibt an, ob es sich bei einem ; bestimmten Jahr um ein Schaltjahr handelt. ; ; in : r17:r16 Jahr ; out : Z=0 Leapyear ; Z=1 NormalYear ; ; LY= ((Y mod 4)=0) & ((Y mod 400)=0 | (Y mod 100)<>0) IsLeapYear: push r16 push r17 push r18 lds r16,dt_year+1 lds r17,dt_year ; Jahr mov r18,r16 andi r18,0b00000011 ; durch 4 ohne Rest teilbar brne isleapyear10 ; wenn nicht, kein Schaltjahr rcall divmod400 ; durch 400 teilbar mov r18,r16 or r18,r17 ; wenn Rest=0 Schaltjahr breq isleapyear10 rcall divmod100s ; Rest durch 100 teilen clr r18 ; für Z-Flag tst r16 ; wenn Rest=0 kein Schaltjahr brne isleapyear10 ser r18 ; für Z-Flag Isleapyear10: tst r18 ; Z-Flag setzen pop r18 pop r17 pop r16 ret .endif ;---------------------------------------------------------------------------------------- .if (_IncDay==1)|(use_Next_Week)|(use_GetEasternSunday==1) ; IncDay gibt ein Datum zurück, ; das um eine bestimmte Anzahl von Tagen verschoben wurde ; ; in: r16 Anzahl der Tage (max. +/- 28) ; dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht IncDay: push r16 push r17 push ZL push ZH push r16 tst r16 breq IncDay90 ; wenn Null fertig IncDay10: rcall GetMonthTab lds r16,dt_month clr r17 add ZL,r16 adc ZH,r17 lds r16,dt_day ; aktueller Tag dec r16 ; 1 -> 0 pop r17 add r16,r17 ; Tage addieren brmi IncDay20 ; wenn Summe negativ IncDay15: lpm r17,Z ; Tage im Monat cp r16,r17 ; 0? lds ZH,dt_Year ; nein->vorheriges Jahr lds ZL,dt_Year+1 sbiw ZH:ZL,1 ; dec(Jahr) sts dt_Year,ZH sts dt_Year+1,ZL ; speichern ldi r17,12 ; Dezember IncDay30: sts dt_month,r17 ; Monat speichern push r16 clr r16 rcall GetMonthTab add ZL,r17 adc ZH,r16 lpm r17,Z ; Tage im Monat pop r16 add r16,r17 ; Rest vom Monatsende abziehen rjmp IncDay80 IncDay60: sts dt_year,ZH sts dt_year+1,ZL IncDay70: sts dt_month,r17 IncDay80: inc r16 sts dt_day,r16 IncDay90: pop ZH pop ZL pop r17 pop r16 ret .endif ;---------------------------------------------------------------------------------------- .if use_NextDay == 1 ; NextDay gibt das Datum des nachfolgenden Tages zurück ; ; in: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; NextDay: push r16 ldi r16,1 rcall IncDay pop r16 ret .endif ;---------------------------------------------------------------------------------------- .if (use_Next_Week == 1)|(use_IncDay==1) ; Next_Week gibt das Datum des Wochentages der nächsten ; Woche zurück ; ; in: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; NextWeek: push r16 ldi r16,7 rcall IncDay pop r16 ret .endif ;---------------------------------------------------------------------------------------- .if use_NextMonth == 1 ; NextMonth gibt das Datum des gleichen Tages des nächsten ; Monats zurück. ; ; in: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht NextMonth: push r16 push r17 push ZL push ZH lds r17,dt_month inc r17 cpi r17,13 ; >12? brcs next_month10 ldi r17,1 ; dann Januar lds ZH,dt_year lds ZL,dt_year+1 adiw ZH:ZL,1 ; und ein Jahr weiter sts dt_year,ZH sts dt_year+1,ZL next_month10: sts dt_month,r17 rcall GetMonthTab clr r16 add ZL,r17 add ZH,r16 next_month20: lpm r17,Z ; Anzahl Tage/Monat inc r17 lds r16,dt_day cp r16,r17 ; Tageszahl > Tage/Monat brcs next_month30 mov r16,r17 dec r16 ; wenn ja, Tag=letzter Tag im Monat next_month30: sts dt_day,r16 pop ZH pop ZL pop r17 pop r18 ret .endif ;---------------------------------------------------------------------------------------- .if use_NextYear == 1 ; NextMonth gibt das Datum des gleichen Tages des nächsten ; Jahres zurück. ; ; in: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht NextYear: push r16 push r17 push ZL push ZH ldi ZL,1 ldi ZH,0 lds r17,dt_Year lds r16,dt_Year+1 add r16,ZL adc r17,ZH ; Jahr+1 sts dt_Year,r17 sts dt_Year+1,r16 lds ZL,dt_Month ; Monat cpi ZL,2 ; Monat=Februar? brne nextyear10 ; wenn nein,fertig lds ZL,dt_day ; Tag cpi ZL,29 ; Tag <= 28? brcs nextyear10 ; wenn ja,fertig ldi ZH,28 ; 28 Tage sts dt_day,ZH ; speichern nextyear10: pop ZH pop ZL pop r17 pop r18 ret .endif ;---------------------------------------------------------------------------------------- .if (use_EndOfTheWeek==1)|(use_GetEasternSunday==1) ; ; EndOfTheWeek gibt das Datum zurück, ; das den letzten Tages der Woche (Sonntag) repräsentiert ; ; in: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jaht ; EndOfTheWeek: push r16 push r17 rcall DayOfTheWeek cpi r16,6 breq EndOfTheWeek10 ldi r17,6 sub r17,r16 mov r16,r17 rcall IncDay EndOfTheWeek10: pop r17 pop r16 ret .endif ;---------------------------------------------------------------------------------------- .if use_GetEasternSunday == 1 ; ; GetEasternSunday gibt das Datum des Ostersonntags ; eines Jahres zurück (gültig 1900..2199) ; ; in: dt_year:dt_year+1 Jahr ; ; out: dt_day Tag ; dt_month Monat ; dt_year:dt_year+1 Jahr ; GetEasternSunday: push r16 push r17 push ZL push ZH lds r17,dt_year lds r16,dt_year+1 ldi ZL,Low(1900) ldi ZH,High(1900) sub r16,ZL sbc r17,ZH ; Jahr - 1900 rcall divmod19 ; Modulo 19 nach r16 clr r17 ldi ZL,low(eastern_tab<<1) ldi ZH,high(eastern_tab<<1) add ZL,r16 adc ZH,r17 lpm r16,Z ; Tageszahl ldi r17,4 ; April cpi r16,22 ; Tageszahl < 22 brcs get_eastern10 dec r17 ; wenn nicht -> März get_eastern10: sts dt_day,r16 sts dt_month,r17 ; Speichern für 'EndOfTheWeek' rcall DayOfTheWeek cpi r16,6 ; Sonntag? brne get_eastern20 push r16 ldi r16,1 rcall IncDay ; Wenn ja, einen Tag weiter pop r16 get_eastern20: rcall EndOfTheWeek ; Sonntag der Woche pop ZH pop ZL pop r17 pop r16 ret ; Tabelle zur Osterberechnung eastern_tab: .db 14,3,23,11,31,18,8,28,16,5,25,13,2,22,10,30,17,7,27,00 .endif ;---------------------------------------------------------------------------------------- .if use_IncSecond == 1 ; IncSecond gibt die um eine Sekunde erhöhte ; Zeit zurück ; ; in: dt_second Sekunde ; dt_minute Minute ; dt_hour Stunde ; ; out: dt_second Sekunde ; dt_minute Minute ; dt_hour Stunde ; wenn 00:00:00 -> CY=1 sonst CY=0 ; IncSecond: push r16 push r17 push r18 ser r18 lds r16,dt_second inc r16 cpi r16,60 brcs incsecond30 clr r16 lds r17,dt_minute inc r17 cpi r17,60 brcs incsecond20 clr r17 lds r18,dt_hour inc r18 cpi r18,24 brcs incsecond10 clr r18 incsecond10: sts dt_hour,r18 incsecond20: sts dt_minute,r17 incsecond30: sts dt_second,r16 add r16,r17 add r16,r18 clc ; CY=0 tst r16 brne incsecond40 ; wenn 00:00:00 ->CY=1 sec ; Übertrag für nächsten Tag incsecond40: pop r18 pop r17 pop r16 ret .endif /* Beispiel rcall Inc_Second brcc abcd rcall NextDay abcd: .... */ ;*************************** Tabellen *************************************************** .if use_DayNames ; Tagesnamen day_names_ger: .db "MoDiMiDoFrSaSo" day_names_eng: .db "MoTuWeThFrSaSo" days_long_ger: .db "Montag",$FF,0,0,0,0,0 .db "Dienstag" ,$FF,0,0,0 .db "Mittwoch" ,$FF,0,0,0 .db "Donnerstag" ,$FF,0 .db "Freitag" ,$FF,0,0,0,0 .db "Samstag" ,$FF,0,0,0,0 .db "Sonntag" ,$FF,0,0,0,0 days_long_eng: .db "Monday",$FF,0,0,0,0,0 .db "Tuesday" ,$FF,0,0,0,0 .db "Wednesday" ,$FF,0,0 .db "Thursday" ,$FF,0,0,0 .db "Friday",$FF,0,0,0,0,0 .db "Saturday" ,$FF,0,0,0 .db "Sunday",$FF,0,0,0,0,0 .endif .if use_Fix_Days ; Fixe Feiertage fixed_days: .db 1, 1 ;Neujahr .db 6, 1 ;Heil.dr.Könige .db 0, 0 ;Karfreitag .db 0, 0 ;Ostermontag .db 0, 0 ;1.Mai .db 0, 0 ;Himmelfahrt .db 0, 0 ;Pfingstmontag .db 0, 0 ;Fronleichnam .db 15, 8 ;Maria Himmelfahrt .db 3,10 ;Tag d.d.Einheit .db 31,10 ;Reformationstag .db 1,11 ;Allerheiligen .db 0, 0 ;Buß-u.Bettag .db 25,12 ;1.Weihnachtstag .db 26,12 ;2.Weihnachtstag .endif ;********************* Subroutinen ****************************************************** .if (use_IncDay==1)|(_IsLeapYear==1)|(use_Next_Week==1) GetMonthTab: ; ldi ZL,low(nyear) ; ldi ZH,High(nyear) ; lsl ZL ; rol ZH ; sbiw ZH:ZL,1 ; rcall IsLeapYear ; brne GetMonthTab10 ; adiw ZH:ZL,12 ldi ZL,Low(month_norm*2) ldi ZH,High(month_norm*2) sbiw ZH:ZL,1 rcall IsLeapYear brne GetMonthTab10 adiw ZH:ZL,12 GetMonthTab10: ret ; Tage im Monat ; J F M A M J J A S O N D month_norm: .db 31,28,31,30,31,30,31,31,30,31,30,31 lyear: .db 31,29,31,30,31,30,31,31,30,31,30,31 .endif ;---------------------------------------------------------------------------------------- .if (use_GetEasternSunday==1)|(use_DayOftheWeek==1) ; ; Modulo 7 ; ; r16= r16 mod 7 mod_seven: subi r16,7 brcc mod_seven subi r16,-7 ret .endif ;---------------------------------------------------------------------------------------- .if (use_GetEasternSunday == 1) ; ; Division/Modulo 19 ; ; in : r17 High(X) ; r16,Low(X) ; ; out: r17 = X div 19 ; r16 = X mod 19 divmod19: push r18 push r19 push r20 clr r18 ldi r19,Low(190) ldi r20,High(190) rjmp divmod19_20 divmod19_10: subi r18,-10 divmod19_20: sub r16,r19 sbc r17,r20 brcc divmod19_10 add r16,r19 ; Modulo 190 ldi r19,19 rjmp divmod19_40 divmod19_30: inc r18 divmod19_40: sub r16,r19 brcc divmod19_30 add r16,r19 mov r17,r18 pop r20 pop r19 pop r18 ret .endif ;---------------------------------------------------------------------------------------- .if use_MulAdd100 == 1 ; MulAdd100 führt eine Multiplkation mit 100 ; und eine anschliessende Additin durch ; ; in: r17 Multiplikant ; r16 Summand ; ; out: r17:r16 = 100*r17+r16 ; ; Formel: 100*x= 64*x+32*x+4*x ; muladd100: push r18 push r19 mov r18,r16 clr r19 ; Null für 'adc' mov r16,r17 clr r17 lsl r16 rol r17 lsl r16 rol r17 add r18,r16 adc r19,r17 ; +4*x lsl r16 rol r17 lsl r16 rol r17 lsl r16 rol r17 add r18,r16 adc r19,r17 ; +32*x lsl r16 rol r17 add r18,r16 adc r19,r17 ; +64*x movw r17:r16,r19:r18 pop r19 pop r18 ret .endif ;---------------------------------------------------------------------------------------- .if (use_GetEasternSunday==1)|(use_DayOftheWeek==1) ; Division/Modulo 100 ; ; in : r17 High(X) ; r16,Low(X) ; ; out: r17 = X div 100 ; r16 = X mod 100 ; divmod100: push r18 push r19 push r20 clr r18 ldi r19,Low(1000) ldi r20,High(1000) rjmp divmod100_20 divmod100_10: subi r18,-10 divmod100_20: sub r16,r19 sbc r17,r20 brcc divmod100_10 add r16,r19 adc r17,r20 tst r17 breq divmod100_50 ldi r19,Low(500) ldi r20,high(500) rjmp divmod100_40 divmod100_30: subi r18,-5 divmod100_40: sub r16,r19 sbc r17,r20 brcc divmod100_30 add r16,r19 adc r17,r20 divmod100_50: ldi r19,100 clr r20 rjmp divmod100_70 divmod100_60: inc r18 divmod100_70: sub r16,r19 sbc r17,r20 brcc divmod100_60 add r16,r19 mov r17,r18 pop r20 pop r19 pop r18 ret .endif ;---------------------------------------------------------------------------------------- .if (_Isleapyear==1)|(use_IncDay==1) ; Division/Modulo 100 ; (schnellere Variante für kleine Zahlen) ; ; in : r17 High(X) ; r16,Low(X) ; ; out: r17 = X div 100 ; r16 = X mod 100 divmod100s: push r18 push r19 push r20 clr r18 ldi r19,100 clr r20 rjmp divmod100s20 divmod100s10: inc r18 divmod100s20: sub r16,r19 sbc r17,r20 brcc divmod100s10 add r16,r19 mov r17,r18 pop r20 pop r19 pop r18 ret .endif ;---------------------------------------------------------------------------------------- .if (_Isleapyear ==1)|(use_IncDay==1) ; Division/Modulo 400 ; ; in : r17 High(X) ; r16,Low(X) ; ; out: r17 = X div 400 ; r16 = X mod 400 divmod400: push r19 push r20 clr r18 ldi r19,Low(400) ldi r20,High(400) rjmp divmod400_20 divmod400_10: inc r18 divmod400_20: sub r16,r19 sbc r17,r20 brcc divmod400_10 add r16,r19 adc r17,r20 pop r20 pop r19 ret .endif ;******************************************** EOF *************************************** /* ; Nachfolgende 2 Macros in eine separate Datei (im Beispiel 'dateTime.mac') ; kopieren und an den Anfang des Hauptprogramms stellen. Oder vor das Haupt- ; programm kopieren. .macro load_date ; Tag, Monat, Jahr ldi r16,@0 sts dt_day,r16 ldi r16,@1 sts dt_month,r16 ldi r16,low(@2) sts dt_year+1,r16 ldi r16,High(@2) sts dt_year,r16 .endmacro .macro load_time ; Sekunde, Minute, Stunde ldi r16,@0 sts dt_second,r16 ldi r16,@1 sts dt_minute,r16 ldi r16,@2 sts dt_hour,r16 .endmacro */ ;**************************************************************************************** /* ; Testprogramm ; ; Erzeugt Tag und Monat aller Feiertage eines Jahres ; in 'holidays. ; ; ; .include "m8def.inc" .include "dateTime.mac" ; Macros .cseg RESET: ldi r16,high(RAMEND) out SPH,r16 ldi r16,Low(RAMEND) out SPL,r16 load_date 29,2,2008 ; aktuelles Datum 20.2.2008 ldi ZL,Low(fixed_days<<1) ldi ZH,High(fixed_days<<1) ldi YL,Low(holidays) ldi YH,High(holidays) ldi r16,30 load: lpm r17,z+ st Y+,r17 dec r16 brne load ; Feste Feiertage laden. ldi YL,Low(holidays+4) ldi YH,High(holidays+4) ; erster beweglicher Feiertag rcall GetEasternSunday ; Ostersonntag berechnen ldi r16,-2 ; 2 Tage vor Ostersonntag rcall incday lds r16,dt_day lds r17,dt_month st Y+,r16 st Y+,r17 ; Karfreitag ldi r16,3 ; 1 Tag nach Ostersonntag rcall incday lds r16,dt_day lds r17,dt_month st Y+,r16 st Y+,r17 ; Ostermontag ldi r16,1 ldi r17,5 st Y+,r16 st Y+,r17 ; 1.Mai ldi r16,20 ; 39 Tage nach Ostersonntag rcall incday ldi r16,18 rcall incday lds r16,dt_day lds r17,dt_month st Y+,r16 st Y+,r17 ; Himmelfahrt ldi r16,11 ; 50 Tage nach Ostersonntag rcall incday lds r16,dt_day lds r17,dt_month st Y+,r16 st Y+,r17 ; Pfingstmontag ldi r16,10 ; 10 Tage nach PfingstMontag rcall incday lds r16,dt_day lds r17,dt_month st Y+,r16 st Y+,r17 ; Fronleichnam adiw YH:YL,8 ; nächster beweglicher Feiertag load_date 24,12,2008 ; Mittwoch der 5.Woche rcall EndOfTheWeek ; vor dem 25.12. ldi r16,-20 rcall incday ldi r16,-19 rcall incday lds r16,dt_day lds r17,dt_month st Y+,r16 st Y+,r17 ; Buß- und Bettag end: rjmp end .include "DateTime.asm" */