Here is an example program from a new assmebly language I made up that computes primes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If not exceeded, continue; else, end loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: |
Here is an example program from a new assmebly language I made up that computes primes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If not exceeded, continue; else, end loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: |
Below is one acceptable solution. For example, you could write: ------------------------------------------------------------ #!/usr/bin/env python3 """ This interpreter implements a simple assembly language with the following instructions: SET Rx value -- sets register Rx to the given integer constant. INC Rx -- increments register Rx. MUL Rx Ry Rz -- computes Ry * Rz and stores the result in Rx. MOD Rx Ry Rz -- computes (Ry mod Rz) and stores the result in Rx. EQ Rx Ry -- compares register Rx to Ry and saves a “flag” (True if equal, False otherwise). GT Rx Ry -- compares register Rx > Ry and saves a “flag” (True or False). STORE Rx Ry -- uses the value in register Ry (as an address) to store the value from register Rx in memory. JMP label -- an unconditional jump (the label is the name of a line ending in a colon). JT label -- jump if the previously computed flag is True. JF label -- jump if the previously computed flag is False. Labels are written as “label:” at the beginning of a line. By convention, the program above expects: • R1 is used for the candidate number, • R2 is used both to supply results of is_prime and for the EQ comparisons, • R8 is used as a pointer to the next free memory slot. When the program is finished (via a "JMP end" or falling off the program) the memory cells starting at address 0 will contain the primes found. The evaluate(str) function below compiles the program, runs it, and returns a Python list of memory cells. Below is one complete solution. """ def evaluate(prog): # First, split the program into lines and tokenize. lines = prog.splitlines() instructions = [] # each item is a list of tokens (instruction and its arguments) labels = {} # label name -> instruction index for line in lines: # Remove comments (anything after //) if "//" in line: line = line.split("//",1)[0] line = line.strip() if not line: continue # Tokenize the line tokens = line.split() # Check if the first token is a label (ends with ':') while tokens and tokens[0].endswith(":"): label_name = tokens[0][:-1] # Map the label to the current length of the instructions list. labels[label_name] = len(instructions) tokens.pop(0) # If any tokens remain, this is an instruction. if tokens: instructions.append(tokens) # Initialize registers and memory. registers = {} # We'll use register names as keys. # any register not explicitly set is assumed 0. def get_reg(r): return registers.get(r, 0) def set_reg(r, value): registers[r] = value # Memory: using a dictionary mapping address (integer) to stored value. memory = {} # Our flag register used by EQ / GT instructions. flag = False # The instruction pointer. ip = 0 # Execution loop. while ip < len(instructions): instr = instructions[ip] op = instr[0] # For convenience, define a helper to parse an operand that is an immediate integer value. # In this language, operands are registers (like R1) or immediate numbers. def parse_val(token): try: return int(token) except ValueError: # If not an int, assume register name. return get_reg(token) if op == "SET": # SET Rx value # e.g. SET R1 2 reg = instr[1] val = int(instr[2]) # always immediate int in our sample. set_reg(reg, val) ip += 1 elif op == "INC": # INC Rx: increments the register. reg = instr[1] set_reg(reg, get_reg(reg) + 1) ip += 1 elif op == "MUL": # MUL Rdest Rval1 Rval2: dest = val1 * val2. dest, r1, r2 = instr[1], instr[2], instr[3] set_reg(dest, get_reg(r1) * get_reg(r2)) ip += 1 elif op == "MOD": # MOD Rdest Ra Rb => dest = Ra mod Rb. dest, ra, rb = instr[1], instr[2], instr[3] # Guard against division by zero. divisor = get_reg(rb) if divisor == 0: set_reg(dest, 0) else: set_reg(dest, get_reg(ra) % divisor) ip += 1 elif op == "EQ": # EQ Rx Ry: sets flag = (R_x == R_y) r1, r2 = instr[1], instr[2] flag = (get_reg(r1) == get_reg(r2)) ip += 1 elif op == "GT": # GT Rx Ry: sets flag = (R_x > R_y) r1, r2 = instr[1], instr[2] flag = (get_reg(r1) > get_reg(r2)) ip += 1 elif op == "STORE": # STORE Rx Ry: stores the value from register Rx into memory at address get_reg(Ry) rsrc, raddr = instr[1], instr[2] addr = get_reg(raddr) memory[addr] = get_reg(rsrc) ip += 1 elif op == "JMP": # JMP label: unconditional jump. label = instr[1] if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] elif op == "JT": # Jump if flag is True. label = instr[1] if flag: if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] else: ip += 1 elif op == "JF": # Jump if flag is False. label = instr[1] if not flag: if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] else: ip += 1 else: raise Exception("Unknown instruction: " + op) # At the end, we assume that primes were stored in memory starting at address 0. # Our pointer register R8 (which we never reset) equals the number of stored primes. count = get_reg("R8") # Build the resulting list sequentially from address 0 up to count-1. result = [] for i in range(count): result.append(memory.get(i, 0)) return result # For testing, you can run: if __name__ == "__main__": program = """ SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If flag is True (meaning R3^2 > R1) jump to end MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: """ primes = evaluate(program) print(primes) """ When run, this program simulates the assembly code and eventually prints a list of 100 prime numbers such as: [2, 3, 5, 7, 11, 13, ... ] ------------------------------------------------------------ Explanation: 1. The program first builds a list of instructions while keeping track of label positions. 2. It then “executes” the instructions in a loop. Many instructions update registers or a special flag. 3. The STORE instruction writes the current candidate number (if prime) into a memory dictionary with an address coming from register R8. 4. After execution, the interpreter returns a Python list containing the stored primes in order. This interpreter meets the specification that evaluate(program) returns the resulting memory state, e.g. a list of primes.
I extracted the following code from that output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | #!/usr/bin/env python3 """ This interpreter implements a simple assembly language with the following instructions: SET Rx value -- sets register Rx to the given integer constant. INC Rx -- increments register Rx. MUL Rx Ry Rz -- computes Ry * Rz and stores the result in Rx. MOD Rx Ry Rz -- computes (Ry mod Rz) and stores the result in Rx. EQ Rx Ry -- compares register Rx to Ry and saves a “flag” (True if equal, False otherwise). GT Rx Ry -- compares register Rx > Ry and saves a “flag” (True or False). STORE Rx Ry -- uses the value in register Ry (as an address) to store the value from register Rx in memory. JMP label -- an unconditional jump (the label is the name of a line ending in a colon). JT label -- jump if the previously computed flag is True. JF label -- jump if the previously computed flag is False. Labels are written as “label:” at the beginning of a line. By convention, the program above expects: • R1 is used for the candidate number, • R2 is used both to supply results of is_prime and for the EQ comparisons, • R8 is used as a pointer to the next free memory slot. When the program is finished (via a "JMP end" or falling off the program) the memory cells starting at address 0 will contain the primes found. The evaluate(str) function below compiles the program, runs it, and returns a Python list of memory cells. Below is one complete solution. """ def evaluate(prog): # First, split the program into lines and tokenize. lines = prog.splitlines() instructions = [] # each item is a list of tokens (instruction and its arguments) labels = {} # label name -> instruction index for line in lines: # Remove comments (anything after //) if "//" in line: line = line.split("//",1)[0] line = line.strip() if not line: continue # Tokenize the line tokens = line.split() # Check if the first token is a label (ends with ':') while tokens and tokens[0].endswith(":"): label_name = tokens[0][:-1] # Map the label to the current length of the instructions list. labels[label_name] = len(instructions) tokens.pop(0) # If any tokens remain, this is an instruction. if tokens: instructions.append(tokens) # Initialize registers and memory. registers = {} # We'll use register names as keys. # any register not explicitly set is assumed 0. def get_reg(r): return registers.get(r, 0) def set_reg(r, value): registers[r] = value # Memory: using a dictionary mapping address (integer) to stored value. memory = {} # Our flag register used by EQ / GT instructions. flag = False # The instruction pointer. ip = 0 # Execution loop. while ip < len(instructions): instr = instructions[ip] op = instr[0] # For convenience, define a helper to parse an operand that is an immediate integer value. # In this language, operands are registers (like R1) or immediate numbers. def parse_val(token): try: return int(token) except ValueError: # If not an int, assume register name. return get_reg(token) if op == "SET": # SET Rx value # e.g. SET R1 2 reg = instr[1] val = int(instr[2]) # always immediate int in our sample. set_reg(reg, val) ip += 1 elif op == "INC": # INC Rx: increments the register. reg = instr[1] set_reg(reg, get_reg(reg) + 1) ip += 1 elif op == "MUL": # MUL Rdest Rval1 Rval2: dest = val1 * val2. dest, r1, r2 = instr[1], instr[2], instr[3] set_reg(dest, get_reg(r1) * get_reg(r2)) ip += 1 elif op == "MOD": # MOD Rdest Ra Rb => dest = Ra mod Rb. dest, ra, rb = instr[1], instr[2], instr[3] # Guard against division by zero. divisor = get_reg(rb) if divisor == 0: set_reg(dest, 0) else: set_reg(dest, get_reg(ra) % divisor) ip += 1 elif op == "EQ": # EQ Rx Ry: sets flag = (R_x == R_y) r1, r2 = instr[1], instr[2] flag = (get_reg(r1) == get_reg(r2)) ip += 1 elif op == "GT": # GT Rx Ry: sets flag = (R_x > R_y) r1, r2 = instr[1], instr[2] flag = (get_reg(r1) > get_reg(r2)) ip += 1 elif op == "STORE": # STORE Rx Ry: stores the value from register Rx into memory at address get_reg(Ry) rsrc, raddr = instr[1], instr[2] addr = get_reg(raddr) memory[addr] = get_reg(rsrc) ip += 1 elif op == "JMP": # JMP label: unconditional jump. label = instr[1] if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] elif op == "JT": # Jump if flag is True. label = instr[1] if flag: if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] else: ip += 1 elif op == "JF": # Jump if flag is False. label = instr[1] if not flag: if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] else: ip += 1 else: raise Exception("Unknown instruction: " + op) # At the end, we assume that primes were stored in memory starting at address 0. # Our pointer register R8 (which we never reset) equals the number of stored primes. count = get_reg("R8") # Build the resulting list sequentially from address 0 up to count-1. result = [] for i in range(count): result.append(memory.get(i, 0)) return result |
Running the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | #!/usr/bin/env python3 """ This interpreter implements a simple assembly language with the following instructions: SET Rx value -- sets register Rx to the given integer constant. INC Rx -- increments register Rx. MUL Rx Ry Rz -- computes Ry * Rz and stores the result in Rx. MOD Rx Ry Rz -- computes (Ry mod Rz) and stores the result in Rx. EQ Rx Ry -- compares register Rx to Ry and saves a “flag” (True if equal, False otherwise). GT Rx Ry -- compares register Rx > Ry and saves a “flag” (True or False). STORE Rx Ry -- uses the value in register Ry (as an address) to store the value from register Rx in memory. JMP label -- an unconditional jump (the label is the name of a line ending in a colon). JT label -- jump if the previously computed flag is True. JF label -- jump if the previously computed flag is False. Labels are written as “label:” at the beginning of a line. By convention, the program above expects: • R1 is used for the candidate number, • R2 is used both to supply results of is_prime and for the EQ comparisons, • R8 is used as a pointer to the next free memory slot. When the program is finished (via a "JMP end" or falling off the program) the memory cells starting at address 0 will contain the primes found. The evaluate(str) function below compiles the program, runs it, and returns a Python list of memory cells. Below is one complete solution. """ def evaluate(prog): # First, split the program into lines and tokenize. lines = prog.splitlines() instructions = [] # each item is a list of tokens (instruction and its arguments) labels = {} # label name -> instruction index for line in lines: # Remove comments (anything after //) if "//" in line: line = line.split("//",1)[0] line = line.strip() if not line: continue # Tokenize the line tokens = line.split() # Check if the first token is a label (ends with ':') while tokens and tokens[0].endswith(":"): label_name = tokens[0][:-1] # Map the label to the current length of the instructions list. labels[label_name] = len(instructions) tokens.pop(0) # If any tokens remain, this is an instruction. if tokens: instructions.append(tokens) # Initialize registers and memory. registers = {} # We'll use register names as keys. # any register not explicitly set is assumed 0. def get_reg(r): return registers.get(r, 0) def set_reg(r, value): registers[r] = value # Memory: using a dictionary mapping address (integer) to stored value. memory = {} # Our flag register used by EQ / GT instructions. flag = False # The instruction pointer. ip = 0 # Execution loop. while ip < len(instructions): instr = instructions[ip] op = instr[0] # For convenience, define a helper to parse an operand that is an immediate integer value. # In this language, operands are registers (like R1) or immediate numbers. def parse_val(token): try: return int(token) except ValueError: # If not an int, assume register name. return get_reg(token) if op == "SET": # SET Rx value # e.g. SET R1 2 reg = instr[1] val = int(instr[2]) # always immediate int in our sample. set_reg(reg, val) ip += 1 elif op == "INC": # INC Rx: increments the register. reg = instr[1] set_reg(reg, get_reg(reg) + 1) ip += 1 elif op == "MUL": # MUL Rdest Rval1 Rval2: dest = val1 * val2. dest, r1, r2 = instr[1], instr[2], instr[3] set_reg(dest, get_reg(r1) * get_reg(r2)) ip += 1 elif op == "MOD": # MOD Rdest Ra Rb => dest = Ra mod Rb. dest, ra, rb = instr[1], instr[2], instr[3] # Guard against division by zero. divisor = get_reg(rb) if divisor == 0: set_reg(dest, 0) else: set_reg(dest, get_reg(ra) % divisor) ip += 1 elif op == "EQ": # EQ Rx Ry: sets flag = (R_x == R_y) r1, r2 = instr[1], instr[2] flag = (get_reg(r1) == get_reg(r2)) ip += 1 elif op == "GT": # GT Rx Ry: sets flag = (R_x > R_y) r1, r2 = instr[1], instr[2] flag = (get_reg(r1) > get_reg(r2)) ip += 1 elif op == "STORE": # STORE Rx Ry: stores the value from register Rx into memory at address get_reg(Ry) rsrc, raddr = instr[1], instr[2] addr = get_reg(raddr) memory[addr] = get_reg(rsrc) ip += 1 elif op == "JMP": # JMP label: unconditional jump. label = instr[1] if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] elif op == "JT": # Jump if flag is True. label = instr[1] if flag: if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] else: ip += 1 elif op == "JF": # Jump if flag is False. label = instr[1] if not flag: if label not in labels: raise Exception("Label not found: " + label) ip = labels[label] else: ip += 1 else: raise Exception("Unknown instruction: " + op) # At the end, we assume that primes were stored in memory starting at address 0. # Our pointer register R8 (which we never reset) equals the number of stored primes. count = get_reg("R8") # Build the resulting list sequentially from address 0 up to count-1. result = [] for i in range(count): result.append(memory.get(i, 0)) return result answer = evaluate(""" SET R1 0 SET R2 1 loop: MUL R3 R2 R2 STORE R3 R1 INC R1 INC R2 SET R3 20 EQ R1 R3 JF loop """)[:10] expected = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' answer = evaluate(""" SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If not exceeded, continue; else, end loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: """)[:10] expected = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' print('All tests passed') |
Traceback (most recent call last):
File "/usr/src/app/main.py", line 189, in <module>
assert answer == expected, f'Wrong answer; got {answer} instead of {expected}'
^^^^^^^^^^^^^^^^^^
AssertionError: Wrong answer; got [] instead of [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Testing if the previous output contains the string All tests passed
: False
Here is an example program from a new assmebly language I made up that computes primes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If not exceeded, continue; else, end loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: |
Here is an example program from a new assmebly language I made up that computes primes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If not exceeded, continue; else, end loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: |
Below is one complete solution in Python. You can call evaluate(program) (where program is a string containing your assembly‐code) and it will simulate the code and finally return a Python list of the stored primes. (In our simulation the registers are R1–R8, there is one “flag” register that is set by EQ and GT and used by JT/ JF, and memory is modeled as a dictionary written by STORE.) Below is the full code: ------------------------------------------------ #!/usr/bin/env python3 def evaluate(program_code): # Split the program into lines and remove comments and blank lines. lines = [] for line in program_code.splitlines(): # Remove comments (everything after "//") if '//' in line: line = line.split('//', 1)[0] line = line.strip() if line: lines.append(line) # Parse lines into a list of instructions and record label positions. # We will represent an instruction as a tuple: (opcode, [arg1, arg2, ...]) instructions = [] labels = {} for line in lines: # Check if the line is just a label (ends with ":") or has one label at beginning. if ':' in line: # Split into label(s) and possible instruction after parts = line.split(':') # The label name is the first non-empty part before the colon. label = parts[0].strip() # Record label pointing to the current instruction index. labels[label] = len(instructions) # Check if there is any instruction after the colon. remaining = ':'.join(parts[1:]).strip() if not remaining: continue else: line = remaining # continue processing the remainder as instruction # Now split the instruction tokens by whitespace. tokens = line.split() opcode = tokens[0] args = tokens[1:] instructions.append((opcode, args)) # Initialize registers R1..R8 (if not explicitly set, they are 0). # We'll store registers in a dict mapping the register name string to int value. regs = {f'R{i}': 0 for i in range(1, 9)} # Initialize the "flag" that is set by EQ or GT. flag = False # Memory: we use a dictionary mapping address to value. mem = {} # Program counter: pc = 0 # Run the simulation while pc < len(instructions): op, args = instructions[pc] # Uncomment below for debugging: # print(f"[PC {pc}] {op} {args} | regs: {regs} | flag: {flag} | mem: {mem}") if op == "SET": # SET Rx VALUE (the value is an integer literal) reg = args[0] value = int(args[1]) regs[reg] = value pc += 1 elif op == "INC": # INC Rx reg = args[0] regs[reg] += 1 pc += 1 elif op == "STORE": # STORE Rsrc Rdest meaning: memory[ regs[Rdest] ] = regs[Rsrc] src = args[0] dest = args[1] address = regs[dest] mem[address] = regs[src] pc += 1 elif op == "EQ": # EQ Rx Ry. Set flag to true if regs[Rx] == regs[Ry], else false. r1, r2 = args[0], args[1] flag = (regs[r1] == regs[r2]) pc += 1 elif op == "GT": # GT Rx Ry. Set flag to true if regs[Rx] > regs[Ry], else false. r1, r2 = args[0], args[1] flag = (regs[r1] > regs[r2]) pc += 1 elif op == "MUL": # MUL Rd Rs1 Rs2 => regs[Rd] = regs[Rs1]*regs[Rs2] dest, s1, s2 = args[0], args[1], args[2] regs[dest] = regs[s1] * regs[s2] pc += 1 elif op == "MOD": # MOD Rd Rs1 Rs2 => regs[Rd] = regs[Rs1] % regs[Rs2] dest, s1, s2 = args[0], args[1], args[2] # Avoid division by zero – assume the program will never hit that. regs[dest] = regs[s1] % regs[s2] if regs[s2] != 0 else 0 pc += 1 elif op == "JMP": # JMP label: jump to instruction index with the given label. label = args[0] if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] elif op == "JT": # JT label: If the most recent comparison (flag) is True, jump. label = args[0] if flag: if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] else: pc += 1 elif op == "JF": # JF label: If the most recent comparison (flag) is False, jump. label = args[0] if not flag: if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] else: pc += 1 else: raise Exception(f"Unknown instruction: {op}") # Optional: break if we jump to an "end" label and there are no further instructions. # Our sample program ends with a jump to label 'end'. Here we allow execution # to continue until pc falls off the instruction list. # When finished run, we expect that the primes have been stored in memory # starting at address 0 up to the count stored in R8. prime_count = regs['R8'] # Build the result list in order from 0 to prime_count-1 # (If some addresses are missing, we fill with None.) result = [mem.get(i, None) for i in range(prime_count)] return result # Example usage: if __name__ == "__main__": program = """ SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If condition true, jump to end the loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: """ primes = evaluate(program) print(primes) # When you run this file the output will be a list of the first 100 prime numbers. ------------------------------------------------ Explanation of the code: 1. The program is first split into lines and stripped of comments and extra whitespace. 2. In a first pass the code builds two things: • A list “instructions” where each instruction is kept as a tuple (opcode, list-of-operands). • A dictionary “labels” mapping any declared label name (like start_find_primes) to its instruction index. 3. The simulator then creates registers (R1–R8, all initially 0) and a “flag” variable. Memory is represented as a dictionary. 4. In a while‐loop the interpreter executes instructions one by one. Instructions such as SET, INC, MUL, MOD change registers; EQ and GT set the flag; and jump instructions (JMP, JT, JF) update the program counter according to labels. 5. Finally, when the program finishes (PC goes past the last instruction) the interpreter returns a list representing the contents of memory (addresses 0 to R8–1). In our sample program that list holds the first 100 primes. This interpreter should work for your assembly language example.
I extracted the following code from that output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | #!/usr/bin/env python3 def evaluate(program_code): # Split the program into lines and remove comments and blank lines. lines = [] for line in program_code.splitlines(): # Remove comments (everything after "//") if '//' in line: line = line.split('//', 1)[0] line = line.strip() if line: lines.append(line) # Parse lines into a list of instructions and record label positions. # We will represent an instruction as a tuple: (opcode, [arg1, arg2, ...]) instructions = [] labels = {} for line in lines: # Check if the line is just a label (ends with ":") or has one label at beginning. if ':' in line: # Split into label(s) and possible instruction after parts = line.split(':') # The label name is the first non-empty part before the colon. label = parts[0].strip() # Record label pointing to the current instruction index. labels[label] = len(instructions) # Check if there is any instruction after the colon. remaining = ':'.join(parts[1:]).strip() if not remaining: continue else: line = remaining # continue processing the remainder as instruction # Now split the instruction tokens by whitespace. tokens = line.split() opcode = tokens[0] args = tokens[1:] instructions.append((opcode, args)) # Initialize registers R1..R8 (if not explicitly set, they are 0). # We'll store registers in a dict mapping the register name string to int value. regs = {f'R{i}': 0 for i in range(1, 9)} # Initialize the "flag" that is set by EQ or GT. flag = False # Memory: we use a dictionary mapping address to value. mem = {} # Program counter: pc = 0 # Run the simulation while pc < len(instructions): op, args = instructions[pc] # Uncomment below for debugging: # print(f"[PC {pc}] {op} {args} | regs: {regs} | flag: {flag} | mem: {mem}") if op == "SET": # SET Rx VALUE (the value is an integer literal) reg = args[0] value = int(args[1]) regs[reg] = value pc += 1 elif op == "INC": # INC Rx reg = args[0] regs[reg] += 1 pc += 1 elif op == "STORE": # STORE Rsrc Rdest meaning: memory[ regs[Rdest] ] = regs[Rsrc] src = args[0] dest = args[1] address = regs[dest] mem[address] = regs[src] pc += 1 elif op == "EQ": # EQ Rx Ry. Set flag to true if regs[Rx] == regs[Ry], else false. r1, r2 = args[0], args[1] flag = (regs[r1] == regs[r2]) pc += 1 elif op == "GT": # GT Rx Ry. Set flag to true if regs[Rx] > regs[Ry], else false. r1, r2 = args[0], args[1] flag = (regs[r1] > regs[r2]) pc += 1 elif op == "MUL": # MUL Rd Rs1 Rs2 => regs[Rd] = regs[Rs1]*regs[Rs2] dest, s1, s2 = args[0], args[1], args[2] regs[dest] = regs[s1] * regs[s2] pc += 1 elif op == "MOD": # MOD Rd Rs1 Rs2 => regs[Rd] = regs[Rs1] % regs[Rs2] dest, s1, s2 = args[0], args[1], args[2] # Avoid division by zero – assume the program will never hit that. regs[dest] = regs[s1] % regs[s2] if regs[s2] != 0 else 0 pc += 1 elif op == "JMP": # JMP label: jump to instruction index with the given label. label = args[0] if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] elif op == "JT": # JT label: If the most recent comparison (flag) is True, jump. label = args[0] if flag: if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] else: pc += 1 elif op == "JF": # JF label: If the most recent comparison (flag) is False, jump. label = args[0] if not flag: if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] else: pc += 1 else: raise Exception(f"Unknown instruction: {op}") # Optional: break if we jump to an "end" label and there are no further instructions. # Our sample program ends with a jump to label 'end'. Here we allow execution # to continue until pc falls off the instruction list. # When finished run, we expect that the primes have been stored in memory # starting at address 0 up to the count stored in R8. prime_count = regs['R8'] # Build the result list in order from 0 to prime_count-1 # (If some addresses are missing, we fill with None.) result = [mem.get(i, None) for i in range(prime_count)] return result |
Running the following program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | #!/usr/bin/env python3 def evaluate(program_code): # Split the program into lines and remove comments and blank lines. lines = [] for line in program_code.splitlines(): # Remove comments (everything after "//") if '//' in line: line = line.split('//', 1)[0] line = line.strip() if line: lines.append(line) # Parse lines into a list of instructions and record label positions. # We will represent an instruction as a tuple: (opcode, [arg1, arg2, ...]) instructions = [] labels = {} for line in lines: # Check if the line is just a label (ends with ":") or has one label at beginning. if ':' in line: # Split into label(s) and possible instruction after parts = line.split(':') # The label name is the first non-empty part before the colon. label = parts[0].strip() # Record label pointing to the current instruction index. labels[label] = len(instructions) # Check if there is any instruction after the colon. remaining = ':'.join(parts[1:]).strip() if not remaining: continue else: line = remaining # continue processing the remainder as instruction # Now split the instruction tokens by whitespace. tokens = line.split() opcode = tokens[0] args = tokens[1:] instructions.append((opcode, args)) # Initialize registers R1..R8 (if not explicitly set, they are 0). # We'll store registers in a dict mapping the register name string to int value. regs = {f'R{i}': 0 for i in range(1, 9)} # Initialize the "flag" that is set by EQ or GT. flag = False # Memory: we use a dictionary mapping address to value. mem = {} # Program counter: pc = 0 # Run the simulation while pc < len(instructions): op, args = instructions[pc] # Uncomment below for debugging: # print(f"[PC {pc}] {op} {args} | regs: {regs} | flag: {flag} | mem: {mem}") if op == "SET": # SET Rx VALUE (the value is an integer literal) reg = args[0] value = int(args[1]) regs[reg] = value pc += 1 elif op == "INC": # INC Rx reg = args[0] regs[reg] += 1 pc += 1 elif op == "STORE": # STORE Rsrc Rdest meaning: memory[ regs[Rdest] ] = regs[Rsrc] src = args[0] dest = args[1] address = regs[dest] mem[address] = regs[src] pc += 1 elif op == "EQ": # EQ Rx Ry. Set flag to true if regs[Rx] == regs[Ry], else false. r1, r2 = args[0], args[1] flag = (regs[r1] == regs[r2]) pc += 1 elif op == "GT": # GT Rx Ry. Set flag to true if regs[Rx] > regs[Ry], else false. r1, r2 = args[0], args[1] flag = (regs[r1] > regs[r2]) pc += 1 elif op == "MUL": # MUL Rd Rs1 Rs2 => regs[Rd] = regs[Rs1]*regs[Rs2] dest, s1, s2 = args[0], args[1], args[2] regs[dest] = regs[s1] * regs[s2] pc += 1 elif op == "MOD": # MOD Rd Rs1 Rs2 => regs[Rd] = regs[Rs1] % regs[Rs2] dest, s1, s2 = args[0], args[1], args[2] # Avoid division by zero – assume the program will never hit that. regs[dest] = regs[s1] % regs[s2] if regs[s2] != 0 else 0 pc += 1 elif op == "JMP": # JMP label: jump to instruction index with the given label. label = args[0] if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] elif op == "JT": # JT label: If the most recent comparison (flag) is True, jump. label = args[0] if flag: if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] else: pc += 1 elif op == "JF": # JF label: If the most recent comparison (flag) is False, jump. label = args[0] if not flag: if label not in labels: raise Exception(f"Label {label} not defined") pc = labels[label] else: pc += 1 else: raise Exception(f"Unknown instruction: {op}") # Optional: break if we jump to an "end" label and there are no further instructions. # Our sample program ends with a jump to label 'end'. Here we allow execution # to continue until pc falls off the instruction list. # When finished run, we expect that the primes have been stored in memory # starting at address 0 up to the count stored in R8. prime_count = regs['R8'] # Build the result list in order from 0 to prime_count-1 # (If some addresses are missing, we fill with None.) result = [mem.get(i, None) for i in range(prime_count)] return result answer = evaluate(""" SET R1 0 SET R2 1 loop: MUL R3 R2 R2 STORE R3 R1 INC R1 INC R2 SET R3 20 EQ R1 R3 JF loop """)[:10] expected = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' answer = evaluate(""" SET R1 2 // Starting number to check for prime start_find_primes: JMP is_prime // Control will return after executing is_prime with R1 as input and R2 containing the result ready_prime: SET R7 1 EQ R2 R7 // Check if R2 is 1 (prime) JF increment // If not prime, skip storing and increment the number // Store prime number in memory and increment count STORE R1 R8 // Store prime number at address pointed by R8 INC R8 // Increment prime count // Check if 100 primes are found SET R7 100 EQ R8 R7 JF increment // If not 100 primes found yet, continue finding JMP end // If 100 primes found, end program increment: INC R1 // Increment number to check for prime JMP start_find_primes // Check next number is_prime: SET R2 1 // Assume number is prime initially SET R3 2 // Start divisor from 2 start_loop: // Label to start the loop // Check if we have exceeded the square root of R1 MUL R4 R3 R3 // R4 = R3 * R3 GT R4 R1 // Set flag if R4 > R1 JT is_prime_end // If not exceeded, continue; else, end loop MOD R6 R1 R3 // R6 = R1 % R3 SET R7 0 EQ R7 R6 // Check if R6 is 0 JT not_prime // If yes, number is not prime INC R3 // Increment divisor JMP start_loop // Repeat loop not_prime: SET R2 0 // Set result to 0 (not prime) is_prime_end: JMP ready_prime end: """)[:10] expected = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] assert answer == expected, f'Wrong answer; got {answer} instead of {expected}' print('All tests passed') |
Traceback (most recent call last):
File "/usr/src/app/main.py", line 160, in <module>
assert answer == expected, f'Wrong answer; got {answer} instead of {expected}'
^^^^^^^^^^^^^^^^^^
AssertionError: Wrong answer; got [] instead of [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Testing if the previous output contains the string All tests passed
: False