Operator Console

Now that we can queue messages to an operator console we need to build the code to provide the console.  Our console code will run as a separate task under its own TCB.  It will use our EXCP SVC to drive an 3270 terminal.

000000                                1 TXXCON   CSECT 
000000 05C0                           2          BALR  R12,0 
                            00002     3          USING *,R12
                                      4 
000002 58B0 0010      00010           5          L     R11,16             CVT 
000006 58B0 B014      00014           6          L     R11,CVTCCB-@CVT(,R11)  CONSOLE COMMUNICATION BLOCK 
                            00000     7          USING @CCB,R11 

We start by establishing a base register. Next we locate the Console Communication Block which is anchored in the CVT.

00000A 5850 0010      00010           9          L     R5,16              CVT ADDRESS 
00000E 5850 5008      00008          10          L     R5,CVTUCB-@CVT(,R5)  UCB TABLE                           
                            00000    11          USING @UCB,R5                                                  
                                     12 *                                                                       
000012                               13 FINDUCB  DS    0H                                                       
000012 D501 CBA6 5004 00BA8 00004    14          CLC   =X'FFFF',UCBCUA    END OF LIST                           
000018 4780 C2C2      002C4          15          BE    FINDERR              YES - ERROR                         
00001C D501 5004 B010 00004 00010    16          CLC   UCBCUA,CCBDEV      OUR TERMINAL UCB                      
000022 4780 C02C      0002E          17          BE    FIND010            YES - BRANCH                          
000026 5850 5000      00000          18          L     R5,UCBNEXT                                               
00002A 47F0 C010      00012          19          B     FINDUCB                                                  
                                     21 *                                                                       
00002E                               22 FIND010  DS    0H      

Now we search through the UCB table to find the UCB for the 3270 that will be used as our operator console. The device address is defined in the CCB.

00002E 9680 5007      00007          23          OI    UCBSTAT,UCBSONLN   FORCE DEVICE ONLINE                   
                                     24 *                                                                       
000032 5810 CB96      00B98          25          L     R1,=A(CNSLINTR)    DEVICE INTERRUPT ROUTINE              
000036 5010 5010      00010          26          ST    R1,UCBINTR                                               
00003A 4110 C352      00354          27          LA    R1,ZECB            INTERRUPT ECB                         
00003E 5010 5014      00014          28          ST    R1,UCBINTRP        SAVE AS INTERRUPT PARM

Now we have to set up the UCB. First we mark the device as online since by default we have left all devices marked offline. We still need some device initialization code to handle this but for now this will work.

We also set up a device interrupt handler that will be called when the device generates an interrupt but no I/O has been started on the device. This occurs when the enter key is pressed on the console indicating that data is available to be read.

We place the address of the interrupt routine into UCBINTR and we save the address of an ECB into the interrupt parameter UCBINTRP.

000BAA                              390 CNSLINTR DS    0H                                                       
                            00BAA   391          USING *,R15                                                    
                            00000   392          USING @UCB,R1                                                  
000BAA 5810 1014      00014         393          L     R1,UCBINTRP                                              
000BAE 58F0 0014      00014         394          L     R15,20                                                   
000BB2 58F0 F028      00028         395          L     R15,MVTPOST-@MVT(,R15)                                   
000BB6 07FF                         396          BR    R15                                                      

This is the interrupt routine. On entry R15 contains the entry point and R1 contains the address of the UCB. We get the ECB address from the interrupt parameter field and then branch to the Post routine to post the ECB. We can simply branch to the Post routine since R14 contains the return address from the caller to the interrupt routine.

000042 BE57 C2E7      002E9          30          STCM  R5,B'0111',WIOB+(IOBDCBPT-@IOB)                          
000046 BE57 C337      00339          31          STCM  R5,B'0111',RIOB+(IOBDCBPT-@IOB)                          
00004A BE57 C30F      00311          32          STCM  R5,B'0111',WIOB2+(IOBDCBPT-@IOB)                         
00004E 9680 C2E6      002E8          33          OI    WIOB+(IOBFLG4-@IOB),IOBFUCBP  DCBPT IS UCB ADDR          
000052 9680 C30E      00310          34          OI    WIOB2+(IOBFLG4-@IOB),IOBFUCBP DCBPT IS UCB ADDR          
000056 9680 C336      00338          35          OI    RIOB+(IOBFLG4-@IOB),IOBFUCBP  DCBPT IS UCB ADDR

Now we can save the UCB address into our IOBs. We also have to remember to set the flag bit that indicates this is a UCB address and not a DCB address. (We still have not defined what a DCB will be)

00005A 4110 C352      00354          37          LA    R1,ZECB            INTERRUPT ECB                         
00005E 5010 C356      00358          38          ST    R1,ECBLIST         SAVE INTO ECB LIST                    
000062 4110 B00C      0000C          39          LA    R1,CCBECB          GET CNSL COMM BLK ECB                 
000066 5010 C35A      0035C          40          ST    R1,ECBLIST+4       SAVE INTO ECB LIST                    
00006A 9680 C35A      0035C          41          OI    ECBLIST+4,X'80'     END OF WAIT LIST 

Now we build our ECB wait list. We will wait until either one of two ECBs are posted. First the the ECB our interrupt routine will post (ZECB). If this ECB is posted we will need to issue a read to the terminal to get the operator input. The second ECB is the Console Communication Block ECB (CCBECB) that is posted when a message is placed on the console queue.

                                     43          $WTO  'TXXOS CONSOLE - &SYSDATE &SYSTIME'                      
00006E 0700                          44+         CNOP  0,4                                                      
000070 4510 C094      00096          45+         BAL   1,IHB10001                                                
000074 1E000078                      46+         DC    AL1(30),AL3(*+3)                                         
000078 E3E7E7D6E240C3D6              47+         DC    C'TXXOS CONSOLE - 01/13/12 10.09'                        
000096                               48+IHB10001 DS    0H                                                       
000096 5810 1000      00000          49+         L     1,0(,1)                                                  
00009A 0A23                          50+         SVC   35

Now we can add a message to the console queue using our SVC 35 routine. Although our $WTO, like other macros in TXXOS, resembles the MVS version, it is not the same. Ours is much simpler.

