@soapbox.pub/wasmboy
Version:
Soapbox fork of Wasmboy.
302 lines (247 loc) • 8.88 kB
text/typescript
// Imports
import { Cpu } from './index';
import {
setZeroFlag,
setSubtractFlag,
setHalfCarryFlag,
setCarryFlag,
getCarryFlag,
checkAndSetEightBitCarryFlag,
checkAndSetEightBitHalfCarryFlag
} from './flags';
import { rotateByteLeft, rotateByteLeftThroughCarry, rotateByteRight, rotateByteRightThroughCarry } from '../helpers/index';
import { u8Portable, u16Portable, i8Portable } from '../portable/portable';
// General Logic Instructions
// Such as the ones found on the CB table and 0x40 - 0xBF
// NOTE: Only CB table uses these for now, was mostly me realizing that I messed up, trying to be all cute and verbose :p
// NOTE: TODO: Refactor honestly shouldn't take that long, and may happen once assembly script is improved
export function addARegister(register: u8): void {
let registerA = Cpu.registerA;
checkAndSetEightBitHalfCarryFlag(registerA, register);
checkAndSetEightBitCarryFlag(registerA, register);
registerA = u8Portable(registerA + register);
Cpu.registerA = registerA;
setZeroFlag(<i32>(registerA === 0));
setSubtractFlag(0);
}
export function addAThroughCarryRegister(register: u8): void {
// Handling flags manually as they require some special overflow
// From: https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js
// CTRL+F adc
let registerA = Cpu.registerA;
let result = u8Portable(registerA + register + getCarryFlag());
setHalfCarryFlag(<i32>((u8Portable(registerA ^ register ^ result) & 0x10) != 0));
let overflowedResult = u16Portable(<u16>registerA + <u16>register + <u16>getCarryFlag());
setCarryFlag(<i32>((overflowedResult & 0x100) > 0));
Cpu.registerA = result;
setZeroFlag(<i32>(result === 0));
setSubtractFlag(0);
}
export function subARegister(register: u8): void {
// Need to convert the register on one line, and flip the sign on another
let negativeRegister: i32 = register;
negativeRegister = negativeRegister * -1;
let registerA = Cpu.registerA;
checkAndSetEightBitHalfCarryFlag(registerA, negativeRegister);
checkAndSetEightBitCarryFlag(registerA, negativeRegister);
registerA = u8Portable(registerA - register);
Cpu.registerA = registerA;
setZeroFlag(<i32>(registerA === 0));
setSubtractFlag(1);
}
export function subAThroughCarryRegister(register: u8): void {
// Handling flags manually as they require some special overflow
// From: https://github.com/nakardo/node-gameboy/blob/master/lib/cpu/opcodes.js
// CTRL+F adc
let registerA = Cpu.registerA;
let result = u8Portable(registerA - register - getCarryFlag());
let carryRegisterCheck = u8Portable((registerA ^ register ^ result) & 0x10);
setHalfCarryFlag(<i32>(carryRegisterCheck != 0));
let overflowedResult = u16Portable(<u16>registerA - <u16>register - <u16>getCarryFlag());
setCarryFlag(<i32>((overflowedResult & 0x100) > 0));
Cpu.registerA = result;
setZeroFlag(<i32>(result === 0));
setSubtractFlag(1);
}
export function andARegister(register: u8): void {
let registerA = Cpu.registerA & register;
Cpu.registerA = registerA;
setZeroFlag(<i32>(registerA === 0));
setSubtractFlag(0);
setHalfCarryFlag(1);
setCarryFlag(0);
}
export function xorARegister(register: u8): void {
let registerA = u8Portable(Cpu.registerA ^ register);
Cpu.registerA = registerA;
setZeroFlag(<i32>(registerA === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
setCarryFlag(0);
}
export function orARegister(register: u8): void {
let registerA = Cpu.registerA | register;
Cpu.registerA = registerA;
setZeroFlag(<i32>(registerA === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
setCarryFlag(0);
}
export function cpARegister(register: u8): void {
// 0xB8 - 0xBF
// CP B
// 1 4
// Z 1 H C
let registerA = Cpu.registerA;
let negativeRegister: i32 = register;
negativeRegister = negativeRegister * -1;
checkAndSetEightBitHalfCarryFlag(registerA, negativeRegister);
checkAndSetEightBitCarryFlag(registerA, negativeRegister);
let tempResult = <i32>registerA + negativeRegister;
setZeroFlag(<i32>(tempResult === 0));
setSubtractFlag(1);
}
// Inlined because closure compiler inlines
export function rotateRegisterLeft(register: u8): u8 {
// RLC register 8-bit
// Z 0 0 C
setCarryFlag(<i32>((register & 0x80) === 0x80));
register = rotateByteLeft(register);
setZeroFlag(<i32>(register === 0));
// Set all other flags to zero
setSubtractFlag(0);
setHalfCarryFlag(0);
// Return the register
return register;
}
// Inlined because closure compiler inlines
export function rotateRegisterRight(register: u8): u8 {
// RLC register 8-bit
// Z 0 0 C
// Check for the last bit, to see if it will be carried
setCarryFlag(<i32>((register & 0x01) > 0));
register = rotateByteRight(register);
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
// Return the register
return register;
}
// Inlined because closure compiler inlines
export function rotateRegisterLeftThroughCarry(register: u8): u8 {
// RL register 8-bit
// Z 0 0 C
// setting has first bit since we need to use carry
let hasHighbit = (register & 0x80) === 0x80;
register = rotateByteLeftThroughCarry(register);
setCarryFlag(<i32>hasHighbit);
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
return register;
}
// Inlined because closure compiler inlines
export function rotateRegisterRightThroughCarry(register: u8): u8 {
// RR register 8-bit
// Z 0 0 C
let hasLowBit = (register & 0x01) === 0x01;
register = rotateByteRightThroughCarry(register);
setCarryFlag(<i32>hasLowBit);
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
return register;
}
// Inlined because closure compiler inlines
export function shiftLeftRegister(register: u8): u8 {
// SLA register 8-bit
// Z 0 0 C
let hasHighbit = (register & 0x80) === 0x80;
register = u8Portable(register << 1);
setCarryFlag(<i32>hasHighbit);
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
return register;
}
// Inlined because closure compiler inlines
export function shiftRightArithmeticRegister(register: u8): u8 {
// SRA register 8-bit
// Z 0 0 C
// NOTE: This C flag may need to be set to 0;
// This preserves the MSB (Most significant bit)
let hasHighbit = (register & 0x80) === 0x80;
let hasLowbit = (register & 0x01) === 0x01;
register = u8Portable(register >> 1);
if (hasHighbit) {
register = register | 0x80;
}
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
setCarryFlag(<i32>hasLowbit);
return register;
}
// Inlined because closure compiler inlines
export function swapNibblesOnRegister(register: u8): u8 {
// SWAP register 8-bit
// Z 0 0 0
let highNibble = register & 0xf0;
let lowNibble = register & 0x0f;
register = u8Portable((lowNibble << 4) | (highNibble >> 4));
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
setCarryFlag(0);
return register;
}
// Inlined because closure compiler inlines
export function shiftRightLogicalRegister(register: u8): u8 {
// SRA register 8-bit
// Z 0 0 C
// NOTE: This C flag may need to be set to 0;
// This does NOT preserve MSB (most significant bit)
let hasLowbit = (register & 0x01) === 0x01;
register = u8Portable(register >> 1);
setZeroFlag(<i32>(register === 0));
setSubtractFlag(0);
setHalfCarryFlag(0);
setCarryFlag(<i32>hasLowbit);
return register;
}
export function testBitOnRegister(bitPosition: u8, register: u8): u8 {
// BIT bitPosition ,register 8-bit
// Z 0 1 -
let testByte: u8 = 0x01 << bitPosition;
let result = register & testByte;
setZeroFlag(<i32>(result === 0x00));
setSubtractFlag(0);
setHalfCarryFlag(1);
return register;
}
export function setBitOnRegister(bitPosition: u8, bitValue: i32, register: u8): u8 {
// RES 0,B or SET 0,B depending on bit value
if (bitValue > 0) {
let setByte: u8 = 0x01 << bitPosition;
register = register | setByte;
} else {
// NOT (byte we want)
// 0000 0100 becomes 1111 1011
let setByte: u8 = ~(0x01 << bitPosition);
register = register & setByte;
}
return register;
}
// Private function for our relative jumps
export function relativeJump(value: u8): void {
// Need to convert the value to i8, since in this case, u8 can be negative
let relativeJumpOffset = i8Portable(<i8>value);
let programCounter = Cpu.programCounter;
programCounter = u16Portable(programCounter + relativeJumpOffset);
// Realtive jump, using bgb debugger
// and my debugger shows,
// on JR you need to jump to the relative jump offset,
// However, if the jump fails (such as conditional), only jump +2 in total
programCounter = u16Portable(programCounter + 1);
Cpu.programCounter = programCounter;
}