Mesen-S/Core/Gsu.Instructions.cpp
2019-08-06 21:28:01 -04:00

735 lines
15 KiB
C++

#include "stdafx.h"
#include "Gsu.h"
#include "Cpu.h"
#include "MemoryManager.h"
void Gsu::STOP()
{
if(!_state.IrqDisabled) {
_state.SFR.Irq = true;
_cpu->SetIrqSource(IrqSource::Coprocessor);
}
_state.ProgramReadBuffer = 0x01; //Run a NOP first when the GSU is restarted
_state.SFR.Running = false;
ResetFlags();
UpdateRunningState();
}
void Gsu::NOP()
{
ResetFlags();
}
void Gsu::CACHE()
{
if(_state.CacheBase != (_state.R[15] & 0xFFF0)) {
_state.CacheBase = _state.R[15] & 0xFFF0;
InvalidateCache();
}
ResetFlags();
}
void Gsu::Branch(bool branch)
{
int8_t offset = (int8_t)ReadOperand();
if(branch) {
WriteRegister(15, _state.R[15] + offset);
}
}
void Gsu::BRA()
{
Branch(true);
}
void Gsu::BLT()
{
Branch(_state.SFR.Sign == _state.SFR.Overflow);
}
void Gsu::BGE()
{
Branch(_state.SFR.Sign != _state.SFR.Overflow);
}
void Gsu::BNE()
{
Branch(!_state.SFR.Zero);
}
void Gsu::BEQ()
{
Branch(_state.SFR.Zero);
}
void Gsu::BPL()
{
Branch(!_state.SFR.Sign);
}
void Gsu::BMI()
{
Branch(_state.SFR.Sign);
}
void Gsu::BCC()
{
Branch(!_state.SFR.Carry);
}
void Gsu::BCS()
{
Branch(_state.SFR.Carry);
}
void Gsu::BCV()
{
Branch(!_state.SFR.Overflow);
}
void Gsu::BVS()
{
Branch(_state.SFR.Overflow);
}
void Gsu::JMP(uint8_t reg)
{
if(_state.SFR.Alt1) {
//LJMP
_state.ProgramBank = _state.R[reg] & 0x7F;
WriteRegister(15, ReadSrcReg());
_state.CacheBase = _state.R[15] & 0xFFF0;
InvalidateCache();
} else {
//JMP
WriteRegister(15, _state.R[reg]);
}
ResetFlags();
}
void Gsu::TO(uint8_t reg)
{
if(_state.SFR.Prefix) {
//MOVE
WriteRegister(reg, ReadSrcReg());
ResetFlags();
} else {
//TO
_state.DestReg = reg;
}
}
void Gsu::FROM(uint8_t reg)
{
if(_state.SFR.Prefix) {
//MOVES
WriteDestReg(_state.R[reg]);
_state.SFR.Overflow = (_state.R[reg] & 0x80) != 0;
_state.SFR.Sign = (_state.R[reg] & 0x8000) != 0;
_state.SFR.Zero = (_state.R[reg] == 0);
ResetFlags();
} else {
//FROM
_state.SrcReg = reg;
}
}
void Gsu::WITH(uint8_t reg)
{
_state.SrcReg = reg;
_state.DestReg = reg;
_state.SFR.Prefix = true;
}
void Gsu::STORE(uint8_t reg)
{
_state.RamAddress = _state.R[reg];
WriteRam(_state.RamAddress, (uint8_t)ReadSrcReg());
if(!_state.SFR.Alt1) {
WriteRam(_state.RamAddress ^ 0x01, ReadSrcReg() >> 8);
}
ResetFlags();
}
void Gsu::LOAD(uint8_t reg)
{
_state.RamAddress = _state.R[reg];
uint16_t value = ReadRamBuffer(_state.RamAddress);
if(!_state.SFR.Alt1) {
value |= ReadRamBuffer(_state.RamAddress ^ 0x01) << 8;
}
WriteDestReg(value);
ResetFlags();
}
void Gsu::LOOP()
{
_state.R[12]--;
_state.SFR.Zero = (_state.R[12] == 0);
_state.SFR.Sign = (_state.R[12] & 0x8000) != 0;
//Loop until counter hits zero
if(!_state.SFR.Zero) {
WriteRegister(15, _state.R[13]);
}
ResetFlags();
}
void Gsu::ALT1()
{
_state.SFR.Prefix = false;
_state.SFR.Alt1 = true;
}
void Gsu::ALT2()
{
_state.SFR.Prefix = false;
_state.SFR.Alt2 = true;
}
void Gsu::ALT3()
{
_state.SFR.Prefix = false;
_state.SFR.Alt1 = true;
_state.SFR.Alt2 = true;
}
void Gsu::MERGE()
{
uint16_t value = (_state.R[7] & 0xFF00) | (_state.R[8] >> 8);
WriteDestReg(value);
_state.SFR.Carry = (value & 0xE0E0) != 0;
_state.SFR.Overflow = (value & 0xC0C0) != 0;
_state.SFR.Sign = (value & 0x8080) != 0;
_state.SFR.Zero = (value & 0xF0F0) != 0;
ResetFlags();
}
void Gsu::SWAP()
{
uint16_t value = (ReadSrcReg() >> 8) | (ReadSrcReg() << 8);
WriteDestReg(value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x8000) != 0;
ResetFlags();
}
void Gsu::Add(uint8_t reg)
{
uint16_t operand;
if(_state.SFR.Alt2) {
//Immediate value
operand = reg;
} else {
operand = _state.R[reg];
}
uint32_t result = ReadSrcReg() + operand;
if(_state.SFR.Alt1) {
//ADC - Add with carry
result += (uint8_t)_state.SFR.Carry;
}
_state.SFR.Carry = (result & 0x10000) != 0;
_state.SFR.Overflow = ~(ReadSrcReg() ^ operand) & (operand ^ result) & 0x8000;
_state.SFR.Sign = (result & 0x8000) != 0;
_state.SFR.Zero = (result & 0xFFFF) == 0;
WriteDestReg(result);
ResetFlags();
}
void Gsu::SubCompare(uint8_t reg)
{
uint16_t operand;
if(_state.SFR.Alt2 && !_state.SFR.Alt1) {
//Immediate value, SUB #val
operand = reg;
} else {
operand = _state.R[reg];
}
int32_t result = ReadSrcReg() - operand;
if(!_state.SFR.Alt2 && _state.SFR.Alt1) {
//SBC - SUB with carry
result -= _state.SFR.Carry ? 0 : 1;
}
_state.SFR.Carry = (result >= 0);
_state.SFR.Overflow = ((ReadSrcReg() ^ operand) & (ReadSrcReg() ^ result) & 0x8000) != 0;
_state.SFR.Sign = (result & 0x8000) != 0;
_state.SFR.Zero = (result & 0xFFFF) == 0;
if(!_state.SFR.Alt2 || !_state.SFR.Alt1) {
//SUB/SBC, other CMP (and no write occurs for CMP)
WriteDestReg(result);
}
ResetFlags();
}
void Gsu::MULT(uint8_t reg)
{
uint16_t operand;
if(_state.SFR.Alt2) {
//Immediate value
operand = reg;
} else {
operand = _state.R[reg];
}
uint16_t value;
if(_state.SFR.Alt1) {
//UMULT - Unsigned multiply
value = (uint16_t)((uint8_t)ReadSrcReg() * (uint8_t)operand);
} else {
//MULT - Signed multiply
value = (uint16_t)((int8_t)ReadSrcReg() * (int8_t)operand);
}
WriteDestReg(value);
_state.SFR.Sign = (value & 0x8000) != 0;
_state.SFR.Zero = value == 0;
ResetFlags();
Step(_state.HighSpeedMode ? 1 : 2);
}
void Gsu::FMultLMult()
{
uint32_t multResult = (int16_t)ReadSrcReg() * (int16_t)_state.R[6];
if(_state.SFR.Alt1) {
//LMULT - "16x16 signed multiply", LSB in R4, MSB in DREG
_state.R[4] = multResult;
}
uint16_t value = multResult >> 16;
WriteDestReg(value);
_state.SFR.Carry = (multResult & 0x8000);
_state.SFR.Sign = (value & 0x8000);
_state.SFR.Zero = (value == 0);
ResetFlags();
Step((_state.HighSpeedMode ? 3 : 7) * (_state.ClockSelect ? 1 : 2));
}
void Gsu::AndBitClear(uint8_t reg)
{
uint16_t operand;
if(_state.SFR.Alt2) {
//Immediate value
operand = reg;
} else {
operand = _state.R[reg];
}
uint16_t value;
if(_state.SFR.Alt1) {
//Bit clear
value = ReadSrcReg() & ~operand;
} else {
//AND
value = ReadSrcReg() & operand;
}
WriteDestReg(value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x8000) != 0;
ResetFlags();
}
void Gsu::SBK()
{
//"Store word data, last RAM address used"
WriteRam(_state.RamAddress, (uint8_t)ReadSrcReg());
WriteRam(_state.RamAddress ^ 1, ReadSrcReg() >> 8);
ResetFlags();
}
void Gsu::LINK(uint8_t value)
{
//"Link Return Address"
_state.R[11] = _state.R[15] + value;
ResetFlags();
}
void Gsu::SignExtend()
{
int16_t value = (int8_t)ReadSrcReg();
WriteDestReg((uint16_t)value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x8000) != 0;
ResetFlags();
}
void Gsu::NOT()
{
uint16_t value = ~ReadSrcReg();
WriteDestReg(value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x8000) != 0;
ResetFlags();
}
void Gsu::LSR()
{
uint16_t src = ReadSrcReg();
_state.SFR.Carry = (src & 0x01) != 0;
uint16_t dst = src >> 1;
WriteDestReg(dst);
_state.SFR.Zero = dst == 0;
_state.SFR.Sign = (dst & 0x8000) != 0;
ResetFlags();
}
void Gsu::ROL()
{
uint16_t src = ReadSrcReg();
uint16_t dst = (src << 1) | (uint8_t)_state.SFR.Carry;
_state.SFR.Carry = (src & 0x8000) != 0;
WriteDestReg(dst);
_state.SFR.Zero = dst == 0;
_state.SFR.Sign = (dst & 0x8000) != 0;
ResetFlags();
}
void Gsu::ASR()
{
uint16_t src = ReadSrcReg();
_state.SFR.Carry = (src & 0x01) != 0;
uint16_t dst = (int16_t)src >> 1;
if(_state.SFR.Alt1) {
dst += (src + 1) >> 16;
}
WriteDestReg(dst);
_state.SFR.Zero = dst == 0;
_state.SFR.Sign = (dst & 0x8000) != 0;
ResetFlags();
}
void Gsu::ROR()
{
uint16_t src = ReadSrcReg();
uint16_t dst = (src >> 1) | ((int)_state.SFR.Carry << 15);
_state.SFR.Carry = (src & 0x01) != 0;
WriteDestReg(dst);
_state.SFR.Zero = dst == 0;
_state.SFR.Sign = (dst & 0x8000) != 0;
ResetFlags();
}
void Gsu::LOB()
{
//"Value of low byte of register"
uint8_t value = (uint8_t)ReadSrcReg();
WriteDestReg(value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x80) != 0;
ResetFlags();
}
void Gsu::HIB()
{
//"Value of high byte of register"
uint8_t value = (uint8_t)(ReadSrcReg() >> 8);
WriteDestReg(value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x80) != 0;
ResetFlags();
}
void Gsu::IbtSmsLms(uint8_t reg)
{
if(_state.SFR.Alt1) {
//LMS - "Load word data from RAM, short address"
_state.RamAddress = ReadOperand() << 1;
uint8_t lsb = ReadRamBuffer(_state.RamAddress);
uint8_t msb = ReadRamBuffer(_state.RamAddress | 0x01);
WriteRegister(reg, (msb << 8) | lsb);
} else if(_state.SFR.Alt2) {
//SMS - "Store word data to RAM, short address"
_state.RamAddress = ReadOperand() << 1;
WriteRam(_state.RamAddress, (uint8_t)_state.R[reg]);
WriteRam(_state.RamAddress | 0x01, _state.R[reg] >> 8);
} else {
//IBT - "Load immediate byte data"
WriteRegister(reg, (int8_t)ReadOperand());
}
ResetFlags();
}
void Gsu::IwtLmSm(uint8_t reg)
{
if(_state.SFR.Alt1) {
//LM - Load memory
_state.RamAddress = ReadOperand();
_state.RamAddress |= ReadOperand() << 8;
uint8_t lsb = ReadRamBuffer(_state.RamAddress);
uint8_t msb = ReadRamBuffer(_state.RamAddress ^ 0x01);
WriteRegister(reg, (msb << 8) | lsb);
} else if(_state.SFR.Alt2) {
//SM - Store Memory
_state.RamAddress = ReadOperand();
_state.RamAddress |= ReadOperand() << 8;
WriteRam(_state.RamAddress, (uint8_t)_state.R[reg]);
WriteRam(_state.RamAddress ^ 0x01, _state.R[reg] >> 8);
} else {
//IWT - Load immediate word
uint8_t lsb = ReadOperand();
uint8_t msb = ReadOperand();
WriteRegister(reg, (msb << 8) | lsb);
}
ResetFlags();
}
void Gsu::OrXor(uint8_t operand)
{
uint16_t operandValue;
if(_state.SFR.Alt2) {
//Immediate value
operandValue = operand;
} else {
//Indirect register value
operandValue = _state.R[operand];
}
uint16_t value;
if(_state.SFR.Alt1) {
//XOR
value = ReadSrcReg() ^ operandValue;
} else {
//OR
value = ReadSrcReg() | operandValue;
}
WriteDestReg(value);
_state.SFR.Zero = value == 0;
_state.SFR.Sign = (value & 0x8000) != 0;
ResetFlags();
}
void Gsu::INC(uint8_t reg)
{
WriteRegister(reg, _state.R[reg] + 1);
_state.SFR.Zero = _state.R[reg] == 0;
_state.SFR.Sign = (_state.R[reg] & 0x8000) != 0;
ResetFlags();
}
void Gsu::DEC(uint8_t reg)
{
WriteRegister(reg, _state.R[reg] - 1);
_state.SFR.Zero = _state.R[reg] == 0;
_state.SFR.Sign = (_state.R[reg] & 0x8000) != 0;
ResetFlags();
}
void Gsu::GetCRamBRomB()
{
if(!_state.SFR.Alt2) {
//GETC - "Get byte from ROM to color register"
_state.ColorReg = GetColor(ReadRomBuffer());
} else if(!_state.SFR.Alt1) {
//RAMB - "Set RAM data bank"
WaitRamOperation();
_state.RamBank = ReadSrcReg() & 0x01;
} else {
//ROMB - "Set ROM data bank"
WaitRomOperation();
_state.RomBank = ReadSrcReg() & 0x7F;
}
ResetFlags();
}
void Gsu::GETB()
{
if(_state.SFR.Alt2 && _state.SFR.Alt1) {
//GETBS - "Get signed byte from ROM buffer"
WriteDestReg((int8_t)ReadRomBuffer());
} else if(_state.SFR.Alt2) {
//GETBL - "Get low byte from ROM buffer"
WriteDestReg((ReadSrcReg() & 0xFF00) | ReadRomBuffer());
} else if(_state.SFR.Alt1) {
//GETBH - "Get high byte from ROM buffer"
WriteDestReg((ReadSrcReg() & 0xFF) | (ReadRomBuffer() << 8));
} else {
//GETB - "Get byte from ROM buffer"
WriteDestReg(ReadRomBuffer());
}
ResetFlags();
}
void Gsu::PlotRpix()
{
if(_state.SFR.Alt1) {
//RPIX - "Read pixel color"
uint8_t value = ReadPixel((uint8_t)_state.R[1], (uint8_t)_state.R[2]);
_state.SFR.Zero = (value == 0);
_state.SFR.Sign = (value & 0x8000);
WriteDestReg(value);
} else {
//PLOT
DrawPixel((uint8_t)_state.R[1], (uint8_t)_state.R[2]);
_state.R[1]++;
}
ResetFlags();
}
void Gsu::ColorCMode()
{
if(_state.SFR.Alt1) {
//CMODE - "Set plot mode"
uint8_t value = (uint8_t)ReadSrcReg();
_state.PlotTransparent = (value & 0x01) != 0;
_state.PlotDither = (value & 0x02) != 0;
_state.ColorHighNibble = (value & 0x04) != 0;
_state.ColorFreezeHigh = (value & 0x08) != 0;
_state.ObjMode = (value & 0x10) != 0;
} else {
//COLOR - "Set plot color"
_state.ColorReg = GetColor((uint8_t)ReadSrcReg());
}
ResetFlags();
}
uint16_t Gsu::GetTileIndex(uint8_t x, uint8_t y)
{
switch(_state.ObjMode ? 3 : _state.ScreenHeight) {
default:
case 0: return ((x & 0xF8) << 1) + ((y & 0xF8) >> 3); break;
case 1: return ((x & 0xF8) << 1) + ((x & 0xF8) >> 1) + ((y & 0xF8) >> 3); break;
case 2: return ((x & 0xF8) << 1) + ((x & 0xF8) << 0) + ((y & 0xF8) >> 3); break;
case 3: return ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break;
}
}
uint32_t Gsu::GetTileAddress(uint8_t x, uint8_t y)
{
uint16_t tileIndex = GetTileIndex(x, y);
return (0x700000 | (_state.ScreenBase << 10)) + (tileIndex * (_state.PlotBpp << 3)) + ((y & 0x07) * 2);
}
uint8_t Gsu::ReadPixel(uint8_t x, uint8_t y)
{
WritePixelCache(_state.SecondaryCache);
WritePixelCache(_state.PrimaryCache);
uint32_t tileAddress = GetTileAddress(x, y);
x = (x & 7) ^ 7;
uint8_t data = 0;
for(int i = 0; i < _state.PlotBpp; i++) {
//Select which byte to read/write based on the current bit (0/1, 16/17, 32/33, 48/49)
uint8_t byteOffset = ((i >> 1) << 4) + (i & 0x01);
data |= ((ReadGsu(tileAddress + byteOffset, MemoryOperationType::Read) >> x) & 1) << i;
Step(_state.ClockSelect ? 5 : 6);
}
return data;
}
bool Gsu::IsTransparentPixel()
{
uint8_t color = _state.ColorFreezeHigh ? (_state.ColorReg & 0x0F) : _state.ColorReg;
switch(_state.PlotBpp) {
default:
case 2: return (color & 0x03) == 0;
case 4: return (color & 0x0F) == 0;
case 8: return color == 0;
}
}
void Gsu::DrawPixel(uint8_t x, uint8_t y)
{
if(!_state.PlotTransparent && IsTransparentPixel()) {
return;
}
uint8_t color = _state.ColorReg;
if(_state.PlotDither && _state.PlotBpp != 8) {
if((x ^ y) & 0x01) {
color >>= 4;
}
color &= 0x0F;
}
if(_state.PrimaryCache.X != (x & 0xF8) || _state.PrimaryCache.Y != y) {
FlushPrimaryCache(x, y);
}
uint8_t xOffset = (x & 7) ^ 7;
_state.PrimaryCache.Pixels[xOffset] = color;
_state.PrimaryCache.ValidBits |= (1 << xOffset);
if(_state.PrimaryCache.ValidBits == 0xFF) {
FlushPrimaryCache(x, y);
}
}
void Gsu::FlushPrimaryCache(uint8_t x, uint8_t y)
{
WritePixelCache(_state.SecondaryCache);
_state.SecondaryCache = _state.PrimaryCache;
_state.PrimaryCache.ValidBits = 0;
_state.PrimaryCache.X = x & 0xF8;
_state.PrimaryCache.Y = y;
}
void Gsu::WritePixelCache(GsuPixelCache &cache)
{
if(cache.ValidBits == 0) {
return;
}
uint32_t tileAddress = GetTileAddress(cache.X, cache.Y);
for(int i = 0; i < _state.PlotBpp; i++) {
uint8_t value = 0;
for(int x = 0; x < 8; x++) {
value |= ((cache.Pixels[x] >> i) & 0x01) << x;
}
//Select which byte to read/write based on the current bit (0/1, 16/17, 32/33, 48/49)
uint8_t byte = ((i >> 1) << 4) + (i & 0x01);
if(cache.ValidBits != 0xFF) {
//Read the pixels in memory before to merge them before writing the cache's content to memory
Step(_state.ClockSelect ? 5 : 6);
value &= cache.ValidBits;
value |= ReadGsu(tileAddress + byte, MemoryOperationType::Read) & ~cache.ValidBits;
}
Step(_state.ClockSelect ? 5 : 6);
WaitForRamAccess();
WriteGsu(tileAddress + byte, value, MemoryOperationType::Write);
}
cache.ValidBits = 0;
}
uint8_t Gsu::GetColor(uint8_t value)
{
if(_state.ColorHighNibble) {
return (_state.ColorReg & 0xF0) | (value >> 4);
}
if(_state.ColorFreezeHigh) {
return (_state.ColorReg & 0xF0) | (value & 0x0F);
}
return value;
}