CSCI 235 — pre-assembly

References

Programming with the goto

Stuff to do

Start by downloading a C project into your csci235 directory.

In your solution, do not modify collatz.c. Only change pa-collatz.c. That way you will always have a copy of a standard C program.

Pointers — How it is done

Using the “facts about arrays” in the C pointers page, transform the two array element assignments in the fill_collatz_table so that that neither assignment uses the brackets. We’re trying for the most machine-like implementation. This means using void * in the second assignment.

Gotos

Now we are heading back to programming in the 60’s. Open the C programming with the goto handout in another window.

The worse if

Yes, the goto has been banned in Java and almost all computer science courses. However work your way down to the discussion of the if and the ifelse. First, apply this transformation to the if in the fill_collatz_table routine. This is probably your first label and your first goto. Be sure to compile and run your program before proceeding.

Now apply the rule for translating the ifelse to collatz. Imagine the spaghetti code we used to write in our introductory programming courses! When the if’s are nested, the spaghetti gets very tangled.

The for

Let’s replace that for in fill_collatz_table with a while. You’ll find this rule about 70% of the way through the handout. Sometimes this transformation is mentioned in the first programming course.

Again, compile and run before plunging on.

The while

This is the hard one. Once you add the backward branching goto, it gets particular messy.

Go to the discussion of the while. You will notice two transformation rules. I suggest you use the second as it tends to be a little faster and, of course, more complex! You’ll find this rule about 60% of the way through the handout.

First apply the rule to the while in the collatz. Do not think too hard! Just apply the rule. Test before proceeding.

And finally apply the rule to the remaining while, which used to be a for, in fill_collatz_table.

What about Java?

Download a tar-gzip of the collatz program written in Java. Extract it and then compile and run it with the following commands. (Isn’t the inconsistency odd.)

javac edu/unca/csci235/Collatz.java 
java edu.unca.csci235.Collatz

Run a couple of commands using javap, the Java disassembler, to view and save the compiled Java bytecode.

javap edu.unca.csci235.Collatz
javap -c edu.unca.csci235.Collatz | more
javap -c edu.unca.csci235.Collatz > Collatz.java-bytecode

Take a look at A Java Programmer’s Guide to Byte Code. The Java Virtual Machine is a stack-based computer that is simulated in software. Open up your Collatz.java-bytecode and go to the collatz routine. (It will be around address 262.) Add some ”comments“ to your code describing what is happen. It should start with something like this:

  static int collatz(int);
    Code:
       0: iconst_0                     [ 0 ]
       1: istore_1                     [ ]       Store in count
       2: iload_0       	       [ n ]    
       3: iconst_1                     [ n 1 ]
       4: if_icmpeq     33             [ ]       Jumps if n == 1

Three significant instruction sets

RISC

Modern Reduced Instruction Set Computers have fairly simple instruction sets. There are load/store instructions that can move the memory location to/from a register. Generally these instructions index memory relative to a register, which can be a stack or frame pointer, a global offset (this one is hard), or a register containing a C pointer.

An assignment statement, such as x = y + z requires, in the worse case, four instructions.

  1. y, is loaded into one register.
  2. z, is loaded into another register.
  3. The two registers are added.
  4. The result is stored in x.

Storing variables in registers, rather than memory, greatly improve performance.

An integer array index, such as V[i] requires, in the worse case, five instructions.

  1. The array pointer, V, is loaded into a register.
  2. The array index, i, is loaded into a register.
  3. The array offset, i*4 is computed by multiplying the array index by four. This is done with a left shift: i<<2.
  4. The array offset is added to the array pointer to obtain a reference to the element location, &A[i], that is &A*i.
  5. A load instruction is used to obtain A[i].

Of course, most compilers do much better by keeping &A[i] in a register.

Branches are performed with the control flow instructions listed in Sample PIC32 (MIPS) instructions Finally, note that the ABI (application binary interface) specifies how registers are used. Note that $a0 to $a3 are used to pass arguments and $v0 and $v1 are used to return results.

IBM System 360

Look at a copy of the IBM System/360 Green card stored at the Computer History Museum. Notice that the LD instruction could add two registers and a displacement to obtain an address. However, the displacement is only twelve bits long.

The Intel x86-64 instruction set

The Intel x86-64 is the the ultimate CISC, Complex Instruction Set Computer. Take a look at the x86-64 instruction set. Go on to a summary of the many options for the ADD instruction.

We will return to this in Chapter 3. We will return to this in CSCI 335.

The assigned task

Take a look at the file pseudo_regs.h . It defines several fake registers that we will use to simulate an assembly language program.

Hopefully, you have a copy of the pc-collatz.c program we wrote last week. If you don’t, you can download this one which contains low-level control structures.

By the way, this is the first time I have tried this lab. It might be rough!

Subtask One

Replace count with the return ”register“ _ir0.

Subtask Two

Replace n with the argument ”register“ _ia0.

Subtask Three

This one is tricky. In the collatz function, replace complex statements like
    _ia0 = 3 * _ia0 + 1 ;
with sequences of statements where the right-hand side of an assignment contains no more than one arithmetic operations and the operands to arithmetic operations are registers. This means that the assignment seen above must be replaced with three assignments. You will need to use a temporary register _it0 to do this.

Subtask Four

This one is messy. Start by changing the header of collatz to the following
    void collatz(void)

Now modify fill_collatz_table to pass its argument in _ia0 and receive its result in _ir0. This is the real world.

Subtask Five

This one is huge because it repeats all the previous subtasks, but for fill_collatz_table.

Modify fill_collatz_table so that it receives its arguments in _pa0 and _ia1. Notice, that it must be _pa0 rather than _ia0 to make the C compiler happy! If you look at a pseudo_regs.h, you will see how the magic of the union makes this possible. Also, replace i with a temporary register.

You will also need to modify print_table to pass arguments to fill_collatz_table in the appropriate registers.

However, the really tricky thing is that fill_collatz_table will need to save copies of its argument and and temporary registers before calling collatz. You will do this with code sequences similar to the following before the call:
    saveTemp0 = _it0 ;
and similar to the following after the call:
    _it0 = saveTemp0 ;
Such is the life of an assembly language programmer.

There will also be some very messy C to handle the array accesses. Hint:

    _it1 = _it0 << 2 ;
    _pt1 = (void *)(((unsigned long)(void *)_pa0) + _it1) ;
    *((int *)_pt1) = _ir0 ;