summaryrefslogtreecommitdiffstats
path: root/c/src/exec/score/cpu/hppa1.1/cpu_asm.s
blob: b14eb7f88f5b9f710b2919014c83f3d3e1ee0fd4 (plain) (blame)
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
788
789
790
791
792
793
794
795
796
797
# 	@(#)cpu_asm.S	1.7 - 95/09/21
# 	
# 
# TODO: 
#       Context_switch needs to only save callee save registers
#       I think this means can skip:    r1, r2, r19-29, r31
#       Ref:     p 3-2 of Procedure Calling Conventions Manual
#       This should be #ifndef DEBUG so that debugger has
#       accurate visibility into all registers
#
#  This file contains the assembly code for the HPPA implementation
#  of RTEMS.
#
#  COPYRIGHT (c) 1994,95 by Division Incorporated
#
#  To anyone who acknowledges that this file is provided "AS IS"
#  without any express or implied warranty:
#      permission to use, copy, modify, and distribute this file
#      for any purpose is hereby granted without fee, provided that
#      the above copyright notice and this notice appears in all
#      copies, and that the name of Division Incorporated not be
#      used in advertising or publicity pertaining to distribution
#      of the software without specific, written prior permission.
#      Division Incorporated makes no representations about the
#      suitability of this software for any purpose.
#
#  $Id$
#

#include <rtems/core/hppa.h>
#include <rtems/core/cpu_asm.h>
#include <rtems/core/cpu.h>

#include <rtems/core/offsets.h>

	.SPACE $PRIVATE$
	.SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31
	.SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82
	.SPACE $TEXT$
	.SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44
	.SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY
	.SPACE $TEXT$
	.SUBSPA $CODE$

#
# Special register usage for context switch and interrupts
# Stay away from %cr28 which is used for TLB misses on 72000
#

isr_arg0           .reg    %cr24
isr_r9             .reg    %cr25

#
# Interrupt stack frame looks like this
#
#  offset                                   item
# -----------------------------------------------------------------
#   INTEGER_CONTEXT_OFFSET             Context_Control
#   FP_CONTEXT_OFFSET                  Context_Control_fp
#
# It is padded out to a multiple of 64
#


#  PAGE^L
#  void __Generic_ISR_Handler()
#
#  This routine provides the RTEMS interrupt management.
#
#  NOTE:
#    Upon entry, the stack will contain a stack frame back to the
#    interrupted task.  If dispatching is enabled, this is the
#    outer most interrupt, (and a context switch is necessary or
#    the current task has signals), then set up the stack to
#    transfer control to the interrupt dispatcher.
#
#
#   We jump here from the interrupt vector.
#   The hardware has done some stuff for us:
#       PSW saved in IPSW
#       PSW set to 0
#       PSW[E] set to default (0)
#       PSW[M] set to 1 iff this is HPMC
#
#       IIA queue is frozen (since PSW[Q] is now 0)
#       privilege level promoted to 0
#       IIR, ISR, IOR potentially updated if PSW[Q] was 1 at trap
#       registers GR  1,8,9,16,17,24,25 copied to shadow regs
#                 SHR 0 1 2  3  4  5  6
#
#   Our vector stub did the following
#       placed vector number is in r1
#
#        stub
#            r1 <- vector number
#            save ipsw under rock
#            ipsw = ipsw & ~1    --  disable ints
#            save qregs under rock
#            qra = _Generic_ISR_handler
#            rfi
#
################################################

#  Distinct Interrupt Entry Points
#
#  The following macro and the 32 instantiations of the macro
#  are necessary to determine which interrupt vector occurred.
#  The following macro allows a unique entry point to be defined
#  for each vector.
#
# r9 was loaded with the vector before branching here
#   scratch registers available:   gr1, gr8, gr9, gr16, gr17, gr24
#
# NOTE:
#  .align 32   doesn not seem to work in the continuation below
#  so just have to count 8 instructions
#
# NOTE:
#    this whole scheme needs to be rethought for TLB traps which
#    have requirements about what tlb faults they can incur.
#    ref:  TLB Operation Requirements in 1.1 arch book

#define THANDLER(vector) \
        mtctl   %r9,  isr_r9 !         \
        b       _Generic_ISR_Handler!        \
        ldi     vector, %r9!       \
        nop ! \
        nop ! \
        nop ! \
        nop ! \
        nop

        .align 4096
	.EXPORT IVA_Table,ENTRY,PRIV_LEV=0
