;------------------------------------------------------------------------------- ;@doc:module ; ; === DateTime === ; ; Contains routines to handle dates and times. ; ;@doc:end ;------------------------------------------------------------------------------- .module DateTime ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.Reset === ; ; Resets the date and time in memory to 12:00:00 on Monday 1st January 2001. ; ; OUTPUTS: ; MEMORY ; * Time.Second - Reset to 00. ; * Time.Minute - Reset to 00. ; * Time.Hour - Reset to 12. ; * Date.DayOfWeek - Reset to Monday (02). ; * Date.DayOfMonth - Reset to 01. ; * Date.Month - Reset to January (01). ; * Date.Year - Reset to 01. ; * DateTime.Control - Reset to 00 (Z80 computer only). ; * Date.Century - Reset to 20 (TI-84+ only). ; ; DESTROYED: ; REGISTERS ; * F ; ;@doc:end ;------------------------------------------------------------------------------- Reset push hl push de push bc ld hl,DefaultDate ld de,DateTime.Now ld bc,8 ldir pop bc pop de pop hl ret DefaultDate .db $00, $00, $12, $02, $01, $01, $01, if(IsCalculator, $20, $00) ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.ToString === ; ; Converts a date and time to a full string in the format ; "Sun,01 Jan 2000.12:00:00". ; ; INPUTS: ; MEMORY ; * Time.Second - The seconds component of the time in packed BCD (00-59). ; * Time.Minute - The minutes component of the time in packed BCD (00-59). ; * Time.Hour - The hours component of the time in packed BCD (00-23). ; * Date.DayOfWeek - The day of the week, from 1 (Sunday) to 7 (Saturday). ; * Date.DayOfMonth - The day of the month in packed BCD (01-31). ; * Date.Month - The month number in packed BCD (01-12). ; * Date.Year - The year number in packed BCD (00-99). ; REGISTERS ; * HL - Pointer to memory to store string. ; ; OUTPUTS: ; MEMORY ; * Initial HL - Points to a complete NUL-terminated date and time string. ; REGISTERS ; * HL - Points to the byte following the string's NUL terminator. ; ; DESTROYED: ; REGISTERS ; * AF, HL, DE, BC ; ;@doc:end ;------------------------------------------------------------------------------- ToString call ToDateString dec hl ld (hl),'.' inc hl jp ToTimeString ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.ToDateString === ; ; Converts a date a string in the format "Sun,01 Jan 2000". ; ; INPUTS: ; MEMORY ; * Date.DayOfWeek - The day of the week, from 1 (Sunday) to 7 (Saturday). ; * Date.DayOfMonth - The day of the month in packed BCD (01-31). ; * Date.Month - The month number in packed BCD (01-12). ; * Date.Year - The year number in packed BCD (00-99). ; * Date.Century - The century number in packed BCD (TI-84+ only). ; REGISTERS ; * HL - Pointer to memory to store string. ; ; OUTPUTS: ; MEMORY ; * Initial HL - Points to a NUL-terminated date string. ; REGISTERS ; * HL - Points to the byte following the string's NUL terminator. ; ; DESTROYED: ; REGISTERS ; * AF, HL, DE, BC ; ;@doc:end ;------------------------------------------------------------------------------ ToDateString ld a,(Date.DayOfWeek) .oscall "Utility.DecodeBCD" dec a cp 7 jr c,+ xor a + ld e,a add a,a add a,e ld e,a ld d,0 push hl ld hl,DayNames add hl,de pop de ld bc,3 ldir ex de,hl ld (hl),',' inc hl ld a,(Date.DayOfMonth) call AppendBcd ld (hl),' ' inc hl ld a,(Date.Month) .oscall "Utility.DecodeBCD" dec a cp 12 jr c,+ xor a + ld e,a add a,a add a,e ld e,a ld d,0 push hl ld hl,MonthNames add hl,de pop de ld bc,3 ldir ex de,hl ld (hl),' ' inc hl .if IsCalculator ld a,(Date.Century) .else ld a,(Date.Year) .oscall "Utility.DecodeBCD" cp 29 ld a,$19 jr nc,+ ld a,$20 + .endif call AppendBcd ld a,(Date.Year) call AppendBcd ld (hl),0 inc hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.ToTimeString === ; ; Converts a time to a string in the format "12:00:00". ; ; INPUTS: ; MEMORY ; * Time.Second - The seconds component of the time in packed BCD (00-59). ; * Time.Minute - The minutes component of the time in packed BCD (00-59). ; * Time.Hour - The hours component of the time in packed BCD (00-23). ; REGISTERS ; * HL - Pointer to memory to store string. ; ; OUTPUTS: ; MEMORY ; * Initial HL - Points to a NUL-terminated time string. ; REGISTERS ; * HL - Points to the byte following the string's NUL terminator. ; ; DESTROYED: ; REGISTERS ; * AF, HL, DE, BC ; ;@doc:end ;------------------------------------------------------------------------------ ToTimeString ld a,(Time.Hour) bit 6,a jr z,+ and %00011111 + and %00111111 call AppendBcd ld (hl),':' inc hl ld a,(Time.Minute) call AppendBcd ld (hl),':' inc hl ld a,(Time.Second) call AppendBcd ld a,(Time.Hour) bit 6,a jr z,TimeStringIs24Hour bit 5,a ld a,'A' jr z,+ ld a,'P' + ld (hl),a inc hl ld (hl),'M' inc hl TimeStringIs24Hour ld (hl),0 inc hl ret MonthNames .db "JanFebMarAprMayJunJulAugSepOctNovDec" DayNames .db "SunMonTueWedThuFriSat" AppendBcd push af srl a srl a srl a srl a cp 10 jr c,+ xor a ; Invalid BCD + add a,'0' ld (hl),a inc hl pop af and $0F cp 10 jr c,+ xor a ; Invalid BCD + add a,'0' ld (hl),a inc hl ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.ParseString === ; ; Converts a string containing a date, time, or elements of both to a date. ; ; INPUTS: ; REGISTERS ; * HL - Pointer to NUL-terminated date and time string. ; ; OUTPUTS: ; MEMORY ; * Time and Date - Updated depending on contents of input string. ; ; DESTROYED: ; REGISTERS ; * AF, HL, DE, BC ; ;@doc:end ;------------------------------------------------------------------------------ ParseString ld a,(hl) or a ret z call IdentifyDateComponent cp 1 jr nz,ParseNotTime ; It's a time. call ReadNumber .oscall "Utility.EncodeBCD" ld (Time.Hour),a ld a,(hl) cp ':' jr nz,+ inc hl call ReadNumber .oscall "Utility.EncodeBCD" ld (Time.Minute),a ld a,(hl) cp ':' jr nz,+ inc hl call ReadNumber .oscall "Utility.EncodeBCD" ld (Time.Second),a + jr ParseString ParseNotTime call ParseDayOfWeek jr z,+ .oscall "Utility.EncodeBCD" ld (Date.DayOfWeek),a jr ParseFindNextBit + call ParseMonth jr z,+ .oscall "Utility.EncodeBCD" ld (Date.Month),a ; So, we have a month... we should also have a day! call ReadNumber .oscall "Utility.EncodeBCD" ld (Date.DayOfMonth),a - ld a,(hl) .oscall "Char.IsNumeric" jr z,FoundYear or a jr z,ParseFindNextBit cp '.' jr z,ParseFindNextBit cp ',' jr z,ParseFindNextBit inc hl jr - FoundYear call ReadNumber .oscall "Utility.EncodeBCD" ld (Date.Year),a + ParseFindNextBit ld a,(hl) or a jr z,ParseString inc hl cp '.' jp z,ParseString cp ':' jp z,ParseString cp ',' jp z,ParseString jr ParseFindNextBit IdentifyDateComponent ld bc,0 push hl IdentifyDateComponentLoop ld a,(hl) inc hl cp '.' jr z,+ cp ',' jr z,+ or a + ; z = end of string. jr nz,+ pop hl ld a,b ret + cp ':' jr nz,+ ld b,1 ; It's a time. + .oscall "Char.IsWhiteSpace" jr nz,+ inc c + jr IdentifyDateComponentLoop ReadNumber ld b,0 ReadNumberLoop - ld a,(hl) .oscall "Char.IsWhitespace" jr nz,+ inc hl jr - + cp '0' jr c,HitEndOfNumber cp '9'*1+1 jr nc,HitEndOfNumber sub '0' ld c,a ld a,b cp 10 jr c,+ xor a + add a,a ld b,a add a,a add a,a add a,b add a,c ld b,a inc hl jr ReadNumberLoop HitEndOfNumber ld a,b ret ContainsThree ld a,(hl) or a jr z,NotContainsThree cp '.' jr z,NotContainsThree cp ',' jr z,NotContainsThree call CompareThree ret z inc hl jr ContainsThree NotContainsThree xor a inc a ret CompareThree push hl push de push bc ld b,3 CompareThreeLoop ld a,(hl) .oscall "Char.ToLower" ld c,a ld a,(de) .oscall "Char.ToLower" cp c jr nz,+ inc hl inc de djnz CompareThreeLoop xor a + pop bc pop de pop hl ret ParseMonth ld de,MonthNames ld bc,12*256+1 jr GetNamedElement ParseDayOfWeek ld de,DayNames ld bc,7*256+1 GetNamedElement - push bc push hl call ContainsThree pop hl pop bc jr z,FoundMonth inc c inc de inc de inc de djnz - ld c,0 FoundMonth ld a,c or a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.GetDayOfWeek === ; ; Gets the day of the week from the day of the month, month and year. ; ; INPUTS: ; REGISTERS ; * DE - Year. ; * C - Month (1..12). ; * B - Day (1..31). ; ; OUTPUTS: ; REGISTERS ; * A - Day of the week (1..7). ; ; DESTROYED: ; REGISTERS ; * AF, HL, DE, BC ; ;@doc:end ;------------------------------------------------------------------------------ GetDayOfWeek ld a,c add a,11 - cp 12 jr c,+ sub 12 jr - + ld c,a ; c = Normalised month (0..11). cp 2 jr nc,+ ; Month is January or February - move to previous year. dec de + push bc ; Save day/month. ; Calculate Doomsday = (2 + Year + (Year / 4) - (Year / 100) + (Year / 400)) % 7 push de ld a,d ld c,e ld hl,0 ld de,100 ld b,16 - sl1 c rla adc hl,hl sbc hl,de jr nc,+ add hl,de dec c + djnz - ld h,a ld l,c pop de push de ; Year srl d \ rr e srl d \ rr e push de ; Year / 4 push hl ; Year / 100 srl h \ rr l srl h \ rr l ; hl = Year / 400 pop de ; de = Year / 100 or a sbc hl,de pop de ; de = Year / 4 add hl,de pop de ; de = Year add hl,de ld de,2 add hl,de ; Now, calculate % 7; ld de,-7 - ld a,h or a jr z,+ add hl,de jr - + ; Now, hl < 256. ld a,l call Mod7 pop bc ; a = Doomsday. ; b = Day of month. ; c = Month. add a,b ; Offset Doomsday by day of month. ld hl,Doomsdays ld d,0 ld e,c add hl,de sub (hl) ; Offset day by Doomsday for this month. jp m,+ call Mod7 inc a ret + - add a,7 jp m,- inc a ret Mod7 cp 7 ; Value must be 0..6 ret c sub 7 jr Mod7 Doomsdays .db 2, 6, 7, 4, 2, 6, 4, 1, 5, 3, 7, 5 ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.FixDayOfWeek === ; ; Updates the day of the week based on the current date. ; ; INPUTS: ; MEMORY ; * Date - The date to set the day of the week to. ; ; OUTPUTS: ; MEMORY ; * Date.DayOfWeek - Day of the week (0..6). ; ; DESTROYED: ; REGISTERS ; * AF, HL, DE, BC ; ;@doc:end ;------------------------------------------------------------------------------ FixDayOfWeek ld a,(Date.Century) .oscall "Utility.DecodeBCD" ld l,a ld h,100 .bcall _HTimesL ld a,(Date.Year) .oscall "Utility.DecodeBCD" ld e,a ld d,0 add hl,de ex de,hl ld a,(Date.DayOfMonth) .oscall "Utility.DecodeBCD" ld b,a ld a,(Date.Month) .oscall "Utility.DecodeBCD" ld c,a call GetDayOfWeek .oscall "Utility.EncodeBCD" ld (Date.DayOfWeek),a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.GetDaysInMonth === ; ; Gets the number of days in a month. ; ; INPUTS: ; REGISTERS ; * A - The month (1..12) to get the number of days for. ; * HL - The year the month is in. ; ; OUTPUTS: ; * A - Number of days in the month. ; ; DESTROYED: ; REGISTERS ; * AF, HL. ; ;@doc:end ;------------------------------------------------------------------------------ GetDaysInMonth cp 2 jr z,DaysInFebruary ld l,a ld h,0 push de ld de,DaysInMonth-1 add hl,de pop de ld a,(hl) ret DaysInFebruary call IsLeapYear ld a,28 ret nz inc a ret ;------------------------------------------------------------------------------- ;@doc:routine ; ; === DateTime.IsLeapYear === ; ; Checks if a year is a leap year or not. ; ; INPUTS: ; REGISTERS ; * HL - The year to check. ; ; OUTPUTS: ; * Z - Zero flag set if HL is a leap year; reset otherwise. ; ; DESTROYED: ; REGISTERS ; * AF. ; ;@doc:end ;------------------------------------------------------------------------------ IsLeapYear ld a,l and %11 ret nz ; Not divisible by 4, clearly not a leap year. push hl push de push bc ; Divide HL by 100. ld a,h ld c,l ld hl,0 ld de,100 ld b,16 - sl1 c rla adc hl,hl sbc hl,de jr nc,+ add hl,de dec c + djnz - ; AC r HL; HL = 0 if year / 100. ld a,h or l jr z,CheckDivisibleBy400 xor a jr ExitIsLeapYear CheckDivisibleBy400 ld a,c and %11 ExitIsLeapYear pop bc pop de pop hl ret DaysInMonth .db /* Jan: */ 31, /* Feb: */ 28, /* Mar: */ 31, /* Apr: */ 30, /* May: */ 31, /* Jun: */ 30 .db /* Jul: */ 31, /* Aug: */ 31, /* Sep: */ 30, /* Oct: */ 31, /* Nov: */ 30, /* Dec: */ 31 .endmodule