-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCPU.ts
124 lines (99 loc) · 2.92 KB
/
CPU.ts
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
124
import {Registers} from "./Registers";
import {RAM} from "./Ram";
import {Logger} from "./Logger";
import {InstructionSet} from "./InstructionSet";
import {Flags} from "./Flags";
import {Graphics} from "./Graphics";
import {EventEmitter} from "events";
import Timer = NodeJS.Timer;
export class CPU extends EventEmitter {
private _registers:Registers;
private _flags:Flags;
private _ram:RAM;
private _graphics:Graphics;
private _stepInterval = 0;
private _stepsPerInterval = 10;
private _instructionSet:InstructionSet;
private _timeoutHandle:Timer;
constructor(ram:RAM, graphics:Graphics) {
super();
this._registers = new Registers();
this._ram = ram;
this._graphics = graphics;
this._instructionSet = new InstructionSet();
this._flags = new Flags();
this._timeoutHandle = null;
}
public step() {
this.executeInstruction(this._ram.getCellValue(this._registers.IP.value));
if (this._flags.jumped) {
this._flags.jumped = false;
} else if (!this._flags.halt) {
this._registers.IP.incrementBy(4);
}
if (this._flags.draw) {
this._flags.draw = false;
this.emit("draw");
}
this.emit("step");
}
public runSynchronouslyUntilHalted():void {
while (!this._flags.halt) {
this.step();
}
}
public runSynchronouslyFor(cycles:number):void {
for (let i = 0; i < cycles; i++) {
if (this._flags.halt) {
break;
}
this.step();
}
}
public run() {
if (this._timeoutHandle === null) {
this.emit("run");
}
this.runSynchronouslyFor(this._stepsPerInterval);
if (this._flags.halt) {
Logger.log(`halt instruction encountered`);
return;
}
this._timeoutHandle = setTimeout(() => {
this.run();
}, this._stepInterval);
}
public reset():void {
this._registers.zeroOut();
this._flags.zeroOut();
this.stop();
}
public stop():void {
clearTimeout(this._timeoutHandle);
this._timeoutHandle = null;
this.emit("stop");
}
public get registers():Registers {
return this._registers;
}
public get flags():Flags {
return this._flags;
}
public get ram():RAM {
return this._ram;
}
public get instructionSet():InstructionSet {
return this._instructionSet;
}
public get graphics():Graphics {
return this._graphics;
}
public get isRunning():boolean {
return this._timeoutHandle !== null;
}
private executeInstruction(opcode:number) {
Logger.log(`executing instruction ${opcode}`);
const instruction = this._instructionSet.findInstructionByOpcode(opcode);
instruction.operation(this);
}
}