Discussion:
Subtracting times
(too old to reply)
Alan Adams
2020-02-18 11:55:52 UTC
Permalink
My head is hurting.

I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.

Complications arise at the point where the low word goes from &7fffffff to
&80000000, and where the high byte increases.

BASIC doesn't allow access to the carry or borrow from arithmetic
operations.

I've developed the following which *seems* to work correctly. However I'm
not sure about all the edge cases.

This is part of a timing system, Next February the high byte of times will
change from &57 to &58, and I would like this code to survive working
across this boundary. I need to reliably detect negative results, which
can occur if a spurious finish time pulse arrives before a start time.

Subtract stamp2 from stamp1 and store the result in resultbuf.
( Although the result is 5 bytes. the elapsed time will always be
contained in the low 4 bytes. This will only overflow after 1 year, 5
months and 2 hours 27 minutes. The system doesn't meed to run for that
long. )

PROCsubstamp(resultbuf%,stamp1%,stamp2%)
LOCAL I%,J%,K%,L%,borrow%
borrow%=0
FOR I%=0 TO3
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
resultbuf%?I%=L%AND&FF
IF L% < 0 THEN borrow%=1 ELSE borrow%=0
NEXT
I%=4
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
IF L% < 0 THEN
resultbuf%!0 = -(resultbuf%!0)
ENDIF
resultbuf%?I%=L%AND&FF
ENDPROC
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Jean-Michel
2020-02-18 18:11:22 UTC
Permalink
Post by Alan Adams
My head is hurting.
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
Complications arise at the point where the low word goes from &7fffffff to
&80000000, and where the high byte increases.
BASIC doesn't allow access to the carry or borrow from arithmetic
operations.
I've developed the following which *seems* to work correctly. However I'm
not sure about all the edge cases.
This is part of a timing system, Next February the high byte of times will
change from &57 to &58, and I would like this code to survive working
across this boundary. I need to reliably detect negative results, which
can occur if a spurious finish time pulse arrives before a start time.
Subtract stamp2 from stamp1 and store the result in resultbuf.
( Although the result is 5 bytes. the elapsed time will always be
contained in the low 4 bytes. This will only overflow after 1 year, 5
months and 2 hours 27 minutes. The system doesn't meed to run for that
long. )
PROCsubstamp(resultbuf%,stamp1%,stamp2%)
LOCAL I%,J%,K%,L%,borrow%
borrow%=0
FOR I%=0 TO3
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
resultbuf%?I%=L%AND&FF
IF L% < 0 THEN borrow%=1 ELSE borrow%=0
NEXT
I%=4
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
IF L% < 0 THEN
resultbuf%!0 = -(resultbuf%!0)
ENDIF
resultbuf%?I%=L%AND&FF
ENDPROC
from Steve Drain (ROOL Forum 18/12/2016)

I wrote a library to support some of this this, as have others. It is a
candidate for Basalt, someday. ;-)

Look at http://kappa.me.uk/Libraries/lbLongs032.zip





Jean-Michel
--
Jean-Michel
Steve Drain
2020-02-21 11:28:37 UTC
Permalink
Post by Jean-Michel
Post by Alan Adams
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
from Steve Drain (ROOL Forum 18/12/2016)
I wrote a library to support some of this this, as have others. It is a
candidate for Basalt, someday. ;-)
Look at http://kappa.me.uk/Libraries/lbLongs032.zip
I suspect that this problem has been satisfactorily solved in plain BASIC, but that library I wrote a good while ago uses fragments of assembly to do the heavy lifting, because of the carry etc.

For interest's sake I put together a couple of routines to deal solely with 5-byte times using assembly at:

http://www.kappa.me.uk/Miscellaneous/swTimeDiff.zip

I am not sure that I have got it right, so please pull it apart. ;-)
Alan Adams
2020-02-21 12:45:19 UTC
Permalink
Post by Steve Drain
Post by Jean-Michel
Post by Alan Adams
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
from Steve Drain (ROOL Forum 18/12/2016)
I wrote a library to support some of this this, as have others. It is a
candidate for Basalt, someday. ;-)
Look at http://kappa.me.uk/Libraries/lbLongs032.zip
I suspect that this problem has been satisfactorily solved in plain BASIC,
but that library I wrote a good while ago uses fragments of assembly to do
the heavy lifting, because of the carry etc.
In BASIC it has to be done byte-by-byte, or using half-words, so that the
borrow/carry can be detected.
Post by Steve Drain
For interest's sake I put together a couple of routines to deal solely
http://www.kappa.me.uk/Miscellaneous/swTimeDiff.zip
I am not sure that I have got it right, so please pull it apart. ;-)
It looks good. I'll test it in a bit.

The boundary conditions seem to be when the low word goes from 7fffffff to
80000000, when it goes from ffffffff to 00000000, and in each case when
the arguments swap.

Then there are the cases where the high bytes differ.

