computer organization
c m s c 311  
s u m m e r   2 0 0 2  

Project #3

Due Date Friday, July 12, 11:59 PM (just before midnight)

Posted: July 6, 2002

Additions

These are clarifications to various project specs.

Clarifications

Purpose

In Project #2, the data was stored in a simulated memory (i.e., an array). However, the code (i.e., the text) was stored in whatever data structure you chose.

In this project, there are several goals:

Academic Integrity Statement

Please note that *all* programming projects in this course (including this one) are to be done independently or with the assistance of the instructional staff of this course only, unless otherwise specified IN PRINT by official webpages.

Please review the policies outlined on the class syllabus concerning the use of class computer accounts and concerning the University's Code of Academic Integrity. The instructors of this course will review the programs submitted by students for potential violations of the Code of Academic Integrity and if it is believed that a violation has occurred it will be referred to the Office of Judicial Programs and the Student Honor Council.

Hardcoding is considered a violation of academic integrity

What Could Have Been

Due to the lack of time, this will simply be an exercise to convert assembly language instructions into binary. If we had had more time, you would have stored the assembly language instructions in binary, and written a decoder to load it from memory, figure out what instructions were there, and run them.

If we had had even more time, we would begin to do some microprogramming. This involves storing control signals in a ROM, and sending control signals to various registers and getting data to move around.

That would be very interesting, but quite time-consuming. But just in case you were curious, that's where we would have gone.

Additional Commands

There are two additional commands to implement.

Format

When writing to a file, there will be a division between the data segment and the text segment. Data formats for object files .o files are somewhat more sophisticated than the format given, and more standardized, so they can integrate with other object code.

The first 32 bit value written to the file is: DABE.

It's often the case that a file has some sort of binary "signature" that indicates what kind of file it is. That way, when you're loading the file, you can determine if it's a legitimate file. While one could certainly write a bogus file that begins with DABE, the odds are pretty low that it would happen by accident.

I'd like to say that DABE stands for Decimal And Binary Encoding, or something officious-sounding. In reality, I chose it for several reasons. First, it can be written in hex. Second, it isn't as morbid-sounding as "DEAD BEEF", which also can be written in hex. Third, it's the name of a former 311 student and undergraduate staffer (from the mid 90s). Yes, his name was really "Dabe" (or at least, his nickname was).

After this initial part, you will store the data part.

Storing the data part

Initially, store the number of labels as a 32 bit value (Admittedly, 32 bits is way too large for the number of possible values).

For each label, do the following:

Storing the text part

The text portion is somewhat easier to store.

This is actually somewhat challenging. Here's what you should do first.

Format of instructions

Note: || means concatentation
Note: 016 means 16 zeroes.
Note: PC refers to the program counter
Note:

Command Explanation
add $rd, $rs, $rt
  • Reg[ d ] = Reg[ s ] + Reg[ t ]
  • R-type instruction
  • B31-26 = 000000 (SPECIAL)
  • B10-6 = 000000
  • B5-0 = 000000 (ADD)
addi $rd, $rs, immed
  • Reg[ d ] = Reg[ s ] + (immed15)16 || immed15-0
  • I-type instruction
  • B31-26 = 001000
  • Immediate value is sign extended
sub $rd, $rs, $rt
  • Reg[ d ] = Reg[ s ] - Reg[ t ]
  • R-type instruction
  • B31-26 = 000000 (SPECIAL)
  • B10-6 = 000000
  • B5-0 = 100010 (SUB)
and $rd, $rs, $rt
  • Reg[ d ] = Reg[ s ] & Reg[ t ]
  • Bitwise AND
  • R-type instruction
  • B31-26 = 000000 (SPECIAL)
  • B10-6 = 000000
  • B5-0 = 100100 (AND)
andi $rd, $rs, immed
  • Reg[ d ] = Reg[ s ] & 016 || immed15-0
  • I-type instruction
  • B31-26 = 001100
  • Immediate value is zero extended
or $rd, $rs, $rt
  • Reg[ d ] = Reg[ s ] | Reg[ t ]
  • Bitwise OR
  • R-type instruction
  • B31-26 = 000000 (SPECIAL)
  • B10-6 = 000000
  • B5-0 = 100101 (OR)
ori $rd, $rs, immed
  • Reg[ d ] = Reg[ s ] | 016 || immed15-0
  • I-type instruction
  • B31-26 = 001101
  • Immediate value is zero extended
xor $rd, $rs, $rt
  • Reg[ d ] = Reg[ s ] ^ Reg[ t ]
  • Bitwise XOR
  • R-type instruction
  • B31-26 = 000000 (SPECIAL)
  • B10-6 = 000000
  • B5-0 = 100110 (XOR)
xori $rd, $rs, immed
  • Reg[ d ] = Reg[ s ] ^ 016 || immed15-0
  • I-type instruction
  • B31-26 = 001110 (CORRECTED)
  • Immediate value is zero extended
beq $rd, $rs, LABEL
  • if ( Reg[ s ] == Reg[ t ] ) jump to LABEL else go to next instruction
  • I-type instruction
  • B31-26 = 000100
  • offset is B15-0
  • Jump to PC + [ (offset15)14 || offset || 02 ]
  • i.e., the offset is shifted to the left by 2, then sign extended.
bne $rd, $rs, LABEL
  • if ( Reg[ s ] != Reg[ t ] ) jump to LABEL else go to next instruction
  • I-type instruction
  • B31-26 = 000101
  • offset is B15-0
  • Jump to PC + [ (offset15)14 || offset || 02 ]
  • i.e., the offset is shifted to the left by 2, then sign extended.
slt $rd, $rs, $rt
  • if ( Reg[ s ] < Reg[ t ] ) Reg[ d ] = 0311 else Reg[ d ] = 032
  • R-type instruction
  • B31-26 = 000000 (SPECIAL)
  • B10-6 = 000000
  • B5-0 = 101010 (SLT)
slti $rd, $rs, immed
  • if ( Reg[ s ] < (immed15)16 || immed15-0) Reg[ d ] = 0311 else Reg[ d ] = 032
  • I-type instruction
  • B31-26 = 001010
jr $rd
  • R-type instruction (sort of)
  • B31-26 = 000000 (SPECIAL)
  • B20-6 = 000 0000 0000 0000
  • B5-0 = 000100 (JR)
j LABEL
  • PC <- PC31-28 || immed || 02
  • immed is 26 bit value
  • J-type instruction
  • B31-26 = 000010