IVA_Table:
	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY

        THANDLER(0)     	/* unused */

        THANDLER(HPPA_INTERRUPT_HIGH_PRIORITY_MACHINE_CHECK)

        THANDLER(HPPA_INTERRUPT_POWER_FAIL)

        THANDLER(HPPA_INTERRUPT_RECOVERY_COUNTER)

        THANDLER(HPPA_INTERRUPT_EXTERNAL_INTERRUPT)

        THANDLER(HPPA_INTERRUPT_LOW_PRIORITY_MACHINE_CHECK)

        THANDLER(HPPA_INTERRUPT_INSTRUCTION_TLB_MISS)

        THANDLER(HPPA_INTERRUPT_INSTRUCTION_MEMORY_PROTECTION)

        THANDLER(HPPA_INTERRUPT_ILLEGAL_INSTRUCTION)

        THANDLER(HPPA_INTERRUPT_BREAK_INSTRUCTION)

        THANDLER(HPPA_INTERRUPT_PRIVILEGED_OPERATION)

        THANDLER(HPPA_INTERRUPT_PRIVILEGED_REGISTER)

        THANDLER(HPPA_INTERRUPT_OVERFLOW)

        THANDLER(HPPA_INTERRUPT_CONDITIONAL)

        THANDLER(HPPA_INTERRUPT_ASSIST_EXCEPTION)

        THANDLER(HPPA_INTERRUPT_DATA_TLB_MISS)

        THANDLER(HPPA_INTERRUPT_NON_ACCESS_INSTRUCTION_TLB_MISS)

        THANDLER(HPPA_INTERRUPT_NON_ACCESS_DATA_TLB_MISS)

        THANDLER(HPPA_INTERRUPT_DATA_MEMORY_PROTECTION)

        THANDLER(HPPA_INTERRUPT_DATA_MEMORY_BREAK)

        THANDLER(HPPA_INTERRUPT_TLB_DIRTY_BIT)

        THANDLER(HPPA_INTERRUPT_PAGE_REFERENCE)

        THANDLER(HPPA_INTERRUPT_ASSIST_EMULATION)

        THANDLER(HPPA_INTERRUPT_HIGHER_PRIVILEGE_TRANSFER)

        THANDLER(HPPA_INTERRUPT_LOWER_PRIVILEGE_TRANSFER)

        THANDLER(HPPA_INTERRUPT_TAKEN_BRANCH)

        THANDLER(HPPA_INTERRUPT_DATA_MEMORY_ACCESS_RIGHTS)

        THANDLER(HPPA_INTERRUPT_DATA_MEMORY_PROTECTION_ID)

        THANDLER(HPPA_INTERRUPT_UNALIGNED_DATA_REFERENCE)

        THANDLER(HPPA_INTERRUPT_PERFORMANCE_MONITOR)

        THANDLER(HPPA_INTERRUPT_INSTRUCTION_DEBUG)

        THANDLER(HPPA_INTERRUPT_DATA_DEBUG)

        .EXIT
        .PROCEND

	.EXPORT _Generic_ISR_Handler,ENTRY,PRIV_LEV=0
_Generic_ISR_Handler:
	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY

# Turn on the D bit in psw so we can start saving stuff on stack
# (interrupt context pieces that need to be saved before the RFI)

        ssm       HPPA_PSW_D, %r0
        mtctl     arg0, isr_arg0

# save interrupt state
        mfctl     ipsw, arg0
        stw       arg0, IPSW_OFFSET(sp)

        mfctl     iir, arg0
        stw       arg0, IIR_OFFSET(sp)

        mfctl     ior, arg0
        stw       arg0, IOR_OFFSET(sp)

        mfctl     pcoq, arg0
        stw       arg0, PCOQFRONT_OFFSET(sp)

	mtctl	  %r0, pcoq
        mfctl     pcoq, arg0
        stw       arg0, PCOQBACK_OFFSET(sp)

        mfctl     %sar, arg0
        stw       arg0, SAR_OFFSET(sp)

# Prepare to re-enter virtual mode
# We need Q in case the interrupt handler enables interrupts
#

        ldil      L%CPU_PSW_DEFAULT, arg0
        ldo       R%CPU_PSW_DEFAULT(arg0), arg0
        mtctl     arg0, ipsw

