File: emulation.S

package info (click to toggle)
mol 0.9.61-6
  • links: PTS
  • area: contrib
  • in suites: woody
  • size: 6,140 kB
  • ctags: 8,491
  • sloc: ansic: 50,560; asm: 2,826; sh: 458; makefile: 373; perl: 165; lex: 135; yacc: 131
file content (759 lines) | stat: -rw-r--r-- 18,886 bytes parent folder | download
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
/**************************************************************
*   
*   Creation Date: <97/07/26 18:23:02 samuel>
*   Time-stamp: <2001/06/24 14:42:41 samuel>
*   
*       <emulation.S>
*       
*       Low-level emulation of some privileged instructions
*
*	
*   Copyright (C) 1998-01 Samuel Rydh
*
*   This program is free software; you can redistribute it and/or
*   modify it under the terms of the GNU General Public License
*   as published by the Free Software Foundation;
*
**************************************************************/


.macro INC_NIP scr
	mfsrr0	\scr
	addi	\scr,\scr,4
	mtsrr0	\scr
.endm

// Why not use sprg0 and sprg1 as scratch registers?
// This is safe as long as interrupts are off.
// XXX: Stack is faster than sprgN

.macro MT_SCRATCH_1 reg ; mtsprg1 \reg ; .endm
.macro MT_SCRATCH_0 reg ; mtsprg0 \reg ; .endm
.macro MF_SCRATCH_1 reg ; mfsprg1 \reg ; .endm
.macro MF_SCRATCH_0 reg ; mfsprg0 \reg ; .endm

/************************************************************************/
/*	Get Instruction							*/
/************************************************************************/

	//////////////////////////////////////////////////////////////
	//
	// _get_instr_opcode
	//	r4	nip
	// ret:	r4	opcode
	//
	// Modifies: r0,r2,r3 (does *NOT* touch cr)
	// NOTE: Might return from the exception!
	
_get_instr_opcode:
	mflr	r0			// save lr
	bl	secint_dsi_get_instr	// set lr to secondary exception handler
	
	// If a missing PTE interrupt occurs, we will end up in secint_dsi
	mfmsr	r3			// r3 = exception MSR
	ori	r2,r3,MSR_DR
	mtmsr	r2
	isync

	lwz     r4,0(r4)		// r4 = saved NIP (if we take an exception)
	mtlr	r0			// restore lr
	
	mtmsr	r3			// restore exception MSR
	isync
	blr

							
	////////////////////////////////////////////////////////
	// secint_dsi_get_instr (exception in get_instr_opcode)
	//	r3	vector_addr (old r3 is saved in sprg0)
	//	r4	mac-nip
	//
	// That is, all registers except r3 are unmodified since
	// the exception occured. 
	// 
	// This function flushes the PTE from both the ITLB and 
	// the DTLB in order to get a ISI exception that will
	// insert a PTE in the hash table.
	//
	// Safe to modify: r0,r2-r5, lr

secint_dsi_get_instr:
	blrl
	// We _know_ this must be a DSI

	mtsrr0	r4			// restore NIP
	lwz	r2,x_MSR(r1)		// restore MSR
	tlbie	r4			// flush PTE from ITLB
	mtsrr1	r2
	b	exception_return
	

/************************************************************************/
/*	Emulate Instruction						*/
/************************************************************************/
	
	//////////////////////////////////////////////////////////////
	// emulate_instr
	//	r1		stack (mregs)
	//	srr0		nip
	//	srr1		reason bits
	//	cr5-7		flag_bits
	//
	// Saved r0-r5 (NOT SAVED: ctr, xer)

emulate_instr:
	mfsrr1	r5
	mtcrf	0xf0,r5			// put flag bits into cr0-cr3
	mfsrr0	r4
	
	bt	14,mac_program_trap	// r5=srr1
	bl	_get_instr_opcode	// r4=nip, r2-r3, r4=ret opcode (cr not touched)
	bt	13,emulate_priv_instr
	bt+	12,emulate_illegal_instr
	bt+	11,mac_program_trap	// bit11 = fpu_exception

	// We should not come here
	
	// r4 = opcode
	// r5 = srr1 bits
	MAC_EXIT_SAVE( RVEC_UNUSUAL_PROGRAM_EXCEP )

	// r4 = opcode
