Hi,
OK, I think I've managed to get it a little better still. It struck me that we could just as well use the DIV32 trick in reverse as well, ie instead of preloading the variables we can do the multiplication and get the result as two 16 bit numbers. This allows us to to the subtraction with larger numbers instead of doing the iterative subtraction. Here's the code:
Code:
'=============================================================
' convert the one wire seconds count to HH:MM:SS
'=============================================================
HMS:
' This uses DIV32 by preloading the system variable DIV32 uses with
' the value we want to divide. To find out how many days has passed
' we divide the number of seconds with 86400 (number of seconds in a day).
' However, DIV32 only supports 16 bit divisor so we need to do the
' division in two steps, 21600 * 4 = 86400.
' Place the entire algorithm in an If/Then test to limit the total seconds to
' a 31-bit number and to check that the DIV32 will return a result <= 16-bits.
' This may seem like limit but it's still something like 44 years.
if AB < $5460 then ' Result of DIV32 will return a quotient that is 16-bit
' and caps the total seconds to 31-bits.
R0 = AB ' Load high word of seconds into system var
R2 = CD ' Load low word of seconds into system var
owDays = DIV32 21600 ' Divide by 86400 in two steps
owDays = owDays / 4
' Now we need to subtract 86400 seconds from the total
' running time for each full day that has passed.
' First we do a dummy multiplication. We would like to multiply by
' 86400 but we can't (since it doesn't fit in a WORD) so we start
' by multiplying by 1/2 of that, or 43200.
Temp1 = owDays * 43200
' Then we retreive the 32bit result of the multiplication from system variables
Temp2 = R0 ' Get high word from system
Temp3 = R2 ' Get low word from system
' And multiply by 2 to get to 86400
Temp2 = Temp2 << 1 ' Shift high word 1 bit
Temp2.0 = Temp3.15 ' Move high bit of LSW into low bit of MSW
Temp3 = Temp3 << 1 ' Shift low word on bit
' Now do the subtraction
Gosub SubtractTime
' At this point ABCD contains anything from 0 to 86399 and we need to
' figure out how many "full hours" is in it. Because 86399 is more than
' what fits in a 16-bit word we're using DIV32 trick to divide the
' number of seconds by 3600 (number of seconds in an hour).
R0 = AB ' High word of what's left in seconds into system var
R2 = CD ' Low word of what's left in seconds into system var
owHH = DIV32 3600 ' Divide by the number of seconds in an hour
' Now we need to subtract 3600 seconds per full hour from the
' total number of seconds passed. As before we do a dummy multiplication
' and retreive the result from the system variables.
Temp1 = owHH * 3600
Temp2 = R0 ' Get high word from system
Temp3 = R2 ' Get low word from system
' Now do the subtraction
Gosub SubtractTime
' Now, we're down to minutes and there can be no more than 3599 seconds left
' so we can easily handle it with just the low word and normal math
owMM = CD / 60
' And finally we can get the seconds by getting the reminder.
owSS = CD // 60
else 'Result of DIV32 21600 will return 65535 because it is > 16-bit
'or the total seconds was a 32-bit number.
'Create an error value for output
owDays = 99
owHH = 99
owMM = 99
owSS = 99
endif
return
SubtractTime:
Temp1 = CD ' Remember low word
CD = CD - Temp3 ' Subtract low word
If Temp1 < CD THEN ' Did we underflow
AB = AB - 1 ' Then borrow
ENDIF
AB = AB - Temp2 ' Subtract high word
RETURN
It does use two extra WORD variables (and we could possible do something about that if strictly needed) but that's a small price to pay. It's now smaller than before and the complete routine (when fed Test Case 5) runs in about 1730 instruction cycles compared to the original 420200.
Try it out if you want.
/Henrik.
Bookmarks