# Now jump to "rest_of_isr_handler" with the rfi
# We are assuming the space queues are all correct already

	ldil 	  L%rest_of_isr_handler, arg0
	ldo	  R%rest_of_isr_handler(arg0), arg0
	mtctl	  arg0, pcoq
	ldo	  4(arg0), arg0
	mtctl	  arg0, pcoq

        rfi
	nop

# At this point we are back in virtual mode and all our
#  normal addressing is once again ok.

rest_of_isr_handler:

#
# Build an interrupt frame to hold the contexts we will need.
# We have already saved the interrupt items on the stack

# At this point the following registers are damaged wrt the interrupt
#  reg    current value        saved value
# ------------------------------------------------
#  arg0   scratch               isr_arg0  (ctl)
#  r9     vector number         isr_r9    (ctl)
#
# Point to beginning of integer context and
# save the integer context
        stw         %r1,R1_OFFSET(sp)
        stw         %r2,R2_OFFSET(sp)
        stw         %r3,R3_OFFSET(sp)
        stw         %r4,R4_OFFSET(sp)
        stw         %r5,R5_OFFSET(sp)
        stw         %r6,R6_OFFSET(sp)
        stw         %r7,R7_OFFSET(sp)
        stw         %r8,R8_OFFSET(sp)
        stw         %r9,R9_OFFSET(sp)
        stw         %r10,R10_OFFSET(sp)
        stw         %r11,R11_OFFSET(sp)
        stw         %r12,R12_OFFSET(sp)
        stw         %r13,R13_OFFSET(sp)
        stw         %r14,R14_OFFSET(sp)
        stw         %r15,R15_OFFSET(sp)
        stw         %r16,R16_OFFSET(sp)
        stw         %r17,R17_OFFSET(sp)
        stw         %r18,R18_OFFSET(sp)
        stw         %r19,R19_OFFSET(sp)
        stw         %r20,R20_OFFSET(sp)
        stw         %r21,R21_OFFSET(sp)
        stw         %r22,R22_OFFSET(sp)
        stw         %r23,R23_OFFSET(sp)
        stw         %r24,R24_OFFSET(sp)
        stw         %r25,R25_OFFSET(sp)
        stw         %r26,R26_OFFSET(sp)
        stw         %r27,R27_OFFSET(sp)
        stw         %r28,R28_OFFSET(sp)
        stw         %r29,R29_OFFSET(sp)
        stw         %r30,R30_OFFSET(sp)
        stw         %r31,R31_OFFSET(sp)

# Now most registers are available since they have been saved
#
# The following items are currently wrong in the integer context
#  reg    current value        saved value
# ------------------------------------------------
#  arg0   scratch               isr_arg0  (ctl)
#  r9     vector number         isr_r9    (ctl)
#
# Fix them

         mfctl      isr_arg0,%r3
         stw        %r3,ARG0_OFFSET(sp)

         mfctl      isr_r9,%r3
         stw        %r3,R9_OFFSET(sp)

#
# At this point we are done with isr_arg0, and isr_r9 control registers
#


# Point to beginning of float context and
# save the floating point context -- doing whatever patches are necessary
        .call ARGW0=GR
        bl          _CPU_Save_float_context,%r2
        ldo         FP_CONTEXT_OFFSET(sp),arg0

# save the ptr to interrupt frame as an argument for the interrupt handler
        copy        sp, arg1

# Advance the frame to point beyond all interrupt contexts (integer & float)
# this also includes the pad to align to 64byte stack boundary
        ldo         CPU_INTERRUPT_FRAME_SIZE(sp), sp

#    r3  -- &_ISR_Nest_level
#    r5  -- value _ISR_Nest_level
#    r4  -- &_Thread_Dispatch_disable_level
#    r6  -- value _Thread_Dispatch_disable_level
#    r9  -- vector number

 	.import   _ISR_Nest_level,data
	ldil	  L%_ISR_Nest_level,%r3
	ldo	  R%_ISR_Nest_level(%r3),%r3
	ldw	  0(%r3),%r5

	.import   _Thread_Dispatch_disable_level,data
	ldil	  L%_Thread_Dispatch_disable_level,%r4
	ldo	  R%_Thread_Dispatch_disable_level(%r4),%r4
	ldw	  0(%r4),%r6

# increment interrupt nest level counter.  If outermost interrupt
# switch the stack and squirrel away the previous sp.
        addi      1,%r5,%r5
        stw       %r5, 0(%r3)

