Noss
nos-supervisor (or
nos simulator)
Noss is a previledge program. A previledge program is a program that is
"out-of-bound" of user programs. A previledge program provides
mechanism for executing a user program, for example, Sx-sim is a
previledge program that "execute" s-code. In our implementation,
previledge programs are written in C. User programs are written
in Nut. Nos is a user program.
Noss controls sx-sim. Noss drives
sx-sim to execute instructions for a "quanta". A quanta is a time
period counted as clock cycles. The eval loop of sx-sim executes
one instruction
and checks for time-out. The number of cycle used is
accumulated. When this number exceeds "quanta", noss takes action
by calling "switchp" in nos.
"switchp" saves the current computation state. The computation
states are pc, sp, fp and ts (program counter, stack pointer, frame
pointer, Sx processor also has a top of stack register) of the current
process. They are saved to the
process descriptor (the data structure to hold states of a
process). The next process in the process queue is
then activated. Its computation state is then "loaded" into
sx-sim. At this point, the user program (the current process) resumes
its execution and continues until noss interrupts it. "switchp" is a
previledge process, it is allowed to run to completion
without interruption.
The situation above describes a time-out situation where a process has
used up its own time allocation. Other kind of interrupts are,
when the process is blocked, and when the process is terminated.
The "blocked" process will be discussed later when we do message
passing and other resource sharing.
Noss is minimal in the sense that it does not do a lot of things by
itself. The only thing it does is to call "switchp".
"switchp" is in user space. noss monitors the state of computation of a
process through global variables: status and activep. The main
loop of noss is "boot". It is as follows.
void
boot(void){
. . .
eval(1); // boot nos
while(M[a_activep] != 0){ // interrupt-execute loop
M[a_status] = status; // update to nos
printf(" * ");
eval(callswitchp); // run switcher
}
}
eval(1) executes "main" user function which will start the operating
system (nos) and creates process queue. At the end of "main"
function, it calls "bootnos" (user-space) which calls "runnable" to
start the current active process. "runnable" issues a kernel call
to "restore C-state" (privilege program) in noss to switch to
user-process and executes it until interrupted
(time-out/blocked/terminated). M[a_activep] and M[a_status] are
global variables in user-space, they are used to communicate between
noss and nos. They appear as "activep" and "status" in nos.
how to compile "run"?
s-obj must contain symbol table with the correct references.
s-obj is generated by gen2.txt. However, gen2.txt just passes
symbol table through. The symbol table is read from n-obj.
n-obj is generated by nut.txt, the compiler. The current version
dumps everything in the symbol table.
The symbols that must be exported are of type FUN and GVAR only.
The following tasks must be done.
1) change nut.txt to output only the necessary one.
2) change gen2.txt to output the s-code reference. However the
number of symbol does not change.
at nut.txt
dumpsym is responsible to output the symbol table. relocate the
reference to function such that the code segment starts at 2. It
is necessary as nut-compiler is used under "nsim" where both the
compiler and the user program to be compiled occupy the same code
segment. Therefore the user program in the code segment will not
start at 2. We would like the object to be relocatable, therefore
the user code should start at 2.
When starting the compiler, (sys 9) is used to find out where to user
code segment is. The global variable "Start" stores this
location, and it is used to relocate the reference to all function call
when output the object.
The following code is added in nut.txt at dumpsym, to output the
correct reference. The data segment is not relocated as it is
already started at 0.
(if (= ty tyFUN)
(set n (shift (getVal i) Start))
; else
(set n (getVal i)))
(print n) (space)
at gen2.txt
when read symtab from n-obj, it recognises the type "fun" and outputs
the s-address corresponding to the n-address. Here is the added
code to outsym, to output symtab.
(set ty (atoi
tok))
(tokenise)
; ref, reloc
(if (= ty
tyFUN)
(do
(set ref (shift (atoi tok) CS))
(print (assoc ref)))
;
else
(prstr tok))
(space)
how to gen "run"
(run
(fn ...))
will generate a call to "run", placing the address pointed to (fn ..)
as its argument.
lit x
call run
jmp y
x: ...
call fn
end
y: ...
The address of x is the next 3 words. jmp x skips the code
(fn...). (fn ..) is deferred and "run" will use x as the starting
address of the process (fn...). When the process returned, it will be
terminated by "end". This is in the function gencall.
;
convert arg to index to symtab
; e is arglist
(def gencall (arg e) (idx a)
(do
(set idx (searchRef arg))
(if (= idx
Runidx) ; is "run"
(do
(outa icLit (+
XP 3)) ; point to code of process
(outa icCall
idx) ; call run
(set a XP)
(outa icJmp 0)
(eval (head e))
(outs icEnd)
(patch a (- XP
a))) ; jump over
; else
(do
; normal call
(while e
(do
(eval (head e))
(set e (tail e))))
(outa icCall
idx)))))
Example of code
generation for "run"
A call to "run" is compiled into passing an address of the code for the
function call (the argument of "run" is a function call). The
argument
to call.run in n-code is just a list of user-function call with its
arguments as usual. However, this argument
will not be evaluated. Instead, the address of the code of this
call
will be generated as an argument of "run". See the following
example:
(def
add (a b) () (+ a b))
(def run (f) () 0)
(def main () ()
(do
(run (add 4 5))))
in n-code
add
(fun.2.2 (+ get.1 get.2 ))
run
(fun.1.1 (lit.0 ))
main
(fun.0.0 (do (call.17 (call.14
lit.4 lit.5 ))))
generate s-code. See line 12-18
1
Call main
2
End
3
Fun add
4
Get 2
5
Get 1
6
Add
7
Ret 3
8
Fun run
9
Lit 0
10 Ret 2
11 Fun
main
12 Lit 15
13 Call
run
14 Jmp 19
15 Lit 4
16 Lit 5
17 Call
add
18 End
19 Ret 1
The line "(run (add 4 5 ))" becomes
12 Lit
15 ;; address of code (add 4 5)
13 Call
run
14 Jmp
19 ;; do not execute now
15 Lit
4 ;; the code (call.add lit.4 lit.5)
16 Lit 5
17 Call
add
18 End
19 . . .
last update 17 Aug 2006