www.pudn.com > vrtxtst.rar > VRTLIB16.S


;************************************************************************* 
; 
; 
;  IMPORTANT - USE OF THIS SOFTWARE IS SUBJECT TO LICENSE RESTRICTIONS 
;  CAREFULLY READ THE LICENSE AGREEMENT BEFORE USING THE SOFTWARE 
; 
; 
;************************************************************************* 
; Issue: 0.03/23-Feb-93 
; 
; Purpose: Minimal, standalone, VRTXmc library kernel Mixed ARM/Thumb 
; 
; Copyright (C) 1993 Advanced RISC Machines Limited. All rights reserved. 
; 
; Advanced RISC Machines Limited does not assume any liability arising out 
; of this program or use thereof neither does it convey any licence under 
; its intellectual property rights. 
; 
; Conditions of use: 
; 
; The terms and conditions under which this software is supplied to you and 
; under which you may use it are described in your licence agreement with 
; your supplier. 
; 
;----------------------------------------------------------------------------; 
; ABOUT THIS CODE                                                            ; 
;                                                                            ; 
; This code depends hardly at all on its target environment and is  ; 
; designed to be very easy to adapt to your particular ARM-based system.     ; 
; You can expect it to take about a couple of hours to re-target.            ; 
;                                                                            ; 
; Much of the code below is generic to the ARM processor and is completely   ; 
; independent of your ARM-based hardware or any operating system kernel that ; 
; may run on it.                                                             ; 
;                                                                            ; 
; WHAT THIS CODE PROVIDES:                                                   ; 
;                                                                            ; 
;      - program termination (__rt_exit)                                     ; 
;      - determining FP instruction-set availability (__rt_fpavailable)      ; 
;                                                                            ; 
;  - Functions to help with heap allocation, stack-limit checking, setjmp    ; 
;    and longjmp. These may need to be customised for your environment,      ; 
;    but can almost certainly be used as-is in a first re-targetting.        ; 
;                                                                            ; 
;  - Fully 'rolled' divide (and remainder) functions.                        ; 
;                                                                            ; 
; WHAT THIS CODE DOES NOT PROVIDE                                            ; 
;                                                                            ; 
;  - Support for exceptions or interrupts.                                   ; 
;                                                                            ; 
;  - A way to print to the debugging channel (use in line SWIs)              ; 
;                                                                            ; 
;----------------------------------------------------------------------------; 
; Revisions of VRTLIB16.S 
; 1.0  P.Cumming  25/04/96  ARM & Thumb RTSTAND.S   
;                           adapted by Samsung & P.Cumming for VRTXmc  
 
DefaultStackIncrement EQU  0     ; At each overflow it grows by at 
                                 ; at least this many bytes. 
				 ;defaults to 0 for VRTmc stack size is fixed 
				 ;at configuration time in vmcconf.c 
 
StackSlop             EQU  256        ; sl is kept this far above the real 
                                      ; stack low-water mark. NOTE: MUST be 
                                      ; = 256 for VRTXmc 
  GBLL EnsureNoFPSupport 
EnsureNoFPSupport     SETL {TRUE}     ; If {TRUE} then the availability of 
                                      ; Floating Point Support is ignored. 
                                      ; If {FALSE} then FP availability is 
                                      ; checked for. 
                                      ; Setting to {TRUE} saves a little 
                                      ; space. 
 
        IMPORT  |__err_handler|, WEAK 
	IMPORT	|vmc_write|, WEAK 
	IMPORT	|user_exit|, WEAK 
	IMPORT	|cpu_interrupt_disable| 
	 
 
;----------------------------------------------------------------------------; 
; Now the symbols we define and EXPORT from this this module.                ; 
;----------------------------------------------------------------------------; 
 
        EXPORT  |__rt_exit| 
	EXPORT	|__rt_exit16| 
        EXPORT  |__rt_fpavailable| 
        EXPORT  |__rt_trap| 
	EXPORT	|__rt_trap16| 
 
;----------------------------------------------------------------------------; 
; Next, optional support for C stack-limit checking. This code should need   ; 
; no attention in a first re-targetting.                                     ; 
;----------------------------------------------------------------------------; 
 
        EXPORT  |__rt_stkovf_split_small| ; veneer 
        EXPORT  |__rt_stkovf_split_big| 
	EXPORT  |__16__rt_stkovf_split_small| ; veneer 
        EXPORT  |__16__rt_stkovf_split_big| 
 
 
;----------------------------------------------------------------------------; 
; Then two C-specific functions which should require no attention in a first ; 
; re-targetting. Note that they depend on __rt_fpavailable.                  ; 
;----------------------------------------------------------------------------; 
 
        EXPORT  |setjmp| 
        EXPORT  |longjmp| 
 
;----------------------------------------------------------------------------; 
; And, finally, generic ARM functions, referred to by the C compiler.        ; 
; You should not need to alter any of these unless you wish to incorporate   ; 
; them in your operating system kernel. See also later comments.             ; 
;----------------------------------------------------------------------------; 
 
        EXPORT  |__rt_udiv| 
        EXPORT  |__rt_udiv10| 
        EXPORT  |__rt_sdiv| 
        EXPORT  |__rt_sdiv10| 
        EXPORT  |__rt_divtest| 
	EXPORT  |__16__rt_udiv| 
        EXPORT  |__16__rt_udiv10| 
        EXPORT  |__16__rt_sdiv|     
        EXPORT  |__16__rt_sdiv10|       
        EXPORT  |__16__rt_divtest| 
 
	EXPORT	|__rt_halt| 
	 
	MACRO 
        RET 
        BX      lr 
        MEND 
 
; Used for ARM/Thumb interwork calls via function pointers 
        EXPORT  |__call_via_r0| 
        EXPORT  |__call_via_r1| 
        EXPORT  |__call_via_r2| 
        EXPORT  |__call_via_r3| 
 
	 
	CODE32 
;----------------------------------------------------------------------------; 
        AREA    |C$$code$$__call_via|, CODE, READONLY, INTERWORK                              ; 
;----------------------------------------------------------------------------; 
 
 
|__call_via_r0| 
        BX r0 
 
|__call_via_r1| 
        BX r1 
 
|__call_via_r2| 
        BX r2 
 
|__call_via_r3| 
        BX r3 
 
;----------------------------------------------------------------------------; 
      AREA    |C$$code$$__rt_stkovf|, CODE, READONLY	                     ; 
; The code area containing __rt_stkovf_*                                     ; 
;----------------------------------------------------------------------------; 
; C stack-limit checking support.                                            ; 
;                                                                            ; 
; NOTES                                                                      ; 
;                                                                            ; 
; 1/ Stack-limit-checking is optional                                        ;   
;    However, the cost of the check is (very) small and the value sometimes  ; 
;    considerable.                                                           ; 
;                                                                            ; 
; 2/ In VRTXmc a call to __rt_stkovf_split is fatal                          ; 
;    This is because the stack size is fixed in vmcconf.c.                   ; 
;    The usable stack size is stack size - stack slop                        ; 
;    Stackslop acts as a safety margin for the compiler stack checking       ;                                        ; 
;    limits and to provide some stack to handle a stack overflow             ; 
;----------------------------------------------------------------------------; 
 
|__rt_stkovf_split_small|                         ; stkovf_split_small_frame ; 
; 
; Enter here when a C function with frame size <= 256 bytes underflows 
; the stack low-water mark + StackSlop (sl). Falls into the big-frame case. 
 
; fall into big-frame case with size of 0. 
 
|__rt_stkovf_split_big|                             ; stkovf_split_big_frame ; 
; 
; Enter here when a C function with frame size > 256 bytes would underflow 
; the stack low-water mark + StackSlop (sl). No stack space has been claimed 
 
        MOV   ip, #DefaultStackIncrement      ; the default increment 
 
        SUB     r10, r10, ip 
        SUB     r10, r10, #StackSlop            ; new stack low-water mark 
						; ok for error handling 
        ADR     ip, StackOverflowError 
	MOV	a3,#StackOverflowErrorLen	;length of stack overflow error message 
        B       save_regs_and_trap 
 
StackOverflowErrorLen	EQU	15 
StackOverflowError 
        DCD     3 
        DCB     "stack overflow", 0 
        ALIGN 
 
;----------------------------------------------------------------------------; 
       AREA    |C$$code$$__rt_halt|, CODE, READONLY, INTERWORK						 ; 
; The code area containing __rt_halt                                         ; 
;----------------------------------------------------------------------------; 
; Support for low-level failures - halt handler                              ; 
;----------------------------------------------------------------------------; 
 
|__rt_halt| 
; 
; void __rt_halt(void); 
        ADR     ip, VRTXmcPanicError 
	MOV	a3,#VRTXmcPanicErrorLen 
        B       save_regs_and_trap 
 
VRTXmcPanicErrorLen	EQU	13 
VRTXmcPanicError 
        DCD     4 
        DCB     "VRTXmc panic", 0 
        ALIGN 
 
;----------------------------------------------------------------------------; 
       AREA    |C$$code$$__rt_trap|, CODE, READONLY, INTERWORK 
; The code area containing __rt_trap                                         ; 
;----------------------------------------------------------------------------; 
; Support for low-level failures - currently stack overflow, divide by 0 and ; 
; floating-point exceptions. If there is a higher level handler, call it;    ; 
; otherwise, print a message and exit gracefully.                            ; 
;                                                                            ; 
; NOTES                                                                      ; 
;                                                                            ; 
; typedef struct { unsigned code; char message[252];} __rt_error;            ; 
; typedef struct { unsigned r[16];} __rt_registers;                          ; 
;                                                                            ; 
;----------------------------------------------------------------------------; 
 
|__rt_trap| 
; 
; void __rt_trap(__rt_error *e, __rt_registers *r,int error_text_len); 
	STMFD   sp!, {a1,a3}             ; save e in case handler returns... 
	BL	cpu_interrupt_disable 
        LDR     ip, err_handler 
        CMP     ip, #0 
        MOVNE   lr, pc    
        MOVNE   pc, ip                ; if got a handler, use it and 
        LDMFD   sp!, {v1,v2}          ; hope not to return... 
 
        ADR     a1, RTErrorHead 
	MOV	a2,#RTErrorHeadLen	;length of run time error 
	LDR	r3,=vmc_write 
	CMP	r3,#0			;check if vmc_write BSP present 
	BEQ	%F1 
	BL	__call_via_r3 
        ADD     a1, v1, #4 
	MOV	a2,v2				;error text length 
	LDR	r3,=vmc_write		;last vmc_write may corrupt r3 so reload 
	BL	__call_via_r3 
        ADR     a1, RTErrorTail 
	MOV	a2,#RTErrorTailLen	;length of program terminated text 
	LDR	r3,=vmc_write		;last vmc_write may corrupt r3 so reload 
	BL	__call_via_r3 
1        
	MOV     a1, #255 
        B       |__rt_exit|           ; and terminate with non-zero exit code 
err_handler 
        DCD     |__err_handler| 
 
