I've found also a few more problems with the [AN575] INT24 routine:
1) when AEXP==0, which means zero floating number, but AARGB0:1 is not zero, return will be incorrect. So AARGB0:1 have to be cleared in that case.
2) problem with overflow checking (for example -32769 (0x8e8001) will result in 0xC78e, instead of 0x8000)
There is really something wrong with rounding. They teach in school that if a number is in the middle between two integers, the highest by absolute value is taken. For example, MATLAB has four rounding functions:
ROUND - round to closest integer (like in school) FIX - round towards zero (ignore fractional part) CEIL - round towards plus infinity FLOOR - round towards minus infinity let x = [-1:0.25:1] » x = [-1:0.25:1] x = -1 -0.75 -0.5 -0.25 0 0.25 0.5 0.75 1 » round(x) ans = -1 -1 -1 0 0 0 1 1 1 » fix(x) ans = -1 0 0 0 0 0 0 0 1 » ceil(x) ans = -1 0 0 0 0 1 1 1 1 » floor(x) ans = -1 -1 -1 -1 0 0 0 0 1 »
I assume that the INT24 routine should implement ROUND function. But why there is a check for AARGB1.0 (LSb of integer result)?...
[Here is] a fixed version (I hope) of INT24 routine (fpint.asm).
#include <p16f84.inc> cblock 0x20 AEXP ; - exponent 2^(AEXP - 127) AARGB0 AARGB1 ;- low byte BEXP BARGB0 BARGB1 ; - temporary for result TEMPB0 TEMPB1 LOOPCOUNT ; - counter SIGN FPFLAGS endc mov macro x, y movlw x movwf y endm EXP EQU AEXP #define _C STATUS, C #define _Z STATUS, Z #define EXPBIAS 0x7F #define MSB 0x7 #define LSB 0x0 #define RND 0x0 #define SAT 0x1 #define IOV 0x2 BSF FPFLAGS, RND BSF FPFLAGS, SAT ;9.4999 8217FF 0009 9 mov 0x82, AEXP mov 0x17, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;9.50 821800 000A 10 mov 0x82, AEXP mov 0x18, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;9.9999 821FFF 000A 10 mov 0x82, AEXP mov 0x1F, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;10.50 822800 000A 11 mov 0x82, AEXP mov 0x28, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;10.999 822FFF 000A 11 mov 0x82, AEXP mov 0x2F, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;10.4999 8227FF 000A 10 mov 0x82, AEXP mov 0x27, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;11.000 823000 000B 11 mov 0x82, AEXP mov 0x30, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;11.4999 8237FF 000B 11 mov 0x82, AEXP mov 0x37, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;11.50 823800 000C 12 mov 0x82, AEXP mov 0x38, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;11.999 823FFF 000C 12 mov 0x82, AEXP mov 0x3F, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;12.0 824000 000C 12 mov 0x82, AEXP mov 0x40, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;12.4999 8247FF 000C 12 mov 0x82, AEXP mov 0x47, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;12.50 824800 000C 12 mov 0x82, AEXP mov 0x48, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;12.9999 824FFF 000C 12 mov 0x82, AEXP mov 0x4F, AARGB0 mov 0xFF, AARGB1 call INT2416 nop ;13.0 825000 000D 13 mov 0x82, AEXP mov 0x50, AARGB0 mov 0x00, AARGB1 call INT2416 nop ;overflow 32767.5 (RND=1, SAT=1) -> 7FFF mov 0x8d, AEXP mov 0x7f, AARGB0 mov 0xff, AARGB1 call INT2416 nop ;overflow -32769 (RND=1, SAT=1) -> 8000 mov 0x8e, AEXP mov 0x80, AARGB0 mov 0x01, AARGB1 call INT2416 nop ;zero -> 0000 clrf AEXP call INT2416 nop ;********************************************************************************************** ; Float to integer conversion ; ; Input: 24 bit floating point number in AEXP, AARGB0, AARGB1 ; ; Use: CALL INT2416 or CALL INT24 ; ; Output: 16 bit 2's complement integer right justified in AARGB0, AARGB1 ; ; Result: AARG <-- INT( AARG ) , RND = 0 ; AARG <-- ROUND( AARG ), RND = 1 ; ; Max Timing: 2+7+8+9+7+7*6-1+11+2 = 87 clks RND = 0 ; 2+7+8+9+7+7*6-1+4+11+2 = 91 clks RND = 1, SAT = 0 ; 2+7+8+9+7+7*6-1+19+2 = 95 clks RND = 1, SAT = 1 ; ; Min Timing: 2+8+2 = 14 clks ; ; PM: 61 DM: 6 ; ;---------------------------------------------------------------------------------------------- INT2416 INT24 RLF AARGB0, W ; save sign in SIGN.7 RRF SIGN, F BSF AARGB0,MSB ; make MSB explicit MOVF EXP,W ; test for zero argument BTFSS _Z GOTO INT24NOTZERO INT24CLEAR CLRF AARGB0 ; if zero, clear AARGB0:1 CLRF AARGB1 ; RETLW 0x00 ; and return INT24NOTZERO ;check if exponent too high (exp >= 15), i.e. overflow ;Special case when exp == -1 and RND==1 (%1.xxx * 2^-1 = %0.1xxx) ;exp<=-2 AARGB0:1=0 ;exp=-1 AARGB0:1=RND ;exp=0 AARGB0:1>>15 ;exp=1 AARGB0:1>>14 ;... ;exp=14 AARGB0:1>>1 ;exp>=15 overflow (note that +-32768 is treated as overflow even when ;-32768 can fit in 16 bit twos complement form. But -32768 will result ;in correct answer for SAT=1 or 0, although with a set IOV flag) ADDLW -EXPBIAS-D'15' ; check if overflow BTFSC _C GOTO SETIOV16 XORLW 0xFF ; get number of shifts MOVWF EXP ; w=-1-(exp-127-15)=14-(exp-127) ANDLW 0xF0 ; clear Z if w > 15 BTFSS _Z GOTO INT24CLEAR ; jump to clear AARG ;do shifts BCF _C ; first shift RRF AARGB0, F RRF AARGB1, F ; rounding BTFSS EXP, 3 ; shift by 8 bits if exp >= 8 GOTO INT24SHIFTS RLF AARGB1, w ; copy bit for rounding MOVF AARGB0, w ; shift AARGB0:1 by 8 bits MOVWF AARGB1 CLRF AARGB0 INT24SHIFTS BCF EXP, 3 ; limit EXP to 0..7 range INCF EXP, F ; preincrement EXP GOTO INT24SHIFTTEST ; jump to decrement and zero check INT24SHIFTSL BCF _C ; shift AARGB0:1 right once RRF AARGB0, F ; and RRF AARGB1, F ; copy bit for rounding INT24SHIFTTEST DECFSZ EXP, F ; repeat if necessary GOTO INT24SHIFTSL SHIFT2416OK BTFSC FPFLAGS,RND BTFSS _C ; round if next bit is set GOTO INT2416OK INCF AARGB1,F BTFSC _Z INCF AARGB0,F BTFSC AARGB0,MSB ; test for overflow GOTO SETIOV16 INT2416OK BTFSS SIGN,MSB ; if sign bit set, negate RETLW 0 COMF AARGB1,F COMF AARGB0,F INCF AARGB1,F BTFSC _Z INCF AARGB0,F RETLW 0 SETIOV16 BSF FPFLAGS,IOV ; set integer overflow flag BTFSS FPFLAGS,SAT ; test for saturation RETLW 0xFF ; return error code in WREG MOVLW 0x80 ; saturate to largest two's MOVWF AARGB0 ; complement 16 bit integer CLRF AARGB1 ; SIGN = 0, 0x 7F FF BTFSS SIGN,MSB ; SIGN = 1, 0x 80 00 COMF AARGB0, F BTFSS SIGN,MSB COMF AARGB1, F RETLW 0xFF ; return error code in WREG ;**********************************************************************************************
Questions: