If your program does not generate the correct output, you will not get full credit for efficiency; if the output ``looks like'' life, then you will get partial credit for correctness, and your efficiency score will be scaled: if it is extremely close to life, your efficiency score will be scaled by 0.6; if it is nowhere near, it will be 0. So, if you just take the given code and comment it, you will get 30 points for correctness and at most 20 points for commenting, or 50 points maximum. If you have a mostly correct program which is fast, you could get 50 * 0.6 = 30 points for efficiency and 20 points for commenting, plus some partial credit for correctness, or 50 + partial credit for correctness. So if your code is close to correct and is likely to get partial credit points, and it's reasonably fast, you should definitely turn that in. It is, of course, a judgement call as to how closely your code's output resembles that of life. If, for example, it looks exactly right in the center of the grid but does not handle the border properly, then you should expect to get the 0.6 scaling. If, on the other hand, it diverges very quickly from the correct output (e.g., will not even do the
. . . x x x . . .to
. x . . x . . x .transformation properly), then you should expect to get a zero for efficiency.
There were still some confusion about modifying driver.asm. You may not modify it. You can make your own versions, and you can certainly inline the routines into your life_xform when it would otherwise call the routines. Your code, however, will be run with different drivers, and any modifications you make to driver.asm will be ignored.
The C-intermixed-with-assembly implementation is:
struct thread_context { unsigned int reg[32]; /* really fewer */ int alive; } thread[256]; int cur_thread = 0; /* initialize the user-level threads package */ void thread_init(void) { int i; for (i = 1; i < 256; i++) thread[i].alive = 0; thread[0].alive = 1; /* main thread */ } void thread_yield(void) { int i; thread[cur_thread].reg[A0] = $a0; ... thread[cur_thread].reg[S0] = $s0; ... thread[cur_thread].reg[SP] = $sp; thread[cur_thread].reg[RA] = $ra; for (i = (cur_thread + 1) % 256; 1; i = (i+1) % 256) { if (thread[i].alive) { cur_thread = i; $a0 = thread[i].reg[A0]; ... $s0 = thread[i].reg[S0]; ... $sp = thread[i].reg[SP]; $ra = thread[i].reg[RA]; jr $ra } } } void thread_exit(void) { thread[cur_thread].alive = 0; thread_yield(); } void bootstrapper(void *arg, void (*fn)(void *arg)) { (*fn)(arg); /* jalr $a1; $a0 is already okay */ thread_exit(); } void thread_spawn(void (*fn)(void *arg), void *arg, void *stack) { int i; for (i = 1; i < 256; i++) { if (thread[i].alive == 0) { thread[i].reg[RA] = bootstrapper; thread[i].reg[A0] = arg; thread[i].reg[A1] = fn; thread[i].reg[SP] = stack; return; } } /* error to reach here */ } void thread_wait(void) { int i, nliving; for (;;) { thread_yield(); for (i = nliving = 0; i < 256; i++) if (thread[i].alive) nliving++ if (nliving == 1) return; } }
bsy@cse.ucsd.edu, last updated