#!/usr/bin/env python
# CdM8 emulator

# V1 By Prof. Alex Shaferenko.  July 2015
# V1.1. GUI modifications by M L Walters, August/Sept 2015
# V1.2. Solved bug in app.reload() where memory not displaying correctly

# Python 2 and 3 compatibility
from __future__ import absolute_import, division, print_function
try:
      input=raw_input # Python 3 style input()
except:
      pass

try:
      # Python 3 TK
      from Tkinter import *
except:
      # Else Python 2 TK
      from tkinter import *


import argparse
import random
random.seed()
import time


memory = 256 * [0]
Regs = [0,0,0,0]
PC = 0
SP = 0
CVZN = 0x0
BP=[]
HALT=False
Running = False
Traceprint=False
cntr=0
fnt=("courier",18)
########################################################## printed output

def EP(s,term=True):
    sys.stderr.write(s+'\n')
    if term:
        quit(-1)

##########################################################
def openimg(filename):
    try:
        imgfile=open(filename+'.img','r')
    except IOError:
        EP(filename+".img: file not found")
        quit(-1)
    return imgfile

parser = argparse.ArgumentParser(description='CdM-8 Emulator v1.0')
parser.add_argument('filename',type=str, help='memory_image_file[.img]')
parser.add_argument('-r',dest='run',action='store_const',const=True,default=False, help="run image and quit")
parser.add_argument('-l',dest='lst',action='store_const',const=True,default=False, help="display assembler listing (FILE.lst)")
parser.add_argument('-w',dest='trace', default="", help="comma-separated list of trace snapshots (format/location): [fmt:]addr[,[fmt:]addr...] with  fmt = x (hex) | d (decimal) | c (ASCII);  addr(hex) = xx (single address) | xx-xx (address range)")
parser.add_argument('-s',dest='save', default="", help="save for restore points (chk): chk:xx[-xx],chk:xx[-xx] ...")
parser.add_argument('-v3',dest='v3',action='store_const',const=True,default=False, help="assume CdM-8 Mark 3 instruction set")
parser.add_argument('-i',dest='ipoints', default="", help="comma-separated list of program execution addresses xx (hex) at which to display trace snapshots: xx[,xx...]")

args = parser.parse_args()
filename=args.filename
if filename[-4:]==".img":
    filename=filename[:-4]



trfmts=[]
traddrs=[]
if args.trace!="":
    trcmd=args.trace
    while trcmd!="":
        if len(trcmd)<2:
            EP("Invalid trace command"+trcmd)
        if trcmd[1]==":":
            trfmt=trcmd[0]
            trcmd=trcmd[2:]
        else:
            trfmt="x"
        if trcmd<2:
            EP("Invalid trace address"+trcmd)
        if len(trcmd)>=2:
            try:
                trfrom=int("0x"+trcmd[0:2],16)
            except :
                EP("Illegal address: "+trcmd[0:2])
            trto=trfrom

        if len(trcmd)==2:
            trcmd=""
        elif len(trcmd)>=3 and trcmd[2]==",":
            trcmd=trcmd[3:]
        elif len(trcmd)>=5 and trcmd[2]=="-":
            try:
                trto=int("0x"+trcmd[3:5],16)
            except :
                EP("Illegal address: "+trcmd[3:5])
            if len(trcmd)==5:
                trcmd=""
            elif trcmd[5]==",":
                trcmd=trcmd[6:]
            else:
                EP("Comma is expected after '"+trcmd[0:5]+"'")

        else:
            EP("Invalid format "+(trcmd+6*' ')[0:6])

        # if we are here, we have trfmt, trfrom, trto.

        while trfrom<=trto:
            traddrs+=[trfrom]
            trfmts+=[trfmt]
            trfrom+=1

if args.save!="":
    savestat=10*[None]
    savecmd=args.save
    while savecmd!="":
        if len(savecmd)<2:
            EP("Invalid save command"+savecmd)
        savepnt=savecmd[0]
        try:
            savepnt=int(savepnt)
        except:
            EP("Illegal save point number: single digit expected")
        savecmd=savecmd[2:]
        if savecmd<2:
            EP("Invalid save address"+savecmd)
        try:
            savefrom=int("0x"+savecmd[0:2],16)
        except :
            EP("Illegal address: "+trcmd[0:2])
        saveto=savefrom

        if len(savecmd)==2:
            savecmd=""
        elif len(savecmd)>=3 and savecmd[2]==",":
            savecmd=trcmd[3:]
        elif len(savecmd)>=5 and savecmd[2]=="-":
            try:
                saveto=int("0x"+savecmd[3:5],16)
            except :
                EP("Illegal address: "+savecmd[3:5])
            if len(savecmd)==5:
                savecmd=""
            elif savecmd[5]==",":
                savecmd=savecmd[6:]
            else:
                EP("Comma is expected after '"+savecmd[0:5]+"'")

        else:
            EP("Invalid format "+(savecmd+6*' ')[0:6])

        # if we are here, we have savepnt, savefrom, saveto.

        savestat[savepnt]=(savefrom,saveto)



IP=[]
if args.ipoints!="":
    for ip in (args.ipoints).split(","):
        try:
            ip0=int("0x"+ip,16)
        except :
            EP("Illegal address: "+ip)
            quit(-1)
        IP = IP+[ip0]



imgfile=openimg(filename)

adr=0
first=True
for s in imgfile:
    if first:
        if s[0:8]!="v2.0 raw":
            EP(filename+".img: not a Logisim image file")
            quit(-1)
        first=False
    else:
        memory[adr]=int("0x"+s[0:2],16)
        adr+=1
imgfile.close()

if args.save:
    for k in range(0,10):
        if savestat[k] is None:
            savestat[k]=[]
            continue
        (adr1,adr2)=savestat[k]
        savestat[k]=(adr1,adr2,list(memory[adr1:adr2+1]))


if args.lst:
    lstwin = Tk()
    lstwin.title("Program listing: "+filename)
    #make my screen dimensions work
    w = 900 #The value of the width
    h = 640 #The value of the height of the window

    # get screen width and height
    ws = lstwin.winfo_screenwidth()#This value is the width of the screen
    hs = lstwin.winfo_screenheight()#This is the height of the screen

    # calculate position x, y
    x = 500+ (w/2)
    y = 0

    #This is responsible for setting the dimensions of the screen and where it is
    #placed
    lstwin.geometry('%dx%d+%d+%d' % (w, h, x, y))


    try:
        lstfile=open(filename+'.lst','r')
    except IOError:
        EP(filename+".lst: file not found")
        quit(-1)


    scrollbar = Scrollbar(lstwin)

    lstfnt = (fnt[0],int(0.75*fnt[1]))
    text = Text(lstwin, yscrollcommand=scrollbar.set, wrap=NONE, font=lstfnt)
    scrollbar.configure(command=text.yview)
    scrollbar.pack(side=RIGHT, fill=Y)

    for s in lstfile:
        text.insert(INSERT, s)
    text.configure(state=DISABLED)
    text.pack(expand=True, fill='both')
    lstfile.close()

def hx(n):
    return format((n+256)%256,"02x")

def convert(fmt,k):
    if fmt==0:
        # hex
        return hx(k)
    if fmt==1:
        if k<128:
            #signed int
            kk=256+k
        else:
            # signed decimal
            kk=k
        return format(kk-256,"3d")
    if fmt==2:
        if k>=32 and k<127:
            return "'"+chr(k)+"'"
        elif k==0:
            return "NUL"
        else:
            return "..."
    if fmt ==3:
       return bin(k)

Pretend=True # Pretend that standard.mlb macros are real machine instructions
def disasm(adr):
    global Pretend
    IR = memory[adr]
    if IR & 0x80 == 0:
        fun = IR>>4
        opcode = ["move","add","addc","sub","and","or","xor","cmp"][fun]
        Rs=(IR>>2)&3
        Rd=IR&3

        if Pretend and Rs==Rd:
            if opcode == "move":
                return "tst r"+str(Rs)
            if opcode == "sub":
                return "clr r"+str(Rs)
            if opcode == "addc":
                return "shl r"+str(Rs)

        return opcode+' r'+str(Rs)+',r'+str(Rd)
    if IR>>5 == 0b100:
        fun = (IR>>2) & 7
        opcode = ["not","neg","dec","inc","shr","shla","shra","rol"][fun]
        Rd=IR&3
        return opcode+' r'+str(Rd)
    if IR>>5 == 0b101:
        fun = (IR>>4) & 1
        opcode = ["st","ld"][fun]
        Rs=(IR>>2)&3
        Rd=IR&3
        return opcode+' r'+str(Rs)+',r'+str(Rd)
    if IR>>4 == 0b1100:
        if args.v3:
            fun = (IR>>2) & 3
            opcode = ["push","pop","stsp","ldsp"][fun]
            Rd=IR&3
            return opcode+' r'+str(Rd)
        fun = (IR>>2) & 3

        if fun<3:
            opcode = ["push","pop","ldsa"][fun]
            Rd=IR&3
            return opcode+' r'+str(Rd)+(",0x"+hx(memory[adr+1]) if fun==2 else "")

        stsel=IR&3
        opcode=["addsp","setsp","pushall","popall"][stsel]
        if stsel==0 or stsel==1:
            return opcode+' '+hx(memory[adr+1])
        return opcode

    if IR>>2 == 0b110100:
        Rd=IR&3
        return "ldi r"+str(Rd)+",0x"+hx(memory[adr+1])
    if IR>>4 ==0b1101:
        fun = IR & 15
        opcode = ["","","","","halt","wait","jsr","rts","osi","rti","crc","osix"]+["<ext0>","<ext1>","<ext2>","<ext3>"]
        opcode = opcode[fun]
        if fun == 6 or fun == 11:
            return opcode+' 0x'+hx(memory[adr+1])
        else:
            return opcode
    if IR>>4 == 0b1110:
        opcode = ["eq","ne","hs","lo","mi","pl","vs","vc","hi","ls","ge","lt","gt","le","r","nop"]
        fun =IR & 15
        pref="b"
        if fun == 15:
            pref=""
        return pref+opcode[fun]+' 0x'+hx(memory[adr+1])
    if IR>>4 == 0b1111:
        return "bbne 0x"+hx((adr-1-(IR&15)+256) % 256)


def step():
    global PC, SP, IP, CVZN, memory, Regs, HALT, random
    def setZN(x):
        global CVZN
        CVZN = CVZN & 0b1100
        if x==0:
            CVZN = CVZN | 2
        elif x>=128:
            CVZN = CVZN | 1

    IR=memory[PC]

    if PC in IP or IR==0xd4:
        regstr=""
        for ind in [0,1,2,3]:
            regstr+=format(Regs[ind],"02x")+" "
        trace=format(cntr,"06d")+": "+"PC="+format(PC,"02x")+" Regs: "+regstr
        memstr=""
        if traddrs!=[]:
            tra=""
            for i in range(len(traddrs)):
                tr=traddrs[i]
                tra+=format(tr,"02x")+"  "
                trfmt=trfmts[i]
                if trfmt == 'x':
                    memstr+="  "+convert(0,memory[tr])
                elif trfmt == 'c':
                    memstr+=" "+convert(2,memory[tr])
                elif trfmt=="d":
                    memstr+=" "+convert(1,memory[tr])
                else:
                    EP("Internal error")
                tr+=1
            trace+=memstr
            global Traceprint
            if not Traceprint:
                print(34*" "+tra)
                Traceprint=True
        if (IR==0xd4):
            trace = trace + " <<< halt >>>"
        print(trace)

    if IR & 0x80 ==0:   # binary ALU
        fun = IR>>4
        Rs = (IR>>2) & 3
        Rd = IR & 3
        X=Regs[Rs]
        Y=Regs[Rd]
        if fun == 0: # move
            Res=X
            setZN(Res)
            CVZN=CVZN & 3

        elif fun == 1 or fun==2 or fun==3 or fun ==7:  # add/addc/sub/cmp
            C=0
            if fun == 3 or fun == 7:
                Y=Y^0xff
                C=1
            if fun == 2:
                C=CVZN>>3
            Res=(X+Y+C) % 256
            CVZN=0
            setZN(Res)
            if X+Y+C >= 256:
                CVZN = CVZN |  8
            if Res>=128 and X<128 and Y<128:
                CVZN=CVZN | 4
            if Res<128 and X>=128 and Y>=128:
                CVZN=CVZN | 4

        elif fun == 4:  # and
            Res = X  & Y
            setZN(Res)
        elif fun ==5:   # or
            Res=X | Y
            setZN(Res)
        elif fun == 6:  # xor
            Res = X ^ Y
            setZN(Res)

        if fun != 7:  # cmp
            Regs[Rd]=Res
            app.changeRegs(Rd)

        app.changeCVZN()
        app.changePC(PC+1)
        return

    if IR>>5==0b100: # ALU unary

        fun = (IR>>2) & 7
        Rd = IR & 3
        X = Regs[Rd]

        if fun == 0:        # not
            Res = X ^ 255
            setZN(Res)
            CVZN = CVZN & 3

        if fun == 1:        # neg
            Res = ((X ^ 255)+1) % 256
            CVZN=0
            setZN(Res)
            if Res>=128 and X>=128:
                CVZN = CVZN | 4
            if X == 0:
                CVZN = CVZN | 8

        if fun == 2:        # dec
            Res = (X+255) % 256
            if X==0:
                CVZN = CVZN & 7
            else:
                CVZN = CVZN | 8
            if X == 128:
                CVZN = CVZN | 4
            else:
                CVZN = CVZN & 11
            setZN(Res)

        if fun == 3:         # inc
            Res = (X+1) % 256
            if X!=255:
                CVZN = CVZN & 7
            else:
                CVZN = CVZN | 8
            if X == 127:
                CVZN = CVZN | 4
            else:
                CVZN = CVZN & 11
            setZN(Res)

        if fun == 4 or fun == 6:        # shr/shra
            Res = X>>1
            if (fun==6 and X>=128) or (fun==4 and CVZN&8 !=0 ):
                Res += 128
            CVZN = CVZN & 3
            CVZN = CVZN | ((X<<3)&8)
            setZN(Res)

        if fun == 5 or fun ==7:         # shla/rol
            Res = (2*X) % 256
            CVZN = 0
            if X>=128:
                CVZN = CVZN | 8
                if fun == 7:
                    Res = Res | 1
                if Res <128 and fun==5:
                    CVZN =CVZN | 4
            elif Res >=128 and fun==5:
                CVZN = CVZN | 4
            setZN(Res)
        Regs[Rd]=Res
        app.changeRegs(Rd)
        app.changeCVZN()
        app.changePC(PC+1)
        return

    if IR >> 5 == 0b101:    # memory
        load = (IR & 0b00010000)>>4
        Rs = (IR & 12) >> 2
        Rd = IR & 3

        if load==1:
            Regs[Rd] = memory[Regs[Rs]]
            app.changeRegs(Rd)
        else:
            memory[Regs[Rs]] = Regs[Rd]
            app.changememory(Regs[Rs])
        app.changePC(PC+1)
        return

    if IR >> 4 == 0b1100:    # stack
        ss = (IR & 12) >> 2
        Rd = IR & 3
        stsel=Rd   # selector for mark 4 architecture
        if ss == 0:  # push
            SP = (SP+255) % 256
            memory[SP]=Regs[Rd]
            app.changeSP()
            app.changememory(SP)

        if ss == 1:  # pop
            Regs[Rd] = memory[SP]
            SP = (SP+1) % 256
            app.changeSP()
            app.changeRegs(Rd)

        if ss == 2:  # stsp or ldsa
            if args.v3:
                SP = Regs[Rd]
                app.changeSP()
            else:   # mark 4 architecture, ldsa
                imop = memory[(PC+1+256)%256]
                Regs[Rd]=SP+imop
                app.changeRegs(Rd)
                app.changePC(PC+2)
                return

        if ss == 3:
            if args.v3:  # ldsp
                Regs[Rd]=SP
                app.changeRegs(Rd)
            else:
                if stsel==0 or stsel==1:
                    imop = memory[(PC+1+256)%256]
                    SP=((1-stsel)*SP+imop+256) % 256
                    app.changeSP()
                    app.changePC(PC+2)
                    return

                if stsel==2:    #pushall
                    for Rd in (3,2,1,0):
                        SP = (SP+255) % 256
                        memory[SP]=Regs[Rd]
                        app.changeSP()
                        app.changememory(SP)

                if stsel==3:    #popall
                    for Rd in (0,1,2,3):
                        Regs[Rd]=memory[SP]
                        SP = (SP+1) % 256
                        app.changeSP()
                        app.changeRegs(Rd)
        app.changePC(PC+1)
        return

    if IR >> 2 == 0b110100:      # ldi
        Rd = IR &3
        imop = memory[(PC+1+256)%256]

        Regs[Rd] = imop
        app.changeRegs(Rd)
        app.changePC(PC+2)
        return

    if IR >> 4 == 0b1101:        # 0-op
        vvww = IR & 0b00001111

        if vvww == 4 or vvww == 5:
            HALT = True
            # print(format(cntr,"06d")+": "+"PC="+format(PC,"02x")+" <<< halt >>>")
            return

        if vvww == 6:           # jsr

            SP = (SP+255) % 256
            memory[SP] = (PC+2+256)%256
            app.changeSP()
            app.changePC(memory[(PC+1+256)%256])
            app.changememory(SP)
            return

        if vvww == 7:           # rts

            app.changePC(memory[SP])
            SP = (SP+1) % 256
            app.changeSP()
            return

        if vvww == 10:          # crc
            temp=(PC+1+256)%256
            app.changePC(memory[SP])
            memory[SP]=temp
            app.changeSP()
            return

        if vvww == 15:
            k=random.randint(0,255)
            Regs[0]=k
            app.changeRegs(0)
            app.changePC(PC+1)
            return

        if vvww >=8:
            EP("Illegal opcode: "+str(IR),term=False)
            app.changePC(PC+1)

    if  IR >> 4 == 14:          # branches

        cccc = IR & 0b00001111

        reverse = cccc & 1

        ccc = cccc >> 1
        C = CVZN >>3
        V = (CVZN >>2) & 1
        Z = (CVZN >>1) & 1
        N = CVZN  & 1

        if ccc == 0:
            dcsn = Z
        elif ccc == 1:
            dcsn = C
        elif ccc == 2:
            dcsn = N
        elif ccc == 3:
            dcsn = V
        elif ccc == 4:
            dcsn = C & (~Z) & 1
        elif ccc == 5:
            dcsn = ~(N ^ V) & 1
        elif ccc == 6:
            dcsn = (~Z) & ~(N ^ V) & 1
        elif ccc == 7:
            dcsn = 1

        dcsn = reverse ^ dcsn

        if dcsn != 0:
            app.changePC(memory[PC+1])
        else:
            app.changePC(PC+2)
        return

    if  (IR >>4) == 0b1111:
        if args.save:               # not ldc, we are having a restore point
            savepnt = IR & 0b1111
            if savepnt>len(savestat)-1:
                EP("Illegal opcode: "+str(IR),term=False)
                app.changePC(PC+1)
                savepnt=0
            if savestat[savepnt]!=[]:
                (adr1,adr2,state)=savestat[savepnt]
                while adr1<=adr2:
                    memory[adr1]=state[0]
                    app.changememory(adr1)
                    adr1+=1
                    state=state[1:]
            app.changePC(PC+1)
            if args.trace:
                if PC not in IP:
                    IP+=[PC]
        else:
            app.changePC(PC+1)
            EP("Illegal opcode: "+str(IR),term=False)





