@soapbox.pub/wasmboy
Version:
Soapbox fork of Wasmboy.
298 lines (257 loc) • 7.67 kB
text/typescript
import { Cpu } from '../cpu/index';
import { eightBitLoadFromGBMemory } from '../memory/load';
import { requestJoypadInterrupt } from '../interrupts/index';
import { checkBitOnByte, setBitOnByte, resetBitOnByte } from '../helpers/index';
import { getSaveStateMemoryOffset } from '../core';
import {
eightBitLoadFromGBMemory,
eightBitStoreIntoGBMemory,
sixteenBitStoreIntoGBMemory,
loadBooleanDirectlyFromWasmMemory,
storeBooleanDirectlyToWasmMemory
} from '../memory/index';
// http://www.codeslinger.co.uk/pages/projects/gameboy/joypad.html
// Joypad Register
// Taken from pandocs
// Bit 7 - Not used
// Bit 6 - Not used
// Bit 5 - P15 Select Button Keys (0=Select)
// Bit 4 - P14 Select Direction Keys (0=Select)
// Bit 3 - P13 Input Down or Start (0=Pressed) (Read Only)
// Bit 2 - P12 Input Up or Select (0=Pressed) (Read Only)
// Bit 1 - P11 Input Left or Button B (0=Pressed) (Read Only)
// Bit 0 - P10 Input Right or Button A (0=Pressed) (Read Only)
// Button Ids will be the following:
// UP - 0
// RIGHT - 1
// DOWN - 2
// LEFT - 3
// A - 4
// B - 5
// SELECT - 6
// START - 7
export class Joypad {
static up: boolean = false;
static down: boolean = false;
static left: boolean = false;
static right: boolean = false;
static a: boolean = false;
static b: boolean = false;
static select: boolean = false;
static start: boolean = false;
static readonly memoryLocationJoypadRegister: i32 = 0xff00;
// Cache some values on the Joypad register
static joypadRegisterFlipped: i32 = 0;
static isDpadType: boolean = false;
static isButtonType: boolean = false;
static updateJoypad(value: i32): void {
Joypad.joypadRegisterFlipped = value ^ 0xff;
Joypad.isDpadType = checkBitOnByte(4, Joypad.joypadRegisterFlipped);
Joypad.isButtonType = checkBitOnByte(5, Joypad.joypadRegisterFlipped);
}
// Save States
// Not doing anything for Joypad for now
static readonly saveStateSlot: i32 = 3;
// Function to save the state of the class
static saveState(): void {
store<i32>(getSaveStateMemoryOffset(0x00, Joypad.saveStateSlot), Joypad.joypadRegisterFlipped);
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x01, Joypad.saveStateSlot), Joypad.isDpadType);
storeBooleanDirectlyToWasmMemory(getSaveStateMemoryOffset(0x02, Joypad.saveStateSlot), Joypad.isButtonType);
}
// Function to load the save state from memory
static loadState(): void {
Joypad.joypadRegisterFlipped = load<i32>(getSaveStateMemoryOffset(0x00, Joypad.saveStateSlot));
Joypad.isDpadType = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x01, Joypad.saveStateSlot));
Joypad.isButtonType = loadBooleanDirectlyFromWasmMemory(getSaveStateMemoryOffset(0x02, Joypad.saveStateSlot));
}
}
// Inlined because closure compiler inlines
export function getJoypadState(): i32 {
// Get the joypad register
let joypadRegister: i32 = Joypad.joypadRegisterFlipped;
if (Joypad.isDpadType) {
// D-pad buttons
// Up
if (Joypad.up) {
joypadRegister = resetBitOnByte(2, joypadRegister);
} else {
joypadRegister = setBitOnByte(2, joypadRegister);
}
// Right
if (Joypad.right) {
joypadRegister = resetBitOnByte(0, joypadRegister);
} else {
joypadRegister = setBitOnByte(0, joypadRegister);
}
// Down
if (Joypad.down) {
joypadRegister = resetBitOnByte(3, joypadRegister);
} else {
joypadRegister = setBitOnByte(3, joypadRegister);
}
// Left
if (Joypad.left) {
joypadRegister = resetBitOnByte(1, joypadRegister);
} else {
joypadRegister = setBitOnByte(1, joypadRegister);
}
} else if (Joypad.isButtonType) {
// A
if (Joypad.a) {
joypadRegister = resetBitOnByte(0, joypadRegister);
} else {
joypadRegister = setBitOnByte(0, joypadRegister);
}
// B
if (Joypad.b) {
joypadRegister = resetBitOnByte(1, joypadRegister);
} else {
joypadRegister = setBitOnByte(1, joypadRegister);
}
// Select
if (Joypad.select) {
joypadRegister = resetBitOnByte(2, joypadRegister);
} else {
joypadRegister = setBitOnByte(2, joypadRegister);
}
// Start
if (Joypad.start) {
joypadRegister = resetBitOnByte(3, joypadRegister);
} else {
joypadRegister = setBitOnByte(3, joypadRegister);
}
}
// Set the top 4 bits to on
joypadRegister = joypadRegister | 0xf0;
return joypadRegister;
}
export function setJoypadState(up: i32, right: i32, down: i32, left: i32, a: i32, b: i32, select: i32, start: i32): void {
if (up > 0) {
_pressJoypadButton(0);
} else {
_releaseJoypadButton(0);
}
if (right > 0) {
_pressJoypadButton(1);
} else {
_releaseJoypadButton(1);
}
if (down > 0) {
_pressJoypadButton(2);
} else {
_releaseJoypadButton(2);
}
if (left > 0) {
_pressJoypadButton(3);
} else {
_releaseJoypadButton(3);
}
if (a > 0) {
_pressJoypadButton(4);
} else {
_releaseJoypadButton(4);
}
if (b > 0) {
_pressJoypadButton(5);
} else {
_releaseJoypadButton(5);
}
if (select > 0) {
_pressJoypadButton(6);
} else {
_releaseJoypadButton(6);
}
if (start > 0) {
_pressJoypadButton(7);
} else {
_releaseJoypadButton(7);
}
}
function _pressJoypadButton(buttonId: i32): void {
// Un stop the CPU
Cpu.isStopped = false;
// Check if the button state changed from not pressed
let isButtonStateChanging: boolean = false;
if (!_getJoypadButtonStateFromButtonId(buttonId)) {
isButtonStateChanging = true;
}
// Set our joypad state
_setJoypadButtonStateFromButtonId(buttonId, true);
// If the button state is changing, check for an interrupt
if (isButtonStateChanging) {
// Determine if it is a button or a dpad button
let isDpadTypeButton = false;
if (buttonId <= 3) {
isDpadTypeButton = true;
}
// Determine if we should request an interrupt
let shouldRequestInterrupt = false;
// Check if the game is looking for a dpad type button press
if (Joypad.isDpadType && isDpadTypeButton) {
shouldRequestInterrupt = true;
}
// Check if the game is looking for a button type button press
if (Joypad.isButtonType && !isDpadTypeButton) {
shouldRequestInterrupt = true;
}
// Finally, request the interrupt, if the button state actually changed
if (shouldRequestInterrupt) {
requestJoypadInterrupt();
}
}
}
// Inlined because closure compiler inlines
function _releaseJoypadButton(buttonId: i32): void {
// Set our joypad state
_setJoypadButtonStateFromButtonId(buttonId, false);
}
function _getJoypadButtonStateFromButtonId(buttonId: i32): boolean {
switch (buttonId) {
case 0:
return Joypad.up;
case 1:
return Joypad.right;
case 2:
return Joypad.down;
case 3:
return Joypad.left;
case 4:
return Joypad.a;
case 5:
return Joypad.b;
case 6:
return Joypad.select;
case 7:
return Joypad.start;
default:
return false;
}
}
function _setJoypadButtonStateFromButtonId(buttonId: i32, isPressed: boolean): void {
switch (buttonId) {
case 0:
Joypad.up = isPressed;
break;
case 1:
Joypad.right = isPressed;
break;
case 2:
Joypad.down = isPressed;
break;
case 3:
Joypad.left = isPressed;
break;
case 4:
Joypad.a = isPressed;
break;
case 5:
Joypad.b = isPressed;
break;
case 6:
Joypad.select = isPressed;
break;
case 7:
Joypad.start = isPressed;
break;
}
}