ASSIGNMENT 2

Assignment goal:

Understanding stack manipulations, and the co-routine mechanism, as needed to create a simple threads-like package. Basic system calls. Further Hands-on experience with CISC features, specifically 80X86.

Program goal:

You are to write a mechanism in 80X86 assembly language that supports an arbitrary number of co-routines ("threads"), and exercises it. After start-up and initialization, your program starts off your co-routine mechanism. Your mechanism should support:

  • Adding a co-routine at runtime.
  • Removing a co-routine at runtime.
  • Stopping the co-routine mechanism and returning to normal operation.
  • Passing a parameter at startup to each co-routine. In the initialization phase, your program should set up two co-routines: a scheduler, and a reader. In addition, there are two other types of co-routines, called master and slave.

    Details

    Below are the details of what each type of co-routine. Note that there is only one scheduler, and one reader, which are initialized at startup. The co-routine mechanism should then start by resuming the scheduler.

    Scheduler

    The scheduler is a co-routine that keeps track of the number of co-routines - it schedules all the other co-routines by resuming them in turn (in round-robin fashion). If at some point there is no co-routine to resume, the scheduler terminates the co-routine mechanism and returns the program to normal operation. Note that the scheduler is a co-routine as well, but it does not schedule itself. To make this convenient, either keep the pointer to its co-routine descriptor separate, or make it entry -1 in the array (if you elect to use an array of co-routine pointers).

    Reader

    The reader reads command lines from the keyboard, in an infinite loop. At each iteration, the reader prompts the user by printing the string:

    Command (n)
    

    where n is the iteration count. After reading any command (except for no-op), the reader prints a line about the command (e.g. "starting slave 5"), and executes it. Commands are usually single-letter commands:

  • Q: quit, i.e. terminate co-routine mechanism.
  • S: add a slave co-routine.
  • M: add a master co-routine.
  • K m: delete co-routine number m, if it exists (this is the only command that is not just one letter).
  • N: no-op. (An empty line or any unrecognized command should be treated by the reader as a no-op).

    Both the S and M commands add a co-routine, passing to the co-routine a number stating how many co-routines of that type have been created. For example, the first master should receive 1. After adding a co-routine, the reader immediately resumes the new co-routine. After receiving and executing a kill command or a No-op, the reader should resume the scheduler. Note that the reader should keep track of the number of slaves and masters, so that it can print the appropriate message, and in order to know if a co-routine to be killed exists, etc. Additionally, if you use an array to store the co-routine pointers, the reader should have index (and ID) number 0.

    Master

    A master is a co-routine that prints letters, from A to Z and then from a to z. After z (if it ever gets there), the master should terminate the co-routine mechanism. After each line printed, the master should resume the scheduler It works according to the pseudo-code (note that there should be 2 SEPARATE calls to resume, e.g. one for each loop - do NOT merge the loops!):

    Master(i) {
    
      for(x from A to Z) {
     	printf("I am master number %d and my letter is %c\n", i, x);
            resume(scheduler)
      }
      for(x from a to z) {
     	printf("I am master number %d and my letter is %c\n", i, x);
            resume(scheduler)
      }
      exit_co_routines();
    }
    

    Slave

    A slave is a co-routine that prints numbers, from 0 to 8 and then from 9 to 1. It repeats this sequence forever. After each line printed, the slave should resume the scheduler It works according to the pseudo-code (note that there should be 2 SEPARATE calls to resume, e.g. one for each loop - do NOT merge the loops!):

    Slave(i) {
      forever() {
         for(x from 0 to 8) {
     	printf("I am slave number %d and my iteration is %d\n", i, x);
            resume(scheduler)
         }
         for(x from 9 to 1) {
     	printf("I am slave number %d and my iteration is %d\n", i, x);
            resume(scheduler)
         }
       }
    }
    

    Additional Requirements and Assumptions

    Except for program initialization and wrap-up, which should be in the function main() written in C, the rest should be written in assembly language.

    To output data to the user (i.e. all runtime printouts), you may not use any library function, but must use LINUX system services (through int 0x80) directly. Specifically, use the "write" system call to the "standard output" file descriptor. However, you may use library functions, such as sprintf, to format your string before making the system call, if you wish to do so.

    You may assume that there are no more than 20 co-routines, and allocate sufficient space in memory for all structures (and stacks) statically. For example, you can have an array of stacks. However, you may not initialize their contents statically - initialization must be done at runtime.

    At the risk of stating the obvious (though clearly not obvious to everybody), your program should never crash, even on errors... That is, you should always check array bounds if there is a chance that they can overflow (e.g. when adding threads), etc.

    What to submit

    To be added by the TAs. Deadline: December 25