Integer division
Integer division is implemented by calling the C library functions __rt_sdiv and __rt_udiv. These check for divison by zero. If integer division by zero is detected, a branch to __rt_div0 is made. So to trap the division by zero, all you need to do is place a breakpoint on __rt_div0.
On entry into __rt_div0, LR will contain the address of the instruction _after_ the call to the __rt_sdiv/__rt_udiv division routine in your application code. Therefore, to identify which line in your source code is the culprit, so you can simply look up the line of C code in the debugger at that address given by LR.
If you need to examine parameters, etc, and save them for post-mortem debugging, you can intercept __rt_div0 like this:
AREA foo, CODE
IMPORT |$Super$$__rt_div0|
EXPORT |$Sub$$__rt_div0|
|$Sub$$__rt_div0|
;; Add code to save whatever registers you need here
;; Take care not to corrupt any needed registers
B |$Super$$__rt_div0|
END
Floating point division (using the software floating point libraries)To find the address of the function in your application code which contains the arithmetic operation which resulted in the exception, simply place a breakpoint on the function "_fp_trapveneer", then look at LR.
This is a function within the C library which is called if an exception occurs. On entry into this function, the registers are in the state they were when the exception occurred. So you can then simply view the registers when the breakpoint is hit.
Example:
---- main.c ----
#include <stdio.h>
#include <fenv.h>
int main(void)
{
float a, b, c;
// Trap the Invalid Operation exception and untrap all other exceptions:
__ieee_status(FE_IEEE_MASK_ALL_EXCEPT, FE_IEEE_MASK_INVALID);
c = 0;
a = b / c;
printf("b / c = %f, ", a);
return 0;
}
---- end main.c ----
Built/executed with:
armcc -g main.c -o main.axf (for RVCT 1.2 and earlier) or:
armcc -g main.c -o main.axf --fpmode ieee_full (for RVCT 2.0 and later)
armsd main.axf
armsd: break @_fp_trapveneer
armsd: go
Breakpoint #1 at PC = 0x0000c77c (_fp_trapveneer + 0)
_fp_trapveneer
+0000 0x0000c77c: 0xe92d5000 .P-. : * stmfd r13!,{r12,r14}
armsd: reg
r0 = 0x00000000 r1 = 0x00000000 r2 = 0x00000000 r3 = 0x00000000
r4 = 0x0000cb14 r5 = 0x00000000 r6 = 0x00000000 r7 = 0x00000000
r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 r11 = 0x00000000
r12 = 0x04000004 r13 = 0x07ffffe8 r14 = 0x000080c8
pc = 0x0000c77c cpsr = %nzcvqIFt_SVC spsr = %nzcvqift_Reserved_00
where r14 = LR = 0x80c8 = the address of the instruction after the "bl _f2d".
If you need to examine parameters, etc, and save them for post-mortem debugging, you can intercept _fp_trapveneer like this:
AREA foo, CODE
IMPORT |$Super$$_fp_trapveneer|
EXPORT |$Sub$$_fp_trapveneer|
|$Sub$$_fp_trapveneer|
;; Add code to save whatever registers you need here
;; Take care not to corrupt any needed registers
B |$Super$$_fp_trapveneer|
END