;****************************************************************** ;* * ;* Funktionen zur Berechnung des 1. Tag im Jahr (CurrentDoomDay), * ;* Tag des Jahres (DayOfYear), Wochentag (GetWeekDay), * ;* Kalenderwoche (GetCalWeek) und Datum im März und Oktober zur * ;* Sommer-/Winterzeitumstellung (SetSummerDate). * ;* * ;* 2005 by Andreas Kassner * ;* * ;****************************************************************** ;Berechnung von Wochentag, Kalenderwoche, ;Datum zur Sommer- und Winterzeitumstellung. ;Dies ist die Routine, welche bei Aufruf alle Funktionen ;durchgeht und die Ergebnisse in das Array im SRAM schreibt. SetWDCWSW: clr null ldi yl,low(DateArray);Y auf Datumarray setzen. ldi yh,high(DateArray) ldd JahrL,y+Jahr ;Aktuelles Jahr lesen. ldd JahrH y+Jahr+1 ldd Month,y+Monat ;Aktuellen Monat lesen. ldd Date,y+Datum ;Aktuelles Datum lesen. rcall GetWeekDay ;Wochentag berechnen std y+WTag,WeekDay ;und in WeekDay schreiben. rcall GetCalWeek ;In Div7Res (Result) ist die Kalenderwoche zum prüfen. std y+Woche,CalWeek ;Kalenderwoche schreiben. rcall SetSummerDate ;Sommer- und Wintertzeitumstellung berechnen. ret .def null =r2 .def Jahrhdt =r16 .def JhdtJahr=r17 .def WeekDay =r16 .def DoomDay =r21 .def Month =r22 .def Date =r23 .def CalWeek =r22 .def DOYL =r24 .def DOYH =r25 .def JahrL =r26 .def JahrH =r27 ;Sommer-/Winterzeitumstellung berechnen ;Es wird der letzte Sonntag im März für ;die Sommerzeitumstellung ermittelt. ;3 Datümer später im Oktober erfolgt die ;Winterzeitumstellung außer es wird ein ;Datum > 31 ermittelt. ;Dann sind es 4 Datümer vorher als im März. ;Z. B.: ;März/Oktober ; 27 30 ; 28 31 ; 29 25 ; 30 26 SetSummerDate: ldi Month,3 ;Monat März setzen ldi Date,31 ;Datum 31 setzen rcall GetWeekDay ;Wochentag in Verbindung des aktuellen Jahres ermitteln. subi WeekDay,6 ;Sonntag subtrahieren. brcc STime1 ;Bei Negativ oder <> 0 ist der 31. kein Sonntag subi WeekDay,-7 ;Bei Negativ mit 7 Tagen ausgleichen. STime1: ldi Date,31 ;Vom 31. den ermittelten sub Date,WeekDay ;Wochentag subtrahieren. std y+SZeitDatum,Date ;Und im SRAM schreiben. subi Date,-3 ;3 Tage später im Oktober ist Winterzeitumstellung. cpi Date,32 ;Wurde mehr als der 31. berechnet? brlo STime2 subi Date,7 ;Dann 7 (4) Tage zurück im Oktober. STime2: std y+WZeitDatum,Date ;Winterzeitumstellung schreiben. ret ;Wochentag berechnen GetWeekDay: rcall CurrentDoomDay ;1. Tag des Jahres (Doomday) berechnen. rcall DayOfYear ;Tag des aktuellen Jahres berechnen. add DOYL,DoomDay ;Doomday + Tag des Jahres adc DOYH,null sbiw DOYL,1 ;-1 zum Ausgleich. movw Div7DDL,DOYL ;Tag des Jahres in Register für Div7 (Division / 7). rjmp Div7 ;(Tag des Jahres + Doomday - 1) / 7 ;Kalenderwoche berechnen GetCalWeek: mov CalWeek,Div7Res cpi DoomDay,4 ;Ist der 1.1. < Freitag? brsh GCW1 ;Wenn nicht, dann weiter. inc CalWeek ;Sonst Woche + 1. GCW1: tst CalWeek ;Ist die Woche bei 0 geblieben (letzte KW im Vorjahr)? brne GCW3 ;Wenn nicht, dann weiter. ldi CalWeek,52 ;Woche 52 vom Vorjahr setzen. cpi DoomDay,4 ;Beginnt das Jahr mit einem Donnerstag? breq GCW2 ;Wenn ja, dann Woche 53 setzen. sbiw JahrL,1 ;Oder beginnt das rcall CurrentDoomDay ;Vorjahr mit cpi DoomDay,3 ;einem Donnerstag? brne GCWEnd ;Wenn nicht, dann bleibt es bei Woche 52. GCW2: ldi CalWeek,53 ;Woche 53 setzen. rjmp GCWEnd GCW3: cpi CalWeek,52+1 ;Wurde die Woche 53 ermittelt? brlo GCWEnd ;Wenn kleiner, dann Ende. cpi DoomDay,3 ;Beginnt das Jahr mit einem Donnerstag? breq GCWEnd ;Wenn ja, dann bei Woche 53 bleiben. adiw JahrL,1 ;Oder beginnt das rcall CurrentDoomDay ;nächste Jahr cpi DoomDay,4 ;mit einem Freitag? breq GCWEnd ;Dann bei Woche 53 bleiben. ldi CalWeek,1 ;Ansonsten letzte Jahreswoche auf 1 setzen. GCWEnd: ret ;Berechnung des Tag des Jahres DayOfYear: clr DOYL ;Tag des Jahres löschen. clr DOYH dec Month ;Monat - 1 breq DOY3 ;Monat = 0? Dann nur Datum addieren. cpi Month,2 ;Monat >= Februar? brlo DOY1 rcall TestSJ ;Dann auf Schaltjahr testen. adc DOYL,null ;Tag des Jahres + Schaltjahr. DOY1: ldi zl,low(Monate*2) ;Z auf Liste der Monatstage setzen. ldi zh,high(Monate*2) DOY2: lpm r16,z+ ;Tagesanzahl lesen add DOYL,r16 ;und auf Tag des Jahres addieren. adc DOYH,null dec Month ;Monatszähler - 1. brne DOY2 ;Wiederhohlen bis Monat = 0. DOY3: add DOYL,Date ;Datum auf Tag des Jahres addieren. adc DOYH,null ret Monate: .db 31,28,31,30,31,30,31,31,30,31,30 CurrentDoomDay: movw DD16uL,JahrL ;Aktuelles Jahr als Dividend. ldi DV16uL,100 ;Divisor 100 setzen. ldi DV16uH,0 ;Divisor 100 setzen. rcall Div16u ;Jahr / 100 = Jahrhundert in Jahrhdt (r16) ;und Jahr im Jahrhundert in DRem16u (Rest).. mov JhdtJahr,DRem16uL ;Restwert der Division in JhdtJahr. mov zl,Jahrhdt ;Z-low aus Jahrhundert setzen. andi zl,3 ;Rest aus Jahrhundert / 4.als Zeiger. clr zh ;Z-high auf 0. subi zl,low(-CenturyDoomDay*2) ;Tabelle der Jahrhundertwochentage addieren. sbci zh,high(-CenturyDoomDay*2) ;Tabelle der Jahrhundertwochentage addieren. lpm DoomDay,z ;Jahrhundertwochentag in WeekDay add DoomDay,JhdtJahr ;WeekDay + Jahre des Jahrhunderts. subi JhdtJahr,1 ;Für die Jahrhunderte 1, 2 und 3 Jahrhundertjahre - 1. brcs CDD2 andi Jahrhdt,3 ;Ist es das erste Jahrhundert innerhalb 4 Jahrhunderte? brne CDD1 subi JhdtJahr,-4 ;Wenn ja, dann Jahrhundertjahre + 4 (+3). CDD1: lsr JhdtJahr ;Jahrhundertjahre / 4 lsr JhdtJahr add DoomDay,JhdtJahr ;WeekDay + Jahrhundertjahre / 4 CDD2: subi DoomDay,35 brcc CDD2 CDD3: subi DoomDay,-7 brcs CDD3 CDDEnd: ret ;Division 16 Bit / 7 ;Teilt einen Wert in Div7DDH:Div7DDL durch 7 ;Ergebnis ist in Div7Res (Kalenderwoche) ;Rest in Div7Rem (Wochentag) ;Maximal möglicher Dividend ist 255*7 (1785) ;Takte min/max: 8/62 bei Dividend 34/371(Max. logischer Wert) .def Div7DDL=r16 .def Div7DDH=r17 .def Div7Res=r18 .def Div7Rem=r16 Div7: clr Div7Res ;Ergebnis löschen. Div71: subi Div7Res,-5 ;5 auf Ergebnis addieren. subi Div7DDL,35 ;35 Tage (5 Wochen) vom Dividend subtrahieren. sbci Div7DDH,0 brcc Div71 ;Wiederholen bis Dividend negativ. Div72: dec Div7Res ;1 vom Ergebnis subtrahieren. subi Div7DDL,-7 ;7 zum Dividend addieren. brcs Div72 ;Wiederhohlen bis Dividend positiv. ret CenturyDoomDay: .db 5,4,2,0 ;Test auf Schaltjahr TestSJ: mov r20,JahrL andi r20,3 ;Ist das Jahr durch 4 Teilbar (Rest 0)? brne TestSJNo ;Wenn nicht, dann kein Schaltjahr ansonsten movw DD16uL,JahrL ;ist das Jahr durch 100 ohne Rest teilbar? ldiw DV16uH,DV16uL, 100 rcall Div16u or DRem16uL,DRem16uH ;Ist Rest = 0? brne TestSJYes ;Wenn nicht, dann doch ein Schaltjahr. andi DRes16uL,3 ;Ansonsten kein Schaltjahr ausser brne TestSJNo ;das Jahr ist durch 400 ohne Rest teilbar. TestSJYes: sec ;Carry setzen für Schaltjahr. ret TestSJNo: clc ;Carry löschen für kein Schaltjahr. ret ;*************************************************************************** ;* ;* "div16u" - 16/16 Bit Unsigned Division ;* ;* This subroutine divides the two 16-bit numbers ;* "dd16uH:dd16uL" (dividend) and "dv16uH:dv16uL" (divisor). ;* The result is placed in "dres16uH:dres16uL" and the remainder in ;* "drem16uH:drem16uL". ;* ;* Number of words :19 ;* Number of cycles :216/232 (Min/Max) ;* Low registers used :2 (drem16uL,drem16uH) ;* High registers used :5 (dres16uL/dd16uL,dres16uH/dd16uH,dv16uL,dv16uH, ;* dcnt16u) ;* ;*************************************************************************** ;***** Subroutine Register Variables .def drem16uL =r14 .def drem16uH =r15 .def dres16uL =r16 .def dres16uH =r17 .def dd16uL =r16 .def dd16uH =r17 .def dv16uL =r18 .def dv16uH =r19 .def dcnt16u =r20 ;***** Code div16u: clr drem16uL ;clear remainder Low byte sub drem16uH,drem16uH ;clear remainder High byte and carry ldi dcnt16u,17 ;init loop counter d16u_1: rol dd16uL ;shift left dividend rol dd16uH dec dcnt16u ;decrement counter breq d16u_3 ;if done rol drem16uL ;shift dividend into remainder rol drem16uH sub drem16uL,dv16uL ;remainder = remainder - divisor sbc drem16uH,dv16uH ; brcc d16u_2 ;if result negative add drem16uL,dv16uL ;restore remainder adc drem16uH,dv16uH clc ;clear carry to be shifted into result rjmp d16u_1 ;else d16u_2: sec ;set carry to be shifted into result rjmp d16u_1 d16u_3: ret ;return .dseg ;Array mit Datum, Woche, KW und Sommer- Winterzeitumstellung DateArray: .byte 8 .equ Datum=0 .equ Monat=1 .equ Jahr=2 .equ WTag=4 ;Wochentag .equ Woche=5 ;Kalenderwoche .equ SZeitDatum=6 ;Datum der Sommerzeitumstellung .equ WZeitDatum=7 ;Datum der Winterzeitumstellung (Normalzeit)