SECD virtual machine for pure_LISP
[ Index ]
[ LISP reference ]
[ LISP tutorial ]
[ SECD collection ]
[ SECD reference ]
This document describes an modified SECD virtual machine,
used for implementation of the pure_LISP language.
Use also the Tutorial - it provides some info about
installation and first steps in the package.
This document is not yet finished, so you may use the next additional sources
of info for my SECD machine:
- file History.txt describing the evolution of my
SECD architecture.
- pascal sources in subdirectory meta, especially file secd_cod.pp, defining
the main parts of the machine (at the begining of this file there is detailed comments).
1. Data types, registers, syntax of the command codes.
Registers
hard_SECK may be treated as 4 registers machine:
- S is the stack.
- E is the environment.
- C is the code (the executed program).
- K is the kernel state.
For efficiency it is represented by 4 meta_registers:
- SK - kernel stack
- EK - kernel environment
- CK - kernel code
- U - the copy of the interrupted user process.
hard_SECK include the boolean userMode. While the kernel is
working userMode is false and if some user process is invoked,
userMode becomes true.
2. Main codes (single process commands).
These codes may be devided into 2 groups:
1. Control codes - they may change E and C registers or may
intrrupt user process in some cases.
2. Primitive codes - they change only S register.
Control codes:
- 15 #prim_p
This code has 3 cases:
- p in [0..63] - starts the primitive code p
- p in [64..127] - starts Interrupt(p)
- p in [128..255] - executes TRApp(p-128)
3. Special codes (user calls, interrupts, runtime error hadling).
These codes changes all registers. They are used to swap control
between kernel and user processes.
- 44 #user0
(s' e' c'.s) e c sk ek ck u --> s' e' c' s e c nil
userMode:=true
This code invokes the user process (s' e' c') and is used by 3 argument
strict_LISP function (user0 S E C)
- 45 #user1
((s'.(e'.c')).s) e c sk ek ck u --> s' e' c' s e c nil
userMode:=true
CPCnt:=0
This code invokes the user process (s' e' c') and is used by 1 argument
strict_LISP function (user1 SEC)
- 46 #intr_i
(i s e.c) --> iSEC
s e c SK EK CK U --> (iSEC.SK) EK CK SK EK CK iSEC
userMode:=false
This code interrupts current user process, save it togheder
with int number i to structure iSEC.
iSEC is saved in register U (note that composite register K
holds the full state of the kernel at the moment of interrupt
begining. This copy will be used if runtime error occures
during the interrupt handling). Kernel is loaded into S E C
registers and iSEC is passed to it.
- 47 #prev_i
s e c --> (S1/E1/C1 s) e c
Loads some of swaped registers.
Interrupts:
- Interrupts 0..63 are caused by some SEC machine commands:
- #intr_0 is started by code #fork
- #intr_1 is started when current user process reaches
the timesharing limit.
- #intr_2 is started when CleanX pre-code (started from
#rtn, #rec and #trrec) find working recipe at top of S.
- #intr_3 is started by #rtn when some lazy evaluation
is completed, but there is a queue of processes, waiting
for the result of this evaluation.
S = (q p s) where q is a queue, p is the primitive code
which must be executed after the recipe substitution.
- Interrupts 64..127 are started by #intr code and are treated as
soft_SEC primitives:
- #intr_64 implements the pure_LISP function (fopen fmod fn)
- #intr_65 - function (fclose f)
- #intr_66 - function (eof f)
- #intr_67 - function (connect IP port)
- #intr_68 - function (listen maxconn port)
- #intr_96 - function (send msg io)
- #intr_97 - function (recv io)
- #intr_98 - function (sendc i stream)
- #intr_99 - function (recvc stream)
- #intr_100 - function (fail n)
- Interrupts 128..255 are caused by runtime errors.
4. Kernel codes (used for multitasking, i/o, ipc, synchronization).
Kernel primitive codes:
Input/output codes:
- 64 #openf
(m fn s) e c --> (ps s) e c
open file fn with mode m (0-input 1-output), result ps is an integer,
representing a stream pointer to the file descriptor.
- 65 #putlex
(ps lex s) e c --> (0/1 s) e c
try to send lexeme lex to stream with descriptor ps.
result is 0 if the try finished, 1 if operation is not finished.
- 66 #putc
(ps i s) e c --> (0/1 s) e c
same as putlex, but sends ascii code i to stream.
- 67 #settag
(i [x.y] a s) e c --> ([i|x.y] s) e c
changes subtag of the node [x.y].
- 68 #setcar
(b [x.y] a s) e c --> ([b.y] s) e c
changes first element (car) of the node [x.y].
- 69 #setcdr
(b [x.y] a s) e c --> ([x.b] s) e c
changes second element (cdr) of the node [x.y].
- 70 #opens
(m socket s) e c --> (ps s) e c
open stream, representing one direction of the TCP connection socket.
m is the direction (0-input 1-output), result ps is an integer,
representing a stream pointer.
- 71 #tcpconn
(P H s) e c --> (socket s) e c
Start TCP connection to host with IP address H on port P.
Result socket is an integer, representing pointer to Linux socket,
traing to establish the connection.
- 72 #tcplsn
(P M s) e c --> (socket s) e c
Start TCP listener on port P, allowing maximum M connections.
Result socket is an integer, representing pointer to Linux socket,
waiting for the client connections.
- 80 #getlex
(ps s) e c --> ((b.lex) s) e c
try to receive lexeme from stream with descriptor ps.
b is 0 if the try finished, 1 if operation is not finished.
lex is received lexeme if the try finished.
- 81 #getc
(ps s) e c --> ((b.i) s) e c
same as getlex, but receives ascii code i.
- 82 #scont
(ps s) e c --> ((b.r) s) e c
continue get/put operation for stream ps.
If operation finished, b is 0 and r is the result, else b is 1.
- 83 #seof
(ps s) e c --> (eof(ps) s) e c
Tests stream ps for eof.
If ps is a TCP connection test is for closed/broken connection.
- 84 #close
(ps s) e c --> (ps_nil s) e c
Closes stream ps.
Returns null stream pointer.
- 85 #pause
(n s) e c --> (n s) e c
pause SECK machine for n milliseconds.
This code is used to leave resources for other programs in Linux if
there is no work for the kernel and user processes.
- 86 #pintr
(n s) e c --> #intr_n(SECK)
This code is same as #intr_i, but gets its interrupt number
from S.
- 87 #gettag
([t|x.y] s) e c --> (t s) e c
Returns subtag of the node [x.y].
Possible subtags:
- 0 - node / pipe without msg handler
- 1 - recipe (thunk)
- 2 - pipe with msg handler
- 3 - stream
- 4 - TCP socket, waiting to be connected (connwait)
- 88 #getconn
(socket s) e c --> ((b.new_socket) s) e c
try to receive new connection from socket (created by #tcplsn or
#tcpconn).
b is 0 if the try finished, 1 if operation is not finished.
new_socket is a ready TCP connection if the try succeed.
(c) Skelet, Sep 2002 in terms of GNU GPL
Mail me at home or
work