jal LABEL
  • PC <- PC31-28 || immed || 02
  • immed is 26 bit value
  • Reg[ 31 ] <- PC + 4 (In the real MIPS, it should be PC + 8, but that's mostly because of pipelining--we use the simpler PC + 4, which is the next instruction).
  • J-type instruction
  • B31-26 = 000011
lw $rt, offset($rs)
  • I-type instruction
  • offset is sign-extended to 32 bits, and added to Reg[ s ] to produce address. The content of that address (which must be word-aligned), is placed in Reg[ t ]
  • B31-26 = 100011
  • B15-0 is the offset
lbu $rd, offset($rs)
  • I-type instruction
  • offset is sign-extended to 32 bits, and added to Reg[ s ] to produce address. The content of that address (which does NOT have to be word-aligned), is placed in Reg[ d ]
  • B31-26 = 100100 (CORRECTED)
  • B15-0 is the offset
sw $rt, offset($rs)
  • I-type instruction
  • offset is sign-extended to 32 bits, and added to Reg[ s ] to produce address. The content of Reg[ t ] is saved at that address (which must be word-aligned)
  • B31-26 = 101011
  • B15-0 is the offset
sb $rt, offset($rs)
  • I-type instruction
  • offset is sign-extended to 32 bits, and added to Reg[ s ] to produce address. The content of Reg[ t ] is saved at that address (which does NOT have to be word-aligned)
  • B31-26 = 101000
  • B15-0 is the offset
lui $rt, immed
  • I-type instruction
  • Reg[ d ] = immed || 016
  • B31-26 = 001111
  • B25-21 = 00000 (normally where rs goes, but leave it all zeroes)
bgtz $rs, LABEL
  • I-type instruction
  • B31-26 = 000111
  • B20-16 = 00000
  • Let offset = B15-0
  • PC-relative addressing. Let target = (offset15)14 || offset || 02
  • PC <- PC + target
bgez $rd, LABEL
  • I-type instruction
  • B31-26 = 000001 (REGIMM)
  • B20-16 = 00001 (BGEZ)
  • Let offset = B15-0
  • PC-relative addressing. Let target = (offset15)14 || offset || 02
  • PC <- PC + target
bltz $rd, LABEL
  • I-type instruction
  • B31-26 = 000001 (REGIMM)
  • B20-16 = 00000 (BLTZ)
  • Let offset = B15-0
  • PC-relative addressing. Let target = (offset15)14 || offset || 02
  • PC <- PC + target
blez $rd, LABEL
  • I-type instruction
  • B31-26 = 000110
  • B20-16 = 00000
  • Let offset = B15-0
  • PC-relative addressing. Let target = (offset15)14 || offset || 02
  • PC <- PC + target
sll $rd, $rt, shift_amt
  • Fall 2002
  • R-type instruction
  • Shift left logical
  • B10-6 is the shift amount (between 0-31)
  • B31-26 = 000000 (SPECIAL)
  • B5-0 = 000000 (SLL)
  • Shift a copy of Reg[ t ] left by shift_amt bits, inserting zeroes into the low order bits.
srl $rd, $rt, shift_amt
  • Fall 2002
  • R-type instruction
  • Shift right logical
  • B10-6 is the shift amount (between 0-31)
  • B31-26 = 000000 (SPECIAL)
  • B5-0 = 000010 (SRL)
  • Shift a copy of Reg[ t ] right by shift_amt bits, inserting zeroes into the high order bits.

Writing to Screen

For the display-hex command, you should output:
Header: DABE

Address: 0x0000 2000
Type:    0x43
Size:    0x0000 0004
Data:    0x23 0x24 0x25 0x00

Address: 0x0000 2000
Type:    0x43
Size:    0x0000 0008
Data:    0x23 0x24 0x25 0x00
         0x23 0x24 0x25 0x00

Number of Instructions: 0x0000 004A
Initial Addresss: 0x0000 0800
Instruction 0: 00 FF 00 FB
Instruction 1: 00 FF 23 CD
Instruction 2: 00 FF 23 CD
Print the header, then for each label, print its address in hex (put a space between every four hex digits), the type using 2 hex digits, the size in hex, the data (if it's byte/byte array/string, list the quantities in hex format, with two hex digits. If it's a word/word array, use 8 hex digits in a row.).

Put up to 8 values on each line, and continue on subsequent lines (without the "Data: " in front). Place a blank line between the "labels".

Writing Binary Files

In C, to write to a file requires the unistd.h library. You need to open a file to write.
 int fd ; // fd is called a file descriptor
 ssize_t w1, w2 ;
 char header1[ 512 ], header2[ 1024 ] ;

 // Opens a file to be written, but only if file does not already exist.
 if ( ( fd = open( "newfile", O_WRONLY|O_CREAT|O_EXCL, 0644 ) ) == -1 )
   {
      cout << "Error opening file" << endl ;
      return -1 ;
   }

  w1 = write( fd, header1, 512 ) ;
  w2 = write( fd, header1, 1024 ) ;

  // close the file
  close( fd ) ;
The write function takes a file descriptor of an opened file, a pointer to some array (any type) or even just a plain structure or variable, and the number of bytes that will be written. The function returns an int-like value telling you how many bytes were actually written.

To open a file for reading (in C),

 int fd ; // fd is called a file descriptor
 ssize_t n1, n2 ;
 char buf[ 512 ], buf2[ 1024 ] ;

 // Opens a file for read only
 if ( ( fd = open( "oldfile", O_RDONLY ) ) == -1 )
   {
      cout << "Error opening file" << endl ;
      return -1 ;
   }

  n1 = read( fd, buf1, 512 ) ;
  n2 = read( fd, buf2, 1024 ) ;

  // close the file
  close( fd ) ;
Nelson Padua-Perez offers the C++ solution.
#include <iostream.h>
#include <stdlib.h>
#include <fstream.h>

struct Person
{
   int id;
   float salary;
};

// write takes a char *, and the number of bytes
void create_file(char *filename)
{
   int x;
   Person p;
   
   ofstream out(filename, ios::out);
   if (!out)
   {
      cerr << "File opening failed!" << endl;
      exit(1);
   } 

   for (x = 1; x <= 5; x++)
   {
      p.id = x;
      p.salary = x * 100;
      out.write(reinterpret_cast<const char *>(&p), sizeof(Person));
   }
}

// read takes a char * pointer, and the number of bytes.
// Notice you can store into any object---endian-ness is an
// issue, however.   If the file stores in big-endian, and the machine
// is little-endian, the number will be read in the wrong order.
// There's no information in the file to determine endian-ness.

void read_file(char *filename)
{
   int x;
   Person p;

   ifstream in(filename, ios::in);

   if (!in)
   {
      cerr << "File opening failed!" << endl;
      exit(1);
   }
   
   for (x = 1; x <= 5; x++)
   {
      in.read(reinterpret_cast<char *>(&p), sizeof(Person));
      cout << "ID: " << p.id << " , SALARY " << p.salary << endl;
   }
}

void read_entry(int id, char *filename)
{
   Person p;

   ifstream in(filename, ios::in);
    
   in.seekg((id - 1) * sizeof(Person));
   in.read(reinterpret_cast<char *>(&p), sizeof(Person));
   cout << "SID: " << p.id << " , SSALARY " << p.salary << endl;
}

void write_entry(int id, float salary, char *filename)
{
   Person p;

   ofstream out(filename, ios::out);

   p.id = id;
   p.salary = salary;
   out.seekp((id - 1) * sizeof(Person));
   out.write(reinterpret_cast<const char *>(&p), sizeof(Person));
}

int main()
{
   create_file("data");
   read_file("data"); // sequential reading

   cout << "READING ENTRY" << endl;
   read_entry(2, "data");

   cout << "RANDOM WRITING" << endl;
   write_entry(2, 1345, "data");
   read_entry(2, "data");

   return 0;
}

OUTPUT

% a.out
ID: 1 , SALARY 100
ID: 2 , SALARY 200
ID: 3 , SALARY 300
ID: 4 , SALARY 400
ID: 5 , SALARY 500
READING ENTRY
SID: 2 , SSALARY 200
RANDOM WRITING
SID: 2 , SSALARY 1345
% 

Submitting

"Primary" posted soon! (Or not...I may just let you submit the files, have a lengthy README, and submit secondaries.)


See the class syllabus for policies concerning email
Last Modified: Wed Jul 10 17:29:43 EDT 2002
left up down right home

Web Accessibility