@gba-kit/arm-emulator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/arm-cpu.d.ts +141 -0
- package/dist/arm-cpu.d.ts.map +1 -0
- package/dist/arm-cpu.js +1875 -0
- package/dist/arm-cpu.js.map +1 -0
- package/dist/cpu-snapshot.d.ts +18 -0
- package/dist/cpu-snapshot.d.ts.map +1 -0
- package/dist/cpu-snapshot.js +2 -0
- package/dist/cpu-snapshot.js.map +1 -0
- package/dist/disassembler.d.ts +17 -0
- package/dist/disassembler.d.ts.map +1 -0
- package/dist/disassembler.js +617 -0
- package/dist/disassembler.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/memory.d.ts +42 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +218 -0
- package/dist/memory.js.map +1 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +51 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +126 -0
- package/dist/utils.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAUxC,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/memory.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARM7TDMI Thumb Emulator - Memory System
|
|
3
|
+
*
|
|
4
|
+
* Implements the GBA memory map with region dispatch, dirty tracking,
|
|
5
|
+
* and MMIO write recording. Uses ArrayBuffer + DataView for each region.
|
|
6
|
+
*/
|
|
7
|
+
import type { MemoryBus, MemoryWrite } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* GBA Memory Map implementation.
|
|
10
|
+
*
|
|
11
|
+
* Each region is backed by an ArrayBuffer. Reads and writes are dispatched
|
|
12
|
+
* by the top byte of the address. All writes are logged for comparison.
|
|
13
|
+
* MMIO writes are recorded separately (order-sensitive).
|
|
14
|
+
*/
|
|
15
|
+
export declare class GbaMemory implements MemoryBus {
|
|
16
|
+
#private;
|
|
17
|
+
readonly ewram: ArrayBuffer;
|
|
18
|
+
readonly iwram: ArrayBuffer;
|
|
19
|
+
readonly mmio: ArrayBuffer;
|
|
20
|
+
readonly palette: ArrayBuffer;
|
|
21
|
+
readonly vram: ArrayBuffer;
|
|
22
|
+
readonly oam: ArrayBuffer;
|
|
23
|
+
readonly rom: ArrayBuffer;
|
|
24
|
+
constructor();
|
|
25
|
+
read8(address: number): number;
|
|
26
|
+
read16(address: number): number;
|
|
27
|
+
read32(address: number): number;
|
|
28
|
+
write8(address: number, value: number): void;
|
|
29
|
+
write16(address: number, value: number): void;
|
|
30
|
+
write32(address: number, value: number): void;
|
|
31
|
+
/** Load a block of bytes into memory at a given address */
|
|
32
|
+
loadBytes(baseAddress: number, data: Uint8Array): void;
|
|
33
|
+
/** Get all memory writes since last reset */
|
|
34
|
+
getWriteLog(): MemoryWrite[];
|
|
35
|
+
/** Get MMIO writes only (order-sensitive) */
|
|
36
|
+
getMmioWriteLog(): MemoryWrite[];
|
|
37
|
+
/** Reset write logs (but keep memory contents) */
|
|
38
|
+
resetWriteLog(): void;
|
|
39
|
+
/** Zero all writable memory regions and reset logs */
|
|
40
|
+
reset(): void;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAazD;;;;;;GAMG;AACH,qBAAa,SAAU,YAAW,SAAS;;IAEzC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;;IA+D1B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAY9B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAmB/B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAoB/B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAc5C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAe7C,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAwB7C,2DAA2D;IAC3D,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI;IAYtD,6CAA6C;IAC7C,WAAW,IAAI,WAAW,EAAE;IAI5B,6CAA6C;IAC7C,eAAe,IAAI,WAAW,EAAE;IAIhC,kDAAkD;IAClD,aAAa,IAAI,IAAI;IAKrB,sDAAsD;IACtD,KAAK,IAAI,IAAI;CAWd"}
|
package/dist/memory.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/** GBA memory region definitions */
|
|
2
|
+
const REGIONS = {
|
|
3
|
+
EWRAM: { base: 0x02000000, size: 0x40000 }, // 256KB
|
|
4
|
+
IWRAM: { base: 0x03000000, size: 0x8000 }, // 32KB
|
|
5
|
+
MMIO: { base: 0x04000000, size: 0x400 }, // 1KB
|
|
6
|
+
PALETTE: { base: 0x05000000, size: 0x400 }, // 1KB
|
|
7
|
+
VRAM: { base: 0x06000000, size: 0x18000 }, // 96KB
|
|
8
|
+
OAM: { base: 0x07000000, size: 0x400 }, // 1KB
|
|
9
|
+
ROM: { base: 0x08000000, size: 0x2000000 }, // 32MB
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* GBA Memory Map implementation.
|
|
13
|
+
*
|
|
14
|
+
* Each region is backed by an ArrayBuffer. Reads and writes are dispatched
|
|
15
|
+
* by the top byte of the address. All writes are logged for comparison.
|
|
16
|
+
* MMIO writes are recorded separately (order-sensitive).
|
|
17
|
+
*/
|
|
18
|
+
export class GbaMemory {
|
|
19
|
+
// Region backing stores
|
|
20
|
+
ewram;
|
|
21
|
+
iwram;
|
|
22
|
+
mmio;
|
|
23
|
+
palette;
|
|
24
|
+
vram;
|
|
25
|
+
oam;
|
|
26
|
+
rom;
|
|
27
|
+
// DataViews for typed access
|
|
28
|
+
#ewramView;
|
|
29
|
+
#iwramView;
|
|
30
|
+
#mmioView;
|
|
31
|
+
#paletteView;
|
|
32
|
+
#vramView;
|
|
33
|
+
#oamView;
|
|
34
|
+
#romView;
|
|
35
|
+
// Write tracking
|
|
36
|
+
#writeLog = [];
|
|
37
|
+
#mmioWriteLog = [];
|
|
38
|
+
constructor() {
|
|
39
|
+
this.ewram = new ArrayBuffer(REGIONS.EWRAM.size);
|
|
40
|
+
this.iwram = new ArrayBuffer(REGIONS.IWRAM.size);
|
|
41
|
+
this.mmio = new ArrayBuffer(REGIONS.MMIO.size);
|
|
42
|
+
this.palette = new ArrayBuffer(REGIONS.PALETTE.size);
|
|
43
|
+
this.vram = new ArrayBuffer(REGIONS.VRAM.size);
|
|
44
|
+
this.oam = new ArrayBuffer(REGIONS.OAM.size);
|
|
45
|
+
this.rom = new ArrayBuffer(REGIONS.ROM.size);
|
|
46
|
+
this.#ewramView = new DataView(this.ewram);
|
|
47
|
+
this.#iwramView = new DataView(this.iwram);
|
|
48
|
+
this.#mmioView = new DataView(this.mmio);
|
|
49
|
+
this.#paletteView = new DataView(this.palette);
|
|
50
|
+
this.#vramView = new DataView(this.vram);
|
|
51
|
+
this.#oamView = new DataView(this.oam);
|
|
52
|
+
this.#romView = new DataView(this.rom);
|
|
53
|
+
}
|
|
54
|
+
/** Resolve an address to a [DataView, offset] pair */
|
|
55
|
+
#resolve(address) {
|
|
56
|
+
const region = (address >>> 24) & 0xff;
|
|
57
|
+
switch (region) {
|
|
58
|
+
case 0x02:
|
|
59
|
+
return [this.#ewramView, (address & 0x3ffff) % REGIONS.EWRAM.size];
|
|
60
|
+
case 0x03:
|
|
61
|
+
return [this.#iwramView, (address & 0x7fff) % REGIONS.IWRAM.size];
|
|
62
|
+
case 0x04:
|
|
63
|
+
return [this.#mmioView, address & 0x3ff];
|
|
64
|
+
case 0x05:
|
|
65
|
+
return [this.#paletteView, address & 0x3ff];
|
|
66
|
+
case 0x06: {
|
|
67
|
+
let offset = address & 0x1ffff;
|
|
68
|
+
// VRAM mirror: 0x10000-0x17FFF mirrors to 0x10000-0x17FFF, above mirrors back
|
|
69
|
+
if (offset >= REGIONS.VRAM.size) {
|
|
70
|
+
offset -= 0x8000;
|
|
71
|
+
}
|
|
72
|
+
return [this.#vramView, offset];
|
|
73
|
+
}
|
|
74
|
+
case 0x07:
|
|
75
|
+
return [this.#oamView, address & 0x3ff];
|
|
76
|
+
case 0x08:
|
|
77
|
+
case 0x09:
|
|
78
|
+
return [this.#romView, address - REGIONS.ROM.base];
|
|
79
|
+
default:
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
read8(address) {
|
|
84
|
+
const resolved = this.#resolve(address);
|
|
85
|
+
if (!resolved) {
|
|
86
|
+
return 0; // Open bus
|
|
87
|
+
}
|
|
88
|
+
const [view, offset] = resolved;
|
|
89
|
+
if (offset >= view.byteLength) {
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
return view.getUint8(offset);
|
|
93
|
+
}
|
|
94
|
+
read16(address) {
|
|
95
|
+
// ARM7TDMI: halfword reads are aligned, unaligned rotates
|
|
96
|
+
const aligned = address & ~1;
|
|
97
|
+
const resolved = this.#resolve(aligned);
|
|
98
|
+
if (!resolved) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
const [view, offset] = resolved;
|
|
102
|
+
if (offset + 1 >= view.byteLength) {
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
const value = view.getUint16(offset, true); // little-endian
|
|
106
|
+
// Rotate for unaligned access on ARMv4T
|
|
107
|
+
if (address & 1) {
|
|
108
|
+
return ((value >>> 8) | (value << 24)) >>> 0;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
read32(address) {
|
|
113
|
+
// ARM7TDMI: word reads are force-aligned, unaligned rotates
|
|
114
|
+
const aligned = address & ~3;
|
|
115
|
+
const resolved = this.#resolve(aligned);
|
|
116
|
+
if (!resolved) {
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
const [view, offset] = resolved;
|
|
120
|
+
if (offset + 3 >= view.byteLength) {
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
const value = view.getUint32(offset, true); // little-endian
|
|
124
|
+
// Rotate for unaligned access
|
|
125
|
+
const rot = (address & 3) * 8;
|
|
126
|
+
if (rot !== 0) {
|
|
127
|
+
return ((value >>> rot) | (value << (32 - rot))) >>> 0;
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
write8(address, value) {
|
|
132
|
+
const resolved = this.#resolve(address);
|
|
133
|
+
if (!resolved) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const [view, offset] = resolved;
|
|
137
|
+
if (offset >= view.byteLength) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
value = value & 0xff;
|
|
141
|
+
view.setUint8(offset, value);
|
|
142
|
+
this.#recordWrite(address, 1, value);
|
|
143
|
+
}
|
|
144
|
+
write16(address, value) {
|
|
145
|
+
const aligned = address & ~1;
|
|
146
|
+
const resolved = this.#resolve(aligned);
|
|
147
|
+
if (!resolved) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const [view, offset] = resolved;
|
|
151
|
+
if (offset + 1 >= view.byteLength) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
value = value & 0xffff;
|
|
155
|
+
view.setUint16(offset, value, true);
|
|
156
|
+
this.#recordWrite(aligned, 2, value);
|
|
157
|
+
}
|
|
158
|
+
write32(address, value) {
|
|
159
|
+
const aligned = address & ~3;
|
|
160
|
+
const resolved = this.#resolve(aligned);
|
|
161
|
+
if (!resolved) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const [view, offset] = resolved;
|
|
165
|
+
if (offset + 3 >= view.byteLength) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
value = value >>> 0;
|
|
169
|
+
view.setUint32(offset, value, true);
|
|
170
|
+
this.#recordWrite(aligned, 4, value);
|
|
171
|
+
}
|
|
172
|
+
#recordWrite(address, size, value) {
|
|
173
|
+
const entry = { address, size, value };
|
|
174
|
+
this.#writeLog.push(entry);
|
|
175
|
+
// Also record MMIO writes separately (order-sensitive)
|
|
176
|
+
if (((address >>> 24) & 0xff) === 0x04) {
|
|
177
|
+
this.#mmioWriteLog.push(entry);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/** Load a block of bytes into memory at a given address */
|
|
181
|
+
loadBytes(baseAddress, data) {
|
|
182
|
+
for (let i = 0; i < data.length; i++) {
|
|
183
|
+
const resolved = this.#resolve(baseAddress + i);
|
|
184
|
+
if (resolved) {
|
|
185
|
+
const [view, offset] = resolved;
|
|
186
|
+
if (offset < view.byteLength) {
|
|
187
|
+
view.setUint8(offset, data[i]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** Get all memory writes since last reset */
|
|
193
|
+
getWriteLog() {
|
|
194
|
+
return this.#writeLog;
|
|
195
|
+
}
|
|
196
|
+
/** Get MMIO writes only (order-sensitive) */
|
|
197
|
+
getMmioWriteLog() {
|
|
198
|
+
return this.#mmioWriteLog;
|
|
199
|
+
}
|
|
200
|
+
/** Reset write logs (but keep memory contents) */
|
|
201
|
+
resetWriteLog() {
|
|
202
|
+
this.#writeLog = [];
|
|
203
|
+
this.#mmioWriteLog = [];
|
|
204
|
+
}
|
|
205
|
+
/** Zero all writable memory regions and reset logs */
|
|
206
|
+
reset() {
|
|
207
|
+
new Uint8Array(this.ewram).fill(0);
|
|
208
|
+
new Uint8Array(this.iwram).fill(0);
|
|
209
|
+
new Uint8Array(this.mmio).fill(0);
|
|
210
|
+
new Uint8Array(this.palette).fill(0);
|
|
211
|
+
new Uint8Array(this.vram).fill(0);
|
|
212
|
+
new Uint8Array(this.oam).fill(0);
|
|
213
|
+
// ROM is not zeroed — it's loaded once
|
|
214
|
+
this.#writeLog = [];
|
|
215
|
+
this.#mmioWriteLog = [];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARM7TDMI Emulator - Core Types
|
|
3
|
+
*/
|
|
4
|
+
/** ARM register indices */
|
|
5
|
+
export declare const SP = 13;
|
|
6
|
+
export declare const LR = 14;
|
|
7
|
+
export declare const PC = 15;
|
|
8
|
+
/** Sentinel address — execution halts when PC reaches this */
|
|
9
|
+
export declare const SENTINEL_ADDR = 3735928558;
|
|
10
|
+
/**
|
|
11
|
+
* Abstract memory bus that the CPU reads/writes through.
|
|
12
|
+
*
|
|
13
|
+
* The full GBA emulator injects GbaSystemBus (dispatches to PPU, APU, etc.).
|
|
14
|
+
*/
|
|
15
|
+
export interface MemoryBus {
|
|
16
|
+
read8(address: number): number;
|
|
17
|
+
read16(address: number): number;
|
|
18
|
+
read32(address: number): number;
|
|
19
|
+
write8(address: number, value: number): void;
|
|
20
|
+
write16(address: number, value: number): void;
|
|
21
|
+
write32(address: number, value: number): void;
|
|
22
|
+
}
|
|
23
|
+
/** Action the debugger can take after a hook fires */
|
|
24
|
+
export type DebugAction = 'continue' | 'break';
|
|
25
|
+
/**
|
|
26
|
+
* Optional hooks for debugging instrumentation.
|
|
27
|
+
*
|
|
28
|
+
* When attached to a CPU, these are called during execution.
|
|
29
|
+
* When absent, no overhead — the CPU takes a fast path.
|
|
30
|
+
*/
|
|
31
|
+
export interface DebugHooks {
|
|
32
|
+
/** Called before executing an instruction. Return 'break' to pause. */
|
|
33
|
+
onInstructionPre?(address: number, instruction: number): DebugAction;
|
|
34
|
+
/** Called after executing an instruction. */
|
|
35
|
+
onInstructionPost?(address: number, instruction: number): void;
|
|
36
|
+
/** Called on every memory read (for memory watchpoints). */
|
|
37
|
+
onMemoryRead?(address: number, size: 1 | 2 | 4, value: number): void;
|
|
38
|
+
/** Called on every memory write (for memory watchpoints). */
|
|
39
|
+
onMemoryWrite?(address: number, size: 1 | 2 | 4, value: number): void;
|
|
40
|
+
}
|
|
41
|
+
/** CPSR condition flags */
|
|
42
|
+
export interface CpsrFlags {
|
|
43
|
+
n: boolean;
|
|
44
|
+
z: boolean;
|
|
45
|
+
c: boolean;
|
|
46
|
+
v: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Result of an arithmetic/logic operation with flags */
|
|
49
|
+
export interface AluResult {
|
|
50
|
+
value: number;
|
|
51
|
+
n: boolean;
|
|
52
|
+
z: boolean;
|
|
53
|
+
c: boolean;
|
|
54
|
+
v: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** A recorded memory write during execution */
|
|
57
|
+
export interface MemoryWrite {
|
|
58
|
+
address: number;
|
|
59
|
+
size: 1 | 2 | 4;
|
|
60
|
+
value: number;
|
|
61
|
+
}
|
|
62
|
+
/** A recorded external function call (bl to an unresolved symbol) */
|
|
63
|
+
export interface ExternalCall {
|
|
64
|
+
/** Address of the bl instruction */
|
|
65
|
+
callSite: number;
|
|
66
|
+
/** Resolved target address (from relocation) */
|
|
67
|
+
targetAddress: number;
|
|
68
|
+
/** Symbol name from relocation table */
|
|
69
|
+
symbolName: string;
|
|
70
|
+
/** Argument registers at time of call */
|
|
71
|
+
r0: number;
|
|
72
|
+
r1: number;
|
|
73
|
+
r2: number;
|
|
74
|
+
r3: number;
|
|
75
|
+
}
|
|
76
|
+
/** Full execution trace from running a function */
|
|
77
|
+
export interface ExecutionResult {
|
|
78
|
+
/** Final register values (r0-r15) */
|
|
79
|
+
registers: Uint32Array;
|
|
80
|
+
/** Final CPSR flags */
|
|
81
|
+
cpsr: CpsrFlags;
|
|
82
|
+
/** All memory writes performed during execution */
|
|
83
|
+
memoryWrites: MemoryWrite[];
|
|
84
|
+
/** All external function calls made during execution */
|
|
85
|
+
externalCalls: ExternalCall[];
|
|
86
|
+
/** Number of instructions executed */
|
|
87
|
+
instructionsExecuted: number;
|
|
88
|
+
/** Whether execution completed normally (returned) vs hit instruction limit */
|
|
89
|
+
completed: boolean;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARM7TDMI Emulator - Core Types
|
|
3
|
+
*/
|
|
4
|
+
/** ARM register indices */
|
|
5
|
+
export const SP = 13;
|
|
6
|
+
export const LR = 14;
|
|
7
|
+
export const PC = 15;
|
|
8
|
+
/** Sentinel address — execution halts when PC reaches this */
|
|
9
|
+
export const SENTINEL_ADDR = 0xdeadbeee;
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,2BAA2B;AAC3B,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;AACrB,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;AACrB,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;AAErB,8DAA8D;AAC9D,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARM7TDMI Thumb Emulator - Bit manipulation and ALU utilities
|
|
3
|
+
*
|
|
4
|
+
* Key TypeScript/JS numeric patterns for 32-bit ARM emulation:
|
|
5
|
+
* - `| 0` truncates to signed 32-bit (like C's int32_t)
|
|
6
|
+
* - `>>> 0` reinterprets as unsigned 32-bit
|
|
7
|
+
* - `Math.imul(a, b)` for correct 32-bit multiply (regular * loses precision above 2^53)
|
|
8
|
+
* - `>>` is arithmetic right shift (sign-extending), `>>>` is logical right shift
|
|
9
|
+
*/
|
|
10
|
+
import type { AluResult } from './types.js';
|
|
11
|
+
/** Extract bits [hi:lo] from a value (inclusive) */
|
|
12
|
+
export declare function bits(value: number, hi: number, lo: number): number;
|
|
13
|
+
/** Extract a single bit */
|
|
14
|
+
export declare function bit(value: number, pos: number): number;
|
|
15
|
+
/** Sign-extend a value from `bitWidth` bits to 32 bits */
|
|
16
|
+
export declare function signExtend(value: number, bitWidth: number): number;
|
|
17
|
+
/** Check if bit 31 is set (negative in signed interpretation) */
|
|
18
|
+
export declare function isNegative(value: number): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Add two 32-bit values with full flag computation.
|
|
21
|
+
* Returns the result and NZCV flags.
|
|
22
|
+
*/
|
|
23
|
+
export declare function addWithFlags(a: number, b: number, carryIn?: number): AluResult;
|
|
24
|
+
/**
|
|
25
|
+
* Subtract two 32-bit values with full flag computation.
|
|
26
|
+
* Computes a - b (with optional borrow).
|
|
27
|
+
*
|
|
28
|
+
* ARM subtraction uses inverted carry: C=1 means no borrow.
|
|
29
|
+
* SUB is implemented as ADD(a, ~b, 1).
|
|
30
|
+
*/
|
|
31
|
+
export declare function subWithFlags(a: number, b: number, carryIn?: number): AluResult;
|
|
32
|
+
/**
|
|
33
|
+
* Logical shift left. Returns [result, carryOut].
|
|
34
|
+
* If amount is 0, carry is unchanged (returns oldCarry).
|
|
35
|
+
*/
|
|
36
|
+
export declare function lsl(value: number, amount: number, oldCarry: boolean): [number, boolean];
|
|
37
|
+
/**
|
|
38
|
+
* Logical shift right. Returns [result, carryOut].
|
|
39
|
+
* amount=0 encodes LSR #32 in Thumb Format 1.
|
|
40
|
+
*/
|
|
41
|
+
export declare function lsr(value: number, amount: number, oldCarry: boolean, immZeroMeans32?: boolean): [number, boolean];
|
|
42
|
+
/**
|
|
43
|
+
* Arithmetic shift right. Returns [result, carryOut].
|
|
44
|
+
* amount=0 encodes ASR #32 in Thumb Format 1.
|
|
45
|
+
*/
|
|
46
|
+
export declare function asr(value: number, amount: number, oldCarry: boolean, immZeroMeans32?: boolean): [number, boolean];
|
|
47
|
+
/**
|
|
48
|
+
* Rotate right. Returns [result, carryOut].
|
|
49
|
+
*/
|
|
50
|
+
export declare function ror(value: number, amount: number, oldCarry: boolean): [number, boolean];
|
|
51
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,oDAAoD;AACpD,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,2BAA2B;AAC3B,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,MAAU,GAAG,SAAS,CAwBjF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,MAAU,GAAG,SAAS,CAMjF;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CASvF;AAED;;;GAGG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EACjB,cAAc,GAAE,OAAe,GAC9B,CAAC,MAAM,EAAE,OAAO,CAAC,CAanB;AAED;;;GAGG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EACjB,cAAc,GAAE,OAAe,GAC9B,CAAC,MAAM,EAAE,OAAO,CAAC,CAenB;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAYvF"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** Extract bits [hi:lo] from a value (inclusive) */
|
|
2
|
+
export function bits(value, hi, lo) {
|
|
3
|
+
return (value >>> lo) & ((1 << (hi - lo + 1)) - 1);
|
|
4
|
+
}
|
|
5
|
+
/** Extract a single bit */
|
|
6
|
+
export function bit(value, pos) {
|
|
7
|
+
return (value >>> pos) & 1;
|
|
8
|
+
}
|
|
9
|
+
/** Sign-extend a value from `bitWidth` bits to 32 bits */
|
|
10
|
+
export function signExtend(value, bitWidth) {
|
|
11
|
+
const shift = 32 - bitWidth;
|
|
12
|
+
return (value << shift) >> shift;
|
|
13
|
+
}
|
|
14
|
+
/** Check if bit 31 is set (negative in signed interpretation) */
|
|
15
|
+
export function isNegative(value) {
|
|
16
|
+
return (value & 0x80000000) !== 0;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Add two 32-bit values with full flag computation.
|
|
20
|
+
* Returns the result and NZCV flags.
|
|
21
|
+
*/
|
|
22
|
+
export function addWithFlags(a, b, carryIn = 0) {
|
|
23
|
+
// Use unsigned interpretation for carry detection
|
|
24
|
+
const ua = a >>> 0;
|
|
25
|
+
const ub = b >>> 0;
|
|
26
|
+
const result = (ua + ub + carryIn) | 0;
|
|
27
|
+
const uresult = result >>> 0;
|
|
28
|
+
// Carry: unsigned overflow
|
|
29
|
+
// We need to detect if ua + ub + carryIn > 0xFFFFFFFF
|
|
30
|
+
// Since JS numbers have enough precision for this sum:
|
|
31
|
+
const fullSum = ua + ub + carryIn;
|
|
32
|
+
const c = fullSum > 0xffffffff;
|
|
33
|
+
// Overflow: signed overflow
|
|
34
|
+
// Positive + Positive = Negative, or Negative + Negative = Positive
|
|
35
|
+
const v = ((a ^ result) & (b ^ result) & 0x80000000) !== 0;
|
|
36
|
+
return {
|
|
37
|
+
value: result,
|
|
38
|
+
n: (uresult & 0x80000000) !== 0,
|
|
39
|
+
z: uresult === 0,
|
|
40
|
+
c,
|
|
41
|
+
v,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Subtract two 32-bit values with full flag computation.
|
|
46
|
+
* Computes a - b (with optional borrow).
|
|
47
|
+
*
|
|
48
|
+
* ARM subtraction uses inverted carry: C=1 means no borrow.
|
|
49
|
+
* SUB is implemented as ADD(a, ~b, 1).
|
|
50
|
+
*/
|
|
51
|
+
export function subWithFlags(a, b, carryIn = 1) {
|
|
52
|
+
// SUB a, b = ADD a, NOT(b), carry
|
|
53
|
+
// For plain SUB: carry = 1 (no borrow)
|
|
54
|
+
// For SBC: carry = CPSR.C (borrow flag)
|
|
55
|
+
const notB = ~b;
|
|
56
|
+
return addWithFlags(a, notB, carryIn);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Logical shift left. Returns [result, carryOut].
|
|
60
|
+
* If amount is 0, carry is unchanged (returns oldCarry).
|
|
61
|
+
*/
|
|
62
|
+
export function lsl(value, amount, oldCarry) {
|
|
63
|
+
if (amount === 0) {
|
|
64
|
+
return [value, oldCarry];
|
|
65
|
+
}
|
|
66
|
+
if (amount >= 32) {
|
|
67
|
+
return [0, amount === 32 ? (value & 1) !== 0 : false];
|
|
68
|
+
}
|
|
69
|
+
const carry = ((value >>> (32 - amount)) & 1) !== 0;
|
|
70
|
+
return [(value << amount) | 0, carry];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Logical shift right. Returns [result, carryOut].
|
|
74
|
+
* amount=0 encodes LSR #32 in Thumb Format 1.
|
|
75
|
+
*/
|
|
76
|
+
export function lsr(value, amount, oldCarry, immZeroMeans32 = false) {
|
|
77
|
+
if (amount === 0) {
|
|
78
|
+
if (immZeroMeans32) {
|
|
79
|
+
// LSR #0 in format 1 means LSR #32
|
|
80
|
+
return [0, isNegative(value)];
|
|
81
|
+
}
|
|
82
|
+
return [value, oldCarry];
|
|
83
|
+
}
|
|
84
|
+
if (amount >= 32) {
|
|
85
|
+
return [0, amount === 32 ? isNegative(value) : false];
|
|
86
|
+
}
|
|
87
|
+
const carry = ((value >>> (amount - 1)) & 1) !== 0;
|
|
88
|
+
return [value >>> amount, carry];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Arithmetic shift right. Returns [result, carryOut].
|
|
92
|
+
* amount=0 encodes ASR #32 in Thumb Format 1.
|
|
93
|
+
*/
|
|
94
|
+
export function asr(value, amount, oldCarry, immZeroMeans32 = false) {
|
|
95
|
+
if (amount === 0) {
|
|
96
|
+
if (immZeroMeans32) {
|
|
97
|
+
// ASR #0 in format 1 means ASR #32
|
|
98
|
+
const carry = isNegative(value);
|
|
99
|
+
return [carry ? 0xffffffff : 0, carry];
|
|
100
|
+
}
|
|
101
|
+
return [value, oldCarry];
|
|
102
|
+
}
|
|
103
|
+
if (amount >= 32) {
|
|
104
|
+
const carry = isNegative(value);
|
|
105
|
+
return [carry ? 0xffffffff : 0, carry];
|
|
106
|
+
}
|
|
107
|
+
const carry = ((value >> (amount - 1)) & 1) !== 0;
|
|
108
|
+
return [value >> amount, carry];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Rotate right. Returns [result, carryOut].
|
|
112
|
+
*/
|
|
113
|
+
export function ror(value, amount, oldCarry) {
|
|
114
|
+
if (amount === 0) {
|
|
115
|
+
return [value, oldCarry];
|
|
116
|
+
}
|
|
117
|
+
const effectiveAmount = amount & 31;
|
|
118
|
+
if (effectiveAmount === 0) {
|
|
119
|
+
// Rotate by 32 = no-op, but carry = bit 31
|
|
120
|
+
return [value, isNegative(value)];
|
|
121
|
+
}
|
|
122
|
+
const result = (value >>> effectiveAmount) | (value << (32 - effectiveAmount)) | 0;
|
|
123
|
+
const carry = ((result >>> 31) & 1) !== 0;
|
|
124
|
+
return [result, carry];
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gba-kit/arm-emulator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ARM7TDMI CPU emulator supporting Thumb and ARM instruction sets",
|
|
5
|
+
"author": "macabeus",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./arm-cpu": {
|
|
15
|
+
"types": "./dist/arm-cpu.d.ts",
|
|
16
|
+
"import": "./dist/arm-cpu.js"
|
|
17
|
+
},
|
|
18
|
+
"./disassembler": {
|
|
19
|
+
"types": "./dist/disassembler.d.ts",
|
|
20
|
+
"import": "./dist/disassembler.js"
|
|
21
|
+
},
|
|
22
|
+
"./cpu-snapshot": {
|
|
23
|
+
"types": "./dist/cpu-snapshot.d.ts",
|
|
24
|
+
"import": "./dist/cpu-snapshot.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/macabeus/gba-kit",
|
|
31
|
+
"directory": "packages/arm-emulator"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/macabeus/gba-kit",
|
|
34
|
+
"bugs": "https://github.com/macabeus/gba-kit/issues",
|
|
35
|
+
"keywords": [
|
|
36
|
+
"gba",
|
|
37
|
+
"gameboy-advance",
|
|
38
|
+
"emulator",
|
|
39
|
+
"arm7tdmi",
|
|
40
|
+
"typescript"
|
|
41
|
+
],
|
|
42
|
+
"files": [
|
|
43
|
+
"dist"
|
|
44
|
+
],
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^25.5.0",
|
|
47
|
+
"vitest": "^4.1.1",
|
|
48
|
+
"typescript": "^6.0.2"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"check-types": "tsc --noEmit",
|
|
54
|
+
"lint": "eslint src/"
|
|
55
|
+
}
|
|
56
|
+
}
|