TXXEXTIR – External Interrupt Routine

The External Interrupt Routine is called from the first level Ext Interrupt handler in low memory when an external interrupt occurs.

000000                                8 TXXEXTIR CSECT ,
000000 18CF                           9          LR    R12,R15
                            00000    10          USING TXXEXTIR,R12                BASE REG
                                     11 *
                            00000    12          USING @TCB,R4
                                     13 *
000002 9180 401B      0001B          14          TM    TCBPSW+3,X'80'     TIMER INTERRUPT ?
000006 4710 C016      00016          15          BO    TIMER              YES - BRANCH
                                     16 *
00000A 9140 401B      0001B          17          TM    TCBPSW+3,X'40'     EXT KEY PRESSED ?
00000E 4710 C026      00026          18          BO    EXTKEY             YES - BRANCH
                                     19 *
000012 8200 C038      00038          20          LPSW  EXTWPSW            GO INTO WAIT STATE

We begin by examining the PSW to determine what type of external interruption occurred.  The two conditions we are interested in are the Interval Timer expired and an operator generated interruption.  If neither of these types of interruptions is indicated a wait state PSW is loaded.

000016                               26 TIMER    DS    0H
000016 5810 C040      00040          27          L     R1,=A(X'80')       RESTART TIMER
00001A 5010 0050      00050          28          ST    R1,ITIMER-@LOWCORE
                                     29 *
00001E 9610 4004      00004          30          OI    TCBFLGS,X'10'      INDICATE TIME EXPIRED
000022 47F0 C02A      0002A          31          B     EXIT               AND EXIT

If the interruption is the result of the Interval Timer expiring then it means the executing task is hogging the CPU.  We set a bit in the TCB flags indicating the task was interrupted by the timer.  This will cause the task to not be dispatched until all other tasks have been given the opportunity to run.

000026                               37 EXTKEY   DS    0H
000026 47F0 C02A      0002A          38          B     EXIT               IGNORE FOR NOW 

00002A                               44 EXIT     DS    0H
00002A 58F0 0014      00014          45          L     R15,20             GET CVT ADDRESS
00002E 58F0 F004      00004          46          L     R15,MVTDISP-@MVT(,R15)   GET DISPATCHER EP
000032 07FF                          47          BR    R15                      BRANCH TO DISPATCHER

000038                               51          DS    0D
000038 0002000000EE0002              52 EXTWPSW  DC    AL1(0,2,0,0),A(@E@EXT)  DISABLED WAIT PSW

For now we will just ignore an external interrupt caused by the operator.  Exit is simply branching to the Dispatcher routine.

[Next – TXXDISP – Dispatcher Routine]

TXXNUC – Nucleus Low Core

Now we can begin creating our small operating system.  It starts with the low core module for the nucleus.  The low core module must be loaded first at location zero.  When we link edit the nucleus we must make sure it is included first.

It begins with the data for mapping the hardware specific addresses in low memory.

000000                                1 TXXNUC   CSECT ,
000000 0000000000000578               2          DC    X'00',X'00',AL2(0),AL4(DISPINIT)  INITIAL PSW
000008 0000000000000000               3          DC    D'0'                              RST NEW PSW
                                      4 *
000010 00000000                       5          DC    V(TXXCVT)                         COMM VECT TABLE
000014 00000000                       6          DC    V(TXXMVT)                         MOD  VECT TABLE
                                      7 *
000018 0000000000000000               8          DC    D'0'          EXT OLD PSW
000020 0000000000000000               9          DC    D'0'          SVC OLD PSW
000028 0000000000000000              10          DC    D'0'          PGM OLD PSW
000030 0000000000000000              11          DC    D'0'          MCK OLD PSW
000038 0000000000000000              12          DC    D'0'          I/O OLD PSW
                                     13 *
000040 0000000000000000              14          DC    D'0'          CHANNEL STATUS WORD
000048 00000000                      15          DC    F'0'          CHANNEL ADDRESS WORD
00004C 00000000                      16          DC    F'0'
000050 00000000                      17          DC    F'0'          INTERVAL TIMER
000054 00000000                      18          DC    F'0'
                                     19 *
