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: