SML  simulator

This is the simulator for "Simple Machine Language", an abstract machine defined in the Brookshear's textbook Chapter 2 Data Manipulation and Appendix C.  Students can write a SML program and run it. 

The SML machine has 256 bytes of memory.  It has 16 registers.  Each register is 8 bits.  Each instruction occupies 2 bytes (16 bits). The instruction has two forms:  
op:4 r:4 s:4 t:4
op:4 r:4 xy:8   

SML instructions

load r xy   //  r <- M[xy]
load r #xy  //  r <- xy
store r xy  //  r -> M[xy]
move s t    //  s -> t
add r s t   //  r <- s + t
addf r s t  //  not implement
or r s t    //  r <- s bit-OR t
and r s t   //  r <- s bit-AND t
xor r s t   //  r <- s bit-XOR t
ror r x     //  r <- rotate-right(r) by x bits
jeq r xy    //  if r == r0  PC <- xy 
jgt r xy    //  if r > r0 PC <- xy
jmp xy      //  PC <- xy, unconditional jump

halt        //  stop

To write SML programs, I define "Assembly language" for it.  Instead of writing in numbers, one can write in mnemonics, a simplified instruction notation. You will be writing numbers in base 10 (decimal). It is easier this way. The display in the simulator is hexadecimal (base 16) to be similar to the textbook.

Simplified mnemonic

L load
S store
M move
A add
O or
N and
X xor
R ror
J jeq
G jgt
B jmp

H halt

register: r, s, t, is  0..15  (registers)
constant: xy is 0..255     (address and constant)

I have included two new instructions (extended the textbook):

G r xy      compare r with r0 and make a jump to xy if  r > r0
B xy        unconditional jump to xy


They are used to make if..then..else kind of construct in a high level programming language.

Sample session

Three examples

a)  add two numbers.  The first number is in the memory location 100, the second is at 101.  The result is stored to location 102. 

We use two registers to keep the values: r1, r2.

L 1 100      ; load the first value
L 2 101      ; load the second value
A 1 1 2      ; add r1 r2, the result in r1
S 1 102      ; store result to memory
H

and we initialise the memory location by

D 100 10
D 101 20
E

and end the assembly program by E. Let us write this program to a file "add.txt". First step is to translate this program into a machine code.  We use "asm.exe" to do that.  The output is store in the file "add.obj".

To run the "asm.exe", we use "cmd".  You click "start" -> "run" then type "cmd". A terminal will pop up.  Change directory to your target directory where "asm.exe" and your "add.txt" reside.

c:sml\test>

Do assemble:

c:sml\test> asm < add.txt > add.obj

The second step is to run the machine code under the simulator "sml.exe".

c:sml\test> sml < add.obj

The output will look like this on your terminal.

00 1164
02 1265
04 5112
06 3166
08 C000

64 0A
65 14


The first few lines are the machine code. Next the simulator shows each step of running the machine code. Each line shows the program counter, the instruction register then the value of each registers (show only 8 registers) then the value of the memory between 100..110.

PC:00 IR:1164 load
R 0:00 1:0A 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 0A 14 00 00 00 00 00 00 00 00 00 00
PC:02 IR:1265 load
R 0:00 1:0A 2:14 3:00 4:00 5:00 6:00 7:00
M[64] 0A 14 00 00 00 00 00 00 00 00 00 00
PC:04 IR:5112 add
R 0:00 1:1E 2:14 3:00 4:00 5:00 6:00 7:00
M[64] 0A 14 00 00 00 00 00 00 00 00 00 00
PC:06 IR:3166 store
R 0:00 1:1E 2:14 3:00 4:00 5:00 6:00 7:00
M[64] 0A 14 1E 00 00 00 00 00 00 00 00 00
PC:08 IR:C000 halt
R 0:00 1:1E 2:14 3:00 4:00 5:00 6:00 7:00
M[64] 0A 14 1E 00 00 00 00 00 00 00 00 00
execute 5 instructions

c:sml\test>


You can see that we add M[100] with M[101] and store it to M[102].  So, it is 10+20 (0A, 14 in hexidecimal).  The result is 30 (1E).

b)  move data in the memory 100,101, to location 102,103

Use r1 to keep data.  Let the file be "move.txt".

L 1 100         ; load r1 with M[100]
S 1 102         ; store it to M[102]
L 1 101         ; do the same with M[101]
S 1 103
H
D 100 11        ; initialise M[100] with 11
D 101 22        ; M[101] = 22
E

