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

import os
import pickle

# r, s and t are stored as integers in elements 1, 2, 3 of list modifier and 
# only the lowest 11 bits are used
# modifier[0] is used to implement the 2, 3, 4, and 5 orders
# modifier[4] is used to store b, the register associated with the main store
# the free and reserved stores are implemented as lists of integers

# aliases for modifier registers
r=1; s=2; t=3; b=4
# bitpattern to use for cleared store
ones=2**20 - 1

# create configuration variable for peripherals
#peripherals=0
# create input tapes and default names
input_tape_name=["", ""]
new_input_tape=[False, False]
input_tape=[[],[]]; input_tape_pos=[0, 0]; input_tape_len=[0, 0]

# array to contain sound, not used
sound=bytearray()

# load various utilities
exec(open("etc.py").read())

crt_exists=False
main_store_assemble=False
# Needed because external assembler can write to reserved store
use_main_store=False

# create 4 tapes
magtape=[0,0,0,0]
# create blocks on each tape
#for i in range(0,4):
#    magtape[i]=[0 for j in range(0,2048)]
#    for j in range(0,2048):
#        block_label=((((i+1)<<11) + j) << 20) + 50
#        magtape[i][j]=[block_label, [0 for k in range(0,50)], 0]

File=open("magtapes.txt", 'rb')
magtape=pickle.load(File, encoding="latin1")
File.close()

# initial parameters for magnetic tapes
magtape_number=0
magtape_speed=0
block_number=201

def clear_start():
    global free, reserved, main, crt_exists
    free=    [ones for i in range(0,2048)]
    reserved=[ones for i in range(0,2048)]
    main=    [ones for i in range(0,16384)]
#    print reserved[0:10]
# reset reserved store to starting values
    load_reserved_store()
# re-initialise crt
    if crt_exists:
        crt_display.destroy()
        crt_exists=False
    set_cause_of_report("none")
    set_start()
    return

def set_start():
    global show_reserved, show_free, debug, reset, step, stop
    global modifier, store, alpha, K, L, M, outputchannel1, outputchannel2
    global input_tape, input_tape_pos, input_tape_len
    global peripherals, tape_reader
    global mainstore_prog
# configure emulator
#   debug switches
    debug=get_debug_setting()
    show_reserved=(debug & 1) != 0
    show_free=    (debug & 2) != 0
    reset=False
    step=False
# turn single step off
    step_off()
    if debug: print("\n")
    modifier=[0, 0, 0, 0, 0]
    alpha=False
    K=0; L=0; M=0
# initialise peripherals
# set inital state with input channel 1 and output channel 1 selected
# no output devices are attached because the channel outputs are printed
    peripherals=0x220c000000
    tape_reader=0
# check there is a tape in reader 1
    if input_tape_name[0]=="":
         report("No tape in tape reader 1")
         return
# clear output channels
    outputchannel1=[]; outputchannel2=[]
# test for main store assembler
    (mainstore_prog, external_assembler)=get_main()
    if external_assembler:
# intermediate output 'temp' is used to facilitate debugging
        print("using external assembler")
# set debug state for external assembler chnage to False to supress debug info
        assemble_debug=True
        (temp, startpoint) = run_assembler(assemble_debug)
        print("assembled program")
        load_store(temp, assemble_debug)
        store=free
        print("starting at", startpoint)
        modifier[r]=startpoint
# input tape has been read by external assembler
        new_input_tape[0]=False
    else:
# start assembler in reserved store at 1024
        store=reserved
        modifier[r]=1024

# clear all lamps
    for i in range(0,10): lamp_show(i, False)
# set display address 0
    display_address(0)
# manual wait lamp on
    lamp_show(manual_wait_light, 1)
    stop=False
    set_run_switch(False)
    run()

# load gui
exec(open("gui.py").read())
# load assembler
exec(open("assembler.py").read())
# set the default for the debugging: show free store orders but not reserved
set_default_debug(2)
set_default_output(1)

