The ALU is complex enough that it is not easy to analyze it with a truth table. You need to think about the data paths through the ALU; how it transforms and combines its inputs to produce the output.
Look at the software-like description that's given
// Implementation: the ALU logic manipulates the x and y inputs
// and operates on the resulting values, as follows:
// if (zx == 1) set x = 0 // 16-bit constant
// if (nx == 1) set x = !x // bitwise not
// if (zy == 1) set y = 0 // 16-bit constant
// if (ny == 1) set y = !y // bitwise not
// if (f == 1) set out = x + y // integer 2's complement addition
// if (f == 0) set out = x & y // bitwise and
// if (no == 1) set out = !out // bitwise not
With very little syntax change, you could feed this to Java and have an ALU() function.
There are two tricky aspects to doing this in hardware.
The first is that you can't change a signal's value so the construct "set x=0" does not work since x already has a value coming from outside the ALU. The solution to this is to always make new signals in each step. Transform the if (zx... to
if (zx) x1 = 0
else x1 = x
Then the next step transforms x1 to x2, etc.
The second problem is that you can't have only some of the hardware work. In this code
if (f == 1) set fun = x2 + y2 // integer 2's complement addition
else set fun = x2 & y2 // bitwise and
both the Add16 and And16 are always their doing their operation. Your hardware needs to choose which part's output is connected to 'fun'. What part did you make in project 1 that can choose between 2 16-bit inputs? Yol'll use it for almost every step in the ALU.
First, get the main output value working so that your ALU will pass ALU-nostat.tst. This test ignores zr and ng. Once it is passing, then you can concentrate on the zr and ng outputs.