000058 0000000000000530              20          DC    X'00',X'00',AL2(0),AL4(EXTHNDLR)  EXT NEW PSW
000060 0000000000000554              21          DC    X'00',X'00',AL2(0),AL4(SVCHNDLR)  SVC NEW PSW
000068 0000000000000520              22          DC    X'00',X'00',AL2(0),AL4(PGMHNDLR)  PGM NEW PSW
000070 0000000000EE0001              23          DC    X'00',X'02',AL2(0),AL4(@E@MCK)    MCK NEW PSW
000078 0000000000000520              24          DC    X'00',X'00',AL2(0),AL4(IOHNDLR)   I/O NEW PSW

The restart PSW is the initial PSW that will be loaded by the nucleus loader.  It will cause execution to begin at DISPINIT – the dispatcher initialization routine.  Locations 16 (x’10’) and 20 (x’14’) are not used by the CPU so we will use them as pointers.  Location 16 will point to a control block that contains system related data and pointers (the Communication Vector Table – or CVT).  Location 20 will point the the Module Vector Table (MVT) which is a list of pointers to other modules.  Our system modules in general will not contain a direct pointer V(module) to a module.  Instead the module address will be obtained from the MVT.

Next we have the OLD PSW areas for external, SVC, program, machine check, and I/O interruptions.  This is where the CPU will store the current PSW when an interrupt occurs.  It is followed by the Channel Status Word and Channel Address Word.  The Interval Timer is used to generate an external interrupt.  A value is loaded into the Interval Timer word.  The CPU will decrement the value.  When the timer is expired an external interrupt will be generated.

Finally we define the NEW SVC values for the various interrupts.  When an interrupt occurs the CPU will load the PSW with the associated value.  If a machine check occurs we load a disabled wait PSW.

                            00080    27 LOWCLEN  EQU   *-TXXNUC
                            00380    28 LOWCFILL EQU   1024-LOWCLEN
000080 0000000000000000              29          DC    (LOWCFILL)X'00'
                                     30 *
000400 0000000000000000              32 EXTREGS  DC    16F'0'             EXT INTERRUPT REG SAVE
000440 0000000000000000              33 IOREGS   DC    16F'0'             I/O INTERRUPT REG SAVE
000480 0000000000000000              34 SVCREGS  DC    16F'0'             SVC INTERRUPT REG SAVE
0004C0 0000000000000000              35 PGMREGS  DC    16F'0'             PGM INTERRUPT REG SAVE

Next we fill the remainder of memory up to 1k with zeros.  Beginning at address 1k we define interrupt register save areas.  This gives us a place to save current register contents when an interrupt occurs.  Because the save areas are in the first 4k of memory we will  not need a base register to access them.

                            00000    39          USING TXXNUC,0      NO BASE REGISTER NEEDED FOR FIRST 4K
                                     48          ENTRY LLDISP
000500                               49 LLDISP   DS    0H
                            00000    50          USING @TCB,R4
                                     51 *
000500 5810 05B8      005B8          52          L     R1,=A(X'80')       RESTART TIMER
000504 5010 0050      00050          53          ST    R1,ITIMER-@LOWCORE              AT DISPATCH
                                     54 *
000508 D207 0518 4018 00518 00018    55          MVC   LLRSTPSW,TCBPSW    COPY TCB PSW INTO LOW CORE
00050E 980F 4020      00020          56          LM    R0,R15,TCBREGS     RESTORE TCB REGISTERS
000512 8200 0518      00518          57          LPSW  LLRSTPSW           DISPATCH
                                     58 *
000516 0000
000518 0000000000000000              60 LLRSTPSW DC    D'0'               DISPATCH PSW
                                     62          DROP  R4

Now comes our Low Level Dispatch routine.  We need some code in low memory (first 4k) to restore registers and PSW when returning from an interrupt.  This code needs to be in low memory so we will not require the use of a base register.  We start by resetting the Interval Timer.  When it expires it will interrupt the currently running task so we reset it to give each task the same execution interval.  On entry to LLDISP Register 4 must contain the address of the TCB to be dispatched.  First the PSW is copied out of the TCB and into low core.  Next the registers are restored from the TCB.  A Load PSW instruction loads the PSW that was saved from the TCB.

000520                               68 PGMHNDLR DS    0H
000520                               69 IOHNDLR  DS    0H
000520 8200 0528      00528          70          LPSW  WAITPSW
                                     71 *
000528                               72          DS    0D
000528 0002000000EE9999              73 WAITPSW  DC    X'00',X'02',AL2(0),A(X'EE9999')

For now if a program or I/O interrupt occurs we will load a wait state PSW.  Later we will provide interrupt handling for these interrupts.

