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]