There's no way to indicate with an integer return, the result when the
high bytes differ by more than 1. Those cases are when the times are more
than a year and a half apart, so in my case, won't occur.
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Alan Adams
2020-02-21 15:17:20 UTC
Permalink
Post by Steve Drain
Post by Jean-Michel
Post by Alan Adams
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
from Steve Drain (ROOL Forum 18/12/2016)
I wrote a library to support some of this this, as have others. It is a
candidate for Basalt, someday. ;-)
Look at http://kappa.me.uk/Libraries/lbLongs032.zip
I suspect that this problem has been satisfactorily solved in plain BASIC,
but that library I wrote a good while ago uses fragments of assembly to do
the heavy lifting, because of the carry etc.
For interest's sake I put together a couple of routines to deal solely
http://www.kappa.me.uk/Miscellaneous/swTimeDiff.zip
I am not sure that I have got it right, so please pull it apart. ;-)
I ran 10 sets of test data. I think it gets it wrong for the last 4. In
each case the sign is wrong for the returned integer. The values are too
small as well. These are the cases where the result is too large for an
integer to hold.

The 5-byte integer values looks correct, but cannot be returned to BASIC.

I compared it with my BASIC routine. The adjustment I made after the last
subtraction was wrong. Omitting it returned the correct answer.

A$ is the bytes (high to low) in a%, similarly for B$ and C$.
the dates are the conversion iof A% and B% to dates.

14:51:20.29 ** Clear **
A$="00 00 00 00 20 " B$="00 7F FF FF C0 " C$="FF 80 00 00 60 "
!C%=-2147483552
A$="00:00:00 01-Jan-1900" B$="13:13:55 06-Sep-1900" !C%=-2147483552

A$="00 7F FF FF C0 " B$="00 00 00 00 20 " C$="00 7F FF FF A0 "
!C%=2147483552
A$="13:13:55 06-Sep-1900" B$="00:00:00 01-Jan-1900" !C%=2147483552

A$="00 80 00 00 20 " B$="00 7F FF FF C0 " C$="00 00 00 00 60 " !C%=96
A$="13:13:56 06-Sep-1900" B$="13:13:55 06-Sep-1900" !C%=96

A$="00 7F FF FF C0 " B$="00 80 00 00 20 " C$="FF FF FF FF A0 " !C%=-96
A$="13:13:55 06-Sep-1900" B$="13:13:56 06-Sep-1900" !C%=-96

A$="00 FF FF FF 20 " B$="01 00 00 00 C0 " C$="FF FF FF FE 60 " !C%=-416
A$="02:27:50 13-May-1901" B$="02:27:54 13-May-1901" !C%=-416

A$="01 00 00 00 C0 " B$="00 FF FF FF 20 " C$="00 00 00 01 A0 " !C%=416
A$="02:27:54 13-May-1901" B$="02:27:50 13-May-1901" !C%=416

A$="01 FF FF FF 20 " B$="00 00 00 00 C0 " C$="01 FF FF FE 60 " !C%=-416
A$="04:55:43 22-Sep-1902" B$="00:00:01 01-Jan-1900" !C%=-416

A$="00 00 00 00 C0 " B$="01 FF FF FF 20 " C$="FE 00 00 01 A0 " !C%=416
A$="00:00:01 01-Jan-1900" B$="04:55:43 22-Sep-1902" !C%=416

A$="FF FF FF FF 20 " B$="00 00 00 00 C0 " C$="FF FF FF FE 60 " !C%=-416
A$="06:57:55 03-Jun-2248" B$="00:00:01 01-Jan-1900" !C%=-416

A$="00 00 00 00 C0 " B$="FF FF FF FF 20 " C$="00 00 00 01 A0 " !C%=416
A$="00:00:01 01-Jan-1900" B$="06:57:55 03-Jun-2248" !C%=416
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Steve Drain
2020-02-22 10:37:24 UTC
Permalink
Post by Alan Adams
Post by Steve Drain
For interest's sake I put together a couple of routines to deal solely
http://www.kappa.me.uk/Miscellaneous/swTimeDiff.zip
I am not sure that I have got it right, so please pull it apart. ;-)
I ran 10 sets of test data. I think it gets it wrong for the last 4. In
each case the sign is wrong for the returned integer. The values are too
small as well. These are the cases where the result is too large for an
integer to hold.
I can see that, and was right to be sceptical of my own code.
Post by Alan Adams
The 5-byte integer values looks correct, but cannot be returned to BASIC.
Yes, the subtraction is fine, but does not produce the desired result.

If the BASIC version is fine, I think I will leave it there.
Steve Drain
2020-02-25 16:05:38 UTC
Permalink
Post by Steve Drain
If the BASIC version is fine, I think I will leave it there.
Last words. Adapting a technique I used to implement 'magic numbers' for
assembler division by a constant I have come up with:

