From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../ARM202U/EXAMPLES/CLSTAND/ERRTEST.C | 86 +++ .../ARM202U/EXAMPLES/CLSTAND/FPESTUB.S | 96 +++ .../ARM202U/EXAMPLES/CLSTAND/MEMMOVE.C | 23 + .../ARM202U/EXAMPLES/CLSTAND/MEMMOVE.H | 3 + .../ARM202U/EXAMPLES/CLSTAND/MEMTEST.C | 89 +++ .../ARM202U/EXAMPLES/CLSTAND/README.TXT | 3 + .../ARM202U/EXAMPLES/CLSTAND/RTSTAND.H | 52 ++ .../ARM202U/EXAMPLES/CLSTAND/RTSTAND.S | 723 ++++++++++++++++++ .../ARM202U/EXAMPLES/CLSTAND/RTSTAND1.H | 56 ++ .../ARM202U/EXAMPLES/CLSTAND/RTSTAND1.S | 787 ++++++++++++++++++++ .../ARM202U/EXAMPLES/CLSTAND/THUMB/RTSTAND.S | 819 +++++++++++++++++++++ 11 files changed, 2737 insertions(+) create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/ERRTEST.C create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/FPESTUB.S create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.C create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.H create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMTEST.C create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/README.TXT create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.H create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.S create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.H create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.S create mode 100644 Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/THUMB/RTSTAND.S (limited to 'Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND') diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/ERRTEST.C b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/ERRTEST.C new file mode 100644 index 0000000..1315b8f --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/ERRTEST.C @@ -0,0 +1,86 @@ +/* + * Standalone Hello World program - tests for presence of the FP + * support code and also shows how to interface to the + * standalone C kernel's error handler. + * + * Copyright (C) 1993 Advanced RISC Machines Limited. + */ + +#include "rtstand.h" + +extern void __swi(0) put_char(int ch); +extern void __swi(2) put_string(char *string); + +/* First let's roll our own, primitive hex number printer. + * Strictly %#.8X format. + */ +static void puth(unsigned n) { + int j; + put_string("0X"); + for (j = 0; j < 8; ++j) { + put_char("0123456789ABCDEF"[n >> 28]); + n <<= 4; + } +} + +static jmp_buf err_label; + +/* This is the function weakly-referenced from the standalone C kernel. + * If it exists, it will be called when a run-time error occurs. + * If the error is a 'pseudo-error', raised because the error-handler + * has been called directly, then the user's register set *r will contain + * random values for a1-a4 and ip (r[0-3], r[12]) and r[15] will be + * identical to r[14]. + */ +void __err_handler(__rt_error *e, __rt_registers *r) { + put_string("errhandler called: code = "); + puth(e->errnum); + put_string(": "); put_string(e->errmess); put_string("\r\n"); + put_string("caller's pc = "); puth(r->r[15]); + put_string("\r\nreturning...\r\n"); +#ifdef LONGJMP + longjmp(err_label, e->errnum); +#endif +} + +#ifdef DIVIDE_ERROR +#define LIMIT 0 +#else +#define LIMIT 1 +#endif + +int main(int argc, char *argv[]) { + int rc; + + put_string("(the floating point instruction-set is "); + if (!__rt_fpavailable()) put_string("not "); + put_string("available)\r\n"); + +/* Set up the jmp_buffer, and if returning due to longjmp then + * goto errlabel + */ + if ((rc = setjmp(err_label)) != 0) goto errlabel; + + if (__rt_fpavailable()) { + float a; + put_string("Using Floating point, but casting to int ...\r\n"); + for (a=(float) 10.0;a>=(float) LIMIT;a-=(float) 1.0) { + put_string("10000 / "); puth((int) a); put_string(" = "); + puth((int) (10000.0/a)); put_string("\r\n"); + } + } else { + int a; + put_string("Using integer arithmetic ...\r\n"); + for (a=10;a>=LIMIT;a--) { + put_string("10000 / "); puth(a); put_string(" = "); + puth(10000/a); put_string("\r\n"); + } + } + return 0; + +errlabel: + put_string("\nReturning from __err_handler() with errnum = "); + puth(rc); + put_string("\r\n\n"); + return 0; +} diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/FPESTUB.S b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/FPESTUB.S new file mode 100644 index 0000000..f015a18 --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/FPESTUB.S @@ -0,0 +1,96 @@ +;;; fpestub.s: library stub for fpe400 (emulator for fp instruction set 3) +;;; +;;; Copyright (C) Advanced RISC Machines Ltd., 1993 + +;;; RCS $Revision: 1.5 $ +;;; Checkin $Date: 1994/01/24 14:19:42 $ +;;; Revising $Author: irickard $ + + AREA |FP$$code|, CODE + + IMPORT __rt_trap, WEAK ; from run-time kernel... + +; change FPE_ to FPS_ to allow FPA support code to be incorporated in library +; (there is currently no veneer support for the combined FPE/FPASC) + IMPORT FPE_Install + IMPORT FPE_DeInstall + EXPORT FPE_GenerateError ; to FPE + + EXPORT __fp_initialise ; to client + EXPORT __fp_finalise ; to client + EXPORT __fp_address_in_emulator ; to client + + IMPORT |FP$$code$$Base| + IMPORT |FP$$code$$Limit| + + +; RISCOS SWI names (for use in very_standalone only). + +Write0 * 2 + (1:SHL:17) +NewLine * 3 + (1:SHL:17) +Exit * &11 + +;****************************************************************************** +; + + +__fp_initialise + B FPE_Install + +__fp_finalise + B FPE_DeInstall + +;****************************************************************************** +; +; Come here for a floating point exception, such as divide by zero. +; +; r0 = error descriptor +; r1 -> cpu register dump +; + +FPE_GenerateError +; still in some non-user mode... + LDR r2, =|__rt_trap| + CMP r2, #0 + BEQ very_standalone + ADD r3, r1, #r13*4 + LDMIA r3, {r13}^ ; retrieve user's sp + NOP + MOVS pc, r2 ; to __rt_ in user mode + + +very_standalone + [ {CONFIG} = 26 + TEQP pc, #0 ; to user mode + | + MSR CPSR_ctl, #&10 ; to user32 + ] + ADD r0, r0, #4 ; ignore the error code + SWI Write0 ; write the message + SWI NewLine + BL |__fp_finalise| ; tidy the ill-instr vector + SWI Exit ; and exit + +|__fp_address_in_emulator| + ; for the benefit of abort handling, determine whether an address + ; is within the code of the fp emulator. (Allowing a data abort or + ; address exception in a floating-point load or store to be reported + ; as occurring at that instruction, rather than somewhere in the code + ; of the emulator). + [ {CONFIG} = 26 + BIC r0, r0, #&fc000003 ; remove PSR bits in case + ] + LDR r1, =|FP$$code$$Base| + CMP r0, r1 + LDRGT r1, =|FP$$code$$Limit| + CMPGT r1, r0 + MOVLE r0, #0 + MOVGT r0, #1 + [ {CONFIG} = 26 + MOVS pc, lr + | + MOV pc, lr + ] + LTORG + + END diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.C b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.C new file mode 100644 index 0000000..8039f11 --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.C @@ -0,0 +1,23 @@ +#include "memmove.h" + +void *__rt_memmove(void *a, const void *b, size_t n) +/* copy memory taking care of overlap */ +/* Relies on sizeof(int)=sizeof(void *) and byte addressing. + Also that memory does not wrap round for direction test. */ +{ + /* do it fast if word aligned ... */ + if ((((int)a | (int)b | (int)n) & 3) == 0) + { int *wa,*wb; + n >>= 2; + if (a < (void *)b) + for (wa = (int *)a, wb = (int *)b; n-- > 0;) *wa++ = *wb++; + else for (wa = n+(int *)a, wb = n+(int *)b; n-- > 0;) *--wa = *--wb; + } + else + { char *ca,*cb; + if (a < (void *)b) + for (ca = (char *)a, cb = (char *)b; n-- > 0;) *ca++ = *cb++; + else for (ca = n+(char *)a, cb = n+(char *)b; n-- > 0;) *--ca = *--cb; + } + return a; +} diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.H b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.H new file mode 100644 index 0000000..480df1b --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMMOVE.H @@ -0,0 +1,3 @@ +typedef unsigned int size_t; /* others (e.g. ) define */ + +extern void *__rt_memmove(void * /*s1*/, const void * /*s2*/, size_t /*n*/); diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMTEST.C b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMTEST.C new file mode 100644 index 0000000..46e8f1a --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/MEMTEST.C @@ -0,0 +1,89 @@ +/* + * Standalone run-time system test. + * + * This program checks that stack overflow checking works and that it + * interacts properly with heap extension. + * + * Copyright (C) 1991 Advanced RISC Machines Limited. + */ + +#include "rtstand.h" + +extern void __swi(0) put_char(int ch); +extern void __swi(2) put_string(char *string); + +/* First, we make a function to claim a large(-ish) stack frame. + * Some obfuscation in case the compiler optimises us away... + */ +static int stack(int n, int v) { + /* claim n KB of stack */ + int x[256],i; + + if (n > 1) v = stack(n-1, v); + for (i = 0; i < 256; ++i) x[i] = v + i; + return x[0] + x[255]; +} + +/* Now we roll our own decimal output function - strictly %d format... + */ +static void puti(int n) { + if (n < 0) { + put_char('-'); + n = -n; + } + if (n > 9) { + int n1 = n / 10; + n = n % 10; + puti(n1); + } + put_char(n + '0'); +} + +/* ...and a hex outputter... strictly %#.8X format. + */ +static void puth(unsigned n) { + int j; + put_string("0X"); + for (j = 0; j < 8; ++j) { + put_char("0123456789ABCDEF"[n >> 28]); + n <<= 4; + } +} + +/* Finally, we sit in a loop extending the heap and claiming ever bigger + * stack franes until something gives. Probably, the heap will give first, + * as currently tuned, and the program will announce "memory exhausted". + * If you tune it differently, it can be made to fail will a stack overflow + * run-time error. Try compiling this -DSTACK_OVERFLOW to provoke it. + */ +int main(int argc, char *argv[]) { + unsigned size, ask, got, total; + void *base; + + put_string("kernel memory management test\r\n"); + + size = 4; /* KB */ + ask = 0; + for (total = 0;;) { + put_string("force stack to "); puti(size); put_string("KB\r\n"); + stack(size, 0); + put_string("request "); puti(ask); put_string(" words of heap - "); + got = __rt_alloc(ask, &base); + total += got; + put_string("allocate "); puti(got); + put_string(" words at "); puth((unsigned)base); put_string("\r\n"); + if (got < ask) break; + ask += got / 2; +#ifdef STACK_OVERFLOW + size *= 2; +#else + size += 4; +#endif + } + + put_string("memory exhausted, "); + puti(total); put_string(" words of heap, "); + puti(size); put_string("KB of stack\r\n"); + + return 0; +} diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/README.TXT b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/README.TXT new file mode 100644 index 0000000..bc81770 --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/README.TXT @@ -0,0 +1,3 @@ +For details of how to use the files in this directory refer to the ARM +Programming Techniques, and in particular to the section on +Deeply Embedded Programming in C. diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.H b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.H new file mode 100644 index 0000000..1b956ea --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.H @@ -0,0 +1,52 @@ +/* + * Standalone C run-time kernel. + * Copyright (C) 1991 Advanced RISC Machines Limited. + */ + +#ifndef __rtstand_h +#define __rtstand_h + + +extern void __rt_exit(int /* code */); /* + * Terminate execution; equivalent to returning from main. + * NOTE: all other details are determined by your implementation. + */ + +typedef struct {unsigned errnum; char errmess[252];} __rt_error; +typedef struct {int r[16];} __rt_registers; + +extern void __err_handler(__rt_error *, __rt_registers *); + +extern int __rt_fpavailable(void); +/* + * Return non-0 iff there is support for the floating-point instruction set. + */ + +extern unsigned __rt_alloc(unsigned /*minwords*/, void ** /*block*/); +/* + * Tries to allocate a block of sensible size >= minwords. Failing that, + * it allocates the largest possible block (may have size zero). + * Sensible size is determined by your implementation (default: 256 words). + * *block is set to a pointer to the start of the allocated block + * (NULL if 'a block of size zero' has been allocated). + */ + +#ifdef __JMP_BUF_SIZE +typedef int jmp_buf[__JMP_BUF_SIZE]; +#else +typedef int jmp_buf[22]; /* size suitable for the ARM */ +#endif /* an array type suitable for holding the data */ + /* needed to restore a calling environment. */ +#ifdef __STDC__ +/* setjmp is a macro so that it cannot be used other than directly called. */ +/* NB that ANSI declare that anyone who undefined the setjmp macro or uses */ +/* (or defines) the name setjmp without including this header will get */ +/* what they deserve. NOTE: -pcc mode doesn't allow circular definitions...*/ +#define setjmp(jmp_buf) (setjmp(jmp_buf)) +#endif + +extern int setjmp(jmp_buf /*env*/); + +extern int longjmp(jmp_buf /*env*/, int /*val*/); + +#endif diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.S b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.S new file mode 100644 index 0000000..325b494 --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND.S @@ -0,0 +1,723 @@ +; Issue: 0.03/23-Feb-93 +; +; Purpose: Minimal, standalone, C-library kernel. +; +; 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 shows you how to write your own minimal, standalone, run-time ; +; support system for code compiled by Advanced RISC Machines's C Compiler. ; +; It can be assembled using Advanced RISC Machines's ARM assembler (armasm) ; +; or any assembler comaptible with it. ; +; ; +; This example code has been written to run under Advanced RISC Machines's ; +; ARM emulation system (ARMulator). It can also run without modification ; +; under Acorm Computer's "RISC OS" operating system for its ARM-based ; +; personal workstations. ; +; ; +; In fact, 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. To get going, you need write only 4 simple fns. ; +; ; +; WHAT THIS CODE PROVIDES: ; +; ; +; - Example, executable implementations (for the ARMulator) of the few ; +; simple functions you need to implement to customise this code to your ; +; environment. These include: ; +; - setting up the initial stack and heap and calling main (__main) ; +; - 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 handling traps, faults, escapes, exceptions or interrupts. ; +; ; +; - A way to print to the debugging channel (use in line SWIs) ; +; ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; The following constant is the Top Of Memory - Adjust this for your system ; +;----------------------------------------------------------------------------; + +TopOfMemory EQU 0x80000 ; 512Kb + +;----------------------------------------------------------------------------; +; Things you may wish to tune, but which you don't need to alter, follow. ; +;----------------------------------------------------------------------------; + +DefaultStackSize EQU 4*1024 ; The stack starts of this big unless + ; over-ridden by __root_stack_size. + +DefaultStackIncrement EQU 1*1024 ; At each overflow it grows by at + ; at least this many bytes. + +StackSlop EQU 512 ; sl is kept this far above the real + ; stack low-water mark. NOTE: MUST be + ; >= 256 or the compiled limit checks + ; will be invalidated. + +MinHeapIncrement EQU 256 ; Min number of WORDS to extend the + ; heap by on calling __rt_alloc. + + GBLL EnsureNoFPSupport +EnsureNoFPSupport SETL {FALSE} ; 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. + +;----------------------------------------------------------------------------; +; Symbols defined in other, separately-assembled modules, must be IMPORTed. ; +; We import them WEAKly so that they need not be defined. ; +;----------------------------------------------------------------------------; + + IF EnsureNoFPSupport = {FALSE} + IMPORT |__fp_initialise|, WEAK + IMPORT |__fp_finalise|, WEAK + ENDIF + +;----------------------------------------------------------------------------; +; The existence of __fp_initialise (imported WEAKly) indicates that floating ; +; point support code (or the access stub thereof) has been linked with this ; +; application. If you wish to load the FP support code separately, you may ; +; want to define some other mechanism for detecting the presence/absence of ; +; floating point support. Note that setjmp and longjmp must know whether the ; +; floating-point instruction set is supported. ; +; __fp_initialise is called by __main and __fp_finalise is called by _exit. ; +;----------------------------------------------------------------------------; + + IMPORT |__root_stack_size|, WEAK + +;----------------------------------------------------------------------------; +; If __root_stack_size, also imported WEAKly, exists, the value it addresses ; +; is used as the initial size of the stack. It can be defined in your C ; +; program as, e.g. int __root_stack_size = 10000; /* 10KB initial stack */ ; +;----------------------------------------------------------------------------; + + IMPORT |__err_handler|, WEAK + +;----------------------------------------------------------------------------; +; If __err_handler exists, errors are passed to it; otherwise, we print a ; +; simple diagnostic message and exit. ; +;----------------------------------------------------------------------------; + + IMPORT |Image$$RW$$Limit| + +;----------------------------------------------------------------------------; +; Image$$RW$$Limit is a linker-created symbol marking the end of the image. ; +; Its value is used as the heap base. ; +;----------------------------------------------------------------------------; + + IMPORT main + +;----------------------------------------------------------------------------; +; The symbol main identifies the C function entered from this code. ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; THE MEMORY MODEL ASSUMED BY THIS IMPLEMENTATION ; +; ; +; +----------------+ <--- top of memory (high address) ; +; | Stack space | ; +; |................| <--- stack pointer (sp) ; +; | Free stack | ; +; |................| <--- stack limit pointer (sl) ; +; +----------------+ <--- stack low-water mark (sl - StackSlop) ; +; | | ; +; | Unused memory | ; +; | | ; +; +----------------+ <--- top of heap (HeapLimit) ; +; | | ; +; | Heap space | ; +; | | ; +; +----------------+ <--- top of application (Image$$RW$$Limit) ; +; | Static data | } ; +; |................| } the application's memory image ; +; | Code | } ; +; +----------------+ <--- application load address ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; Now the symbols we define and EXPORT from this this module. ; +;----------------------------------------------------------------------------; +; First, symbols identifying the four functions you have to implement to ; +; make this run-time kernel work on your hardware. ; +;----------------------------------------------------------------------------; + + EXPORT |__main| + EXPORT |__rt_exit| + EXPORT |__rt_fpavailable| + EXPORT |__rt_trap| + +;----------------------------------------------------------------------------; +; Then some simple support for C heap management. It interacts with stack- ; +; limit checking but should require no attention in a first re-targetting. ; +;----------------------------------------------------------------------------; + + EXPORT |__rt_alloc| + +;----------------------------------------------------------------------------; +; 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| + +;----------------------------------------------------------------------------; +; 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| + +;----------------------------------------------------------------------------; + AREA |C$$data| ; This module's data area ; +;----------------------------------------------------------------------------; + +HeapLimit + DCD |Image$$RW$$Limit| ; initialised by the linker. + +;----------------------------------------------------------------------------; +; This code has to run in but 26-bit ARM modes and 32-bit modes. To allow ; +; for this, the code is carefully written so that all PSR restoration in ; +; 26-bit mode is via the following macro. ; +;----------------------------------------------------------------------------; + + MACRO + RET $cond + IF {CONFIG} = 26 + MOV$cond.S pc, lr + ELSE + MOV$cond pc, lr + ENDIF + MEND + +;----------------------------------------------------------------------------; +; The following four SWI definitions are specific to ARMulator/RISC OS. ; +; However, you will need to replace the whole of this following section... ; +; and all uses of these SWIs should also be replaced. ; +;----------------------------------------------------------------------------; + +WriteC EQU 0 ; Write r0 to error/debug stream. +Write0 EQU 2 ; Write 0-terminated string pointed + ; to by r0 to error/debug stream. +Exit EQU 17 ; Terminate program execution. + +;----------------------------------------------------------------------------; + AREA |C$$code$$__main|, CODE, READONLY +; The code area containing __main, __rt_exit ; +;----------------------------------------------------------------------------; + + ENTRY ; Define the image entry point. + +|__main| +; +; This is the initial entry point to the image. +; Have to establish a stack for C +; No arguments are passed to main from an embedded application, +; so argc and argv are set up to 0 + + MOV sp, #TopOfMemory ; Initial stack pointer... + MOV fp, #0 ; No previous frame, so fp=0 + + LDR a3, RootStackSize + CMP a3, #0 ; Is RootStackSize defined? + LDRNE a3, [a3] ; Yes: use value... + CMPNE a3, #DefaultStackSize ; but check caller not being silly. + MOVLE a3, #DefaultStackSize ; No/silly: use default size. + + SUB sl, sp, a3 ; stack low-water mark + ADD sl, sl, #StackSlop ; sl = LWM + StackSlop + + IF EnsureNoFPSupport = {FALSE} + LDR a1, fp_initialise ; initialise FP code if present + CMP a1, #0 + MOVNE lr, pc + MOVNE pc, a1 + ENDIF + + MOV a1, #0 ; set argc to 0 + MOV a2, #0 ; and argv to NUL + BL main ; Call main, falling through to + ; exit on return. + +|__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 + SWI Exit ; suicide... + +RootStackSize + DCD |__root_stack_size| + + 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$$__rt_trap|, CODE, READONLY +; 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); + + STMFD sp!, {a1} ; save e in case handler returns... + LDR ip, err_handler + CMP ip, #0 + MOVNE lr, pc + IF {CONFIG} = 26 + MOVNES pc, ip ; if got a handler, use it and + ELSE + MOVNE pc, ip ; if got a handler, use it and + ENDIF + LDMFD sp!, {v1} ; hope not to return... + + 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 code +err_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: ", 0 + +RTErrorTail + 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_trap + +StackOverflowError + DCD 3 + DCB "stack overflow", 0 + ALIGN + + +;----------------------------------------------------------------------------; + 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, sl, 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, sl, 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, sl, 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 + 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 NE +dividebyzero + ADR ip, DivideByZeroError + B save_regs_and_trap + +DivideByZeroError + DCD 1 + DCB "divide by 0", 0 + ALIGN + + END diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.H b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.H new file mode 100644 index 0000000..0366f05 --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.H @@ -0,0 +1,56 @@ +/* + * Standalone C run-time kernel. + * Copyright (C) 1991 Advanced RISC Machines Limited. + */ + +#ifndef __rtstand_h +#define __rtstand_h + + +extern void __rt_exit(int /* code */); /* + * Terminate execution; equivalent to returning from main. + * NOTE: all other details are determined by your implementation. + */ + +typedef struct {unsigned errnum; char errmess[252];} __rt_error; +typedef struct {int r[16];} __rt_registers; + +extern void __err_handler(__rt_error *, __rt_registers *); + +extern int __rt_fpavailable(void); +/* + * Return non-0 iff there is support for the floating-point instruction set. + */ + +extern unsigned __rt_alloc(unsigned /*minwords*/, void ** /*block*/); +/* + * Tries to allocate a block of sensible size >= minwords. Failing that, + * it allocates the largest possible block (may have size zero). + * Sensible size is determined by your implementation (default: 256 words). + * *block is set to a pointer to the start of the allocated block + * (NULL if 'a block of size zero' has been allocated). + */ + +#ifdef __JMP_BUF_SIZE +typedef int jmp_buf[__JMP_BUF_SIZE]; +#else +typedef int jmp_buf[22]; /* size suitable for the ARM */ +#endif /* an array type suitable for holding the data */ + /* needed to restore a calling environment. */ +#ifdef __STDC__ +/* setjmp is a macro so that it cannot be used other than directly called. */ +/* NB that ANSI declare that anyone who undefined the setjmp macro or uses */ +/* (or defines) the name setjmp without including this header will get */ +/* what they deserve. NOTE: -pcc mode doesn't allow circular definitions...*/ +#define setjmp(jmp_buf) (setjmp(jmp_buf)) +#endif + +extern int setjmp(jmp_buf /*env*/); + +extern int longjmp(jmp_buf /*env*/, int /*val*/); + +typedef unsigned int size_t; /* others (e.g. ) define */ + +extern void *__rt_memmove(void * /*s1*/, const void * /*s2*/, size_t /*n*/); + +#endif diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.S b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.S new file mode 100644 index 0000000..7d85dfa --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.S @@ -0,0 +1,787 @@ +; Issue: 0.03/23-Feb-93 +; +; Purpose: Minimal, standalone, C-library kernel. +; +; 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 shows you how to write your own minimal, standalone, run-time ; +; support system for code compiled by Advanced RISC Machines's C Compiler. ; +; It can be assembled using Advanced RISC Machines's ARM assembler (armasm) ; +; or any assembler comaptible with it. ; +; ; +; This example code has been written to run under Advanced RISC Machines's ; +; ARM emulation system (ARMulator). It can also run without modification ; +; under Acorm Computer's "RISC OS" operating system for its ARM-based ; +; personal workstations. ; +; ; +; In fact, 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. To get going, you need write only 4 simple fns. ; +; ; +; WHAT THIS CODE PROVIDES: ; +; ; +; - Example, executable implementations (for the ARMulator) of the few ; +; simple functions you need to implement to customise this code to your ; +; environment. These include: ; +; - setting up the initial stack and heap and calling main (__main) ; +; - 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 handling traps, faults, escapes, exceptions or interrupts. ; +; ; +; - A way to print to the debugging channel (use in line SWIs) ; +; ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; The following constant is the Top Of Memory - Adjust this for your system ; +;----------------------------------------------------------------------------; + +TopOfMemory EQU 0x80000 ; 512Kb + +;----------------------------------------------------------------------------; +; Things you may wish to tune, but which you don't need to alter, follow. ; +;----------------------------------------------------------------------------; + +DefaultStackSize EQU 4*1024 ; The stack starts of this big unless + ; over-ridden by __root_stack_size. + +DefaultStackIncrement EQU 1*1024 ; At each overflow it grows by at + ; at least this many bytes. + +StackSlop EQU 512 ; sl is kept this far above the real + ; stack low-water mark. NOTE: MUST be + ; >= 256 or the compiled limit checks + ; will be invalidated. + +MinHeapIncrement EQU 256 ; Min number of WORDS to extend the + ; heap by on calling __rt_alloc. + + GBLL EnsureNoFPSupport +EnsureNoFPSupport SETL {FALSE} ; 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. + +;----------------------------------------------------------------------------; +; Symbols defined in other, separately-assembled modules, must be IMPORTed. ; +; We import them WEAKly so that they need not be defined. ; +;----------------------------------------------------------------------------; + + IF EnsureNoFPSupport = {FALSE} + IMPORT |__fp_initialise|, WEAK + IMPORT |__fp_finalise|, WEAK + ENDIF + +;----------------------------------------------------------------------------; +; The existence of __fp_initialise (imported WEAKly) indicates that floating ; +; point support code (or the access stub thereof) has been linked with this ; +; application. If you wish to load the FP support code separately, you may ; +; want to define some other mechanism for detecting the presence/absence of ; +; floating point support. Note that setjmp and longjmp must know whether the ; +; floating-point instruction set is supported. ; +; __fp_initialise is called by __main and __fp_finalise is called by _exit. ; +;----------------------------------------------------------------------------; + + IMPORT |__root_stack_size|, WEAK + +;----------------------------------------------------------------------------; +; If __root_stack_size, also imported WEAKly, exists, the value it addresses ; +; is used as the initial size of the stack. It can be defined in your C ; +; program as, e.g. int __root_stack_size = 10000; /* 10KB initial stack */ ; +;----------------------------------------------------------------------------; + + IMPORT |__err_handler|, WEAK + +;----------------------------------------------------------------------------; +; If __err_handler exists, errors are passed to it; otherwise, we print a ; +; simple diagnostic message and exit. ; +;----------------------------------------------------------------------------; + + IMPORT |Image$$RW$$Limit| + +;----------------------------------------------------------------------------; +; Image$$RW$$Limit is a linker-created symbol marking the end of the image. ; +; Its value is used as the heap base. ; +;----------------------------------------------------------------------------; + + IMPORT main + +;----------------------------------------------------------------------------; +; The symbol main identifies the C function entered from this code. ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; THE MEMORY MODEL ASSUMED BY THIS IMPLEMENTATION ; +; ; +; +----------------+ <--- top of memory (high address) ; +; | Stack space | ; +; |................| <--- stack pointer (sp) ; +; | Free stack | ; +; |................| <--- stack limit pointer (sl) ; +; +----------------+ <--- stack low-water mark (sl - StackSlop) ; +; | | ; +; | Unused memory | ; +; | | ; +; +----------------+ <--- top of heap (HeapLimit) ; +; | | ; +; | Heap space | ; +; | | ; +; +----------------+ <--- top of application (Image$$RW$$Limit) ; +; | Static data | } ; +; |................| } the application's memory image ; +; | Code | } ; +; +----------------+ <--- application load address ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; Now the symbols we define and EXPORT from this this module. ; +;----------------------------------------------------------------------------; +; First, symbols identifying the four functions you have to implement to ; +; make this run-time kernel work on your hardware. ; +;----------------------------------------------------------------------------; + + EXPORT |__main| + EXPORT |__rt_exit| + EXPORT |__rt_fpavailable| + EXPORT |__rt_trap| + +;----------------------------------------------------------------------------; +; Then some simple support for C heap management. It interacts with stack- ; +; limit checking but should require no attention in a first re-targetting. ; +;----------------------------------------------------------------------------; + + EXPORT |__rt_alloc| + +;----------------------------------------------------------------------------; +; 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| + +;----------------------------------------------------------------------------; +; 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| + +;----------------------------------------------------------------------------; + AREA |C$$data| ; This module's data area ; +;----------------------------------------------------------------------------; + +HeapLimit + DCD |Image$$RW$$Limit| ; initialised by the linker. + +;----------------------------------------------------------------------------; +; This code has to run in but 26-bit ARM modes and 32-bit modes. To allow ; +; for this, the code is carefully written so that all PSR restoration in ; +; 26-bit mode is via the following macro. ; +;----------------------------------------------------------------------------; + + MACRO + RET $cond + IF {CONFIG} = 26 + MOV$cond.S pc, lr + ELSE + MOV$cond pc, lr + ENDIF + MEND + +;----------------------------------------------------------------------------; +; The following four SWI definitions are specific to ARMulator/RISC OS. ; +; However, you will need to replace the whole of this following section... ; +; and all uses of these SWIs should also be replaced. ; +;----------------------------------------------------------------------------; + +WriteC EQU 0 ; Write r0 to error/debug stream. +Write0 EQU 2 ; Write 0-terminated string pointed + ; to by r0 to error/debug stream. +Exit EQU 17 ; Terminate program execution. + +;----------------------------------------------------------------------------; + AREA |C$$code$$__main|, CODE, READONLY +; The code area containing __main, __rt_exit ; +;----------------------------------------------------------------------------; + + ENTRY ; Define the image entry point. + +|__main| +; +; This is the initial entry point to the image. +; Have to establish a stack for C +; No arguments are passed to main from an embedded application, +; so argc and argv are set up to 0 + + MOV sp, #TopOfMemory ; Initial stack pointer... + MOV fp, #0 ; No previous frame, so fp=0 + + LDR a3, RootStackSize + CMP a3, #0 ; Is RootStackSize defined? + LDRNE a3, [a3] ; Yes: use value... + CMPNE a3, #DefaultStackSize ; but check caller not being silly. + MOVLE a3, #DefaultStackSize ; No/silly: use default size. + + SUB sl, sp, a3 ; stack low-water mark + ADD sl, sl, #StackSlop ; sl = LWM + StackSlop + + IF EnsureNoFPSupport = {FALSE} + LDR a1, fp_initialise ; initialise FP code if present + CMP a1, #0 + MOVNE lr, pc + MOVNE pc, a1 + ENDIF + + MOV a1, #0 ; set argc to 0 + MOV a2, #0 ; and argv to NUL + BL main ; Call main, falling through to + ; exit on return. + +|__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 + SWI Exit ; suicide... + +RootStackSize + DCD |__root_stack_size| + + 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$$__rt_trap|, CODE, READONLY +; 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); + + STMFD sp!, {a1} ; save e in case handler returns... + LDR ip, err_handler + CMP ip, #0 + MOVNE lr, pc + IF {CONFIG} = 26 + MOVNES pc, ip ; if got a handler, use it and + ELSE + MOVNE pc, ip ; if got a handler, use it and + ENDIF + LDMFD sp!, {v1} ; hope not to return... + + 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 code +err_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: ", 0 + +RTErrorTail + 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_trap + +StackOverflowError + DCD 3 + DCB "stack overflow", 0 + ALIGN + + +;----------------------------------------------------------------------------; + 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, sl, 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, sl, 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, sl, 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 + 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 NE +dividebyzero + ADR ip, DivideByZeroError + B save_regs_and_trap + +DivideByZeroError + DCD 1 + DCB "divide by 0", 0 + ALIGN + +;----------------------------------------------------------------------------; + AREA |C$$code$$__rt_memmove|, CODE, READONLY +; The code area containing __rt_memmove() extracted from the ARM C Library ; +; Note that as this was produced using armcc -apcs 3/32bit it is only ; +; intended for use in 32 bit modes ; +;----------------------------------------------------------------------------; + + EXPORT |__rt_memmove| +|__rt_memmove| + ORR a4,a1,a2 + ORR a4,a4,a3 + ANDS a4,a4,#3 + BNE |L000064.J4.__rt_memmove| + MOV a3,a3,LSR #2 + CMP a1,a2 + MOVLT a4,a1 + BLT |L000034.J9.__rt_memmove| + ADD a4,a1,a3,LSL #2 + ADD a2,a2,a3,LSL #2 + B |L000050.J13.__rt_memmove| +|L00002c.J8.__rt_memmove| + LDR ip,[a2],#4 + STR ip,[a4],#4 +|L000034.J9.__rt_memmove| + MOV ip,a3 + SUB a3,a3,#1 + CMP ip,#0 + BHI |L00002c.J8.__rt_memmove| + MOV pc,lr +|L000048.J12.__rt_memmove| + LDR ip,[a2,#-4]! + STR ip,[a4,#-4]! +|L000050.J13.__rt_memmove| + MOV ip,a3 + SUB a3,a3,#1 + CMP ip,#0 + BHI |L000048.J12.__rt_memmove| + MOV pc,lr +|L000064.J4.__rt_memmove| + CMP a1,a2 + MOVLT a4,a1 + BLT |L000084.J19.__rt_memmove| + ADD a4,a1,a3 + ADD a2,a2,a3 + B |L0000a0.J23.__rt_memmove| +|L00007c.J18.__rt_memmove| + LDRB ip,[a2],#1 + STRB ip,[a4],#1 +|L000084.J19.__rt_memmove| + MOV ip,a3 + SUB a3,a3,#1 + CMP ip,#0 + BHI |L00007c.J18.__rt_memmove| + MOV pc,lr +|L000098.J22.__rt_memmove| + LDRB ip,[a2,#-1]! + STRB ip,[a4,#-1]! +|L0000a0.J23.__rt_memmove| + MOV ip,a3 + SUB a3,a3,#1 + CMP ip,#0 + BHI |L000098.J22.__rt_memmove| + MOV pc,lr + + END diff --git a/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/THUMB/RTSTAND.S b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/THUMB/RTSTAND.S new file mode 100644 index 0000000..605926f --- /dev/null +++ b/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/THUMB/RTSTAND.S @@ -0,0 +1,819 @@ +; Issue: 1.00/19-Jan-95 +; +; Purpose: Minimal, standalone, C-library kernel for Thumb +; +; Copyright (C) 1995 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 shows you how to write your own minimal, standalone, Thumb ; +; run-time support system for code compiled by Advanced RISC Machines's ; +; Thumb C Compiler. It can be assembled using Advanced RISC Machines's Thumb ; +; assembler. ; +; ; +; This code may be used to build a ROM image. It may also be run under ; +; Advanced RISC Machines's ARM emulation system (ARMulator). ; +; ; +; In fact, 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. ; +; ; +; 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. To get going, you need write only 4 simple fns. ; +; ; +; WHAT THIS CODE PROVIDES: ; +; ; +; - Example, executable implementations of the few ; +; simple functions you need to implement to customise this code to your ; +; environment. These include: ; +; - setting up the initial stack and heap and calling main (__main) ; +; - program termination (__rt_exit) ; +; ; +; - 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 handling traps, faults, escapes, exceptions or interrupts. ; +; ; +; - A way to print to the debugging channel (use in line SWIs) ; +; ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; The following constant is the Top Of RAM - Adjust this for your system ; +;----------------------------------------------------------------------------; + +TopOfMemory EQU 0x80000 ; 512Kb + +;----------------------------------------------------------------------------; +; Things you may wish to tune, but which you don't need to alter, follow. ; +;----------------------------------------------------------------------------; + +DefaultStackSize EQU 4*1024 ; The stack starts of this big unless + ; over-ridden by __root_stack_size. + +DefaultStackIncrement EQU 1*1024 ; At each overflow it grows by at + ; at least this many bytes. + +StackSlop EQU 512 ; sl is kept this far above the real + ; stack low-water mark. NOTE: MUST be + ; >= 256 or the compiled limit checks + ; will be invalidated. + +MinHeapIncrement EQU 256 ; Min number of WORDS to extend the + ; heap by on calling __rt_alloc. + +;----------------------------------------------------------------------------; +; Symbols defined in other, separately-assembled modules, must be IMPORTed. ; +; We import them WEAKly so that they need not be defined. ; +;----------------------------------------------------------------------------; + + IMPORT __root_stack_size, WEAK + +;----------------------------------------------------------------------------; +; If __root_stack_size, also imported WEAKly, exists, the value it addresses ; +; is used as the initial size of the stack. It can be defined in your C ; +; program as, e.g. int __root_stack_size = 10000; /* 10KB initial stack */ ; +;----------------------------------------------------------------------------; + + IMPORT __err_handler, WEAK + +;----------------------------------------------------------------------------; +; If __err_handler exists, errors are passed to it; otherwise, we print a ; +; simple diagnostic message and exit. ; +;----------------------------------------------------------------------------; + + IMPORT |Image$$RO$$Base| + IMPORT |Image$$RO$$Limit| + IMPORT |Image$$RW$$Base| + IMPORT |Image$$RW$$Limit| + IMPORT |Image$$ZI$$Base| + IMPORT |Image$$ZI$$Limit| + +;----------------------------------------------------------------------------; +; The above symbols are created by the linker to define various sections in ; +; the ROM/RAM image. ; +; ; +; Image$$RO$$Base defines the code (ROM) base address ; +; Image$$RO$$Limit defines the code limit and the start of a section of ; +; data initialisation values which are copied to RAM ; +; in __main below before main is called. ; +; Image$$RW$$Base defines the data (RAM) base address ; +; Image$$RW$$Limit defines the data end address ; +; Image$$ZI$$Base defines the base of a section to be initialised with 0s ; +; Image$$ZI$$Limit defines the end of the region to be initialised with 0s ; +; (must be the same as Image$$RW$$Limit in this model) ; +;----------------------------------------------------------------------------; + + IMPORT main + +;----------------------------------------------------------------------------; +; The symbol main identifies the C function entered from this code. ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; THE MEMORY MODEL ASSUMED BY THIS IMPLEMENTATION ; +; ; +; RAM ; +; ; +; +------------------+ <--- top of memory (high address) ; +; | Stack space | ; +; |..................| <--- stack pointer (sp) ; +; | Free stack | ; +; |..................| <--- stack limit pointer (sl) ; +; +------------------+ <--- stack low-water mark (sl - StackSlop) ; +; | | ; +; | Unused memory | ; +; | | ; +; +------------------+ <--- top of heap (HeapLimit) ; +; | | ; +; | Heap space | ; +; | | ; +; +------------------+ <--- top of fixed data (Image$$RW$$Limit) ; +; | Zero init data | (=Image$$ZI$$Limit) ; +; +------------------+ <--- top of initialised (Image$$ZI$$Base) ; +; | Initialised data | data ; +; +------------------+ <--- Data base address (Image$$RW$$Base) ; +; ; +; ROM ; +; ; +; +------------------+ <--- Top of ROM image ; +; | Initial values | } Copied to "Initialised data" section in RAM ; +; | for Init data | } on statup in __main below ; +; +------------------+ <--- End of code (Image$$RO$$Limit) ; +; | Code | ; +; +------------------+ <--- Code base address (Image$$RO$$Base) ; +;----------------------------------------------------------------------------; + +;----------------------------------------------------------------------------; +; Now the symbols we define and EXPORT from this module. ; +;----------------------------------------------------------------------------; +; First, symbols identifying the four functions you have to implement to ; +; make this run-time kernel work on your hardware. ; +;----------------------------------------------------------------------------; + + EXPORT __main + EXPORT __rt_exit + EXPORT __rt_trap + +;----------------------------------------------------------------------------; +; Then some simple support for C heap management. It interacts with stack- ; +; limit checking but should require no attention in a first re-targetting. ; +;----------------------------------------------------------------------------; + + EXPORT __rt_alloc + +;----------------------------------------------------------------------------; +; Next, optional support for C stack-limit checking. This code should need ; +; no attention in a first re-targetting. ; +;----------------------------------------------------------------------------; + + 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. ; +;----------------------------------------------------------------------------; + + 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 __16__rt_udiv + EXPORT __16__rt_udiv10 + EXPORT __16__rt_sdiv + EXPORT __16__rt_sdiv10 + EXPORT __16__rt_divtest + +;----------------------------------------------------------------------------; + AREA |C$$data| ; This module's data area ; +;----------------------------------------------------------------------------; + +HeapLimit + DCD |Image$$RW$$Limit| ; initialised by the linker. + +;----------------------------------------------------------------------------; +; Macro to return from a function ; +; We use the BX instruction below rather than MOV pc, lr so that the return ; +; will work correctly if we are called from ARM state ; +;----------------------------------------------------------------------------; + + MACRO + RET + BX lr + MEND + +;----------------------------------------------------------------------------; +; The following four SWI definitions are specific to ARMulator/RISC OS. ; +; However, you will need to replace the whole of this following section... ; +; and all uses of these SWIs should also be replaced. ; +;----------------------------------------------------------------------------; + +WriteC EQU 0 ; Write r0 to error/debug stream. +Write0 EQU 2 ; Write 0-terminated string pointed + ; to by r0 to error/debug stream. +Exit EQU 17 ; Terminate program execution. + + CODE16 + +TBit EQU 1 ; Bit to set in register to enter + ; Thumb state with BX + +;----------------------------------------------------------------------------; +; Use area name "!!!" so this area is placed first as AREAs are sorted by +; area name. + AREA |!!!|, CODE, READONLY, INTERWORK +; The code area containing __main, __rt_exit ; +;----------------------------------------------------------------------------; + + ENTRY ; Define the image entry point. + +__main + CODE32 ; Entered in ARM state presumeably + ADR lr, __main_16+TBit + BX lr + CODE16 +__main_16 +; +; This is the initial entry point to the image. +; Have to establish a stack for C +; No arguments are passed to main from an embedded application, +; so argc and argv are set up to 0 + + LDR r0, =TopOfMemory ; Set up initial stack pointer + MOV sp, r0 + + MOV r0, #0 + MOV fp, r0 ; No previous frame, so fp=0 + + LDR r0, =DefaultStackSize + LDR r1, =__root_stack_size + CMP r1, #0 ; Is RootStackSize defined? + BEQ %F0 ; No => Use default + LDR r1, [r1] ; Yes => Get value + CMP r1, r0 ; But check value >= DefaultStackSize + BCC %F0 ; if >= use default in r0. + MOV r0, r1 +0 + MOV r1, sp + SUB r1, r0 ; stack low limit + LDR r0, =StackSlop + ADD r1, r0 ; plus a bit spare + MOV sl, r1 + +; Now initialise the data segment by copying the initial values from ROM +; to RAM and by clearing the zero init segment to 0. + + LDR r0, =|Image$$RO$$Limit| ; Get pointer to ROM initial values + LDR r1, =|Image$$RW$$Base| ; And RAM data segment + LDR r3, =|Image$$ZI$$Base| ; Zero init base = top of initialised segment + CMP r0, r1 ; Check that they are different, (they may be + BEQ %F3 ; the same if the image is running in RAM) + B %F2 +1 + LDMIA r0!, {r2} ; Copy the initialising data over + STMIA r1!, {r2} +2 CMP r1, r3 + BCC %B1 +3 + LDR r1, =|Image$$ZI$$Limit| ; Top of area to be zero initialised + MOV r2, #0 + B %F5 + +4 STMIA r3!, {r2} ; Clear out zero init segment +5 CMP r3, r1 + BCC %B4 + + MOV r0, #0 ; set argc to 0 + MOV r1, #0 ; and argv to NUL + BL main ; Call main, falling through to + ; exit on return. + +__rt_exit ; exit +; +; void __rt_exit(int code); +; Terminate execution, optionally setting return code (ignored here). +; MUST NOT RETURN. + + SWI Exit ; suicide... + +;----------------------------------------------------------------------------; + AREA |C$$code$$__rt_trap|, CODE, READONLY +; 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_trap +; +; void __rt_trap(__rt_error *e, __rt_registers *r); + + PUSH {r0} ; save e in case handler returns... + 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, RTErrorHead ; No handler, or handler returned + SWI Write0 ; write preamble... + POP {r0} + ADD r0, #4 + SWI Write0 ; write error diagnosis + ADR r0, RTErrorTail + SWI Write0 ; write postlude + MOV a1, #255 + BL __rt_exit ; 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_trap + 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_trap + + ALIGN +RTErrorHead + DCB 10, 13, "run time error: ", 0 + + ALIGN +RTErrorTail + 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. + + LDR r2, =MinHeapIncrement + CMP r0, r2 ; round up to at least + BGE %F0 + MOV r0, r2 ; MinHeapIncrement words... +0 + MOV ip, sl + LDR r2, =-StackSlop + ADD ip, r2 ; current stack low-water mark + LDR r2, =HeapLimit + LDR r3, [r2] ; current heap high-water mark + CMP r3, ip + BLE %F1 + MOV r3, #0 ; no space, *block = NULL + MOV r0, #0 ; no space, return 0 +1 + STR r3, [r1] + LSL r0, #2 ; Convert size request to bytes + ADD r3, r0 ; proposed new heap limit + CMP r3, ip + BLE %F2 + ADD r0, ip ; Reduce size request by amount + SUB r0, r3 ; of overlap + MOV r3, ip ; new high-water = stack low-water +2 + STR r3, [r2] + LSR r0, #2 ; Convert return size to words + RET + +;----------------------------------------------------------------------------; + AREA |C$$code$$__rt_stkovf|, CODE, READONLY +; The code area containing __rt_stkovf_* ; + + [ :LNOT::DEF:STACK_EXTENSION + GBLL STACK_EXTENSION +STACK_EXTENSION SETL {FALSE} + ] +;----------------------------------------------------------------------------; +; C stack-limit checking support. ; +; ; +; NOTES ; +; ; +; 1/ This code is only called if stack checking is enabled with the ; +; -apcs 3/swst option on the Thumb compiler. Typically you will only do ; +; this when debugging. ; +; ; +; 2/ The code may be configured to either perform automatic stack extension ; +; or to generate an error on stack overflow. By default it is set up to ; +; generate an error. To change this to extend the stack uncomment the ; +; following line. ; +; ; +;STACK_EXTENSION SETL {TRUE} ; +; ; +; 3/ The stack extension 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. ; +; ; +; 4/ Failure of the stack-limit check is fatal. However, failure of the low- ; +; level heap allocator is passed back to its caller. ; +; ; +; 5/ This implementation of stack extension never reduces the size of the ; +; stack. It simply moves the low-water mark downwards. It is easy to do ; +; better, but, of course, it takes more code and is more target-specific. ; +;----------------------------------------------------------------------------; + +__16__rt_stkovf_split_small +; +; 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. + + [ STACK_EXTENSION + MOV ip, sp ; fall into big-frame case with size of 0. + ] + +__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 has been claimed +; but the proposed new stack pointer is in ip. + + [ STACK_EXTENSION + PUSH {r0,r1} + ADD r0, sp, #8 ; get original sp + MOV r1, ip + SUB r0, r1 ; frame size required... + LDR r1, =DefaultStackIncrement + CMP r0, r1 ; rounded up to at least + BGE %F0 + MOV r0, r1 ; the default increment +0 + MOV r1, sl + SUB r1, r0 + LDR r0, =StackSlop + SUB r1, r0 ; new stack low-water mark + MOV sl, r1 + + LDR r1, =HeapLimit + LDR r1, [r1] ; check doesn't collide with + CMP r1, sl ; the heap. + ADD sl, r0 ; restore safety margin + ; (preserves CC) + + POP {r0, r1} + BGT stackoverflow + RET ; and return if OK... + + ] +stackoverflow + SUB sp, sp, #8*4 ; Room for R8..R15 + PUSH {r0-r7} + MOV r7, lr + ADR r0, StackOverflowError + BL save_regs_and_trap + + ALIGN +StackOverflowError + DCD 3 + DCB "stack overflow", 0 + ALIGN + + +;----------------------------------------------------------------------------; + 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. ; +; ; +;----------------------------------------------------------------------------; + + 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 + + +setjmp +; +; int setjmp(jmp_buf env); +; Saves everything that might count as a register variable in 'env'. + + STMIA a1!, {r4-r7} + MOV r0, r8 + MOV r1, r9 + MOV r2, sl + MOV r3, fp + STMIA a1!, {r0-r3} + MOV r0, sp + MOV r1, lr + STMIA a1!, {r0-r1} + MOV a1, #0 ; must return 0 from a direct call + RET + +longjmp +; int longjmp(jmp_buf env, int val); + + ADD r0, #4*4 + LDMIA r0!, {r2-r7} ; Restore r8 .. lr + MOV r8, r2 + MOV r9, r3 + MOV sl, r4 + MOV fp, r5 + MOV sp, r6 + MOV lr, r7 + SUB r0, #10*4 + LDMIA r0!, {r4-r7} ; Restore v1..v4 + MOV r0, r1 + BNE %F0 + MOV r0, #1 ; Must not return 0 +0 + 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. ; +; ; +;----------------------------------------------------------------------------; + +; 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. + +__16__rt_sdiv + ASR a4, a2, #31 + EOR a2, a4 + SUB a2, a4 + + ASR a3, a1, #31 + EOR a1, a3 + SUB a1, a3 + + BEQ dividebyzero + + 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 dividebyzero + +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 dividebyzero + RET + +dividebyzero + SUB sp, sp, #8*4 ; Room for R8..R15 + PUSH {r0-r7} + MOV r7, lr + ADR r0, DivideByZeroError + BL save_regs_and_trap + + ALIGN +DivideByZeroError + DCD 1 + DCB "divide by 0", 0 + ALIGN + + END -- cgit v1.2.3