Thursday, March 14, 2019

PC/370 Assembler Object Code Translator

I had the great fortune to begin my career as an Assembler Programmer/Consultant at a large IBM MVS/370 mainframe data center. Designing & developing applications at the machine code level was invaluable in sharpening my skills. Here I'll provide a brief glimpse into the "ancient" 370 Assembler realm along with a fun PC/370 Object Code Translator I described in my initial blog post. This is it below - the animated GIF shows how instructions (in object code) are evaluated one-by-one by pressing the <Enter Key>. The program is loaded into memory & then it "translates itself" starting with offset x"0000". Each opcode is evaluated & the correct source code translation is displayed before bumping to the next opcode. When I shared this on an Assembler 370 Usenet Group there were several professors that asked if they could use this as an instructional aid in their classrooms - some as far as Bangalore.

The program is extremely precise in it's design & coding - the only blunder was the misspelling of "Translater" in the footing line of the distributed object code!




Below is a small pamphlet known as the "Yellow Card" - I still have mine tucked safely away. It's jam-packed with everything you need. With all the 2:00am core dumps I worked on it didn't take me long to know this pamphlet inside & out. Working on other programmer's abends made me extremely committed to developing neat, concise & efficient code that is virtually bug-free... my trademark.



Here's a closer look at the "Yellow Card". This section displays the 370 Instruction Set from x'00' thru x'FF' with pertinent information such as the source code mnemonic, instruction format, etc...



The machine formats describe how many bytes the instruction takes & the required operands, bit masks, registers & addresses. The machine instructions take up 2, 4 or 6 bytes.



I originally created my Object Code Translator in CICS (1991) then modified it to run on a Windows 486 PC (1996) using PC/370 (an ASM/370 Emulator written by Donald Higgins & eventually sold to Micro Focus - renamed as MF/370). It's just a fun exercise created in my spare time - having a neat, well-thought out programming style enables me to build complex applications very rapidly.

The major difference between the PC/370 (left) & CICS (right) versions are the screen & keyboard controls. The mainframe uses a dumb terminal that relays activity once all data entry is complete & a control key is pressed. The PC is more interactive with 100% control given to the programmer with every press of a key. CICS has special commands to SEND & RECEIVE maps/data/actions while in PC/370 I need to explicitly code the routines for keyboard handling & video screen display.


Below shows key handling for PC/370 (left) & CICS (right). I coded the routine KBGET to retrieve keystrokes which are then evaluated (using a copybook of key values I created - @K#ESC, @K#F1, etc...). CICS maps the keys in the HANDLE  AID command prior to issuing a RECEIVE MAP (ENTER, PF1, etc.) & control is passed to the corresponding address upon return to the program.


This is the routine that constructs the screen output. PC/370 allows a more versatile display with colors (the CICS version was a dull green monochrome).


***********************************************************************
*        BUILD DUMP FORMATTED SCREEN DISPLAY                          *
***********************************************************************
         CNOP  0,4                           SUBROUTINE ALIGNMENT
BUILD    EQU   *
         ST    6,BUILD@                      SAVE RETURN ADDRESS
*
         LA    4,MAP1I                       POINT TO MAP1 AREA
         LH    5,=Y(L'MAP1I)                 LOAD MAP1 LENGTH
         LA    6,=C' '                       POINT TO HEX00
         LH    7,=Y(1)                       LOAD LENGTH OF 1
         ICM   7,B'1000',=C' '               PAD CHAR = LOW-VALUES
         MVCL  4,6                           CLEAR MAP1 AREA (ORIG)
*
         MVC   ARG,OFFARG                    MOVE OFFARG BYTE 1
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   M1OFFO(1),ARGZ                MOVE ARGUMENT ZONE
         MVC   M1OFFO+1(1),ARGN              MOVE ARGUMENT NUMERIC
         MVC   ARG,OFFARG+1                  MOVE OFFARG BYTE 2
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   M1OFFO+2(1),ARGZ              MOVE ARGUMENT ZONE
         MVC   M1OFFO+3(1),ARGN              MOVE ARGUMENT NUMERIC
*
         MVC   ARG,LEN                       MOVE LENGTH BYTE 1
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   M1LENO(1),ARGZ                MOVE ARGUMENT ZONE
         MVC   M1LENO+1(1),ARGN              MOVE ARGUMENT NUMERIC
         MVC   ARG,LEN+1                     MOVE LENGTH BYTE 2
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   M1LENO+2(1),ARGZ              MOVE ARGUMENT ZONE
         MVC   M1LENO+3(1),ARGN              MOVE ARGUMENT NUMERIC
*
         LA    6,16                          SET LOOP COUNTER
         LA    7,M1LND                       POINT TO MAP LINE 1
         ST    7,MAPADDR                     SAVE MAP LINE ADDRESS
BCLEAR   EQU   *
         MVI   3(7),C' '                     MOVE SPACE TO 1ST BYTE
         MVC   4(78,7),3(7)                  PROPAGATE THRU LINE
         BCT   6,BCLEAR                      LOOP TIL ALL CLEAR
*
         LH    6,OFFSET                      LOAD OFFSET VALUE
         LH    7,LEN                         LOAD MODULE LENGTH
         CVD   6,DISP                        CONVERT TO DECIMAL
         ZAP   PLO,DISP+5(3)                 XFER TO LO POSITION
         ZAP   PHI,PLO                       INIT HI TO LO COUNTER
         SR    7,6                           SUB OFFSET FROM LENGTH
         STH   7,REM                         STORE REMAINING LENGTH
         L     7,PTR                         POINT TO LOAD MODULE
         AR    7,6                           ADD OFFSET VALUE
         ST    7,STARTPT                     SAVE STARTING POINT
         LH    6,REM                         LOAD REMAINING LENGTH
         ZAP   LINES,=P'0'                   INIT LINE COUNTER
BLOOP    EQU   *
         AP    LINES,=P'1'                   ADD +1 TO LINE COUNTER
         ZAP   PLO,PHI                       SET NEW LO TO PREV HI
         AP    PLO,=P'1'                     INCREMENT LO BY +1
         CH    6,=H'16'                      COMPARE REMAINING TO 16
         BL    BLOW                          LOW - ALTERNATE ROUTINE
         LH    5,=H'16'                      SET LOOP COUNTER = 16
         SH    6,=H'16'                      DECREMENT LENGTH CTR
         B     BSTH                          STORE NEW LENGTH
BLOW     EQU   *
         LR    5,6                           SET LOOP CTR = REM.LEN
         SR    6,6                           REMAINING LENGTH = 0
BSTH     EQU   *
         STH   6,REM                         SAVE REMAINING LENGTH
         L     3,MAPADDR                     POINT TO NEXT MAP LINE
         LA    3,3(,3)                       BUMP PAST LENGTH/ATTRIB.
         CVB   6,DISP                        CONVERT DISPLACEMENT
         STH   6,HALF                        STORE DISP IN HALF-WORD
         MVC   ARG,HALF                      MOVE OFFSET BYTE 1
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   0(1,3),ARGZ                   MOVE ARGUMENT ZONE
         MVC   1(1,3),ARGN                   MOVE ARGUMENT NUMERIC
         MVC   ARG,HALF+1                    MOVE OFFSET BYTE 2
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   2(1,3),ARGZ                   MOVE ARGUMENT ZONE
         MVC   3(1,3),ARGN                   MOVE ARGUMENT NUMERIC
         LA    3,9(,3)                       POINT TO HEX DUMP AREA
         LA    4,40(,3)                      POINT TO CHARACTER AREA
BDUMP    EQU   *
         MVC   ARG,0(7)                      MOVE LOAD MODULE BYTE
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   0(1,3),ARGZ                   MOVE ZONE      PORTION
         MVC   1(1,3),ARGN                   MOVE NUMERIC   PORTION
         MVC   0(1,4),ARGT                   MOVE CHARACTER PORTION
         LA    4,1(,4)                       BUMP MAP LINE (CHAR-FMT)
         LA    3,2(,3)                       BUMP MAP LINE (HEX-FMT)
         LA    7,1(,7)                       BUMP LOAD MODULE PTR
         AP    PHI,=P'1'                     ADD +1 TO HI POSITION
         CVB   6,DISP                        CONVERT DISP TO BINARY
         STH   6,HALF                        STORE IN HALF-WORD
         TM    HALF+1,X'01'                  SKIP SPACE EVERY 4 HEX
         BNO   BSKIP                         NOT 4TH - SKIP THIS
         TM    HALF+1,X'02'                  SKIP SPACE EVERY 4 HEX
         BNO   BSKIP                         NOT 4TH - SKIP THIS
         LA    3,1(,3)                       SKIP EXTRA SPACE PLEASE
BSKIP    EQU   *
         AP    DISP,=P'1'                    ADD +1 TO DISPLACEMENT
         BCT   5,BDUMP                       LOOP TIL LINE FINISHED
*
         L     3,MAPADDR                     RELOAD MAP LINE ADDRESS
         LA    3,71(,3)                      POINT TO LO/HI AREA
         UNPK  0(5,3),PLO                    UNPACK LO POSITION
         OI    4(3),X'F0'                    MAKE LAST DIGIT CHAR.
         CP    PLO,PHI                       IS LO EQUAL TO HI ???
         BNL   *+18                          YES - THEN DON'T PUT HI!!
         MVI   5(3),C'-'                     NO  - MOVE DASH
         UNPK  6(5,3),PHI                    UNPACK HI POSITION
         OI    10(3),X'F0'                   MAKE LAST DIGIT CHAR.
*
         L     3,MAPADDR                     LOAD MAP LINE ADDR AGAIN
         LA    3,82(,3)                      BUMP TO NEXT POSITION
         ST    3,MAPADDR                     SAVE NEW FMTARR ADDRESS
         CP    LINES,=P'16'                  MAP AREA FILLED ???
         BNL   BUILDX                        YES - EXIT PLEASE
         LH    6,REM                         LOAD REMAINING LENGTH
         CH    6,=H'0'                       GREATER THAN ZERO ???
         BH    BLOOP                         YES - MORE LINES TO FILL
BUILDX   EQU   *
         L     6,BUILD@                      RESTORE LINK REGISTER
         BR    6                             BRANCH ON LINK REGISTER
***********************************************************************
         EJECT



This routine searches for the current opcode & builds the instruction based on the associated format:

***********************************************************************
*        TRANSLATE OBJECT CODE TO SOURCE CODE                         *
***********************************************************************
         CNOP  0,4                           SUBROUTINE ALIGNMENT
OPXLAT   EQU   *
         ST    6,OPXLAT@                     SAVE RETURN ADDRESS
*
         MVC   M1XLTO,BLANKS                 CLEAR XLATE LINE
         LH    6,OFFARG                      LOAD OFFSET ARGUMENT
         L     7,PTR                         LOAD MODULE ADDRESS
         AR    7,6                           POINT TO OP-CODE OFFSET
*
         LA    6,OPTABLE                     POINT TO OPCODE TABLE
OPXLOOP  EQU   *
         CLI   0(6),X'FF'                    END OF TABLE ???
         BE    OPXFND                        YES - USE DUMMY ENTRY
         CLC   0(1,6),0(7)                   TABLE OPCODE MATCH ???
         BE    OPXFND                        YES - CONTINUE PLEASE
         LA    6,12(,6)                      BUMP TABLE ENTRY
         B     OPXLOOP                       TEST NEXT TABLE ENTRY
OPXFND   EQU   *
         MVC   OPENTRY,0(6)                  MOVE TABLE ENTRY
         XC    HALF,HALF                     CLEAR HALF-WORD AREA
         MVC   HALF+1(1),OPLEN               MOVE INSTRUCTION LENGTH
         LH    6,OFFARG                      LOAD INSTRUCTION OFFSET
         AH    6,HALF                        ADD  INSTRUCTION LENGTH
         CH    6,LEN                         OFFSET MORE THAN LENGTH??
         BNH   *+16                          NO  - CONTINUE PLEASE
         MVC   OPENTRY,OPTABLEX              MOVE TABLE ENTRY (DUMMY)
         MVC   HALF+1(1),OPLEN               MOVE INSTRUCTION LENGTH
*
         SR    3,3                           INIT INSTRUCTION LENGTH
         LH    3,HALF                        LOAD INSTRUCTION LENGTH
         SRA   3,1                           DIVIDE LENGTH BY 2
         LA    4,M1XLTO                      POINT TO MAP AREA
         LR    5,7                           POINT TO INSTRUCTION
OPXOBJ   EQU   *
         MVC   ARG,0(5)                      MOVE 1ST BYTE TO ARGUMENT
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   0(1,4),ARGZ                   MOVE ARGUMENT ZONE
         MVC   1(1,4),ARGN                   MOVE ARGUMENT NUMERIC
         MVC   ARG,1(5)                      MOVE 2ND BYTE TO ARGUMENT
         BAL   6,XLATE                       TRANSLATE ARGUMENT
         MVC   2(1,4),ARGZ                   MOVE ARGUMENT ZONE
         MVC   3(1,4),ARGN                   MOVE ARGUMENT NUMERIC
         LA    4,5(,4)                       BUMP MAP POINTER
         LA    5,2(,5)                       BUMP OBJ POINTER
         BCT   3,OPXOBJ                      PRINT INSTRUCTION OBJECT
*
         MVC   M1XLTO+23(6),OPMNE            MOVE MNEMONIC TO MAP
         LA    5,M1XLTO+34                   POINT TO OPERAND MAP AREA
         L     4,OPRTN                       LOAD FORMAT ROUTINE ADDR
         BALR  6,4                           GOTO FORMAT ROUTINE NOW...
OPXLATX  EQU   *
         L     6,OPXLAT@                     RESTORE LINK REGISTER
         BR    6                             BRANCH ON LINK REGISTER
***********************************************************************
         EJECT


Below is a small portion of the opcode table named "OPTABLE". It contains the opcode, instruction length in bytes, source code mnemonic & machine code format (actually the address of the machine code format routine).

***********************************************************************
*        OP-CODE TABLE                                                *
***********************************************************************
         CNOP  0,4                           FULL-WORD ALIGNMENT
OPTABLE  EQU   *
         DC    X'04',AL1(2),CL6'SPM   ',A(RR1)
         DC    X'05',AL1(2),CL6'BALR  ',A(RR1)
         DC    X'06',AL1(2),CL6'BCTR  ',A(RR1)
         DC    X'07',AL1(2),CL6'BCR   ',A(RR2)
         DC    X'0A',AL1(2),CL6'SVC   ',A(RR3)
         DC    X'0B',AL1(2),CL6'BSM   ',A(RR1)
         DC    X'0C',AL1(2),CL6'BASSM ',A(RR1)
         DC    X'0D',AL1(2),CL6'BASR  ',A(RR1)
         DC    X'0E',AL1(2),CL6'MVCL  ',A(RR1)

...
<SNIP>
...
         DC    X'F1',AL1(6),CL6'MVO   ',A(SSA)
         DC    X'F2',AL1(6),CL6'PACK  ',A(SSA)
         DC    X'F3',AL1(6),CL6'UNPK  ',A(SSA)
         DC    X'F8',AL1(6),CL6'ZAP   ',A(SSA)
         DC    X'F9',AL1(6),CL6'CP    ',A(SSA)
         DC    X'FA',AL1(6),CL6'AP    ',A(SSA)
         DC    X'FB',AL1(6),CL6'SP    ',A(SSA)
         DC    X'FC',AL1(6),CL6'MP    ',A(SSA)
         DC    X'FD',AL1(6),CL6'DP    ',A(SSA)
OPTABLEX EQU   *
         DC    X'FF',AL1(2),CL6'***   ',A(OPXLATX)
***********************************************************************
         EJECT


And here's a sample of a machine code format routine. This one is RR2 format which is typically a Register-to-Register instruction but in this case it's more of a Bitmask-to-Register format - that's why I deemed it RR2.

***********************************************************************
*        RR2   FORMAT:                       B'1111',R1               *
***********************************************************************
         CNOP  0,4                           FULL-WORD ALIGNMENT
RR2      EQU   *
         ST    6,SAVE@                       SAVE RETURN ADDRESS
*
         MVC   M1XLTO+17(3),=C'RR2'          MOVE FORMAT TO MAP
*
         MVO   RR(3),1(1,7)                  ISOLATE MASK1
         BAL   6,FMTBITS                     CONVERT/MOVE TO MAP
         MVI   0(5),C','                     MOVE COMMA TO MAP
         LA    5,1(,5)                       BUMP MAP 1 POSITION
         XC    RR,RR                         CLEAR REGISTER AREA
         MVC   RR+1(1),1(7)                  MOVE REG1/REG2
         NI    RR+1,X'0F'                    ISOLATE REG2
         BAL   6,FMTR                        CONVERT/MOVE TO MAP
*
         L     6,SAVE@                       RESTORE LINK REGISTER
         BR    6                             BRANCH ON LINK REGISTER
***********************************************************************
         EJECT


This was an interesting & fun exercise... a complex technical challenge that was quickly brought to life without the slightest problem. I miss the good old Assembler days...