unhandled_priv_inst:
	MAC_EXIT_SAVE( RVEC_PRIV_INST )

	// r4 = opcode
	// r5 = srr1 bits
emulate_illegal_instr:
	MAC_EXIT_SAVE( RVEC_ILLEGAL_INST )

	
	/////////////////////////////////////////////////////////////
	// mac_trap
	//
	//	r5	srr1 bits
	//	r2	exception vector
	//
	// r0,r2-r5 may be modified

	// Trap instructions or user-mode privileged/illegal instructions
mac_program_trap:
	li	r2,0x700
	// fall through...
mac_trap:
	mfsrr0	r4
	lwz	r3,xMSR(r1)
	stw	r4,xSRR0(r1)		// mac-srr0 = nip
	mr	r4,r3
	rlwimi	r4,r5,0,0,15
	rlwimi	r4,r3,0,6,6		// copy MSR_VEC bit of xMSR to SRR1
	stw	r4,xSRR1(r1)		// srr1 = (msr & (0xffff|MSR_VEC)) | (srr1 & ~(0xffff|MSR_VEC))

	rlwinm.	r4,r3,0,25,25		// copy and test MSR_IP
	rlwimi	r4,r3,0,19,19		// copy MSR_ME
	stw	r4,xMSR(r1)
	beq+	1f
	oris	r2,r2,0xfff0		// NIP = 0xfff00700
1:	mtsrr0	r2
	bl	_msr_altered		// modifies r0,r2-r5 (srr1 is updated)
	b	emulation_done_noinc_chint

	
	/////////////////////////////////////////////////////////////
	// emulation_done_xxx
	//
	// The emulation of the instruction was successful.

emulation_done_noinc_chint:		// check interrupt
	lwz	r3,xINTERRUPT(r1)	// This check is only necessary if we
	cmpwi	r3,0			// set xINTERRUPT ourselves
	beq+	emulation_done_noinc
	MAC_EXIT_SAVE( RVEC_INTERRUPT )

emulation_done:
	INC_NIP /**/ r4
emulation_done_noinc:
	lwz	r4,x_MSR(r1)		// Singlestepping fix
	andi.	r4,r4,MSR_SE
	beq+	exception_return
	MAC_EXIT_SAVE( RVEC_TRACE_TRAP )

emulation_done_notrace:			// Don't trace rfi
	lwz	r3,xINTERRUPT(r1)
	cmpwi	r3,0
	beq+	exception_return
	MAC_EXIT_SAVE( RVEC_INTERRUPT )

/************************************************************************/
/*	Emulate Privileged Instruction					*/
/************************************************************************/

#define OPCODE(x,y)     ((x<<10)+y)
	
	/////////////////////////////////////////////////////////////
	// emulate_priv_instr
	//	r4	opcode
	//	r5	srr1
	//	srr0	nip
	//
	// r0,r2-r5,lr free

emulate_priv_instr:
	lwz	r3,xMSR(r1)
	andi.	r3,r3,MSR_PR		// only emulate in supervisor mode
	bne-	mac_program_trap	// r5 = srr1
	
	rlwinm	r2,r4,32-1,22,31
	rlwimi	r2,r4,16,16,21		// r2 = opcode
	rlwinm	r5,r4,14,24,28		// r5 = reg_num << 3 (instr. bits 6-10)

	// The instructions should be sorted in "most probable first"
	// The performance measurement was done on an 350 Mhz, B&W G3 machine (DEC turned off)
	// Results in [] are from the old implementation (<0.9.55)
	
	cmpwi	cr1,r2,OPCODE(31,339)		// mfspr, <3.0 MHz> (2.3 MHz)
	beq-	cr1,emulate_mfspr

	cmpwi	cr2,r2,OPCODE(31,467)		// mtspr <2.9 MHz> (<2.3 MHz)
	beq-	cr2,emulate_mtspr

	cmpwi	cr3,r2,OPCODE(31,83)		// mfmsr <3.0 MHz> (2.3 MHz) [1.9 MHz]
	beq-	cr3,emulate_mfmsr

	cmpwi	cr4,r2,OPCODE(31,146)		// mtmsr <2.0 MHz> (1.8 MHz) [1.57 MHz]
	beq-	cr4,emulate_mtmsr

	cmpwi	cr1,r2,OPCODE(19,50)		// rfi <2.2 MHz> (1.8 MHz)
	beq-	cr1,emulate_rfi

	cmpwi	cr2,r2,OPCODE(31,595)		// mfsr
	beq-	cr2,emulate_mfsr

	cmpwi	cr3,r2,OPCODE(31,659)		// mfsrin
	beq-	cr3,emulate_mfsrin
	
	cmpwi	cr4,r2,OPCODE(31,210)		// mtsr
	beq-	cr4,emulate_mtsr
	
	cmpwi	cr1,r2,OPCODE(31,242)		// mtsrin
	beq-	cr1,emulate_mtsrin
	
	cmpwi	cr2,r2,OPCODE(31,306)		// tlbie (0.48 Mhz) [0.53] (without tlbie code)
	beq-	cr2,emulate_tlbie		//       (0.41 Mhz) (with empty table)
	
	cmpwi	cr3,r2,OPCODE(31,566)		// tlbsync (2.3 Mhz)
	beq-	cr3,emulate_tlbsync
	
	cmpwi	cr4,r2,OPCODE(31,467)		// dcbi <0.67*> (0.8 MHz) [0.3/0.34 Mhz] (no emulator code)
	beq-	cr4,emulate_dcbi

	// Program-trap, illegal instruction (0.47 MHz) [0.25 Mhz]
	
	b	unhandled_priv_inst		// r4 = opcode


