66ec04689749457b5c9a9185cb9b6aeea0c0d1fc
[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 (if (instr-zero? B-ptr modifier #f)
210                             ((core-get A-ptr) 'A-num)
211                             1))))
212       ((JMN)
213        (list (addr+ ptr (if (not (instr-zero? B-ptr modifier #f)
214                                  ((core-get A-ptr) 'A-num)
215                                  1)))))
216       ((DJN)
217        (list (addr+ ptr (if (not (instr-zero? B-ptr modifier #t)
218                                  ((core-get A-ptr) 'A-num)
219                                  1)))))
220       ((SEQ CMP)
221        (list (addr+ ptr (if (compare-instrs A-ptr B-ptr modifier =) 2 1))))
222       ((SNE)
223        (list (addr+ ptr (if (compare-instrs A-ptr B-ptr modifier =) 1 2))))
224       ((SLT)
225        (list (addr+ ptr (if (compare-instrs A-ptr B-ptr modifier <) 2 1))))
226       ((SPL)
227        (list (addr+ ptr 1) (addr+ ptr ((core-get A-ptr) 'A-num))))
228       ((NOP)
229        (list (addr+ ptr 1)))
230       (else
231        (error "Unrecognised opcode" (instr 'opcode))))))
232
233 (define (compare-instrs A-ptr B-ptr modifier test)
234   (let ((A-instr (core-get A-ptr))
235         (B-instr (core-get B-ptr)))
236     (case modifier
237       ((A) (test (A-instr 'A-num) (B-instr 'A-num)))
238       ((B) (test (A-instr 'B-num) (B-instr 'B-num)))
239       ((AB) (test (A-instr 'A-num) (B-instr 'B-num)))
240       ((BA) (test (A-instr 'B-num) (B-instr 'A-num)))
241       ((F) (and
242             (test (A-instr 'A-num) (B-instr 'A-num))
243             (test (A-instr 'B-num) (B-instr 'B-num))))
244       ((X) (and
245             (test (A-instr 'A-num) (B-instr 'B-num))
246             (test (A-instr 'B-num) (B-instr 'A-num))))
247       ((I) (and
248             (if (eq? test =)
249                 (and
250                  (eq? (A-instr 'opcode) (B-instr 'opcode))
251                  (eq? (A-instr 'modifier) (B-instr 'modifier))
252                  (eq? (A-instr 'A-mode) (B-instr 'B-mode))
253                  (eq? (A-instr 'B-mode) (B-instr 'A-mode)))
254                 #t)
255             (test (A-instr 'A-num) (B-instr 'B-num))
256             (test (A-instr 'B-num) (B-instr 'A-num)))))))
257
258 (define (instr-zero? ptr modifier decrement)
259   (let ((instr (core-get ptr)))
260     (case modifier
261       ((A AB)
262        (if decrement (instr 'set-A-num! (addr+ (instr 'A-num) -1)))
263        (= 0 (instr 'A-num)))
264       ((A AB)
265        (if decrement (instr 'set-B-num! (addr+ (instr 'B-num) -1)))
266        (= 0 (instr 'B-num)))
267       ((X I F)
268        (if decrement
269            (begin
270              (instr 'set-A-num! (addr+ (instr 'A-num) -1))
271              (instr 'set-B-num! (addr+ (instr 'B-num) -1))))
272        (and (= 0 (instr 'A-num))
273             (= 0 (instr 'B-num)))))))
274
275 (define (combine-and-store A-ptr B-ptr modifier name f)
276   (case modifier
277     ((A) ((core-get B-ptr) 'set-A-num!
278           (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'A-num)) name))
279     ((B) ((core-get B-ptr) 'set-B-num!
280           (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'B-num)) name))
281     ((AB) ((core-get B-ptr) 'set-B-num!
282            (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'A-num)) name))
283     ((BA) ((core-get B-ptr) 'set-A-num!
284            (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'B-num)) name))
285     ((F I) ((core-get B-ptr) 'set-A-num!
286             (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'A-num))) name
287            ((core-get B-ptr) 'set-B-num!
288             (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'B-num)) name))
289     ((X) ((core-get B-ptr) 'set-A-num!
290           (f ((core-get B-ptr) 'A-num) ((core-get A-ptr) 'B-num)) name)
291      ((core-get B-ptr) 'set-B-num!
292       (f ((core-get B-ptr) 'B-num) ((core-get A-ptr) 'A-num)) name))))
293
294 (define (eval-operand mode num ptr)
295   (addr+ ptr
296          (case mode
297            ((immediate) 0)
298            ((direct) num)
299            ((indirect-A) (addr+ num ((core-get (addr+ ptr num)) 'A-num)))
300            ((indirect-B) (addr+ num ((core-get (addr+ ptr num)) 'B-num)))
301            ((pre-indirect-A)
302             (let ((aux-instr (core-get (addr+ ptr num))))
303               ((aux-instr set-A-num! (addr+ -1 (aux-instr 'A-num))))
304               (addr+ num (aux-instr 'A-num))))
305            ((pre-indirect-B)
306             (let ((aux-instr (core-get (addr+ ptr num))))
307               (aux-instr set-B-num!(addr+ -1 (aux-instr 'B-num)))
308               (addr+ num (aux-instr 'B-num))))
309            ((post-indirect-A)
310             (let* ((aux-instr (core-get (addr+ ptr num)))
311                    (old-A-num (aux-instr 'A-num)))
312               (aux-instr set-A-num! (addr+ 1 (aux-instr 'A-num)))
313               (addr+ num old-A-num)))
314            ((post-indirect-B)
315             (let* ((aux-instr (core-get (addr+ ptr num)))
316                    (old-B-num (aux-instr 'B-num)))
317               (aux-instr set-B-num! (addr+ 1 (aux-instr 'B-num)))
318               (addr+ num old-B-num)))
319            (else
320             (error "Unrecognized mode" mode)))))
321
322 ;;; TEST CODE
323
324 (define addressing-test
325   (make-prog 'at (list
326                    (make-instr 'DAT 'F 'immediate 42 'immediate 53 'at)
327                    (make-instr 'DAT 'F 'immediate 123 'immediate 256 'at)
328                    (make-instr 'MOV 'A 'indirect-B 4 'direct 7 'at)
329                    (make-instr 'NOP 'I 'immediate 0 'immediate 0 'at)
330                    (make-instr 'NOP 'I 'immediate 0 'immediate 0 'at)
331                    (make-instr 'NOP 'I 'immediate 0 'immediate 0 'at)
332                    (make-instr 'DAT 'F 'immediate -5 'immediate -6 'at)) 2))
333
334 (define imp
335   (make-prog 'imp (list (make-instr 'MOV 'I 'direct 0 'direct 1 'imp)) 0))
336
337 (define dwarf
338   (make-prog 'dwarf (list
339                      (make-instr 'DAT 'F 'immediate 0 'immediate -1 'dwarf)
340                      (make-instr 'ADD 'AB 'immediate 5 'direct -1 'dwarf)
341                      (make-instr 'MOV 'I 'direct -2 'indirect-B -2 'dwarf)
342                      (make-instr 'JMP 'I 'immediate -2 'immediate 0 'dwarf)) 1))
343
344 (initialize-core)
345 (define players (install-progs (list dwarf imp)))
346
347 (dump-core)