📄 crt.s
字号:
; NOTE: not such luxury with uC/OS (yet).; ADR a1, RTErrorHead; SWI Write0 ; write preamble...; ADD a1, v1, #4; SWI Write0 ; write error diagnosis; ADR a1, RTErrorTail; SWI Write0 ; write postlude; MOV a1, #255 B |__rt_exit| ; and terminate with non-zero exit codeerr_handler DCD |__err_handler|save_regs_and_trap 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|RTErrorHead DCB 10, 13, "run time error: ", 0RTErrorTail DCB 10, 13, "program terminated", 10, 13, 10, 13, 0 ALIGN;----------------------------------------------------------------------------;; YOU SHOULDN'T NEED TO ALTER ANY OF THE FOLLOWING IN A FIRST RETARGETTING. ;;----------------------------------------------------------------------------;;----------------------------------------------------------------------------; AREA |C$$code$$__rt_alloc|, CODE, READONLY; The code area containing __rt_alloc ;;----------------------------------------------------------------------------;; Primitive support for heap memory management. ;; ;; NOTES ;; ;; 1/ The allocator embeds knowledge of the memory layout and interacts with ;; the stack limit checking code. Here we assume a single address space ;; with the stack at the top growing down and the heap below it growing ;; up, with a gap (free memory) in between. ;; ;; 2/ Failure of the stack-limit check is fatal. However, failure of the low- ;; level heap allocator is passed back to its caller. ;;----------------------------------------------------------------------------;|__rt_alloc| ; alloc;; unsigned __rt_alloc(unsigned minwords, void **block);;; This tries to allocate a block of sensible size >= minwords. Failing that,; it allocates the largest possible block of sensible size. If it can't do; that, it returns zero. *block is set to point to the start of the allocated; block (NULL if none has been allocated).;; NOTE: works in units of WORDS, NOT bytes.;; In this implementation, sl - StackSlop marks the end of allocatable store. CMP a1, #MinHeapIncrement ; round up to at least MOVLT a1, #MinHeapIncrement ; MinHeapIncrement words... LDR a3, HeapLimitAdr LDR a4, [a3] ; current heap high-water mark SUB ip, sl, #StackSlop ; current stack low-water mark CMP a4, ip MOVGE a4, #0 ; no space, *block = NULL STR a4, [a2] MOVGE a1, #0 ; no space, return 0 ADD a4, a4, a1, LSL #2 ; proposed new heap limit CMP a4, ip SUBGT a2, a4, ip ; byte overlap, >= 0 by earlier code SUBGT a1, a1, a2, LSR #2 ; reduce word request MOVGT a4, ip ; new high-water = stack low-water STR a4, [a3] RET HeapLimitAdr DCD HeapLimit;----------------------------------------------------------------------------; 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 - you can compile your C code without ;; stack-limit checks (#pragma nocheck_stack or cc -zps0). However, the ;; cost of the check is (very) small and the value sometimes considerable. ;; ;; 2/ The limit check embeds knowledge of the memory layout and interacts ;; with the primitive memory management supported by __rt_alloc. Here, we ;; assume a single address space with the stack at the top growing down ;; and the heap below it growing up, with a gap (free memory) in between. ;; ;; 3/ Failure of the stack-limit check is fatal. However, failure of the low- ;; level heap allocator is passed back to its caller. ;; ;; 4/ This implementation never reduces the size of the stack. It simply ;; moves the low-water mark monatonically downwards. It is easy to do ;; better, but, of course, it takes more code and is more target-specific. ;;----------------------------------------------------------------------------;|__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). The stack space required has; already been claimed by decrementing sp, so we set the proposed sp (ip); to the actual sp and fall into the big-frame case. MOV ip, sp ; 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; but the proposed new stack pointer is in ip. SUB ip, sp, ip ; frame size required... CMP ip, #DefaultStackIncrement ; rounded up to at least MOVLT ip, #DefaultStackIncrement ; the default increment SUB sl, sl, ip SUB sl, sl, #StackSlop ; new stack low-water mark LDR ip, HeapLimitAdr ; check doesn't collide with LDR ip, [ip] ; the heap. CMP ip, sl ADD sl, sl, #StackSlop ; restore safety margin BGT stackoverflow RET ; and return if OK...stackoverflow ADR ip, StackOverflowError B save_regs_and_trapStackOverflowError DCD 3 DCB "stack overflow", 0 ALIGN;----------------------------------------------------------------------------; 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 ipdiv_core CMP a3, a4 MOVHI a4, a4, ASL #1 BHI div_corediv_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 IF {CONFIG} = 26 LDMFD sp!,{pc}^ ELSE LDMFD sp!,{pc} ENDIF; 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 IF {CONFIG} = 26 MOVS pc, ip ELSE MOV pc, ip ENDIF;; Test for division by zero (used when division is voided).|__rt_divtest| ; divtest ; CMPS a1, #0 RET NEdividebyzero ADR ip, DivideByZeroError B save_regs_and_trapDivideByZeroError DCD 1 DCB "divide by 0", 0 ALIGN END
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -