Skip to content

Commit 769034e

Browse files
authored
Fixed Save States for Graphics (#333)
* Fixed graphics Save State * Fixed Save State Test and golden output
1 parent d49fea2 commit 769034e

Some content is hidden

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

45 files changed

+726216
-726120
lines changed

core/debug/debug-graphics.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ export function getLY(): i32 {
2323
return Graphics.scanlineRegister;
2424
}
2525

26+
export function getScrollX(): i32 {
27+
return Graphics.scrollX;
28+
}
29+
30+
export function getScrollY(): i32 {
31+
return Graphics.scrollY;
32+
}
33+
34+
export function getWindowX(): i32 {
35+
return Graphics.windowX;
36+
}
37+
38+
export function getWindowY(): i32 {
39+
return Graphics.windowY;
40+
}
41+
2642
// TODO: Render by tile, rather than by pixel
2743
export function drawBackgroundMapToWasmMemory(showColor: i32): void {
2844
// http://www.codeslinger.co.uk/pages/projects/gameboy/graphics.html

core/graphics/graphics.ts

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,38 +95,46 @@ export class Graphics {
9595
static saveState(): void {
9696
// Graphics
9797
store<i32>(getSaveStateMemoryOffset(0x00, Graphics.saveStateSlot), Graphics.scanlineCycleCounter);
98-
eightBitStoreIntoGBMemory(Graphics.memoryLocationScanlineRegister, Graphics.scanlineRegister);
98+
store<u8>(getSaveStateMemoryOffset(0x04, Graphics.saveStateSlot), <u8>Graphics.scanlineRegister);
99+
store<u8>(getSaveStateMemoryOffset(0x05, Graphics.saveStateSlot), <u8>Graphics.scrollX);
100+
store<u8>(getSaveStateMemoryOffset(0x06, Graphics.saveStateSlot), <u8>Graphics.scrollY);
101+
store<u8>(getSaveStateMemoryOffset(0x07, Graphics.saveStateSlot), <u8>Graphics.windowX);
102+
store<u8>(getSaveStateMemoryOffset(0x08, Graphics.saveStateSlot), <u8>Graphics.windowY);
99103

100104
// 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);
105+
store<u8>(getSaveStateMemoryOffset(0x09, Graphics.saveStateSlot), <u8>Lcd.currentLcdMode);
106+
store<u8>(getSaveStateMemoryOffset(0x0a, Graphics.saveStateSlot), <u8>Lcd.coincidenceCompare);
107+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0b, Graphics.saveStateSlot), Lcd.enabled);
108+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0c, Graphics.saveStateSlot), Lcd.windowTileMapDisplaySelect);
109+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0d, Graphics.saveStateSlot), Lcd.windowDisplayEnabled);
110+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0e, Graphics.saveStateSlot), Lcd.bgWindowTileDataSelect);
111+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x0f, Graphics.saveStateSlot), Lcd.bgTileMapDisplaySelect);
112+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x10, Graphics.saveStateSlot), Lcd.tallSpriteSize);
113+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x11, Graphics.saveStateSlot), Lcd.spriteDisplayEnable);
114+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x12, Graphics.saveStateSlot), Lcd.bgDisplayEnabled);
111115
}
112116

113117
// Function to load the save state from memory
114118
static loadState(): void {
115119
// Graphics
116120
Graphics.scanlineCycleCounter = load<i32>(getSaveStateMemoryOffset(0x00, Graphics.saveStateSlot));
117-
Graphics.scanlineRegister = eightBitLoadFromGBMemory(Graphics.memoryLocationScanlineRegister);
121+
Graphics.scanlineRegister = load<u8>(getSaveStateMemoryOffset(0x04, Graphics.scanlineRegister));
122+
Graphics.scrollX = load<u8>(getSaveStateMemoryOffset(0x05, Graphics.saveStateSlot));
123+
Graphics.scrollY = load<u8>(getSaveStateMemoryOffset(0x06, Graphics.saveStateSlot));
124+
Graphics.windowX = load<u8>(getSaveStateMemoryOffset(0x07, Graphics.saveStateSlot));
125+
Graphics.windowY = load<u8>(getSaveStateMemoryOffset(0x08, Graphics.saveStateSlot));
118126

119127
// 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));
128+
Lcd.currentLcdMode = load<u8>(getSaveStateMemoryOffset(0x09, Graphics.saveStateSlot));
129+
Lcd.coincidenceCompare = load<u8>(getSaveStateMemoryOffset(0x0a, Graphics.saveStateSlot));
130+
Lcd.enabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0b, Graphics.saveStateSlot));
131+
Lcd.windowTileMapDisplaySelect = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0c, Graphics.saveStateSlot));
132+
Lcd.windowDisplayEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0d, Graphics.saveStateSlot));
133+
Lcd.bgWindowTileDataSelect = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0e, Graphics.saveStateSlot));
134+
Lcd.bgTileMapDisplaySelect = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x0f, Graphics.saveStateSlot));
135+
Lcd.tallSpriteSize = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x10, Graphics.saveStateSlot));
136+
Lcd.spriteDisplayEnable = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x11, Graphics.saveStateSlot));
137+
Lcd.bgDisplayEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x12, Graphics.saveStateSlot));
130138
}
131139
}
132140

core/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ export {
8585
getStackPointer,
8686
getOpcodeAtProgramCounter
8787
} from './debug/debug-cpu';
88-
export { getLY, drawBackgroundMapToWasmMemory, drawTileDataToWasmMemory, drawOamToWasmMemory } from './debug/debug-graphics';
88+
export {
89+
getLY,
90+
getScrollX,
91+
getScrollY,
92+
getWindowX,
93+
getWindowY,
94+
drawBackgroundMapToWasmMemory,
95+
drawTileDataToWasmMemory,
96+
drawOamToWasmMemory
97+
} from './debug/debug-graphics';
8998
export { getDIV, getTIMA, getTMA, getTAC } from './debug/debug-timer';
9099
export { updateDebugGBMemory } from './debug/debug-memory';

core/interrupts/interrupts.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,48 @@ export class Interrupts {
6969

7070
// Function to save the state of the class
7171
static saveState(): void {
72+
// Interrupt Master Interrupt Switch
7273
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x00, Interrupts.saveStateSlot), Interrupts.masterInterruptSwitch);
7374
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x01, Interrupts.saveStateSlot), Interrupts.masterInterruptSwitchDelay);
7475

75-
// 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
76+
// Interrupt Enabled
77+
store<u8>(getSaveStateMemoryOffset(0x10, Interrupts.saveStateSlot), <u8>Interrupts.interruptsEnabledValue);
78+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x11, Interrupts.saveStateSlot), Interrupts.isVBlankInterruptEnabled);
79+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x12, Interrupts.saveStateSlot), Interrupts.isLcdInterruptEnabled);
80+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x13, Interrupts.saveStateSlot), Interrupts.isTimerInterruptEnabled);
81+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x14, Interrupts.saveStateSlot), Interrupts.isSerialInterruptEnabled);
82+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x15, Interrupts.saveStateSlot), Interrupts.isJoypadInterruptEnabled);
83+
84+
// Interrupt Request
85+
store<u8>(getSaveStateMemoryOffset(0x20, Interrupts.saveStateSlot), <u8>Interrupts.interruptsRequestedValue);
86+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x21, Interrupts.saveStateSlot), Interrupts.isVBlankInterruptRequested);
87+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x22, Interrupts.saveStateSlot), Interrupts.isLcdInterruptRequested);
88+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x23, Interrupts.saveStateSlot), Interrupts.isTimerInterruptRequested);
89+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x24, Interrupts.saveStateSlot), Interrupts.isSerialInterruptRequested);
90+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x25, Interrupts.saveStateSlot), Interrupts.isJoypadInterruptRequested);
7791
}
7892

7993
// Function to load the save state from memory
8094
static loadState(): void {
95+
// Interrupt Master Interrupt Switch
8196
Interrupts.masterInterruptSwitch = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x00, Interrupts.saveStateSlot));
8297
Interrupts.masterInterruptSwitchDelay = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x01, Interrupts.saveStateSlot));
8398

84-
Interrupts.updateInterruptEnabled(eightBitLoadFromGBMemory(Interrupts.memoryLocationInterruptEnabled));
85-
Interrupts.updateInterruptRequested(eightBitLoadFromGBMemory(Interrupts.memoryLocationInterruptRequest));
99+
// Interrupt Enabled
100+
Interrupts.interruptsEnabledValue = load<u8>(getSaveStateMemoryOffset(0x10, Interrupts.saveStateSlot));
101+
Interrupts.isVBlankInterruptEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x11, Interrupts.saveStateSlot));
102+
Interrupts.isLcdInterruptEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x12, Interrupts.saveStateSlot));
103+
Interrupts.isTimerInterruptEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x13, Interrupts.saveStateSlot));
104+
Interrupts.isSerialInterruptEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x14, Interrupts.saveStateSlot));
105+
Interrupts.isJoypadInterruptEnabled = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x15, Interrupts.saveStateSlot));
106+
107+
// Interrupt Request
108+
Interrupts.interruptsRequestedValue = load<u8>(getSaveStateMemoryOffset(0x20, Interrupts.saveStateSlot));
109+
Interrupts.isVBlankInterruptRequested = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x21, Interrupts.saveStateSlot));
110+
Interrupts.isLcdInterruptRequested = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x22, Interrupts.saveStateSlot));
111+
Interrupts.isTimerInterruptRequested = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x23, Interrupts.saveStateSlot));
112+
Interrupts.isSerialInterruptRequested = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x24, Interrupts.saveStateSlot));
113+
Interrupts.isJoypadInterruptRequested = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x25, Interrupts.saveStateSlot));
86114
}
87115
}
88116