/************************************************************************/
/*	Mac-Register access						*/
/************************************************************************/
			
.macro	EMU_LOAD_GPR reg, scr
	LI_PHYS( \scr, load_gpr_table )
	add	\scr,\reg,\scr
	mtlr	\scr
	blrl
.endm

.macro	EMU_STORE_GPR reg, scr
	LI_PHYS( \scr, store_gpr_table )
	add	\scr,\reg,\scr
	mtlr	\scr
	blrl
.endm

get_gpr_table_addr:
	blr
store_gpr_table:
	stw	r0,xGPR0(r1)	;  blr	/* 0 */
	stw	r0,xGPR1(r1)	;  blr
	stw	r0,xGPR2(r1)	;  blr
	stw	r0,xGPR3(r1)	;  blr
	stw	r0,xGPR4(r1)	;  blr
	stw	r0,xGPR5(r1)	;  blr	/* 5 */
	mr	r6,r0		;  blr
	mr	r7,r0		;  blr
	mr	r8,r0		;  blr
	mr	r9,r0		;  blr
	mr	r10,r0		;  blr	/* 10 */
	mr	r11,r0		;  blr
	mr	r12,r0		;  blr
	mr	r13,r0		;  blr
	mr	r14,r0		;  blr
	mr	r15,r0		;  blr	/* 15 */
	mr	r16,r0		;  blr
	mr	r17,r0		;  blr
	mr	r18,r0		;  blr
	mr	r19,r0		;  blr
	mr	r20,r0		;  blr	/* 20 */
	mr	r21,r0		;  blr
	mr	r22,r0		;  blr
	mr	r23,r0		;  blr	
	mr	r24,r0		;  blr
	mr	r25,r0		;  blr	/* 25 */
	mr	r26,r0		;  blr
	mr	r27,r0		;  blr
	mr	r28,r0		;  blr
	mr	r29,r0		;  blr
	mr	r30,r0		;  blr	/* 30 */
	mr	r31,r0		;  blr

load_gpr_table:
	lwz	r0,xGPR0(r1)	;  blr	/* 0 */
	lwz	r0,xGPR1(r1)	;  blr
	lwz	r0,xGPR2(r1)	;  blr
	lwz	r0,xGPR3(r1)	;  blr
	lwz	r0,xGPR4(r1)	;  blr
	lwz	r0,xGPR5(r1)	;  blr	/* 5 */
	mr	r0,r6		;  blr
	mr	r0,r7		;  blr
	mr	r0,r8		;  blr
	mr	r0,r9		;  blr
	mr	r0,r10		;  blr	/* 10 */
	mr	r0,r11		;  blr
	mr	r0,r12		;  blr
	mr	r0,r13		;  blr
	mr	r0,r14		;  blr
	mr	r0,r15		;  blr	/* 15 */
	mr	r0,r16		;  blr
	mr	r0,r17		;  blr
	mr	r0,r18		;  blr
	mr	r0,r19		;  blr
	mr	r0,r20		;  blr	/* 20 */
	mr	r0,r21		;  blr
	mr	r0,r22		;  blr
	mr	r0,r23		;  blr	
	mr	r0,r24		;  blr
	mr	r0,r25		;  blr	/* 25 */
	mr	r0,r26		;  blr
	mr	r0,r27		;  blr
	mr	r0,r28		;  blr
	mr	r0,r29		;  blr
	mr	r0,r30		;  blr	/* 30 */
	mr	r0,r31		;  blr


/************************************************************************/
/*	Instruction Emulation						*/
/************************************************************************/

	//////////////////////////////////////////////////////////
	// emulate_xxxxx
	//	r4	opcode (uninteresting)
	//	r5	regnum<<3 (from opcode bits 6-10)
	//
	// May modify: r0,r2-r5 (lr)

emulate_mfmsr:
	lwz	r0,xMSR(r1)
	EMU_STORE_GPR r5, /**/ r2
	b	emulation_done

emulate_mfspr:
	rlwinm	r2,r4,32-14,25,29
	rlwimi	r2,r4,32-4,20,24		// r2 = spr# <<2
	addi	r3,r1,xSPR_BASE
	lwzx	r0,r2,r3			// value in r0
	addi	r3,r1,K_SPR_HOOKS
	lwzx	r3,r2,r3			// hook  in r3
	cmpwi	r3,0
	beq-	1f				// non-zero?
	mtlr	r3
	blrl
1:
	EMU_STORE_GPR r5, /**/ r3
	b	emulation_done

emulate_mtspr:
	rlwinm	r2,r4,32-14,25,29
	rlwimi	r2,r4,32-4,20,24		// r2 = spr#
	EMU_LOAD_GPR r5, /**/ r3		// value in r0
	addi	r3,r1,K_SPR_HOOKS
	lwzx	r3,r2,r3			// hook in r3
	cmpwi	r3,0
	beq-	1f				// non-zero?
	addi	r3,r3,4				// branch to hook +4
	mtlr	r3
	blrl
1:	
	addi	r3,r1,xSPR_BASE
	stwx	r0,r2,r3			// value in r0
	b	emulation_done

emulate_mtmsr:
	EMU_LOAD_GPR r5, /**/ r2		// r0 = new xMSR
	lwz	r3,xMSR(r1)
	MT_SCRATCH_0 r3				// save old xMSR
	stw	r0,xMSR(r1)
	INC_NIP /**/ r4
	bl	_msr_altered			// r0,r2-r5, (srr1 is modified)
	
	lwz	r0,xMSR(r1)			// MSR_POW transition?
	rlwinm.	r0,r0,0,13,13			// MSR_POW == bit13
	beq+	emulation_done_noinc_chint

	MF_SCRATCH_0 r2				// old xMSR
	rlwinm.	r2,r2,0,13,13
	bne+	emulation_done_noinc_chint

	MAC_EXIT_SAVE( RVEC_MSR_POW )		// POW 0->1 => doze

emulate_rfi:
	lwz	r2,xSRR0(r1)			// new nip = SRR0
	lwz	r3,xMSR(r1)
	mtsrr0	r2
	lwz	r4,xSRR1(r1)
	rlwimi	r3,r4,0,16,31			// msr = (msr & ~0xffff) | (SRR1 & 0xffff)
	rlwimi	r3,r4,0,6,6			// MSR_VEC must be copied too
	stw	r3,xMSR(r1)
	bl	_msr_altered			// r0,r2-r5, (srr1 is updated)
	
	lwz	r3,K_BREAK_FLAGS(r1)		// break at rfi support
	andi.	r3,r3,BREAK_RFI
	beq+	emulation_done_notrace
	li	r4,BREAK_RFI			// r4 = flag causing the break
	MAC_EXIT_SAVE( RVEC_BREAK )

emulate_tlbie:
#if 0
	INC_NIP /**/ r3
	rlwinm	r5,r4,32-8,24,28		// r5 = #B << 3
	EMU_LOAD_GPR r5, /**/ r2		// value ret. in r0
	LI_VIRT(r3,do_tlbie)
	mr	r4,r0				// r4 = ea
	b	call_kernel_save
#else
	b	emulation_done
