summaryrefslogtreecommitdiffstats
path: root/Bachelor/Mikroprozessorsysteme2/ARM202U/EXAMPLES/CLSTAND/RTSTAND1.S
blob: 7d85dfa70d16151e43104c0e11a8d287fb1c3a49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
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