c:sml\test> asm < move.txt > move.obj

c:sml\test> sml < move.obj

00 1164
02 3166
04 1165
06 3167
08 C000

64 0B
65 16

PC:00 IR:1164 load
R 0:00 1:0B 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 0B 16 00 00 00 00 00 00 00 00 00 00
PC:02 IR:3166 store
R 0:00 1:0B 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 0B 16 0B 00 00 00 00 00 00 00 00 00
PC:04 IR:1165 load
R 0:00 1:16 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 0B 16 0B 00 00 00 00 00 00 00 00 00
PC:06 IR:3167 store
R 0:00 1:16 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 0B 16 0B 16 00 00 00 00 00 00 00 00
PC:08 IR:C000 halt
R 0:00 1:16 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 0B 16 0B 16 00 00 00 00 00 00 00 00
execute 5 instructions

c:sml\test>


In the final step, you can observe the memory location 102,103 (0B,16) are the copy of location 100,101.

c)  In this example, a decision is made and the program reaches a "branch".  One way is chosen if the test is true.  The other is chosen when the test is false. We use a sequence of branches to achieve this effect. When consider the destination of branch, remember that each instruction occupies two locations (two bytes) in the memory. 

We will do a simple decision.  If the value at the memory location 100 is equal 5, then we set r1 to 1 else we set it to 0. We use r0 to keep 5 and use it to compare with M[100].  r2 is used to keep the value of M[100]. The instruction jump-if-equal (J) compare r with r0 if they are equal then the jump is made.

L 0 #5        ;  set r0 = 5
L 2 100       ;  get value from M[100]
J 2 10        ;  if it is 5 then goto instruction at 8
L 1 #0        ;  set r1 = 0 (they are not equal)
B 12          ;  goto location 12 (H)            
L 1 #1        ;  set r1 = 1 (they are equal) 
H
D 100 5       ;  M[100] is set to 5
E


Note about location of jump, remember that each instruction occupies 2 bytes, so J 2 10, jump if equal to location 10, refer to instruction L 1 #1.  And B 12 refers to instruction at location 12 which is H.

c:sml\test> asm < compare.txt > compare.obj

c:sml\test> sml < compare.obj

PC:00 IR:2005 load#
R 0:05 1:00 2:00 3:00 4:00 5:00 6:00 7:00
M[64] 05 00 00 00 00 00 00 00 00 00 00 00
PC:02 IR:1264 load
R 0:05 1:00 2:05 3:00 4:00 5:00 6:00 7:00
M[64] 05 00 00 00 00 00 00 00 00 00 00 00
PC:04 IR:B20A jeq
R 0:05 1:00 2:05 3:00 4:00 5:00 6:00 7:00
M[64] 05 00 00 00 00 00 00 00 00 00 00 00
PC:0A IR:2101 load#
R 0:05 1:01 2:05 3:00 4:00 5:00 6:00 7:00
M[64] 05 00 00 00 00 00 00 00 00 00 00 00
PC:0C IR:C000 halt
R 0:05 1:01 2:05 3:00 4:00 5:00 6:00 7:00
M[64] 05 00 00 00 00 00 00 00 00 00 00 00
execute 5 instructions


You can observe the sequence of "jump".  The result in r1 is 1.

Meta command

Help to setup the memory content

D xy v      ;  M[xy] = v
E           ; end of section

The assembler

The source contains code section and optional data section. The comment starts with ";" to the end of line. The last command of the code section must be "H".  The last meta command of the data section must be "E".  This is a simple example of a source of SML program.

; a simple example
L 1 100     ;  load r1 with M[100]
L 2 101     ;  load r2 with M[101]
A 3 1 2     ;  add r1 r2
H           ;  halt
D 100 20    ;  data section M[100] = 20
D 101 30    ;  M[101] = 30
E           ;  end

The assembler reads the source program in SML language and outputs a machine language file. The code starts at address 0. Run it from the command line:

C:>asm < source > object

The output object file of the above example is:
00 1164
02 1265
04 5312
06 C000
FF
64 14
65 1E
FF

The first column is the address. The first section is the code.  The second section is the data. The "FF" marks the end of each section.

The simulator

Start the simulator, reading the object file and execute it. Start execute at PC = 0 and run until HALT (or execute more than 100 instructions)

C:>sml < object

Tools

The SML package (source and executable for simulator and assembler):   sml2-2014.zip    (update version)
Just unzip and look for the executable files in the subdirectory /test  


last update 23 March 2014