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.
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:
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).
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:
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.
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(); }
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) } } }
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.
To be added by the TAs. Deadline: December 25