# compute and save new stack (with frame)
# just in case we are nested -- simpler this way
        comibf,=  1,%r5,stack_done
        ldo       128(sp),%r7

#
# Switch to interrupt stack allocated by the interrupt manager (intr.c)
#
        .import   _CPU_Interrupt_stack_low,data
	ldil	  L%_CPU_Interrupt_stack_low,%r7
	ldw	  R%_CPU_Interrupt_stack_low(%r7),%r7
        ldo       128(%r7),%r7

stack_done:
# save our current stack pointer where the "old sp" is supposed to be
        stw       sp, -4(%r7)
# and switch stacks (or advance old stack in nested case)
        copy      %r7, sp

# increment the dispatch disable level counter.
        addi      1,%r6,%r6
        stw       %r6, 0(%r4)

# load address of user handler
        .import   _ISR_Vector_table,data
	ldil	  L%_ISR_Vector_table,%r8
	ldo	  R%_ISR_Vector_table(%r8),%r8
        ldwx,s    %r9(%r8),%r8

# invoke user interrupt handler
# Interrupts are currently disabled, as per RTEMS convention
# The handler has the option of re-enabling interrupts
# NOTE:  can not use 'bl' since it uses "pc-relative" addressing
#    and we are using a hard coded address from a table
#  So... we fudge r2 ourselves (ala dynacall)
#
        copy      %r9, %r26
        .call  ARGW0=GR, ARGW1=GR
        blr       %r0, rp
        bv,n      0(%r8)

post_user_interrupt_handler:

# Back from user handler(s)
# Disable external interrupts (since the interrupt handler could
# have turned them on) and return to the interrupted task stack (assuming
# (_ISR_Nest_level == 0)

        rsm        HPPA_PSW_I, %r0
        ldw        -4(sp), sp

#    r3  -- &_ISR_Nest_level
#    r5  -- value _ISR_Nest_level
#    r4  -- &_Thread_Dispatch_disable_level
#    r6  -- value _Thread_Dispatch_disable_level

 	.import   _ISR_Nest_level,data
	ldil	  L%_ISR_Nest_level,%r3
	ldo	  R%_ISR_Nest_level(%r3),%r3
	ldw	  0(%r3),%r5

	.import   _Thread_Dispatch_disable_level,data
	ldil	  L%_Thread_Dispatch_disable_level,%r4
	ldo	  R%_Thread_Dispatch_disable_level(%r4),%r4
	ldw	  0(%r4), %r6

# decrement isr nest level
	addi      -1, %r5, %r5
        stw       %r5, 0(%r3)

# decrement dispatch disable level counter and, if not 0, go on
        addi       -1,%r6,%r6
        comibf,=   0,%r6,isr_restore
        stw        %r6, 0(%r4)

# check whether or not a context switch is necessary
	.import    _Context_Switch_necessary,data
	ldil	   L%_Context_Switch_necessary,%r8
	ldw	   R%_Context_Switch_necessary(%r8),%r8
	comibf,=,n 0,%r8,ISR_dispatch

# check whether or not a context switch is necessary because an ISR
#    sent signals to the interrupted task
	.import    _ISR_Signals_to_thread_executing,data
	ldil	   L%_ISR_Signals_to_thread_executing,%r8
	ldw	   R%_ISR_Signals_to_thread_executing(%r8),%r8
	comibt,=,n 0,%r8,isr_restore

# OK, something happened while in ISR and we need to switch to a task
# other than the one which was interrupted or the
#    ISR_Signals_to_thread_executing case
# We also turn on interrupts, since the interrupted task had them
#   on (obviously :-) and Thread_Dispatch is happy to leave ints on.
#

ISR_dispatch:
        ssm        HPPA_PSW_I, %r0

        .import    _Thread_Dispatch,code
        .call
        bl         _Thread_Dispatch,%r2
        ldo        128(sp),sp

        ldo        -128(sp),sp

        rsm        HPPA_PSW_I, %r0

isr_restore:

# Get a pointer to beginning of our stack frame
        ldo        -CPU_INTERRUPT_FRAME_SIZE(sp), %arg1

# restore float
        .call ARGW0=GR
        bl         _CPU_Restore_float_context,%r2
        ldo        FP_CONTEXT_OFFSET(%arg1), arg0

        copy       %arg1, %arg0

#   ********** FALL THRU **********

# Jump here from bottom of Context_Switch
# Also called directly by _CPU_Context_Restart_self via _Thread_Restart_self
# restore interrupt state
#

	.EXPORT _CPU_Context_restore
_CPU_Context_restore:

# Turn off Q & I so we can write pcoq
        rsm        HPPA_PSW_Q + HPPA_PSW_I, %r0

        ldw        IPSW_OFFSET(arg0), %r8
        mtctl      %r8, ipsw

        ldw        SAR_OFFSET(arg0), %r9
        mtctl      %r9, sar

        ldw        PCOQFRONT_OFFSET(arg0), %r10
        mtctl      %r10, pcoq

        ldw        PCOQBACK_OFFSET(arg0), %r11
        mtctl      %r11, pcoq

#
# restore integer state
#
        ldw         R1_OFFSET(arg0),%r1
        ldw         R2_OFFSET(arg0),%r2
        ldw         R3_OFFSET(arg0),%r3
        ldw         R4_OFFSET(arg0),%r4
        ldw         R5_OFFSET(arg0),%r5
        ldw         R6_OFFSET(arg0),%r6
        ldw         R7_OFFSET(arg0),%r7
        ldw         R8_OFFSET(arg0),%r8
        ldw         R9_OFFSET(arg0),%r9
        ldw         R10_OFFSET(arg0),%r10
        ldw         R11_OFFSET(arg0),%r11
        ldw         R12_OFFSET(arg0),%r12
        ldw         R13_OFFSET(arg0),%r13
        ldw         R14_OFFSET(arg0),%r14
        ldw         R15_OFFSET(arg0),%r15
        ldw         R16_OFFSET(arg0),%r16
        ldw         R17_OFFSET(arg0),%r17
        ldw         R18_OFFSET(arg0),%r18
        ldw         R19_OFFSET(arg0),%r19
        ldw         R20_OFFSET(arg0),%r20
        ldw         R21_OFFSET(arg0),%r21
        ldw         R22_OFFSET(arg0),%r22
        ldw         R23_OFFSET(arg0),%r23
        ldw         R24_OFFSET(arg0),%r24
        ldw         R25_OFFSET(arg0),%r25
# skipping r26 (aka arg0) until we are done with it
        ldw         R27_OFFSET(arg0),%r27
        ldw         R28_OFFSET(arg0),%r28
        ldw         R29_OFFSET(arg0),%r29
        ldw         R30_OFFSET(arg0),%r30
        ldw         R31_OFFSET(arg0),%r31

# Must load r26 last since it is arg0
        ldw         R26_OFFSET(arg0),%r26

isr_exit:
        rfi
        .EXIT
        .PROCEND

#
#  This section is used to context switch floating point registers.
#  Ref:  6-35 of Architecture 1.1
#
#  NOTE:    since integer multiply uses the floating point unit,
#           we have to save/restore fp on every trap.  We cannot
#           just try to keep track of fp usage.

	.align 32
	.EXPORT _CPU_Save_float_context,ENTRY,PRIV_LEV=0
_CPU_Save_float_context:
	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY
        fstds,ma    %fr0,8(%arg0)
        fstds,ma    %fr1,8(%arg0)
        fstds,ma    %fr2,8(%arg0)
        fstds,ma    %fr3,8(%arg0)
        fstds,ma    %fr4,8(%arg0)
        fstds,ma    %fr5,8(%arg0)
        fstds,ma    %fr6,8(%arg0)
        fstds,ma    %fr7,8(%arg0)
        fstds,ma    %fr8,8(%arg0)
        fstds,ma    %fr9,8(%arg0)
        fstds,ma    %fr10,8(%arg0)
        fstds,ma    %fr11,8(%arg0)
        fstds,ma    %fr12,8(%arg0)
        fstds,ma    %fr13,8(%arg0)
        fstds,ma    %fr14,8(%arg0)
        fstds,ma    %fr15,8(%arg0)
        fstds,ma    %fr16,8(%arg0)
        fstds,ma    %fr17,8(%arg0)
        fstds,ma    %fr18,8(%arg0)
        fstds,ma    %fr19,8(%arg0)
        fstds,ma    %fr20,8(%arg0)
        fstds,ma    %fr21,8(%arg0)
        fstds,ma    %fr22,8(%arg0)
        fstds,ma    %fr23,8(%arg0)
        fstds,ma    %fr24,8(%arg0)
        fstds,ma    %fr25,8(%arg0)
        fstds,ma    %fr26,8(%arg0)
        fstds,ma    %fr27,8(%arg0)
        fstds,ma    %fr28,8(%arg0)
        fstds,ma    %fr29,8(%arg0)
        fstds,ma    %fr30,8(%arg0)
        fstds       %fr31,0(%arg0)
        bv          0(%r2)
        addi        -(31*8), %arg0, %arg0        ; restore arg0 just for fun
	.EXIT
	.PROCEND

	.align 32
	.EXPORT _CPU_Restore_float_context,ENTRY,PRIV_LEV=0
_CPU_Restore_float_context:
	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY
        addi        (31*8), %arg0, %arg0         ; point at last double
        fldds       0(%arg0),%fr31
        fldds,mb    -8(%arg0),%fr30
        fldds,mb    -8(%arg0),%fr29
        fldds,mb    -8(%arg0),%fr28
        fldds,mb    -8(%arg0),%fr27
        fldds,mb    -8(%arg0),%fr26
        fldds,mb    -8(%arg0),%fr25
        fldds,mb    -8(%arg0),%fr24
        fldds,mb    -8(%arg0),%fr23
        fldds,mb    -8(%arg0),%fr22
        fldds,mb    -8(%arg0),%fr21
        fldds,mb    -8(%arg0),%fr20
        fldds,mb    -8(%arg0),%fr19
        fldds,mb    -8(%arg0),%fr18
        fldds,mb    -8(%arg0),%fr17
        fldds,mb    -8(%arg0),%fr16
        fldds,mb    -8(%arg0),%fr15
        fldds,mb    -8(%arg0),%fr14
        fldds,mb    -8(%arg0),%fr13
        fldds,mb    -8(%arg0),%fr12
        fldds,mb    -8(%arg0),%fr11
        fldds,mb    -8(%arg0),%fr10
        fldds,mb    -8(%arg0),%fr9
        fldds,mb    -8(%arg0),%fr8
        fldds,mb    -8(%arg0),%fr7
        fldds,mb    -8(%arg0),%fr6
        fldds,mb    -8(%arg0),%fr5
        fldds,mb    -8(%arg0),%fr4
        fldds,mb    -8(%arg0),%fr3
        fldds,mb    -8(%arg0),%fr2
        fldds,mb    -8(%arg0),%fr1
        bv          0(%r2)
        fldds,mb    -8(%arg0),%fr0
	.EXIT
	.PROCEND

#
# These 2 small routines are unused right now.
# Normally we just go thru _CPU_Save_float_context (and Restore)
#
# Here we just deref the ptr and jump up, letting _CPU_Save_float_context
#  do the return for us.
#
	.EXPORT _CPU_Context_save_fp,ENTRY,PRIV_LEV=0
_CPU_Context_save_fp:
	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY
        bl          _CPU_Save_float_context, %r0
        ldw         0(%arg0), %arg0
	.EXIT
	.PROCEND

	.EXPORT _CPU_Context_restore_fp,ENTRY,PRIV_LEV=0
_CPU_Context_restore_fp:
	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY
        bl          _CPU_Restore_float_context, %r0
        ldw         0(%arg0), %arg0
	.EXIT
	.PROCEND


#  void _CPU_Context_switch( run_context, heir_context )
#
#  This routine performs a normal non-FP context switch.
#

	.align 32
	.EXPORT _CPU_Context_switch,ENTRY,PRIV_LEV=0,ARGW0=GR,ARGW1=GR
_CPU_Context_switch:
    	.PROC
	.CALLINFO FRAME=64
	.ENTRY

