← Back to blog

Cobalt COBOL

Files

Download: rev_cobaltcobol.zip

Challenge

This reversing challenge ships a stack of IBM 80-column punch cards alongside a reference for the IBM 029 keypunch (“Hollerith Code”). The story is that the punch cards encode a COBOL code update for an ancient asteroid mining facility, and we are asked to analyze it. The challenge text also gives us the full punch-card zone layout and character set we will need to decode the cards.

Cobalt COBOL
When intercepting a delivery towards a quite old Asteroid Cobalt Mining Facility we got our hands on a stack of punch cards, it looks like these are some kind of code update? Since the last COBOL programmer died over a century ago we hoped that maybe you can analyze this ancient relic.

Punch Card Zones and the IBM 029 Code (“Hollerith Code”):
(use "&" – "9" for multiple-punch-mode)

       __________________________________________________________________
      /&-0123456789 ABCDEFGHIJKLMNOPQR/STUVWXYZ ¢.<(+|!$*);¬,%_>?:#@'="
12 Y / x            xxxxxxxxx                   xxxxxx
11 X|   x                    xxxxxxxxx                xxxxxx
   0|    x                            xxxxxxxxx             xxxxx
   1|     x         x        x        x
   2|      x         x        x        x        x     x          x
   3|       x         x        x        x        x     x    x     x
   4|        x         x        x        x        x     x    x     x
   5|         x         x        x        x        x     x    x     x
   6|          x         x        x        x        x     x    x     x
   7|           x         x        x        x        x     x    x     x
   8|            x         x        x        x  xxxxxxxxxxxxxxxxxxxxxxx
   9|             x         x        x        x
    |_____________________________________________________________________

    Blank/Space is an empty column.
    For IBM/360 column binary format equivalents see below.

Punch Card Encoding Reference

The challenge provides the column layout for IBM 80-column cards. The zone punches (rows 12, 11, 0) combine with numeric punches (rows 1-9) to select a character, following the IBM 029 keypunch character set.

The zone punches occupy the first columns:

Columns 1-3: Zone punches A, B, C
        Columns 4-6: Zone punches D, E, F
        Columns 7-9: Zone punches G, H, J
        Columns 10-12: Zone punches K, L, M

The numeric punches and special characters alternate across the body of the card:

Columns 13-18: Digits 0-9
        Columns 19-24: Special characters (e.g., punctuation marks)
        Columns 25-30: Digits 0-9
        Columns 31-36: Special characters
        Columns 37-42: Digits 0-9
        Columns 43-48: Special characters
        Columns 49-54: Digits 0-9
        Columns 55-60: Special characters
        Columns 61-66: Digits 0-9
        Columns 67-72: Special characters

The trailing columns hold the alphabetic punches:

Columns 73-80: Letters A-Z

Each "x" in the representation above indicates a punched hole in the corresponding position.
To read a punch card, you would interpret the presence or absence of a hole in each position (zone, numeric, and alphabetic) to determine the character represented by the card.
It's important to note that punch cards and the IBM 029 Code are outdated technologies and are no longer in widespread use. They have been replaced by modern digital storage and processing systems.

This was used to create the file:
https://github.com/digitaltrails/punchedcardreader

The punchedCardReader python script attempts to scan images of IBM 80 column punch cards extracting any recognisable text.

convert -size 360x360 xc:white -font "FreeMono" -pointsize 12 -fill black -draw @test.txt test.jpg

The IBM model 029 keypunch

The IBM model 029 keypunch, introduced around 1964, was the most common keypunch of the late 1960's and early 1970's. These were found almost everywhere computers were to be found, and they supported a full 64 printing characters, with graphics that represented a compromize between the needs of programmers and commercial applications. The closely allied EBCD and IBMEL character sets are also given here. While these character sets are sufficient for FORTRAN, they are upward compatable from the 026 commercial character set and they have a large degree of compatability with the 70x0 character set (with which they are compared here):

029  &-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#@'="¢.<(+|!$*);¬ ,%_>?
IBME ¹-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#²'="].<(+|[$*);¬³,%_>?
EBCD &-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZ:#@'="[.<(+|]$*);^\,%_>?
     ________________________________________________________________
    /&-0123456789ABCDEFGHIJKLMNOPQR/STUVWXYZb#@'>V?.¤[<§!$*];^±,%v\¶
12 / O           OOOOOOOOO                        OOOOOO
11|   O                   OOOOOOOOO                     OOOOOO
 0|    O                           OOOOOOOOO                  OOOOOO
 1|     O        O        O        O
 2|      O        O        O        O       O     O     O     O
 3|       O        O        O        O       O     O     O     O
 4|        O        O        O        O       O     O     O     O
 5|         O        O        O        O       O     O     O     O
 6|          O        O        O        O       O     O     O     O
 7|           O        O        O        O       O     O     O     O
 8|            O        O        O        O OOOOOOOOOOOOOOOOOOOOOOOO
 9|             O        O        O        O
  |__________________________________________________________________

In the IBMEL character set, the following characters are misprinted:

    ¹ (12)     logical and
    ² (8-4)    logical or
    ³ (0-8-2)  subscript ten

Decoding the Punch Cards

The cards are scanned into an ASCII grid of x (hole) and space (no hole). Transposing the x/whitespace columns against the IBM 029 charset above lets us parse each column into a character. Decoding all the cards and sorting by the line number produces the original COBOL source.

The scanned card image looks like this:

image

After decoding, sorting the result by its leading line numbers reveals the COBOL program:

