Comp 210 Lab 11: JAM

JAM simulator, JAM


JAM simulator

First we will discuss some of the basics of using the JAM simulator.

Embarrassingly, in order to run the simulator from DrScheme, you must set the language level to Quasi-R4Rs. It's unfortunate that this language-level is necessary.

To start the Jam2000 simulator, first start DrScheme, and then evaluate the expression

   (load "/home/comp210/Projects/Jam/jam.ss")
Scheme's load function just processes the contents of the indicated file. You can take a look at the file jam.ss; it is just Scheme code. (Though all it does is load some other files; it might be more interesting to look at, e.g., jam-cpu.ss.)

Loading jam.ss defines the simulator code, and starts it running as well. You'll see:

   [00000]:  00000000 (halt) %
The first number in brackets is the address of the ``current'' memory bucket (0 in this case). The next number is the contents of the bucket (initially 0). If this corresponds to an encoded instruction, then the decoded (assembly) version follows in parentheses. Finally, the % is the prompt for the Jam2000 simulator. The ``current'' memory bucket is the one named by the PC, so in general the prompt should be interpreted as
   [PC]:  mem[PC]  (disassemble mem[PC]) %
where "mem[PC]" means "take the number stored in register PC, interpret it as a memory address, and fetch the contents of the indicated memory location". For example, mem[17] denotes the contents of the 17th memory location (which might happen to be 4321). "Disassemble" is a function which takes a number, and returns the assembly-code representation of that instruction.

To do: Enter the following short Jam2000 program (which adds 8 and 14) into the simulator:

   +00000849      ; (ldi R4 8)     ; R4 <- 8
   +00001439      ; (ldi R3 14)    ; R3 <- 14
    00034510      ; (add R5 R4 R3) ; R5 <- R4 + R3 (i.e., 22)
    00000000      ; (halt)         ; end of program
You can either Notice how the display updates: every time you enter an instruction (or its encoding), the encoding is placed into mem[PC], and the PC is incremented by 1. (This is just the process of the Jam2000 simulator interface, not the Jam2000 machine itself.) Thus you are placing these instructions into mem[0], mem[1], and mem[2].

To do: Look at the contents of memory locations 0 through 3 using the m command. You should see a listing of your program. Double-check that it is correct.

To do: Execute (x) the program we just typed in above. Nothing seemed to happen (even after looking at the status again). Well, one small change--the program counter stepped from 4 (instruction "halt") to 5. What happened?

Recall from class, what the CPU does repeatedly:

  1. Fetch the number mem[PC].
  2. Increment the PC by 1.
  3. Decode the previously fetched number, since it will be interpreted as an encoded instruction.
  4. Perform the indicated instruction.

Q / To do: So what happened when we typed x above? (Hint: What was the PC? What was mem[PC]? What instruction did mem[PC] correspond to?) Aha! Try setting the PC to 0 (How? See the help screen), and then execute the program. To check if it was successful, re-examine the status of the registers.

In conclusion, here's a summary of the important simulator commands:


JAM

Now let's try using the simulator with some bigger sample Jam programs.

Here is a reference card for the Jam2000 instructions. They are in four groups:

The following program calculates 1 + 2 + 3 + ... + 10; blank lines labels are solely to improve readability.

                  ; R0 will contain "i", which will count 1..10
                  ; R1 will contain "max", which is 10
                  ; R2 will contain "sumSoFar"
                  ; R3 will contain the constant 1
   (ldi R0 1)     ; i        <- 1
   (ldi R1 10)    ; max      <- 10
   (ldi R2 0)     ; sumSoFar <- 0
   (ldi R3 1)     ; r3       <- 1
         
   loop:
   (sub R4 R0 R1) ; compare i with max (actually, compute i-max)
   (bgz R4 done)  ; if i-max > 0, branch to label "done"
   (add R2 R2 R0) ; sumSoFar <- sumSoFar + i
   (add R0 R0 R3) ; i        <- i + 1
   (jmpi loop)    ; go back and repeat
         
   done:
   (print R2)     ; print sumSoFar
   (newline)
   (halt)         ; end of program
Labels are just names for specific specific memory locations. E.g., loop stands for the fourth memory address after the initial instruction. Branch and jump instructions don't actually use labels as destinations, but only immediates. Our simple JAM simulator isn't able to convert labels to numbers, so you have to instead.

Note how the program uses registers to hold the constants 1 and 10. This is a common technique since most instructions require their arguments to be in registers.

To do: Enter the following Jam assembly code into the simulator starting at location 2000. Using m, check what you just typed in. Now execute it. Don't forget to compute addresses for the labels first.

Remember that the JAM simulator really has two parts, the "inspector" which allows you to look at and modify the JAM contents, including entering your program, and the "simulator" which executes your program.

To do: Try modifying the above to sum the cubes of 1..10.

The following code fragment is essentially equivalent to (set! x (- x y)):

                  ; assume x stored at location 100
                  ; assume y stored at location 200

		  ; R0 stores a copy of x
		  ; R1 stores a copy of y
		  ; R2 stores the address of x, 100
		  ; R3 stores the address of y, 200
		  ; R4 stores x-y

   (ldi R2 100)   ; R2 <- 100
   (ld  R0 R2)    ; R0 <- Mem[R2] = Mem[100] = x

   (ldi R3 200)   ; R1 <- 200
   (ld  R1 R1)    ; R1 <- Mem[R1] = Mem[200] = y

   (sub R4 R0 R1) ; R3 <- R0 - R1 = x - y

   (st  R4 R2)    ; x <- x - y
Executing this fragment isn't very interesting because we haven't given values to x and y and because we don't do much with x-y.

To do: Place values for x and y in memory. Add to the program the ability to print its result.

Q: Working with just the given code fragment, how could we change the code if our machine didn't have five registers? How few registers could you get away with?

You'll need to reuse registers for multiple purposes. Any machine has a small fixed number of registers (less than 100 in realistic machines), so you can't always keep distinct values in distinct registers. In general, figuring out what should be stored in each register (register allocation) is difficult.