Skip to content

Commit 46310f1

Browse files
authored
Fixed Save States, Added a new "Save State per Frame" test, and other various improvements (#332)
* Made a simple headless example with it working for playing a game * Added another comment * Can't get any visuals from the core for some reason... * Fixed save states for WasmBoy Sound should no longer break save states, and tobutobu can be save and loaded during the intro * Left a TODO for before we push up fixes for demo` * Updated AssemblyScript * Fixed up all the tests, and allowed passing timeout to runWasmExport * Fixed Out of Memory errors on build * Added terser for the debugger for OOM errors
1 parent 945f889 commit 46310f1

File tree

90 files changed

+3965363
-1444
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+3965363
-1444
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ script:
1111
- npm run demo:build:apps
1212
- npm run test:accuracy:nobuild
1313
- npm run test:integration:nobuild
14-
#TODO: - npm run wasmerboy:build
14+
- npm run test:core:nobuild
15+
#TODO: - npm run wasmerboy:build

core/graphics/graphics.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ import { resetTileCache } from './tiles';
99
import { initializeColors } from './colors';
1010
import { Cpu } from '../cpu/index';
1111
import { Config } from '../config';
12-
import { Memory, eightBitLoadFromGBMemory, eightBitStoreIntoGBMemory } from '../memory/index';
12+
import {
13+
Memory,
14+
eightBitLoadFromGBMemory,
15+
eightBitStoreIntoGBMemory,
16+
loadBooleanDirectlyFromWasmMemory,
17+
storeBooleanDirectlyToWasmMemory
18+
} from '../memory/index';
1319

1420
export class Graphics {
1521
// Current cycles
@@ -87,19 +93,40 @@ export class Graphics {
8793

8894
// Function to save the state of the class
8995
static saveState(): void {
96+
// Graphics
9097
store<i32>(getSaveStateMemoryOffset(0x00, Graphics.saveStateSlot), Graphics.scanlineCycleCounter);
91-
store<u8>(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot), <u8>Lcd.currentLcdMode);
92-
9398
eightBitStoreIntoGBMemory(Graphics.memoryLocationScanlineRegister, Graphics.scanlineRegister);
99+
100+
// LCD
101+
store<u8>(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot), <u8>Lcd.currentLcdMode);
102+
store<u8>(getSaveStateMemoryOffset(0x05, Graphics.saveStateSlot), <u8>Lcd.coincidenceCompare);
103+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x06, Graphics.saveStateSlot), Lcd.enabled);
104+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x07, Graphics.saveStateSlot), Lcd.windowTileMapDisplaySelect);
105+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x08, Graphics.saveStateSlot), Lcd.windowDisplayEnabled);
106+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x09, Graphics.saveStateSlot), Lcd.bgWindowTileDataSelect);
107+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0a, Graphics.saveStateSlot), Lcd.bgTileMapDisplaySelect);
108+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0b, Graphics.saveStateSlot), Lcd.tallSpriteSize);
109+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0c, Graphics.saveStateSlot), Lcd.spriteDisplayEnable);
110+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0d, Graphics.saveStateSlot), Lcd.bgDisplayEnabled);
94111
}
95112

96113
// Function to load the save state from memory
97114
static loadState(): void {
115+
// Graphics
98116
Graphics.scanlineCycleCounter = load<i32>(getSaveStateMemoryOffset(0x00, Graphics.saveStateSlot));
99-
Lcd.currentLcdMode = load<u8>(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot));
100-
101117
Graphics.scanlineRegister = eightBitLoadFromGBMemory(Graphics.memoryLocationScanlineRegister);
102-
Lcd.updateLcdControl(eightBitLoadFromGBMemory(Lcd.memoryLocationLcdControl));
118+
119+
// LCD
120+
Lcd.currentLcdMode = load<u8>(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot));
121+
Lcd.coincidenceCompare = load<u8>(getSaveStateMemoryOffset(0x05, Graphics.saveStateSlot));
122+
Lcd.enabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x06, Graphics.saveStateSlot));
123+
Lcd.windowTileMapDisplaySelect = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x07, Graphics.saveStateSlot));
124+
Lcd.windowDisplayEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x08, Graphics.saveStateSlot));
125+
Lcd.bgWindowTileDataSelect = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x09, Graphics.saveStateSlot));
126+
Lcd.bgTileMapDisplaySelect = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0a, Graphics.saveStateSlot));
127+
Lcd.tallSpriteSize = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0b, Graphics.saveStateSlot));
128+
Lcd.spriteDisplayEnable = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0c, Graphics.saveStateSlot));
129+
Lcd.bgDisplayEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0d, Graphics.saveStateSlot));
103130
}
104131
}
105132

