|
|||||||||||||||||
Problem Set 7: Machines Assigned: Tuesday March 29, 2016 Due: Tuesday April 5, 2016 Points: 12 This is an individual problem set. You may consult with a friend but the work you submit should be your own. Our phones, our laptops, the university's mainframe computers, they are actually quite similar. All are based on a very flexible design developed in the 1940s by the Hungarian-born mathematician John von Neumann (among others). It is remarkable that the design, sometimes referred to as the stored-program computer, or the von Neumann architecture, has persisted in the face of the never-ending revolution in technology. The heart of the idea is very simple. We all know that computers store information in the form of bits, the ubiquitous binary digits 1 and 0. (A string of 8 of them is called a byte.) Computers actually contain two kinds of memory, persistent (or non-volatile) storage that retains information when the computer is off. This is where our files are stored. The other type, ephemeral (or volatile) storage retains information when the computer is powered-up but is erased when the computer is off. This type of memory is sometimes called random-access memory (RAM) because each storage cell (i.e., each byte) has an address and the individual cells can be efficiently accessed in random order. The other piece to understand is that it's possible to design circuitry to manipulate the stored bits in various ways. We can design an addition circuit for example. When presented with input bits representing say, the integers 6 and 3, it will produce as output the bits representing the integer 9. We can design subtraction circuits, comparison circuits, we can design circuits that will load information from RAM or store information in RAM. And so on for the various sorts of simple operations that a computer can carry out. Most computers have a couple hundred operations. Lets say we're building a computer and we've designed circuitry for say 8 operations. von Neumann's key idea was to assign bit patterns to each of the operations. For example, we might assign the bit pattern 010 to the ADD operation and the bit pattern 011 to the SUB operation. Then these patterns (or opcodes) can be stored in the computer's RAM along with the data. Since operations usually have operands, the opcodes are usually packaged-up in RAM together with representations of their operands:
When executed, the above instruction would add the contents of opnd2 to the contents of opnd3 and store the result in opnd1. We'll call the combination of opcode and operands an instruction. To simplify the whole setup, instructions usually have a fixed size, on many computers, the are packed into 4 consecutive bytes of RAM. (And by the way, 4 consecutive bytes are usually referred to as a word of memory.) If we know where in the RAM to look for them, we can execute any sequence of instructions that someone might like to load into the memory. This process is called, naturally enough, coding (aka programming). Instead of being fixed and rigid, stored-program computers are the essence of flexibilty! This might seem like a simple idea but it's also a powerful one and, as we said above, it has remained in place for over 70 years. It is the basic design of virtually all computers. Essential PartsIn order to make this scheme work, it turns out to be most efficient to modularize the design, housing the above described circuitry, the "smarts" of the computer, in a central processing unit (or CPU). Modern CPUs contain many components, including:
OperandsAs we noted above, an instruction includes information about the required operands. For example, in the instruction
registers R0, R1 and R2 are operands of the ADD instruction. When executed, this instruction would cause the computer to add the contents of registers R1 and R2 and deposit the sum in register R0. Different sorts of instructions will have different sorts of operands. For example, the load instruction:
Let base be the value in register R1. Then this instruction loads the bits stored in RAM address (base + 12), into register R0. The Instruction CycleGiven the above, a stored-program computer cycles through the following instruction cycle:
The Simple Virtual MachineThis part of the problem set involves working with Simple Virtual Machine, henceforth known as SVM. SVM is simple because it only has the very basics of the full von Neumann architecture, it has neither a stack nor a heap. It is virtual because we'll actually implement it in (Python) software.SVM has 8 registers and 16 instructions. Registers
SVM InstructionsSVM has 16 instructions. In the descriptions below, Rd, Rs and Rt refer to one of the general purpose registers, with Rd denoting a destination of an operation and Rs and Rt denoting source registers. We'll use the symbol RAM to refer to the random access memory, the symbol addr to refer to a non-negative integer address in memory and the notation RAM[addr] to refer to the contents of location addr in the memory. We'll use the symbol disp to refer to an integer displacement that may (or may not) be added to the PC register to alter the flow of control.All instructions leave the RA and PSW register alone unless specified otherwise.
SegmentsMany modern computer architectures separate the program and data into different pieces called segments. In particular, the parts of program containing (static) data, are called data segments while the parts of program containing instructions are called (oddly enough) text segments.Example: RemainderLet the data segment DATA = [M; N], where M and N are natural numbers. The following SVM program (in a text segment) computes M mod N storing the remainder in register R0. The line numbers on the left are for readability.
Example: FactorialLet the data segment DATA = [N], where N is a natural number. The following SVM program computes N! storing the result in register R0. The line numbers on the left are for readability.
Example: Count DataData items in the data segment terminated by the sentinal -1.
Example: Sum DataData items in the data segment terminated by the sentinal -1.
Example: Average DataData items in the data segment terminated by the sentinal -1. In this example, we must count the data and sum the data. We first compute the count. Since we need all 4 registers to computer the sum, we'll store the count in location 0 of RAM. Then the data starts at postion 1.
Part A (2 Points) isFactorLet the data segment DATA = [M; N; ...], where M and N are natural numbers. Feel free to place any values that you want in the data segment after M and N. Write an SVM program to compute (isFactor M N) storing a 0 in register R0 if M is not a factor of N and storing a 1 in R0 if M is a factor of N.Part B (3 Points) powerLet the data segment DATA = [M; N; ...], where M and N are natural numbers. Write an SVM program to compute MN storing the result in register R0. Remember that M0 = 1 and that for N > 0, MN = M(MN-1).Part C (7 Points) SVM ImplementationWe can implement a virtual machine for SVM in Python. Our machine is designed to be as simple as possible. In thinking about the operation of the machine, we must be able to represent SVM programs in RAM and we must be able to implement the instruction cycle that executes the programs instructions using the 6 registers.RepresentationsPrograms in the language of SVM are sequences of SVM instructions. We'll represent an instruction as a pair(operation, operands)where operation is a representation of one of the 16 operations, and operands is a list of operands. Thinking about the 16 instructions specified above, it seems natural integer constants to distinguish them: (LOD, STO, MOV, HLT) = (0, 1, 2, 3) (ADD, SUB, MUL, DIV) = (4, 5, 6, 7) (CMP, LI, JSR, R) = (8, 9, 10, 11) (JMP, BLT, BEQ, BGT) = (12, 13, 14, 15)
In order to represent the operands, we'll need to refer to the
four general purpose registers, and to register Zero so it seems
natural to introduce symbolic names for them:
So under this scheme, the instruction Lod R0, 8, R3 would be represented as the pair (0, [0, 8, 3]).
Finally, as far as representing whole SVM programs goes, it seems
natural to represent both the data segment and the text segment as
lists.
Given these representation choices, a multiplication program could be represented in Python as follows:
Implementing the Virtual MachineNow that we've settled on a way to represent SVM programs in Python, we turn our attention to implementing the virtual machine that executes the programs. Our implementation should accept a program (image) and a program counter value:svm : image -> int -> unitWhen provided with an image and a PC value, e.g., svm(pc, image), (normally svm(len(data), image)) it should carry out the above described instruction cycle.
Execution of the program is carried out relative to the RAM and the six
registers (PC, PSW and the four general purpose registers R0, ..., R3).
We can represent both the PC and the PSW registers as integer variables
and the four general purpose registers R0 through
R3 using a 4-tuple:
Then the outline of the VM would look as follows:
Complete the definition of the svm virtual machine. Feel free to use the following harness code. Test out your SVM program written for Parts A and B by running them in the VM that you wrote for Part C. |