Just cut out the shift of the 7bit value to make it 8bit
unsigned char interpolate(unsigned char i, unsigned char a, unsigned char b)
{
PRODL = i;
PRODH = b-a+1;
__asm
movf _PRODL, W
mulwf _PRODH, 0
__endasm;
return a + PRODH;
}
Uhm how about:
unsigned char interpolate(unsigned char i, unsigned char a, unsigned char b)
{
return ( (unsigned char) ( (b-(a + 1) ) * i ) ) + a;
}
It’s less optimised than the ASM version (actually the function itself takes as long but it uses one more auto register so there are two instructions to save and restore it when the function is called. The speed difference is only calling overhead) Slightly OT: I’m pretty darned sure that *should* work… But uhm… It outputs code that uses PRODL instead of PRODH, which means it’s outputting the low byte of the 16bit result.. Shouldn’t it be outputting the high byte if it’s truncating to 8bit? Is it just me or did I just find a bug? Surely someone would have noticed this before… but that’s not right… Just to simplify:
i’m not sure what point you are trying to make with those examples, but the reason we take the lsb is that as long as you don’t overflow, math still works. this would not be the case if we took the msb.
i’m not sure what point you are trying to make with those examples, but the reason we take the lsb is that as long as you don’t overflow, math still works. this would not be the case if we took the msb.
Sure, math works if you use LSB and don’t overflow; but math works if you take MSB and do overflow
I guess my point is… C is weird / how can you get the correct result without a shift?
Sorry, I do not completely get it. If b < a then for me, the algorithm does not work.
example (d is decimal, b is binary):
a is d3 b00000011
b is d15 b00001111
put the next value in prodh, differentiate two cases:
1) b-a+1 is d13 b00001101
2) a-b+1 is d245 b11110101
Now multiply i with prodh. Lets say i is an 8 bit value of 100
1) b00001101 * b01100100 = prodl contains b00010100 and prodh contains b00000101 this results in a final value of d8 (d5 of prodh plus the min d3 makes d8). is this correct?
d100/d255 gives something like 0.39, mutiply this with 12 and you get 4.7, seems fair enough.
2) b11110101 * b01100100 = prodl contains b10110100 and prodh contains b01011111. This is wrong.
What am I not getting (maybe I should go to bed?)
What I want to do is this assembler function: I have to 8-bit values a and b, and need to linearly interpolate between these in 4, 5 or 6 bit steps (ie 16, 32 or 64 steps), the step number is given in i (which has 4, 5 or 6 bit)
for example:
a is 100
b is 187
example for 5-bit i
if i is 0, then the result should be 100
if i is 63, then the result should be 187
if i is 23, then the result should be 131
And I cannot reproduce the above example. When I use an i with less bits, do I have to shift it left as well? and do I have to fill up the lower digits with 1 or 0?
sorry, still having problems. This is my code, very similar to the code in MB64_POT_ScaleValue.
;; --------------------------------------------------------------------------
;; FUNCTION: pca9635_Interpolate
;; C_DECLARATION: void ASM_Interpolate(unsigned char step, unsigned char val1, unsigned char val2)
;; DESCRIPTION: Testing the interpolation algorithm: interpolate between value 1 and value 2 in
;; 16 steps and return the corresponding step value.
;;
;; IN: 4bit step in WREG
;; 8bit value 1 in MIOS_PARAMETER1
;; 8bit value 2 in MIOS_PARAMETER2
;;
;; C IN: step number in step
;; value 1 in val1
;; value 2 in val2
;;
;; OUT: interpolated value in WREG
;;
;; C OUT: interpolated value as unsigned char
;;
;; USES: MIOS_PARAMETER3
;; --------------------------------------------------------------------------
;_pca9635_interpolate ; (for C)
pca9635_interpolate
#if _pca9635_def==1 ; note this is using the define from the fix in the asm file!
incf _mod_skel_var, F, BANKED
#endif
;save step value in variable
movwf pca9635_StepCounter
;find out if we have to swap the values
;move MIOS_PARAMETER1 into W
movf MIOS_PARAMETER2, W
;compare, skip the swap if MIOS_PARAMETER2 > MIOS_PARAMETER1
cpfsgt MIOS_PARAMETER1
rgoto pca9635_interpolate_NoSwap
;we just swap the values and invert the step counter
;using a temporary variable
movff MIOS_PARAMETER1, pca9635_Temp
movff MIOS_PARAMETER2, MIOS_PARAMETER1
movff pca9635_Temp, MIOS_PARAMETER2
;invert the step counter and delete the upper nibble (for four bit counter)
comf pca9635_StepCounter, F
movlw 0x0f
andwf pca9635_StepCounter, F
pca9635_interpolate_NoSwap
;now find the difference between MIDI_PARAMETER1 and MIDI_PARAMETER2
;load MIDI_PARAMETER1 into W
movf MIOS_PARAMETER1, W
;subtract W from MIOS_PARAMETER2, store result in W
subwf MIOS_PARAMETER2, W
;add 1 for rounding errors
addlw 1
;now divide this value by the number of steps
;because the next operation is a floating point 16bit calculaton, we leave it
;as it is and take away the decimals later
;to get the result in the upper byte PRODH, shift the step
;counter four to the left (for 4-bit counter)
rlncf pca9635_StepCounter, F
rlncf pca9635_StepCounter, F
rlncf pca9635_StepCounter, F
rlncf pca9635_StepCounter, F
;therefore, multiply the difference with the number of steps
mulwf pca9635_StepCounter
;now we just need to get the high byte
movf PRODH, W
;add the MIOS_PARAMETER1 (the low value)
addwf MIOS_PARAMETER1, W
;test send the result
call MIOS_MIDI_TxBufferPut
return
Essentially, I get the difference of the two values, and multiply them with a 4-bit step counter which is shifted 4 bits to the left. The result then is in PRODH. But the closer I come to the high value, the more inaccurate it gets: If the max value is 150, the min value is 100 and the step number is 15 (from 0-15 steps), then I get 147 and not 150. What did I do wrong?