#endif
	
emulate_tlbsync:
	b	emulation_done

emulate_dcbi:
	b	unhandled_priv_inst		// r4 = opcode

emulate_mfsrin:
	rlwinm	r2,r4,32-8,24,28		// r2 = #B << 3
	EMU_LOAD_GPR r2, /**/ r3		// r0 = reg B
	rlwinm	r3,r0,6,26,29			// r3 = #sr << 2
	b	1f
emulate_mfsr:
	rlwinm	r3,r4,32-14,26,29		// r3 = #sr << 2
1:	addi	r2,r1,xSEGR_BASE
	lwzx	r0,r3,r2
	EMU_STORE_GPR r5, /**/ r2
	b	emulation_done

emulate_mtsrin:
	rlwinm	r2,r4,32-8,24,28		// r2 = #B << 8
	EMU_LOAD_GPR r2, /**/ r3		// r0 = reg B
	rlwinm	r4,r0,4,28,31			// r4 = #sr
	b	1f	
emulate_mtsr:
	rlwinm	r4,r4,32-16,28,31		// r4 = #sr
1:	EMU_LOAD_GPR r5, /**/ r2
	mr	r5,r0				// r5 = new_value
	INC_NIP /**/ r2
	LI_VIRT(r3,do_mtsr)
	b	call_kernel_save
	
/************************************************************************/
/*	Initialize SRP table						*/
/************************************************************************/

.macro WRITE_SPR_HOOK spr_num, hook
	LI_PHYS( r7, \hook)
	stw	r7,(((\spr_num)*4) + K_SPR_HOOKS)(r1)
.endm
.macro CLEAR_SPR_HOOK spr_num
	li	r7,0
	stw	r7,(((\spr_num)*4) + K_SPR_HOOKS)(r1)
.endm

	//////////////////////////////////////////////////////////////
	// initiailize_spr_table
	//
	// May modify r0,r2-r12, ctr
	
initialize_spr_table:
	// Default action is letting the emulator handle it

	LI_PHYS( r7, unhandled_spr )
	addi	r8,r1,K_SPR_HOOKS-4
	li	r9,1024
	mtctr	r9
1:	stwu	r7,4(r8)
	bdnz	1b

	// These registers have no side effects
	
	CLEAR_SPR_HOOK	SRR0
	CLEAR_SPR_HOOK	SRR1
	CLEAR_SPR_HOOK	SPRG0
	CLEAR_SPR_HOOK	SPRG1
	CLEAR_SPR_HOOK	SPRG2
	CLEAR_SPR_HOOK	SPRG3
	CLEAR_SPR_HOOK	DSISR
	CLEAR_SPR_HOOK	DAR
	CLEAR_SPR_HOOK	EAR

	// SPRs that have side effects

	WRITE_SPR_HOOK	PVR,	spr_read_only
	WRITE_SPR_HOOK	SDR1,	spr_sdr1

#ifdef DISABLE_DEC_REG
	CLEAR_SPR_HOOK	DEC
	CLEAR_SPR_HOOK	TBWU
	CLEAR_SPR_HOOK	TBWL	
#else
	WRITE_SPR_HOOK	DEC,	spr_dec
	CLEAR_SPR_HOOK	TBWU
	CLEAR_SPR_HOOK	TBWL
	//WRITE_SPR_HOOK	TBWU,	spr_tbwu
	//WRITE_SPR_HOOK	TBWL,	spr_tbwl
#endif
	// BATs
	.irp	nn,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
	WRITE_SPR_HOOK	IBAT0U+\nn, spr_bat
	.endr

	blr


/************************************************************************/
/*	SPR - emulation							*/
/************************************************************************/

	////////////////////////////////////////////////////////////
	// read (offset 0)
	//	r0	spr_value
	//	r2	spr# << 2	*not used*
	//	r5	dreg << 3
	//
	// write (offset 4)
	//	r0	value_in_gpr
	//	r2	spr# << 2	*used on return*
	//
	// Safe to modify:	r3,r4, (write r5), lr 
	// NOT SAVED:		ctr, xer

	
spr_read_only:	
	blr				// allow read
	b	emulation_done		// ignore write	
	
unhandled_spr:
	b	unhandled_spr_read	// read hook	(offs 0)
