DSCPU
The Denison Simple CPU Simulator
File | Comments |
---|---|
Program from description below. | |
The following CPU description is based on The Relatively Simple CPU defined by John D. Carpinelli in his textbook, Computer Systems Organization & Architecture. A few changes and additions have been made to make the model more challenging. We will call this CPU the Denison Simple CPU, or DSCPU.
The DSCPU has 8-bit opcodes and 16-bit addresses. Instructions are variable length, depending on the instruction, and may be either 8 bits (an opcode and no explicit operands), 16 bits (an opcode and an 8-bit immediate value as a source operand), or 24 bits (an opcode followed by a 16-bit source operand address).
There are 3 registers directly controlled by the programmer:
Programmer Visible Registers | |||
---|---|---|---|
Name | Size (bits) | Description | |
AC |
8 |
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 |
General purpose register. R supplies the 2nd operand of all two-operand arithmetic and logic instructions. It can also be used as a temporary storage area. | |
Flag |
4 |
Status flags given as follows: | |
Z |
1 |
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. Z is set/reset as a result of ADD, SUB, INAC, CLAC, AND, OR, XOR, NOT, RL, RR, LSL, and LSR. | |
C |
1 |
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. C is set/reset as a result of ADD, SUB, INAC, RL, RR, LSL, LSR. | |
V |
1 |
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. V is set/reset as a result of ADD, SUB, INAC, RL, RR, ASL, and ASR. | |
N |
1 |
Negative flag. This bit is set if the result of the operation is negative - the msb of the result is set. N is set/reset as a result of ADD, SUB, INAC, CLAC, AND, OR, XOR, NOT, RL, RR, SL, and ASR. |
The DSCPU also contains several registers in addition to those specified in the instruction set:
CPU Hidden Registers | |||
---|---|---|---|
Name | Size (bits) | Description | |
PC |
16 |
Program counter. PC contains the address of the next instruction to be executed or the address of the next operand of the current instruction. | |
IR |
8 |
Instruction register. IR contains the opcode, as fetched from memory, of the instruction currently being executed. | |
AR |
16 |
Address register. AR is used in the interface to memory and holds the address for a memory request. The AR must be loaded prior to a request to read memory (for a load) or for a write to memory (for a store). | |
DR |
8 |
Data register. DR is also used in the interface to memory. It holds the data value to store on a memory write operation, and must be loaded prior to the write request. It holds the value read from memory following a read request. | |
TR |
8 |
Temporary register. TR holds data during the execution of an instruction. |
The DSCPU has access to 65536 bytes of physical memory, mem. Addresses are 16 bits in length, and every memory byte is accessible by an address in the range 0 to 65535. Reads and writes from/to memory and achieved through the use of AR and DR. To perform a read, AR is loaded with the desired address, a, and then a memory read is requested. The completion of the read results in the contents, mem[a], in DR. To perform a write, AR is loaded with the desired address, a, and the data value to be written, d, is loaded into DR. The memory write is then requested.
In the following table, an A operand refers to a 16-bit memory address and is composed of the two bytes (MSB followed by LSB) following the opcode in memory. An I operand refers to an 8-bit immediate value and is found in the byte immediately following the opcode in memory.
Instruction | Opcode (binary) |
Operand | Semantics |
---|---|---|---|
NOP |
0000 0000 |
- |
None (other than advancing PC) |
LDAC |
0000 0001 |
A |
AC <- mem[A] |
STAC |
0000 0010 |
A |
mem[A] <- AC |
MVAC |
0000 0011 |
- |
R <- AC |
MOVR |
0000 0100 |
- |
AC <- R |
JUMP |
0000 0101 |
A |
PC <- A |
JMPZ |
0000 0110 |
A |
if (Z==1) then PC <- A |
JPNZ |
0000 0111 |
A |
if (Z==0) then PC <- A |
JMPC |
0001 0000 |
A |
if (C==1) then PC <- A |
JMPV |
0001 0001 |
A |
if (V==1) then PC <- A |
JMPN |
0001 0010 |
A |
if (N==1) then PC <- A |
ADD |
0000 1000 |
- |
AC <- AC + R; ZCVN update |
SUB |
0000 1001 |
- |
AC <- AC - R; ZCVN update |
INAC |
0000 1010 |
- |
AC <- AC + 1; ZCVN update |
CLAC |
0000 1011 |
- |
AC <- 0; ZCVN update |
AND |
0000 1100 |
- |
AC <- AC & R; ZN update {bitwise AND} |
OR |
0000 1101 |
- |
AC <- AC | R; ZN update {bitwise OR} |
XOR |
0000 1110 |
- |
AC <- AC ^ R; ZN update {bitwise XOR} |
NOT |
0000 1111 |
- |
AC <- !(AC); ZN update {bitwise complement} |
RL |
0001 0110 |
- |
AC <- AC rotated left one bit position; ZCVN update |
RR |
0001 0111 |
- |
AC <- AC rotated right one bit position; ZCVN update |
LSL |
0001 0100 |
- |
AC <- AC shifted left one bit position, zero fill in lsb; ZCVN update |
LSR |
0001 0101 |
- |
AC <- AC shifted right one bit position, zero fill in msb; ZCVN update |
MVI |
0001 0011 |
I |
AC <- I |
HALT |
1111 1111 |
- |
Halt execution |
Using appropriate data structures to represent memory and ALL registers (both programmer visible, and the hidden CPU registers), write a simulator to simulate DSCPU. To do this, we need a mechanism to load code and data into the memory of the simulator.
The programs to be run by your DSCPU simulator will be stored in a text file. Each line of the text file represents a single byte to be stored in successive location of the DSCPU memory. Each byte will be represented by two ASCII characters for the two hexidecimal digits required for the byte. Hex digits may be in lower or upper case. From the perspective of loading a program, there is no distinction between the code and data of the program. Also, there is no stack. A file containing the following lines:
00
0b
0a
02
20
00
00
ff
is a 6 instruction program :
NOP
CLAC
INAC
STAC 0x2000
NOP
HALT
The fetch cycle of the DSCPU consists of 3 subphases:
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.
For all instruction functions:
PC, AC, R, Flag, AR, DR
Return, no action
This is a 3-byte instruction: Opcode, 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.
Note that, based on the fetch phases above, PC and AR point to the first byte of the address - the high-order half of the address A.
The execution of the LDAC instruction is as follows:
Phase |
Register Transfer |
Comment |
---|---|---|
LDAC1 |
DR<-mem[AR] |
Read the high-order half of the address A into DR |
PC<-PC + 1 |
Increment PC |
|
AR<-AR+1 |
Increment AR |
|
Print out registers PC, AR, DR labeled with LDAC1 |
||
LDAC2 |
TR <- DR |
Save high-order half of address A |
DR <- mem[AR] |
Get low-order half of address A |
|
PC <- PC + 1 |
Increment PC |
|
Print out registers PC, AR, DR, TR labeled with LDAC2 | ||
LDAC3 |
AR <- TR || DR |
Form address A and put in AR |
Print out register AR labeled with LDAC3 | ||
LDAC4 |
DR <- mem[AR] |
Read memory at address A |
Print out register DR labeled with LDAC4 | ||
LDAC5 |
AC <- DR |
Update AC with value read |
Print out register AC labeled with LDAC5 |
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 parallel in hardware.
The LDAC is one of the more complex instructions. I have provided you with the details of the exection subphases 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 subphases for each instruction. At the completion of each subphase you must print out all registers that are modified during the subphase.
For the STAC Instruction, prior to storing the data in memory, you must print the location to be stored and print the same data after memory has been updated.
Using a word processor and the guide presented for LDAC, for each instruction, provide the execution subphases and the operations in each subphase.
Your finished project must be submitted via email. It is assumed that you wrote your program in C++. Your main program must be dscpu.cpp. In my own solution, I have C++ classes defined for the register set, for memory, for the alu, and for the cpu. Each of these consists of a separate ".h" file for the interface specification and a ".cpp" file for the implementation. You are free to design as you see fit, but structure is important. Also, at least 15% of the grade will be based on the comments and following the Denison C++ coding conventions, and style/neatness. 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.
Please Note: My program is around 1000 lines. If you wait until the day before this assignment is due, it is very unlikely that you will be successful.