cat decoded.txt | sort -un
000001  IDENTIFICATION DIVISION. PROGRAM-ID. CC.
000002  DATA DIVISION. WORKING-STORAGE SECTION.
000003  01 F. 05 R OCCURS 9 TIMES. 10 FC OCCURS 9 TIMES. 15 C PIC 9(1).
000004  01 A. 05 AF OCCURS 9 TIMES INDEXED BY FIN. 10 AFF PIC 9(1).
000005  01 T PIC 9(1). 01 I PIC 9(3). 01 P PIC X(1548) VALUE
000006  "K36_B>8963AC 7EHC_IMGDMRKHQWOLU>SPY4WT09<X552>9 63BD 7FIC_JNG"&
000007  "DNSKHRXOLV0SPZ5WT21<X662> _63CE 7GJC_KOGDOTKHSYOLW1SP>ZWT32<X"&
000008  "772>_A63DF 7HKC_LPGDPUKHTZOLYVSP0<WT43<X882>AB63EG 7ILC_MQGDQ"&
000009  "VKHVROLZWSP1>WT54<X992>BC63FH 7JMC_NRGDSNKHWSOL<XSP20WT65<X  "&
000010  "2>CD63GI 7KNC_PJGDTOKHXTOL>YSP31WT76<X__2>DE63HJ 7MFC_QKGDUPK"&
000011  "HYUOL0ZSP42WT87<XAA2>EF6SL5CDP9GITBKNXFOS>JSX3NW07R<5_V2 DZ6D"&
000012  "HKRK4CCO8GHSAKMWEOR<ISW2MW>6Q<4 U29CY6CGJQJ3CBN7GGR_KLVDOQZHS"&
000013  "V1LW<5P<39T28BX6BFIPI2CAM6GFQ KKUCOPYGSU0KWZ4O<28S27AW6AEHOH1"&
000014  "C_L5GEP9KJTBOOXFST>JWY3N<17R26_V6_DGNG0C K4GDO8KISAONWESS<IWX"&
000015  "2M<06Q25 U6 CFMF>C9J3GCN7KHR_OMVDSRZHWW1L<>5P249T69BELE<C8I2G"&
000016  "BM6KGQ OLUCSQYGWV0K<<4O238S68ADKDZC7H1GAL5KFP9OKTBSPXFWU>J<Z3"&
000017  "N227R67_CJCY56G0  K4DCO8IGSANKWESO<IXS2M0W6Q5< BIBX46F>9 J3CC"&
000018  "N7HGR_MKVDROZHWS1L>W5P4<9AHAW36E<8 I2BCM6GGQ LKUCQOYGVS0K<W4O"&
000019  "3<8_G_V26DZ7 H1ACL5FGP9KKTBPOXFUS>JZW3N2<7 F U16CY6 G0_CK4EGO"&
000020  "8JKSAOOWETS<IYW2M1<69E9T06BX5 F> CJ3DGN7IKR_NOVDSSZHXW1L0<58D"&
000021  "8S>6AW4 E<9CI2CGM6HKQ MOUCRSYGWW0K><47C7R<6_V3 DZ8CH1BGL5GKP9"&
000022  "LOTBQSXFVW>J<<36B6QZ6 U2 CY7CG0AGK4FKO8KOSAPSWEUW<IZ<25A5PYZ9"&
000023  "T02BX47F>99J3BCN7FHR_KJVDOOZHST14_4OX>8S>4AW39E<8_I2AEM6EJQ J"&
000024  "LUCNQYGRV03 3NW17R<6_V2_DZ7BH1_GL5DLP9INTBMSXFQX>292MYW6Q0> U"&
000025  "44CY96G0B_K4FEO8KGSAOLWESQ<181LXY5P>19T36BX88F>ABJ3EGN7JIR_NN"&
000026  "VDRSZ070KW<4O<38S28AW7 E<_DI2DIM6IKQ MPUCQUY>6>JYT3N0Y7R41_V9"&
000027  "3DZB8H1FBL5KDP9OITBSNX<5<IXV2M><6Q33 U85CYA G0EDK4JFO8NKSARPW"&
000028  "Z4ZHWX1L<05P259T77BX_AF>DFJ3IHN7MMR_QRVYAGOPTVY>WW>2R<Z4>292 "&
000029  "850  EGDBHCIN_KNRJPPMOOSKWSUXV140><V657 5867FD4GFDCLEGPKPDSUM"&
000030  "TRRXWSXO21<ZZ3>692>AB28 5CE8JG_GNCNKFPSIOTLZTOZ1R0>U05X9 <A91"&
000031  "AB4AC7HE HNBJJEQQGMNOV>". 01 PB PIC X(11). 01 P0 PIC 9(8) VALUE
000032  1. 01 P1 OCCURS 9 TIMES. 05 Q PIC 9(4) VALUE 0. 01 PS  PIC 9(1)
000033  VALUE 0. 01 PRS OCCURS 9 TIMES. 05 PR PIC 9(1). 01 PWK. 05 PK
000034  OCCURS 20 TIMES. 10 PKF PIC 9(1) VALUE 0. 01 PKI PIC 9(2) VALUE
000035  1. 01 R0 PIC 9(24). 01 R1 PIC 9(20). 01 RI PIC 9(2). 01 RC PIC
000036  X(1). 01 FI PIC 9(2). 01 RCS PIC X(40) VALUE
000037  " _ABCDEFGHIJKLMNOPQRSTUVWXYZ<>0123456789". 01 FW.
000038  05 FL OCCURS 30 TIMES. 10 FC PIC X(1).  PROCEDURE DIVISION.
000039  INITIALIZE T INITIALIZE F PERFORM VM WITH TEST AFTER UNTIL PS
000040  EQUALS 1 IF T IS ZERO THEN PERFORM DECODE ELSE  DISPLAY "WRONG"
000041  END-DISPLAY END-IF STOP RUN. CONV. MOVE 5 TO RI PERFORM 10 TIMES
000042  COMPUTE R1 = FUNCTION MOD (R0(RI:2) 40) + 1 END-COMPUTE MOVE
000043  RCS(R1:1) TO RC MOVE RCS(R1:1) TO FL(FI) ADD 2 TO RI ADD 1 TO FI
000044  END-PERFORM. DECODE. MOVE 1 TO FI MOVE PWK TO R0(5:20)COMPUTE R0
000045  = FUNCTION MOD (((519*R0) - 9524936758751936028873)
000046  18446744073709551557) END-COMPUTE PERFORM CONV MOVE PWK TO R0
000047  (5:20) COMPUTE R0 = FUNCTION MOD (((655*R0) -
000048  5139944510939323535175) 18446744073709551557) END-COMPUTE
000049  PERFORM CONV MOVE PWK TO R0(5:20) COMPUTE R0 = FUNCTION MOD
000050  (((301*R0) - 5165552119864536862147) 18446744073709551557)
000051  END-COMPUTE PERFORM CONV DISPLAY FW.VM.MOVE P(P0:11) TO PB
000052  INITIALIZE Q(1) PERFORM 11 TIMES ADD 1 TO Q(1) INITIALIZE RI
000053  INSPECT RCS TALLYING RI FOR CHARACTERS BEFORE INITIAL PB(Q(1):1)
000054  COMPUTE RI = FUNCTION MOD ((RI - P0 - Q(1) + 1) 40) END-COMPUTE
000055  ADD 1 TO RI MOVE RCS(RI:1) TO PB(Q(1):1) END-PERFORM EVALUATE PB
000056  (1:1) WHEN "W" MOVE PB(2:1) TO Q(1) MOVE PB(3:9) TO R(Q(1)) ADD
000057  11 TO P0 WHEN "J" MOVE PB(2:4) TO Q(1) ADD 5 TO P0 ADD Q(1) TO
000058  P0 WHEN "R" MOVE Q(9) TO P0 WHEN "X" MOVE PB(2:4) TO Q(1) ADD 5
000059  TO P0 MOVE P0 TO Q(9) MOVE Q(1) TO P0 WHEN "I" EVALUATE PB(2:1)
000060  WHEN NUMERIC MOVE PB(2:1) TO Q(1) INITIALIZE PR(Q(1)) WHEN "A"
000061  INITIALIZE A END-EVALUATE ADD 2 TO P0 WHEN "V" MOVE PB(2:1) TO
000062  Q(1) MOVE PB(3:1) TO Q(2) ADD 3 TO P0 MOVE C(Q(2), Q(1))TO PR(1)
000063  WHEN "Y" MOVE PB(2:1) TO Q(1) MOVE PB(3:1) TO Q(2) ADD 3 TO P0
000064  ACCEPT PR(1) END-ACCEPT MOVE PR(1) TO PK(PKI) ADD 1 TO PKI MOVE
000065  PR(1) TO C(Q(2), Q(1)) WHEN "Z" ADD 1 TO P0 IF PR(1) IS EQUAL TO
000066  ZERO THEN MOVE 1 TO T END-IF WHEN "A" ADD 1 TO P0 MOVE 1 TO AFF(
000067  PR(1)) WHEN "C" ADD 1 TO P0 INITIALIZE FIN SEARCH AF WHEN AF(FIN
000068  ) = ZERO MOVE 1 TO T END-SEARCH WHEN "_" MOVE 1 TO PS WHEN OTHER
000069  DISPLAY "?" END-EVALUATE.

With the source recovered, we sort it into order and compile it with GnuCOBOL to get a runnable binary:

cat decoded.txt | sort -un > decoded_sorted.txt
cobc -x -o cobalt decoded_sorted.txt

For reference on building and running COBOL CTF challenges:

COBOL CTF
https://samsclass.info/129S/COBOL.shtml

imo you can transpose those x and whitespace, so that it represents the charset given above, parses them all and you'll get the cobol code

Punch Card Decoder

To automate the punch-card-to-text step, the following Python script defines the IBM 029 character map (12-bit columns of zone + numeric rows), reads a scanned grid (scans.txt), and reconstructs the text by reading each column top-to-bottom and looking it up in the map. Each card is delimited by an ________ border, and the script validates that every card has 12 rows with a consistent column count.

#!/usr/bin/env python3

# Imports
import os
from sys import exit

# Decoding map
char_map = {}
char_map["100000000000"] = "&"
char_map["010000000000"] = "-"
char_map["001000000000"] = "0"
char_map["000100000000"] = "1"
char_map["000010000000"] = "2"
char_map["000001000000"] = "3"
char_map["000000100000"] = "4"
char_map["000000010000"] = "5"
char_map["000000001000"] = "6"
char_map["000000000100"] = "7"
char_map["000000000010"] = "8"
char_map["000000000001"] = "9"
char_map["100100000000"] = "A"
char_map["100010000000"] = "B"
char_map["100001000000"] = "C"
char_map["100000100000"] = "D"
char_map["100000010000"] = "E"
char_map["100000001000"] = "F"
char_map["100000000100"] = "G"
char_map["100000000010"] = "H"
char_map["100000000001"] = "I"
char_map["010100000000"] = "J"
char_map["010010000000"] = "K"
char_map["010001000000"] = "L"
char_map["010000100000"] = "M"
char_map["010000010000"] = "N"
char_map["010000001000"] = "O"
char_map["010000000100"] = "P"
char_map["010000000010"] = "Q"
char_map["010000000001"] = "R"
char_map["001100000000"] = "/"
char_map["001010000000"] = "S"
char_map["001001000000"] = "T"
char_map["001000100000"] = "U"
char_map["001000010000"] = "V"
char_map["001000001000"] = "W"
char_map["001000000100"] = "X"
char_map["001000000010"] = "Y"
char_map["001000000001"] = "Z"
char_map["000010000010"] = ":"
char_map["000001000010"] = "#"
char_map["000000100010"] = "@"
char_map["000000010010"] = "'"
char_map["000000001010"] = "="
char_map["000000000110"] = '"'
char_map["100010000010"] = "`"
char_map["100001000010"] = "."
char_map["100000100010"] = "<"
char_map["100000010010"] = "("
char_map["100000001010"] = "+"
char_map["100000000110"] = "|"
char_map["010010000010"] = "!"
char_map["010001000010"] = "$"
char_map["010000100010"] = "*"
char_map["010000010010"] = ")"
char_map["010000001010"] = ";"
char_map["010000000110"] = "^"
char_map["001010000010"] = "~"
char_map["001001000010"] = ","
char_map["001000100010"] = "%"
char_map["001000010010"] = "_"
char_map["001000001010"] = ">"
char_map["001000000110"] = "?"
char_map["000000000000"] = " "

Example = """
    __________________________________________________________________________________
12|           x    xx     x        x x xx  xx   x  xxx            x      xxxxxx        |
11|            xx    x xx      x    x x      x            x x  xx      x               |
 0|  xxxx    x    x      x    x  x        x              x   x   x    x                |
 1|                         x                   x  x   x            x    x             |
 2|                                                                                    |
 3|               x           x  x                       x            x      xx        |
 4|                    x             x              xx         x                       |
 5|           x     xx   xx        xx       xx                   xx         x          |
 6|      xx     x       x      x        x x               x     x      x  xx           |
 7|                                            x x          x                          |
 8|                x                       x   x x                          x          |
 9|          x x                       x                                               |
  |____________________________________________________________________________________|
"""

def decode_message(punchcard):
    """Loop columns and parse columns to charmap and print text.
    """
    num_rows = len(punchcard)
    num_cols = len(punchcard[0])
    parsed_result = ""
    for col in range(num_cols):
        binary_column = "".join(punchcard[row][col] for row in range(num_rows))
        parsed_result += char_map.get(binary_column, '?')
    return parsed_result

def parse_punchcard(data):
    """Parse punchard grid where
    """
    output_decoded_text = []
    punchcard_num = 1
    parsing_message_flag = False
    for row in data.strip().split('\n'):
        if "________" in row:
            # ____ => Start / End Card
            if parsing_message_flag:
                num_rows = len(punchcard)
                if num_rows != 12:
                    print(f"[-] Punchcard {punchcard_num}: {num_rows} <> 12")
                    exit()
                num_cols = set([len(c) for c in punchcard])
                if len(num_cols) != 1:
                    print(
                        f"[-] Punchcard {punchcard_num}: inconsistent number of columns - {num_cols}")
                    exit()
                decoded_message = decode_message(punchcard)
                if decoded_message:
                    print(f"[+] Punchcard {punchcard_num}: {decoded_message}")
                    output_decoded_text.append(decoded_message)
                parsing_message_flag = False
                punchcard_num += 1
            else:
                parsing_message_flag = True
                punchcard = []
        if parsing_message_flag:
            # Expecting each card to have <digit>|<data>|
            index_end = row.find('|')
            if index_end > 0:
                index = row[:index_end].strip()
                if index.isdigit():
                    punchcard.append((row.split("|")[1]).replace(
                        " ", "0").replace("x", "1"))

    return output_decoded_text

def main():
    current_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(current_dir, 'scans.txt')
    with open(file_path, 'r') as file:
        ascii_art = file.read()

    decoded_messages = parse_punchcard(ascii_art)

if __name__ == "__main__":
    main()

Disassembling the COBOL VM

The COBOL program is really a tiny virtual machine. The large P string is the bytecode, but it is obfuscated: each character is shifted by its position modulo the length of the VM’s character set (RCS in the COBOL, chmap here). The script below reverses that position-dependent shift to recover the program, then walks the decoded stream and decodes each opcode (W = load register, J = jump, R = return, X = call, I = initialize, V = read from table into TEMP, Y = read input into table, Z = set zero flag, A = set affine flag, C = check, _ = exit) into a human-readable listing.

pcode = b"K36_B}8963AC 7EHC_IMGDMRKHQWOLU}SPY4WT09{X552}9 63BD 7FIC_JNGDNSKHRXOLV0SPZ5WT21{X662} _63CE 7GJC_KOGDOTKHSYOLW1SP}ZWT32{X772}_A63DF 7HKC_LPGDPUKHTZOLYVSP0{WT43{X882}AB63EG 7ILC_MQGDQVKHVROLZWSP1}WT54{X992}BC63FH 7JMC_NRGDSNKHWSOL{XSP20WT65{X  2}CD63GI 7KNC_PJGDTOKHXTOL}YSP31WT76{X__2}DE63HJ 7MFC_QKGDUPKHYUOL0ZSP42WT87{XAA2}EF6SL5CDP9GITBKNXFOS}JSX3NW07R{5_V2 DZ6DHKRK4CCO8GHSAKMWEOR{ISW2MW}6Q{4 U29CY6CGJQJ3CBN7GGR_KLVDOQZHSV1LW{5P{39T28BX6BFIPI2CAM6GFQ KKUCOPYGSU0KWZ4O{28S27AW6AEHOH1C_L5GEP9KJTBOOXFST}JWY3N{17R26_V6_DGNG0C K4GDO8KISAONWESS{IWX2M{06Q25 U6 CFMF}C9J3GCN7KHR_OMVDSRZHWW1L{}5P249T69BELE{C8I2GBM6KGQ OLUCSQYGWV0K{{4O238S68ADKDZC7H1GAL5KFP9OKTBSPXFWU}J{Z3N227R67_CJCY56G0  K4DCO8IGSANKWESO{IXS2M0W6Q5{ BIBX46F}9 J3CCN7HGR_MKVDROZHWS1L}W5P4{9AHAW36E{8 I2BCM6GGQ LKUCQOYGVS0K{W4O3{8_G_V26DZ7 H1ACL5FGP9KKTBPOXFUS}JZW3N2{7 F U16CY6 G0_CK4EGO8JKSAOOWETS{IYW2M1{69E9T06BX5 F} CJ3DGN7IKR_NOVDSSZHXW1L0{58D8S}6AW4 E{9CI2CGM6HKQ MOUCRSYGWW0K}{47C7R{6_V3 DZ8CH1BGL5GKP9LOTBQSXFVW}J{{36B6QZ6 U2 CY7CG0AGK4FKO8KOSAPSWEUW{IZ{25A5PYZ9T02BX47F}99J3BCN7FHR_KJVDOOZHST14_4OX}8S}4AW39E{8_I2AEM6EJQ JLUCNQYGRV03 3NW17R{6_V2_DZ7BH1_GL5DLP9INTBMSXFQX}292MYW6Q0} U44CY96G0B_K4FEO8KGSAOLWESQ{181LXY5P}19T36BX88F}ABJ3EGN7JIR_NNVDRSZ070KW{4O{38S28AW7 E{_DI2DIM6IKQ MPUCQUY}6}JYT3N0Y7R41_V93DZB8H1FBL5KDP9OITBSNX{5{IXV2M}{6Q33 U85CYA G0EDK4JFO8NKSARPWZ4ZHWX1L{05P259T77BX_AF}DFJ3IHN7MMR_QRVYAGOPTVY}WW}2R{Z4}292 850  EGDBHCIN_KNRJPPMOOSKWSUXV140}{V657 5867FD4GFDCLEGPKPDSUMTRRXWSXO21{ZZ3}692}AB28 5CE8JG_GNCNKFPSIOTLZTOZ1R0}U05X9 {A91AB4AC7HE HNBJJEQQGMNOV}"
chmap = b" _ABCDEFGHIJKLMNOPQRSTUVWXYZ{}0123456789"

decoded = ''

for i in range(len(pcode)):
    pbyte = pcode[i]
    j = (chmap.index(pbyte) - (i + 1)) % len(chmap)

    decoded += chr(chmap[j])

instructions = []

i = 0

while i < len(decoded):
    c = decoded[i]

    if c == 'W':
        a1 = decoded[i+1]
        a2 = decoded[i+2:i+2+9]
        instructions.append((i+1, 'R[{}] = {}'.format(a1, a2)))
        i += 10
    elif c == 'J':
        a1 = decoded[i+1:i+1+4]
        instructions.append((i+1, 'jmp {:04d}'.format(i+1+5+int(a1))))
        i += 4
    elif c == 'R':
        instructions.append((i+1, 'ret'))
    elif c == 'X':
        a1 = decoded[i+1:i+1+4]
        instructions.append((i+1, 'call {:04d}'.format(int(a1))))
        i += 4
    elif c == 'I':
        a1 = decoded[i+1]
        if a1 == 'A':
            instructions.append((i+1, 'initialize A'))
        else:
            instructions.append((i+1, 'initialize PR({}'.format(a1)))
    elif c == 'V':
        a1 = decoded[i+1]
        a2 = decoded[i+2]
        instructions.append((i+1, 'TEMP = C[{},{}]'.format(a1, a2)))
        i += 2
    elif c == 'Y':
        a1 = decoded[i+1]
        a2 = decoded[i+2]
        instructions.append((i+1, 'read -> C[{},{}]'.format(a1, a2)))
        i += 2
    elif c == 'Z':
        instructions.append((i+1, 'setz T (if TEMP == 0)'))
    elif c == 'A':
        instructions.append((i+1, 'AFF[TEMP] = 1'))
    elif c == 'C':
        instructions.append((i+1, 'setz T (if AF contains 0)'))
    elif c == '_':
        instructions.append((i+1, 'exit'))
    else:
        instructions.append((i+1, '?'))

    i += 1

for i, inst in instructions:
    print(str(i).rjust(4, '0'), inst)