core/interrupts/interrupts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export class Interrupts {
7373
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x01, Interrupts.saveStateSlot), Interrupts.masterInterruptSwitchDelay);
7474

7575
// Interrupts enabled and requested are stored in actual GB memory, thus, don't need to be saved
76+
// Other classes have special logic on write, but this just checks bits on bytes, so should be fine
7677
}
7778

7879
// Function to load the save state from memory

core/memory/memory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ export class Memory {
9191
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x08, Memory.saveStateSlot), Memory.isMBC2);
9292
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x09, Memory.saveStateSlot), Memory.isMBC3);
9393
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0a, Memory.saveStateSlot), Memory.isMBC5);
94+
95+
store<i32>(getSaveStateMemoryOffset(0x0b, Memory.saveStateSlot), Memory.DMACycles);
96+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0f, Memory.saveStateSlot), Memory.isHblankHdmaActive);
97+
store<i32>(getSaveStateMemoryOffset(0x10, Memory.saveStateSlot), Memory.hblankHdmaTransferLengthRemaining);
98+
store<i32>(getSaveStateMemoryOffset(0x14, Memory.saveStateSlot), Memory.hblankHdmaSource);
99+
store<i32>(getSaveStateMemoryOffset(0x18, Memory.saveStateSlot), Memory.hblankHdmaDestination);
94100
}
95101

96102
// Function to load the save state from memory
@@ -106,6 +112,12 @@ export class Memory {
106112
Memory.isMBC2 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x08, Memory.saveStateSlot));
107113
Memory.isMBC3 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x09, Memory.saveStateSlot));
108114
Memory.isMBC5 = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0a, Memory.saveStateSlot));
115+
116+
Memory.DMACycles = load<i32>(getSaveStateMemoryOffset(0x0b, Memory.saveStateSlot));
117+
Memory.isHblankHdmaActive = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0f, Memory.saveStateSlot));
118+
Memory.hblankHdmaTransferLengthRemaining = load<i32>(getSaveStateMemoryOffset(0x10, Memory.saveStateSlot));
119+
Memory.hblankHdmaSource = load<i32>(getSaveStateMemoryOffset(0x14, Memory.saveStateSlot));
120+
Memory.hblankHdmaDestination = load<i32>(getSaveStateMemoryOffset(0x18, Memory.saveStateSlot));
109121
}
110122
}
111123

core/sound/accumulator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class SoundAccumulator {
2222
static mixerVolumeChanged: boolean = false;
2323
static mixerEnabledChanged: boolean = false;
2424

25-
//If a channel was updated, need to also track if we need to need to mix them again
25+
// If a channel was updated, need to also track if we need to need to mix them again
2626
static needToRemixSamples: boolean = false;
2727
}
2828

core/sound/channel1.ts