00009C                               52 RUNLOOP  DS    0H                                                       
00009C 9240 CB0F      00B11          53          MVI   BUFR,C' '                                                
0000A0 D27E CB10 CB0F 00B12 00B11    54          MVC   BUFR+1(L'BUFR-1),BUFR   CLEAR BUFFER                     
0000A6 92C3 C376      00378          55          MVI   DATA,X'C3'              SET WCC                          
                                     56 *                                                                       
0000AA D703 C34E C34E 00350 00350    57          XC    WECB,WECB               CLEAR WRITE ECB                  
0000B0 4110 C2D2      002D4          58          LA    R1,WIOB                 POINT TO IOB                     
0000B4 0A00                          59          SVC   0                                                        
                                     60 *                                                                       
0000B6 4110 C34E      00350          61          LA    R1,WECB                                                  
0000BA 0A01                          62          SVC   1                       WAIT FOR WRITE TO COMPLETE

Now we begin our main processing loop. We start by clearing out the input buffer. We then set the 3270 Write Command Code. We issue an EXCP using our Write IOB (WIOB) and then wait for the I/O to complete. I will not go into great detail about the 3270 data stream here. You can find more information at my website www.tommysprinkle.com/mvs/P3270.

0000BC                               64 RUNLOOP1 DS    0H                                                       
0000BC D703 C352 C352 00354 00354    65          XC    ZECB,ZECB                                                
0000C2 4110 C356      00358          66          LA    R1,ECBLIST                                               
0000C6 5610 CB9A      00B9C          67          O     R1,=A(X'80000000')  INDICATE ECB LIST                    
0000CA 0A01                          68          SVC   1                   WAIT FOR SOMETHING TO HAPPEN         
                                     69 *                                                                       
0000CC 5810 C35A      0035C          70          L     R1,ECBLIST+4        CHECK FOR WTO POST                   
0000D0 9140 1000      00000          71          TM    0(R1),X'40'         IS IT WTO POST                       
0000D4 4710 C102      00104          72          BO    WTO                  YES - GET IT 

Now we wait using our ECB Wait List. When we come back from the wait we determine which ECB has been posted. We test the CCBECB to see if a message has been queued. If so we branch to process it, else we continue to process operator input.

0000D8 D703 C34A C34A 0034C 0034C    74          XC    RECB,RECB           CLEAR READ ECB                       
0000DE 4110 C322      00324          75          LA    R1,RIOB              POINT TO READ IOB                   
0000E2 0A00                          76          SVC   0                   READ INPUT FROM TERMINAL             
                                     77 *                                                                       
0000E4 4110 C34A      0034C          78          LA    R1,RECB             WAIT FOR                             
0000E8 0A01                          79          SVC   1                           I/O TO COMPLETE              
                                     80 *                                                                       
0000EA 4100 C154      00156          82          LA   R0,OPMSG                                                  
0000EE 0A0B                          83          SVC  11                  GET DATE/TIME                         
                                     84          $WTL OPMSG,L'OPMSG                                             
0000F0 4110 C154      00156          85+         LA    R1,OPMSG                                                 
0000F4 4100 0028      00028          86+         LA    R0,L'OPMSG                                               
0000F8 8900 0018      00018          87+         SLL   R0,24                                                    
0000FC 1610                          88+         OR    R1,R0                                                    
0000FE 0A23                          89+         SVC   35                                                       
                                     90 *                                                                       
000100 47F0 C09A      0009C          91          B     RUNLOOP             IGNORE FOR NOW

We issue a terminal read CCW using our Read IOB (RIOB) to get the operator input. For now we simply ignore the input but we do write a message indicating operator input. We issue a SVC 11 call to get the formatted date & time. We then use the $WTL macro to queue the message using SVC 35. Our $WTL is similar to our $WTO except that the $WTL is provided a pointer and the length of the message text. We then branch back to our main loop so we issue a write to the console and unlock the keyboard.

000104                               94 WTO      DS    0H                                                       
000104 D703 B00C B00C 0000C 0000C    95          XC    CCBECB,CCBECB      CLEAR ECB                              
00010A                               96 WTO010   DS    0H                                                       
00010A 5850 B004      00004          97          L     R5,CCBMBQ          FIRST ELEMENT ON QUEUE                
00010E 1255                          98          LTR   R5,R5                                                    
000110 4780 C13A      0013C          99          BZ    WTO030             BRANCH IF QUEUE EMPTY                 
                                    100 *                                                                       
                            00000   101          USING @CMB,R5

If we are processing a queued message we start by picking the first CMB element on the queue.

000114 4110 5004      00004         103          LA    R1,CMBTEXT         TEXT                                  
000118 4100 004F      0004F         104          LA    R0,79                                                    
00011C 45E0 C17C      0017E         105          BAL   R14,ADDLINE        ADD TEXT TO CONSOLE BUFFER

Now we point to the text in the Console Message and call the ADDLINE routine to add the text to our terminal output buffer.

000120 5820 5000      00000         107          L     R2,CMBNEXT         NEXT BUFFER                           
000124 5020 B004      00004         108          ST    R2,CCBMBQ          REMOVE FROM CCB QUEUE
                                    109 *                                                                       
000128                              110 WTO020   DS    
000128 5810 B008      00008         111          L     R1,CCBFMQ          GET TOP OF FREE QUEUE                 
00012C 5010 5000      00000         112          ST    R1,CMBNEXT                                               
000130 BA15 B008      00008         113          CS    R1,R5,CCBFMQ       PLACE ON FREE QUEUE                   
000134 4770 C126      00128         114          BNZ   WTO020                                                   
                                    115 *                                                                       
000138 47F0 C102      00104         116          B     WTO                GO PROCESS NEXT ELEMENT

Now we remove the element from the queue. We then add the CMB back to the free queue. Once that is done we go back and check to see if there is another CMB on the queue.

00013C                              119 WTO030   DS   0H                                                        
00013C 9240 C376      00378         120          MVI  DATA,X'40'          SET WCC                               
000140 D703 C34E C34E 00350 00350   121          XC   WECB,WECB           CLEAR WRITE ECB                       
000146 4110 C2FA      002FC         122          LA   R1,WIOB2            POINT TO IOB                          
00014A 0A00                         123          SVC  0                                                         
                                    124 *                                                                       
00014C 4110 C34E      00350         125          LA   R1,WECB                                                   
000150 0A01                         126          SVC  1                   WAIT FOR I/O                          
                                    127 *                                                                       
000152 47F0 C0BA      000BC         128          B    RUNLOOP1

When all the CMB elements have been processed we come here to write the updated buffer to the console terminal.

00017E                              134 ADDLINE  DS   0H                                                        
00017E 50E0 C1CE      001D0         135          ST   R14,ADDLINXT       SAVE RETURN ADDRESS                    
000182 58F0 C1D2      001D4         136          L    R15,ADDLCT                                                
000186 41F0 F001      00001         137          LA   R15,1(,R15)        ADD ONE                                
00018A 50F0 C1D2      001D4         138          ST   R15,ADDLCT                                                
                                    139 *                                                                       
00018E 59F0 CB9E      00BA0         140          C    R15,=F'24'                                                
000192 4740 C1A8      001AA         141          BL   ADDL010            DON'T NEED TO SCROLL UP                
                                    142 *                                                                       
000196 45E0 C232      00234         143          BAL  R14,PAGEUP                                                
00019A 41F0 0017      00017         144          LA   R15,23                                                    
00019E 50F0 C1D2      001D4         145          ST   R15,ADDLCT                                                
0001A2 41F0 CAB4      00AB6         146          LA   R15,L23                                                   
0001A6 47F0 C1B2      001B4         147          B    ADDL020                                                   
                                    148 *                                                                       
                                    149 *                                                                       
0001AA                              150 ADDL010   DS   0H                                                       
0001AA 06F0                         151           BCTR R15,0               SUBTRACT ONE                         
0001AC 89F0 0002      00002         152           SLL  R15,2              MULTIPLY BY 4                         
0001B0 58FF C1D6      001D8         153           L    R15,ADDLOFF(R15)   GET LINE OFFSET

The ADDLINE routine adds a line of text to the console output buffer. We get the current count of lines displayed on the terminal and add one. Since the terminal has 24 lines and one line is used for input we can display 23 lines. If we exceed 23 lines we call the PAGEUP routine to scroll the data up. If the line count is less we use the line number as an index to look up the address of the text area for the line.

0001B4                              155 ADDL020  DS    0H 
0001B4 18E0                         156          LR    R14,0              TEXT LENGTH                           
0001B6 12EE                         157          LTR   R14,R14                                                  
0001B8 4780 C1C0      001C2         158          BZ    ADDL030                                                  
                                    159 *                                                                       
0001BC 06E0                         160          BCTR  R14,0             SUBTRACT FOR EX                        
0001BE 44E0 C1C6      001C8         161          EX    R14,ADDLMVC                                              
                                    162 *                                                                       
0001C2                              163 ADDL030  DS    0H                                                       
0001C2 58E0 C1CE      001D0         164          L     R14,ADDLINXT      RESTORE R14                            
0001C6 07FE                         165          BR    R14                                                      
                                    166 *                                                                       
0001C8 D200 F000 1000 00000 00000   167 ADDLMVC  MVC   0(1,R15),0(R1)

Now we use an executed MVC to copy the text into the terminal output buffer at the proper location.

0001D0 00000000                     168 ADDLINXT DC    F'0'                                                     
0001D4 00000000                     169 ADDLCT   DC    F'0'                                                     
0001D8 0000037E000003D2             170 ADDLOFF  DC    A(L01,L02,L03,L04,L05,L06,L07,L08,L09,L10)               
000200 000006C60000071A             171          DC    A(L11,L12,L13,L14,L15,L16,L17,L18,L19,L20)               
000228 00000A0E00000A62             172          DC    A(L21,L22,L23)

This is the save area for the return address (R14), the count of lines currently displayed on the terminal, and the offset lookup table.

000234                              175 PAGEUP   DS    0H                                                       
000234 D24E C37C C3D0 0037E 003D2   176          MVC   L01,L02                                                  
00023A D24E C3D0 C424 003D2 00426   177          MVC   L02,L03                                                  
000240 D24E C424 C478 00426 0047A   178          MVC   L03,L04                                                  
000246 D24E C478 C4CC 0047A 004CE   179          MVC   L04,L05                                                  
00024C D24E C4CC C520 004CE 00522   180          MVC   L05,L06                                                  
000252 D24E C520 C574 00522 00576   181          MVC   L06,L07                                                  
000258 D24E C574 C5C8 00576 005CA   182          MVC   L07,L08                                                  
00025E D24E C5C8 C61C 005CA 0061E   183          MVC   L08,L09                                                  
000264 D24E C61C C670 0061E 00672   184          MVC   L09,L10                                                  
00026A D24E C670 C6C4 00672 006C6   185          MVC   L10,L11                                                  
000270 D24E C6C4 C718 006C6 0071A   186          MVC   L11,L12                                                  
000276 D24E C718 C76C 0071A 0076E   187          MVC   L12,L13                                                  
00027C D24E C76C C7C0 0076E 007C2   188          MVC   L13,L14                                                  
000282 D24E C7C0 C814 007C2 00816   189          MVC   L14,L15                                                  
000288 D24E C814 C868 00816 0086A   190          MVC   L15,L16                                                  
00028E D24E C868 C8BC 0086A 008BE   191          MVC   L16,L17                                                  
000294 D24E C8BC C910 008BE 00912   192          MVC   L17,L18                                                  
00029A D24E C910 C964 00912 00966   193          MVC   L18,L19                                                  
0002A0 D24E C964 C9B8 00966 009BA   194          MVC   L19,L20                                                  
0002A6 D24E C9B8 CA0C 009BA 00A0E   195          MVC   L20,L21                                                  
0002AC D24E CA0C CA60 00A0E 00A62   196          MVC   L21,L22                                                  
0002B2 D24E CA60 CAB4 00A62 00AB6   197          MVC   L22,L23                                                  	
0002B8 9240 CAB4      00AB6         198          MVI   L23,C' '                                                 
0002BC D24D CAB5 CAB4 00AB7 00AB6   199          MVC   L23+1(L'L23-1),L23                                       
0002C2 07FE                         200          BR    R14

The PAGEUP routine moves every line up one position in the output buffer. It discards the contents of line one and clears out the contents of line 23.

0002D4                              211 WIOB     DS    0F                                                       
0002D4 00000000                     212          DC    XL4'00'             FLAGS, SENSE                         
0002D8 00000350                     213          DC    A(WECB)             ECB                                  
0002DC 0000000000000000             214          DC    XL8'00'             CSW                                  
0002E4 00000360                     215          DC    A(CCW)                                                   
0002E8 00000000                     216          DC    A(0)                UCB                                  
0002EC 00000000                     217          DC    XL4'00'                                                  
0002F0 00000000                     218          DC    XL4'00'                                                  
0002F4 0000000000000000             219          DC    
                                    220 *                                                                       
                                    221 *                                                                       
0002FC                              222 WIOB2    DS    0F                                                       
0002FC 00000000                     223          DC    XL4'00'             FLAGS, SENSE                         
000300 00000350                     224          DC    A(WECB)             ECB                                  
000304 0000000000000000             225          DC    XL8'00'             CSW                                  
00030C 00000368                     226          DC    A(CCW2)                                                  
000310 00000000                     227          DC    A(0)                UCB                                  
000314 00000000                     228          DC    XL4'00'                                                  
000318 00000000                     229          DC    XL4'00'                                                  
00031C 0000000000000000             230          DC    XL8'00'                                                  
                                    231 *                                                                       
                                    232 *                                                                       
000324                              233 RIOB     DS    0F                                                       
000324 00000000                     234          DC    XL4'00'             FLAGS, SENSE                         
000328 0000034C                     235          DC    A(RECB)             ECB                                  
00032C 0000000000000000             236          DC    XL8'00'             CSW                                  
000334 00000370                     237          DC    A(CCWR)             CCW                                  
000338 00000000                     238          DC    A(0)                UCB                                  
00033C 00000000                     239          DC    XL4'00'                                                  
000340 00000000                     240          DC    XL4'00'                                                  
000344 0000000000000000             241          DC    XL8'00'                                                  
                                    242 *                                                                       
                                    243 *                                                                       
00034C 00000000                     244 RECB     DC    F'0'                READ ECB                             
000350 00000000                     245 WECB     DC    F'0'                WRITE ECB                            
000354 00000000                     246 ZECB     DC    F'0'                INTERRUPT ECB                        
000358 0000000000000000             247 ECBLIST  DC    A(0,0)                                                   
                                    248 *                                                                       
000360                              249          DS    0D                                                       
000360 0500037820000799             250 CCW      DC    X'05',AL3(DATA),X'20',AL3(DATALEN)                       
000368 0100037820000799             251 CCW2     DC    X'01',AL3(DATA),X'20',AL3(DATALEN)                       
000370 06000B1120000080             252 CCWR     DC    X'06',AL3(BUFR),X'20',AL3(BUFRLEN)

These are the definitions for our I/O Blocks (IOB), our ECBs, the ECB List, and the CCW’s.

000378 C3                           254 DATA     DC    X'C3'                                                    
                                    255          $SBA  R=1,C=1                                                  
000379 11                           256+         DC    X'11'                                                    
00037A 4040                         257+         DC   X'4040'                                                   
00037C 1DE8                         258          DC    X'1DE8'                                                  
00037E 4040404040404040             259 L01      DC    CL79' '                                                  
                                    260          $SBA  R=2,C=1                                                  
0003CD 11                           261+         DC    X'11'                                                    
0003CE C150                         262+         DC   X'C150'                                                   
0003D0 1DE8                         263          DC    X'1DE8'                                                  
0003D2 4040404040404040             264 L02      DC    CL79' ' 
                                    265          $SBA  R=3,C=1                                                  
000421 11                           266+         DC    X'11'                                                    
000422 C260                         267+         DC   X'C260'                                                   
000424 1DE8                         268          DC    X'1DE8'                                                  
                     .
                     .
                     .
                                    365          $SBA  R=23,C=1                                                 
000AB1 11                           366+         DC    X'11'                                                    
000AB2 5B60                         367+         DC   X'5B60'                                                   
000AB4 1DE8                         368          DC    X'1DE8'                                                  
000AB6 4040404040404040             369 L23      DC    CL79' '                                                  
                                    370          $SBA  R=24,C=1                                                 
000B05 11                           371+         DC    X'11'                                                    
000B06 5CF0                         372+         DC   X'5CF0'                                                   
000B08 1DE8                         373          DC    X'1DE8'                                                  
000B0A 7E7E7E6E                     374          DC    C'===>' 
000B0E 1DC113                       375          DC    X'1DC113'                                                
                            00799   376 DATALEN  EQU   *-DATA 

This is the terminal output buffer. It begins with a Write Control Character (WCC). We then define the 23 lines of output. Each line begins with a Set Buffer Address (SBA) sequence to set the Row/Column position of the buffer. (Again refer to my 3270 page on my website for more on 3270 control codes) Next comes a Start Field command (x1D) that specified a display only field (xE8). This repeats for lines 2 through 23. Line 24 is the input line. It begins with a SBA and a Start Field defining an output only field. This will be our prompt ‘===>’ and is followed by another Start Field (x1D) indicating an input field (xC1) followed by an Insert Cursor (x13) command to position the cursor in the first position of the input field.

I use the $SBA macro to generate the SBA buffer codes. I could have calculated them by hand or looked them up in a table and hard coded them. I also could have used a subroutine to calculate them at execution time. Since I already had the $SBA macro I chose to use it.

000B11                              378 BUFR     DS    CL128              INPUT BUFFER                          
                            00080   379 BUFRLEN  EQU   *-BUFR                                                   

This is the input buffer. We will never have this much input since our input line is 74 characters long but it never hurts to have a buffer a little larger.

[Next – ]

S035WTO – Write To Operator Console

We will create the WTO SVC to queue messages to the console.  When the SVC is called Register 1 contains the address of the message in the bottom three bytes (24 bit address) and the high order byte contains the length of the message.  If the message length is greater than 79 bytes it will be truncated.  If it is less than 79 bytes it will be padded with blanks.

000000                               14 S035WTO  CSECT , 
000000 18CF                          15          LR    R12,R15            SET UP 
                            00000    16          USING S035WTO,R12              BASE REGISTER 
                                     17 *
000002 18B1                          18          LR    R11,R1             COPY PARM REGISTER
000004 58A0 0010      00010          19          L     R10,16             CVT
000008 58A0 A014      00014          20          L     R10,CVTCCB-@CVT(,R10)   CONSOLE COMMUNICATION BLOCK 
                            00000    21          USING @CCB,R10 
                                     22 * 
00000C 12AA                          23          LTR   R10,R10            DO WE HAVE A CONSOLE 
00000E 4780 C09A      0009A          24          BZ    WTO900              NO - GO ABEND 
                                     25 * 
000012                               26 WTO010   DS    0H 
000012 5890 A008      00008          27          L     R9,CCBFMQ          GET FREE BUFFER  
000016 1299                          28          LTR   R9,R9               AND FREE
000018 4780 C0A2      000A2          29          BZ    WTO910               NO - CAN'T DO 
                                     30 * 
00001C 5820 9000      00000          31          L     R2,0(,R9)          FWD CHAIN PTR 
000020 BA92 A008      00008          32          CS    R9,R2,CCBFMQ       REMOVE FROM CHAIN
000024 4770 C012      00012          33          BNZ   WTO010 
                                     34 * 
                            00000    35          USING @CMB,R9

First we locate the CCB and take a message buffer off the free buffer queue.  If no free buffers are available we abend.

000028 D703 9000 9000 00000 00000    37          XC    CMBNEXT,CMBNEXT    CLEAR FWD CHAIN PTR
00002E 181B                          38          LR    R1,R11             COPY
000030 182B                          39          LR    R2,R11                 PARM VALUE
000032 8810 0018      00018          40          SRL   R1,24              SHIFT DOWN TO GET LENGTH
000036 1211                          41          LTR   R1,R1              CHECK LENGTH
000038 4780 C0AA      000AA          42          BZ    WTO920               ERROR IF ZERO
                                     43 *
00003C 5910 C0C0      000C0          44          C     R1,=F'79'          CHECK MAX LENGTH 
000040 4740 C048      00048          45          BL    WTO020
                                     46 *
000044 4110 004F      0004F          47          LA    R1,79
000048                               48 WTO020   DS    0H 
000048 0610                          49          BCTR  R1,0               SUBTRACT ONE FOR EX
00004A 9240 9004      00004          50          MVI   CMBTEXT,C' '       CLEAR OUT MESSAGE BUFFER
00004E D24D 9005 9004 00005 00004    51          MVC   CMBTEXT+1(L'CMBTEXT-1),CMBTEXT
000054 4410 C094      00094          52          EX    R1,WTOEX           COPY IN TEXT

The message text is copied into the free buffer.

000058                               54 WTO030   DS    0H 
000058 4120 A004      00004          55          LA    R2,CCBMBQ          CONSOLE MESSAGE QUEUE
00005C                               56 WTO040   DS    0H 
00005C 5810 2000      00000          57          L     R1,0(,R2)          NEXT ELEMENT
000060 1211                          58          LTR   R1,R1
000062 4780 C06C      0006C          59          BZ    WTO050             BRANCH IF LAST ELEMENT 
                                     60 * 
000066 1821                          61          LR    R2,R1              NEXT ELEMENT 
000068 47F0 C05C      0005C          62          B     WTO040
                                     63 * 
00006C                               65 WTO050   DS    0H 
00006C BA19 2000      00000          66          CS    R1,R9,0(R2)        ADD TO CHAIN
000070 4770 C058      00058          67          BNZ   WTO030             GO TRY AGAIN

The free buffer is placed on the message queue at the end of the chain.

000074 9140 A00C      0000C          69          TM    CCBECB,X'40'       ECB ALREADY POSTED ?
000078 4710 C08A      0008A          70          BO    WTO060               YES - SKIP POST
                                     71 * 
00007C 4110 A00C      0000C          72          LA    R1,CCBECB
                                     73          @CALL POST
000080 58F0 0014      00014          74+         L     R15,20             MVT ADDRESS 
000084 58F0 F028      00028          75+         L     R15,MVTPOST-@MVT(,R15) ROUTINE ADDRESS 
000088 05EF                          76+         BALR  R14,R15
                                     77 * 
00008A                               78 WTO060   DS    0H 
                                     79          @CALL EXIT 
00008A 58F0 0014      00014          80+         L     R15,20             MVT ADDRESS
00008E 58F0 F024      00024          81+         L     R15,MVTEXIT-@MVT(,R15) ROUTINE ADDRESS
000092 05EF                          82+         BALR  R14,R15 
                                     84 * 
000094 D200 9004 B000 00004 00000    85 WTOEX    MVC   CMBTEXT(0),0(R11)  EXECUTED MOVE

If the ECB is already posted we can go ahead and exit now.  If the ECB is not posted we call the Post routine to post it and then we exit the SVC.

00009A                               88 WTO900   DS    0H 
00009A 4110 0123      00123          89          LA    R1,X'123'          NO CONSOLE DEFINED
00009E 47F0 C0B2      000B2          90          B     WTO999
                                     91 *
0000A2                               92 WTO910   DS    0H 
0000A2 4110 0223      00223          93          LA    R1,X'223'
0000A6 47F0 C0B2      000B2          94          B     WTO999 
                                     95 *
0000AA                               96 WTO920   DS    0H
0000AA 4110 0323      00323          97          LA    R1,X'323'
0000AE 47F0 C0B2      000B2          98          B     WTO999
                                     99 * 
0000B2                              100 WTO999   DS    0H 
                                    101          @CALL ABEND 
0000B2 58F0 0014      00014         102+         L     R15,20             MVT ADDRESS
0000B6 58F0 F020      00020         103+         L     R15,MVTABEND-@MVT(,R15) ROUTINE ADDRESS
0000BA 05EF                         104+         BALR  R14,R15

Here are the various abend codes if something goes wrong.

[Next – ]

Operator Console

Now that we have the ability to perform I/O we can think about communicating with a device.  We will start with the beginnings of an operator console.  We will use a locally attached 3270 device as the our console.  We will need to have a UCB defined for the device.

To begin we will start with providing the ability to write to the console.  To do this we need to define two new control blocks.  The first is the Console Communication Block (CCB) and the other is the Console Message Buffer (CMB).

The CCB defines a console and is the anchor for the console buffers.

@CCB     DSECT ,                  CONSOLE COMMUNICATIONS BUFFER 
CCBNEXT  DS    A                  NEXT CONSOLE                  
CCBMBQ   DS    A                  MESSAGE BUFFER QUEUE          
CCBFMQ   DS    A                  FREE MESSAGE QUEUE            
CCBECB   DS    F                  MESSAGE AVAILABLE ECB         
CCBDEV   DS    XL2                CONSOLE DEVICE ADDRESS        
CCBQLEN  DS    XL2                QUEUE LENGTH

It begins with a pointer to the next CCB but for now we will only support one console. CCBMBQ points to Console Message Buffers (CMB’s) that are queued to the console. The CMB’s contain the message text to be written to the console. CCBFMQ is a queue of free message buffers for the console. When a message is queued to the console a free buffer is removed from the free buffer queue and added to the message buffer queue. CCBECB is an ECB that will be posted when a message is placed on the message queue. CCBDEV is the device address of the console. CCBQLEN is the total number of message buffers allocated to the console.

@CMB     DSECT ,                  CONSOLE COMMUNICATIONS BUFFER 
CMBNEXT  DS    A                  NEXT BUFFER                   
CMBTEXT  DS    CL79               TEXT                          
CMBFLAG  DS    X                  FLAG

The Console Message Buffer consists of a pointer to the next buffer on the queue. We allow for a message length of 79 bytes.

We define the CCB and CMB’s in module TXXCCB.  We will define the message buffers in this module.

000000                                1 TXXCCB   CSECT ,
                                      2          @CCB  DSECT=N,DEV=010.
000000                                3+CCB010   DS    0F 
000000 00000000                       4+         DC    A(0)
000004 00000000                       5+         DC    A(0)
000008 00000014                       6+         DC    A(@M0101)
00000C 00000000                       7+         DC    F'0'
000010 0010                           8+         DC    XL2'010'
000012 0040                           9+         DC    AL2(64)
                                     10+*
000014 0000006840404040              12+@M0101   DC     A(@M0102),CL79' ',X'00'
000068 000000BC40404040              13+@M0102   DC     A(@M0103),CL79' ',X'00'
                              .
                              .
                              .
00146C 000014C040404040              74+@M01063  DC     A(@M01064),CL79' ',X'00' 
0014C0 0000000040404040              75+@M01064  DC     A(@M01065),CL79' ',X'00'
                            00000    76+@M01065  EQU    0
                                     77          END   ,

Here is the assembly listing for TXXCCB which is the expansion of the @CCB macro.  We define 64 message buffers and they are all placed on the free buffer queue.

[Next – ]

SVC 0 – EXCP

EXCP provides a SVC interface to schedule I/O requests.  The IOB is the parameter list for the SVC.

000000                                6 S000EXCP CSECT ,
000000 18CF                           7          LR    R12,R15            SET BASE
                            00000     8          USING S000EXCP,R12               REGISTER
                                      9 *
000002 18A1                          10          LR    R10,R1             COPY IOB POINTER
                            00000    11          USING @IOB,R10
                                     12 *
000004 9180 A014      00014          13          TM    IOBFLG4,IOBFUCBP   NO DCB, IOBDCBPT PTS TO UCB
000008 47E0 C0FC      000FC          14          BNO   EXCP990              NO - CAN'T DEAL WITH IT NOW
                                     15 * 
00000C 1F44                          16          SLR   R4,R4
00000E BF47 A015      00015          17          ICM   R4,B'0111',IOBDCBPT   GET UCB ADDRESS
                            00000    18          USING @UCB,R4

The IOB address is saved into Register 10.  For now since we haven’t defined the Data Control Block (DCB) we only accept requests passing the UCB address instead of the DCB address.

000012 5810 0010      00010          20          L     R1,16              CVT
000016 5810 1008      00008          21          L     R1,CVTUCB-@CVT(,R1)   UCB TABLE
00001A                               22 EXCP010  DS    0H 
00001A D501 C15C 4004 0015C 00004    23          CLC   =X'FFFF',UCBCUA    END OF THE LINE 
000020 4780 C0DC      000DC          24          BE    EXCP900            BAD UCB ADDRESS
                                     25 * 
000024 1914                          26          CR    R1,R4              UCB WE ARE LOOKING FOR
000026 4780 C032      00032          27          BE    EXCP020              YES - WE FOUND IT
                                     28 *
00002A 5810 1000      00000          29          L     R1,0(,R1)          NEXT UCB
00002E 47F0 C01A      0001A          30          B     EXCP010              LOOP BACK

The UCB address is validated.

000032                               33 EXCP020  DS    0H
000032 9180 4007      00007          34          TM    UCBSTAT,UCBSONLN   IS UCB ONLINE
000036 47E0 C0E4      000E4          35          BNO   EXCP910              NO - CAN'T DO I/O
                                     36 *
00003A 4110 006C      0006C          37          LA    R1,IORBLEN         LENGTH OF IORB
00003E BF18 C15E      0015E          38          ICM   R1,B'1000',=AL1(128)  SP ID
                                     39          @CALL GETMAIN
000042 58F0 0014      00014          40+         L     R15,20             MVT ADDRESS
000046 58F0 F018      00018          41+         L     R15,MVTGMAIN-@MVT(,R15) ROUTINE ADDRESS
00004A 05EF                          42+         BALR  R14,R15
00004C 12FF                          43          LTR   R15,R15            CHECK FOR STORAGE RETURNED
00004E 4770 C0EC      000EC          44          BNZ   EXCP920
                                     45 * 
000052 18B1                          46          LR    R11,R1             IORB ADDRESS
                            00000    47          USING @IORB,R11
                                     48 *
000054 4110 0060      00060          49          LA    R1,TCBLEN          LENGTH OF TCB
000058 BF18 C15E      0015E          50          ICM   R1,B'1000',=AL1(128)   SP ID
                                     51          @CALL GETMAIN
00005C 58F0 0014      00014          52+         L     R15,20             MVT ADDRESS
000060 58F0 F018      00018          53+         L     R15,MVTGMAIN-@MVT(,R15) ROUTINE ADDRESS
000064 05EF                          54+         BALR  R14,R15
000066 12FF                          55          LTR   R15,R15
000068 4770 C0EC      000EC          56          BNZ   EXCP920
                                     57 *
00006C 1891                          58          LR    R9,R1               TCB ADDRESS
                            00000    59          USING @TCB,R9

If the UCB is offline the request is rejected.  Now we allocate storage for a IORB that will be passed to the Schedule I/O routine.  We also allocate storage for a TCB that will be added to the dispatch queue when the I/O request has been completed by the device.

00006E D76B B000 B000 00000 00000    61          XC    0(IORBLEN,R11),0(R11)   CLEAR IORB STORAGE
000074 5040 B004      00004          62          ST    R4,IORBUCB          UCB ADDRESS
000078 50A0 B008      00008          63          ST    R10,IORBIOB         IOB ADDRESS
00007C 1F11                          64          SLR   R1,R1
00007E BF17 A011      00011          65          ICM   R1,B'0111',IOBSTART GET CCW ADDRESS
000082 5010 B00C      0000C          66          ST    R1,IORBCCW          CCW ADDRESS
000086 5090 B010      00010          67          ST    R9,IORBTCB          TCB ADDRESS
00008A 4110 C0CE      000CE          68          LA    R1,EXCPDIE          DIE ROUTINE
00008E 5010 B014      00014          69          ST    R1,IORBDIE         DIE ROUTINE ADDRESS

Now we set up the IORB with the necessary pointers.  The UCB, CCW and IOB addresses come from the IOB.

000092 D75F 9000 9000 00000 00000    71          XC    0(TCBLEN,R9),0(R9) CLEAR TCB
000098 D207 9018 C158 00018 00158    72          MVC   TCBPSW,=X'FF000000'
00009E 4110 C10E      0010E          73          LA    R1,BACKEND         BACK END EXCP ROUTINE
0000A2 5010 901C      0001C          74          ST    R1,TCBPSW+4
0000A6 5010 905C      0005C          75          ST    R1,TCBREG15        SET EP IN R15
0000AA 50B0 9024      00024          76          ST    R11,TCBREG1        SAVE IORB ADDRESS IN R1
0000AE 4110 C0D0      000D0          77          LA    R1,EXCPRET         RETURN SVC
0000B2 5010 9058      00058          78          ST    R1,TCBREG14        SAVE AS R14

Now we set up the TCB to be ready for dispatch.  We build a PSW with interrupts enabled.  The instruction address in the PSW points to our EXCP back end processing routine.  We also place that address into the Register 15 area and we place the IORB address in the Register 1 area.  When the TCB is dispatched execution will begin at our EXCP back end routine, R15 will contain the address of the routine and R1 will contain the address of the IORB.  The TCB storage will be freed when the back end routine exits via SVC 3.

0000B6 4110 B000      00000          80          LA    R1,@IORB
                                     81          @CALL STARTIO
0000BA 58F0 0014      00014          82+         L     R15,20             MVT ADDRESS
0000BE 58F0 F030      00030          83+         L     R15,MVTSIO-@MVT(,R15) ROUTINE ADDRESS
0000C2 05EF                          84+         BALR  R14,R15
                                     85 *
                                     86          @CALL EXIT
0000C4 58F0 0014      00014          87+         L     R15,20             MVT ADDRESS
0000C8 58F0 F024      00024          88+         L     R15,MVTEXIT-@MVT(,R15) ROUTINE ADDRESS
0000CC 05EF                          89+         BALR  R14,R15

Now we call the Schedule I/O routine passing it the address of our IORB.  Once the I/O is scheduled we can exit the SVC routine.

0000CE                               92 EXCPDIE  DS    0H
0000CE 07FE                          93          BR    R14

Our Disabled Interrupt Exit simply returns back to the caller.

0000D0                               96 EXCPRET  DS    0H
                                     97          @CALL EXIT
0000D0 58F0 0014      00014          98+         L     R15,20             MVT ADDRESS
0000D4 58F0 F024      00024          99+         L     R15,MVTEXIT-@MVT(,R15) ROUTINE ADDRESS
0000D8 05EF                         100+         BALR  R14,R15
0000DA 0A03                         101          SVC   3

Here we exit via the Exit routine.

0000DC                              104 EXCP900  DS    0H
0000DC 4110 0100      00100         105          LA    R1,X'100'          ABEND CODE
0000E0 47F0 C104      00104         106          B     EXCP999
                                    107 *
0000E4                              108 EXCP910  DS    0H
0000E4 4110 0200      00200         109          LA    R1,X'200'
0000E8 47F0 C104      00104         110          B     EXCP999
                                    111 *
0000EC                              112 EXCP920  DS    0H
0000EC 4110 0300      00300         113          LA    R1,X'300'
0000F0 47F0 C104      00104         114          B     EXCP999 
                                    115 *
0000F4                              116 EXCP930  DS    0H
0000F4 4110 0900      00900         117          LA    R1,X'900' 
0000F8 47F0 C104      00104         118          B     EXCP999
                                    119 *
0000FC                              120 EXCP990  DS    0H
0000FC 4110 0F00      00F00         121          LA    R1,X'F00'
000100 47F0 C104      00104         122          B     EXCP999
                                    123 *
000104                              124 EXCP999  DS    0H 
                                    125          @CALL ABEND 
000104 58F0 0014      00014         126+         L     R15,20             MVT ADDRESS
000108 58F0 F020      00020         127+         L     R15,MVTABEND-@MVT(,R15) ROUTINE ADDRESS 
00010C 05EF                         128+         BALR  R14,R15

These are the various Abend exit points.  An Abend code is placed into Register 1 and the Abend routine is called.  The Abend routine does not return back to the caller.

                                    131          DROP   R4,R9,R10,R11,R12 
                                    133 *
00010E                              134 BACKEND  DS    0H 
00010E 18CF                         135          LR    R12,R15            ESTABLISH BASE
                            0010E   136          USING BACKEND,R12                      REGISTER
                                    137 *
000110 18B1                         138          LR    R11,R1             IORB ADDRESS 
                            00000   139          USING @IORB,R11
                                    140 *
000112 58A0 B008      00008         141          L     R10,IORBIOB        IOB ADDRESS 
                            00000   142          USING @IOB,R10

The EXCP BACKEND routine is executed when the TCB we passed to the Schedule I/O routine is dispatched after the I/O is completed by the device.  The I/O Interruption routine places the TCB on the Dispatcher queue.

000116 D200 A002 B022 00002 00022   144          MVC   IOBSENS0,IORBSEN0  COPY 
00011C D200 A003 B023 00003 00023   145          MVC   IOBSENS1,IORBSEN1      SENSE BYTES
000122 D206 A009 B025 00009 00025   146          MVC   IOBCSW,IORBCSW+1   COPY CSW
000128 D200 A004 B021 00004 00021   147          MVC   IOBECBCC,IORBECBC  COPY I/O COMPLETION CODE

We start by copying status information from the IORB into the IOB.

00012E 1F11                         149          SLR   R1,R1
000130 BF17 A005      00005         150          ICM   R1,B'0111',IOBECBPT  GET ECB ADDRESS
                                    151          @CALL POST 
000134 58F0 0014      00014         152+         L     R15,20             MVT ADDRESS
000138 58F0 F028      00028         153+         L     R15,MVTPOST-@MVT(,R15) ROUTINE ADDRESS
00013C 05EF                         154+         BALR  R14,R15
                                    155 *
00013E 4100 B000      00000         156          LA    R0,@IORB           POINT TO IORB
000142 BF18 C050      0015E         157          ICM   R1,B'1000',=AL1(128)   SP ID 
                                    158          @CALL FREEMAIN
000146 58F0 0014      00014         159+         L     R15,20             MVT ADDRESS 
00014A 58F0 F01C      0001C         160+         L     R15,MVTFMAIN-@MVT(,R15) ROUTINE ADDRESS
00014E 05EF                         161+         BALR  R14,R15 
                                    162 * 
000150 0A03                         163          SVC   3                  EXIT

We Post the ECB pointed to by the IOB before we free the IORB storage and exit via SVC 3.

[Next – ]

I/O Block – @IOB

The IOB is used the parameter block for an EXCP request.  Here it is modeled after the MVS control block.

@IOB     DSECT ,                                           
IOBFLG1  DS    X                  FLAG BYTE                
IOBFLG2  DS    X                  FLAG BYTE                
IOBSENS0 DS    X                  SENSE BYTE 0             
IOBSENS1 DS    X                  SENSE BYTE 1             
IOBECBCC DS    X                  COMPLETION CODE          
IOBECBPT DS    AL3                ECB ADDRESS              
IOBFLG3  DS    X                  FLAG BYTE                
IOBCSW   DS    XL7                CHANNEL STATUS WORD      
IOBSIOCC DS    X                  SIO CONDITION CODE       
IOBSTART DS    AL3                CCW ADDRESS              
IOBFLG4  DS    X                  FLAG BYTE                
IOBFUCBP EQU   X'80'              DCB ADDRESS POINTS TO UCB
*                                                          
IOBDCBPT DS    AL3                DCB ADDRESS              
IOBRESTR DS    XL4                                         
IOBINCAM DS    XL2                                         
IOBERRCT DS    XL2                                         
IOBSEEK  DS    XL8                SEEK ADDRESS             
IOBLEN   EQU   *-@IOB

The ECB address points to a ECB that will be posted when the EXCP request is complete.  If the IOBFUCBP flag is set in IOBFLG4 then IOBDCBPT is the address of a UCB instead of the address of a DCB.

[Next – ]

TXXIOIR – I/O Interrupt Routine

The I/O Interrupt Routine is called from the low-level interrupt handler in the Nucleus module.  The low-level handler saves the registers and interrupt PSW into the current TCB before transferring control to the IOIR.

000000                                5 TXXIOIR  CSECT ,
000000 18CF                           6          LR    R12,R15            SET BASE
                            00000     7          USING TXXIOIR,R12                REGISTER
                                      8 * 
                            00000     9          USING @TCB,R4
                                     10 *
000002 1F11                          11          SLR   R1,R1              ZERO R1
000004 BF13 401A      0001A          12          ICM   R1,B'0011',TCBPSW+2   GET UNIT CUA FOR INTERRUPT
                                     13 *

The address of the device generating the interrupt is obtained from the interrupt PSW.

000008 5850 0010      00010          14          L     R5,16              CVT ADDRESS
00000C 5855 0008      00008          15          L     R5,CVTUCB-@CVT(R5)  POINT TO UCB LIST
                            00000    16          USING @UCB,R5
000010                               18 UCB010   DS    0H 
000010 D501 C11C 5004 0011C 00004    19          CLC   =X'FFFF',UCBCUA     END OF UCB LIST
000016 4780 C110      00110          20          BE    IOHND060              UNKNOWN DEVICE
                                     21 *
00001A BD13 5004      00004          22          CLM   R1,B'0011',UCBCUA   UCB FOR INTERRUPT 
00001E 4780 C02A      0002A          23          BE    UCB020                YES - CONTINUE
                                     24 * 
000022 5850 5000      00000          25          L     R5,UCBNEXT          POINT TO NEXT UCB
000026 47F0 C010      00010          26          B     UCB010              LOOP BACK

The UCB table is searched to locate the UCB associated with the interrupt.

00002A                               29 UCB020   DS    0H 
00002A D207 501C 0040 0001C 00040    30          MVC   UCBCSW,CSW-@LOWCORE SAVE CSW 
000030 58A0 500C      0000C          31          L     R10,UCBIOB          POINT TO IORB
000034 12AA                          32          LTR   R10,10               IS THERE ONE 
000036 4780 C0F6      000F6          33          BZ    IOHND040               NO - BRANCH 
                                     34 * 
                            00000    35          USING @IORB,R10
                                     36 *
00003A 9180 A020      00020          37          TM    IORBFLAG,IORBFSEN   RESCHEDUE TO GET SENSE INFO
00003E 4710 C0AC      000AC          38          BO    IOHND010              YES - SKIP NORMAL PROCESSING
                                     39 *
000042 D207 A024 501C 00024 0001C    40          MVC   IORBCSW,UCBCSW      COPY CSW INTO IORB
000048 D500 C11E 5021 0011E 00021    41          CLC   =X'80',UCBCSW+5     PCI ?
00004E 4710 C09E      0009E          42          BO    PCI010                YES - BRANCH
                                     43 *  
000052 927F A021      00021          44          MVI  IORBECBC,X'7F'       PRIME I/O COMPLETION CODE
000056 D503 C118 5020 00118 00020    45          CLC  =C'0C00',UCBCSW+4    DEV END + CHAN END
00005C 4780 C0AC      000AC          46          BE   IOHND010                YES - GOOD COMPLETION
                                     47 * 
000060 9241 A021      00021          48          MVI  IORBECBC,X'41'       ERROR COMPLETION CODE 
000064 9102 A024      00024          49          TM   IORBCSW,X'02'        UNIT CHECK ? 
000068 47E0 C0AC      000AC          50          BNO  IOHND010               NO - SKIP SENSE

When the UCB is located the CSW is saved into the UCB.  A check is done to see if there is an outstanding I/O request for this device.  If there is no outstanding request we branch to process an unsolicited interrupt.  We then check to see is this is a reschedule of the IORB to get sense data.  If it is a reschedule we can skip the normal processing.

Normal processing starts by copying the CSW into the IORB.  A check is then done to see if the interrupt is a result of a Program Controlled Interrupt flag set in a CSW.  If so we branch to call the PCI routine specified in the IORB.  Next we check for good completion indicated by Device End + Channel End in the CSW.  If not we set the ECB completion code to x’41’ and then examine the CSW to see if a Unit Check is indicated.  If so we will issue a sense request to the device.

00006C 9680 A020      00020          52          OI   IORBFLAG,IORBFSEN    INDICATE SENSE IN PROGRESS
                                     53 *
000070 4110 A022      00022          54          LA   R1,IORBSEN0          SENSE AREA
000074 5010 A02C      0002C          55          ST   R1,IORBWORK           BUILD SENSE CCW
000078 9204 A02C      0002C          56          MVI  IORBWORK,X'04'        CMD=SENSE 
00007C 4110 0002      00002          57          LA   R1,2                  LENGTH=2 
000080 5010 A030      00030          58          ST   R1,IORBWORK+4
000084 9220 A030      00030          59          MVI  IORBWORK+4,X'20'      SLI
                                     60 * 
000088 4110 A02C      0002C          61          LA   R1,IORBWORK           POINT TO SENSE CCW 
00008C 5010 0048      00048          62          ST   R1,CAW-@LOWCORE       SAVE INTO CAW 
000090 1F11                          63          SLR  R1,R1                 CLEAR R1
000092 BF13 5004      00004          64          ICM   R1,B'0011',UCBCUA    GET UNIT ADDRESS 
000096 9C00 1000      00000          65          SIO   0(R1)
00009A 47F0 C106      00106          66          B     IOHND050             AND EXIT

We begin by setting the bit in the IORB to indicate sense in progress.  We then build a Sense CCW in the work area and then issue a SIO to the device to obtain the first two bytes of sense data.

00009E                               69 PCI010   DS    0H
00009E 4110 A000      00000          70          LA    R1,@IORB             POINT TO IORB
0000A2 58F0 A018      00018          71          L     R15,IORBPCI          GET PCI ROUTINE ADDR
0000A6 05EF                          72          BALR  R14,R15              CALL IT
0000A8 47F0 C106      00106          73          B     IOHND050             AND EXIT

Here we handle a PCI interrupt by calling the PCI routine specified in the IORB.

0000AC                               76 IOHND010 DS    0H
0000AC 4110 A000      00000          77          LA    R1,@IORB           POINT TO IORB
0000B0 58F0 A014      00014          78          L     R15,IORBDIE        DIE ROUTINE ADDRESS
0000B4 05EF                          79          BALR  R14,R15            CALL DIE
                                     80 *
0000B6 18BA                          81          LR    R11,R10            SAVE IORB ADDRESS 
                                     82 *
0000B8                               83 IOHND020 DS    0H
0000B8 58A0 A000      00000          84          L     R10,IORBNEXT       POINT TO NEXT IORB ON CHAIN
0000BC 50A0 500C      0000C          85          ST    R10,UCBIOB         UPDATE IOB CHAIN OFF UCB
                                     86 *
0000C0 12AA                          87          LTR   R10,R10            ANOTHER IORB TO SCHEDULE
0000C2 4780 C0D8      000D8          88          BZ    IOHND030             NO - BRANCH
                                     89 * 
0000C6 5810 A00C      0000C          90          L     R1,IORBCCW         GET CCW
0000CA 5010 0048      00048          91          ST    R1,CAW-@LOWCORE    SAVE INTO CAW
0000CE 1F11                          92          SLR   R1,R1              ZERO R1
0000D0 BF13 5004      00004          93          ICM   R1,B'0011',UCBCUA  GET DEVICE ADDRESS 
0000D4 9C00 1000      00000          94          SIO   0(R1)

Now we are ready to call the DIE routine specified in the IORB.  We can then remove the IORB from the UCB queue and check to see if there is another IORB to process.  If so we issue the SIO for the request.

0000D8                               96 IOHND030 DS    0H 
0000D8 18AB                          97          LR    R10,R11            RESTORE IORB ADDRESS
0000DA 5840 A010      00010          98          L     R4,IORBTCB         GET TCB TO DISPATCH
0000DE 5810 0010      00010          99          L     R1,16              CVT
0000E2 5810 1004      00004         100          L     R1,CVTTASKQ-@CVT(,R1) PONIT TO TCB QUEUE = A(WAIT TCB)
0000E6 5820 1000      00000         101          L     R2,TCBNEXT-@TCB(,R1)  NEXT TCB 
0000EA 5020 4000      00000         102          ST    R2,0(,R4)          CHAIN ON
0000EE 5040 1000      00000         103          ST    R4,0(,R1)                  NEW TCB
0000F2 47F0 C106      00106         104          B     IOHND050           AND EXIT

Final processing consists of adding the TCB specified in the IORB to the Task Queue.  We chain it on immediately after the Wait Task TCB.

0000F6                              107 IOHND040 DS    0H 
0000F6 58F0 5010      00010         108          L     R15,UCBINTR        UCB INTERRUPT ADDRESS
0000FA 12FF                         109          LTR   R15,R15            BRANCH 
0000FC 4780 C106      00106         110          BZ    IOHND050                 IF NONE
                                    111 *
000100 4110 5000      00000         112          LA    R1,@UCB            POINT TO UCB
000104 05EF                         113          BALR  R14,R15            CALL INTERRUPT ROUTINE

Here we handle an unsolicited interrupt.  If an interrupt routine is specified in the UCB we branch to it passing the address of the UCB in Register 1.

000106                              116 IOHND050 DS    0H
000106 58F0 0014      00014         117          L     R15,20                  MVT 
00010A 58F0 F004      00004         118          L     R15,MVTDISP-@MVT(,R15)     DISPATCHER
00010E 07FF                         119          BR    R15
                                    120 *
000110                              122 IOHND060 DS    0H
000110 47F0 C106      00106         123          B     IOHND050

Eventually we exit back to the Dispatcher.

[Next – ]

TXXSIO – Schedule I/O Request

This is the routine that schedules I/O requests.  The request is passed in the form of an IORB.  If the device is available a Start I/O command is issued.  If the device is busy the IORB is placed on the UCB IORB queue to be scheduled when the device becomes available.

000000                               15 TXXSIO   CSECT , 
000000 900F 102C      0002C          16          STM   R0,R15,IORBWORK-@IORB(R1)   SAVE CALLER'S REGS
000004 18CF                          17          LR    R12,R15            R12 IS BASE REG
                            00000    18          USING TXXSIO,R12
                                     19 * 
000006 18B1                          20          LR    R11,R1             COPY IORB ADDRESS
                            00000    21          USING @IORB,R11
                                     22 *  
000008 5840 B004      00004          23          L     R4,IORBUCB         GET UCB ADDRESS 
                            00000    24          USING @UCB,R4 
                                     25 * 
00000C 9101 B020      00020          26          TM    IORBFLAG,IORBOFLN  SECHEDULE TO OFFLINE DEVICE 
000010 4710 C01C      0001C          27          BO    SIO005               YES - SKIP UCB CHECK
                                     28 *
000014 9180 4007      00007          29          TM    UCBSTAT,UCBSONLN   DEVICE ONLINE?  
000018 47E0 C070      00070          30          BNO   SIO030               NO - CAN'T SCHEDULE I/O 
                                     31 *
00001C                               32 SIO005   DS    0H

To begin the IORB address passed in Register 1 is saved into R11.  A check is made to see if this I/O request should be scheduled to an offline UCB.  If not a check is made to see if the device is online.  If the device is offline the I/O request is rejected.

00001C D703 B000 B000 00000 00000    33          XC    IORBNEXT,IORBNEXT  ZERO FWD POINTER
                                     34 *
000022 5820 400C      0000C          35          L     R2,UCBIOB          IORB CHAIN HEAD 
000026 1222                          36          LTR   R2,R2              ANY QUEUED I/O REQUESTS
000028 4780 C042      00042          37          BZ    SIO020               NO - GO ISSUE I/O 
                                     38 *
00002C 1832                          39          LR    R3,R2              FIRST IORB
00002E                               40 SIO010   DS    0H
00002E 1823                          41          LR    R2,R3              COPY IORB ADDRESS
000030 5830 2000      00000          42          L     R3,IORBNEXT-@IORB(,R2)   NEXT IORB ON CHAIN 
000034 1233                          43          LTR   R3,R3              END OF CHAIN 
000036 4770 C02E      0002E          44          BNZ   SIO010               NO - LOOP BACK
                                     45 *
00003A 50B0 2000      00000          46          ST    R11,0(,R2)         ADD TO CHAIN
00003E 47F0 C068      00068          47          B     SIOXT                AND EXIT

Next a check is done to see if any I/O requests are currently queued to the UCB.  If the queue is empty we branch to issue the SIO to the device.  If there are any IORB’s on the UCB queue this IORB is placed at the end of the list.

000042                               50 SIO020   DS    0H
000042 50B0 400C      0000C          51          ST    R11,UCBIOB         CHAIN IORB ONTO UCB
000046 5810 B00C      0000C          52          L     R1,IORBCCW         CCW ADDRESS
00004A 5010 0048      00048          53          ST    R1,CAW-@LOWCORE    SAVE INTO CAW
00004E 1F11                          54          SLR   R1,R1 
000050 BF13 4004      00004          55          ICM   R1,B'0011',UCBCUA  DEVICE ADDRESS
000054 9C00 1000      00000          56          SIO   0(R1)              START I/O 
000058 4780 C068      00068          57          BC    8,SIOXT             CC=0 SIO ACCEPTED 
00005C 4740 C07A      0007A          58          BC    4,SIO040            CC=1 CCW STORED
000060 4720 C092      00092          59          BC    2,SIO060            CC=2 BUSY 
000064 4710 C08A      0008A          60          BC    1,SIO050            CC=1 NOT-OPERATIONAL

The IORB is queued to the UCB as the active request (top IORB).  The CCW address from the IORB is copied to the CAW and a SIO is issued to the device.  A return code is then set depending on the Condition Code set by the SIO.

000068                               62 SIOXT    DS    0H
000068 980E B02C      0002C          63          LM    R0,R14,IORBWORK    RESTORE CALLER'S REGS
00006C 1FFF                          64          SLR   R15,R15            SET RC
00006E 07FE                          65          BR    R14                RETURN TO CALLER
                                     66 *
000070                               68 SIO030   DS   0H 
000070 980E B02C      0002C          69          LM   R0,R14,IORBWORK     RESTORE CALLER'S REGS
000074 41F0 000C      0000C          70          LA   R15,12              DEVICE OFFLINE
000078 07FE                          71          BR   R14                 RETURN TO CALLER 
                                     72 *
00007A                               74 SIO040   DS   0H 
00007A D207 B024 0040 00024 00040    75          MVC  IORBCSW,CSW-@LOWCORE  COPY CSW 
000080 980E B02C      0002C          76          LM   R0,R14,IORBWORK     RESTORE CALLER'S REGS
000084 41F0 0008      00008          77          LA   R15,8               CSW STORED
000088 07FE                          78          BR   R14                 RETURN TO CALLER
                                     79 * 
00008A                               81 SIO050   DS   0H 
00008A 947F 4007      00007          82          NI   UCBSTAT,X'FF'-UCBSONLN  TAKE UCB OFFLINE 
00008E 47F0 C070      00070          83          B    SIO030              DEVICE OFFLINE 
                                     84 *
000092                               86 SIO060   DS   0H
000092 980E B02C      0002C          87          LM   R0,R14,IORBWORK     RESTORE CALLER'S REGS
000096 41F0 0010      00010          88          LA   R15,16              DEVICE BUSY
00009A 07FE                          89          BR   R14                 RETURN TO CALLER

Here the caller’s registers are restored and and the appropriate return code is set in Register 15 based on the SIO condition code.  If Condition Code 4 was returned indicating a CSW was stored the CSW is copied into the IORB.  If Condition Code 1was returned indicating the device is not operational the device is marked offline.

[Next – ]

I/O Request Block – @IORB

The I/O Request Block (IORB) defines an I/O request.  The IORB may be for an active I/O request being processed by the device, it may be a queued request waiting on a device or it may be a completed I/O request.

An IORB is scheduled to a device by the TXXSIO (Schedule I/O) module.  The UCB has a pointer to a queue of IORB requests.  The top IORB on the UCB queue is the active request.

@IORB    DSECT ,                                                     
IORBNEXT DS    A                  NEXT IORB ON CHAIN                 
IORBUCB  DS    A                  UCB ADDRESS                        
IORBIOB  DS    A                  IOB ADDRESS                        
IORBCCW  DS    A                  CCW ADDRESS                        
IORBTCB  DS    A                  TCB TO BEE DISPATCHED ON COMPLETION
IORBDIE  DS    A                  DISABLED EXIT ROUTINE              
IORBPCI  DS    A                  PCI EXIT ROUTINE                   
IORBERR  DS    A                  ERROR EXIT ROUTINE                 
*                                                                    
IORBFLAG DS    X                  FLAGS                              
IORBFSEN EQU   X'80'              RESCHEDULE FOR SENSE               
IORBOFLN EQU   X'01'              SECHED I/O TO OFFLINE DEVICE       
*                                                                    
IORBECBC DS    X                  I/O COMPLETION CODE                
IORBSEN0 DS    X                  SENSE BYTE ZERO                    
IORBSEN1 DS    X                  SENSE BYTE ONE                     
IORBCSW  DS    XL8                CSW                                
*                                                                    
IORBWORK DS    XL64               WORK AREA

The IORBNEXT field is used to maintain the IORB queue off the UCB.  They are queued oldest to newest.  IORBUCB points to the UCB the I/O request is for.  The IORBIOB points to the IOB associated with the EXCP request that created the IORB.  The IORBCCW points to the CCW program.

IORBTCB points to a TCB that will be added to the dispatch chain when the I/O is complete.  The TCB is supplied by the caller of the TXXSIO routine.  The TCB PSW should point to a routine that will handle the completion of the I/O request.  The TCBDIE field contains the address of a routine that will be called from the I/O Interrupt routine.

The status fields (IORBECBC, IORBSEN0, IORBSEN1, and IORBCSW) return the status of the completed I/O request.

The IORBWORK is a 64 byte work area to be used by TXXSIO.

[Next – ]

Unit Control Block – @UCB

The Unit Control Block (UCB) maintains the status of an I/O Device. There is one UCB for each I/O device managed by the OS.

@UCB     DSECT ,
UCBNEXT  DS    A                  NEXT UCB
UCBCUA   DS    XL2                DEVICE ADDRESS
*
UCBDEVT  DS    X                  DEVICE TYPE
UCBTRDR  EQU   1                  - CARD READER
UCBTPUN  EQU   2                  - CARD PUNCH
UCBTPRT  EQU   3                  - PRINTER
UCBTDASD EQU   4                  - DASD
UCBTTAPE EQU   5                  - TAPE
UCBTTERM EQU   6                  - TERMINAL
*
UCBSTAT  DS    X                  DEVICE STATUS
UCBSONLN EQU   X'80'              DEVICE ONLINE
UCBSMNT  EQU   X'08'              MOUNTED
*
UCBDEVNM DS    CL4                DEVICE TYPE NAME
UCBIOB   DS    A                  PTR TO IOB QUEUE
UCBINTR  DS    A                  INTERRUPT ROUTINE
UCBINTRP DS    F                  INTERRUPT PARAMETER
UCBDVEXT DS    A                  DEVICE EXTENSION
UCBCSW   DS    XL8                CHANNEL STATUS WORD

The UCB’s are chained together and must be arranged in ascending sequence based on the Device Address. The UCBDEVT field indicates the class of device. The actual device type is specified in the UCBDEVNM field as a four-byte character field.

The UCBIOB field points to the IORB queue. The first IORB is the currently active request.  If additional requests are scheduled for the device they are chained off the active request.

The UCBINTR field contains a pointer to the interrupt routine to receive control if no active IORB is queued when an I/O Interrupt occurs.  If this field is zero then no action is taken and the interrupt is ignored.  The UCBINTRP is used to maintain a parameter value to be used the the interrupt routine.

UCBDVEXT is a pointer to a device specific extension.  If there is no device specific extension associated with a device this field is zero.

The UCBCSW field contains a copy of the Channel Status Word at the time the interrupt occurred.

@UCBDASD DSECT ,
UCBDVOL  DS    CL6                VOLUME SERIAL
UCBDTRKC DS    H                  TRACKS/CYLINDER
UCBDTRKZ DS    H                  TRACK SIZE
UCBDVTOC DS    0XL8               VTOC EXTENT
UCBDVTOB DS    XL4                VTOC BEGIN CCHH
UCBDVTOE DS    XL4                VTOC END   CCHH

This is the initial description of the DASD Device Specific Extension.  I say initial because it may change as the DASD specific I/O code is developed.

It contains the Volume Serial, the number of tracks per cylinder, the track size and the VTOC extent description.  This information will be filled in when the DASD Volume is mounted.

[Next – I/O Request Block – @IORB]

I/O Management

With the basic Dispatcher functions up and running we can now turn to I/O management.  Our I/O management code will consist of several components.  We will need a control block to keep the status of each device.  For this we will use a Unit Control Block (UCB).  Each device we manage will have a UCB in our UCB Table.  The UCB’s are chained together and must be defined in sequence based on the Device Address (CUA).

We will then need a component to schedule and queue I/O requests.  If a device is available the I/O request can immediately be issued.  If the device is busy processing another request the new one will be placed in a queue anchored off the UCB.  An I/O request will be represented by a control block, the I/O Request Block (IORB).

When an I/O request is completed by the device an interrupt will be generated.  We will then need an I/O Interrupt processing routine to signal the originator of the request and start the next queued request for the device.

We will also build a SVC interface to the I/O scheduler.  The SVC will take a control block, the I/O Block (IOB) and will build the IORB and then call the I/O Scheduler.  It will also provide for posting an ECB when the I/O is complete.

Finally we will begin work on a 3270 Console that will use the SVC I/O interface.

In building and testing the I/O code I found and corrected a few bugs in the previous code.  I had to fix the Nucleus Loader because when the Nucleus Load Module required loading two blocks of data the second was being loaded at the wrong location in memory.

I also discovered a bug in the Wait SVC processing.  If the Wait SVC was called and the ECB was already posted the SVC was exiting to the Dispatcher instead of exiting through the Exit Routine.  This resulted in leaving the RB associated with the Wait SVC on the TCB RB Chain.  The fix was to use the Exit Routine which deletes the RB.

[Next – Unit Control Block – @UCB]