DEFFNdiff(a%,b%)
LOCAL a,b,c:c=2^32
IF !a%<0 THEN a=c-(NOT!a%+1) ELSE a=!a%
IF !b%<0 THEN b=c-(NOT!b%+1) ELSE b=!b%
=(a%!4-b%!4)*c+a-b

The returned value is a signed float and if assigned to an integer it
will give a 'Number to big' error if out of the range you want. I think
it is exact within that range.

I will be interested to know where the bugs are. ;-)
Alan Adams
2020-02-25 16:35:46 UTC
Permalink
Post by Steve Drain
Post by Steve Drain
If the BASIC version is fine, I think I will leave it there.
Last words. Adapting a technique I used to implement 'magic numbers' for
DEFFNdiff(a%,b%)
LOCAL a,b,c:c=2^32
IF !a%<0 THEN a=c-(NOT!a%+1) ELSE a=!a%
IF !b%<0 THEN b=c-(NOT!b%+1) ELSE b=!b%
=(a%!4-b%!4)*c+a-b
That's so terse it makes my eyes water.

I don't think I will try to understand it.

Alan
Post by Steve Drain
The returned value is a signed float and if assigned to an integer it
will give a 'Number to big' error if out of the range you want. I think
it is exact within that range.
I will be interested to know where the bugs are. ;-)
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Steve Drain
2020-02-27 17:10:49 UTC
Permalink
Post by Alan Adams
That's so terse it makes my eyes water.
I don't think I will try to understand it.
The original problem could be solved if BASIC would do arithmetic with
unsigned integers. A 5-byte float can hold an unsigned integer exactly,
but we have to get those integers into floats. I think the method I
posted earlier is actually over-thought and more elaborate than it needs
to be. Please let me know if this works for you:

DEFFNdiff(a%,b%)
LOCAL a,b,c:c=2^32
IF !a%<0 THEN a=c+!a% ELSE a=!a%
IF !b%<0 THEN b=c+!b% ELSE b=!b%
=(a%!4-b%!4)*c+a-b

This, some other ramblings, and an extension of the assembler routine
that Martin posted, which will return negative values, are at:

http://www.kappa.me.uk/Miscellaneous/swTimeDiff.zip

I think the assembler routine is the most useful, but the BASIC one is
quite satisfying. ;-)
Steve Drain
2020-02-27 18:06:40 UTC
Permalink
Ooops!

=(a%?4-b%?4)*c+a-b
Alan Adams
2020-02-28 11:21:49 UTC
Permalink
Post by Steve Drain
Post by Alan Adams
That's so terse it makes my eyes water.
I don't think I will try to understand it.
The original problem could be solved if BASIC would do arithmetic with
unsigned integers. A 5-byte float can hold an unsigned integer exactly,
but we have to get those integers into floats. I think the method I
posted earlier is actually over-thought and more elaborate than it needs
With your followup correction, yes it works well.
Post by Steve Drain
DEFFNdiff(a%,b%)
LOCAL a,b,c:c=2^32
IF !a%<0 THEN a=c+!a% ELSE a=!a%
IF !b%<0 THEN b=c+!b% ELSE b=!b%
=(a%!4-b%!4)*c+a-b
This, some other ramblings, and an extension of the assembler routine
http://www.kappa.me.uk/Miscellaneous/swTimeDiff.zip
I think the assembler routine is the most useful, but the BASIC one is
quite satisfying. ;-)
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
j***@gmail.com
2020-04-22 19:00:24 UTC
Permalink
Sorry that I'm late to the party here. In order to calculate the elapsed time you can just subtract the lower 4 bytes.

So if Start% is the lower four bytes of the start time & Finish% is the lower four bytes of the finish time:

TimeTaken% = Finish% - Start%

This "Just works" and will always produce the correct result even with rollovers.

If TimeTaken% < 0 then the finish was before the start.

Jeff
Martin
2020-04-22 23:07:24 UTC
Permalink
On 22 Apr in article
Post by j***@gmail.com
Sorry that I'm late to the party here. In order to calculate the
elapsed time you can just subtract the lower 4 bytes.
So if Start% is the lower four bytes of the start time & Finish% is
TimeTaken% = Finish% - Start%
This "Just works" and will always produce the correct result even with rollovers.
If TimeTaken% < 0 then the finish was before the start.
I don't think this works if the time difference is over 35 weeks....
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
j***@gmail.com
2020-04-23 10:13:55 UTC
Permalink
Post by Martin
On 22 Apr in article
Post by j***@gmail.com
Sorry that I'm late to the party here. In order to calculate the
elapsed time you can just subtract the lower 4 bytes.
So if Start% is the lower four bytes of the start time & Finish% is
TimeTaken% = Finish% - Start%
This "Just works" and will always produce the correct result even with rollovers.
If TimeTaken% < 0 then the finish was before the start.
I don't think this works if the time difference is over 35 weeks....
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
No, but Alan is timing a canoeist down a slalom course. If it takes 35 weeks then there's something amiss.
Martin
2020-04-23 17:01:39 UTC
Permalink
On 23 Apr in article
Post by j***@gmail.com
Post by Martin
On 22 Apr in article
Post by j***@gmail.com
Sorry that I'm late to the party here. In order to calculate the
elapsed time you can just subtract the lower 4 bytes.
So if Start% is the lower four bytes of the start time &
TimeTaken% = Finish% - Start%
This "Just works" and will always produce the correct result even with rollovers.
If TimeTaken% < 0 then the finish was before the start.
I don't think this works if the time difference is over 35
weeks....
No, but Alan is timing a canoeist down a slalom course. If it
takes 35 weeks then there's something amiss.
Indeed. But what happens when the 5-byte hex times are...
start time = 58 00 00 04 00
end time = 57 00 00 05 00
gives x100 ... when it should be flagged as negative and duff!
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
John Williams (News)
2020-04-23 17:06:44 UTC
Permalink
Post by Martin
Indeed. But what happens when the 5-byte hex times are...
start time = 58 00 00 04 00
end time = 57 00 00 05 00
gives x100 ... when it should be flagged as negative and duff!
I've already added a note in my programming library, but can we have a
definitive answer on this, please!

