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