000530                               81 EXTHNDLR DS    0H
000530 900F 0400      00400          82          STM   R0,R15,EXTREGS     SAVE REGISTERS
                                     83 *
000534 05C0                          84          BALR  R12,0              ESTABLISH BASE REGISTER
                            00536    85          USING *,R12
                                     86 *
000536 58B0 0010      00010          87          L     R11,16             GET CVT ADDRESS
00053A 5840 B000      00000          88          L     R4,CVTCTCB-@CVT(,R11) GET CURRENT TCB
                            00000    89          USING @TCB,R4
                                     90 *
00053E D207 4018 0018 00018 00018    91          MVC   TCBPSW,EXTOPSW-@LOWCORE  SAVE INT PSW
000544 D23F 4020 0400 00020 00400    92          MVC   TCBREGS(64),EXTREGS      SAVE INT   REGISTERS
                                     93 *
00054A 58B0 0014      00014          94          L     R11,20             GET MVT ADDRESS
00054E 58F0 B000      00000          95          L     R15,MVTEXTIR-@MVT(,R11)   GET EXT INTRUPT RTN
000552 07FF                          96          BR    R15                BRANCH
                                     97 *
                                     98          DROP  R4,R12

Now comes the External Interrupt handler.  First the contents of the registers are saved into our low core register save area.  The current TCB address is obtained from the CVT [Current TCB – CVTCTCB].  The interrupt PSW and registers are copied into the TCB.  The external interrupt routine is located in the MVT and control is passed to it.

000554                              105 SVCHNDLR DS    0H
000554 900F 0480      00480         106          STM   R0,R15,SVCREGS     SAVE INTERRUPT REGISTERS
                                    107 *
000558 05C0                         108          BALR  R12,0              ESTABLISH
                            0055A   109          USING *,R12                       BASE REGISTER
                                    110 *
00055A 5840 0010      00010         111          L     R4,16              CVT
00055E 5840 4000      00000         112          L     R4,CVTCTCB-@CVT(,R4)  GET CURRENT TCB
                            00000   113          USING @TCB,R4
                                    114 *
000562 D207 4018 0020 00018 00020   115          MVC   TCBPSW,SVCOPSW-@LOWCORE   SAVE INTERRUPT PSW
000568 D23F 4020 0480 00020 00480   116          MVC   TCBREGS(64),SVCREGS       SAVE INTERRUPT REGS
                                    117 *
00056E 58F0 0014      00014         118          L     R15,20             MVT
000572 58F0 F010      00010         119          L     R15,MVTSVCIR-@MVT(,R15)   SVC INTERRUPT RTN
000576 07FF                         120          BR    R15
                                    121 *
                                    123          DROP  R4,R12

The SVC interrupt handler is the same as the external interrupt handler except it passes control the SVC Interrupt Routine.

000578                              127 DISPINIT DS    0H
                            00000   128          USING @IPLPARM,R10
                                    129 *
000578 D503 05BC A000 005BC 00000   130          CLC   =C'@IPL',@IPLID
00057E 4780 0586      00586         131          BE    DISPI010
                                    132 *
000582 8200 05A0      005A0         133          LPSW  ERRINIT
                                    134 *
                                    135 *
000586                              136 DISPI010 DS    0H
000586 58F0 0014      00014         137          L     R15,20             MVT ADDRESS
00058A 58F0 F00C      0000C         138          L     R15,MVTDINIT-@MVT(,R15)   DISPATCER INIT ROUTINE
00058E 05EF                         139          BALR  R14,R15            INTIALIZE DISPATCHER
                                    140 *
000590 58F0 0014      00014         141          L     R15,20             MVT ADDRESS
000594 58F0 F004      00004         142          L     R15,MVTDISP-@MVT(,R15)  DISPATCHER ADDRESS
000598 07FF                         143          BR    R15
                                    144 *
0005A0                              146          DS    0D
0005A0 0000000000000002             147 ERRINIT  DC    A(0,2,0,0),A(@E@IPL)

Finally we have the Dispatcher Initialization.  We expect a pointer to some information from the LOADER (memory size, IPL Device).  If the IPL Parameters are not valid a wait stat PSW is loaded.  Once the IPL Parm Block is validated the  Dispatcher Initialization Module is called.  After initialization is complete control is passed to the Dispatcher.

[Next – TXXEXTIR – External Interrupt Routine]