An agreed one that I can save as a note!

John
--
John WILLIAMS, now back in the UK - no attachments to these addresses!
Non-RISC OS posters change user to johnrwilliams or put 'risc' in subject!
Who is John WILLIAMS? http://petit.four.free.fr/picindex/author/
j***@gmail.com
2020-04-23 18:18:44 UTC
Permalink
Post by Martin
Indeed. But what happens when the 5-byte hex times are...
start time = 58 00 00 04 00
end time = 57 00 00 05 00
gives x100 ... when it should be flagged as negative and duff!
That would require the judge on the finish line to press his button 35 weeks before the starter presses his button. Cannot happen.
Martin
2020-04-23 20:23:51 UTC
Permalink
On 23 Apr in article
Post by j***@gmail.com
Post by Martin
Indeed. But what happens when the 5-byte hex times are...
start time = 58 00 00 04 00
end time = 57 00 00 05 00
gives x100 ... when it should be flagged as negative and duff!
That would require the judge on the finish line to press his button
35 weeks before the starter presses his button. Cannot happen.
Unfortunately the OP in his first post said...
Post by j***@gmail.com
I need to reliably detect negative results, which can occur if a
spurious finish time pulse arrives before a start time.
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
David Higton
2020-04-24 14:24:46 UTC
Permalink
Post by Martin
On 23 Apr in article
Post by Martin
Indeed. But what happens when the 5-byte hex times are...
start time = 58 00 00 04 00
end time = 57 00 00 05 00 gives x100 ... when it should be
flagged as negative and duff!
That would require the judge on the finish line to press his button 35
weeks before the starter presses his button. Cannot happen.
Unfortunately the OP in his first post said...
I need to reliably detect negative results, which can occur if a spurious
finish time pulse arrives before a start time.
Still OK; it won't go wrong unless the spurious pulse arrives between
(roughly) 17 weeks and 35 weeks before the start time.

David

David Higton
2020-04-23 19:57:52 UTC
Permalink
Post by Martin
On 23 Apr in article
Post by j***@gmail.com
Post by Martin
On 22 Apr in article
Post by j***@gmail.com
Sorry that I'm late to the party here. In order to calculate the
elapsed time you can just subtract the lower 4 bytes.
So if Start% is the lower four bytes of the start time & Finish% is
TimeTaken% = Finish% - Start%
This "Just works" and will always produce the correct result even
with rollovers.
If TimeTaken% < 0 then the finish was before the start.
I don't think this works if the time difference is over 35 weeks....
No, but Alan is timing a canoeist down a slalom course. If it takes 35
weeks then there's something amiss.
Indeed. But what happens when the 5-byte hex times are...
start time = 58 00 00 04 00
end time = 57 00 00 05 00 gives x100 ... when it should be flagged as
negative and duff!
As has already been pointed out, the answers are all correct within a
certain range of times, something like 35 weeks, which is orders of
magnitude greater than can happen in this use case.

It's not a general solution, but it is entirely correct and appropriate
for what the OP's needs.

David
j***@mdfs.net
2020-02-24 01:39:51 UTC
Permalink
Post by Alan Adams
The 5-byte integer values looks correct, but cannot be returned to BASIC.
While you can't use the 5-byte integers, you can store and retrieve
them with | and hold them in reals, as long as you treat them as
opaque values and don't "look" at them. So you can do: |here%=|there%
and bar=FNfoo with DEFFNfoo ending with =|somewhere

I do this with my Phone library to hold UK telephone numbers in
5-byte objects.

jgh
Steve Drain
2020-02-24 10:23:59 UTC
Permalink
Post by j***@mdfs.net
Post by Alan Adams
The 5-byte integer values looks correct, but cannot be returned to BASIC.
While you can't use the 5-byte integers, you can store and retrieve
them with | and hold them in reals, as long as you treat them as
opaque values and don't "look" at them. So you can do: |here%=|there%
and bar=FNfoo with DEFFNfoo ending with =|somewhere
In Basalt time values are held in float variables in just this way, but
the manipulation is transparent when used with appropriate keywords.

