CS330 Programming Project 1
CPU Simulator
CPU Description
The following CPU description is based on The Relatively Simple CPU defined
by John D. Carpinelli in COMPUTER SYSTEMS ORGANIZATION & ARCHITECTURE.
A few changes and additions have been made to make the model more challenging.
We will call this CPU RSCPU.
The RSCPU has 8-bit op codes and 16-bit addresses. Instructions are
either 8 bits, 16 bits, or 24 bits in length.
Registers
There are 3 registers directly controlled by the programmer:
-
AC: 8-bit accumulator. AC receives the result of arithmetic and
logic instructions. It provides one of the operands for two-operand arithmetic
and logic instructions. Data is loaded to/from AC from/to memory.
-
R: 8-bit general purpose register which supplies the 2nd operand
of all two-operand arithmetic and logic instructions. It can also be used
as a temporary storage area.
-
Flag: 4-bit register that contains status flags. The programmer cannot directly
write to the Flag. The Flag is set as a result of the execution of instructions
(set/reset as a result of the current operation):
-
Z: Zero Flag: This bit is set/reset as a result of arithmetic or
logic operations. If the result of an arithmetic/logic operation is zero,
then Z is set to 1. If the result of an arithmetic/logic operation is not
zero, then Z is set to 0.
The Zero Flag is set/reset as a result of ADD, SUB, INAC, CLACL, AND,
OR, XOR, NOT, RL, RR, LSL, and LSR.
-
C: Carry Flag: This bit reflects the result of the carry out of
the most significant bit of the result of the last executed arithmetic
operation. In other words, if data is treated as unsigned, it represents
the result too large in the case of add or a borrow in the case of subtract.
The Carry Flag is set/reset as a result of RL, RR, LSL, and LSR, ADD,
INAC and SUB.
Carry is also set on LSR and RR if data is moved out of the least-significant bit.
-
V: Overflow Flag: This bit is set if the carry into the most significant
bit of the result does not equal the carry out of the most significant
bit of the result of the last executed arithmetic operation. It is reset
if the carry into of the most significant bit equals the carry out of the
most significant bit of the result of the last arithmetic operation. This
is like the carry except for signed data, the addition of two positive
numbers results in a negative number.
The Overflow flag is set/reset as a result of RL, RR, LSL, LSR, ADD,
INAC, and SUB.
-
N: Negative Flag: This bit is set if the result of the operation
is negative - the msb of the result is set.
The Negative Flag is set/reset as a result of AND, OR, XOR, NOT, RL,
RR, LSL, LSR, ADD, INAC, and SUB.
You Must use the following definitions (or equivalent constant
definitions) for the bits in the flag register:
- #define Z 1 (least significant bit of the register)
- #define C 2
- #define V 4
- #define N 8 (most significant bit of the register)
As the result of operations, you will be oring the appropriate
flag bits on and/or anding the appropriate flag bits off.
The RSCPU also contains several registers in addition to those specified
in the instruction set. The following registers are used
control the fetch/execute of the instructions:
-
AR16-bit address register which supplies an address to memory
-
PC16-bit Program Counter which contains the address of the next
instruction to be executed or the address of the next operand of the current
instruction.
-
DR8-bit data register which receives instructions and data from
memory and transfers data to memory.
-
IR8-bit instruction register which contains the opcode fetched from
memory to be executed.
-
TR8-bit temporary register which holds data during instruction execution.
Instruction Set
An Op Code followed by the letter A means that an address follows the Op
Code in the program. M[A] means memory addressed by A.
Mneumonic |
Op Code |
Operation |
NOP |
0000 0000 |
No Operation |
LDAC |
0000 0001 A |
AC<-M[A] |
STAC |
0000 0010 A |
M[A]<-AC |
MVAC |
0000 0011 |
R<-AC |
MOVR |
0000 0100 |
AC<-R |
JUMP |
0000 0101 A |
GOTO A |
JMPZ |
0000 0110 A |
If (Z=1) then GOTO A |
JPNZ |
0000 0111 A |
If (Z=0) then GOTO A |
JMPC |
0001 0000 A |
If (C=1) then GOTO A |
JV |
0001 0001 A |
If (V=1) then GOTO A |
JN |
0001 0111 A |
If (N=1) then GOTO A |
ADD |
0000 1000 |
AC<-AC + R, ZCVN set/reset |
SUB |
0000 1001 |
AC<-AC - R, ZCVN set/reset |
INAC |
0000 1010 |
AC<-AC + 1, ZCVN set/reset |
CLAC |
0000 1011 |
AC<-0, Z<-1 and CNV reset |
AND |
0000 1100 |
AC<-AC bitwise AND R, ZN set/reset |
OR |
0000 1101 |
AC<-AC bitwise OR R, ZN set/reset |
XOR |
0000 1110 |
AC<-AC bitwise XOR R, ZN set/reset |
NOT |
0000 1111 |
AC<bitwise NOT (AC), ZN set/reset |
RL |
0001 0010 |
AC<-AC rotated left one position ZCVN set/reset |
RR |
0001 0011 |
AC<-AC rotated right one position ZCVN set/reset |
LSL |
0001 0100 |
AC<-AC shifted left one position ZCVN set/reset |
LSR |
0001 0101 |
AC<-AC shifted right one position ZCVN set/reset |
MVI |
0001 0110 D |
AC<-D AC loaded with 8 bits following instruction |
HALT |
1111 1111 |
Halt Execution |
To Do
Using appropriate data structures to represent memory and ALL registers,
write a simulator to simulate RSCPU. Take Note, the 16- bit address
register AR implies memory size of 65536 bytes.
Your program must:
- Output
your name and a description of the program.
- Prompt the user for the name of a file containing
the program.
- Echo the name of the file.
- Read the program into memory and then execute it from
memory. Always load the program at M[0]. Initialize PC to 0 and begin
executing the program.
The data in the file will be characters making up a hexadecimal representation
of the program. Each line will contain a byte. A file containing:
00
0b
0a
02
20
00
00
ff
is a 6 instruction program :
-
NOP
-
Clear Accumulator
-
Increment Accumulator
-
Store value in Accumulator at address 2000
-
NOP
-
halt
Your program must print out an initial value of :
Fetch/Decode
The fetch cycle of the RSCPU consists of 3 sub-phases:
-
Fetch1: AR<-PC
-
Fetch2: DR<-M[AR], PC<-PC+1
-
Fetch3: IR<-DR, AR<-PC
Fetch1, Fetch2, and Fetch3 must be done consecutively. However, note, Fetch2
and Fetch3 each have two operations. Those two operations could be done
simultaneously in hardware.
You must provide a fetch function.
At the completion of Fetch1, print out the label Fetch1 and the values
of AR and PC. At the completion of Fetch2, print out a label Fetch2 and
the values in DR and PC. At the completion of Fetch3, print out a label
Fetch3 and the value of IR and AR. Be sure to identify the data printed.
In the Decode Cycle, determine the instruction contained in IR and call
the appropriate function to execute the instruction. You must provide a
function for each instruction.
Execute Cycle
Execution of the function of the instruction decoded.
Prior to returning, each instruction function must print the following:
Based on the instruction, you may also be required to print other information.
NOP Instruction
Return, no action
LDAC Instruction
Multiword instruction: Op Code, high-order half of address, and
low-order
half of the address. The execution function must first get the address
from memory then the data from the memory location and load it into the
accumulator.
Based on fetch, PC and AR point to the address - the high-order half
of the address A.
The execution of the LDAC instruction is as follows:
-
LDAC1: Read the high-order half of the address A into DR: DR<-M[AR]
Increment PC: PC<-PC+1
Increment AR: AR<-AR+1
Print out values of DR, PC, and AR labeled with LDAC1
-
LDAC2: obtain the low-order half of the address A, first you must save
contents of DR as you need it:
TR<-DR
DR<-M[AR]
PC<-PC+1
With the label LDAC2, print out TR, DR, AR, and PC.
-
LDAC3: Form the address AR<-TR,DR
With the label LDAC3, print out AR
-
LDAC4: DR<-M[AR]
With the label LDAC4, print out DR
-
LDAC5: AC<-DR
With the label LDAC5, print out AC
LDAC1 through LDAC5 are execution subcycles. The subcycles must be executed
in consecutively. Like execute, there are multiple operations going on
in some of the subphases. Some could be done in paralell in hardware.
Remaining Instructions:
The LDAC is one of the more complex instructions. I have provided you with
the details of the exectuion sub-phases of LDAC and exactly what to print
out. Using LDAC as a guide, use the definition of the instruction given
in the table above to determine the execution sub-phases for each instruction.
At the completion of each sub-phase you must print out each register that
is modified during the sub-phase.
For the STAC Instruction, prior to storing the data in memory,
you must print the address where data is to be stored,
the contents of memory location prior to the store, and print the same data after
memory has been updated.
Deliverables
The project has 8 deliverables.
Your program must be written in C++. You are responsible for checking the output
file generated by turnin to ensure that the file turned in properly. Programs
that do not compile are not worth any points. Programs that do not properly
run to a normal completion are worth very little. You must test your
code to ensure that every instruction produces the correct result.
-
Using a word processor and the guide presented for LDAC, for each instruction,
provide the execution subphases and the operations in each subphase.
- Implement the noop and halt instructions. Your file should be named rscpu1.cpp. In turnin, select cs398c and rscpu1.
- Add the implementation of the clac, inac, mvac, and movr instructions to rscpu1.cpp. Your file should be named rscpu2.cpp. In turnin, select cs398c and rscpu2.
- Add the implementation of the ldac, stac, and mvi instructions to rscpu2.cpp. Your file should be named rscpu3.cpp. In turnin, select cs398c and rscpu3.
- Add the implementation of the and, or, xor, and not instructions to rscpu3.cpp. Your file should be named rscpu4.cpp. In turnin, select cs398c and rscpu4.
- Add the implementation of the add and sub instructions to rscpu4.cpp. Your file should be named rscpu5.cpp. In turnin, select cs398c and rscpu5.
- Add the implementation of the rl, rr, lsl, and lsr instructions to rscpu5.cpp. Your file should be named rscpu6.cpp. In turnin, select cs398c and rscpu6.
- Add the implementation of the jump, jmpz, jpnz, jmpc, jv, and jn instructions to rspcu6.cpp. Your file should be named rscpu7.cpp. In turnin, select cs398c and rscpu7.
Please Note: My completed program is 835 lines. This is not trivial. Even though the deliverables are small, they are not trivial. There is a lot going on in all of the instructions. If you wait until the day before assignments are due, it is very unlikely that you will be successful.
If you lost points on a deliverable, you must fix the error on the next deliverable.
The penalty for errors not fixed on a subsequent deliverable will be harsher on the
subsequent deliverable.
You should create test files to test all of the functionality for
each deliverable. You should think about the functionality of each
instruction and the flags that should be set/reset as a result of its
execution. You should create test files on your own to ensure all
instructions work properly. I've provided some examples. Your testing should not
be limited to these examples. I also have provided a link to
my turnin output for each deliverable.
test1.1
test2.2
rscpu1
test2.1
test2.2
rscpu2
test3.1
test3.2
test3.3
test3.4
rscpu3
test4.1
rscpu4
test5.1
test5.2
test.v1
test.v2
rscpu5
test6.1
rscpu6
test7.1
rscpu7
Program that executes:
- noop
- clac
- mvi 186
- stac 17
- ldac 17
- halt
The output prints exactly what
occurs in each subcycle of the fetch and subcycle of
each instruction. The goal is to aid in your understanding of
how the RSCPU works.
Last modified by Barbara Bracken 8/11/2024
© This project is copyright protected by Barbara Bracken