Newer
Older
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
export const FREQUENCY_DELTA = 100000;
export type Callback<T> = (owner: T, params?: any) => void;
/**
* Abstract class that implements the basic functionality
* part of the definition of the Observer pattern.
*
* @see {@link https://en.wikipedia.org/wiki/Observer_pattern}
*/
export class Observable {
private events: Record<string, [Callback<this>]> = {};
bind(event: string, callback: Callback<this>) {
const callbacks = this.events[event] ?? [];
if (callbacks.includes(callback)) return;
callbacks.push(callback);
this.events[event] = callbacks;
}
unbind(event: string, callback: Callback<this>) {
const callbacks = this.events[event] ?? [];
if (!callbacks.includes(callback)) return;
const index = callbacks.indexOf(callback);
callbacks.splice(index, 1);
this.events[event] = callbacks;
}
trigger(event: string, params?: any) {
const callbacks = this.events[event] ?? [];
callbacks.forEach((c) => c(this, params));
}
}
export type RomInfo = {
name?: string;
data?: Uint8Array;
size?: number;
extra?: Record<string, string | undefined>;
};
export type BenchmarkResult = {
delta: number;
count: number;
cycles: number;
frequency_mhz: number;
};
export enum Feature {
Debug = 1,
Palettes,
Benchmark,
Keyboard,
KeyboardChip8,
KeyboardGB
}
/**
* Enumeration that describes the multiple pixel
* formats and the associated size in bytes.
*/
export enum PixelFormat {
RGB = 3,
RGBA = 4
}
export interface ObservableI {
bind(event: string, callback: Callback<this>): void;
unbind(event: string, callback: Callback<this>): void;
trigger(event: string): void;
}
/**
* Top level interface that declares the main abstract
* interface of an emulator structured entity.
* Should allow typical hardware operations to be performed.
*/
export interface Emulator extends ObservableI {
/**
* The descriptive name of the emulator.
*/
get name(): string;
/**
* The name of the the hardware that is being emulated
* by the emulator (eg: Super Nintendo).
*/
get device(): string;
/**
* A URL to a website that describes the device that is
* being emulated by the emulator (eg: Wikipedia link).
*/
get deviceUrl(): string | undefined;
/**
* A semantic version string for the current version
* of the emulator.
*
* @see {@link https://semver.org}
*/
get version(): string;
/**
* The URL to the page describing the current version
* of the emulator.
*/
get versionUrl(): string | undefined;
get repository(): string | undefined;
get repositoryUrl(): string | undefined;
/**
* The features available and compatible with the emulator,
* these values will influence the associated GUIs.
*/
get features(): Feature[];
/**
* The complete set of engine names that can be used
* in the re-boot operation.
*/
get engines(): string[];
/**
* The name of the current execution engine being used
* by the emulator.
*/
get engine(): string | null;
/**
* The complete set of file extensions that this emulator
* supports.
*/
get romExts(): string[];
/**
* The pixel format of the emulator's display
* image buffer (eg: RGB).
*/
get pixelFormat(): PixelFormat;
/**
* Gets the complete image buffer as a sequence of
* bytes that respects the current pixel format from
* `getPixelFormat()`. This method returns an in memory
* pointer to the heap and not a copy.
*/
get imageBuffer(): Uint8Array;
/**
* Gets information about the ROM that is currently
* loaded in the emulator, using a structure containing
* the information about the ROM that is currently
* loaded in the emulator.
*/
get romInfo(): RomInfo;
/**
* The current CPU frequency (logic) of the emulator,
* should impact other elements of the emulator.
*/
get frequency(): number;
set frequency(value: number);
/**
* The recommended frequency delta in hertz for scale up
* and scale down operations in the CPU frequency.
*/
get frequencyDelta(): number | null;
/**
* The current logic framerate of the running emulator.
*/
get framerate(): number;
/**
* A dictionary that contains the register names associated
* with their value either as strings or numbers.
*/
get registers(): Record<string, string | number>;
/**
* Obtains the pixel buffer for the VRAM tile at the given
* index.
*
* @param index The index of the tile to obtain pixel buffer.
* @returns The pixel buffer of the tile at the given index.
*/
getTile(index: number): Uint8Array;
/**
* Boot (or reboots) the emulator according to the provided
* set of options.
*
* @param options The options that are going to be used for
* the booting operation of the emulator.
*/
boot(options: any): void;
/**
* Toggle the running state of the emulator between paused
* and running, prevents consumers from the need to access
* the current running state of the emulator to implement
* a logic toggle.
*/
toggleRunning(): void;
pause(): void;
resume(): void;
/**
* Resets the emulator machine to the start state and
* re-loads the ROM that is currently set in the emulator.
*/
reset(): void;
keyPress(key: string): void;
keyLift(key: string): void;
/**
* Changes the palette of the emulator to the "next" one.
*/
changePalette?: { (): void };
/**
* Runs a benchmark operation in the emulator, effectively
* measuring the performance of it.
*
* @param count The number of benchmark iterations to be
* run, increasing this value will make the benchmark take
* more time to be executed.
* @returns The result metrics from the benchmark run.
*/
benchmark?: { (count?: number): BenchmarkResult };
}
export class EmulatorBase extends Observable {
get deviceUrl(): string | undefined {
return undefined;
}
get versionUrl(): string | undefined {
return undefined;
}
get repository(): string | undefined {
return undefined;
}
get repositoryUrl(): string | undefined {
return undefined;
}
get features(): Feature[] {
return [];
}
get frequencyDelta(): number | null {
return FREQUENCY_DELTA;
}
}