save_regs_and_trap 
	BL	cpu_interrupt_disable 
        STMFD   sp!, {sp, lr, pc} 
        STMFD   sp!, {r0-r12} 
        STR     lr, [sp, #4*15]       ; caller's pc is my lr 
        MOV     a2, sp 
        MOV     a1, ip 
        B       |__rt_trap| 
 
RTErrorHeadLen	EQU	18 
RTErrorHead 
        DCB     10, 13, "run time error: ", 0 
 
RTErrorTailLen	EQU	24 
RTErrorTail 
        DCB     10, 13, "program terminated", 10, 13, 10, 13, 0 
 
        ALIGN 
 
 
;----------------------------------------------------------------------------; 
        AREA    |C$$code$$__exit|, CODE, READONLY, INTERWORK 
; The code area containing __rt_exit                                 ; 
;----------------------------------------------------------------------------; 
 
 
|__rt_exit|                                ; exit 
; 
; void __rt_exit(int code); 
; Terminate execution, optionally setting return code (ignored here). 
; MUST NOT RETURN. 
 
  IF EnsureNoFPSupport = {FALSE} 
        LDR     a2, fp_finalise            ; finalise FP code if present 
        CMP     a2, #0 
        MOVNE   lr, pc 
        MOVNE   pc, a2 
  ENDIF 
	LDR	r3, =_user_exit 
	LDR	r3,[r3] 
	CMP	r3, #0 
	BLNE	__call_via_r3	;jump to user exit handler if present 
	MOV	lr, pc		;else loop forever 
	NOP			 
	NOP 
	MOV	pc, lr		;loop forever 
 
 
		 
;----------------------------------------------------------------------------; 
        AREA    |C$$code$$__rt_fpavailable|, CODE, READONLY, INTERWORK				 ; 
; The code area containing __rt_fpavailable                                  ; 
;----------------------------------------------------------------------------; 
 
|__rt_fpavailable| 
; 
; int __rt_fpavailable(); return non-0 if FP support code linked. 
 
  IF EnsureNoFPSupport = {FALSE} 
        LDR     a1, fp_initialise 
  ELSE 
        MOV     a1, #0 
  ENDIF 
        RET 
 
 
;----------------------------------------------------------------------------; 
       AREA    |C$$code$$__jmp|, CODE, READONLY, INTERWORK							 ; 
; The code area containing setjmp, longjmp                                   ; 
;----------------------------------------------------------------------------; 
; Setjmp and longjmp support.                                                ; 
;                                                                            ; 
; NOTES                                                                      ; 
;                                                                            ; 
; 1/ Specific to C and not implementable in C.                               ; 
;                                                                            ; 
; 2/ Interacts with stack management and possibly with memory management.    ; 
;    e.g. on a chunked stack, longjmp must de-allocate jumped-over chunks.   ; 
;                                                                            ; 
; 3/ Must know whether the floating-point instruction-set is supported!      ; 
;    (DEPENDS ON __rt_fpavailable to discover this).                         ; 
;                                                                            ; 
;----------------------------------------------------------------------------; 
 
        MAP 0                 ; This structure maps the jmp_buf 
sj_v1   #       4             ; data type assumed by the C compiler. 
sj_v2   #       4             ; First, space to save the v-registers... 
sj_v3   #       4 
sj_v4   #       4 
sj_v5   #       4 
sj_v6   #       4 
sj_sl   #       4             ; then the frame registers sl, fp, sp (ap), 
sj_fp   #       4             ; and pc/lr... 
sj_ap   #       4 
sj_pc   #       4 
sj_f4   #       3*4           ; and finally the floating-point reisters, 
sj_f5   #       3*4           ; used only if floating point support is 
sj_f6   #       3*4           ; available. 
sj_f7   #       3*4 
 
 
|setjmp|                      ; setjmp 
; 
; int setjmp(jmp_buf env); 
; Saves everything that might count as a register variable in 'env'. 
 
        STMIA   a1!, {v1-v6, r10, fp, sp, lr} 
        MOV     v6, a1                 ; v6 safe in env - use to point past 
                                       ; saved lr (at 1st FP slot) 
        BL      |__rt_fpavailable| 
        CMP     a1, #0 
        BEQ     setjmp_return          ; no fp 
        STFE    f4, [v6, #sj_f4-sj_f4] 
        STFE    f5, [v6, #sj_f5-sj_f4] 
        STFE    f6, [v6, #sj_f6-sj_f4] 
        STFE    f7, [v6, #sj_f7-sj_f4] 
        MOV     a1, #0                 ; must return 0 from a direct call 
setjmp_return 
        LDMDB   v6, {v6, r10, fp, sp, lr} 
        RET 
 
|longjmp|                                                          ; longjmp ; 
; int longjmp(jmp_buf env, int val); 
 
        MOV     v1, a1                 ; save env ptr over call to fpavailable 
        MOVS    v6, a2                 ; ensure non-0 return value... 
        MOVEQ   v6, #1                 ; (must NOT return 0 on longjmp(env, 0)) 
        BL      |__rt_fpavailable| 
        CMP     a1, #0 
        BEQ     longjmp_return 
        LDFE    f7, [v1, #sj_f7] 
        LDFE    f6, [v1, #sj_f6] 
        LDFE    f5, [v1, #sj_f5] 
        LDFE    f4, [v1, #sj_f4] 
longjmp_return 
        MOV     a1, v6 
        LDMIA   v1, {v1-v6, r10, fp, sp, lr} 
        RET 
 
 
	CODE16 
;----------------------------------------------------------------------------; 
      AREA    |C$$code$$__16__rt_stkovf|, CODE, READONLY, INTERWORK						 ; 
; The code area containing __16__rt_stkovf_*                                 ; 
;----------------------------------------------------------------------------; 
 
|__16__rt_stkovf_split_small| 
; 
; Enter here when a C function with frame size <= 256 bytes underflows 
; the stack low-water mark + StackSlop (sl).  Falls into the big-frame case. 
 
 
|__16__rt_stkovf_split_big| 
; 
; Enter here when a C function with frame size > 256 bytes would underflow 
; the stack low-water mark + StackSlop (sl). No stack space is been claimed 
 
        PUSH    {r0,r1} 
	MOV	r1,#DefaultStackIncrement	;0 default stack increment 
        MOV     r0, r1                          ; the default increment 
        MOV     r1, r10 	; r10 is sl 
        SUB     r1, r0 
        LDR     r0, =StackSlop 
        SUB     r1, r0                          ; new stack low-water mark 
        MOV     r10, r1    	;r10 is sl 
        POP     {r0, r1} 
        SUB     sp, sp, #8*4                    ; Room for R8..R15 
        PUSH    {r0-r7} 
        MOV     r7, lr 
        ADR     r0, StackOverflowError16 
	MOV	r1, #StackOverflowErrorLen 
	MOV	r8, r1				;store message length in r8 
        BL      save_regs_and_trap16 
 
        ALIGN 
StackOverflowError16 
        DCD     3 
        DCB     "stack overflow", 0 
        ALIGN 
 
;----------------------------------------------------------------------------; 
        AREA    |C$$code$$__rt_trap16|, CODE, READONLY, INTERWORK 
; The code area containing __rt_trap                                         ; 
;----------------------------------------------------------------------------; 
; Support for low-level failures - currently stack overflow and divide by 0. ; 
; If there is a higher level handler, call it otherwise, print a message and ; 
; exit gracefully.                                                           ; 
;                                                                            ; 
; NOTES                                                                      ; 
;                                                                            ; 
; typedef struct { unsigned code; char message[252];} __rt_error;            ; 
; typedef struct { unsigned r[16];} __rt_registers;                          ; 
;                                                                            ; 
;----------------------------------------------------------------------------; 
 
__rt_trap16 
; 
; void __rt_trap16(__rt_error *e, __rt_registers *r); 
	PUSH    {r0}                  ; save e in case handler returns... 
	LDR	r3, = cpu_interrupt_disable 
	BL	call_via_r3 
        LDR     r3, =__err_handler 
        CMP     r3, #0 
        BEQ     %F0 
 
	BL      call_via_r3           ; Call the routine pointed to by R3 
        		              ; Note: BL sets bit 0 of LR so return will 
                                      ; be to Thumb state. This is why we use this 
                                      ; rather than the sequence 
                                      ;         MOV     lr, pc 
                                      ;         BX      r3 
                                      ; which may return to ARM state! 
 
0 
        ADR     r0, RTErrorHead16       ; No handler, or handler returned 
	MOV	r1, #RTErrorHeadLen     ;length of run time error text 
	LDR	r3,=vmc_write 
	CMP	r3,#0			;check if vmc_write BSP present 
	BEQ	%F1 
	BL	call_via_r3		;call vmc_write could be arm or thumb 
        POP     {r0} 
        ADD     r0, #4 
	MOV	r1, r8			;r8 contains length of error message 
	LDR	r3,=vmc_write		;vmc_write may destroy r3 so reload 
	BL	call_via_r3 
        ADR     r0, RTErrorTail16 
	MOV	r1,#RTErrorTailLen	;length of program terminated text    
	LDR	r3,=vmc_write         
	BL	call_via_r3 
 
1       MOV     a1, #255 
 	BL      __rt_exit16             ; and terminate with non-zero exit code 
	 
call_via_r3 
        BX      r3 
 
; SP has already been decremented by 16 * 4 and R0..R7 saved. 
; IP points to the error description 
; R7 contains the LR register which has been destroyed as a BL was required 
; to get here. 
 
save_regs_and_trap16 
	PUSH	{r0,r1}		;save regs r0,r1 used by cpu_interrupt_disable 
	LDR	r3, = cpu_interrupt_disable 
	BL	call_via_r3 
	POP	{r0,r1} 
        MOV     ip, r0 
        MOV     r0, r8 
        MOV     r1, r9 
        MOV     r2, r10 
        MOV     r3, r11 
        MOV     r4, r12 
        ADD     r5, sp, #16*4         ; Take account of previous SP adjustment 
        ADD     r6, sp, #8*4          ; Pointer to hi reg save area 
        STMIA   r6!, {r0-r5, r7} 
        STR     r7, [sp, #15*4]       ; Save my pc as callers lr 
        MOV     a2, sp 
        MOV     a1, ip 
        B       __rt_trap16 
 
        ALIGN 
RTErrorHead16 
        DCB     10, 13, "run time error: ", 0 
 
        ALIGN 
RTErrorTail16 
        DCB     10, 13, "program terminated", 10, 13, 10, 13, 0 
 
        ALIGN 
 
	 
_user_exit	DCD	|user_exit| 
;----------------------------------------------------------------------------; 
        AREA    |C$$code$$__exit16|, CODE, READONLY, INTERWORK 
; The code area containing __main, __rt_exit                                 ; 
;----------------------------------------------------------------------------; 
 
__rt_exit16                                  ; exit 
; 
; void __rt_exit16(int code); 
; Terminate execution, optionally setting return code (ignored here). 
; MUST NOT RETURN. 
 
	LDR	r3, =_user_exit 
	LDR	r3,[r3] 
	CMP	r3, #0 
	BEQ	loop_forever 
	BL	call_via_r3_exit	;jump to user exit handler if present 
loop_forever 
	MOV	lr, pc 
	NOP 
	NOP 
	MOV	pc, lr		;loop forever 
 
call_via_r3_exit 
        BX      r3 
 
 
  IF EnsureNoFPSupport = {FALSE} 
fp_initialise 
        DCD     |__fp_initialise| 
fp_finalise 
        DCD     |__fp_finalise| 
  ENDIF 
 
	ALIGN 
 
	CODE32 
;----------------------------------------------------------------------------; 
       AREA    |C$$code$$__divide|, CODE, READONLY, INTERWORK							 ; 
; The code area containing __rt_sdiv, __rt_udiv, __rt_sdiv_10, __rt_udiv10   ; 
;----------------------------------------------------------------------------; 
; GENERIC ARM FUNCTIONS - divide and remainder.                              ; 
;                                                                            ; 
; NOTES                                                                      ; 
;                                                                            ; 
; 1/ You may wish to make these functions part of your O/S kernel, replacing ; 
;    the implementations here by branches to the relevant entry addresses.   ; 
;                                                                            ; 
; 2/ Each divide function is a div-rem function, returning the quotient in   ; 
;    r0 and the remainder in r1. Thus (r0, r1) -> (r0/r1, r0%r1). This is    ; 
;    understood by the C compiler.                                           ; 
;                                                                            ; 
; 3/ Because of its importance in many applications, divide by 10 is treated ; 
;    as a special case. The C compiler recognises divide by 10 and generates ; 
;    calls to __rt_{u,s}div10, as appropriate.                               ; 
;                                                                            ; 
; 4/ Each of the implementations below has been coded with smallness as a    ; 
;    higher priority than speed.  Unrolling the loops will allow faster      ; 
;    execution, but will produce much larger code.  If the speed of divides  ; 
;    is critical then unrolled versions can be extracted from the ARM ANSI C ; 
;    Library.                                                                ; 
;                                                                            ; 
;----------------------------------------------------------------------------; 
 
; div_core is used by __rt_sdiv and __rt_udiv, and corrupts a3, a4 and ip 
div_core 
        CMP     a3, a4 
        MOVHI   a4, a4, ASL #1 
        BHI     div_core 
div_core2 
        CMP     a2, a4 
        ADC     ip, ip, ip 
        SUBHS   a2, a2, a4 
        CMP     a1, a4 
        MOVLO   a4, a4, LSR #1 
        BLO     div_core2 
        MOV     a1, ip 
        RET 
 
; Signed divide of a2 by a1: returns quotient in a1, remainder in a2 
; Quotient is truncated (rounded towards zero). 
; Sign of remainder = sign of dividend. 
; Destroys a3, a4 and ip 
; Negates dividend and divisor, then does an unsigned divide; signs 
; get sorted out again at the end. 
 
|__rt_sdiv| 
        MOVS    a3, a1 
        BEQ     dividebyzero            ; ip now unwanted 
 
        RSBMI   a1, a1, #0              ; absolute value of divisor 
        EOR     a3, a3, a2 
        ANDS    ip, a2, #&80000000 
        ORR     a3, ip, a3, LSR #1 
        STMFD   sp!,{a3,lr} 
        ; saved a3: 
        ;  bit 31  sign of dividend (= sign of remainder) 
        ;  bit 30  sign of dividend EOR sign of divisor (= sign of quotient) 
        RSBNE   a2, a2, #0              ; absolute value of dividend 
 
        MOV     a3, a2 
        MOV     a4, a1 
        MOV     ip, #0 
        BL      div_core 
        LDMFD   sp!,{a3} 
        MOVS    a3, a3, ASL #1 
        RSBMI   a1, a1, #0 
        RSBCS   a2, a2, #0    
        LDMFD   sp!,{pc} 
 
; Unsigned divide of a2 by a1: returns quotient in a1, remainder in a2 
; Destroys a4, ip and r5 
 
|__rt_udiv| 
        MOVS    a4, a1 
        BEQ     dividebyzero 
 
        MOV     ip, #0 
        MOV     a3, #&80000000 
        CMP     a2, a3 
        MOVLO   a3, a2 
        B       div_core 
 
; 
; Fast unsigned divide by 10: dividend in a1, divisor in a2. 
; Returns quotient in a1, remainder in a2. 
; Also destroys a3. 
; 
; Calculate x / 10 as (x * 2**32/10) / 2**32. 
; That is, we calculate the most significant word of the double-length 
; product. In fact, we calculate an approximation which may be 1 off 
; because we've ignored a carry from the least significant word we didn't 
; calculate. We correct for this by insisting that the remainder < 10 
; and by incrementing the quotient if it isn't. 
 
|__rt_udiv10|                                                       ; udiv10 ; 
        MOV     a2, a1 
        MOV     a1, a1, LSR #1 
        ADD     a1, a1, a1, LSR #1 
        ADD     a1, a1, a1, LSR #4 
        ADD     a1, a1, a1, LSR #8 
        ADD     a1, a1, a1, LSR #16 
        MOV     a1, a1, LSR #3 
        ADD     a3, a1, a1, ASL #2 
        SUB     a2, a2, a3, ASL #1 
        CMP     a2, #10 
        ADDGE   a1, a1, #1 
        SUBGE   a2, a2, #10 
        RET 
 
; 
; Fast signed divide by 10: dividend in a1, divisor in a2. 
; Returns quotient in a1, remainder in a2. 
; Also destroys a3 and a4. 
; Quotient is truncated (rounded towards zero). 
; Make use of __rt_udiv10 
 
|__rt_sdiv10|                    ; sdiv10  
        MOV     ip, lr 
        MOVS    a4, a1 
        RSBMI   a1, a1, #0 
        BL      __rt_udiv10 
        CMP     a4, #0 
        RSBMI   a1, a1, #0 
        RSBMI   a2, a2, #0 
        MOV     pc, ip 
   
 
; 
; Test for division by zero (used when division is voided). 
 
|__rt_divtest|                  ; divtest  
        CMPS    a1, #0 
        BEQ dividebyzero	;replaces original RET NE macro 
	RET 
dividebyzero 
        ADR     ip, DivideByZeroError 
	MOV	a3,#DivideByZeroErrorLen 
        B       save_regs_and_trap 
 
DivideByZeroErrorLen	EQU	12 
DivideByZeroError 
        DCD     1 
        DCB     "divide by 0", 0 
        ALIGN 
 
	CODE16 
;----------------------------------------------------------------------------; 
       AREA    |C$$code$$__16__divide|, CODE, READONLY, INTERWORK							 ; 
; The code area containing __rt_sdiv, __rt_udiv, __rt_sdiv_10, __rt_udiv10   ; 
;----------------------------------------------------------------------------; 
|__16__rt_sdiv| 
        ASR     a4, a2, #31 
        EOR     a2, a4 
        SUB     a2, a4 
 
        ASR     a3, a1, #31 
        EOR     a1, a3 
        SUB     a1, a3 
 
        BEQ     dividebyzero16 
 
        PUSH    {a3, a4}        ; Save so we can look at signs later on 
 
        LSR     a4, a2, #1 
        MOV     a3, a1 
 
s_loop  CMP     a3, a4 
        BNLS    %FT0 
        LSL     a3, #1 
0       BLO     s_loop 
 
        MOV     a4, #0 
        B       %FT0 
s_loop2 LSR     a3, #1 
0       CMP     a2, a3 
        ADC     a4, a4 
        CMP     a2, a3 
        BCC     %FT0 
        SUB     a2, a3 
0 
        CMP     a3, a1 
        BNE     s_loop2 
        MOV     a1, a4 
 
        POP     {a3, a4} 
 
        EOR     a3, a4 
        EOR     a1, a3 
        SUB     a1, a3 
 
        EOR     a2, a4 
        SUB     a2, a4 
 
        RET 
 
; Unsigned divide of a2 by a1: returns quotient in a1, remainder in a2 
; Destroys a4, ip and r5 
 
|__16__rt_udiv| 
        LSR     a4, a2, #1 
        MOV     a3, a1 
        BEQ     dividebyzero16 
 
u_loop  CMP     a3, a4 
        BNLS    %FT0 
        LSL     a3, #1 
0       BLO     u_loop 
 
        MOV     a4, #0 
        B       %FT0 
u_loop2 LSR     a3, #1 
0       CMP     a2, a3 
        ADC     a4, a4 
        CMP     a2, a3 
        BCC     %FT0 
        SUB     a2, a3 
0 
        CMP     a3, a1 
        BNE     u_loop2 
        MOV     a1, a4 
 
        RET 
 
; Fast unsigned divide by 10: dividend in a1, divisor in a2. 
; Returns quotient in a1, remainder in a2. 
; Also destroys a3. 
; 
; Calculate x / 10 as (x * 2**32/10) / 2**32. 
; That is, we calculate the most significant word of the double-length 
; product. In fact, we calculate an approximation which may be 1 off 
; because we've ignored a carry from the least significant word we didn't 
; calculate. We correct for this by insisting that the remainder < 10 
; and by incrementing the quotient if it isn't. 
 
|__16__rt_udiv10|                                                         ; udiv10 ; 
        MOV     a2, a1 
        LSR     a1, #1 
        LSR     a3, a1, #1 
        ADD     a1, a3 
        LSR     a3, a1, #4 
        ADD     a1, a3 
        LSR     a3, a1, #8 
        ADD     a1, a3 
        LSR     a3, a1, #16 
        ADD     a1, a3 
        LSR     a1, #3 
        ASL     a3, a1, #2 
        ADD     a3, a1 
        ASL     a3, #1 
        SUB     a2, a3 
        CMP     a2, #10 
        BLT     %FT0 
        ADD     a1, #1 
        SUB     a2, #10 
0 
        RET 
 
; 
; Fast signed divide by 10: dividend in a1, divisor in a2. 
; Returns quotient in a1, remainder in a2. 
; Also destroys a3 and a4. 
; Quotient is truncated (rounded towards zero). 
; Make use of __rt_udiv10 
 
|__16__rt_sdiv10| 
        ASR     a4, a1, #31 
        EOR     a1, a4 
        SUB     a1, a4 
 
        MOV     a2, a1 
        LSR     a1, #1 
        LSR     a3, a1, #1 
        ADD     a1, a3 
        LSR     a3, a1, #4 
        ADD     a1, a3 
        LSR     a3, a1, #8 
        ADD     a1, a3 
        LSR     a3, a1, #16 
        ADD     a1, a3 
        LSR     a1, #3 
        ASL     a3, a1, #2 
        ADD     a3, a1 
        ASL     a3, #1 
        SUB     a2, a3 
        CMP     a2, #10 
        BLT     %FT0 
        ADD     a1, #1 
        SUB     a2, #10 
0 
        EOR     a1, a4 
        SUB     a1, a4 
        EOR     a2, a4 
        SUB     a2, a4 
        RET 
 
; 
; Test for division by zero (used when division is voided). 
 
|__16__rt_divtest|                                                     ; divtest ; 
        CMPS    a1, #0 
        BEQ     dividebyzero16 
        RET 
 
dividebyzero16 
        SUB     sp, sp, #8*4                    ; Room for R8..R15 
        PUSH    {r0-r7} 
        MOV     r7, lr 
        ADR     r0, DivideByZeroError16 
	MOV	r1,#DivideByZeroErrorLen 
	MOV	r8,r1	;r8 = rt_trap parameter containing length 
        BL      save_regs_and_trap16 
 
        ALIGN 
DivideByZeroError16 
        DCD     1 
        DCB     "divide by 0", 0 
        ALIGN 
 
   
	END