Section 9.3
An Example of a CSC-1 Assembler Program

The simple program we looked at earlier can be used to step through the basics of the CSC-1 assembler and simulator. First, we need to assemble the source program, which is in file simple.as. All CSC-1 assembler programs must reside in files whose names end in ".as". The % shown in the following examples is a UNIX prompt. If you running in MS-DOS, the prompt is likely to be C:\>.

% asm
Name of assembler source file?  simple.as

If there are no errors, the assembler creates two files:

simple.o the object file, contains numerical codes for opcodes, operands and labels
simple.lis the listing file, contains a copy of the original source program, only with memory addresses along the left edge

The simple.o file is the only one that the simulator (xeq) needs but you should probably print out the simple.lis file, or have it in another window to view as you run the program.

Here's what the listing file looks like for simple.as:

   0:          LOD    A
   1:          ADD    B
   2:          STD    C
   3:          HLT
   4:  A:      NUM    5
   5:  B:      NUM    7
   6:  C:      NUM

This tells us that variable A resides at memory address 4, B at 5 and C at 6. All object programs in the CSC-1 start at location 0 (which is true of most real-world systems, too).

To run the program, type:

% xeq
Name of object file? simple.o
>>

The >> prompt is the simulator's way of asking for information. You can type any of the simulator's commands here, which are all described in README. If you want to run the program to the end, type xx which means "execute for a long time."

>> xx
Address: 0      LOD    4
Address: 1      ADD    5
Address: 2      STD    6
Address: 3      HLT

As the simulator does the machine instructions, it prints out their opcodes and numerical operands. Since it doesn't know anything about the original labels, like A, B or C, it cannot use them. Symbolic debuggers, as exist for languages like C and C++ and Java, retain information about which addresses were assigned to the labels so that at run time, the programmer can refer to these symbolic addresses, or labels, instead of numerical addresses.

You can inspect memory's contents by using the m command. For example, once this program finishes, variable C, or location 6, should have the number 12 in it, since this program adds 5+7 and stores the answer in C:

>> m6
memory[   6]=    12      (     12)    0000000000001100

Whenever memory or the registers are printed out, the value is shown in 3 forms: signed decimal, unsigned decimal and binary. The unsigned decimal is shown in parentheses and binary at the end.

To see the registers and other status information, use the s command:

>> s
COMPUTER HAS BEEN HALTED by the HLT instruction
Steps=4
a=        12   (        12) 0000000000001100
s=         0   (         0) 0000000000000000
pc=        4
Condition bits:  c=0  n=0  v=0  z=0

Since the HLT (halt) instructed was done recently, the first line says that the computer has been halted. To run the program again, use r to restart, or just quit by typing q and rerun the xeq program.

The three registers that are visible to the assembler programmer are A, S and PC. The others (MAR, MBR and TMP) hide behind the scenes and cannot be changed explicitly by an assembler program, anyway. The PC register can never have a negative number in it, so its value is shown simply as an unsigned decimal number. Notice also the four condition bits: C, N, V and Z which show the values on these lines coming out of the ALU after the most recently executed instruction.

Let's rerun the simple program. First, use the "r" command to restart the simulator:

>> r
WARNING! This does not reset memory to initial values.
   You may want to reload the object file by using the
   'l' command before restarting.

This sets the PC register back to 0 so that the next instruction will be the first, at address 0.

Next, let's put 0 back into memory location 6, which is variable C:

>> m6=0

This is the way to store values into memory, by using the m command, followed by the address, then an equals sign, followed by the value. There should be no spaces anywhere. If you store a negative value, such as -1, the simulator converts it to the appropriate 2's complement bit pattern. Let's store -1 into location 10, which is unused for this program, and then inspect it:

>> m10=-1
>> m10
memory[  10]=    -1      (  65535)    1111111111111111

Notice that -1 is stored in 2's complement in 16 bits, so it comes out as all 1's. This is also the bit pattern that corresponds to 65535 if the value is considered unsigned.

Recall that all the memory words and all the registers are 16 bits long (except for the MAR, which is 12). In 16 bits, unsigned binary numbers that correspond to the decimal values 0 up to 65535 can be stored. Treating these bit patterns as 2's complement numbers instead, the values range from -32768 up to +32767. Since addresses are only 12 bit long, they can go from 0 up to 4095, and are never considered negative.