###################################### GUI
memrep=[0]*256 # Keeps track of how memory rows are dislayed
class App:
    global BP,step,disasm
    def changePC(self,pc):
        global PC, BP, memrep
        if Running:
           PC=(pc+256) % 256
           return
        # Clear prev PC address highlight
        #print(memrep[PC]) # debug
        if memrep[PC]==3:
            self.canvas.itemconfig(self.mem[PC], font=(fnt[0], int(fnt[1]*.5), "normal"))
        else:
            self.canvas.itemconfig(self.mem[PC], font=(fnt[0], fnt[1], "normal"))

        if PC in BP:
            self.canvas.itemconfig(self.mem[PC], fill="red")#, font="-weight normal")
        else:
            self.canvas.itemconfig(self.mem[PC], fill="black")#, font="-weight normal")
        # Highlight new PC address
        if memrep[(pc+256)%256]==3:
            self.canvas.itemconfig(self.mem[(pc+256)%256], fill="green", font=(fnt[0], int(fnt[1]*.5), "bold"))
        else:
            self.canvas.itemconfig(self.mem[(pc+256)%256], fill="green", font=(fnt[0], fnt[1], "bold"))
        #self.canvas.itemconfig(self.mem[(pc+256)%256], fill="green", font=(fnt[0], int(fnt[1]*.5), "bold"))#"-weight bold")

        PC=(pc+256) % 256 # Update PC
        self.canvas.itemconfig(self.pc, text=hx(PC))
        self.canvas.itemconfig(self.asm, text=disasm(PC))

    def changeSP(self):
         global SP
         if Running: return
         self.canvas.itemconfig(self.sp, text=hx(SP))
         for i in range (0,25):
            ind = (SP+i+256)%256
            if ind<256 and ind>30:
                self.canvas.itemconfig(self.stk[i], text=hx(memory[ind]))
            else:
                self.canvas.itemconfig(self.stk[i], text="")
    def changeRegs(self,k):
        global Regs
        if Running: return
        self.canvas.itemconfig(self.rgs[k], text=hx(Regs[k])) # hex row
        self.canvas.itemconfig(self.rgsDec[k], text=convert(1,Regs[k])) # dec row
        self.canvas.itemconfig(self.rgsChar[k], text=convert(2,Regs[k])) # Char row
        #print("*"+convert(1,Regs[k]))# debug
        self.canvas.itemconfig(self.rgsBin[k], text=format(Regs[k],"08b")) # Bin row


    def changeCVZN(self):
        if Running: return
        global CVZN
        self.canvas.itemconfig(self.cvzn, text=format(CVZN,"04b"))

    def changememory(self, k):
        if Running: return
        global memory, memrep
        #print("memrep", k, " ", memrep[k])# debug
        if memrep[k]==0:
            self.canvas.itemconfig(self.mem[k], text=hx(memory[k]), font=fnt)#"-weight normal")
        else:
            #print("memrep="+str(memrep[k])) #debug
            if memrep[k]==3:
                  self.canvas.itemconfig(self.mem[k], text=format(memory[k],"08b"),
                        font=(fnt[0],int(fnt[1]*.5), "normal"))
            else:
                  self.canvas.itemconfig(self.mem[k], text=convert(memrep[k], memory[k]),
                        font=fnt)

    # Constants - note cannot change from within functions, only read values!
    mem=256*[0]
    rgs=[0]*4
    rgsDec=[0]*4
    rgsChar=[0]*4
    rgsBin=[0]*4
    stk=25*[0]
    pc=0
    sp=0
    asm=0
    svzn=0
    fmt=16*[0]
    no_of_fmts=4
    spdrawbase=950
    spdrheight=560

    def __init__(self, master):

        def draw_hdr(x, y, txt, style=None):
            if style== None: style=fnt
            return self.canvas.create_text(x,y,text=txt,fill='brown',font=style)

        def draw_val(x,y,txt, style=None):
            if style==None: style=fnt
            return self.canvas.create_text(x,y,text=txt,fill='black',font=style)

        def draw_clk(x,y,txt,tag, style=None):
            if style== None: style=fnt
            return self.canvas.create_text(x,y,text=txt,fill='black', tags=tag, font=style)

        def draw_hdr_clk(x,y,txt,tag, style=None):
            if style== None: style=fnt
            return self.canvas.create_text(x,y,text=txt,fill='brown', tags=tag, font=style)



        frame = Frame(master, bg="black", borderwidth=3)
        frame.pack()
        # Changed canvas size - Mick
        self.canvas = Canvas(frame, width=1050,height=630)
        self.canvas.pack()

        # Draw registers
        for k in range(0,4):
            # Register column labels
            draw_hdr(70+80*k,540,"r"+str(k), style=(fnt[0],fnt[1], "bold"))

        for k in range(0,4):
            # Hex
            self.rgs[k]=draw_val(70+80*k,560,hx(Regs[k]))
            # Dec
            self.rgsDec[k]=draw_val(70+80*k,580,convert(1,Regs[k]))
            # Char
            self.rgsChar[k]=draw_val(70+80*k,600,convert(2,Regs[k]))
            # Bin
            self.rgsBin[k]=draw_val(70+80*k,620,format(Regs[k],"08b"), style=(fnt[0],int(fnt[1]*.7+1)))




        # draw flags, PC
        draw_hdr(400,540,"CVZN", style=(fnt[0],fnt[1], "bold"))
        self.cvzn=draw_val(400,560,format(CVZN,"04b"))


        draw_hdr(500, 540,"PC", style=(fnt[0],fnt[1], "bold"))
        self.pc=draw_val(500, 560, hx(PC))
        draw_hdr(550,540,"=>")
        self.asm=draw_val(660, 540,disasm(PC)[0:12])

        # Draw Stack Pointer SP
        draw_hdr(self.spdrawbase, self.spdrheight-20,"SP", style=(fnt[0],fnt[1], "bold"))
        self.sp=draw_val(self.spdrawbase, self.spdrheight, hx(SP))

        draw_hdr(self.spdrawbase+30, self.spdrheight,"=>", style=(fnt[0],fnt[1], "bold"))
        for i in range(0,25):
            xpos = self.spdrawbase+60
            ypos = self.spdrheight-20*i
            ind = (SP+i+256)%256
            if ind>30 and ind<256:
                self.stk[i]=draw_val(xpos, ypos, hx(memory[ind]))
            else:
                self.stk[i]=draw_val(xpos, ypos, "")

        # Mouse event to change memory representation
        membutton = [0]*16
        for i in range(0,16):
            # define click areas to call togglerep()
            # hd=draw_hdr_clk(70-40,30+i*30,format(15-i,"01x"),str(15-i))
            # self.canvas.tag_bind(hd,"<Button-1>", lambda ev: self.togglerep(ev))

            # Change to use tk buttons - Mick
            membutton[i] = Button(self.canvas, text=format(15-i,"01x"), font=(fnt[0],int(fnt[1]*.8), "bold"),
                                    fg="brown",
                                    command=lambda memrow=15-i: self.togglerep(memrow) )
            membutton[i].place(x=5, y=13+i*30)

        # Lower memory address column labels
        for j in range(0,16):
            draw_hdr(70 + j*55,510,format(j,"01x"), style=(fnt[0], int(fnt[1]*.9)))

        # Memory contents as table
        #memrep=[0]*256 # Keeps track of how memory rows are dislayed
        for i in range(0,16):
            for j in range(0,16):
                index=(15-i)*16+j
                mm=draw_clk(70 + j*55,30+i*30, hx(memory[index]),str(index))
                self.mem[(15-i)*16+j]=mm
                self.canvas.tag_bind(mm,"<Double-Button-1>", lambda ev: self.settrap(ev))



        self.Step =Button(master, text=" Step  ", command = lambda : self.makestep())
        self.Step.pack(side=LEFT)

        self.Run =Button(master, text=" RUN   ", command = lambda : self.dorun())
        self.Run.pack(side=LEFT)

        self.slowrunning=False
        self.SlowRun =Button(master, text=" Slow RUN   ", command = lambda : self.slowrun(delay=0.5))
        self.SlowRun.pack(side=LEFT)

        self.Reload =Button(master, text="Reload", command = lambda : self.reload())
        self.Reload.pack(side=LEFT)

        self.Quit = Button(master, text="Quit", command = lambda: self.endprog())
        self.Quit.pack(side=LEFT)

    def endprog(self):
        self.slowrunning=False
        root.update()
        time.sleep(0.1)
        quit(0)

    def reload(self):
        global filename,memory,PC,SP,BP,CVZN,Regs,fmt
        imgfile=openimg(filename)
        adr=0
        first=True
        PC=0
        SP=0
        self.changeSP()
        CVZN=0
        self.changeCVZN()
        Regs=[0,0,0,0]
        for k in range(0,4):
            self.changeRegs(k)
        for bp in BP:
            self.canvas.itemconfig(self.mem[bp], fill="red")#, font="-weight bold")

        for s in imgfile:
            if first:
                if s[0:8]!="v2.0 raw":
                    EP(s+"\n wrong file")
                    quit(-1)
                first=False
            else:
                memory[adr]=int("0x"+s[0:2],16)
                ### ??? Problem here!!! - solved! MLW
                self.canvas.itemconfig(self.mem[adr], text=convert(memrep[adr], memory[adr]))
                #print("Memrep adr = "+str(adr)+" Value = "+str(memrep[adr])) #Debug
                if memrep[adr]==3:
                  self.canvas.itemconfig(self.mem[adr], fill="black", font=(fnt[0], int(fnt[1]*0.5), "normal"))
                  self.canvas.itemconfig(self.mem[adr], text=format(memory[adr],"08b"))
                else:
                  self.canvas.itemconfig(self.mem[adr], fill="black", font=(fnt[0], int(fnt[1]), "normal"))
                  self.canvas.itemconfig(self.mem[adr], text=convert(memrep[adr], memory[adr]))
                adr+=1
        imgfile.close()
        self.changePC(0)
        SP=0
        self.changeSP()
        CVZN=0
        self.changeCVZN()
        Regs=[0,0,0,0]
        for k in range(0,4):
            self.changeRegs(k)
        for bp in BP:
            self.canvas.itemconfig(self.mem[bp], fill="red")#, font="-weight bold")

    def makestep(self):
        step()


    def slowrun(self, delay=0.5):
            if self.slowrunning==True:
                  self.slowrunning=False
                  self.SlowRun["text"]=" Slow RUN"
            else:
                self.slowrunning=True
                self.SlowRun["text"]="    PAUSE    "
                #self.makestep()
                while self.slowrunning:
                    if (PC in BP) or (memory[PC]==0xd4):
                        self.slowrunning=False
                        self.SlowRun["text"]="Slow RUN"
                        break
                    self.makestep()
                    root.update()
                    time.sleep(delay)


    def dorun(self):
        global PC,step, HALT, Running, cntr
        cntr=0
        Running=True
        #self.canvas.itemconfig(self.mem[PC], fill="black")#, font="-weight normal")# this does not work?
        if memrep[PC]==3:
            self.canvas.itemconfig(self.mem[PC], fill="black", font=(fnt[0], int(fnt[1]*0.5), "normal"))
        else:
            self.canvas.itemconfig(self.mem[PC], fill="black", font=(fnt[0], int(fnt[1]), "normal"))
        while not HALT:
            step()
            cntr+=1
            if cntr>=50000:
                EP("Max step count reached... bailing out")
                break
            if PC in BP:
                break
        HALT = False
        Running=False
        for k in range(256):
            #pass
            #self.canvas.itemconfig(self.mem[k], text=convert(memrep[k], memory[k]))
            self.changememory(k)


        self.changeSP()
        self.changeCVZN()
        for k in range(0,4):
            self.changeRegs(k)

        for bp in BP:

            if memrep[bp]==3:
                  self.canvas.itemconfig(self.mem[bp], font=(fnt[0], int(fnt[1]*0.5), "normal"))
            else:
                  self.canvas.itemconfig(self.mem[bp], font=(fnt[0], int(fnt[1]), "normal"))
            if bp==PC:
                  self.canvas.itemconfig(self.mem[bp], fill="green", font="-weight bold")
            else:
                  self.canvas.itemconfig(self.mem[bp], fill="red")#, font="-weight bold")

        self.changePC(PC)



    def settrap(self,event):
        global BP
        canvas = event.widget
        x = canvas.canvasx(event.x)
        y = canvas.canvasy(event.y)
        item=canvas.find_closest(x, y)
        index=int(canvas.gettags(item)[0])
        found=False
        for k in range(len(BP)):
            if BP[k]==index:
                found=True
                del BP[k]
                break

        #itemfont = tkFont.font(item['font'])    # Get its font
        if found:
            if index==PC:
                  canvas.itemconfig(item,fill="green", font="-weight bold")
            else:
                canvas.itemconfig(item,fill="black")
        else:
            canvas.itemconfig(item,fill="red")
            #print("wt normal!")# debug
            BP+=[index]

    def togglerep(self, i):
        global BP,no_of_fmts, memrep
        #canvas = event.widget
        #x = canvas.canvasx(event.x)
        #y = canvas.canvasy(event.y)
        #item=canvas.find_closest(x, y)
        #i=int(canvas.gettags(item)[0])
        # print("*"+str(i)) #debug
        self.fmt[i] = (self.fmt[i]+1) % self.no_of_fmts
        for j in range(0,16):
            memrep[i*16+j]=self.fmt[i]
            if self.fmt[i]==3:
                  self.canvas.itemconfig(self.mem[i*16+j], text=format(memory[i*16+j],"08b"),
                        font=(fnt[0],int(fnt[1]*.5)))
            else:
                  self.canvas.itemconfig(self.mem[i*16+j], text=convert(memrep[i*16+j], memory[i*16+j]),
                        font=fnt)


app=0
if args.run:
    class apc:
        def changePC(self,n):
            global PC
            PC=(n+256) % 256
            return
        def changeRegs(self,k):
            return
        def changeCVZN(self):
            return
        def changeSP(self):
            return
        def changememory(self,k):
            return
    app=apc()
    app.changePC(0x00)
    cntr=0
    Running=True
    while not HALT:
        step()
        cntr+=1
        if cntr>=50000:
            EP("Max step count reached... bailing out")
            break
    quit(0)



root = Tk()
root.title("CdM-8 System Level Emulator: "+filename)

# scale font size to screen resolution/size
#scrht = root.winfo_screenheight()
#fnt=('courier',int(scrht//80+1), "normal") # scale font size to screen resolution/size
fnt=('courier', 15, "normal") #looks better without scaling!
#print (scrht, " font = ",fnt) # Debug


app = App(root)
app.changePC(0x00)

root.mainloop()
#root.destroy() # optional
