finding magnitude of difference between two numbers

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

finding magnitude of difference between two numbers

2,574 Views
stevec
Contributor III

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.

Labels (1)
0 Kudos
14 Replies

1,375 Views
bigmac
Specialist III

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

 

0 Kudos

1,375 Views
stevec
Contributor III

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!

0 Kudos

1,375 Views
stevec
Contributor III

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;

0 Kudos

1,375 Views
bigmac
Specialist III

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

 

0 Kudos

1,375 Views
stevec
Contributor III

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!!

0 Kudos

1,376 Views
bigmac
Specialist III

Hello Steve,

 

With both CW and CCW rotation, there are now four conditions that need to be differentiated -

  1. CW rotation, no wrap (n2 >= n1
  2. CW rotation, with wrap (n1 > n2)
  3. CCW rotation, no wrap (n1 >= n2)
  4. CCW rotation, with wrap (n2 > n1)

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

 

0 Kudos

1,376 Views
bigmac
Specialist III

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

0 Kudos

1,376 Views
stevec
Contributor III

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.

0 Kudos

1,376 Views
bigmac
Specialist III

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

 

0 Kudos

1,376 Views
kef
Specialist I

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.

 

 

0 Kudos

1,376 Views
bigmac
Specialist III

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

0 Kudos

1,376 Views
kef
Specialist I

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.

0 Kudos

1,376 Views
stevec
Contributor III

Thanks kef. How would that work in assembler then? It would be more starightforward in C but I don't have that luxury.

0 Kudos

1,376 Views
kef
Specialist I
  • 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:

 

0 Kudos