# version 2.0 27/1/19
# Lots of bugs corrected, see update2.0.pdf

# Translation table from teleprinter code to and from ascii
holes=    ( 0,   1,   2,    3,   4,   5,   6,   7,   8,    9,   10,   11,   12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31)
printedfs=('b', 'f', '\r', '0', 'r', '7', '2', 's', '\n', '8', '5',  '/',  '9', '(', '=', 'e', 'p', '4', '3', '%', '6', 't', 'n', '-', '1', '*', ')', '"', '.', '+', ' ', '$')
printedls=('b', 'F', '\r', 'O', 'R', 'K', 'U', 'S', '\n', 'L', 'H',  'A',  'M', 'Z', 'E', 'D', 'P', 'G', 'Y', 'W', 'J', 'T', 'N', 'B', 'I', 'C', 'X', '"', 'V', 'Q', ' ', '$')
file2tape_fs=dict(zip(printedfs, holes))
file2tape_ls=dict(zip(printedls, holes))
tape2file_fs=dict(zip(holes, printedfs))
tape2file_ls=dict(zip(holes, printedls))

# Conversion between modifier letters and binary representation
modifier_letter = ['f', 'r', 's', 't']
modifier_binary = {'f':0, 'r':1, 's':2, 't':3, 'sr':0, 'tr':1}

def convert_integer_to_order(order):
    m=order & 2047
    opcode=(order & 0x3F800) >>11
    modifier_bits=(order & 0xC0000) >>18
    s=str(opcode) + modifier_letter[modifier_bits] + str(m)
    while len(s)<9: s=s + " "
    return s

def convert_order_to_integer(order):
        if order=='': return 
# extract operator, modifer and address
        i=1
        while (order[0:i]).isdigit(): i=i+1
        op=order[0:i-1]
        j=i+1
        while (order[i:j]).isalpha(): j=j+1
        modifier_letter=order[i-1:j-1]
        frst=modifier_binary[modifier_letter]
        address=order[j-1:]
        return (frst << 18) + (int(op) << 11) + int(address)

def load_reserved_store():
    global reserved
    File=open("reserved_store.txt", 'r')
    i=0
    for line in File.readlines():
        line=line.rstrip()
        if line=="": continue
        orders=line.split('\t')
        for j in range(0,4):
            reserved[512+i+j]=convert_order_to_integer(orders[j+1])
        i=i+4
    File.close()

maxM=2**39 - 1
minM=-2**39
carryM=2**40
signM=2**39
maxA=2**78 - 1
minA=-2**78
carryA=2**79
signA=2**78
signx = 2**31
maxFM=(1.0 - 2**-32)*2**127
minFM=-2**128
 
def get_a(m):
    "returns address part of location m in current store as integer"
    return store[m] & 0x7FF

def put_a(m, q):
    "stores modifier q in address part of location m in current store"
    store[m] = (store[m] & 0xFF800) | (modifier[q] & 0x7FF)

def M_to_signed_int(M):
    "converts M as positive long integer to a signed long integer"
    return (M^signM) - signM

def signed_int_to_M(M):
    "converts long integer to (long) accumulator format"
    "detects if overflow has occured and sets alpha"
    global alpha
    alpha = alpha or (M>maxM) or (M<minM)
    M=(M + carryM) & 0xFFFFFFFFFF
    return M

def ML_to_signedA(M,L):
    "converts M and L to signed long integer A"
    A = (M << 39) + (L & 0x7FFFFFFFFF)
    return (A^signA) - signA

def signedA_to_ML(A):
    "converts A to M and L, checking overflow"
    global alpha
    alpha = alpha or (A>maxA) or (A<minA)
    A=A + carryA
    M = (A >> 39) & 0xFFFFFFFFFF
    return (M, (A & 0x7FFFFFFFFF) | (L & 0x8000000000))

def round_accumulator(M, L):
    "rounds M depending on digit 1 of L"
    M1=M
    if M1!=maxM and (L & 0x4000000000): M1=M1 + 1
    return M1

def get_M_from_store(m):
    "returns long integer M constructed from successive locations"
    if use_main_store:
        M=main[main_store_address]
        return M
# get even part of address
    even_m=m & 0x7FE
    M=(store[even_m] << 20) + store[even_m+1]
    return M

def put_M_to_store(M, m):
    "stores M as integers in successive locations"
    if use_main_store:
#        print "put M", m, hex(M), main_store_address
        main[main_store_address]=M
        return
# get even part of address
    even_m=m & 0x7FE
    store[even_m]  = int((M >> 20) & 0xFFFFF)
    store[even_m+1]= int(M & 0xFFFFF)

def clearL0():
    global L
    L=L & 0x7FFFFFFFFF

def M2float(M):
    "converts floating accumulater format to float, checking standard form"
    if M==0: return 0.0
# test for standard floating point number
    if (M & 0xc000000000) and (~M & 0xc000000000):
        x=(M >> 8) & 0xFFFFFFFF
        x=(x^signx) - signx
        x=float(x)/signx
        p=(M & 0xFF) - 128
        return x*2**p
    else: report("non-standard floating point")

def float2M(FM):
    "converts floating point number to accumulator format"
# This code is inelegant but must correspond to approximately to the hardware
    global alpha
    if FM==0.0: return 0
    FM1=FM; p=0
    while FM1>=1.0 or FM1<-1.0:
        FM1=FM1*0.5
        p=p+1
    while FM1>=-0.5 and FM1<0.5:
        FM1=2.0*FM1
        p=p-1
    M1=int(FM1*signx)
    M1 = (M1 + signx) ^ signx
    if p>=128 or p<-128: alpha=True
    return (M1 << 8) | (p+128)

def read_input_tape(i):
# read input tapes to buffers and edit cr lf
    global input_tape, input_tape_pos, input_tape_len
    FS=0; LS=1
    shift=FS
    input_tape[i]=[]
    if os.path.exists(input_tape_name[i]):
        File=open(input_tape_name[i], 'r')
        tape=File.read()
        File.close()
        for c in tape:

# linefeed converted to cr lf
            if c=="\n": input_tape[i].append(file2tape_fs["\r"])

            fs=file2tape_fs.get(c, None)
            ls=file2tape_ls.get(c, None)

            if shift==FS:
                if fs: input_tape[i].append(fs)
                elif ls:
                    input_tape[i].append(27)
                    input_tape[i].append(ls)
                    shift=LS
                else: print("illegal char on input tape", c)
            elif shift==LS:
                if ls: input_tape[i].append(ls)
                elif fs:
                    input_tape[i].append(31)
                    input_tape[i].append(fs)
                    shift=FS
                else: print("illegal char on input tape", c)

    new_input_tape[i]=False
    input_tape_pos[i]=0
    input_tape_len[i]=len(input_tape[i])

def print_output_tapes(k):
    FS=0; LS=1
    if k==1: outputfile=open("outputtape.txt", 'w')
    for (num, channel) in [(1,outputchannel1), (2,outputchannel2)]:
        if channel!=[]:
            if k==0: print("\nOutput on Channel ", num)
            if k==1: outputfile.write("\nOutput on Channel " + str(num))
            shift=FS; x=""
            for c in channel:
                if c==31: shift=FS
                if c==27: shift=LS
                if (c!=31) and (c!=27):
                    if shift==FS: x=x + tape2file_fs[c]
                    if shift==LS: x=x + tape2file_ls[c]

            if k==0: print("\n", x)
            if k==1: outputfile.write("\n" + str(x))

    if k==1: outputfile.close()
