-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtc.a
557 lines (483 loc) · 18.6 KB
/
rtc.a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
GETTIME = $FF50
I2CREADBYTE = $FEC6
I2C_DEV = $6F
RTCI2C
!byte 0,0,0
!macro cmpTime .addr1, .addr2 {
+load16BitImmediate .addr1, TEMP_PTR
+load16BitImmediate .addr2, TEMP_PTR2
jsr cmpTimeI2cCall
}
; --------------------------------------------------
; This routine compares two I2C time stamps. The addresses of the timestamps
; have to be given through TEMP_PTR and TEMP_PTR2.
;
; Zero flag is set if the two values are equal. Carry is set if
; the value to which TEMP_PTR points is larger or equal than the one to which
; TEMP_PTR2 points.
; --------------------------------------------------
cmpTimeI2cCall
sed ; switch to bcd mode
ldy #2
php ; initialize loop
.cmpLoop
plp ; throw previous comparison result away
lda (TEMP_PTR),y
cmp (TEMP_PTR2), y ; perform comparison
bne .cmpDone ; if we found a difference we are done
php ; save flags at this point as they contain potentially the end result
dey
bpl .cmpLoop ; decrease and check looping values, if neccessary look at next values
plp ; restore result of last comparison. It is the end result
.cmpDone
cld ; switch back to binary mode
rts
!macro diffTimeSimple .addr1, .addr2 {
+load16BitImmediate .addr1, TEMP_PTR
+load16BitImmediate .addr2, TEMP_PTR2
jsr diffTimeSimpleCall
}
!macro diffTime .addr1, .addr2 {
+load16BitImmediate .addr1, TEMP_PTR
+load16BitImmediate .addr2, TEMP_PTR2
jsr diffTimeCall
}
!macro addTimeSimple .addr1, .addr2 {
+load16BitImmediate .addr1, TEMP_PTR
+load16BitImmediate .addr2, TEMP_PTR2
jsr addTimeSimpleCall
}
; --------------------------------------------------
; This macro calculates A + X mod N. We have to use 16 bit arithmetic
; because in our main use case (mod 60 in BCD) for instance the intermediate
; result 59 + 59 = 118 does not fit in one byte (in BCD the maximum value
; of a byte is 99).
;
; It returns the result in the accu. Carry is set if an overflow occured.
; --------------------------------------------------
!macro addModN .modulus {
sta ARG1
stz ARG1 + 1
stx ARG2
stz ARG2 + 1
+add16Bit ARG1, ARG2 ; ARG2 = ARG1 + ARG2
+cmp16BitImmediate .modulus, ARG2 ; .modulus >= ARG2?
beq .reduce ; .modulus == ARG2 => reduce and set carry upon return
bcs .clearCarryNoReduce ; .modulus > ARG2 => do not reduce and clear carry upon return
.reduce
+sub16BitImmediate .modulus, ARG2 ; Reduce: ARG2 = ARG2 - .modulus
sec
.addDone
lda ARG2 ; load result in accu
rts
.clearCarryNoReduce
clc
bra .addDone
}
ARG1
!byte 0, 0
ARG2
!byte 0, 0
TEMP_MODN
!byte 0, 0
UNDERFLOW_OCCURRED
!byte 0
; --------------------------------------------------
; This macro calculates A - X mod N. We have to use 16 bit arithmetic
; because in our main use case (mod 60 in BCD) intermediate results
; may not fit in one byte, as the maximum value of a byte in BCD is 99.
;
; It returns the result in the accu. Carry is set if an underflow occured.
; --------------------------------------------------
!macro subModN .modulus {
sta ARG1
stz ARG1 + 1
stx ARG2
stz ARG2 + 1
; determine if there will an underflow, i.e. dtermine if X > A
stz UNDERFLOW_OCCURRED
lda ARG2
cmp ARG1
beq .startCalc ; values are equal => No underflow, carry has to be clear
bcc .startCalc ; ARG2 < ARG1 (i.e. X < A) => No underflow carry has to be clear
inc UNDERFLOW_OCCURRED
.startCalc
; negate .ARG2 mod .modulus, i.e. calculate .modulus - ARG2
+load16BitImmediate .modulus, TEMP_MODN
+sub16Bit ARG2, TEMP_MODN
; add .ARG1 to the negated value
+move16Bit TEMP_MODN, ARG2
+add16Bit ARG1, ARG2
; check if we have to reduce result
+cmp16BitImmediate .modulus, ARG2
beq .reduceSub ; we just hit the modulus => we have to reduce result
bcs .doneSubN ; .modulus >= ARG2 => with the above test we can be sure that .modulus > ARG2. No reduction neccessary.
.reduceSub
+sub16BitImmediate .modulus, ARG2 ; reduce mod .modulus. ARG2 = ARG2 - .modulus
.doneSubN
; make sure that carry is set to correct value upon return
clc
lda UNDERFLOW_OCCURRED
beq .finishSubN ; Did we precalculate that an underflow occurs?
sec ; yes => set carry
.finishSubN
lda ARG2 ; load result in acccu
}
; --------------------------------------------------
; This routine calculates A + X mod $60 (in BCD)
;
; It returns the result in the accu. Carry is set if an overflow occured.
; --------------------------------------------------
addMod60Call
+addModN $60
rts
; --------------------------------------------------
; This macro calculates A - X mod $60 (in BCD)
;
; It returns the result in the accu. Carry is set if an underflow occured.
; --------------------------------------------------
subMod60Call
+subModN $60
rts
; --------------------------------------------------
; This routine calculates A - 1 mod $60 (in BCD)
;
; It returns result in accu. Carry is set if an underflow occured.
; --------------------------------------------------
decMod60
ldx #1
jsr subMod60Call
rts
; --------------------------------------------------
; This routine calculates A + 1 mod $60 (in BCD)
;
; It returns result in accu. Carry is set if an overflow occured.
; --------------------------------------------------
incMod60
ldx #1
jsr addMod60Call
rts
.TIME_UNDERFLOW_MINUTES
!byte 0
.TIME_UNDERFLOW_HOURS
!byte 0
.TIME_TEMP
!byte 0
; --------------------------------------------------
; This routine calculates the time span between two given time stamps.
; The time stamps have to be referenced by TMP_PTR and TEMP_PTR2 respectively.
;
; This routine subtracts the value of *TEMP_PTR2 from *TEMP_PTR. The
; result is stored in *TEMP_PTR2. It assumes *TEMP_PTR >= *TEMP_PTR2.
; --------------------------------------------------
diffTimeSimpleCall
sed ; set BCD mode
ldy #0
stz .TIME_UNDERFLOW_MINUTES
stz .TIME_UNDERFLOW_HOURS
; process seconds
lda (TEMP_PTR2), y
tax ; TEMP_PTR2 in X
lda (TEMP_PTR), y ; TEMP_PTR in A
jsr subMod60Call ; A = A - X mod 60
sta (TEMP_PTR2), y ; save new seconds in TEMP_PTR2
bcc .noUnderflowSeconds
inc .TIME_UNDERFLOW_MINUTES ; record underflow
.noUnderflowSeconds
; process minutes
iny
lda (TEMP_PTR), y ; accu contains minutes of TEMP_PTR
ldx .TIME_UNDERFLOW_MINUTES
beq .noUnderflowMinutes ; was there an underflow?
; process underflow from seconds
;underflowSub
jsr decMod60 ; yes => subtract underflow A = A - 1 mod 60
bcc .noUnderflowMinutes ; have we generated an underflow in the hours?
inc .TIME_UNDERFLOW_HOURS ; yes, we have!
.noUnderflowMinutes
; now process minutes
sta .TIME_TEMP ; store minutes of TEMP_PTR (perhaps decremented due to underflow)
lda (TEMP_PTR2), y ; load minutes of TEMP_PTR2
tax ; minutes of TEMP_PTR2 are in X
lda .TIME_TEMP ; minutes of TEMP_PTR are in A
;regularSub
jsr subMod60Call ; A = A - X mod 60
sta (TEMP_PTR2), y ; save new minutes in TEMP_PTR2
bcc .noHourUnderflow
; Either line underflowSub or line regularSub but not both can generate an underflow.
; Reasoning: If line underflowSub created an underflow the minutes are at 59
; before line regularSub. And from that value we subtract at most 59, so no
; additional underflow can occur
inc .TIME_UNDERFLOW_HOURS
.noHourUnderflow
; process hours
iny
sec
lda (TEMP_PTR), y
sbc (TEMP_PTR2), y ; Hours of TEMP_PTR in A
ldx .TIME_UNDERFLOW_HOURS ; Subtract TEMP_PTR2 from A
beq .storeResult ; no underflow => we are nearly done
dec ; A = A - 1 Take underflow into account.
.storeResult
sta (TEMP_PTR2), y ; write result for hours
cld ; set binary mode
rts
; --------------------------------------------------
; This routine adds the given time intervals. The interval have to be
; referenced by TMP_PTR and TEMP_PTR2 respectively. This routine add the
; value of *TEMP_PTR to *TEMP_PTR2. The result is stored in *TEMP_PTR2.
; --------------------------------------------------
addTimeSimpleCall
sed ; set BCD mode
ldy #0
stz .TIME_UNDERFLOW_MINUTES
stz .TIME_UNDERFLOW_HOURS
; process seconds
lda (TEMP_PTR2), y
tax ; TEMP_PTR2 => X
lda (TEMP_PTR), y ; TEMP_PTR => A
jsr addMod60Call ; A = A + X mod $60
bcc .noOverflowSec
inc .TIME_UNDERFLOW_MINUTES ; yes I know, this should be overflow not underflow ....
.noOverflowSec
sta (TEMP_PTR2), y ; store result for seconds
; process minutes
iny
lda (TEMP_PTR2),y ; TEMP_PTR2 in A
ldx .TIME_UNDERFLOW_MINUTES
beq .noMinutesOverflow ; checkForOveflow
;overflowAdd
jsr incMod60 ; Overflow occurred increment A
bcc .noMinutesOverflow ; Did this create an overflow for the hours?
inc .TIME_UNDERFLOW_HOURS ; yes
.noMinutesOverflow
tax ; TEMP_PTR2 => X
lda (TEMP_PTR), y ; TEMP_PTR => A
;normalAdd
jsr addMod60Call ; A = A + X mod $60
sta (TEMP_PTR2), y
bcc .noHoursOverflow ; did an overflow occur?
inc .TIME_UNDERFLOW_HOURS ; yes
.noHoursOverflow
; process hours
; In an argument that mirrors subtraction only the line
; overflowAdd or the line normalAdd but not both can
; result in an overflow
iny
clc
lda (TEMP_PTR2), y ; A = Hours of TEMP_PTR2
adc (TEMP_PTR), y ; A = A + Hours of TEMP_PTR
ldx .TIME_UNDERFLOW_HOURS
beq .noAdditionalOverflow ; Did we have an overflow?
inc ; yes
.noAdditionalOverflow
sta (TEMP_PTR2), y ; store result in TEMP_PTR2
cld ; back to binary
rts
!macro copyTs .srcAddr, .targetAddr {
lda .srcAddr
sta .targetAddr
lda .srcAddr + 1
sta .targetAddr + 1
lda .srcAddr + 2
sta .targetAddr + 2
}
!macro copyTsIndirect .ptr, .target {
ldy #0
lda (.ptr), y
sta .target
iny
lda (.ptr), y
sta .target + 1
iny
lda (.ptr), y
sta .target + 2
}
!macro copyTsIndirectTarget .src, .ptr {
ldy #0
lda .src
sta (.ptr),y
iny
lda .src + 1
sta (.ptr),y
iny
lda .src + 2
sta (.ptr),y
}
.CONST_TS_MIDNIGHT
!byte 0,0,$24
.HELP_INTERVAL_UNTIL_MIDNIGHT
!byte 0,0,0
.HELP_TS2
!byte 0,0,0
.HELP_TS1
!byte 0,0,0
.TEMP_PTR
!byte 0,0
.TEMP_PTR2
!byte 0,0
; --------------------------------------------------
; This routine calculates the time interval between two given time stamps.
; The time stamps have to be referenced by TMP_PTR and TEMP_PTR2 respectively.
; The result is stored in *TEMP_PTR2.
;
; It correctly handles the case where a time interval wraps around at midnight.
; TEMP_PTR is the timestamp of the beginning of the interval and TEMP_PTR2 holds
; a timestamp for the end of the interval.
; --------------------------------------------------
diffTimeCall
; save pointers
+move16Bit TEMP_PTR, .TEMP_PTR
+move16Bit TEMP_PTR2, .TEMP_PTR2
jsr cmpTimeI2cCall ; Test *TEMP_PTR >= *TEMP_PTR2
beq .noWrapAround ; *TEMP_PTR == *TEMP_PTR2 => no wrap around
bcs .wrapAround ; *TEMP_PTR >= *TEMP_PTR2 => wrap around
.noWrapAround ; *TEMP_PTR < *TEMP_PTR2 => no wrap around
+copyTsIndirect TEMP_PTR, .HELP_TS1
+copyTsIndirect TEMP_PTR2, .HELP_TS2
+diffTimeSimple .HELP_TS2, .HELP_TS1 ; Calculate time diff with reversed parameters to fulfill preconditon of diffTimeSimpleCall
; restore pointers
+move16Bit .TEMP_PTR, TEMP_PTR
+move16Bit .TEMP_PTR2, TEMP_PTR2
; result is in .HELP_TS1 => copy to *TEMP_PTR2
+copyTsIndirectTarget .HELP_TS1, TEMP_PTR2
rts
.wrapAround
; copy time stamps
+copyTs .CONST_TS_MIDNIGHT, .HELP_INTERVAL_UNTIL_MIDNIGHT
+copyTsIndirect TEMP_PTR, .HELP_TS2
; .HELP_TS2 = .HELP_INTERVAL_UNTIL_MIDNIGHT - .HELP_TS2 = 24:00:00 - *TEMP_PTR
+diffTimeSimple .HELP_INTERVAL_UNTIL_MIDNIGHT, .HELP_TS2
; *TEMP_PTR2 = .HELP_TS2 + *TEMP_PTR2
+move16Bit .TEMP_PTR2, TEMP_PTR2
+load16BitImmediate .HELP_TS2, TEMP_PTR
jsr addTimeSimpleCall
; restore pointers
+move16Bit .TEMP_PTR, TEMP_PTR
rts
; --------------------------------------------------
; This macro reads a byte from the RTCs registers via the I2C-bus.
; --------------------------------------------------
!macro readI2c .byteOffset {
ldx #I2C_DEV
ldy #.byteOffset
jsr I2CREADBYTE
bcs i2cError
sta RTCI2C + .byteOffset
}
; --------------------------------------------------
; This routine reads the current time from the built in Real Time Clock.
; It does that by accessing the device directly via the I2C-bus and not via
; the corresponding Kernal call.
;
; After calling this routine the current time is stored at the three
; bytes at RTCI2C. First byte is seconds, the second is minutes and the
; third is hors. The values are BCD. Upon return the carry is set if
; an error occurred.
; --------------------------------------------------
.getTimeI2C
+readI2c 0
lda RTCI2C
and #%01111111 ; bit eight in the seconds register is some flag we don't care about
sta RTCI2C
+readI2c 1
+readI2c 2
i2cError
rts
; --------------------------------------------------
; This macro allows to fill the target address with a timestamp that
; holds the current time.
; --------------------------------------------------
!macro getTimestamp .targetAddr {
+load16BitImmediate .targetAddr, TEMP_PTR
jsr getTimeStampCall
}
; --------------------------------------------------
; This routine reads the current time from the built in Real Time Clock.
; It does that by accessing the device directly via the I2C-bus and not via
; the corresponding Kernal call.
;
; After calling this routine the current time is stored at the three
; bytes to which TEMP_PTR points. First byte is seconds, the second is
; minutes and the third is hors. The values are BCD. Upon return the
; carry is set if an error occurred.
; --------------------------------------------------
getTimeStampCall
jsr .getTimeI2C
+copyTsIndirectTarget RTCI2C, TEMP_PTR
rts
.BTOX_TEMP
!byte 0
.HEX_NIBBLE_LO
!byte 0
.HEX_NIBBLE_HI
!byte 0
; --------------------------------------------------
; This routine splits a byte into nibbles and returns the nibbles
; as hex digits in the memory locations .HEX_NIBBLE_LO/HI
; --------------------------------------------------
btox
jsr splitByte
stx .BTOX_TEMP
tax
lda HEX_CHARS, X
sta .HEX_NIBBLE_HI
ldx .BTOX_TEMP
lda HEX_CHARS, X
sta .HEX_NIBBLE_LO
rts
!macro getTimeStr .targetAddr, .srcAddr {
+load16BitImmediate .targetAddr, TEMP_PTR
+load16BitImmediate .srcAddr, TEMP_PTR2
jsr formatTimeStrCall
}
; --------------------------------------------------
; This routine returns the time as read from the RTC as a string. The
; address of the receiving string has to be specified through
; TEMP_PTR/TEMP_PTR + 1. The string has to have room for at least 8 characters.
; The three bytes that are evaluated have to be referenced by TEMP_PTR2/
; TEMP_PTR2 + 1 and have to be BCD.
;
; This routine does not return an error.
; --------------------------------------------------
formatTimeStrCall
; process seconds
ldy #8
lda (TEMP_PTR2) ; load seconds as BCD value
jsr btox
lda .HEX_NIBBLE_LO
sta (TEMP_PTR), y
dey
lda .HEX_NIBBLE_HI
sta (TEMP_PTR), y
dey
lda #58 ; save colon
sta (TEMP_PTR), y
; process minutes
+inc16Bit TEMP_PTR2
dey
lda (TEMP_PTR2) ; load minutes as BCD value
jsr btox
lda .HEX_NIBBLE_LO
sta (TEMP_PTR), y
dey
lda .HEX_NIBBLE_HI
sta (TEMP_PTR), y
dey
lda #58 ; save colon
sta (TEMP_PTR), y
; process hours
+inc16Bit TEMP_PTR2
dey
lda (TEMP_PTR2) ; load hours as BCD value
jsr btox
lda .HEX_NIBBLE_LO
sta (TEMP_PTR), y
dey
lda .HEX_NIBBLE_HI
sta (TEMP_PTR), y
; write length byte at position 0
dey
lda #8
sta (TEMP_PTR),y
rts