3270

From OSDev.wiki
Jump to navigation Jump to search
This page is a stub.
You can help the wiki by accurately adding more contents 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.

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);
}

See also

External sources