I have a particular requirement to determine the magnitude of the difference between two 16 bit values (not the sign). The max value of each number is 8191 before it wraps around to zero so I need to account for this. In my application I have a 16 bit count going up or down (yes I know 8191 is 13 bits..but it's held in two bytes). I must compare the previous count value with the current count value and determine the magnitude of the difference taking into account the wrap round ( so old value = 8190, new value is 2...difference is 4 not 8188). The max value of this magnitude is likely to be less than ten.
Any thoughts? I am having to get back into thinking at assembler level again to support a legacy project. I have been writing in C for several years and am a bit rusty!
Oh it's for HC08.
Hello,
So you have a modulo value of 8192. There is no need to sign extend provided the result of a subtraction is positive. I think that the following C function achieves the required result.
#define MOD 8192// n1, n2 must fall within range 0 to MOD - 1word get_diff( word n1, word n2){ if (n2 >= n1) return (n2 - n1); else return (MOD - n2 + n1);}
Now a subroutine to accomplish the same process in assembler - I have not tested the code; I will leave this to you.
; RAM allocations:N1 EQU RAMStartN2 EQU RAMStart+2MOD EQU 8192GET_DIFF: LDHX N2 CPHX N1 BLO GD1 ; Branch if N2 < N1 LDA N2+1 ; Low byte SUB N1+1 PSHA LDA N2 ; High byte SBC N1 BRA GD2 ; Branch alwaysGD1: LDA #(MOD%256) ; Low byte SUB N2+1 TAX LDA #(MOD/256) ; High byte SBC N2 PSHA TXA ; Low byte PULX ADD N1+1 PSHA TXA ; High byte ADC N1GD2: PSHA PULH PULX RTS ; H:X contains difference value
Regards,
Mac
Thanks Mac..I'll need to spend a while getting my head round that and thanks for putting the C code up. It makes it easier to understand. I'll let you know how it goes!
Hi Mac,
I've just done a quick walkthrough with some sample values. I believe the result needs to be ANDed with 0x01ff to mask off the bits above 8191.
So it should be
return (MOD - n2 + n1)%MOD;
Hello Steve,
Did you see my modified code where I reversed N1 and N2?
If you are actually using HC908 (and not HCS08) device, the use of the assembly code as it stands, will require allocation of variables to page 0 RAM, for direct addressing. This is a limitation of the LDHX and CPHX instructions, that does not occur for any HCS08 device.
Regards,
Mac
Hi Mac,
No I didn't see the modified code. I think I assumed it was your original code.
The actual application is to measure the rotational speed of a pulley wheel. There is a radio transmitter attached to it which gives an output of the number of revolutions it has done (one transmission per revolution). The count goes up when rotating in one direction and down when rotating in another. The maximum count is 8191 before it sets back to zero. Similarly from zero it goes back to 8191. I measure the time between transmissions. Because of possible radio interference I need to know how many rotations have occured since the last good update so I need to know the magnitude of the difference between current and previous counts (as the count could be going up or down). The rollover situation must also be catered for.
I've not checked the assembler but using your revised C code as an example..n1 (first)= 1006, n2(second) =1004
8192 -1006 + 1004 gives 8190 whereas the difference I actually want is 2
using your original code but doing a %8192 gives
8192 - 1004 + 1006 gives 8194 which when %8192 gives 2
Am I making sense here or is it getting late!!
Hello Steve,
With both CW and CCW rotation, there are now four conditions that need to be differentiated -
So a further test is required to differentiate between 1 and 4, and between 2 and 3. I will suggest to compare the difference with MOD/2, as follows -
#define MOD 8192
#define HALFMOD MOD/2
// n1, n2 must fall within range 0 to MOD - 1
word get_diff( word n1, word n2)
{
word diff;
if (n2 >= n1) {
diff = n2 - n1;
if (diff < HALFMOD) return diff; // CW rotation, no wrap
else return (MOD - diff); // CCW rotation, with wrap
}
else { // n2 < n1
diff = n1 - n2;
if (diff < HALFMOD) return diff; // CCW rotation, no wrap
else return (MOD - diff); // CW rotation, with wrap
}
}
Or perhaps the following simplified variation -
word get_diff( word n1, word n2)
{
word diff;
if (n2 >= n1)
diff = n2 - n1;
else
diff = n1 - n2;
if (diff < HALFMOD) return diff;
else return (MOD - diff);
}
Regards,
Mac
Hello Steve,
And here is the equivalent assembly code -
N1 EQU ZRAMStart
N2 EQU ZRAMStart+2
MOD EQU 8192
HALFMOD EQU MOD/2
GET_DIFF: LDHX N2
CPHX N1
BLO GD1 ; Branch if N2 < N1
; Calculate N2 - N1
LDA N2+1 ; Low byte
SUB N1+1
PSHA
LDA N2 ; High byte
SBC N1
BRA GD2 ; Branch always
GD1: ; Calculate N1 - N2
LDA N1+1 ; Low byte
SUB N2+1
PSHA
LDA N1 ; High byte
SBC N2
GD2: PSHA
PULH
PULX ; H:X is difference magnitude
CPHX #HALFMOD
BLO GD3 ; Exit if no wrap around
; Calculate MOD - difference
PSHX ; Push difference to stack
PSHH
LDA #(MOD%256) ; Low byte
SUB 2,SP
TAX
LDA #(MOD/256) ; High byte
SBC 1,SP
PSHA
PULH
AIS #2 ; Adjust stack pointer
GD3: RTS ; H:X contains difference value
Regards,
Mac
Hi Mac,
Thanks very much for the replies. It's gettingf late here now so I've not had a chance to study your latest. I will look at tomorrow.
Hello,
Belatedly, here is an even simpler version. Firstly the C function, followed by the equivalent assembly sub-routine.
#define MOD 8192#define HALFMOD MOD/2word get_diff( word n1, word n2){ int diff; diff = n2 - n1; if (diff < 0) diff += MOD; if (diff < HALFMOD) return diff; return (MOD - diff);}
N1 EQU Z_RAMStartN2 EQU Z_RAMStart+2MOD EQU 8192HALFMOD EQU MOD/2GET_DIFF: ; Calculate N2 - N1 LDA N2+1 ; Low byte SUB N1+1 TAX LDA N2 ; High byte SBC N1 BCC GD1 ; Add MOD when difference negative PSHA TXA ADD #(MOD%256) TAX PULA ADC #(MOD/256)GD1: PSHA PULH ; H:X is difference magnitude CPHX #HALFMOD BLO GD2 ; Exit if no wrap around ; Calculate MOD - difference PSHX ; Push difference to stack PSHH LDA #(MOD%256) ; Low byte SUB 2,SP TAX LDA #(MOD/256) ; High byte SBC 1,SP PSHA PULH AIS #2 ; Adjust stack pointerGD2: RTS ; H:X contains difference value
Regards,
Mac
Mac, what about n1 = 8190 (-2) and n2 = 2, also for n1 = 2 and n2 = 8190 (-2). I don't see how your C code would get required +-4.
Hello Kef,
I mixed up N1 and N2. I assume this is a TPM-like problem where the time difference between two events is needed, and where the more recent event may wrap around. If N1 represents the first event, and N2 the second event, the time difference would become N2 + (mod - N1). The following modified code would now appear to work for the example values.
.
#define MOD 8192
// n1, n2 must fall within range 0 to MOD - 1
word get_diff( word n1, word n2)
{
if (n2 >= n1) return (n2 - n1);
else return (MOD - n1 + n2);
}
Here is the amended assembler sub-routine.
.
; RAM allocations:
N1 EQU RAMStart
N2 EQU RAMStart+2
MOD EQU 8192
GET_DIFF: LDHX N2
CPHX N1
BLO GD1 ; Branch if N2 < N1
LDA N2+1 ; Low byte
SUB N1+1
PSHA
LDA N2 ; High byte
SBC N1
BRA GD2 ; Branch always
GD1: LDA #(MOD%256) ; Low byte
SUB N1+1
TAX
LDA #(MOD/256) ; High byte
SBC N1
PSHA
TXA ; Low byte
PULX
ADD N2+1
PSHA
TXA ; High byte
ADC N2
GD2: PSHA
PULH
PULX
RTS ; H:X contains difference value
Regards,
Mac
You need to sign extend your 13bit value to 16bit value. It may look like this
#define numbits 13
signed int value;
if( value & (1<<(numbits -1)) ) // is sign bit set?
value |= ((-1) << numbits );
Now you can take the difference like value - oldvalue, where oldvalue is also sign extended like above.
Thanks kef. How would that work in assembler then? It would be more starightforward in C but I don't have that luxury.
I doubt it. CW special edition compiled code limit is 32kB or even 64kB. That's a lot for S08
LDA value:0 ; take MS byte of 13bit value
BIT #(1<<(13-1-8)) ; test sign bit
BEQ L1
ORA #(-1) << (13-8) ; sign extend value
STA value:0
L1: