#### Simulator for a simple CPU (sARM.py) -
#### (c) Alan Clements 2014
#### License: For non-
#### 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-
#### 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-
#### If you answer n to the request to turn of single-
#### 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-
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-
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-
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-
sys.exit() #Exit on insufficent operands
L = False
d = int(instruction[1][1:]) #Get the destation -
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 = -
else:
s = symboltable.get(s_name[1:],-
if s == -
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-
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-
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 #-
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] == '-
s2 = -
else:
s2 = symboltable.get(s2_name[1:],-
if s2 == -
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 -
def PUSH (SP,destination,L): #define some stack operations
SP = SP -
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 -
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 -
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,-
if num_val == -
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:-
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 -
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[-
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 -
###########################################################################################################################
single_step = True #Determines whether we execute line
by line or run continuously -
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
-
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 -
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] -
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,-
if PC == -
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 -
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,-
if PC == -
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-
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-
PC = symboltable.get(target,-
if PC == -
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-
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-
PC = symboltable.get(target,-
if PC == -
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-
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-
PC = symboltable.get(target,-
if PC == -
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-
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-
PC = symboltable.get(target,-
if PC == -
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],-
if target == -
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
-
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,-
if target == -
print('Label error: Label',target,'in line',opcode,'is not in the symbol table')
sys.exit()
reg[d] = reg[d] -
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-
if (a -
Z,N = 1,0 #set Z = 1 and N = 0 if same
if (a -
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-
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 < -
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 < -
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 < -
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 < -
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] -
else:
reg[p] = reg[q] -
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:-
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
X##################################################################################################################################
# This section deals with single-
if trace: #if trace is on then print the registers etc
if inst[-
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:-
if single_step: #Single step mode -
input()
display()
if inst[-
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:-
print()
if b_lab: #print labelled line
if inst[0].startswith('$'):
# print(inst[1:], 'pc =', PC)
display()
if inst[-
token1 = inst[-
token2 = token1[0:-
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 -
# 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
This is a python program that takes a .txt file (assembly language program) and executes
it -
It is still a work in progress and not all functions are complete. THE user manual is not complete.
It has an extended instruction set for experimental purposes -
You can download the Python source file here as a .txt file. You will have to change the extension to .py to run this file.
Alternately, to download it as a.py file, right click here and save the file. SARM.py
The user manual for this CPU simulator is here.