---
# System prepended metadata

title: CSPT13 Computer Architecture IV

---

# CSPT13 Computer Architecture IV
## PUSH & POP
```
"""CPU functionality."""

import sys

HLT = 0b00000001
LDI = 0b10000010
PRN = 0b01000111
MUL = 0b10100010
PUSH = 0b01000101
POP = 0b01000110

SP = 7

class CPU:
    """Main CPU class."""

    def __init__(self):
        """Construct a new CPU."""
        self.registers = [0] * 8
        self.registers[7] = 0xF4
        self.pc = 0
        self.ram = [0] * 256
        self.halted = False

    def load(self, filename):
        """Load a program into memory."""

        address = 0
        # open the file
        with open(filename) as my_file:
            # go through each line to parse and get
            # the instruction
            for line in my_file:
                # try and get the instruction/operand in the line
                comment_split = line.split("#")
                maybe_binary_number = comment_split[0]
                try:
                    x = int(maybe_binary_number, 2)
                    self.ram_write(x, address)
                    address += 1
                except:
                    continue


    def alu(self, op, reg_a, reg_b):
        """ALU operations."""

        if op == MUL:
            self.registers[reg_a] *= self.registers[reg_b]
            self.pc += 3
        else:
            raise Exception("Unsupported ALU operation")

    def trace(self):
        """
        Handy function to print out the CPU state. You might want to call this
        from run() if you need help debugging.
        """

        print(f"TRACE: %02X | %02X %02X %02X |" % (
            self.pc,
            #self.fl,
            #self.ie,
            self.ram_read(self.pc),
            self.ram_read(self.pc + 1),
            self.ram_read(self.pc + 2)
        ), end='')

        for i in range(8):
            print(" %02X" % self.reg[i], end='')

        print()

    def ram_read(self, address):
        return self.ram[address]

    def ram_write(self, value, address):
        self.ram[address] = value

    def run(self):
        """Run the CPU."""
        while not self.halted:
            instruction_to_execute = self.ram_read(self.pc)
            operand_a = self.ram_read(self.pc + 1)
            operand_b = self.ram_read(self.pc + 2)
            self.execute_instruction(instruction_to_execute, operand_a, operand_b)

    def execute_instruction(self, instruction, operand_a, operand_b):
        if instruction == HLT:
            self.halted = True
            self.pc += 1
        elif instruction == PRN:
            print(self.registers[operand_a])
            self.pc += 2
        elif instruction == LDI:
            self.registers[operand_a] = operand_b
            self.pc += 3
        elif instruction == MUL:
            self.alu(instruction, operand_a, operand_b)
        elif instruction == PUSH:
            # decrement the stack pointer
            self.registers[SP] -= 1
            # store the operand in the stack
            self.ram_write(self.registers[operand_a], self.registers[SP])
            self.pc += 2
        elif instruction == POP:
            self.registers[operand_a] = self.ram_read(self.registers[SP])
            self.registers[SP] += 1
            self.pc += 2
        else:
            print("idk what to do.")
            pass

```

## CALL & RET
```
import sys

# a machine that simply executes an instruction

# op-code - they represent the instruction that is supposed to be executed
PRINT_HI = 1
HALT = 2
PRINT_NUM = 3
SAVE = 4 # save a value in a given register
PRINT_REGISTER = 5 # print value stored in register
ADD = 6 # takes in two registers, A and B and adds both values contained in the registers and stores it in reg A
PUSH = 7 # takes in a register and stores the value in that register on top of the stack
POP = 8 # takes in a register and stores the topmost element in the stack in it
CALL = 9
RET = 10
PRINT_SUBROUTINE_INSTRUCTION = 11

def load_memory():
    program = [
        PRINT_HI, # prints hi
        SAVE, # saves 7 into register 2
        7,
        2,
        CALL, # jump to the address stored in register 2
        2,
        HALT, # stop the program
        PRINT_SUBROUTINE_INSTRUCTION,
        SAVE,
        500,
        0,
        RET # jump back to address stored in the top of the stack (7, which is HALT)
    ]

    space_for_stack = 128 - len(program)
    memory = program + [0] * space_for_stack
    return memory

memory = load_memory()
program_counter = 0 # points to the current instruction we need to execute next
running = True
registers = [0] * 8
stack_pointer_register = 7 # register number that contains address of stack pointer
registers[stack_pointer_register] = len(memory) - 1

# keep looping while not halted
while running:
    command_to_execute = memory[program_counter]

    if command_to_execute == PRINT_HI:
        print("hi")
        program_counter += 1
    elif command_to_execute == PRINT_NUM:
        number_to_print = memory[program_counter + 1]
        print(f"{number_to_print}")
        program_counter += 2
    elif command_to_execute == HALT:
        running = False
        program_counter += 1
    elif command_to_execute == SAVE:
        value_to_save = memory[program_counter + 1]
        register_to_save_it_in = memory[program_counter + 2]
        registers[register_to_save_it_in] = value_to_save
        program_counter += 3
    elif command_to_execute == PRINT_REGISTER:
        register_to_print = memory[program_counter + 1]
        print(f"{registers[register_to_print]}")
        program_counter += 2
    elif command_to_execute == ADD:
        register_a = memory[program_counter + 1]
        register_b = memory[program_counter + 2]
        sum_of_registers = registers[register_a] + registers[register_b]
        registers[register_a] = sum_of_registers
        program_counter += 3
    elif command_to_execute == PUSH:
        registers[stack_pointer_register] -= 1 # decrement stack pointer
        register_to_get_value_in = memory[program_counter + 1]
        value_in_register = registers[register_to_get_value_in]
        memory[registers[stack_pointer_register]] = value_in_register
        program_counter += 2
    elif command_to_execute == POP:
        register_to_pop_value_in = memory[program_counter + 1]
        registers[register_to_pop_value_in] = memory[registers[stack_pointer_register]]
        registers[stack_pointer_register] += 1
        program_counter += 2
    elif command_to_execute == CALL:
        # stores the address of next instruction on top of the stack
        registers[stack_pointer_register] -= 1
        address_of_next_instruction = program_counter + 2
        memory[registers[stack_pointer_register]] = address_of_next_instruction
        # it jumps to the address stored in that register
        register_to_get_address_from = memory[program_counter + 1]
        program_counter = registers[register_to_get_address_from]
    elif command_to_execute == RET:
        # doesn't take in any operands, sets the program counter to the topmost element of the stack and pop it
        program_counter = memory[registers[stack_pointer_register]]
        registers[stack_pointer_register] += 1
    elif command_to_execute == PRINT_SUBROUTINE_INSTRUCTION:
        print("i'm in a subroutine")
        program_counter += 1
    else:
        print(f"Unknown instruction {command_to_execute}")
        sys.exit(1)

print(f"registers: {registers}")
print(f"memory: {memory}")
```