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