3270
This page or section is a stub. You can help the wiki by accurately contributing to it. |
The 3270 is a family of terminals compatible and often used with the S/390 and z/Architecture mainframes. They are relatively simple to code for, and act as a "streaming" terminal (that is, it receives commands and applies them to an state) - so it virtually works the same as a VT100.
Contents |
Usage
The 3270 operates like a normal terminal - it has PF0-PF15 keys which many programs use to perform operations (especially interactive programs). Receiving data is done through a normal I/O interruption generated by the 3270.
A local non-SNA console known as the SYSG is present on modern day mainframes, to use said console the traditional 3270 commands are mostly deprecated - and instead relies exclusively on the SCLP - this local non-SNA version can't use traditional CCW command chains or CSS addressing.
Sending commands
3270 supports various CCW commands - all of them can be used, most important ones are CMD_WRITE_CR, CMD_WRITE_NOCR and CMD_READ_BUFFER. See Command SubSystem for more information about CCW commands.
#define X3270_CMD_SELECT 0x0B #define X3270_CMD_SELECT_WRITE 0x4B #define X3270_CMD_WRITE_NOCR (CSS_CMD_WRITE) // Write without carriage return #define X3270_CMD_WRITE_CR 0x09 // Write with carriage return #define X3270_CMD_READ_BUFFER (CSS_CMD_READ) // Read the buffer #define X3270_CMD_READ_MODIFIED 0x06 // Read modified #define X3270_CMD_NOP (CSS_CMD_CONTROL) // No operation #define X3270_CMD_WSF 0x11 // Write Structured Field
Composing packets
Composing packets consists of creating a buffer with the desired 3270 commands (not to be confused with CCW commands). And sending it via a READ CCW.
Traditional HLASM
* USE =A(BUFFER) SINCE WE'RE NOT DECLARING BUFFER AS * AN EXTERNAL SYMBOL BUFFER $SF (SKIP) * TELL THE 3270 WHERE OUR TEXT SHOULD BE PLACED AT * MOST 3270 TERMINALS CAN USE UP TO 24 ROWS AND 80 COLUMNS * OTHER MODELS MAY HAVE MORE $SBA (24,5) * THE HELLO WORLD MESSAGE DC C'HELLO 3270 WORLD!' * TELL THE 3270 WE'RE ENDING THIS $SF (SKIP)
Then an HLASM routine would send the CCW command to take =A(BUFFER) and the 3270 will load said buffer and start interpreting it.
C
On C there are no HLASM macros to assist in the creation of a buffer, instead it has to be created manually.
enum x3270_order { X3270_ORDER_PROGRAM_TAB = 0x05, X3270_ORDER_SET_BUFFER_ADDR = 0x11, X3270_ORDER_INSERT_CURSOR = 0x13, X3270_ORDER_ERASE_UNPROTECTED = 0x12, X3270_ORDER_START_FIELD = 0x1D, X3270_ORDER_START_FIELD_EXTENDED = 0x29, X3270_ORDER_MODIFY_FIELD = 0x2C, X3270_ORDER_SET_ATTRIBUTE = 0x28 };
Addressing
The 3270 has a legacy addressing scheme, relying exclusively on EBCDIC characters to represent addresses below a 4096 characters barrier (12-bits of addresses). This is not present on the local non-SNA version.
const unsigned char ebcdic_map[] = { 0x40, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F }; uint16_t x3270_address(uint16_t addr) { uint8_t tmp[2]; // 12-bit conversion does not need to be done since it's higher than the imposed 4K limit if(addr >= 0xfff) { tmp[0] = (addr >> 8) & 0x3F; tmp[1] = addr & 0x3F; } else { // Take only in account low 6-bits */ tmp[0] = ebcdic_map[(addr >> 6) & 0x3F]; tmp[1] = ebcdic_map[addr & 0x3F]; } return *((uint16_t *)&tmp[0]); }
Example write
Writing to the 3270 can be, generally broken down into several steps:
- An initial byte known as the Write Control Character (WCC)
- The SBA command to set the address the text is going to be written at
- The text itself, encoded in EBCDIC
- Another final SF to terminate (SKIP)
Some steps are optional, but are provided for completeness.
// the schid depends upon the system #define X3270_SCHID 0x00010001 void x3270_compose_buffer(uint8_t *buf, size_t n) { const char *msg = "Hello world"; size_t offset = 0, addr; // starting wcc buf[offset++] = 0x00; // sba will now set the address to 0,0 (top-left of the screen) buf[offset++] = X3270_ORDER_SET_BUFFER_ADDR; addr = x3270_address(0); buf[offset++] = (addr >> 8) & 0xff; buf[offset++] = addr & 0xff; // the data that is to be written memcpy(&buf[offset], msg, strlen(msg)); offset += strlen(msg); // optional terminating sf buf[offset++] = X3270_ORDER_START_FIELD; buf[offset++] = 0x00; // send the buffer to the 3270 via a ccw... kernel_css_write(X3270_SCHID, buf, n); }