public bool Tick() { var spriteAddress = 0xfe00 + 4 * _i; switch (_state) { case State.ReadingY: _spriteY = _oemRam.GetByte(spriteAddress); _state = State.ReadingX; break; case State.ReadingX: _spriteX = _oemRam.GetByte(spriteAddress + 1); if (_spritePosIndex < _sprites.Length && Between(_spriteY, _registers.Get(GpuRegister.Ly) + 16, _spriteY + _lcdc.GetSpriteHeight())) { _sprites[_spritePosIndex++] = new SpritePosition(_spriteX, _spriteY, spriteAddress); } _i++; _state = State.ReadingY; break; } return(_i < 40); }
private void DisplayProgress() { if (_cpu.State == State.OPCODE && _mem.GetByte(_registers.PC) == 0x22 && _registers.HL >= 0x9800 && _registers.HL < 0x9c00) { if (_registers.A != 0) { _os.Write(_registers.A); } } else if (IsByteSequenceAtPc(0x7d, 0xe6, 0x1f, 0xee, 0x1f)) { _os.Write('\n'); } }
private static void CopyValues(IAddressSpace addressSpace, int from, int to, int length) { for (var i = length - 1; i >= 0; i--) { var b = addressSpace.GetByte(0xfe00 + from + i) % 0xff; addressSpace.SetByte(0xfe00 + to + i, b); } }
private int GetTestResult(Gameboy gb) { IAddressSpace mem = gb.Mmu; if (!_testStarted) { var i = 0xa000; foreach (var v in new[] { 0x80, 0xde, 0xb0, 0x61 }) { if (mem.GetByte(i++) != v) { return(0x80); } } _testStarted = true; } int status = mem.GetByte(0xa000); if (gb.Cpu.State != State.OPCODE) { return(status); } var reg = gb.Cpu.Registers; int ii = reg.PC; foreach (int v in new int[] { 0xe5, 0xf5, 0xfa, 0x83, 0xd8 }) { if (mem.GetByte(ii++) != v) { return(status); } } var c = (char)reg.A; _text.Append(c); _os?.Write(c); reg.PC += 0x19; return(status); }
public static bool IsInfiniteLoop(Gameboy gb) { Cpu cpu = gb.Cpu; if (cpu.State != State.OPCODE) { return(false); } Registers regs = cpu.Registers; IAddressSpace mem = gb.Mmu; int i = regs.PC; bool found = true; foreach (int v in new int[] { 0x18, 0xfe }) { // jr fe if (mem.GetByte(i++) != v) { found = false; break; } } if (found) { return(true); } i = regs.PC; foreach (int v in new int[] { 0xc3, BitUtils.GetLsb(i), BitUtils.GetMsb(i) }) { // jp pc if (mem.GetByte(i++) != v) { return(false); } } return(true); }
public int GetByte(int address) { switch (_dmgBootstrap) { case 0 when !Gbc && (address >= 0x0000 && address < 0x0100): return(BootRom.GameboyClassic[address]); case 0 when Gbc && address >= 0x000 && address < 0x0100: return(BootRom.GameboyColor[address]); case 0 when Gbc && address >= 0x200 && address < 0x0900: return(BootRom.GameboyColor[address - 0x0100]); } return(address == 0xff50 ? 0xff : _addressSpace.GetByte(address)); }
public void Tick() { if (!_transferInProgress) { return; } if (++_ticks < 648 / _speedMode.GetSpeedMode()) { return; } _transferInProgress = false; _restarted = false; _ticks = 0; for (var i = 0; i < 0xa0; i++) { _oam.SetByte(0xfe00 + i, _addressSpace.GetByte(_from + i)); } }
public int GetByte(int address) => _addressSpace.GetByte(Translate(address));
public void Tick() { _clockCycle++; var speed = _speedMode.GetSpeedMode(); if (_clockCycle >= (4 / speed)) { _clockCycle = 0; } else { return; } if (State == State.OPCODE || State == State.HALTED || State == State.STOPPED) { if (_interruptManager.IsIme() && _interruptManager.IsInterruptRequested()) { if (State == State.STOPPED) { _display.Enabled = true; } State = State.IRQ_READ_IF; } } switch (State) { case State.IRQ_READ_IF: case State.IRQ_READ_IE: case State.IRQ_PUSH_1: case State.IRQ_PUSH_2: case State.IRQ_JUMP: HandleInterrupt(); return; case State.HALTED when _interruptManager.IsInterruptRequested(): State = State.OPCODE; break; } if (State == State.HALTED || State == State.STOPPED) { return; } var accessedMemory = false; while (true) { var pc = Registers.PC; switch (State) { case State.OPCODE: ClearState(); _opcode1 = _addressSpace.GetByte(pc); accessedMemory = true; if (_opcode1 == 0xcb) { State = State.EXT_OPCODE; } else if (_opcode1 == 0x10) { CurrentOpcode = _opcodes.Commands[_opcode1]; State = State.EXT_OPCODE; } else { State = State.OPERAND; CurrentOpcode = _opcodes.Commands[_opcode1]; if (CurrentOpcode == null) { throw new InvalidOperationException($"No command for 0x{_opcode1:X2}"); } } if (!_haltBugMode) { Registers.IncrementPc(); } else { _haltBugMode = false; } break; case State.EXT_OPCODE: if (accessedMemory) { return; } accessedMemory = true; _opcode2 = _addressSpace.GetByte(pc); CurrentOpcode ??= _opcodes.ExtCommands[_opcode2]; if (CurrentOpcode == null) { throw new InvalidOperationException($"No command for {_opcode2:X}cb 0x{_opcode2:X2}"); } State = State.OPERAND; Registers.IncrementPc(); break; case State.OPERAND: while (_operandIndex < CurrentOpcode.Length) { if (accessedMemory) { return; } accessedMemory = true; _operand[_operandIndex++] = _addressSpace.GetByte(pc); Registers.IncrementPc(); } _ops = CurrentOpcode.Ops.ToList(); State = State.RUNNING; break; case State.RUNNING: if (_opcode1 == 0x10) { if (_speedMode.OnStop()) { State = State.OPCODE; } else { State = State.STOPPED; _display.Enabled = false; } return; } else if (_opcode1 == 0x76) { if (_interruptManager.IsHaltBug()) { State = State.OPCODE; _haltBugMode = true; return; } else { State = State.HALTED; return; } } if (_opIndex < _ops.Count) { var op = _ops[_opIndex]; var opAccessesMemory = op.ReadsMemory() || op.WritesMemory(); if (accessedMemory && opAccessesMemory) { return; } _opIndex++; var corruptionType = op.CausesOemBug(Registers, _opContext); if (corruptionType != null) { HandleSpriteBug(corruptionType.Value); } _opContext = op.Execute(Registers, _addressSpace, _operand, _opContext); op.SwitchInterrupts(_interruptManager); if (!op.Proceed(Registers)) { _opIndex = _ops.Count; break; } if (op.ForceFinishCycle()) { return; } if (opAccessesMemory) { accessedMemory = true; } } if (_opIndex >= _ops.Count) { State = State.OPCODE; _operandIndex = 0; _interruptManager.OnInstructionFinished(); return; } break; case State.HALTED: case State.STOPPED: return; } } }
public void Tick() { if (_fetchingDisabled && _state == State.ReadTileId) { if (_fifo.GetLength() <= 8) { _fifo.Enqueue8Pixels(EmptyPixelLine, _tileAttributes); } return; } if (--_divider == 0) { _divider = 2; } else { return; } stateSwitch: switch (_state) { case State.ReadTileId: _tileId = _videoRam0.GetByte(_mapAddress + _xOffset); _tileAttributes = _gbc ? TileAttributes.ValueOf(_videoRam1.GetByte(_mapAddress + _xOffset)) : TileAttributes.Empty; _state = State.ReadData1; break; case State.ReadData1: _tileData1 = GetTileData(_tileId, _tileLine, 0, _tileDataAddress, _tileIdSigned, _tileAttributes, 8); _state = State.ReadData2; break; case State.ReadData2: _tileData2 = GetTileData(_tileId, _tileLine, 1, _tileDataAddress, _tileIdSigned, _tileAttributes, 8); _state = State.Push; goto stateSwitch; // Sorry mum case State.Push: if (_fifo.GetLength() <= 8) { _fifo.Enqueue8Pixels(Zip(_tileData1, _tileData2, _tileAttributes.IsXFlip()), _tileAttributes); _xOffset = (_xOffset + 1) % 0x20; _state = State.ReadTileId; } break; case State.ReadSpriteTileId: _tileId = _oemRam.GetByte(_sprite.GetAddress() + 2); _state = State.ReadSpriteFlags; break; case State.ReadSpriteFlags: _spriteAttributes = TileAttributes.ValueOf(_oemRam.GetByte(_sprite.GetAddress() + 3)); _state = State.ReadSpriteData1; break; case State.ReadSpriteData1: if (_lcdc.GetSpriteHeight() == 16) { _tileId &= 0xfe; } _tileData1 = GetTileData(_tileId, _spriteTileLine, 0, 0x8000, false, _spriteAttributes, _lcdc.GetSpriteHeight()); _state = State.ReadSpriteData2; break; case State.ReadSpriteData2: _tileData2 = GetTileData(_tileId, _spriteTileLine, 1, 0x8000, false, _spriteAttributes, _lcdc.GetSpriteHeight()); _state = State.PushSprite; break; case State.PushSprite: _fifo.SetOverlay(Zip(_tileData1, _tileData2, _spriteAttributes.IsXFlip()), _spriteOffset, _spriteAttributes, _spriteOamIndex); _state = State.ReadTileId; break; } }