def run():
    global go, block_number, magtape_speed, reserved, new_input_tape
# set count for periodic update
    k=0
# run loop, only exits on stop order
    while not stop:
# check if run switch has been raised
        while run_switch.get()==0:
# check for new input tapes
            for i in [0, 1]:
                if new_input_tape[i]: read_input_tape(i)
            gui.update()
            set_emulator_run_light(False)
            lamp_show(manual_wait_light, 1)
# turn manual wait light off
        lamp_show(manual_wait_light, 0)
# set store neon to current store
        set_store_neon(store)
# set the light on the emulator control panel
        set_emulator_run_light(True)
# get next order from store
        order=store[modifier[r]]
# output debug information
        if debug:
            if store==reserved and show_reserved or store==free and show_free:
                ss=str([modifier[0],modifier[2],modifier[3],modifier[4]])
                while len(ss)<18: ss=ss + " "

                print('{:4d}'.format(modifier[r]), \
                      convert_integer_to_order(order), \
                      ss, \
                      '{:15s}'.format(hex(M)), \
                      '{:15s}'.format(hex(L)), \
                      alpha)
        obey(order)
        modifier[r]=modifier[r]+1

# if single step pause here until button pressed, only in free store
        if step:
            if store==free:
                while go==False: gui.update()
                go=False

# gui must be updated so run_switch can be detected but this slows program
# so update every 100 orders
        k=k+1
        if k>100:
# test the magnetic tape is moving
            if magtape_speed:
#                print "blockmark", block_number, magtape_speed, hex(reserved[2]), reserved[2]&0x7FF
# if the tape is moving increment block number
                block_number+=magtape_speed
                if (reserved[2] & 0x7FF) == 2047:
# set sign and stop tape
                    reserved[2]=2047
                    magtape_speed=0
# increment the count in reserved[2]
                else: reserved[2]+=1

            gui.update()
            k=0
# a stop order has been obeyed
    set_emulator_run_light(False)

# print output on terminal and/or in file
    x=get_output_setting()
    if x & 1: print_output_tapes(0)
    if x & 2: print_output_tapes(1)
# check for save magnetic tape
    if save_magnetic_tape():
        File=open("magtape.txt", 'wb')
        pickle.dump(magtape, File)
        File.close()
    return

# load order code functions
exec(open("ordercode.py").read())

def obey(order):
    global main_store_address, use_main_store

# unpack order
    m=order & 0x7FF
    op=(order >> 11) & 0x7F
    modifier_bits=order >> 18

# modify address by previous 2, 3, 4 or 5 orders
    m=m + modifier[0]
    modifier[0]=0

# test for main store instruction, use_main_store is set for each order
    use_main_store=False
    if mainstore_prog and modifier_bits==1 and (op<48 or op>=96):
# flag to potentially use main store
        use_main_store=True
# decode main store address
        if m & 0x400:
            if m & 0x200:
                if m & 0x100:
# negative free store address
                    use_main_store=False
                else:
# indirect modified
                     main_store_address=((main[m & 0xFF] >> 20) + modifier[b]) & 0x3FFF
            else:
# direct modified
                     main_store_address=(m & 0x1FF) + modifier[b]
        else:
            if m & 0x200:
# main store direct
                main_store_address=m & 0x1FF
            else: 
                if m & 0x100:
# main store indirect
                    main_store_address=(main[m & 0xFF] >> 20) & 0x3FFF
                else:
# free store positive
                    use_main_store=False
                    m=m &0xFF

# deal with modifier functions 64<=op<96, cannot have main store address
    if (op & 0x60) ^ 0x40:
# non-modifier function
        m=m + modifier[modifier_bits]
# obey order
        ops[op](m & 0x7FF)
    else:
# translate sr and tr modifiers, if first bit is 0 use relative address 
        if not (modifier_bits & 0x2): m=m + modifier[r]
# set q=2 for s modifier q=3 for t
        q=(modifier_bits & 0x1) | 0x2
# obey order
        ops[op](m & 0x7FF, q)

mainloop()