core/joypad/joypad.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ import { Cpu } from '../cpu/index';
22
import { eightBitLoadFromGBMemory } from '../memory/load';
33
import { requestJoypadInterrupt } from '../interrupts/index';
44
import { checkBitOnByte, setBitOnByte, resetBitOnByte } from '../helpers/index';
5+
import { getSaveStateMemoryOffset } from '../core';
6+
import {
7+
eightBitLoadFromGBMemory,
8+
eightBitStoreIntoGBMemory,
9+
sixteenBitStoreIntoGBMemory,
10+
loadBooleanDirectlyFromWasmMemory,
11+
storeBooleanDirectlyToWasmMemory
12+
} from '../memory/index';
513

614
// http://www.codeslinger.co.uk/pages/projects/gameboy/joypad.html
715
// Joypad Register
@@ -52,11 +60,19 @@ export class Joypad {
5260
static readonly saveStateSlot: i32 = 3;
5361

5462
// Function to save the state of the class
55-
static saveState(): void {}
63+
static saveState(): void {
64+
store<i32>(getSaveStateMemoryOffset(0x00, Joypad.saveStateSlot), Joypad.joypadRegisterFlipped);
65+
66+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x01, Joypad.saveStateSlot), Joypad.isDpadType);
67+
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x02, Joypad.saveStateSlot), Joypad.isButtonType);
68+
}
5669

5770
// Function to load the save state from memory
5871
static loadState(): void {
59-
Joypad.updateJoypad(eightBitLoadFromGBMemory(Joypad.memoryLocationJoypadRegister));
72+
Joypad.joypadRegisterFlipped = load<i32>(getSaveStateMemoryOffset(0x00, Joypad.saveStateSlot));
73+
74+
Joypad.isDpadType = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x01, Joypad.saveStateSlot));
75+
Joypad.isButtonType = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x02, Joypad.saveStateSlot));
6076
}
6177
}
6278

core/timers/timers.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,6 @@ export class Timers {
155155
// Timer Control
156156
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x16, Timers.saveStateSlot), Timers.timerEnabled);
157157
store<i32>(getSaveStateMemoryOffset(0x17, Timers.saveStateSlot), Timers.timerInputClock);
158-
159-
// Old Not too sture
160-
eightBitStoreIntoGBMemory(Timers.memoryLocationTimerCounter, Timers.timerCounter);
161158
}
162159

163160
// Function to load the save state from memory

test/core/save-state.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('WasmBoy Core Save State', () => {
5353
0, // graphicsDisableScanlineRendering: i32,
5454
1, // audioAccumulateSamples: i32,
5555
0, // tileRendering: i32,
56-
1, // tileCaching: i32,
56+
0, // tileCaching: i32,
5757
0 // enableAudioDebugging: i32
5858
);
5959

@@ -68,7 +68,7 @@ describe('WasmBoy Core Save State', () => {
6868
}
6969

7070
// Testing input
71-
if (i === -1) {
71+
if (i === 160) {
7272
wasmboy.setJoypadState(
7373
0, // up: i32,
7474
0, // right: i32,
@@ -79,10 +79,32 @@ describe('WasmBoy Core Save State', () => {
7979
0, // select: i32,
8080
1 // start: i32
8181
);
82+
} else if (i === 220) {
83+
wasmboy.setJoypadState(
84+
0, // up: i32,
85+
0, // right: i32,
86+
0, // down: i32,
87+
0, // left: i32,
88+
0, // a: i32,
89+
0, // b: i32,
90+
0, // select: i32,
91+
1 // start: i32
92+
);
93+
} else if (i === 260) {
94+
wasmboy.setJoypadState(
95+
0, // up: i32,
96+
0, // right: i32,
97+
0, // down: i32,
98+
0, // left: i32,
99+
1, // a: i32,
100+
0, // b: i32,
101+
0, // select: i32,
102+
0 // start: i32
103+
);
82104
}
83105

84106
// Run some frames
85-
wasmboy.executeMultipleFrames(2);
107+
wasmboy.executeMultipleFrames(5);
86108
wasmboy.clearAudioBuffer();
87109

88110
wasmboy.saveState();

0 commit comments

Comments
 (0)