612f20af108b27d01049f880d84f6f03539510c4
[jars.git] / mars.scm
1 ;;;
2 ;;; An implementation of the Memory Array Redcode Simulator (MARS)
3 ;;;
4
5 (import (chicken io)
6         (chicken string)
7         (chicken random)
8         (chicken condition)
9         matchable)
10
11 ;;; Constants
12 ;;
13
14 (define core-size 20)
15 (define max-steps 10000)
16
17
18 ;;; Instructions
19 ;;
20
21 (define ((make-instr opcode modifier A-mode A-num B-mode B-num name) . args)
22   (match args
23     (('copy n) (make-instr opcode modifier A-mode A-num B-mode B-num n))
24     (('copy-from! other n)
25      (set! opcode (other 'opcode))
26      (set! modifier (other 'modifier))
27      (set! A-mode (other 'A-mode))
28      (set! A-num (other 'A-num))
29      (set! B-mode (other 'B-mode))
30      (set! B-num (other 'B-num))
31      (set! name n))
32     (('opcode) opcode)
33     (('modifier) modifier)
34     (('A-mode) A-mode)
35     (('A-num) A-num)
36     (('B-mode) B-mode)
37     (('B-num) B-num)
38     (('name) name)
39     (('print) (print opcode
40                      "." modifier
41                      " " (mode-string A-mode) A-num
42                      ", " (mode-string B-mode) B-num
43                      (if (null? name) "" (conc " ; " name))))
44     (('set-opcode! x n) (set! opcode x) (set! name n))
45     (('set-modifier! x n) (set! modifier x) (set! name n))
46     (('set-A-mode! x n) (set! A-mode x) (set! name n))
47     (('set-A-num! x n) (set! A-num x) (set! name n))
48     (('set-B-mode! x n) (set! B-mode x) (set! name n))
49     (('set-B-num! x n) (set! B-num x) (set! name n))))
50
51 (define (mode-string mode)
52    (case mode
53      ((immediate) "#")
54      ((direct) "$")
55      ((indirect-A) "*")
56      ((indirect-B) "@")
57      ((pre-indirect-A) "{")
58      ((pre-indirect-B) "<")
59      ((post-indirect-A) "}")
60      ((post-indirect-B) ">")))
61
62 (define initial-instruction
63   (make-instr 'DAT 'F 'immediate 0 'immediate 0 '()))
64
65
66 ;;; Memory setup and addressing
67 ;;
68
69 (define core (make-vector core-size '()))
70
71 (define (core-get i)
72   (vector-ref core i))
73
74 (define (core-set! i x)
75   (vector-set! core i x))
76
77 (define (initialize-core)
78   (let loop ((i 0))
79     (unless (>= i core-size)
80       (core-set! i (initial-instruction 'copy '()))
81       (loop (+ i 1)))))
82
83 (define (dump-core)
84   (let loop ((i 0))
85     (unless (>= i core-size)
86       (print* i ":\t")
87       ((core-get i) 'print)
88       (loop (+ i 1)))))
89
90 (define (addr+ . args)
91   (foldl (lambda (a b)
92            (modulo (+ a b core-size) core-size))
93          0 args))
94
95 ;;; Programmes
96 ;;
97
98 (define (make-prog name instrs offset)
99   (list name instrs offset))
100
101 (define (prog-name prog) (list-ref prog 0))
102 (define (prog-instrs prog) (list-ref prog 1))
103 (define (prog-offset prog) (list-ref prog 2))
104
105 (define (install-prog prog addr)
106   (let loop ((ptr addr)
107              (instrs (prog-instrs prog)))
108     (unless (null? instrs)
109       (core-set! ptr ((car instrs) 'copy (prog-name prog)))
110       (loop (addr+ ptr 1) (cdr instrs))))
111   (make-player (prog-name prog)
112                (addr+ addr (prog-offset prog))))
113
114 (define (can-install-prog? prog-len addr)
115   (let loop ((ptr addr)
116              (remaining prog-len))
117     (if (= remaining 0)
118         #t
119         (if (null? ((core-get ptr) 'name))
120             (loop (addr+ ptr 1)
121                   (- remaining 1))
122             #f))))
123
124 (define (install-progs progs)
125   (let loop ((players '())
126              (progs-left progs))
127     (if (null? progs-left)
128         players
129         (let ((addr (pseudo-random-integer core-size))
130               (prog (car progs-left)))
131           (if (can-install-prog? (length (prog-instrs prog)) addr)
132               (loop (cons (install-prog prog addr) players)
133                     (cdr progs-left))
134               (loop players progs-left))))))
135
136 (define (make-player name ptr)
137   (list name ptr))
138
139 (define (player-name player) (car player))
140 (define (player-ptrs player) (cdr player))
141
142 (define (player-set-ptrs! player ptrs)
143   (set-cdr! player ptrs))
144
145
146 ;;; Executive function
147 ;;
148
149 (define (run players steps-left)
150   (cond
151    ((<= steps-left 0) players)      ;Tie between remaining players
152    ;; ((<= (length players) 1) players) ;There's only one player left who thus wins
153    (else
154     (let* ((player (car players))
155            (other-players (cdr players))
156            (ptrs (player-ptrs player))
157            (new-ptrs (execute-instr (car ptrs) (player-name player))))
158       (if (null? new-ptrs)
159           (run other-players (- steps-left 1))
160           (begin
161             (player-set-ptrs! player (append (cdr ptrs) new-ptrs))
162             (run (append other-players (list player)) (- steps-left 1))))))))
163
164 (define (execute-instr ptr name)
165   (let* ((instr (core-get ptr))
166          (A-ptr (eval-operand (instr 'A-mode) (instr 'A-num) ptr))
167          (B-ptr (eval-operand (instr 'B-mode) (instr 'B-num) ptr))
168          (modifier (instr 'modifier)))
169     (case (instr 'opcode)
170       ((DAT)
171        '()) ;Game over, man, game over!
172       ((MOV)
173        (if (eq? modifier 'I)
174            ((core-get B-ptr) 'copy-from! (core-get A-ptr) name)
175            (combine A-ptr B-ptr modifier name (lambda (x y) y)))
176        (list (addr+ ptr 1)))
177       ((ADD)
178        (combine-and-store A-ptr B-ptr modifier name addr+)
179        (list (addr+ ptr 1)))
180       ((SUB)
181        (combine-and-store A-ptr B-ptr modifier name
182                           (lambda (x y) (addr+ x (- y))))
183        (list (addr+ ptr 1)))
184       ((MUL)
185        (combine-and-store A-ptr B-ptr modifier name
186                           (lambda (x y) (modulo (* (addr+ x core-size)
187                                                    (addr+ y core-size))
188                                                 core-size)))
189        (list (addr+ ptr 1)))
190       ((DIV)
191        (condition-case 
192            (begin
193              (combine-and-store A-ptr B-ptr modifier name
194                                 (lambda (x y) (quotient (addr x core-size)
195                                                         (addr y core-size))))
196              (list (addr+ ptr 1)))
197          ((exn arithmetic) '())))
198       ((MOD)
199        (condition-case
200            (begin
201              (combine-and-store A-ptr B-ptr modifier name
202                                 (lambda (x y) (remainder (addr x core-size)
203                                                          (addr y core-size))))
204              (list (addr+ ptr 1)))
205          ((exn arithmetic) '())))
206       ((JMP)
207        (list (addr+ ptr ((core-get A-ptr) 'A-num))))
208       ((JMZ)
209        (list (addr+ ptr
210                     (if (case modifier
211                           ((A BA)
212                            (= 0 ((core-get A-ptr) 'A-num)))
213                           ((B AB)
214                            (= 0 ((core-get A-ptr) 'B-num)))
215                           ((X I F)
216                            (and (= 0 ((core-get A-ptr) 'A-num))
217                                 (= 0 ((core-get A-ptr) 'B-num)))))
218                         ((core-get A-ptr) 'A-num)
219                         1))))
220       ((JMN)
221        (list (addr+ ptr
222                     (if (not (case modifier
223                                ((A BA)
224                                 (= 0 ((core-get A-ptr) 'A-num)))
225                                ((B AB)
226                                 (= 0 ((core-get A-ptr) 'B-num)))
227                                ((X I F)
228                                 (and (= 0 ((core-get A-ptr) 'A-num))
229                                      (= 0 ((core-get A-ptr) 'B-num))))))
230                         ((core-get A-ptr) 'A-num)
231                         1))))
232       ((DJN))
233       ((SEQ CMP))
234       ((SNE))
235       ((SLT))
236       ((SPL))
237       ((NOP) (list (addr+ ptr 1)))
238       (else
239        (error "Unrecognised opcode" (instr 'opcode))))))
240
241 (define (combine-and-store A-ptr B-ptr modifier name f)
242   (case modifier
243     ((A) ((core-get B-ptr) 'set-A-num!
244           (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'A-num)) name))
245     ((B) ((core-get B-ptr) 'set-B-num!
246           (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'B-num)) name))
247     ((AB) ((core-get B-ptr) 'set-B-num!
248            (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'A-num)) name))
249     ((BA) ((core-get B-ptr) 'set-A-num!
250            (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'B-num)) name))
251     ((F I) ((core-get B-ptr) 'set-A-num!
252             (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'A-num))) name
253            ((core-get B-ptr) 'set-B-num!
254             (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'B-num)) name))
255     ((X) ((core-get B-ptr) 'set-A-num!
256           (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'B-num)) name)
257      ((core-get B-ptr) 'set-B-num!
258       (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'A-num)) name))))
259
260 (define (eval-operand mode num ptr)
261   (addr+ ptr
262          (case mode
263            ((immediate) 0)
264            ((direct) num)
265            ((indirect-A) (addr+ num ((core-get (addr+ ptr num)) 'A-num)))
266            ((indirect-B) (addr+ num ((core-get (addr+ ptr num)) 'B-num)))
267            ((pre-indirect-A)
268             (let ((aux-instr (core-get (addr+ ptr num))))
269               ((aux-instr set-A-num! (addr+ -1 (aux-instr 'A-num))))
270               (addr+ num (aux-instr 'A-num))))
271            ((pre-indirect-B)
272             (let ((aux-instr (core-get (addr+ ptr num))))
273               (aux-instr set-B-num!(addr+ -1 (aux-instr 'B-num)))
274               (addr+ num (aux-instr 'B-num))))
275            ((post-indirect-A)
276             (let* ((aux-instr (core-get (addr+ ptr num)))
277                    (old-A-num (aux-instr 'A-num)))
278               (aux-instr set-A-num! (addr+ 1 (aux-instr 'A-num)))
279               (addr+ num old-A-num)))
280            ((post-indirect-B)
281             (let* ((aux-instr (core-get (addr+ ptr num)))
282                    (old-B-num (aux-instr 'B-num)))
283               (aux-instr set-B-num! (addr+ 1 (aux-instr 'B-num)))
284               (addr+ num old-B-num)))
285            (else
286             (error "Unrecognized mode" mode)))))
287
288 ;;; TEST CODE
289
290 (define addressing-test
291   (make-prog 'at (list
292                    (make-instr 'DAT 'F 'immediate 42 'immediate 53 'at)
293                    (make-instr 'DAT 'F 'immediate 123 'immediate 256 'at)
294                    (make-instr 'MOV 'A 'indirect-B 4 'direct 7 'at)
295                    (make-instr 'NOP 'I 'immediate 0 'immediate 0 'at)
296                    (make-instr 'NOP 'I 'immediate 0 'immediate 0 'at)
297                    (make-instr 'NOP 'I 'immediate 0 'immediate 0 'at)
298                    (make-instr 'DAT 'F 'immediate -5 'immediate -6 'at)) 2))
299
300 (define imp
301   (make-prog 'imp (list (make-instr 'MOV 'I 'direct 0 'direct 1 'imp)) 0))
302
303 (define dwarf
304   (make-prog 'dwarf (list
305                      (make-instr 'DAT 'F 'immediate 0 'immediate -1 'dwarf)
306                      (make-instr 'ADD 'AB 'immediate 5 'direct -1 'dwarf)
307                      (make-instr 'MOV 'I 'direct -2 'indirect-B -2 'dwarf)
308                      (make-instr 'JMP 'I 'immediate -2 'immediate 0 'dwarf)) 1))
309
310 (initialize-core)
311 (define players (install-progs (list dwarf imp)))
312
313 (dump-core)