Lines changed: 91 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -194,36 +194,101 @@ export class Channel1 {
194194

195195
// Function to save the state of the class
196196
static saveState(): void {
197-
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Channel1.saveStateSlot), Channel1.isEnabled);
198-
store<i32>(getSaveStateMemoryOffset(0x01, Channel1.saveStateSlot), Channel1.frequencyTimer);
199-
store<i32>(getSaveStateMemoryOffset(0x05, Channel1.saveStateSlot), Channel1.envelopeCounter);
200-
store<i32>(getSaveStateMemoryOffset(0x09, Channel1.saveStateSlot), Channel1.lengthCounter);
201-
store<i32>(getSaveStateMemoryOffset(0x0e, Channel1.saveStateSlot), Channel1.volume);
202-
203-
store<u8>(getSaveStateMemoryOffset(0x13, Channel1.saveStateSlot), Channel1.dutyCycle);
204-
store<u8>(getSaveStateMemoryOffset(0x14, Channel1.saveStateSlot), <u8>Channel1.waveFormPositionOnDuty);
205-
206-
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x19, Channel1.saveStateSlot), Channel1.isSweepEnabled);
207-
store<i32>(getSaveStateMemoryOffset(0x1a, Channel1.saveStateSlot), Channel1.sweepCounter);
208-
store<u16>(getSaveStateMemoryOffset(0x1f, Channel1.saveStateSlot), Channel1.sweepShadowFrequency);
209-
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x21, Channel1.saveStateSlot), Channel1.isEnvelopeAutomaticUpdating);
197+
// Cycle Counter
198+
store<i32>(getSaveStateMemoryOffset(0x00, Channel1.saveStateSlot), Channel1.cycleCounter);
199+
200+
// NRx0
201+
store<u8>(getSaveStateMemoryOffset(0x04, Channel1.saveStateSlot), <u8>Channel1.NRx0SweepPeriod);
202+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x05, Channel1.saveStateSlot), Channel1.NRx0Negate);
203+
store<u8>(getSaveStateMemoryOffset(0x06, Channel1.saveStateSlot), <u8>Channel1.NRx0SweepShift);
204+
205+
// NRx1
206+
store<u8>(getSaveStateMemoryOffset(0x07, Channel1.saveStateSlot), <u8>Channel1.NRx1Duty);
207+
store<u16>(getSaveStateMemoryOffset(0x09, Channel1.saveStateSlot), <u16>Channel1.NRx1LengthLoad);
208+
209+
// NRx2
210+
store<u8>(getSaveStateMemoryOffset(0x0a, Channel1.saveStateSlot), <u8>Channel1.NRx2StartingVolume);
211+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0b, Channel1.saveStateSlot), Channel1.NRx2EnvelopeAddMode);
212+
store<u8>(getSaveStateMemoryOffset(0x0c, Channel1.saveStateSlot), <u8>Channel1.NRx2EnvelopePeriod);
213+
214+
// NRx3
215+
store<u8>(getSaveStateMemoryOffset(0x0d, Channel1.saveStateSlot), <u8>Channel1.NRx3FrequencyLSB);
216+
217+
// NRx4
218+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0e, Channel1.saveStateSlot), Channel1.NRx4LengthEnabled);
219+
store<u8>(getSaveStateMemoryOffset(0x0f, Channel1.saveStateSlot), <u8>Channel1.NRx4FrequencyMSB);
220+
221+
// Channel Properties
222+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x10, Channel1.saveStateSlot), Channel1.isEnabled);
223+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x11, Channel1.saveStateSlot), Channel1.isDacEnabled);
224+
store<i32>(getSaveStateMemoryOffset(0x12, Channel1.saveStateSlot), Channel1.frequency);
225+
store<i32>(getSaveStateMemoryOffset(0x16, Channel1.saveStateSlot), Channel1.frequencyTimer);
226+
store<i32>(getSaveStateMemoryOffset(0x1a, Channel1.saveStateSlot), Channel1.envelopeCounter);
227+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x1e, Channel1.saveStateSlot), Channel1.isEnvelopeAutomaticUpdating);
228+
store<i32>(getSaveStateMemoryOffset(0x1f, Channel1.saveStateSlot), Channel1.lengthCounter);
229+
store<i32>(getSaveStateMemoryOffset(0x23, Channel1.saveStateSlot), Channel1.volume);
230+
231+
// Square Duty
232+
store<u8>(getSaveStateMemoryOffset(0x27, Channel1.saveStateSlot), Channel1.dutyCycle);
233+
store<u8>(getSaveStateMemoryOffset(0x28, Channel1.saveStateSlot), <u8>Channel1.waveFormPositionOnDuty);
234+
235+
// Square Sweep
236+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x29, Channel1.saveStateSlot), Channel1.isSweepEnabled);
237+
store<i32>(getSaveStateMemoryOffset(0x2a, Channel1.saveStateSlot), Channel1.sweepCounter);
238+
store<u16>(getSaveStateMemoryOffset(0x2e, Channel1.saveStateSlot), Channel1.sweepShadowFrequency);
239+
storeBooleanDirectlyToWasmMemory(
240+
getSaveStateMemoryOffset(0x31, Channel1.saveStateSlot),
241+
Channel1.sweepNegateShouldDisableChannelOnClear
242+
);
210243
}
211244