And I, too, have a more-or-less equivalent library in just BASIC.

In neither is there a function to satisfy the OP. The nearest is a time
difference in seconds, which always fits into an signed integer.
Alan Adams
2020-02-24 11:12:00 UTC
Permalink
Post by Steve Drain
Post by j***@mdfs.net
Post by Alan Adams
The 5-byte integer values looks correct, but cannot be returned to BASIC.
While you can't use the 5-byte integers, you can store and retrieve
them with | and hold them in reals, as long as you treat them as
opaque values and don't "look" at them. So you can do: |here%=|there%
and bar=FNfoo with DEFFNfoo ending with =|somewhere
In Basalt time values are held in float variables in just this way, but
the manipulation is transparent when used with appropriate keywords.
And I, too, have a more-or-less equivalent library in just BASIC.
In neither is there a function to satisfy the OP. The nearest is a time
difference in seconds, which always fits into an signed integer.
And thank you to all who have contributed. I now have several options to
use. My preference is Martin's, because although all the options return
the time difference in an integer for valid ranges, Martin's returns a
defined -1 to indicate negative or overflow, which helps with the calling
code.
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Martin
2020-02-24 13:05:57 UTC
Permalink
Post by Alan Adams
My preference is Martin's, because although all the options return
the time difference in an integer for valid ranges, Martin's
returns a defined -1 to indicate negative or overflow, which helps
with the calling code.
In in case anyone is interested, I have posted it below. I had to
send it direct to Alan because I was away with no csap access.

DEF FNsub(A%,B%) = USR tsub% :REM = B% - A% (=> 5 byte times)
REM Returns -1 (error) if result negative,
REM or over &7fffffff (~35 weeks)

