Machine Level Programming for Sx-chip

The machine language for Sx-chip is S-code. S-code is the instruction set of Som-v2.  It is a simplified assembly language suitable to be a platform independent machine language (similar to Java byte-code and Java virtual machine, JVM).  S-code execution is stack-based.  It uses a stack to keep operands during excution of the operation code.  For example, to do "1+2", the sequence of operations is:

1) push 1
2) push 2
3) do add

the stack looks like after step 2:

top

2
1

bottom

and after step 3:

top

3

bottom

This stack (called "evaluation stack") is kept in the main memory.  The pointer to this stack stored in the register SP in the Sx-chip.

The set of operation codes in S-code includes:

add sub mul div
and or xor not
eq ne lt le ge gt
shl shr

The data-transfer codes include:

get.v put.v    get/put local variables
ld.a  st.a     ld/st global variables
ldx stx        ld/st element of an array
lit.n          pushing a constant n
inc.v dec.v    increment/decrement local var

control-transfer codes are:

call.a ret.n     call/ret a function
fun.n            function header
jmp.a jt.a jf.a  jump, jump if true/false

special codes:

array          allocate free mem
sys.n          system function

For detail of S-code including the encoding please see here.

Machine code in Z-assembler

Z-assembler (Zas) is a tool to convert a program written in S-code to an executable program (an object code).  A Sx-chip simulator can be used to "run" this object code.  The source file for Zas consists of two sections: symbol definition, and s-code.

; comment
name  value
...
#
:label
  opcode[.operand]
  ..
#

name is the symbolic name denotes a value (for example a local variable number, an address of a global variable). An operand can be a lable or a name.  Zas will resolve the value of labels and names. The output of Zas is the object file.  The format of object file conforms to Som-v2 object format.

Example of programs in S-code

1  Calculate the sum from 1 to 10.

;  a b s are local variables
sum a b
  s = 0
  while a <= b
    s = s + a
    a = a + 1
  ret s

main
  print sum 1 10

in S-code

; a program to sum 1..10

a  3          ; local vars, reverse order
b  2          ; of their appearance
s  1
#
:main
  lit.1
  lit.10
  call.sum    ; sum(1,10)
  sys.1       ; print
  sys.13      ; stop

:sum
  fun.3
  lit.0
  put.s       ; s = 0
  jmp.in
:loop
  get.s
  get.a
  add
  put.s       ; s = s + a
  inc.a       ; a = a + 1
:in
  get.a
  get.b
  le
  jt.loop     ; loop if a <= b
  get.s
  ret.4
#

2  Double every elements in an array.

To access an element of an array, ldx/stx are used.  ldx requires {ads,index} in the stack.  stx requires {ads,index,value} in the stack.  This is the example:

create an array ax with 10 elements

  lit.10
  array
  st.ax        ;  ax = new 10

s = ax[i]

  ld.ax
  get.i
  ldx
  put.s

ax[k] = s
 
  ld.ax
  get.k
  get.s
  stx

Now we can write the program:

double x
  ret 2 * x

main
  ax = new N
  i = 0
  while i <= N
    s = double( ax[i])
    ax[i] = s
    i = i + 1

;  double every elements of an array

N 10
ax 1000     ; address of ax[.]
x 1
i 2
s 1
#
:main
  lit.N
  array
  st.ax     ; ax = new N
  lit.0
  put.i     ; i = 0
  jmp.in
:loop
  ld.ax
  get.i
  ldx
  call.double
  put.s     ; s = double ax[i]
  ld.ax
  get.i
  get.s
  stx       ; ax[i] = s
  inc.i
:in
  get.i
  lit.N
  le
  jt.loop   ; loop if i <= N
  sys.13    ; stop

:double
  fun.1
  get.x
  lit.2
  mul       ; 2 * x
  ret.2
#

How to calculate offset in "fun" and "ret"

The function header  "fun"contains the offset to create a new activation record.  This offset is
  fs - pv + 1

where fs is the size of the activation record, pv is the number of passing paramters.  The offset for "ret" is fs.

Look at the diagram of the activation record to understand the calculation. The old activation record is at FP'.  On top of it is the evaluation stack.  The passing parameters are on the stack at SP'.  Now the size of the new activation record is the number of all variables (pv+lv) pluses a slot to store FP'.

hi mem
..
PC'  <- SP
FP'  <- FP
..
lv
..
pv   <- SP'
..
sp   <- SP''
..   <- FP'   
lo mem

Hand-on

Try to assemble the first program, "sum-s.txt".  Use the assembler, Zas.

c:> zas sum-s.txt > sum-s.lst

The object file output is "a.obj".  The screen output is the listing file "sum-s.lst".  This file is inspeced by humans to detect and correct any error in the source file. The listing looks like this:

1 lit 1   287
2 lit 10   2591
3 call 6   1568
4 sys 1   292
5 sys 13   3364
6 fun 2   550
7 lit 0   31
8 put 1   281
9 jmp 6   1564
10 get 1   280
11 get 3   792
12 add 0   1
13 put 1   281
14 inc 3   802
15 get 3   792
16 get 2   536
17 le 0   12
18 jt -8   -2019
19 get 1   280
20 ret 4   1044

The first column shows the address of the S-code in the memory.  The second column shows the S-code.  The last column shows the actual machine code (executable code). Please note that the defined symbols and lables had resolved into proper values. The object code with the format suitable for Sx-simulator is this:

5678920
1 20
287 2591 1568 292 3364 550 31 281 1564 280
792 1 281 802 792 536 12 -2019 280 1044
0 0

Use Sx-simulator to run the object file.

C:\prabhas\bag\sx0\test>sx0 a.obj
load program, last address 21
DP 1
1 2 3 7 8 9 15 16 17 18
10 11 12 13 14 15 16 17 18
10 11 12 13 14 15 16 17 18
...
10 11 12 13 14 15 16 17 18
19 20 4 <55>5

The screen shows the sequence of address of the code during execution. You can match this with the listing to see how the program proceeds. The "print" to screen (sys.1) from the program appears as "<55>".

last update 3 July 2011 (National election day)