212245
// Function to load the save state from memory
213246
static loadState(): void {
214-
Channel1.isEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Channel1.saveStateSlot));
215-
Channel1.frequencyTimer = load<i32>(getSaveStateMemoryOffset(0x01, Channel1.saveStateSlot));
216-
Channel1.envelopeCounter = load<i32>(getSaveStateMemoryOffset(0x05, Channel1.saveStateSlot));
217-
Channel1.lengthCounter = load<i32>(getSaveStateMemoryOffset(0x09, Channel1.saveStateSlot));
218-
Channel1.volume = load<i32>(getSaveStateMemoryOffset(0x0e, Channel1.saveStateSlot));
219-
220-
Channel1.dutyCycle = load<u8>(getSaveStateMemoryOffset(0x13, Channel1.saveStateSlot));
221-
Channel1.waveFormPositionOnDuty = load<u8>(getSaveStateMemoryOffset(0x14, Channel1.saveStateSlot));
222-
223-
Channel1.isSweepEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x19, Channel1.saveStateSlot));
224-
Channel1.sweepCounter = load<i32>(getSaveStateMemoryOffset(0x1a, Channel1.saveStateSlot));
225-
Channel1.sweepShadowFrequency = load<u16>(getSaveStateMemoryOffset(0x1f, Channel1.saveStateSlot));
226-
Channel1.isEnvelopeAutomaticUpdating = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x21, Channel1.saveStateSlot));
247+
// Cycle Counter
248+
Channel1.cycleCounter = load<i32>(getSaveStateMemoryOffset(0x00, Channel1.cycleCounter));
249+
250+
// NRx0
251+
Channel1.NRx0SweepPeriod = load<u8>(getSaveStateMemoryOffset(0x04, Channel1.saveStateSlot));
252+
Channel1.NRx0Negate = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x05, Channel1.saveStateSlot));
253+
Channel1.NRx0SweepShift = load<u8>(getSaveStateMemoryOffset(0x06, Channel1.saveStateSlot));
254+
255+
// NRx1
256+
Channel1.NRx1Duty = load<u8>(getSaveStateMemoryOffset(0x07, Channel1.saveStateSlot));
257+
Channel1.NRx1LengthLoad = load<u16>(getSaveStateMemoryOffset(0x09, Channel1.saveStateSlot));
258+
259+
// NRx2
260+
Channel1.NRx2StartingVolume = load<u8>(getSaveStateMemoryOffset(0x0a, Channel1.saveStateSlot));
261+
Channel1.NRx2EnvelopeAddMode = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0b, Channel1.saveStateSlot));
262+
Channel1.NRx2EnvelopePeriod = load<u8>(getSaveStateMemoryOffset(0x0c, Channel1.saveStateSlot));
263+
264+
// NRx3
265+
Channel1.NRx3FrequencyLSB = load<u8>(getSaveStateMemoryOffset(0x0d, Channel1.saveStateSlot));
266+
267+
// NRx4
268+
Channel1.NRx4LengthEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0e, Channel1.saveStateSlot));
269+
Channel1.NRx4FrequencyMSB = load<u8>(getSaveStateMemoryOffset(0x0f, Channel1.saveStateSlot));
270+
271+
// Channel Properties
272+
Channel1.isEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x10, Channel1.saveStateSlot));
273+
Channel1.isDacEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x11, Channel1.saveStateSlot));
274+
Channel1.frequency = load<i32>(getSaveStateMemoryOffset(0x12, Channel1.saveStateSlot));
275+
Channel1.frequencyTimer = load<i32>(getSaveStateMemoryOffset(0x16, Channel1.saveStateSlot));
276+
Channel1.envelopeCounter = load<i32>(getSaveStateMemoryOffset(0x1a, Channel1.saveStateSlot));
277+
Channel1.isEnvelopeAutomaticUpdating = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x1e, Channel1.saveStateSlot));
278+
Channel1.lengthCounter = load<i32>(getSaveStateMemoryOffset(0x1f, Channel1.saveStateSlot));
279+
Channel1.volume = load<i32>(getSaveStateMemoryOffset(0x23, Channel1.saveStateSlot));
280+
281+
// Square Duty
282+
Channel1.dutyCycle = load<u8>(getSaveStateMemoryOffset(0x27, Channel1.saveStateSlot));
283+
Channel1.waveFormPositionOnDuty = load<u8>(getSaveStateMemoryOffset(0x28, Channel1.saveStateSlot));
284+
285+
// Square Sweep
286+
Channel1.isSweepEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x29, Channel1.saveStateSlot));
287+
Channel1.sweepCounter = load<u8>(getSaveStateMemoryOffset(0x2a, Channel1.saveStateSlot));
288+
Channel1.sweepShadowFrequency = load<u8>(getSaveStateMemoryOffset(0x2e, Channel1.saveStateSlot));
289+
Channel1.sweepNegateShouldDisableChannelOnClear = loadBooleanDirectlyFromWasmMemory(
290+
getSaveStateMemoryOffset(0x31, Channel1.saveStateSlot)
291+
);
227292
}
228293

229294
static initialize(): void {

0 commit comments

Comments
 (0)