.tsub%
; Entry r0 = A% => 5 byte start time (word aligned) lsb..msb
; r1 = B% => 5 byte stop time (word aligned) lsb..msb
; Exit r0 = B%-A% 4 byte time difference or -1 if start after stop
; r0 = r3 - r2 (low word)
; r4 = r6 - r5 (high byte)
LDR r2,[r0] ; get low 4 bytes start
LDR r3,[r1] ; get low 4 bytes stop
LDRB r5,[r0,#4] ; get high byte start
LDRB r6,[r1,#4] ; get high byte stop
SUBS r0,r3,r2 ; get low bytes difference
SBCS r4,r6,r5 ; get high bytes difference - set Z if zero
; r4 = 0 if positive, -1 if negative, else over ~70 weeks
Bne error ;
TST r0,#1<<31 ; r0 'negative' if over 35 weeks
MOVeq pc,r14 ; ok to return positive result

.error
; Return error indicator if negative or > ~35 weeks positive
MVN r0,#0 ; set return to -1 to indicate error
MOV pc,r14

It could be extended, and prbably improved!

Martin
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
Matthew Phillips
2020-02-19 08:35:46 UTC
Permalink
Post by Alan Adams
My head is hurting.
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
Complications arise at the point where the low word goes from &7fffffff to
&80000000, and where the high byte increases.
BASIC doesn't allow access to the carry or borrow from arithmetic
operations.
I've developed the following which *seems* to work correctly. However I'm
not sure about all the edge cases.
I don't think it's doing quite what you want it to do. From the above, my
understanding is that you want a signed result, so that if stamp1% is
three centiseconds bigger than stamp2% you want a positive number (listing
the bytes in memory order):

03 00 00 00 00

and if stamp1% is three centiseconds less than stamp2% you want a negative
result:

FD FF FF FF FF

I also take it that you want a five-byte result, but that in your use case
four bytes would always contain the differences.
Post by Alan Adams
This is part of a timing system, Next February the high byte of times will
change from &57 to &58, and I would like this code to survive working
across this boundary. I need to reliably detect negative results, which
can occur if a spurious finish time pulse arrives before a start time.
Subtract stamp2 from stamp1 and store the result in resultbuf.
( Although the result is 5 bytes. the elapsed time will always be
contained in the low 4 bytes. This will only overflow after 1 year, 5
months and 2 hours 27 minutes. The system doesn't meed to run for that
long. )
PROCsubstamp(resultbuf%,stamp1%,stamp2%)
LOCAL I%,J%,K%,L%,borrow%
borrow%=0
FOR I%=0 TO3
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
resultbuf%?I%=L%AND&FF
IF L% < 0 THEN borrow%=1 ELSE borrow%=0
NEXT
I%=4
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
IF L% < 0 THEN
resultbuf%!0 = -(resultbuf%!0)
ENDIF
If you want the absolute difference, so that in the above examples of stamp1%
and stamp2% differing by 3 centiseconds you would always get 03 00 00 00 00
no matter which of the two was the larger value, you should retain the three
lines above but change the following line to resultbuf%?I%=0

But that's only if you're sure the result fits in 4 bytes.
Post by Alan Adams
resultbuf%?I%=L%AND&FF
ENDPROC
If you wnat a signed result, you can simplify by making the FOR-NEXT loop
with I% run from 0 to 4 and lose all the lines after the NEXT.
--
Matthew Phillips
Durham
Alan Adams
2020-02-19 10:03:26 UTC
Permalink
Post by Matthew Phillips
Post by Alan Adams
My head is hurting.
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
Complications arise at the point where the low word goes from &7fffffff to
&80000000, and where the high byte increases.
BASIC doesn't allow access to the carry or borrow from arithmetic
operations.
I've developed the following which *seems* to work correctly. However I'm
not sure about all the edge cases.
I don't think it's doing quite what you want it to do. From the above, my
understanding is that you want a signed result, so that if stamp1% is
three centiseconds bigger than stamp2% you want a positive number (listing
03 00 00 00 00
and if stamp1% is three centiseconds less than stamp2% you want a negative
FD FF FF FF FF
I also take it that you want a five-byte result, but that in your use case
four bytes would always contain the differences.
I do want a 4-byte result - the final stage after calling this is

interval%=!resultbuf%. i need this value to be correctly signed.
Post by Matthew Phillips
Post by Alan Adams
This is part of a timing system, Next February the high byte of times will
change from &57 to &58, and I would like this code to survive working
across this boundary. I need to reliably detect negative results, which
can occur if a spurious finish time pulse arrives before a start time.
<snip>
Post by Matthew Phillips
Post by Alan Adams
IF L% < 0 THEN
resultbuf%!0 = -(resultbuf%!0)
ENDIF
That bit is intended to ensure that the 40-byte part is correctly signed.
Post by Matthew Phillips
If you want the absolute difference, so that in the above examples of stamp1%
and stamp2% differing by 3 centiseconds you would always get 03 00 00 00 00
no matter which of the two was the larger value, you should retain the three
lines above but change the following line to resultbuf%?I%=0
But that's only if you're sure the result fits in 4 bytes.
If anyone takes a year and a half to paddle round a slalom course, we can
time them with a calendar.
Post by Matthew Phillips
Post by Alan Adams
resultbuf%?I%=L%AND&FF
ENDPROC
If you wnat a signed result, you can simplify by making the FOR-NEXT loop
with I% run from 0 to 4 and lose all the lines after the NEXT.
The following shows why this makes my head hurt.

adding two positive numbers shouldn't produce a negative result.
Post by Matthew Phillips
A%=&7FFFFFFF
PRINT A%
2.14748365E9
Post by Matthew Phillips
B%=A%+1
PRINT B%
-2.14748365E9
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Sebastian Barthel
2020-02-19 13:04:17 UTC
Permalink
Post by Alan Adams
Post by Matthew Phillips
Post by Alan Adams
My head is hurting.
I need a BASIC routine to subtract one 5-byte time from another and
return a correctly signed result.
Complications arise at the point where the low word goes from
&7fffffff to &80000000, and where the high byte increases.
BASIC doesn't allow access to the carry or borrow from arithmetic
operations.
But that's only if you're sure the result fits in 4 bytes.
If anyone takes a year and a half to paddle round a slalom course, we
can time them with a calendar.
Post by Matthew Phillips
Post by Alan Adams
resultbuf%?I%=L%AND&FF
ENDPROC
If you wnat a signed result, you can simplify by making the FOR-NEXT
loop with I% run from 0 to 4 and lose all the lines after the NEXT.
The following shows why this makes my head hurt.
adding two positive numbers shouldn't produce a negative result.
Post by Matthew Phillips
A%=&7FFFFFFF PRINT A%
2.14748365E9
Post by Matthew Phillips
B%=A%+1 PRINT B%
-2.14748365E9
But: that's no problem at all since You are NOT doing some kind of
integer mathematics here. You are trying to change the bytes as they are
stored in memory.

The example with B%=A%+1 results in a "higher" value in memory - but the
BASIC interprets it as a signed value, and therefore it changes to
negative value if the top-most bit has been set.
This only means that You can't print this values directly via PRINT -
there is always the need to use an output-procedure if a "time" value is
intended.


If You really know that the times measured never (!) execeed the 4 Bytes
then I dont know why anyone should deal with the highest (the 5th) Byte.
If these time stamp%s are taken as start and stop times then they are
ordered since You already know wich is the higher/later one.


One thing - and thats the first I wanted to mention - more:
If this is on a 32 Bit Machine (as an Archimedes or RPC or Pi) then there
are only 4 Bytes per integer variable% !
If You use a construct such as stamp1%?I% with I%=4 there will be a result
- but probably not the one, wich gives the correct value for the 5th
Byte. I think this should give You the least significant Byte of the next
32Bit variable wich is stored in memory directly after (behind) the
stamp1% . This means: stamp1%?4 doesn't give the 5th Byte of stamp1% as
intendend, but will result in Byte 1 of stamp2% ( hence the same as
stamp2%?0 ).


All the best,
SBn
Alan Adams
2020-02-19 16:41:53 UTC
Permalink
Post by Sebastian Barthel
Post by Alan Adams
Post by Matthew Phillips
Post by Alan Adams
My head is hurting.
I need a BASIC routine to subtract one 5-byte time from another and
return a correctly signed result.
Complications arise at the point where the low word goes from
&7fffffff to &80000000, and where the high byte increases.
BASIC doesn't allow access to the carry or borrow from arithmetic
operations.
But that's only if you're sure the result fits in 4 bytes.
If anyone takes a year and a half to paddle round a slalom course, we
can time them with a calendar.
Post by Matthew Phillips
Post by Alan Adams
resultbuf%?I%=L%AND&FF
ENDPROC
If you wnat a signed result, you can simplify by making the FOR-NEXT
loop with I% run from 0 to 4 and lose all the lines after the NEXT.
The following shows why this makes my head hurt.
adding two positive numbers shouldn't produce a negative result.
Post by Matthew Phillips
A%=&7FFFFFFF PRINT A%
2.14748365E9
Post by Matthew Phillips
B%=A%+1 PRINT B%
-2.14748365E9
But: that's no problem at all since You are NOT doing some kind of
integer mathematics here. You are trying to change the bytes as they are
stored in memory.
The example with B%=A%+1 results in a "higher" value in memory - but the
BASIC interprets it as a signed value, and therefore it changes to
negative value if the top-most bit has been set.
This only means that You can't print this values directly via PRINT -
there is always the need to use an output-procedure if a "time" value is
intended.
The inconsistency is that BASIC shows a number as signed, but treats it in
addition as unsigned. To be consistent it should signal an overflow in the
example above.
Post by Sebastian Barthel
If You really know that the times measured never (!) execeed the 4 Bytes
then I dont know why anyone should deal with the highest (the 5th) Byte.
If these time stamp%s are taken as start and stop times then they are
ordered since You already know wich is the higher/later one.
I don't know which, that's where the problem lies.

Suppose the finisher knocks his button. Then the starter starts someone.
We now have a finish timestamp which is before a start timestamp. To
detect this I need to get the difference returned as a negative number.

The 5th byte becomes important in Februiary 2021. Currently it has the
value &57. Next Feb it moves to &58. I need to ensure that a start with
byte5 as 57 and a finish with a byte5 of 58 doesn't result in a negative
number, nor a ridiculously large one. While it would potentially only
affect a small number of runs, it could also result in a crash somewhere
in the system when presented with invalid data.
Post by Sebastian Barthel
If this is on a 32 Bit Machine (as an Archimedes or RPC or Pi) then there
are only 4 Bytes per integer variable% !
If You use a construct such as stamp1%?I% with I%=4 there will be a result
- but probably not the one, wich gives the correct value for the 5th
Byte. I think this should give You the least significant Byte of the next
32Bit variable wich is stored in memory directly after (behind) the
stamp1% . This means: stamp1%?4 doesn't give the 5th Byte of stamp1% as
intendend, but will result in Byte 1 of stamp2% ( hence the same as
stamp2%?0 ).
I'm not using the 5th byte as part of the answer. I'm using the
calculation of the 5th byte to determine whether to negate the result that
I do use, i.e. bytes 1 to 4. There's no reason in fact to store it after
calculating it - that's left over from some testing situations.
Post by Sebastian Barthel
All the best,
SBn
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
j***@mdfs.net
2020-02-19 20:01:03 UTC
Permalink
When I've done this I've manipulated the 5-byte number as
two 3-byte numbers, something like:

REM time1%=>first 5-byte time
REM time2%=>second 5-byte time
REM sum% =>7 bytes to hold 5-byte result
REM temp1A%=>7 bytes to hold intermediate result
REM temp1B%=>7 bytes to hold intermediate result
REM temp2A%=>7 bytes to hold intermediate result
REM temp2B%=>7 bytes to hold intermediate result
:
temp1A%!0=time1%!0:temp1A%?3=0
temp1B%!0=time1%!3:temp1B%!5=0
:
temp2A%!0=time2%!0:temp2A%?3=0
temp2B%!0=time2%!3:temp2B%!5=0
:
sum%!0=temp1A%!0+temp2A%!0
sum%!3=temp1B%!0+temp2B%!0+sum%?3
:
(similar for subtraction)

The memory use and operations can be optimised, and reading the
thread I'd probably go with something like:

REM time1%=>first 5-byte time
REM time2%=>second 5-byte time
REM sum% =>8 bytes to hold 5-byte result

sum%!0=0:sum%!4=0
FOR A%=0 TO 4
sum%!A%=time1%?A%+time2%?A%+sum%?A%
NEXT A%

(similar for subtraction)
(I've been staring at that for a while, it looks
too simple to be correct!)

jgh
Matthew Phillips
2020-02-23 20:46:26 UTC
Permalink
Post by Alan Adams
Post by Matthew Phillips
Post by Alan Adams
My head is hurting.
I need a BASIC routine to subtract one 5-byte time from another and return
a correctly signed result.
Complications arise at the point where the low word goes from &7fffffff to
&80000000, and where the high byte increases.
BASIC doesn't allow access to the carry or borrow from arithmetic
operations.
I've developed the following which *seems* to work correctly. However I'm
not sure about all the edge cases.
I don't think it's doing quite what you want it to do. From the above, my
understanding is that you want a signed result, so that if stamp1% is
three centiseconds bigger than stamp2% you want a positive number (listing
03 00 00 00 00
and if stamp1% is three centiseconds less than stamp2% you want a negative
FD FF FF FF FF
I also take it that you want a five-byte result, but that in your use case
four bytes would always contain the differences.
I do want a 4-byte result - the final stage after calling this is
interval%=!resultbuf%. i need this value to be correctly signed.
When you say "correctly signed" do you mean that you always want the result
to be positive, so that it expresses the difference in time between stamp1%
and stamp2% regardless of which is the earlier?

Or do you mean that you want the answer to be negative if stamp1% is
earlier than stamp2% and positive if stamp2% is earlier?

I was assuming the latter from your other postings, because you say that you
need to detect the condition of the finisher knocking a button before the
starter starts someone.

If you want the result to be +/- as I had assumed, then you need to get rid
of the bit that says

IF L% < 0 THEN
resultbuf%!0 = -(resultbuf%!0)
ENDIF

That will not be helping. As I said...
Post by Alan Adams
Post by Matthew Phillips
Post by Alan Adams
IF L% < 0 THEN
resultbuf%!0 = -(resultbuf%!0)
ENDIF
That bit is intended to ensure that the 40-byte part is correctly signed.
You get a correctly signed plus or minus result by just looping through the
five bytes doing the subtraction and borrowing. That is the beauty of
two's-complement representation of negative numbers: exactly the same
operation is done at the byte level no matter whether it is signed or
unsigned arithmentic. The only difference is in the detection of carrying or
overflow. You've already made it clear that the times you are dealing with a
relatively close to each other (within a day or two, say) so you will not get
any overflow from subtracting them and the top byte can be ignored
completely.
Post by Alan Adams
The following shows why this makes my head hurt.
adding two positive numbers shouldn't produce a negative result.
Post by Matthew Phillips
A%=&7FFFFFFF
PRINT A%
2.14748365E9
Post by Matthew Phillips
B%=A%+1
PRINT B%
-2.14748365E9
Well, that's a quirk of BBC BASIC that we were discussing a few weeks ago.
BASIC stupidly, in my opinion, allows you to do arithmetic on signed values
without warning about overflows.

I suggest you use:

PROCsubstamp(resultbuf%,stamp1%,stamp2%)
LOCAL I%,J%,K%,L%,borrow%
borrow%=0
FOR I%=0 TO3
J%=stamp1%?I%
K%=stamp2%?I%
L%=J%-K%-borrow%
resultbuf%?I%=L%AND&FF
IF L% < 0 THEN borrow%=1 ELSE borrow%=0
NEXT
ENDPROC

And then your answer can be read from !resultbuf% as a 4-byte signed value.

This will only work if the two timestamps are within 497 days of each other.

You are probably worried what will happen when we have these two timestamps:

A: 57FFFFFFFF
B: 5800000000

B is one centisecond higher than A.

Take FF off 0 and we get 1, borrow one.
Take FF and a borrow off 0 and we get 0
Take FF and a borrow off 0 and we get 0
Take FF and a borrow off 0 and we get 0
Take 57 and a borrow off 58 and we also get 0, but you're only looping from 0
to 3 so we don't even calculate this bit.

Therefore you get the answer 00000001 which is what you want.

Similarly if you take B off A, you will get FFFFFFFF which is -1 and is
correct.

If you know your answer is always going to fit in 4 bytes, just ditch all the
code after the end of your FOR-NEXT loop.
--
Matthew Phillips
Durham
Martin
2020-02-19 23:54:27 UTC
Permalink
Post by Alan Adams
I need a BASIC routine to subtract one 5-byte time from another and
return a correctly signed result.
Can it be assumed that both 5-byte times are work aligned?
--
Martin Avison
Note that unfortunately this email address will become invalid
without notice if (when) any spam is received.
Alan Adams
2020-02-20 11:18:29 UTC
Permalink
Post by Martin
Post by Alan Adams
I need a BASIC routine to subtract one 5-byte time from another and
return a correctly signed result.
Can it be assumed that both 5-byte times are work aligned?
As the buffers are created with DIM, I believe they are aligned.
--
Alan Adams, from Northamptonshire
***@adamshome.org.uk
http://www.nckc.org.uk/
Loading...