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...