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 – ]