Код:
; time.asm SC200 timekeeping code
;
; Copyright (C) 2008 David Forbes
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; ------------------------ Timekeeping -----------------------
;
; This code increments the local time from the jiffy (1/300sec)
; all the way to the century. It may be called at several places
; as needed, for instance 1PPS starts at the second, GPS needs
; day correction, etc. This deals with leap years too.
;
; JifISR is called by timer at a 300 Hz rate
;
JifISR pshh ; used in screensaver code below
bclr TOF,T2SC ; clear overflow bit
inc Jiffies
lda Jiffies ; have enough jiffies gone by to equal one tick?
cmp JifTick ; (this depends on mains frequency)
bne NoJifI ; no, skip
clr Jiffies
bsr IncTick ; count up another tick
bset GotJifI,Time ; flag that we saw timer interrupt
NoJifI pulh
rti ; return from ISR to main code
;
; PpsISR is called when 1PPS on com port makes IRQ go low at a 1 Hz rate
;
PpsISR pshh ; to be safe!
bset IACK,INTSCR ; acknowledge interrupt on Irq, re-enable
bset IrqOn,Time ; turn on Irq mode since it*s wiggling
bsr IncSecs ; inc time by one second
clr Jiffies
clr Ticks ; put us at the top of the second for hands
pulh
rti ; return from ISR to main code
;
; Use timer interrupt to synchronize display to Hertz,
; but don*t increment seconds with timer when we have
; an atomic clock feeding us 1PPS on Irq.
;
; IncTick increments the time by one tick (1/Hertz sec)
;
IncTick inc Blink ; advance blinking state
inc Ticks ; advance tick count
lda Ticks
FirstH cmp Hertz ; overflow to secs? (depends on mains freq)
bne TimeDon
brset IrqOn,Time,TimeDon ; 1PPS mode, let Irq increment seconds
clr Ticks ; don*t clear if 1PPS to prevent backwards twitch
;
; IncSec increments the time by one second from 1PPS Irq
; It handles overflows into the minutes and hours.
;
IncSecs lda Secs
and #3
bne NoFLW
jsr MakeFLW ; a new word every four seconds
NoFLW inc Secs
lda Secs
cmp #60 ; overflow?
bne TimeDon
clr Secs ; yes, go to next minute
inc Mins
lda Mins
cmp #60 ; overflow?
bne TimeDon
clr Mins ; yes, go to next hour
;
; Once an hour, advance the screensaver to the next position
; in the list. This subtly moves the display but spreads out
; and phosphor burn over a 4 by 8 pixel area.
;
inc ScrX ; bump screensaver index hourly
lda ScrX
cmp #ScrTabL ; end of table?
blt ScrDon ; yes, return to start
clr ScrX
ScrDon clrh
ldx ScrX ; get pointer to screensaver
lda XScrTab,x ; get x offset
sta XSaver
lda YScrTab,x ; get y offset
sta YSaver
inc Hrs ; bump hours
lda Hrs
cmp #24 ; past end?
blt TimeDon ; no, skip reset
clr Hrs ; reset hours to 0 o*clock amd...
;
; Update the calendar as the days drag on
;
IncDays inc Days ; bump day of month
clrh
ldx Mons ; look up days in a month
lda Years
and #3 ; Is it an even multiple of 4?
bne INotLY ; no, use first part of table
aix #12 ; use leap year part
INotLY lda Days ; get current day
cmp NDays,x ; is it past end of month?
ble TimeDon ; no, skip reset
mov #1,Days ; reset day to 1 and...
inc Mons ; bump months
lda Mons
cmp #12 ; past end?
ble TimeDon ; no, skip reset
mov #1,Mons ; reset month to 1
inc Years ; bump years
lda Years
cmp #100 ; past end?
blt TimeDon ; no, skip reset
clr Years ; reset years to 0
inc Century ; bump century
TimeDon rts ; don*t handle overflow (not Y10K compliant!)
;
; Move backwards in calendar if needed due to GPS -> local conversion
; On month borrow, we go to the last day in the previous month
;
DecDays dec Days ; unbump day of month
bne DecDone ; still in the same month
dec Mons ; unbump months
bne DecYrDn ; still in same year
mov #12,Mons ; oops, set month to December and
dec Years ; unbump years
bge DecYrDn ; Gone back a century?
mov #99,Years ; yes, set years to 99
dec Century ; unbump century
DecYrDn clrh
ldx Mons ; look up days in the new month
lda Years
and #3 ; Is it an even multiple of 4?
bne DNotLY ; no, use first part of table
aix #12 ; use leap year part
DNotLY lda NDays,x ; Get last day of month
sta Days ; save as current day
DecDone rts
;
; ----------------------- Time string generator -----------------------
;
; MakeTim fills in the time and date strings in RAM with the
; current time in ASCII. The month string pointer is set also.
;
MakeTim brclr Hr12,Time,Not12M ; 12/24 hour mode
lda #"1"
sta HrSelStr
lda #"2"
sta HrSelStr+1
bra Not24M
Not12M lda #"2"
sta HrSelStr
lda #"4"
sta HrSelStr+1
Not24M lda Century ; do century
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta KYrs
pshh
pula
ora #$30
sta HYrs
lda Years ; do year
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TYrs
pshh
pula
ora #$30
sta UYrs
lda Mons ; do month
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TMon
pshh
pula
ora #$30
sta UMon
lda Days ; do day
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TDay
pshh
pula
ora #$30
sta UDay ; Remove leading zero on day of month
lda TDay
cmp #"0" ; is it a zero?
bne TDnotZ ; if 10+, don*t do zeading zero work
lda UDay ; move units to first position in string
sta TDay
mov #NL,UDay ; turn 2nd digit into term char.
;
; hours are tricky if 12 hour mode - leading zero is quashed
;
TDnotZ lda Hrs ; do hour
brclr Hr12,Time,NotG12H ; 24 hour mode, skip the insanity
tsta
bne NotZH
lda #12 ; zero is 12 o*clock
NotZH cmp #13
blt NotG12H ; map 13-23 to 1-11
sub #12
NotG12H clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta THrs
pshh
pula
ora #$30
sta UHrs
brclr Hr12,Time,THnotZ ; skip zeading zero check if 24 hr
lda THrs
cmp #"0" ; is it a zero?
bne THnotZ ; if 10-12, don*t do zeading zero work
lda UHrs ; move units to first position in string
sta THrs
clr UHrs ; turn 2nd digit into term char.
THnotZ lda Mins ; do minute
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TMins
pshh
pula
ora #$30
sta UMins
lda Secs ; do second
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TSecs
pshh
pula
ora #$30
sta USecs
;
; Make DST string from DST flag byte
;
brclr 0,DST,NotDS ; select which string to use
ldhx #OnStr
bra DSDone
NotDS ldhx #OffStr
DSDone mov x+,DSTStr
mov x+,DSTStr+1
mov x+,DSTStr+2 ; copy it to RAM
mov x+,DSTStr+3
;
; Make timezone strings for hours and minutes
;
; Range is -12 to +11 and leading zero is quashed
;
lda #"+"
sta ZonSign ; set zone sign display positive
lda Zone ; get timezone hour
tsta
bpl NotZNeg ; if GMT+nn, skip this
nega ; make it positive
psha ; save it for now
lda #"-"
sta ZonSign ; set zone sign display negative
pula
NotZNeg clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TZone
pshh
pula
ora #$30
sta UZone
lda TZone
cmp #"0" ; is it a zero?
bne ZHnotZ ; if 10-12, don*t do zeading zero work
lda UZone ; move units to first position in string
sta TZone
clr UZone ; turn 2nd digit into term char.
ZHnotZ lda Zmins ; do minute
clrh
ldx #10
div ; H:A/X -> ArH
ora #$30
sta TZmin
pshh
pula
ora #$30
sta UZMin
;
; Make text string for month of year in MonthStr
;
lda Mons ; get months to make string pointer
deca ; first entry is month 1 - Jan.
lsla
sta MonthStr ; save month x 2
lsla ; make month x 4
add MonthStr ; add month x 2 = month x 6
tax
clrh
lda JanStr,x ; copy the string to RAM
sta MonthStr
lda JanStr+1,x ; this is rather brute-force, but it works
sta MonthStr+1
lda JanStr+2,x
sta MonthStr+2
lda JanStr+3,x
sta MonthStr+3
lda JanStr+4,x
sta MonthStr+4
lda JanStr+5,x
sta MonthStr+5
;
; Make day of week from date variables
;
; Peter M. Klausler gives this algorithm on his website.
;
; For dates in 1900 through 2099, compute the day of the week as follows.
;
; 1. Subtract 1900.
; 2. To that number, add one fourth of itself, discarding any remainder.
; This sum is the year*s skew value
; 3. If the month in question is January or February in a leap year,
; subtract 1 from the sum.
; 4. Add the date in the month.
; 5. Add the month*s skew value from the table
; 6. The sum is the number of days after Sunday on which the date falls.
;
GetWDay lda Century ; assumes Century>=19
sub #19 ; Jan 1 1900 = Sunday
ldx #100 ; this will fail in 2156
mul
add Years ; years since 1900
sta WDay
lsra
lsra
add WDay ; year * 5 is Jan 1 Sunday cycle period
sta WDay ; save it
lda #3
and Years
bne NotLY ; check for leap years
lda Mons
cmp #2 ; Jan or Feb
bgt NotLY
dec WDay ; first 2 months of leap year shifted
NotLY lda WDay
add Days ; add in day of month
sta WDay
clrh
ldx Mons ; lookup start day of month
lda DayLkUp,x
add WDay
clrh
ldx #7 ; take modulo 7
div
pshh ; remainder is weekday
pula
sta WDay
;
; Fill WDayStr with the correct string for this weekday
;
MakWDay lda WDay ; get numeric weekday
lsla
lsla
lsla ; index into 8 byte string table
tax
clrh
lda SunStr,x ; copy the string tediously
sta WDayStr
lda SunStr+1,x
sta WDayStr+1
lda SunStr+2,x
sta WDayStr+2
lda SunStr+3,x
sta WDayStr+3
lda SunStr+4,x
sta WDayStr+4
lda SunStr+5,x
sta WDayStr+5
lda SunStr+6,x
sta WDayStr+6
rts
;
; ----------------------------- Tables -----------------------------
;
; Number of days in a month
;
; JanFebMarAprMayJunJulAugSepOctNovDec
NDays fcb 0,31,28,31,30,31,30,31,31,30,31,30,31 ; normal year
fcb 31,29,31,30,31,30,31,31,30,31,30,31 ; leap year
;
; Months of the year - each string is 6 bytes long
;
JanStr fcb "Jan",0,0,0
FebStr fcb "Feb",0,0,0
MarStr fcb "March",0
AprStr fcb "April",0
MayStr fcb "May",0,0,0
JunStr fcb "June",0,0
JulStr fcb "July",0,0
AugStr fcb "Aug",0,0,0
SepStr fcb "Sept",0,0
OctStr fcb "Oct",0,0,0
NovStr fcb "Nov",0,0,0
DecStr fcb "Dec",0,0,0
;
; Position table for screensaver
;
; This table is 2 * 31 entries long, but has 32
; different x-y positions on screen. The display sits
; at the first and last positions for one not two hours,
; so that movement is obtained every hour.
; 31 is a prime number... this ensures that every
; hour numeral gets put in every screen position.
;
xScrTab fcb 255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0
fcb 1,1,1,1,1,1,1,1,2,2,2,2,2,2,2
fcb 2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1
fcb 0,0,0,0,0,0,0,0,255,255,255,255,255,255,255
;
YScrTab fcb 250,252,254,0,2,4,6,8,8,6,4,2,0,254,252,250
fcb 250,252,254,0,2,4,6,8,8,6,4,2,0,254,252
fcb 250,252,254,0,2,4,6,8,8,6,4,2,0,254,252,250
fcb 250,252,254,0,2,4,6,8,8,6,4,2,0,254,252
;
ScrTabL equ *-YScrTab
;
; Which day of week this month starts on (if Jan 1=Sunday)
;
DayLkUp fcb 0,0,3,3,6,1,4,6,2,5,0,3,5 ; Jan is 1 (ignore zero)
;
; Weekday strings, leaving out the "day" part
; Each is eight bytes long for easy lookup
;
SunStr fcb "Sun",0,0,0,0,0
fcb "Mon",0,0,0,0,0
fcb "Tues",0,0,0,0
fcb "Wednes",0,0
fcb "Thurs",0,0,0
fcb "Fri",0,0,0,0,0
fcb "Satur",0,0,0
Перенесен примерно до подпрограммы "MakeTim", далее я пока не совался. Подпрограмма "DecDays" нужна только для работы с синхронизацией по GPS в теоретическом случае пересечения линии перемены дат. Потому не рассматривалось.
Социальные закладки