; gettime.s12
; Implementation of the GETTIME routine, and a small program that performs a
; very simple test of the routine (i.e. for one known value).
;
; GETTIME inspects the 32-bit unsigned CLOCK value that is used to count the
; number of 10ms ticks since the beginning of time (i.e. system startup or
; something). GETTIME stores at the address pointed to by X the number of
; [X+0] = hours
; [X+1] = minutes
; [X+2] = seconds
; elapsed, according to CLOCK.
;
; Essentially:
; seconds = (CLOCK / 100) Mod 60;
; minutes = (CLOCK / 100 / 60) Mod 60;
; hours = (CLOCK / 100 / 60 / 60);
; The tricky part here is, of course, the fact that CLOCK is 32 bits and that
; the CPU12 doesn't really support 32 bit values. :-(
;
; We've got:
; * EDIV : Extended DIVision - 32 bits in (Y:D) divided by 16 bits in X
; results in 16 bits quotient in Y, and 16 bits remainder in D
;
; Problem 1: (CLOCK / 100) can not always be represented by a 16 bit value
; Solution 1.a: (for seconds) rearrange terms:
; seconds = (CLOCK Mod 6000) / 100
; Problem 2: The value (100*60*60) is larger than 0xFFFF (and again CLOCK/100,
; CLOCK/60 or CLOCK/6000, ... is not neccessarily smaller than 0xFFFF).
; Solution 2.a: (for hours):
; 60*60*100 = 2*2*3*5 * 2*2*3*5 * 2*2*5*5 = 2^6 * 3^2 * 5^4 = 2^6 * 5625
; Implement division with (60*60*100) as CLOCK / 2^6 / 5625, where division
; with 2^6 is implemented using right-shifting.
;
; To find the minutes, multiply hours with 60*60*100 (using something similar
; to the above), and subtract this from CLOCK. The result of this is at most
; (60*60*100)-1, which when divided by 100 is at most 3600 < 0xFFFF.
; Boilerplate code
ORG $1000
start:
movw #$0BAD,CLOCK+2 ; this should be 544 hours, 16 minutes
movw #$C0DE,CLOCK ; and 4 seconds (note 544 > 255, so timmar will
; be incorrect)
ldx #timmar
jsr GETTIME
bra start
; resultat
timmar RMB 1
minuter RMB 1
sek RMB 1
; clocka
CLOCK RMB 4
GETTIME:
; intro - store registers
pshd
pshy
pshx
; calc seconds : (CLOCK Mod 6000) / 100
ldd CLOCK
ldy CLOCK+2
ldx #6000
ediv ; D = CLOCK Mod 6000
ldy #0
ldx #100
ediv ; Y = (CLOCK Mod 6000) / 100
xgdy ; D <=> Y
pulx
stab 2,X
pshx
; calc hours
; problem: 60*60*100 = 360'000 > 0xFFFF :-(
; 60*60*100 = 2*2*3*5 * 2*2*3*5 * 2*2*5*5 = 2^6 * 3^2 * 5^4 = 2^6 * 5625
; I.e shift left by 6 and then divide by 5625. hooray
ldd CLOCK ; lower bits
lsrd ; 6x Logic Shift Right on D
lsrd
lsrd
lsrd
lsrd
lsrd
pshd ; put it away for now
ldaa CLOCK+3 ; bits 16 ... 23
lsla ; shift left twice => high bits = bits that would have
lsla ; been shiftet away
oraa 0,SP ; or with high bits from CLOCK-thing
staa 0,SP
ldd CLOCK+2 ; all high bits
lsrd ; 6x again
lsrd
lsrd
lsrd
lsrd
lsrd
puly ; put low bits in Y
xgdy ; Y <=> D
ldx #5625
ediv ; Y = (CLOCK >> 6) / 5625 = CLOCK / 360'000
xgdy ; Y <=> D
pulx
stab 0,X
pshx
; calc minutes
; multiply hours by 5625 and then 2^6; subtract this from CLOCK
; THEN divide stuff
ldy #5625 ; Note: hours is still in D
emul ; result in (Y:D)
xgdy ; Y <=> D
lsld ; 6x shift high bits to left
lsld
lsld
lsld
lsld
lsld
pshd ; and put that away (1)
xgdy ; Y <=> D
psha ; store A for later
lsra ; shift right twice => low bits = bits that would have
lsra ; been shifted out
oraa 2,SP ; or with low bits from (1)
staa 2,SP ;
pula ; restore whole low bits
lsld ; 6x shift low bits to left
lsld
lsld
lsld
lsld
lsld
ldy CLOCK+2 ; load clock value
pshd ; prepare subtraction (low bits)
ldd CLOCK
subd 0,SP
std 0,SP
xgdy
sbcb 3,SP
sbca 2,SP
std 2,SP
puld
puly ; so, the result of the subtraction is Y:D
ldx #6000
ediv ; temp1 = Y = (Clock - Hours*stuff) / (60*100)
xgdy
ldy #0
ldx #60
ediv ; D = temp1 Mod 60
pulx
stab 1,X ; store results
; phew, done
; outro - restore registers
;pulx ; already done
puly
puld
rts
;EOF --}}}1-- vim:syntax=asm:foldmethod=marker:ts=4:noexpandtab: