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:

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)