#### Simulator for a simple CPU (sARM.py) - Version V1.1 of 3 April 2014 #### (c) Alan Clements 2014 #### License: For non-commercial use #### http://alanclements.org/sarmassembler.html #### This program was written and tested using Notepad and Python 3.3 (with IDLE) on a PC system running Windows 8.1 #### This program takes a text file in .txt format and executes the code line-by-line #### The simulator displays the register contents (eight registers) #### In this version there is only a small memory and stack #### The simulated CPU provide RISC, CISC and stack processor instructions because it is designed to help students experiment with instruction sets #### Author: Alan Clements #### Email address: clementscomputerorganization AT gmail DOT com #### This program expects a notepad-compatible source file with the extension .txt #### If you answer n to the request to turn of single-step, an instruction is executed at each carriage return #### Register contents are displayed in both decimal and hexadecimal values. THe PC, stack, and 8 memory locations are displayed as well as the instruction import sys import random random.seed() #initialise random number generator ####This version uses a predefined source file located C:\Users\Alan\Desktop\assembtest.txt. Other users will have to change this. ##Splash header print('sARM simulator: A simple ARM style simulator ') print('This simlates RISC CISC and stack-based code ') print('sARM is not intended as tool for writing serious assembly programs but as a means of introducing assembly language ') print('(c) Alan Clements 2014') print('') Ask = input("Do you wish to use the default file? Type 'Yes' or 'No' ") #Use either pre-defined file or provide a default source file nada = ('No','NO','no','n','N') if Ask in nada: file = open(input('Input file to assemble '), 'r') else: file = open("C:\\Users\Alan\\Desktop\\assembtest.txt", 'r') #Open the existing sample file prog = file.readlines() #prog is the original source file as text. We have to process this to remove spaces and resolve labels file.close # This list defines all opcodes all_ops = {'ADD', 'ADD_M','M_ADD','AND','BEQ','B','BGT','BL','BLT','BNE','BRA','BSR','DCB','CLR','CMP','DEC','DCBZ','END','EOR','EQU','IN','INC', 'JMP','LDR','LSL','LSR','MIN','MAX','MLA','MMP', 'MOV','MUL','NAM','NOT','NOP','OR','OUT', 'PRINT','PUSH','PULL','RBEQ','RND','RTL', 'RTS','SEQ','S_MUL','SNE','STOP','STR','SUB','S_ADD','S_SUB','S_DUP','S_SWAP','TRAP'} class_d = {'END' : 2, 'EQU':3} class_0 = {'NOP' : 10, 'RTS': 11, 'STOP': 12, 'RTL': 13} class_1 = {'CLR' : 21, 'DEC': 23, 'INC': 22, 'NOT': 24, 'RND': 27, 'IN': 28, 'OUT': 29} class_1a = {'B' : 31, 'BL' : 30, 'BRA': 31, 'BEQ': 32, 'BNE': 33, 'BSR': 39, 'BGT': 303, 'BLT': 304} class_1b = {'DCBZ' : 38} class_2a = {'MOV' : 41, 'CMP': 42, 'LSL': 43, 'LSR': 44, 'ADD_M': 48, 'M_ADD': 49} class_2b = {'LDR' : 51, 'STR': 52, 'MMP': 59} class_2c = {'JMP' : 501} class_3 = {'ADD' : 61, 'AND': 63, 'EOR': 65, 'MUL': 66, 'OR': 64, 'SUB': 62, 'MLA': 67} class_3a = {'MIN' : 601, 'MAX': 602, 'SEQ': 610, 'SNE': 611} class_3b = {'RBEQ' : 610} class_4 = {'PUSH' : 71, 'PULL': 72} class_4a = {'S_ADD': 81, 'S_SUB':82, 'S_MUL':83, 'S_DUP':84, 'S_SWAP': 85} class_5 = {'TRAP' : 91} source = [] #Here's where the source code will go labeltable = {} #This is for the symbol/label table data_memory = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] #data memory (it's small) reg = [0,0,0,0,0,0,0,0] #register fle stack = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] #16 location stack # these functions are used to extract one or more operands from an op-code def operand1(instruction): #get one operand in a register reg_name = instruction[1] reg_num = int(reg_name[1:]) return reg_num def operand2(instruction): #get two operands: register and register/literal if len(instruction) < 3: #check that we have two operands print('Too few operands in 3-operand instruction at', old_PC, 'instruction', instruction) sys.exit() #Exit on insufficent operands L = False d = int(instruction[1][1:]) #Get the destation - first elelent - strip R convert to integer s_name = instruction[2] if s_name[0] == 'R': s = int(s_name[1:]) if s_name[0] == '#': L = True if s_name[1:3] =='0B': #check for binary integer s = int((s_name[1:]),2) elif s_name[1:3] =='0X': #check for hexadeximal integer s = int((s_name[1:]),16) elif s_name[1:].isdigit(): s = int(s_name[1:]) elif s_name[1] == '-': s = -int(s_name[2:]) else: s = symboltable.get(s_name[1:],-1) if s == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if L and (s > 255): print('Literal range error. Program halted at PC =',PC-1) sys.exit() return d,s,L def operand2a(instruction): #get 2 operands: register and memory address for LDR and STR instructions error = False # reg_name = instruction[1] # read first operand d = int(reg_name[1:]) #d is the register number of the first operand s_name = instruction[2] #get the second operand if s_name[0] == '[' and s_name[1] == 'R' and s_name[3] == ']': #ensure that operand 2 is of the form [R0] s = int(s_name[2]) #if correct form get the register number else: #if not correct form, print message and stop error = True if error or s > 7: print('Address mode error in LDR or STR') sys.exit() return d,s,L def operand3(instruction): #get 3 operands: d, s1, s2 L is true for a literaloperand if len(instruction) < 4: #check that we have three operands print('Too few operands in 3-operand instruction at', old_PC, 'instruction', instruction) sys.exit() #Exit on insufficent operands L = False d = int (instruction[1][1:]) #get destination register number s1 = int (instruction[2][1:]) #get source 1 register number s2_name = instruction[3] #get source 2 name to test for r0 or #12 or #-12 or #Label if s2_name[0] == 'R': s2 = int (s2_name[1:]) #if it's a register, get the number if s2_name[0] == '#': #if it's a literal, sort out integer, negative, lable L = True if s2_name[1:3] =='0B': #check for binary integer s2 = int((s2_name[1:]),2) elif s2_name[1:3] =='0X': #check for hexadeximal integer s2 = int((s2_name[1:]),16) elif s2_name[1:].isdigit(): #if digit, strip off # and convert to integer s2 = int(s2_name[1:]) elif s2_name[1] == '-': #if negative, strip off # and -, convert to integer and negate s2 = -int(s2_name[2:]) else: s2 = symboltable.get(s2_name[1:],-1) #if none of the above, look up in the symbol table if s2 == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if s2 > 255: sys.exit() return d,s1,s2,L #here we provide some stack opeations #note that the program is inconsistant - some instructions are executed in-line and some as functions def PUSH (SP,destination,L): #define some stack operations SP = SP - 1 if not L: stack[SP] = reg[destination] #push register else: stack[SP] = destination #push literal return SP def PULL (SP,a): reg[a] = stack[SP] SP = SP + 1 return SP def StackAdd(SP): p = stack[SP] SP = SP + 1 q = stack[SP] stack[SP] = p + q return SP def StackSub(SP): p = stack[SP] SP = SP + 1 q = stack[SP] stack[SP] = p - q return SP def StackMul(SP): p = stack[SP] SP = SP + 1 q = stack[SP] stack[SP] = p * q return SP def StackDup(SP): p = stack[SP] SP = SP - 1 stack[SP] = p return SP def StackSwap(SP): p = stack[SP] q = stack[SP+1] stack[SP] = q stack[SP+1] = p return SP def convert(num_val): print('num_value',num_val) if num_val.startswith('0X'): return int(num_val,16) elif num_val.startswith("0B"): return int(num_val,2) else: num_val = symboltable.get(num_val,-1) #find PC value of the label and jump if num_val == -1: # print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() return int(num_val) def display(): #display machine status inst1 = ' '.join(inst) #make a temporary string of the instruction for printing if not single_step: print() #print blank line but not in sinrungle step mode print('PC ={0:3}'.format(old_PC),'Registers', '%3s'%reg[0],'%3s'%reg[1],'%3s'%reg[2],'%3s'%reg[3],'%3s'%reg[4],'%3s'%reg[5],'%3s'%reg[6],'%3s'%reg[7], '{0:25}'.format(inst1),'Z =',Z,'N =',N,'C =',C,'V =',V,'SP =',SP, 'LR =',LR, 'Memory',data_memory[0:8],'Stack =',stack[SP:-1]) print('Registers in hexadecimal', ' '.join([hex(i) for i in reg])) return ############################################################################################################ # This is entry point to the code # # First process the text file containing the assembly language # # We generate a new source file with comments etc removed and interpret that # ############################################################################################################ #first clean up the source file #here we remove blank lines and comment lines #begin by prefixing labels with $ to make them identifiable in later processing for line in range(0, len(prog)): #read all lines codeline = prog[line] codeline = codeline.upper() #convert to upper case while ',' in codeline: #remove commas codeline = codeline.replace(',',' ') #convert commas to spaces while ' ' in codeline: #remove multiple spaces codeline = codeline.replace(' ',' ') if codeline[0].isalpha(): #if first element is a character then prepend a $ marker codeline = '$' + codeline prog[line] = codeline prog = [l.strip() for l in prog if l.strip()] #remove blank lines prog is now the partically tidied file prog1 = [] #create new text file without blank lines and cooment lines for line in range(0, len(prog)): #scan each line and remove those beginning with ; codeline = prog[line] if codeline[0] != ';': prog1.append(codeline) if codeline[0] == ';': continue for line in range(0, len(prog1)): codeline = prog1[line] for word in range(0, len(codeline)): #now remove any comment fields token = codeline[word] #read a token from the command if token.startswith(';'): #if it's a comment codeline = codeline[0:word] #then just keep previous fields prog1[line] = codeline break #stop scanning when ";" found #look for repeated labels duplicates = {} for line in range(0, len(prog1)): #scan the code line by line codeline = prog1[line].split() #convert string to list of tokens if codeline[0].startswith('$'): #look for a label that starts with '$' if codeline[0] in duplicates: #If it's already in the list we have a duplicate print('ERROR - second instance of label',codeline[0],'found at line',line) sys.exit() #And stop else: duplicates.update({codeline[0]:line}) #If label not repeated add to the table symboltable = {} #create a symbol table with labels prog2 = [] #remove equate directives and put data in symbol table for line in range (0, len(prog1)): equate = False codeline = prog1[line].split() #codeline is the current line as a sequence of lists for word in range(0,len(codeline)): if codeline[word] == 'EQU': #check for an equate directive equate = True label = codeline[0] #get the label (first item) if codeline[2].isdigit(): #check whether the operand is a numeric value or another label value = int(codeline[2]) #if it's a numeric value, capture it else: value = symboltable.get(codeline[2]) #else look look up the label in the symbol table symboltable.update({label[1:]:value}) #update the symbol table (remove $ from label) if not equate: prog2.append(codeline) #if this is not an equate line, then add it to the final source code for line in range(0, len(prog2)): #look for labels and add them to the symboltable inst = prog2[line] if inst[0].startswith('$'): newlabel = inst[0].replace('$','') #rememver to delete the $ that we've used as a label marker if newlabel in symboltable: #test for multiply defined label print('Label clash at line',line,'Progream terminated') #provide error message and go home sys.exit() #terminate on error symboltable.update({newlabel:line}) #update the symbol table print ('Symbol table ') #display the symbol table for labels,values in symboltable.items(): print(labels, values) print() listing = [] #Create and print a listing file with no blanks, and no labels for i in range (0,len(prog2)): this_line = prog2[i] #get a ine if this_line[-1] == '@BREAK': #is the rightmost element a break command? this_line.pop() #if is is then drop if off the list if this_line[0][0] == '$': #does the line start with a label? listing.append(this_line[1:]) #if so, copy the line without the label else: listing.append(this_line) print('Line', '%3s'%i,' ',' '.join(listing[i])) # At this stage we're good to go. We have a program in prog2 ########################################################################################################################### # Main loop - read and execute the code # ########################################################################################################################### single_step = True #Determines whether we execute line by line or run continuously - initally assume single step mode if input('Enter "y" to turn off single step mode ') == 'y': #ask if we want to turn single step off single_step = False b_lab = False #turn off (initially) trace on label mode run = True #Execution continues while run is true cycle = 0 #number of instructions executed - we are recording the instruction count trace = False #trace flag: true to start trace mode and false to turn it off first_time = True #This flag supresses printing the first line Z,N,C,V = 0,0,0,0 #Status flags: Zero, negative, carry, overflow ***NOT YET FULLY IMPLEMENTED*** PC = 0 #program counter SP = 15 #initialize stack pointer - stack frows to lower addresses LR = 0 #link register for use by branch and link instructions reg_history = [] r0,r1,r2,r3,r4,r5,r6,r7 =[],[],[],[],[],[],[],[] #Define the NAMED register set. THIS FUNCTION IS NOT YET USED. IT WAS INTENDED TO HOLD PRESERVED VALUES while run: #MAIN LOOP until Run is false, or STOP command, or error old_PC = PC #save current PC for later display inst = prog2[PC] #read the next instruction to be executed NOTE: inst is the instruction, opcode is the instruction without any label PC = PC + 1 if inst[0].startswith('$'): #get opcode which is first or second element opcode = inst[1:] #second element if we have a lable else: opcode = inst[0:] #first element if we don't have a label if opcode[0] not in all_ops: #check for valid op=code print ('Ilegal opcode error') #if not in the list then stop and exit print ('Operation',opcode[0],'in line',opcode,'is not in the instruction set') sys.exit() #exit here inst_class = class_0.get(opcode[0],0) #look for entry in class 0 if inst_class == 10: #NOP pass #Do nothing for NOP if inst_class == 11: #RTS PC = stack[SP] #Pull PC off the stack SP = SP + 1 #increment stack pointer if inst_class == 13: #RTL (return using link register) PC = LR #Copy link register to PC to return if inst_class == 12: #look for STOP run = False #clear the run flag to halt execution print('STOP execution at PC =',old_PC,'Total number of instructions',cycle) break #terminate execution messageand leave the execution loop if (class_1.get(opcode[0])) == 21: #test for CLR (CLR r0 clears register, CLR 4 clears memory, CLR Lable clears memory) clr_operand = opcode[1] #get the operand which is r0, 123, or label if clr_operand[0] == 'R': #if the operand begins with R, extract the register number and clear it reg[int(clr_operand[1])] = 0 elif clr_operand[0].isdigit(): #now look for numeric operand data_memory[int(clr_operand)] = 0 #clear the memory elif clr_operand[0].isalpha(): d = symboltable.get(clr_operand) #if not numeric then try the symbol table data_memory[d] = 0 #clear the memory if (class_1.get(opcode[0])) == 27: #test for RND (generate random number in range 0 to 255) reg[int(opcode[1][1:])] = random.randint(0,255) #increment the register if (class_1.get(opcode[0])) == 22: #test for INC (increment register) d = int(opcode[1][1:]) #get the register address reg[d] = reg[d] + 1 #increment the register if (class_1.get(opcode[0])) == 23: #test for DEC (increment register) d = int(opcode[1][1:]) #get the register address reg[d] = reg[d] - 1 #increment the register if (class_1.get(opcode[0])) == 24: #test for NOT (Boolean invert) ~~~@@@TEST@@@~~~ d = inst[1] #get the register address d = int(d[1:]) print (' X =',d, bin(d)) reg[d] = ~reg[d]&0xFF #complement the register print ('NOT X =',d, bin(d)) if (class_1.get(opcode[0])) == 28: #test for IN (read char into specified register) d = int(opcode[1][1:]) #get the register address reg[d] = int(input('Enter number into register ')) if (class_1.get(opcode[0])) == 29: #test for OUT (print char in register) d = int(opcode[1][1:]) #get the register address print ("Contents of register ",d, " are: ",reg[d]) if (class_1a.get(opcode[0])) == 30: #test for BL (BL 123, or BL NEXT) target = opcode[1] #get the operand (integer or label) LR = PC #save return address in link register if (target.isdigit()): #see if the opeand is numeric PC = int(target) #if target is an integer than load PC else: #otherwise, we need to look up label value PC = symboltable.get(target) #find PC value of the label and jump to it if (class_1a.get(opcode[0])) == 31: #test for BRA (BRA 123, or BRA NEXT) target = opcode[1] #get the operand (integer or label) if (target.isdigit()): #see if the opeand is numeric PC = int(target) #if target is an integer than load PC else: #otherwise, we need to look up label value PC = symboltable.get(target,-1) #find PC value of the label and jump to it if PC == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if (class_1a.get(opcode[0])) == 39: #test for BSR SP = SP - 1 #push return address stack[SP] = PC target = opcode[1] #get the operand (int or label) if (target.isdigit()): PC = int(target) #if target is an integer than load PC else: PC = symboltable.get(target,-1) #find PC value of the label and jump if PC == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if (class_1a.get(opcode[0])) == 32: #test for BEQ (e.g., BEQ 123, or BEQ NEXT) target = opcode[1] #get the operand (integer or label) if (target.isdigit()): #see if the opeand is numeric if Z == 1: #test Z-bit PC = int(target) #if target is an integer than load PC else: #otherwise, we need to look up label value if Z == 1: #test Z-bit PC = symboltable.get(target,-1) #find PC value of the label and jump to it if PC == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if (class_1a.get(opcode[0])) == 33: #test for BNE (e.g., BNE 123, or BNE NEXT) target = opcode[1] #get the operand (integer or label) if (target.isdigit()): #see if the opeand is numeric if Z != 1: #test Z-bit PC = int(target) #if target is an integer than load PC else: #otherwise, we need to look up label value if Z != 1: #test Z-bit PC = symboltable.get(target,-1) #find PC value of the label and jump to it if PC == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if (class_1a.get(opcode[0])) == 303: #test for BGT (e.g., BGT 123, or BGT NEXT) target = opcode[1] #get the operand (integer or label) if (target.isdigit()): #see if the opeand is numeric if N != 1: #test N-bit (not negative to take branch) PC = int(target) #if target is an integer than load PC else: #otherwise, we need to look up label value if N != 1: #test N-bit PC = symboltable.get(target,-1) #find PC value of the label and jump to it if PC == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if (class_1a.get(opcode[0])) == 304: #test for BLT (e.g., BLT 123, or BLT NEXT) target = opcode[1] #get the operand (integer or label) if (target.isdigit()): #see if the opeand is numeric if N == 1: #test N-bit (not negative to take branch) PC = int(target) #if target is an integer than load PC else: #otherwise, we need to look up label value if N == 1: #test N-bit PC = symboltable.get(target,-1) #find PC value of the label and jump to it if PC == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() if (class_3b.get(opcode[0])) == 610: #test for RBEQ (e.g., RBEQ r0,r1,Label i.e., branch to Label on r0=r1) print('opcode', opcode) if reg[int(opcode[1][1:])] == reg[int(opcode[2][1:])]: #compare the two registers if (opcode[3].isdigit()): #see if the operand is numeric i.e., the register number PC = int(opcode[3]) else: target = symboltable.get(opcode[3],-1) if target == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() PC = int(target) if (class_2c.get(opcode[0])) == 501: #test for JMP (e.g., JMP [r0]) target = opcode[1] #get the operand [[r0] if (target[2].isdigit()): #see if the opeand is numeric i.e., the register number PC = reg[int(target[2])] #If all is well, then do the jump - load the PC with the new address if (class_1b.get(opcode[0])) == 38: #test for DCBZ (e.g., DCBZ r0,#10 or DCBZ r0,NEXT) target = opcode[2] #this is a loop branch that ends on 0 count or Z = 1 counter = opcode[1] d = int(counter[1:]) if not(target.isdigit()): target = symboltable.get(target,-1) if target == -1: #if label not valid, display error message and stop print('Label error: Label',target,'in line',opcode,'is not in the symbol table') sys.exit() reg[d] = reg[d] - 1 if reg[d] !=0 and Z ==0: PC = int(target) if (class_2a.get(opcode[0])) == 41: #test for MOV PC,LR (special case of MOV) if opcode[1] == 'PC' and opcode[2] == 'LR': PC = LR #if we find MOV PC,LR load PC with return address and leave if (class_2a.get(opcode[0])) == 41: #test for MOV (e.g., MOV r1,r2 or MOV r3,#12) p,q,L = operand2(opcode) if L: reg[p] = q else: reg[p] = reg[q] if (class_2a.get(opcode[0])) == 42: #test for CMP p,q,L = operand2(opcode) if L: a, b = reg[p], q else: a, b = reg[p], reg[q] Z,N = 0,1 #set Z-bit to 0 and N-bit to 1 initially if (a - b) == 0: # do comparison Z,N = 1,0 #set Z = 1 and N = 0 if same if (a - b) > 0: N = 0 #if a > b then clean N as not negative if (class_2a.get(opcode[0])) == 43: #test for LSL (e.g., LSL r1,r2 or LSL r3,#12) p,q,L = operand2(opcode) if L: reg[p] = reg[p] << q else: reg[p] = reg[p] << reg[q] if (class_2a.get(opcode[0])) == 44: #test for LSR p,q,L = operand2(opcode) if L: reg[p] = reg[p] >> q else: reg[p] = regp[p] >> reg[q] if (class_2b.get(opcode[0])) == 51: #test for LDR (load register register indirect) p,q,L = operand2a(opcode) reg[p] = data_memory[reg[q]] if (class_2b.get(opcode[0])) == 52: #test for STR (store register register indirect) p,q,L = operand2a(opcode) data_memory[reg[q]] = reg[p] if (class_2b.get(opcode[0])) == 59: #test for MMP move [rs]+ to [rd]+ d1 = inst[1] #this is a memory-to-memory move with auto postincrementing d2 = int(d1[1:]) s1 = inst[2] s2 = int(s1[1:]) data_memory[reg[d2]] = data_memory[reg[s2]] reg[d2] = reg[d2] + 1 reg[s2] = reg[s2] + 1 if (class_3.get(opcode[0])) == 61: #test for ADD r0,r1,r2 (d = s1 + s2) p,q,r,L = operand3(opcode) a = reg[q] if L: #get register value or literal for source 2 b = r else: b = reg[r] if (a > 127) or (b > 128) or a < -128 or b < -128: print ('Source range error in addition at PC =',PC) sys.exit() Z,N,C,V = 0,0,0,0 s = a + b if s == 0: Z = 1 if s < 0: N = 1 if s > 127: C = 1 if s < -128: C = 1 if (((a > 0) and (b > 0) and (s > 7)) or ((a < 0) and (b < 0) and (s > 8))): V = 1 reg[p] = s if (class_3.get(opcode[0])) == 67: #test for MLA r0,r1,r2 (d = d + s1 * s2) p,q,r,L = operand3(opcode) s = reg[p] a = reg[q] if L: #get register value or literal for source 2 b = r else: b = reg[r] if (a > 127) or (b > 128) or a < -128 or b < -128: print ('Source range error in addition at PC =',PC) sys.exit() Z,N,C,V = 0,0,0,0 s = s + a * b if s == 0: Z = 1 if s < 0: N = 1 if s > 127: C = 1 if s < -128: C = 1 reg[p] = s if (class_3.get(opcode[0])) == 62: #test for SUB Z,N = 0,0 p,q,r,L = operand3(opcode) if L: reg[p] = reg[q] - r else: reg[p] = reg[q] - reg[r] if reg[p] == 0: Z = 1 if reg[p] < 0: N = 1 if (class_3.get(opcode[0])) == 66: #test for MUL Z,N = 0,0 print('multiply') p,q,r,L = operand3(opcode) if L: reg[p] = reg[q] * r else: reg[p] = reg[q] * reg[r] if reg[p] == 0: Z = 1 if reg[p] < 0: N = 1 if (class_3.get(opcode[0])) == 63: #test for AND p,q,r,L = operand3(opcode) if L: reg[p] = reg[q] & r else: reg[p] = reg[q] & reg[r] if reg[p] == 0: Z = 1 if (class_3.get(opcode[0])) == 64: #test for OR p,q,r,L = operand3(opcode) if L: reg[p] = reg[q] | r else: reg[p] = reg[q] | reg[r] if reg[p] == 0: Z = 1 if (class_3.get(opcode[0])) == 65: #test for EOR p,q,r,L = operand3(opcode) if L: reg[p] = reg[q] ^ r else: reg[p] = reg[q] ^ reg[r] if reg[p] == 0: Z = 1 if (class_3a.get(opcode[0])) == 601: #test for MIN MIN r0,r1,r2 returns the minimum of r1 and r2 in r0 p,q,r,L = operand3(opcode) if L: if reg[q] <= r: reg[p] = reg[q] else: reg[p] = r else: if reg[q] <= reg[r]: reg[p] = reg[q] else: reg[p] = reg[r] if (class_3a.get(opcode[0])) == 602: #test for MAX p,q,r,L = operand3(opcode) if L: if reg[q] >= r: reg[p] = reg[q] else: reg[p] = r else: if reg[q] >= reg[r]: reg[p] = reg[q] else: reg[p] = reg[r] if (class_3a.get(opcode[0])) == 610: #test for SEQ (SEQ r0,r1,r2 sets r0 to 11111111 if r1 = r2 or 00000000 otherwise p,q,r,L = operand3(opcode) if L: if reg[q] == r: reg[p] = 0b11111111 else: reg[p] = 0 else: if reg[q] >= reg[r]: reg[p] = 0b11111111 else: reg[p] = 0 if (class_3a.get(opcode[0])) == 611: #test for SNE (SNE r0,r1,r2 sets r0 to 11111111 if r1 != r2 or 00000000 otherwise p,q,r,L = operand3(opcode) if L: if reg[q] == r: reg[p] = 0b11111111 else: reg[p] = 0 else: if reg[q] >= reg[r]: reg[p] = 0b11111111 else: reg[p] = 0 if (class_2a.get(opcode[0])) == 48: #ADD_M add to memory p,q,L = operand2a(opcode) #Format ADD_M r0,[r1] data_memory[reg[q]] = data_memory[reg[q]] + reg[p] if (class_2a.get(opcode[0])) == 49: #M_ADD add memory to register p,q,L = operand2a(opcode) #Format M_ADD r0,[r1] reg[p] = data_memory[reg[q]] + reg[p] ################################################################################################ #### stack operations checked here ### ################################################################################################ if (class_4.get(opcode[0])) == 71: #Test for PUSH L = False #Set not literal mode if opcode[1][0] =='R': #If PUSH r0 then do it SP = PUSH(SP,operand1(opcode),L) else: #If not register operand, assume literal L = True #Tell function to expect a literal literal = opcode[1][1:] print('literal',literal) SP = PUSH(SP,convert(literal),L) #Push literal value if (class_4.get(opcode[0])) == 72: SP = PULL(SP,operand1(opcode)) if (class_4a.get(opcode[0])) == 81: SP = StackAdd(SP) if (class_4a.get(opcode[0])) == 82: SP = StackSub(SP) if (class_4a.get(opcode[0])) == 83: SP = StackMul(SP) if (class_4a.get(opcode[0])) == 84: SP = StackDup(SP) if (class_4a.get(opcode[0])) == 85: SP = StackSwap(SP) ################################################################################################# ### THis section deals with the trap instruction which is an operating system call ### ### Normally in a real system the OS would implement trap functions. Here they are build in ### ################################################################################################# if (class_5.get(opcode[0])) == 91: #TRAP instruction if len(opcode) == 4: pass #Look for three operand TRAP (not yet implemented) if len(opcode) == 3: #look for two operand TRAP if opcode[1] == 'TRACE': single_step = False #Turn off single step if we are using trace if opcode[2] == 'ON': trace = True if opcode[2] == 'OFF': trace = False if opcode[1] == 'LABELS': single_step = False #Turn off single step if we are using trace mode if opcode[2] == 'ON': b_lab = True if opcode[2] == 'OFF': b_lab = False if opcode[1] == 'REG' and opcode[2] == 'CHANGE': pass #This function not yet implemented if len(opcode) == 2: #Look for one operand TRAP (can be used to selectively print data) if opcode[1] == 'SP': print('Current SP =', SP, 'Current stack =', stack[SP:-1]) if opcode[1] == 'MEM': print('Current memory =', data_memory) if opcode[1] == 'STATUS': print('Processor status: PC=',PC,'SP=',SP,'LR=',LR,'Z bit=',Z,'N bit=', N,'Cycles =',cycle) if opcode[1] == 'REG': print('Registers =', reg) if (class_5.get(opcode[0])) != 91: #are we executing a TRAP (don't increment cycle counter for a TRAP instruction)? cycle = cycle + 1 #if not then increment cycle count ################################################################################################################################## # This section deals with single-steping, the trace mode, and user breakpoints if trace: #if trace is on then print the registers etc if inst[-1] == 'BREAK': #check for break point (last word of an instruction) inst.pop() #drop the BREAK tag for printing inst1 = ' '.join(inst) #make a temporary string for printing print('PC = {0:3}'.format(PC), ' Registers',reg, ' Memory',data_memory[0:7], ' Opcode {0:20}'.format(inst1),'Z',Z,'C',C,'N',N,'V',V,'SP',SP, 'Link',LR,' Current stack',stack[SP:-1]) if single_step: #Single step mode - display after instruction and wait for enter input() display() if inst[-1] == 'BREAK' and not (trace or single_step): #check for break point (last word of an instruction) and not in trace mode inst.pop() #drop the BREAK tag for printing inst1 = ' '.join(inst) #make a temporary string for printing print('Breakpoint at next PC =',PC, ' Opcode {0:20}'.format(inst1),'Instructions executed',cycle) print('PC =',old_PC, 'Registers ',reg, 'Memory',data_memory[0:8],'Z=',Z,'N=',N,'SP=',SP, 'Link=',LR,' Current stack',stack[SP:-1]) print() if b_lab: #print labelled line if inst[0].startswith('$'): # print(inst[1:], 'pc =', PC) display() if inst[-1].endswith('WATCH'): #look for register watch of the form r4=12,watch token1 = inst[-1] #get the register assignment r4=12.watch token2 = token1[0:-6] #extract r4=12 r_num = int(token2[1]) #get number of register to watch r_val = int(token2[3:]) #get value being monitored r_act = reg[r_num] #get actual contents of monitored register if r_act == r_val: #check for the value monitored print('Register',r_num,'monitored for',r_val) display() ################################################################################################################################# ################################################################################################################################# #Notes on instruction types # sARM operations: 'ADD','AND','BEQ','B','BL','BNE','CMP','EOR','LDR','LSL','LSR','MOV', # 'MUL','NOT,'NOP','OR','RTL','STOP','STR','SUB' # Experimental and stack operations: 'BSR','PUSH','PULL','RBEQ','RTS','S_ADD','S_SUB','S_MUL','S_DUP','S_SWAP' # General operations not implemented by sARM: 'CLR','DEC','DCBZ','IN','INC','MIN','MAX','MLA','OUT','PRINT','SEQ','SNE','TRAP' # CISC memory operations: 'ADD_M','M_ADD','MMP' ############################################################################################################################# #SAMPLE CODE - I USED IN TESTING - this is meaningless code # mov r0,#0 # nop #xx add r1,r1,r0 r1=3.watch # add r0,r0,#1 # bra xx # # #; PUT RANDOM NUMBERS IN 4 CONSECUTIVE MEMORY LOCATIOND # mov r0,#4 ;4 locations to fill # clr r1 ;pointer #Loop rnd r2 ;put a random value in r2 # str r2,[r1] ;store in memory # add r1,r1,#1 ;increment pointer # sub r0,r0,#1 ;decrement count # bne Loop ;repeat until all done # bra copy #xxxx equ 2 #; SEARCH MEMORY FOR LARGEST VALUE # mov r0,#4 ;4 locations to read # mov r3,#0 ;dummy biggest # clr r1 ;pointer #xxx1 ldr r2,[r1] ;read from memory break # cmp r3,r2 ;compare new value break # bgt xxx2 ;skip on old greater than new # mov r3,r2 ;record new large value #xxx2 add r1,r1,#1 ;increment pointer break # sub r0,r0,#1 ;decrement count # bne xxx1 ;repeat until all done #xxyxx equ 4 #; COPY LIST AND REVERSE IT WITH TWO POINTERS # mov r0,#4 ;locations to copy #Src equ 0 ;source location #Dest equ 4 ;destination location # mov r1,#Src ;load source pointer # mov r2,#Dest ;load destination pointer # add r2,r2,#3 ;make destination point to end #Looop ldr r3,[r1] ;REPEAT read element # str r3,[r2] ; store in memory # add r1,r1,#1 ; increment source pointer # sub r2,r2,#1 ; decrement source pointer # sub r0,r0,#1 ; decrement count # bne Looop ;repeat until all done # stop