summaryrefslogtreecommitdiff
path: root/src/vm/vm.js
blob: eea949a0336f8f3b38e3ef23915f954dc5ce179b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
'use strict';


const { Core } = require('./core.js');
const {
	DAT,
	MOV, ADD, SUB, CMP, SLT,
	JMP, JMZ, JMN, DJN, SPL,
} = require('./instruction.js');


class Warrior {
	constructor(start) {
		this.queue = [start];
	}

	isDead() { return (this.queue.length === 0); }

	append(heads) {
		this.queue.push(...heads);
	}

	next() {
		const pc = this.queue[0];
		this.queue = this.queue.slice(1);
		return pc;
	}

	peek() {
		const pc = this.queue[0];
		return pc;
	}
};


class RedcodeVm {
	constructor(coresize, warriors) {
		this.core = new Core(coresize);
		this.warriors = this.core.initialize(warriors)
			.map(start => new Warrior(start));
	}

	step() {
		let running = 0;

		for (let i=0; i<this.warriors.length; i++) {
			const warrior = this.warriors[i];
			if (warrior.isDead()) {
				continue;
			} else {
				running += 1;
			}

			const pc = warrior.next();
			const ins = this.core.data[pc];
			let next;
			console.log(i, this.core.pretty(ins));
			switch(ins.opcode) {
				case 'DAT':
					next = DAT(this.core, pc, ins);
					break;
				case 'MOV':
					next = MOV(this.core, pc, ins);
					break;
				case 'ADD':
					next = ADD(this.core, pc, ins);
					break;
				case 'SUB':
					next = SUB(this.core, pc, ins);
					break;
				case 'CMP':
					next = CMP(this.core, pc, ins);
					break;
				case 'SLT':
					next = SLT(this.core, pc, ins);
					break;
				case 'JMP':
					next = JMP(this.core, pc, ins);
					break;
				case 'JMZ':
					next = JMZ(this.core, pc, ins);
					break;
				case 'JMN':
					next = JMN(this.core, pc, ins);
					break;
				case 'DJN':
					next = DJN(this.core, pc, ins);
					break;
				case 'SPL':
					next = SPL(this.core, pc, ins);
					break;
				default:
					throw `core corruption: bad instruction ${ins}`
			}

			warrior.append(next);
		}
		
		return running;
	}

	warriorsAlive() {
		return this.warriors.reduce(
			(count, warrior) => warrior.isDead() ? count : count+1,
			0
		);
	}

	run(maxSteps) {
		let steps = 0;
		while(this.warriorsAlive() > 1 && steps < maxSteps) {
			this.step();
			steps += 1;
		}

		return this.warriors.map(
			(w, i) => ({ warrior: i, alive: !w.isDead() })
		);
	}
};


exports.RedcodeVm = RedcodeVm;