unhandled_spr_write:			// write hook	(offs 4)
	srwi	r4,r2,2
	mr	r5,r0
	// r4 = spr#
	// r5 = register-value
	MAC_EXIT_SAVE( RVEC_SPR_WRITE )

unhandled_spr_read:
	srwi	r4,r2,2
	srwi	r5,r5,3
	// r4 = spr#
	// r5 = dest gpr
	MAC_EXIT_SAVE( RVEC_SPR_READ )

spr_bat:
	blr				// read has no side-effects
	INC_NIP /**/ r4
	LI_VIRT( r3, do_mtbat )
	bl	save_middle_regs	// Must do this before touching r6-r12
	srwi	r4,r2,2			// r4 = spr#
	mr	r5,r0			// r5 = value
	li	r6,0			// not forced
	b	call_kernel

spr_sdr1:
	blr				// read has no side-effects
	INC_NIP /**/ r4
	LI_VIRT( r3,do_mtsdr1 )
	mr	r4,r0			// r4 = value
	b	call_kernel_save


/************************************************************************/
/*	Machine State Register						*/
/************************************************************************/

	//////////////////////////////////////////////////////////////
	// FIX_MSR_BITS
	//	mb		mbase (in current address mode)
	//	phys_mb		mbase (physical address)
	//	
	// Modifies r0,r3-r5
	//
	// IMPORTANT: Callee must check xINTERRUPT to see if a return
	// to emulator is necessary (otherwise the interrupt might be
	// delayed...)

.macro FIX_MSR_BITS mb
	lwz	r0,xMSR(\mb)			// [r0] = xMSR, IR,DR in bits 26,27

	// set sr bases from K_MSR_SR_TABLE[ PR IR DR ]
	rlwinm	r5,r0,0,26,27			// IR,DR bits
	rlwimi	r5,r0,32-8,25,25		// PR bit
	addi	r5,r5,K_MSR_SR_TABLE
	lwzux	r3,r5,\mb
	lwz	r4,4(r5)
	lwz	r5,8(r5)
	stw	r3,K_CUR_SR_BASE(\mb)
	stw	r4,K_SR_DATA(\mb)
	stw	r5,K_SR_INST(\mb)

	// In splitmode? If so set the prepare_splitmode flag.
	rlwinm.	r5,r0,0,26,27			// MSR_IR, MSR_DR
	beq-	1f
	cmpwi	r5,MSR_IR | MSR_DR
	beq+	1f
	lwz	r3,K_SPLITMODE_ALGORITHM(\mb)
	b	11f
1:
	lwz	r3,K_TRANSL_DBAT0U(\mb)
	lwz	r4,K_TRANSL_DBAT0L(\mb)
	stw	r3,K_DBAT0U(\mb)
	stw	r4,K_DBAT0L(\mb)
	li	r3,0
11:	stw	r3,K_IN_SPLITMODE(\mb)
	stw	r3,K_PREPARE_SPLITMODE(\mb)

	// copy MSR_FP, MSR_FEx, MSR_SE and  MSR_BE to physical msr
	lwz	r5,x_MSR(\mb)			// r5 = x_MSR
	li	r4,MSR_FE0 | MSR_FE1 | MSR_SE | MSR_FP | MSR_BE
	and	r3,r0,r4			// r3 = (xMSR & MSR_FEx)
	andc	r5,r5,r4			// r5 = (x_MSR & ~MSR_FEx)
	or	r5,r5,r3			// r5 = x_MSR

	// Always clearing the VEC bit generates extra interrupts.
	// On the other hand, enabling the altivec unit is cheap.
	rlwinm	r5,r5,0,7,5			// Clear MSR_VEC (bit 6)
	
	// Clear MSR_FP if the FPU is not ready for use
	lwz	r3,xFPU_STATE(\mb)
	cmpwi	r3,FPU_STATE_IN_USE
	beq	13f
	rlwinm	r5,r5,0,19,17			// Clear MSR_FP (bit 18)
13:
	// Set MSR_SE if BREAK_SINGLE_STEP is set
	lwz	r4,K_BREAK_FLAGS(\mb)
	andi.	r4,r4,BREAK_SINGLE_STEP
	beq+	2f
	ori	r5,r5,MSR_SE			// Enable single-stepping
