📄 vrtlib.s
字号:
; 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
BNE user_exit ;jump to user exit handler if present
MOV lr, pc ;else loop forever
NOP
NOP
MOV pc, lr ;loop forever
IF EnsureNoFPSupport = {FALSE}
fp_initialise
DCD |__fp_initialise|
fp_finalise
DCD |__fp_finalise|
ENDIF
;----------------------------------------------------------------------------;
; AREA |C$$code$$__rt_fpavailable|, CODE, READONLY ;
; 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 ;
; 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
;----------------------------------------------------------------------------;
AREA |C$$code$$__divide|, CODE, READONLY ;
; 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
RET NE
dividebyzero
ADR ip, DivideByZeroError
MOV a3,#DivideByZeroErrorLen
B save_regs_and_trap
DivideByZeroErrorLen EQU 11
DivideByZeroError
DCD 1
DCB "divide by 0", 0
ALIGN
END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -