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.