2:	stw	r5,x_MSR(\mb)
	
	// MSR_EE set? If so, interrupt |= dec_interrupt || irq;
	// XXX:	Reimplement this!
	rlwinm.	r0,r0,0,16,16			// MSR_EE
	beq	20f
	lwz	r3,xIRQ(\mb)
	cmpwi	r3,0
	crandc	eq,eq,FBIT_DecINT
	li	r3,1
	crandc	eq,eq,FBIT_TimerINT
	beq+	20f
	stw	r3,xINTERRUPT(\mb)
20:
.endm

	
	////////////////////////////////////////////////////////////
	// _msr_altered (to be called whenever xMSR is modified)
	//
	// r:	srr1	_msr
	//
	// modifies: r0,r2-r5
	//
	// Note: must not be called when all registers have been saved
	// 
	// IMPORTANT: Callee must check xINTERRUPT to see if a return
	// to emulator is necessary (otherwise a DEC/irq might be delayed...)

_msr_altered:
	mflr	r2			// save lr
	MT_SCRATCH_1 r2

	lwz	r2,K_CUR_SR_BASE(r1)	// save current sr base
	FIX_MSR_BITS r1			// modifies r0,r3-r5

	lwz	r3,K_PREPARE_SPLITMODE(r1)
	cmpwi	r3,0
	beq+	1f
	stw	r3,K_RELOAD_SR(r1)	// flag segement registers for a reload
	bl	prepare_splitmode	// mod r0,r3-r5
	b	2f
1:
	lwz	r3,K_CUR_SR_BASE(r1)	// has sr base changed?
	sub.	r3,r2,r3		// if so, reload registers 
	beq+	2f
	stw	r3,K_RELOAD_SR(r1)	// flag segment registers for a reload
2:
	lwz	r4,x_MSR(r1)		// Fix srr1
	mtsrr1	r4

	MF_SCRATCH_1 r2
	mtlr	r2			// restore lr
	blr

	// r3 = kernel_vars_t *
GLOBAL_SYMBOL(r__msr_altered):
	mr	r8,r3
	mflr	r9			// save lr
	FIX_MSR_BITS r8			// modifies r3-r5
	mtlr	r9
	blr


/************************************************************************/
/*	MSR Segment Register Table					*/
/************************************************************************/
	
	/////////////////////////////////////////////////////////////
	// initialize_sr_offs_table
	//
	// Copy sr_offs_table to K_MSR_SR_TABLE
	// r1 is added to each element
	
initialize_msr_sr_table:
	mflr	r8			// Get address of table
	bl	sr_offs_table
	mflr	r3
	mtlr	r8

	li	r5,4*8			// #words in table	
	mtctr	r5
	addi	r3,r3,-4
	addi	r4,r1,K_MSR_SR_TABLE-4
1:
	lwzu	r6,4(r3)
	add	r6,r6,r1		// And add r1
	stwu	r6,4(r4)
	bdnz	1b
	blr

	// Used to construct msr_sr_table (mbase is added)
sr_offs_table:
	blrl
	/*	K_CUR_SR_BASE,		K_SR_DATA_BASE,		K_SR_INST_BASE,		dummy */
	
	.long	K_UNMAPPED_SR_BASE,	K_UNMAPPED_SR_BASE,	K_UNMAPPED_SR_BASE,	0
	.long	K_SPLIT_SR_BASE,	K_SV_SR_BASE,		K_UNMAPPED_SR_BASE,	0	/* DR */
	.long	K_SPLIT_SR_BASE,	K_UNMAPPED_SR_BASE,	K_SV_SR_BASE,		0	/* IR */
	.long	K_SV_SR_BASE,		K_SV_SR_BASE,		K_SV_SR_BASE,		0	/* DR|IR */

	.long	K_UNMAPPED_SR_BASE,	K_UNMAPPED_SR_BASE,	K_UNMAPPED_SR_BASE,	0	/* PR */
	.long	K_SPLIT_SR_BASE,	K_USER_SR_BASE,		K_UNMAPPED_SR_BASE,	0	/* PR|DR */
	.long	K_SPLIT_SR_BASE,	K_UNMAPPED_SR_BASE,	K_USER_SR_BASE,		0	/* PR|IR */
	.long	K_USER_SR_BASE,		K_USER_SR_BASE,		K_USER_SR_BASE,		0	/* PR|DR|IR */