summaryrefslogtreecommitdiffstats
path: root/private/ntos/fw/mips/j4inter.s
blob: 45072f85afe887662fb49a3744cf3df442c18962 (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
#if defined(JAZZ) && defined(R4000)


/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    j4inter.s

Abstract:

    This module contains the interrupt dispatcher and various interrupt
    routines for the selftest. The exception dispatcher resides in rom
    because the BEV bit in the psr is set.

Author:

    Lluis Abello (lluis)  8-May-91

Environment:

    Executes in kernal mode.


Revision History:


--*/
#include "led.h"
#include "ksmips.h"
#include "selfmap.h"
#include "dmaregs.h"

#ifdef DUO
#include <duoprom.h>
#else
#include <jazzprom.h>
#endif

//
// PROM entry point definitions.
// Define base address of prom entry vector and prom entry macro.
//
#define PROM_BASE (KSEG1_BASE | 0x1fc00000)
#define PROM_ENTRY(x) (PROM_BASE + ((x) * 8))

#define PutLedDisplay PROM_ENTRY(14)
#ifdef DUO
//
// Interrupt indexes in the cause register
//
#define SOFT0_INT       0          // software int 0
#define SOFT1_INT       1          // software int 1
#define DMA_INT         2          // hardware int 0
#define DEVICE_INT      3          // hardware int 1
#define EISA_INT        4          // hardware int 2
#define TIMER_INT       5          // hardware int 3
#define IP_INT          6          // hardware int 4
#define R400_TIMER_INT  7          // hardware int 5

#else

//
// Interrupt indexes in the cause register
//
#define SOFT0_INT       0          // software int 0
#define SOFT1_INT       1          // software int 1
#define DMA_INT         2          // hardware int 0
#define DEVICE_INT      3          // hardware int 1
#define EISA_INT        4          // hardware int 2
#define EISA_NMI_INT    5          // hardware int 3
#define TIMER_INT       6          // hardware int 4
#define R400_TIMER_INT  7          // hardware int 5

#endif
//
// Interrupt mask, to enable just the desired interrupts.
//

#define INT_MASK ((1<<TIMER_INT) | (1<<DEVICE_INT))

//
// Stack frame offset definitions.
//
#define IntFrameT0      0x4
#define IntFrameT1      0x8
#define IntFrameT2      0xC
#define IntFrameT3      0x10
#define IntFrameT4      0x14
#define IntFrameT5      0x18
#define IntFrameT6      0x1C
#define IntFrameT7      0x20
#define IntFrameT8      0x24
#define IntFrameT9      0x28
#define IntFrameAT      0x2C
#define IntFrameRa      0x30
#define IntFrameA0      0x34
#define IntFrameA1      0x38
#define IntFrameA2      0x3C
#define IntFrameA3      0x40
#define IntFrameV0      0x44
#define IntFrameV1      0x48
#define IntFrameSize    0x50


    .data
FwSavedK1Address:
    .space  4               // address where to save exception return address.
InterruptTable:             // vector of 8 interrupt handler pointers
    .word  0:8              // initialy set to zero.
DeviceIntTable:             // vector of 10 device interrupt handler pointers
    .word  0:10             // initialy set to zero.
InterruptNotExpectedMsg:
    .ascii  "Interrupt    not expected.\r\n\0"
#define IntOffset 10
.globl TimerTicks
TimerTicks:                 // Counter of timer ticks. Decremented by TimerInterrupt handler.
    .word  0

    .text
    .set noreorder
    .set noat
//
//++
//ConnectInterrupts
//
// Routine Description:
//
//      This routine initializes the interrupt table with pointers to
//      the interrupt handlers.
//
//      Handlers for the following interrupts are installed:
//          MCT_ADR Interval Timer
//          Sonic
//          Video
//
//      It also enables interrupts.
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--

        LEAF_ENTRY(ConnectInterrupts)
//
// Initialize the interrupt dispatch table
//
        la      t0,InterruptTable               // get address of table
        la      t1,DeviceInt                    // address of routine
        sw      t1,DEVICE_INT*4(t0)             // store in table
        la      t1,IntervalTimerInt             // get address of routine
        sw      t1,TIMER_INT*4(t0)              // store it in table
        la      t1,R4000TimerInt                // get address of routine
        sw      t1,R400_TIMER_INT*4(t0)         // store it in table
//
// Initialize the device interrupt dispatch table
//
        la      t0,DeviceIntTable               // get address of table
        la      t1,SonicInterrupt               // address of handler
        sw      t1,ETHERNET_DEVICE(t0)          // store in table
        la      t1,VideoInterrupt               // address of handler
        sw      t1,VIDEO_DEVICE(t0)             // store in table
//
// Initialize the exception dispatch table
//
        li      t0,KSEG1_BASE                   // lookup table
        la      t1,SelftestExceptionHandler           // address of Exception Handler
        sw      t1,0x1014(t0)// write in firmware vector

//
// read the interrupt pending register a few times to clear
// interrupts.
//
        li      t0,INTERRUPT_VIRTUAL_BASE
        lbu     zero,0(t0)
        lbu     zero,0(t0)
        lbu     zero,0(t0)
//
// Enable interrupts.
//

        li      t0,(1<< PSR_BEV)+(1<<PSR_IE)+(INT_MASK<<PSR_INTMASK)+(1 << PSR_CU1)
        mtc0    t0,psr
        nop
        nop
        nop
        j       ra                              // return to caller
        nop
        .end    ConnectInterrupts
//++
// SelftestExceptionHandler
//
// Routine Description:
//
//      This routine is called as a result of an exception
//      If the exception is an interrupt it calls the interrupt handler
//      otherwise it jumps to the monitor.
//      This routine is called using the firmware exception handling convection.
//      It must return to k1 and put the address where to return from
//      the exception in k0.
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--
        LEAF_ENTRY (SelftestExceptionHandler)
        la      k0,FwSavedK1Address             //
        sw      k1,0(k0)                        // save fw return address
        mfc0    k0,cause                        // get cause register
        li      k1,R4000_XCODE_MASK             // get mask to extract cause of exception
        and     k0,k1                           // get cause of exception
        beq     k0,zero,10f                     // if 0 is an interrupt.
        la      k0,MonitorExceptionHandler      // otherwise
        j       k0                              // Jump to the Monitor
        ori     k1,zero,COMMON_EXCEPTION        // signal that a common exception occurred.
10:
        la      k0,InterruptDispatcher          // call interrupt dispatcher
        j       k0
        nop
    ALTERNATE_ENTRY(ExceptionReturn)
        la      k0,FwSavedK1Address             //
        lw      k1,0(k0)                        // load fw return address
        mfc0    k0,epc                          // set address where to return from exception
        j       k1                              // jump to firmware
        nop
        .end    SelftestExceptionHandler

//++
// InterruptDispatcher
//
// Routine Description:
//
//      This routine is called as a result of an interrupt
//      It looks in the interrupt dispatch table for a handler and
//      jumps to it if it founds any.
//      This routine is called from rom. The interrupt handler must return
//      to k1 and put the address where to return from the exception in k0.
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--
        LEAF_ENTRY (InterruptDispatcher)
        subu    sp,sp,IntFrameSize          // make room in the stack
        sw      t0,IntFrameT0(sp)           // save temporay registers
        sw      t1,IntFrameT1(sp)
        sw      t2,IntFrameT2(sp)
        sw      t3,IntFrameT3(sp)
        sw      t4,IntFrameT4(sp)
        sw      t5,IntFrameT5(sp)
        sw      t6,IntFrameT6(sp)
        sw      t7,IntFrameT7(sp)
        sw      t8,IntFrameT8(sp)
        sw      t9,IntFrameT9(sp)
        sw      AT,IntFrameAT(sp)
        sw      a0,IntFrameA0(sp)
        sw      a1,IntFrameA1(sp)
        sw      a2,IntFrameA2(sp)
        sw      a3,IntFrameA3(sp)
        sw      v0,IntFrameV0(sp)
        sw      v1,IntFrameV1(sp)
        sw      ra,IntFrameRa(sp)
        mfc0    t1,cause                    // get cause register
        mfc0    t2,psr                      // get psr register
        la      t0,InterruptTable           // get address of interrupt table.
        and     t2,t2,t1                    // and them to discard disabled interrupts
        li      t3,0x20                     // Index for dispatch table
CheckNextInt:
        andi    t4,t2,(1<<15)               // check for interrupt starting
        bne     t4,zero,JumpToInterrupt     // with higher priority
        subu    t3,t3,0x4                   // Next table index
        bne     t3,zero,CheckNextInt
        sll     t2,t2,1                     // shift IntPend field to check next
JumpToInterrupt:                            // t3 has the interrupt index
        addu    t0,t0,t3                    // add offset to table
        lw      t0,0(t0)                    // get routine address
        nop
        bne     t0,zero,GoToHandler
        nop
        jal     HandlerForNoHandlers
        ori     a0,t3,0xC0                  // a0 has the encoded interrupt number
        j       ExitInterrupt
        nop
GoToHandler:
        jal     t0
        nop
ExitInterrupt:
        lw      ra,IntFrameRa(sp)           // restore stack
        lw      t0,IntFrameT0(sp)           // restore temporay registers
        lw      t1,IntFrameT1(sp)
        lw      t2,IntFrameT2(sp)
        lw      t3,IntFrameT3(sp)
        lw      t4,IntFrameT4(sp)
        lw      t5,IntFrameT5(sp)
        lw      t6,IntFrameT6(sp)
        lw      t7,IntFrameT7(sp)
        lw      t8,IntFrameT8(sp)
        lw      t9,IntFrameT9(sp)
        lw      AT,IntFrameAT(sp)
        lw      a0,IntFrameA0(sp)
        lw      a1,IntFrameA1(sp)
        lw      a2,IntFrameA2(sp)
        lw      a3,IntFrameA3(sp)
        lw      v0,IntFrameV0(sp)
        lw      v1,IntFrameV1(sp)
        b       ExceptionReturn             // branch to return from exception
        addu    sp,sp,IntFrameSize          // restore stack
    .end        InterruptDispatcher
//
//++
//DeviceInt
//
// Routine Description:
//
//      This routine is called as a result of a Hardware Interrupt 1
//      This is a device interrupt. The routine reads the interrupt
//      source register and dispatches to the proper interrupt handler
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--
        LEAF_ENTRY(DeviceInt)
        li      t0,INTERRUPT_VIRTUAL_BASE   // address of interrupt source reg
        lbu     a0,0(t0)                    // read interrupt
        la      t0, DeviceIntTable          // base address of dispatch table.
        addu    t0,t0,a0                    // add offset to base
        lw      t0,0(t0)                    // read handler address
        nop
        beq     t0,zero,HandlerForNoHandlers// hang diplay something in the led
        nop
        j       t0                          // go to routine.
        nop
        .end DeviceInt

//++
//VideoInterrupt
//
// Routine Description:
//
//      This routine is called as a result of a video interrupt.
//      It does nothing is just here no to take a video interrupt as
//      an error.
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--
        LEAF_ENTRY(VideoInterrupt)
        j       ra                          // return to caller.
        nop
        .end VideoInterrupt
//
//++
//IntervalTimerInt
//
// Routine Description:
//
//      This routine is called as a result of a Hardware Interrupt 4
//      This is an MCTADR IntervalTimer interrupt. The routine decrements
//      a counter used for timeouts during the self-test.
//      A test that needs timeout facilites must set the number of milliseconds
//      into the TimerTicks variable and poll it until it's zero.
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--
        LEAF_ENTRY(IntervalTimerInt)
        la      t5,TimerTicks               // get address of counter
        lw      t2,0(t5)                    // read counter
        li      t1,DMA_VIRTUAL_BASE         // base address of MCTADR
        beq     t2,zero,NoDecrement
#ifdef DUO
        lw      t1,DmaTimerInterruptAcknowledge(t1)
#else
        lw      t1,DmaIntervalTimer(t1)     // read register to clear int.
#endif
        addiu   t2,t2,-1                    // decrement counter by one
NoDecrement:
        sw      t2,0(t5)                    // store new counter value
        j       ra                          // return to caller.
        nop
        .end IntervalTimerInt
//
//++
//R4000 TimerInt
//
// Routine Description:
//
//      This routine is called as a result of an R4000 timer Interrupt
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--
        LEAF_ENTRY(R4000TimerInt)
        li      t0,1000
        mtc0    t0,compare                  // clear interrupt
        j       ra                          // return to caller.
        nop
        .end    R4000TimerInt
//
//++
//HandlerForNoHandlers
//
// Routine Description:
//
//      This routine is called when an interrupt is received and no handler
//      has been set for it.
//      It displays a message and then hangs blinking the error code
//      in the LED.
//
// Arguments:
//
//      a0 has the interrupt value.
//
// Return Value:
//
//      None.
//
//--

    LEAF_ENTRY(HandlerForNoHandlers)
        srl     t0,a0,4                         // get second digit
        andi    t0,t0,0xF
        slti    t1,t0,10
        bne     t1,zero,10f                     // branch if 0-9
        addiu   t0,t0,0x30                      // addjust value
        addiu   t0,t0,0x41-0x30-10              // readjust value if A-F
10:
        andi    t1,a0,0xF                       // get less significant digit
        slti    t2,t1,10
        bne     t2,zero,10f                     // branch if 0-9
        addiu   t1,t1,0x30                      // addjust value
        addiu   t1,t1,0x41-0x30-10              // readjust value if A-F
10:
        la      a0,InterruptNotExpectedMsg      // get message address
        sb      t0,IntOffset(a0)                // write to digit interrupt #
        sb      t1,IntOffset+1(a0)              // into message
        li      a1,0                            // column  0
        jal     FwPrint                         // display error
        li      a2,1                            // row 0
        li      t0,PutLedDisplay                // get address of led
        lui     a0,LED_BLINK
        jal     t0
        ori     a0,a0,LED_NOT_INTERRUPT
    .end    HandlerForNoHandlers
//
//++
//DisableInterrupt
//
// Routine Description:
//
//      This routine disables interrupts.  By clearing the IEc bit in the psr
//
// Arguments:
//
//      None.
//
// Return Value:
//
//      None.
//
//--

    LEAF_ENTRY(DisableInterrupts)
        li      t0,(1<< PSR_BEV) | (1 << PSR_CU1)
        mtc0    t0,psr
        nop
        nop
        nop
        nop
        j       ra
        nop
    .end DisableInterrupts
#endif //R4000 && JAZZ