# Save the integer context
        stw         %r1,R1_OFFSET(arg0)
        stw         %r2,R2_OFFSET(arg0)
        stw         %r3,R3_OFFSET(arg0)
        stw         %r4,R4_OFFSET(arg0)
        stw         %r5,R5_OFFSET(arg0)
        stw         %r6,R6_OFFSET(arg0)
        stw         %r7,R7_OFFSET(arg0)
        stw         %r8,R8_OFFSET(arg0)
        stw         %r9,R9_OFFSET(arg0)
        stw         %r10,R10_OFFSET(arg0)
        stw         %r11,R11_OFFSET(arg0)
        stw         %r12,R12_OFFSET(arg0)
        stw         %r13,R13_OFFSET(arg0)
        stw         %r14,R14_OFFSET(arg0)
        stw         %r15,R15_OFFSET(arg0)
        stw         %r16,R16_OFFSET(arg0)
        stw         %r17,R17_OFFSET(arg0)
        stw         %r18,R18_OFFSET(arg0)
        stw         %r19,R19_OFFSET(arg0)
        stw         %r20,R20_OFFSET(arg0)
        stw         %r21,R21_OFFSET(arg0)
        stw         %r22,R22_OFFSET(arg0)
        stw         %r23,R23_OFFSET(arg0)
        stw         %r24,R24_OFFSET(arg0)
        stw         %r25,R25_OFFSET(arg0)
        stw         %r26,R26_OFFSET(arg0)
        stw         %r27,R27_OFFSET(arg0)
        stw         %r28,R28_OFFSET(arg0)
        stw         %r29,R29_OFFSET(arg0)
        stw         %r30,R30_OFFSET(arg0)
        stw         %r31,R31_OFFSET(arg0)

# fill in interrupt context section
        stw         %r2, PCOQFRONT_OFFSET(%arg0)
        ldo         4(%r2), %r2
        stw         %r2, PCOQBACK_OFFSET(%arg0)

# Generate a suitable IPSW by using the system default psw
#  with the current low bits added in.

        ldil        L%CPU_PSW_DEFAULT, %r2
        ldo         R%CPU_PSW_DEFAULT(%r2), %r2
        ssm         0, %arg2
        dep         %arg2, 31, 8, %r2
        stw         %r2, IPSW_OFFSET(%arg0)

# at this point, the running task context is completely saved
# Now jump to the bottom of the interrupt handler to load the
# heirs context

        b           _CPU_Context_restore
        copy        %arg1, %arg0

        .EXIT
        .PROCEND


/*
 * Find first bit
 * NOTE:
 *   This is used (and written) only for the ready chain code and
 *   priority bit maps.
 *   Any other use constitutes fraud.
 *   Returns first bit from the least significant side.
 *   Eg:  if input is 0x8001
 *        output will indicate the '1' bit and return 0.
 *   This is counter to HPPA bit numbering which calls this
 *   bit 31.  This way simplifies the macros _CPU_Priority_Mask
 *   and _CPU_Priority_Bits_index.
 *
 *   NOTE:
 *       We just use 16 bit version
 *       does not handle zero case
 *
 *  Based on the UTAH Mach libc version of ffs.
 */

        .align 32
	.EXPORT hppa_rtems_ffs,ENTRY,PRIV_LEV=0,ARGW0=GR
hppa_rtems_ffs:
    	.PROC
	.CALLINFO FRAME=0,NO_CALLS
	.ENTRY

#ifdef RETURN_ERROR_ON_ZERO
	comb,=	%arg0,%r0,ffsdone	; If arg0 is 0
	ldi	-1,%ret0		;   return -1
#endif

#if BITFIELD_SIZE == 32
	ldi	31,%ret0		; Set return to high bit
	extru,=	%arg0,31,16,%r0		; If low 16 bits are non-zero
	addi,tr	-16,%ret0,%ret0		;   subtract 16 from bitpos
	shd	%r0,%arg0,16,%arg0	; else shift right 16 bits
#else
	ldi	15,%ret0		; Set return to high bit
#endif
	extru,=	%arg0,31,8,%r0		; If low 8 bits are non-zero
	addi,tr	-8,%ret0,%ret0		;   subtract 8 from bitpos
	shd	%r0,%arg0,8,%arg0	; else shift right 8 bits
	extru,=	%arg0,31,4,%r0		; If low 4 bits are non-zero
	addi,tr	-4,%ret0,%ret0		;   subtract 4 from bitpos
	shd	%r0,%arg0,4,%arg0	; else shift right 4 bits
	extru,=	%arg0,31,2,%r0		; If low 2 bits are non-zero
	addi,tr	-2,%ret0,%ret0		;   subtract 2 from bitpos
	shd	%r0,%arg0,2,%arg0	; else shift right 2 bits
	extru,=	%arg0,31,1,%r0		; If low bit is non-zero
	addi	-1,%ret0,%ret0		;   subtract 1 from bitpos
ffsdone:
        bv,n    0(%r2)
        nop
        .EXIT
        .PROCEND