mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2025-01-25 00:30:48 +00:00
Split the bus into IoRegisters and "the rest"
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
This commit is contained in:
parent
e8715b941b
commit
156cb66904
@ -1,13 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <cstdint>
|
||||||
#include <tuple>
|
#include <vector>
|
||||||
|
|
||||||
#include <Rom.h>
|
#include <Rom.h>
|
||||||
#include <Ram.h>
|
#include <Ram.h>
|
||||||
#include <Bus.h>
|
#include <Bus.h>
|
||||||
#include <Processor.h>
|
|
||||||
#include <Signal.h>
|
#include "IoRegisters.h"
|
||||||
|
|
||||||
namespace EightBit {
|
namespace EightBit {
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
@ -30,257 +30,25 @@ namespace EightBit {
|
|||||||
RomPageSize = 0x4000
|
RomPageSize = 0x4000
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
|
|
||||||
BASE = 0xFF00,
|
|
||||||
|
|
||||||
// Port/Mode Registers
|
|
||||||
P1 = 0x0, // R/W Mask5
|
|
||||||
SB = 0x1, // R/W Mask8
|
|
||||||
SC = 0x2, // R/W Bit7 | Bit0
|
|
||||||
|
|
||||||
// Timer control
|
|
||||||
DIV = 0x4, // R/W Mask8
|
|
||||||
TIMA = 0x5, // R/W Mask8
|
|
||||||
TMA = 0x6, // R/W Mask8
|
|
||||||
TAC = 0x7, // R/W Mask3
|
|
||||||
|
|
||||||
// Interrupt Flags
|
|
||||||
IF = 0xF, // R/W Mask5
|
|
||||||
IE = 0xFF, // R/W Mask5
|
|
||||||
|
|
||||||
// Sound Registers
|
|
||||||
NR10 = 0x10, // R/W Mask7
|
|
||||||
NR11 = 0x11, // R/W Bit7 | Bit6
|
|
||||||
NR12 = 0x12, // R/W Mask8
|
|
||||||
NR13 = 0x13, // W 0
|
|
||||||
NR14 = 0x14, // R/W Bit6
|
|
||||||
NR21 = 0x16, // R/W Bit7 | Bit6
|
|
||||||
NR22 = 0x17, // R/W Mask8
|
|
||||||
NR23 = 0x18, // W 0
|
|
||||||
NR24 = 0x19, // R/W Bit6
|
|
||||||
NR30 = 0x1A, // R/W Bit7
|
|
||||||
NR31 = 0x1B, // R/W Mask8
|
|
||||||
NR32 = 0x1C, // R/W Bit6 | Bit5
|
|
||||||
NR33 = 0x1D, // W 0
|
|
||||||
NR34 = 0x1E, // R/W Bit6
|
|
||||||
NR41 = 0x20, // R/W Mask6
|
|
||||||
NR42 = 0x21, // R/W Mask8
|
|
||||||
NR43 = 0x22, // R/W Mask8
|
|
||||||
NR44 = 0x23, // R/W Bit6
|
|
||||||
NR50 = 0x24, // R/W Mask8
|
|
||||||
NR51 = 0x25, // R/W Mask8
|
|
||||||
NR52 = 0x26, // R/W Mask8 Mask8
|
|
||||||
|
|
||||||
WAVE_PATTERN_RAM_START = 0x30,
|
|
||||||
WAVE_PATTERN_RAM_END = 0x3F,
|
|
||||||
|
|
||||||
// LCD Display Registers
|
|
||||||
LCDC = 0x40, // R/W Mask8
|
|
||||||
STAT = 0x41, // R/W Mask7
|
|
||||||
SCY = 0x42, // R/W Mask8
|
|
||||||
SCX = 0x43, // R/W Mask8
|
|
||||||
LY = 0x44, // R Mask8 zeroed
|
|
||||||
LYC = 0x45, // R/W Mask8
|
|
||||||
DMA = 0x46, // W 0
|
|
||||||
BGP = 0x47, // R/W Mask8
|
|
||||||
OBP0 = 0x48, // R/W Mask8
|
|
||||||
OBP1 = 0x49, // R/W Mask8
|
|
||||||
WY = 0x4A, // R/W Mask8
|
|
||||||
WX = 0x4B, // R/W Mask8
|
|
||||||
|
|
||||||
// Boot rom control
|
|
||||||
BOOT_DISABLE = 0x50,
|
|
||||||
};
|
|
||||||
|
|
||||||
// IF and IE flags
|
|
||||||
enum Interrupts {
|
|
||||||
VerticalBlank = Processor::Bit0, // VBLANK
|
|
||||||
DisplayControlStatus = Processor::Bit1, // LCDC Status
|
|
||||||
TimerOverflow = Processor::Bit2, // Timer Overflow
|
|
||||||
SerialTransfer = Processor::Bit3, // Serial Transfer
|
|
||||||
KeypadPressed = Processor::Bit4 // Hi-Lo transition of P10-P13
|
|
||||||
};
|
|
||||||
|
|
||||||
enum LcdcControl {
|
|
||||||
DisplayBackground = Processor::Bit0,
|
|
||||||
ObjectEnable = Processor::Bit1,
|
|
||||||
ObjectBlockCompositionSelection = Processor::Bit2,
|
|
||||||
BackgroundCodeAreaSelection = Processor::Bit3,
|
|
||||||
BackgroundCharacterDataSelection = Processor::Bit4,
|
|
||||||
WindowEnable = Processor::Bit5,
|
|
||||||
WindowCodeAreaSelection = Processor::Bit6,
|
|
||||||
LcdEnable = Processor::Bit7
|
|
||||||
};
|
|
||||||
|
|
||||||
enum LcdStatusMode {
|
|
||||||
HBlank = 0b00,
|
|
||||||
VBlank = 0b01,
|
|
||||||
SearchingOamRam = 0b10,
|
|
||||||
TransferringDataToLcd = 0b11
|
|
||||||
};
|
|
||||||
|
|
||||||
Bus();
|
Bus();
|
||||||
|
|
||||||
Signal<int> DisplayStatusModeUpdated;
|
|
||||||
|
|
||||||
Ram& VRAM() { return m_videoRam; }
|
Ram& VRAM() { return m_videoRam; }
|
||||||
Ram& OAMRAM() { return m_oamRam; }
|
Ram& OAMRAM() { return m_oamRam; }
|
||||||
|
IoRegisters& IO() { return m_ioPorts; }
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void triggerInterrupt(int cause) {
|
|
||||||
pokeRegister(IF, peekRegister(IF) | cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeRegister(int offset, uint8_t content) {
|
|
||||||
write(BASE + offset, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pokeRegister(int offset, uint8_t content) {
|
|
||||||
poke(BASE + offset, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t readRegister(int offset) {
|
|
||||||
return read(BASE + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t peekRegister(int offset) {
|
|
||||||
return peek(BASE + offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void checkTimers(int cycles);
|
|
||||||
|
|
||||||
int timerClockTicks() {
|
|
||||||
switch (timerClock()) {
|
|
||||||
case 0b00:
|
|
||||||
return 1024; // 4.096 Khz
|
|
||||||
case 0b01:
|
|
||||||
return 16; // 262.144 Khz
|
|
||||||
case 0b10:
|
|
||||||
return 64; // 65.536 Khz
|
|
||||||
case 0b11:
|
|
||||||
return 256; // 16.384 Khz
|
|
||||||
default:
|
|
||||||
UNREACHABLE;
|
|
||||||
}
|
|
||||||
throw std::domain_error("Invalid timer clock specification");
|
|
||||||
}
|
|
||||||
|
|
||||||
int timerClock() {
|
|
||||||
return peekRegister(TAC) & Processor::Mask2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool timerEnabled() {
|
|
||||||
return !timerDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool timerDisabled() {
|
|
||||||
return (peekRegister(TAC) & Processor::Bit2) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void incrementDIV(int cycles) {
|
|
||||||
m_divCounter.word += cycles;
|
|
||||||
pokeRegister(DIV, m_divCounter.high);
|
|
||||||
}
|
|
||||||
|
|
||||||
void incrementTIMA() {
|
|
||||||
uint16_t updated = peekRegister(TIMA) + 1;
|
|
||||||
if (updated & Processor::Bit8) {
|
|
||||||
triggerInterrupt(TimerOverflow);
|
|
||||||
updated = peekRegister(TMA);
|
|
||||||
}
|
|
||||||
pokeRegister(TIMA, updated & Processor::Mask8);
|
|
||||||
}
|
|
||||||
|
|
||||||
void incrementLY() {
|
|
||||||
pokeRegister(LY, (peekRegister(LY) + 1) % TotalLineCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetLY() {
|
|
||||||
pokeRegister(LY, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void transferDma() {
|
|
||||||
if (m_dmaTransferActive) {
|
|
||||||
OAMRAM().poke(m_dmaAddress.low, peek(m_dmaAddress.word));
|
|
||||||
m_dmaTransferActive = ++m_dmaAddress.low < 0xa0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateLcdStatusMode(int mode) {
|
|
||||||
const auto current = m_ioPorts.peek(STAT) & ~Processor::Mask2;
|
|
||||||
m_ioPorts.poke(STAT, current | mode);
|
|
||||||
DisplayStatusModeUpdated.fire(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void disableBootRom() { m_disableBootRom = true; }
|
|
||||||
void enableBootRom() { m_disableBootRom = false; }
|
|
||||||
|
|
||||||
void disableGameRom() { m_disableGameRom = true; }
|
void disableGameRom() { m_disableGameRom = true; }
|
||||||
void enableGameRom() { m_disableGameRom = false; }
|
void enableGameRom() { m_disableGameRom = false; }
|
||||||
|
|
||||||
bool bootRomDisabled() const { return m_disableBootRom; }
|
|
||||||
bool bootRomEnabled() const { return !bootRomDisabled(); }
|
|
||||||
|
|
||||||
bool gameRomDisabled() const { return m_disableGameRom; }
|
bool gameRomDisabled() const { return m_disableGameRom; }
|
||||||
bool gameRomEnabled() const { return !gameRomDisabled(); }
|
bool gameRomEnabled() const { return !gameRomDisabled(); }
|
||||||
|
|
||||||
void loadBootRom(const std::string& path);
|
void loadBootRom(const std::string& path);
|
||||||
void loadGameRom(const std::string& path);
|
void loadGameRom(const std::string& path);
|
||||||
|
|
||||||
void pressRight() { m_p14 = m_p10 = false; triggerKeypadInterrupt(); }
|
|
||||||
void releaseRight() { m_p14 = m_p10 = true; }
|
|
||||||
|
|
||||||
void pressLeft() { m_p14 = m_p11 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseLeft() { m_p14 = m_p11 = true; }
|
|
||||||
|
|
||||||
void pressUp() { m_p14 = m_p12 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseUp() { m_p14 = m_p12 = true; }
|
|
||||||
|
|
||||||
void pressDown() { m_p14 = m_p13 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseDown() { m_p14 = m_p13 = true; }
|
|
||||||
|
|
||||||
void pressA() { m_p15 = m_p10 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseA() { m_p15 = m_p10 = true; }
|
|
||||||
|
|
||||||
void pressB() { m_p15 = m_p11 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseB() { m_p15 = m_p11 = true; }
|
|
||||||
|
|
||||||
void pressSelect() { m_p15 = m_p12 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseSelect() { m_p15 = m_p12 = true; }
|
|
||||||
|
|
||||||
void pressStart() { m_p15 = m_p13 = false, triggerKeypadInterrupt(); }
|
|
||||||
void releaseStart() { m_p15 = m_p13 = true; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual uint8_t& reference(uint16_t address, bool& rom) {
|
virtual uint8_t& reference(uint16_t address, bool& rom);
|
||||||
|
|
||||||
rom = true;
|
|
||||||
if ((address < 0x100) && bootRomEnabled())
|
|
||||||
return m_bootRom.reference(address);
|
|
||||||
if ((address < 0x4000) && gameRomEnabled())
|
|
||||||
return m_gameRomBanks[0].reference(address);
|
|
||||||
if ((address < 0x8000) && gameRomEnabled())
|
|
||||||
return m_gameRomBanks[m_romBank].reference(address - 0x4000);
|
|
||||||
|
|
||||||
rom = false;
|
|
||||||
if (address < 0xa000)
|
|
||||||
return VRAM().reference(address - 0x8000);
|
|
||||||
if (address < 0xc000)
|
|
||||||
return m_ramBanks.size() == 0 ? rom = true, placeDATA(0xff) : m_ramBanks[m_ramBank].reference(address - 0xa000);
|
|
||||||
if (address < 0xe000)
|
|
||||||
return m_lowInternalRam.reference(address - 0xc000);
|
|
||||||
if (address < 0xfe00)
|
|
||||||
return m_lowInternalRam.reference(address - 0xe000); // Low internal RAM mirror
|
|
||||||
if (address < 0xfea0)
|
|
||||||
return OAMRAM().reference(address - 0xfe00);
|
|
||||||
if (address < 0xff00)
|
|
||||||
return rom = true, placeDATA(0xff);
|
|
||||||
if (address < 0xff80)
|
|
||||||
return m_ioPorts.reference(address - 0xff00);
|
|
||||||
return m_highInternalRam.reference(address - 0xff80);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Rom m_bootRom; // 0x0000 - 0x00ff
|
Rom m_bootRom; // 0x0000 - 0x00ff
|
||||||
@ -289,10 +57,9 @@ namespace EightBit {
|
|||||||
std::vector<Ram> m_ramBanks; // 0xa000 - 0xbfff (switchable)
|
std::vector<Ram> m_ramBanks; // 0xa000 - 0xbfff (switchable)
|
||||||
Ram m_lowInternalRam; // 0xc000 - 0xdfff (mirrored at 0xe000)
|
Ram m_lowInternalRam; // 0xc000 - 0xdfff (mirrored at 0xe000)
|
||||||
Ram m_oamRam; // 0xfe00 - 0xfe9f
|
Ram m_oamRam; // 0xfe00 - 0xfe9f
|
||||||
Ram m_ioPorts; // 0xff00 - 0xff7f
|
IoRegisters m_ioPorts; // 0xff00 - 0xff7f
|
||||||
Ram m_highInternalRam; // 0xff80 - 0xffff
|
Ram m_highInternalRam; // 0xff80 - 0xffff
|
||||||
|
|
||||||
bool m_disableBootRom;
|
|
||||||
bool m_disableGameRom;
|
bool m_disableGameRom;
|
||||||
|
|
||||||
bool m_rom;
|
bool m_rom;
|
||||||
@ -306,41 +73,9 @@ namespace EightBit {
|
|||||||
int m_romBank;
|
int m_romBank;
|
||||||
int m_ramBank;
|
int m_ramBank;
|
||||||
|
|
||||||
register16_t m_divCounter;
|
|
||||||
int m_timerCounter;
|
|
||||||
int m_timerRate;
|
|
||||||
|
|
||||||
register16_t m_dmaAddress;
|
|
||||||
bool m_dmaTransferActive;
|
|
||||||
|
|
||||||
bool m_scanP15;
|
|
||||||
bool m_scanP14;
|
|
||||||
|
|
||||||
bool m_p15; // misc keys
|
|
||||||
bool m_p14; // direction keys
|
|
||||||
bool m_p13; // down/start
|
|
||||||
bool m_p12; // up/select
|
|
||||||
bool m_p11; // left/b
|
|
||||||
bool m_p10; // right/a
|
|
||||||
|
|
||||||
void checkTimer(int cycles);
|
|
||||||
|
|
||||||
void validateCartridgeType();
|
void validateCartridgeType();
|
||||||
|
|
||||||
void mask(uint16_t address, uint8_t masking) {
|
|
||||||
poke(address, peek(address) | ~masking);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mask(uint8_t masking) {
|
|
||||||
mask(ADDRESS().word, masking);
|
|
||||||
}
|
|
||||||
|
|
||||||
void triggerKeypadInterrupt() {
|
|
||||||
triggerInterrupt(Interrupts::KeypadPressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bus_WrittenByte(uint16_t address);
|
void Bus_WrittenByte(uint16_t address);
|
||||||
void Bus_ReadingByte(uint16_t address);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
202
LR35902/inc/IoRegisters.h
Normal file
202
LR35902/inc/IoRegisters.h
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <Ram.h>
|
||||||
|
#include <Processor.h>
|
||||||
|
#include <Register.h>
|
||||||
|
#include <Signal.h>
|
||||||
|
|
||||||
|
namespace EightBit {
|
||||||
|
namespace GameBoy {
|
||||||
|
|
||||||
|
class Bus;
|
||||||
|
|
||||||
|
class IoRegisters : public EightBit::Ram {
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
|
||||||
|
BASE = 0xFF00,
|
||||||
|
|
||||||
|
// Port/Mode Registers
|
||||||
|
P1 = 0x0, // R/W Mask5
|
||||||
|
SB = 0x1, // R/W Mask8
|
||||||
|
SC = 0x2, // R/W Bit7 | Bit0
|
||||||
|
|
||||||
|
// Timer control
|
||||||
|
DIV = 0x4, // R/W Mask8
|
||||||
|
TIMA = 0x5, // R/W Mask8
|
||||||
|
TMA = 0x6, // R/W Mask8
|
||||||
|
TAC = 0x7, // R/W Mask3
|
||||||
|
|
||||||
|
// Interrupt Flags
|
||||||
|
IF = 0xF, // R/W Mask5
|
||||||
|
IE = 0xFF, // R/W Mask5
|
||||||
|
|
||||||
|
// Sound Registers
|
||||||
|
NR10 = 0x10, // R/W Mask7
|
||||||
|
NR11 = 0x11, // R/W Bit7 | Bit6
|
||||||
|
NR12 = 0x12, // R/W Mask8
|
||||||
|
NR13 = 0x13, // W 0
|
||||||
|
NR14 = 0x14, // R/W Bit6
|
||||||
|
NR21 = 0x16, // R/W Bit7 | Bit6
|
||||||
|
NR22 = 0x17, // R/W Mask8
|
||||||
|
NR23 = 0x18, // W 0
|
||||||
|
NR24 = 0x19, // R/W Bit6
|
||||||
|
NR30 = 0x1A, // R/W Bit7
|
||||||
|
NR31 = 0x1B, // R/W Mask8
|
||||||
|
NR32 = 0x1C, // R/W Bit6 | Bit5
|
||||||
|
NR33 = 0x1D, // W 0
|
||||||
|
NR34 = 0x1E, // R/W Bit6
|
||||||
|
NR41 = 0x20, // R/W Mask6
|
||||||
|
NR42 = 0x21, // R/W Mask8
|
||||||
|
NR43 = 0x22, // R/W Mask8
|
||||||
|
NR44 = 0x23, // R/W Bit6
|
||||||
|
NR50 = 0x24, // R/W Mask8
|
||||||
|
NR51 = 0x25, // R/W Mask8
|
||||||
|
NR52 = 0x26, // R/W Mask8 Mask8
|
||||||
|
|
||||||
|
WAVE_PATTERN_RAM_START = 0x30,
|
||||||
|
WAVE_PATTERN_RAM_END = 0x3F,
|
||||||
|
|
||||||
|
// LCD Display Registers
|
||||||
|
LCDC = 0x40, // R/W Mask8
|
||||||
|
STAT = 0x41, // R/W Mask7
|
||||||
|
SCY = 0x42, // R/W Mask8
|
||||||
|
SCX = 0x43, // R/W Mask8
|
||||||
|
LY = 0x44, // R Mask8 zeroed
|
||||||
|
LYC = 0x45, // R/W Mask8
|
||||||
|
DMA = 0x46, // W 0
|
||||||
|
BGP = 0x47, // R/W Mask8
|
||||||
|
OBP0 = 0x48, // R/W Mask8
|
||||||
|
OBP1 = 0x49, // R/W Mask8
|
||||||
|
WY = 0x4A, // R/W Mask8
|
||||||
|
WX = 0x4B, // R/W Mask8
|
||||||
|
|
||||||
|
// Boot rom control
|
||||||
|
BOOT_DISABLE = 0x50,
|
||||||
|
};
|
||||||
|
|
||||||
|
// IF and IE flags
|
||||||
|
enum Interrupts {
|
||||||
|
VerticalBlank = Processor::Bit0, // VBLANK
|
||||||
|
DisplayControlStatus = Processor::Bit1, // LCDC Status
|
||||||
|
TimerOverflow = Processor::Bit2, // Timer Overflow
|
||||||
|
SerialTransfer = Processor::Bit3, // Serial Transfer
|
||||||
|
KeypadPressed = Processor::Bit4 // Hi-Lo transition of P10-P13
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LcdcControl {
|
||||||
|
DisplayBackground = Processor::Bit0,
|
||||||
|
ObjectEnable = Processor::Bit1,
|
||||||
|
ObjectBlockCompositionSelection = Processor::Bit2,
|
||||||
|
BackgroundCodeAreaSelection = Processor::Bit3,
|
||||||
|
BackgroundCharacterDataSelection = Processor::Bit4,
|
||||||
|
WindowEnable = Processor::Bit5,
|
||||||
|
WindowCodeAreaSelection = Processor::Bit6,
|
||||||
|
LcdEnable = Processor::Bit7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LcdStatusMode {
|
||||||
|
HBlank = 0b00,
|
||||||
|
VBlank = 0b01,
|
||||||
|
SearchingOamRam = 0b10,
|
||||||
|
TransferringDataToLcd = 0b11
|
||||||
|
};
|
||||||
|
|
||||||
|
IoRegisters(Bus& bus);
|
||||||
|
|
||||||
|
Signal<int> DisplayStatusModeUpdated;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void transferDma();
|
||||||
|
|
||||||
|
void triggerInterrupt(int cause) {
|
||||||
|
poke(IF, peek(IF) | cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkTimers(int cycles);
|
||||||
|
|
||||||
|
int timerClockTicks();
|
||||||
|
|
||||||
|
int timerClock();
|
||||||
|
bool timerEnabled();
|
||||||
|
bool timerDisabled();
|
||||||
|
|
||||||
|
void incrementDIV(int cycles);
|
||||||
|
void incrementTIMA();
|
||||||
|
|
||||||
|
void incrementLY();
|
||||||
|
void resetLY();
|
||||||
|
|
||||||
|
void updateLcdStatusMode(int mode);
|
||||||
|
|
||||||
|
void disableBootRom() { m_disableBootRom = true; }
|
||||||
|
void enableBootRom() { m_disableBootRom = false; }
|
||||||
|
|
||||||
|
bool bootRomDisabled() const { return m_disableBootRom; }
|
||||||
|
bool bootRomEnabled() const { return !bootRomDisabled(); }
|
||||||
|
|
||||||
|
void pressRight() { m_p14 = m_p10 = false; triggerKeypadInterrupt(); }
|
||||||
|
void releaseRight() { m_p14 = m_p10 = true; }
|
||||||
|
|
||||||
|
void pressLeft() { m_p14 = m_p11 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseLeft() { m_p14 = m_p11 = true; }
|
||||||
|
|
||||||
|
void pressUp() { m_p14 = m_p12 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseUp() { m_p14 = m_p12 = true; }
|
||||||
|
|
||||||
|
void pressDown() { m_p14 = m_p13 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseDown() { m_p14 = m_p13 = true; }
|
||||||
|
|
||||||
|
void pressA() { m_p15 = m_p10 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseA() { m_p15 = m_p10 = true; }
|
||||||
|
|
||||||
|
void pressB() { m_p15 = m_p11 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseB() { m_p15 = m_p11 = true; }
|
||||||
|
|
||||||
|
void pressSelect() { m_p15 = m_p12 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseSelect() { m_p15 = m_p12 = true; }
|
||||||
|
|
||||||
|
void pressStart() { m_p15 = m_p13 = false, triggerKeypadInterrupt(); }
|
||||||
|
void releaseStart() { m_p15 = m_p13 = true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Bus& m_bus;
|
||||||
|
|
||||||
|
bool m_disableBootRom;
|
||||||
|
|
||||||
|
register16_t m_divCounter;
|
||||||
|
int m_timerCounter;
|
||||||
|
int m_timerRate;
|
||||||
|
|
||||||
|
register16_t m_dmaAddress;
|
||||||
|
bool m_dmaTransferActive;
|
||||||
|
|
||||||
|
bool m_scanP15;
|
||||||
|
bool m_scanP14;
|
||||||
|
|
||||||
|
bool m_p15; // misc keys
|
||||||
|
bool m_p14; // direction keys
|
||||||
|
bool m_p13; // down/start
|
||||||
|
bool m_p12; // up/select
|
||||||
|
bool m_p11; // left/b
|
||||||
|
bool m_p10; // right/a
|
||||||
|
|
||||||
|
void checkTimer(int cycles);
|
||||||
|
|
||||||
|
void mask(uint16_t address, uint8_t masking) {
|
||||||
|
poke(address, peek(address) | ~masking);
|
||||||
|
}
|
||||||
|
|
||||||
|
void triggerKeypadInterrupt() {
|
||||||
|
triggerInterrupt(Interrupts::KeypadPressed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bus_WrittenByte(uint16_t address);
|
||||||
|
void Bus_ReadingByte(uint16_t address);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -579,104 +579,104 @@ std::string EightBit::GameBoy::Disassembler::io(uint8_t value) {
|
|||||||
switch (value) {
|
switch (value) {
|
||||||
|
|
||||||
// Port/Mode Registers
|
// Port/Mode Registers
|
||||||
case Bus::P1:
|
case IoRegisters::P1:
|
||||||
return "P1";
|
return "P1";
|
||||||
case Bus::SB:
|
case IoRegisters::SB:
|
||||||
return "SB";
|
return "SB";
|
||||||
case Bus::SC:
|
case IoRegisters::SC:
|
||||||
return "SC";
|
return "SC";
|
||||||
case Bus::DIV:
|
case IoRegisters::DIV:
|
||||||
return "DIV";
|
return "DIV";
|
||||||
case Bus::TIMA:
|
case IoRegisters::TIMA:
|
||||||
return "TIMA";
|
return "TIMA";
|
||||||
case Bus::TMA:
|
case IoRegisters::TMA:
|
||||||
return "TMA";
|
return "TMA";
|
||||||
case Bus::TAC:
|
case IoRegisters::TAC:
|
||||||
return "TAC";
|
return "TAC";
|
||||||
|
|
||||||
// Interrupt Flags
|
// Interrupt Flags
|
||||||
case Bus::IF:
|
case IoRegisters::IF:
|
||||||
return "IF";
|
return "IF";
|
||||||
case Bus::IE:
|
case IoRegisters::IE:
|
||||||
return "IE";
|
return "IE";
|
||||||
|
|
||||||
// LCD Display Registers
|
// LCD Display Registers
|
||||||
case Bus::LCDC:
|
case IoRegisters::LCDC:
|
||||||
return "LCDC";
|
return "LCDC";
|
||||||
case Bus::STAT:
|
case IoRegisters::STAT:
|
||||||
return "STAT";
|
return "STAT";
|
||||||
case Bus::SCY:
|
case IoRegisters::SCY:
|
||||||
return "SCY";
|
return "SCY";
|
||||||
case Bus::SCX:
|
case IoRegisters::SCX:
|
||||||
return "SCX";
|
return "SCX";
|
||||||
case Bus::LY:
|
case IoRegisters::LY:
|
||||||
return "LY";
|
return "LY";
|
||||||
case Bus::LYC:
|
case IoRegisters::LYC:
|
||||||
return "LYC";
|
return "LYC";
|
||||||
case Bus::DMA:
|
case IoRegisters::DMA:
|
||||||
return "DMA";
|
return "DMA";
|
||||||
case Bus::BGP:
|
case IoRegisters::BGP:
|
||||||
return "BGP";
|
return "BGP";
|
||||||
case Bus::OBP0:
|
case IoRegisters::OBP0:
|
||||||
return "OBP0";
|
return "OBP0";
|
||||||
case Bus::OBP1:
|
case IoRegisters::OBP1:
|
||||||
return "OBP1";
|
return "OBP1";
|
||||||
case Bus::WY:
|
case IoRegisters::WY:
|
||||||
return "WY";
|
return "WY";
|
||||||
case Bus::WX:
|
case IoRegisters::WX:
|
||||||
return "WX";
|
return "WX";
|
||||||
|
|
||||||
// Sound Registers
|
// Sound Registers
|
||||||
case Bus::NR10:
|
case IoRegisters::NR10:
|
||||||
return "NR10";
|
return "NR10";
|
||||||
case Bus::NR11:
|
case IoRegisters::NR11:
|
||||||
return "NR11";
|
return "NR11";
|
||||||
case Bus::NR12:
|
case IoRegisters::NR12:
|
||||||
return "NR12";
|
return "NR12";
|
||||||
case Bus::NR13:
|
case IoRegisters::NR13:
|
||||||
return "NR13";
|
return "NR13";
|
||||||
case Bus::NR14:
|
case IoRegisters::NR14:
|
||||||
return "NR14";
|
return "NR14";
|
||||||
case Bus::NR21:
|
case IoRegisters::NR21:
|
||||||
return "NR21";
|
return "NR21";
|
||||||
case Bus::NR22:
|
case IoRegisters::NR22:
|
||||||
return "NR22";
|
return "NR22";
|
||||||
case Bus::NR23:
|
case IoRegisters::NR23:
|
||||||
return "NR23";
|
return "NR23";
|
||||||
case Bus::NR24:
|
case IoRegisters::NR24:
|
||||||
return "NR24";
|
return "NR24";
|
||||||
case Bus::NR30:
|
case IoRegisters::NR30:
|
||||||
return "NR30";
|
return "NR30";
|
||||||
case Bus::NR31:
|
case IoRegisters::NR31:
|
||||||
return "NR31";
|
return "NR31";
|
||||||
case Bus::NR32:
|
case IoRegisters::NR32:
|
||||||
return "NR32";
|
return "NR32";
|
||||||
case Bus::NR33:
|
case IoRegisters::NR33:
|
||||||
return "NR33";
|
return "NR33";
|
||||||
case Bus::NR34:
|
case IoRegisters::NR34:
|
||||||
return "NR34";
|
return "NR34";
|
||||||
case Bus::NR41:
|
case IoRegisters::NR41:
|
||||||
return "NR41";
|
return "NR41";
|
||||||
case Bus::NR42:
|
case IoRegisters::NR42:
|
||||||
return "NR42";
|
return "NR42";
|
||||||
case Bus::NR43:
|
case IoRegisters::NR43:
|
||||||
return "NR43";
|
return "NR43";
|
||||||
case Bus::NR44:
|
case IoRegisters::NR44:
|
||||||
return "NR44";
|
return "NR44";
|
||||||
case Bus::NR50:
|
case IoRegisters::NR50:
|
||||||
return "NR50";
|
return "NR50";
|
||||||
case Bus::NR51:
|
case IoRegisters::NR51:
|
||||||
return "NR51";
|
return "NR51";
|
||||||
case Bus::NR52:
|
case IoRegisters::NR52:
|
||||||
return "NR52";
|
return "NR52";
|
||||||
|
|
||||||
case Bus::WAVE_PATTERN_RAM_START:
|
case IoRegisters::WAVE_PATTERN_RAM_START:
|
||||||
return "WAVE_PATTERN_RAM_START";
|
return "WAVE_PATTERN_RAM_START";
|
||||||
case Bus::WAVE_PATTERN_RAM_END:
|
case IoRegisters::WAVE_PATTERN_RAM_END:
|
||||||
return "WAVE_PATTERN_RAM_END";
|
return "WAVE_PATTERN_RAM_END";
|
||||||
|
|
||||||
// Boot rom control
|
// Boot rom control
|
||||||
case Bus::BOOT_DISABLE:
|
case IoRegisters::BOOT_DISABLE:
|
||||||
return "BOOT_DISABLE";
|
return "BOOT_DISABLE";
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -24,20 +24,20 @@ void EightBit::GameBoy::Display::initialise() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Display::render() {
|
void EightBit::GameBoy::Display::render() {
|
||||||
m_scanLine = m_bus.peekRegister(Bus::LY);
|
m_scanLine = m_bus.IO().peek(IoRegisters::LY);
|
||||||
if (m_scanLine < RasterHeight) {
|
if (m_scanLine < RasterHeight) {
|
||||||
m_control = m_bus.peekRegister(Bus::LCDC);
|
m_control = m_bus.IO().peek(IoRegisters::LCDC);
|
||||||
if (m_control & Bus::LcdEnable) {
|
if (m_control & IoRegisters::LcdEnable) {
|
||||||
if (m_control & Bus::DisplayBackground)
|
if (m_control & IoRegisters::DisplayBackground)
|
||||||
renderBackground();
|
renderBackground();
|
||||||
if (m_control & Bus::ObjectEnable)
|
if (m_control & IoRegisters::ObjectEnable)
|
||||||
renderObjects();
|
renderObjects();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<int, 4> EightBit::GameBoy::Display::createPalette(const int address) {
|
std::array<int, 4> EightBit::GameBoy::Display::createPalette(const int address) {
|
||||||
const auto raw = m_bus.peekRegister(address);
|
const auto raw = m_bus.IO().peek(address);
|
||||||
std::array<int, 4> palette;
|
std::array<int, 4> palette;
|
||||||
palette[0] = raw & 0b11;
|
palette[0] = raw & 0b11;
|
||||||
palette[1] = (raw & 0b1100) >> 2;
|
palette[1] = (raw & 0b1100) >> 2;
|
||||||
@ -47,12 +47,12 @@ std::array<int, 4> EightBit::GameBoy::Display::createPalette(const int address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Display::renderObjects() {
|
void EightBit::GameBoy::Display::renderObjects() {
|
||||||
const auto objBlockHeight = (m_control & Bus::ObjectBlockCompositionSelection) ? 16 : 8;
|
const auto objBlockHeight = (m_control & IoRegisters::ObjectBlockCompositionSelection) ? 16 : 8;
|
||||||
renderObjects(objBlockHeight);
|
renderObjects(objBlockHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Display::loadObjectAttributes() {
|
void EightBit::GameBoy::Display::loadObjectAttributes() {
|
||||||
const auto objBlockHeight = (m_control & Bus::ObjectBlockCompositionSelection) ? 16 : 8;
|
const auto objBlockHeight = (m_control & IoRegisters::ObjectBlockCompositionSelection) ? 16 : 8;
|
||||||
for (int i = 0; i < 40; ++i) {
|
for (int i = 0; i < 40; ++i) {
|
||||||
m_objectAttributes[i] = ObjectAttribute(m_oam, 4 * i, objBlockHeight);
|
m_objectAttributes[i] = ObjectAttribute(m_oam, 4 * i, objBlockHeight);
|
||||||
}
|
}
|
||||||
@ -61,8 +61,8 @@ void EightBit::GameBoy::Display::loadObjectAttributes() {
|
|||||||
void EightBit::GameBoy::Display::renderObjects(int objBlockHeight) {
|
void EightBit::GameBoy::Display::renderObjects(int objBlockHeight) {
|
||||||
|
|
||||||
std::vector<std::array<int, 4>> palettes(2);
|
std::vector<std::array<int, 4>> palettes(2);
|
||||||
palettes[0] = createPalette(Bus::OBP0);
|
palettes[0] = createPalette(IoRegisters::OBP0);
|
||||||
palettes[1] = createPalette(Bus::OBP1);
|
palettes[1] = createPalette(IoRegisters::OBP1);
|
||||||
|
|
||||||
for (int i = 0; i < 40; ++i) {
|
for (int i = 0; i < 40; ++i) {
|
||||||
|
|
||||||
@ -94,20 +94,20 @@ void EightBit::GameBoy::Display::renderObjects(int objBlockHeight) {
|
|||||||
|
|
||||||
void EightBit::GameBoy::Display::renderBackground() {
|
void EightBit::GameBoy::Display::renderBackground() {
|
||||||
|
|
||||||
auto palette = createPalette(Bus::BGP);
|
auto palette = createPalette(IoRegisters::BGP);
|
||||||
|
|
||||||
const auto window = (m_control & Bus::WindowEnable) != 0;
|
const auto window = (m_control & IoRegisters::WindowEnable) != 0;
|
||||||
const auto bgArea = (m_control & Bus::BackgroundCodeAreaSelection) ? 0x1c00 : 0x1800;
|
const auto bgArea = (m_control & IoRegisters::BackgroundCodeAreaSelection) ? 0x1c00 : 0x1800;
|
||||||
const auto bgCharacters = (m_control & Bus::BackgroundCharacterDataSelection) ? 0 : 0x800;
|
const auto bgCharacters = (m_control & IoRegisters::BackgroundCharacterDataSelection) ? 0 : 0x800;
|
||||||
|
|
||||||
const auto wx = m_bus.peekRegister(Bus::WX);
|
const auto wx = m_bus.IO().peek(IoRegisters::WX);
|
||||||
const auto wy = m_bus.peekRegister(Bus::WY);
|
const auto wy = m_bus.IO().peek(IoRegisters::WY);
|
||||||
|
|
||||||
const auto offsetX = window ? wx - 7 : 0;
|
const auto offsetX = window ? wx - 7 : 0;
|
||||||
const auto offsetY = window ? wy : 0;
|
const auto offsetY = window ? wy : 0;
|
||||||
|
|
||||||
const auto scrollX = m_bus.peekRegister(Bus::SCX);
|
const auto scrollX = m_bus.IO().peek(IoRegisters::SCX);
|
||||||
const auto scrollY = m_bus.peekRegister(Bus::SCY);
|
const auto scrollY = m_bus.IO().peek(IoRegisters::SCY);
|
||||||
|
|
||||||
renderBackground(bgArea, bgCharacters, offsetX - scrollX, offsetY - scrollY, palette);
|
renderBackground(bgArea, bgCharacters, offsetX - scrollX, offsetY - scrollY, palette);
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,8 @@ EightBit::GameBoy::Bus::Bus()
|
|||||||
m_ramBanks(0),
|
m_ramBanks(0),
|
||||||
m_lowInternalRam(0x2000),
|
m_lowInternalRam(0x2000),
|
||||||
m_oamRam(0xa0),
|
m_oamRam(0xa0),
|
||||||
m_ioPorts(0x80),
|
m_ioPorts(*this),
|
||||||
m_highInternalRam(0x80),
|
m_highInternalRam(0x80),
|
||||||
m_disableBootRom(false),
|
|
||||||
m_disableGameRom(false),
|
m_disableGameRom(false),
|
||||||
m_rom(false),
|
m_rom(false),
|
||||||
m_banked(false),
|
m_banked(false),
|
||||||
@ -19,30 +18,12 @@ EightBit::GameBoy::Bus::Bus()
|
|||||||
m_higherRomBank(true),
|
m_higherRomBank(true),
|
||||||
m_ramBankSwitching(false),
|
m_ramBankSwitching(false),
|
||||||
m_romBank(1),
|
m_romBank(1),
|
||||||
m_ramBank(0),
|
m_ramBank(0) {
|
||||||
m_timerCounter(0),
|
|
||||||
m_timerRate(0),
|
|
||||||
m_dmaTransferActive(false),
|
|
||||||
m_scanP15(false),
|
|
||||||
m_scanP14(false),
|
|
||||||
m_p15(true),
|
|
||||||
m_p14(true),
|
|
||||||
m_p13(true),
|
|
||||||
m_p12(true),
|
|
||||||
m_p11(true),
|
|
||||||
m_p10(true) {
|
|
||||||
ReadingByte.connect(std::bind(&GameBoy::Bus::Bus_ReadingByte, this, std::placeholders::_1));
|
|
||||||
WrittenByte.connect(std::bind(&GameBoy::Bus::Bus_WrittenByte, this, std::placeholders::_1));
|
WrittenByte.connect(std::bind(&GameBoy::Bus::Bus_WrittenByte, this, std::placeholders::_1));
|
||||||
m_divCounter.word = 0xabcc;
|
|
||||||
m_dmaAddress.word = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::reset() {
|
void EightBit::GameBoy::Bus::reset() {
|
||||||
|
m_ioPorts.reset();
|
||||||
pokeRegister(NR52, 0xf1);
|
|
||||||
pokeRegister(LCDC, DisplayBackground | BackgroundCharacterDataSelection | LcdEnable);
|
|
||||||
m_divCounter.word = 0xabcc;
|
|
||||||
m_timerCounter = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::loadBootRom(const std::string& path) {
|
void EightBit::GameBoy::Bus::loadBootRom(const std::string& path) {
|
||||||
@ -60,85 +41,10 @@ void EightBit::GameBoy::Bus::loadGameRom(const std::string& path) {
|
|||||||
validateCartridgeType();
|
validateCartridgeType();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::Bus_ReadingByte(const uint16_t address) {
|
|
||||||
auto io = ((address >= BASE) && (address < 0xff80)) || (address == 0xffff);
|
|
||||||
if (io) {
|
|
||||||
auto ioRegister = address - BASE;
|
|
||||||
switch (ioRegister) {
|
|
||||||
|
|
||||||
// Port/Mode Registers
|
|
||||||
case P1: {
|
|
||||||
auto p14 = m_scanP14 && !m_p14;
|
|
||||||
auto p15 = m_scanP15 && !m_p15;
|
|
||||||
auto live = p14 || p15;
|
|
||||||
auto p10 = live && !m_p10;
|
|
||||||
auto p11 = live && !m_p11;
|
|
||||||
auto p12 = live && !m_p12;
|
|
||||||
auto p13 = live && !m_p13;
|
|
||||||
pokeRegister(P1,
|
|
||||||
((int)!p10)
|
|
||||||
| ((int)!p11 << 1)
|
|
||||||
| ((int)!p12 << 2)
|
|
||||||
| ((int)!p13 << 3)
|
|
||||||
| Processor::Bit4 | Processor::Bit5
|
|
||||||
| Processor::Bit6 | Processor::Bit7);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SB:
|
|
||||||
break;
|
|
||||||
case SC:
|
|
||||||
mask(Processor::Bit7 | Processor::Bit0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Timer control
|
|
||||||
case DIV:
|
|
||||||
case TIMA:
|
|
||||||
case TMA:
|
|
||||||
break;
|
|
||||||
case TAC:
|
|
||||||
mask(Processor::Mask3);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Interrupt Flags
|
|
||||||
case IF:
|
|
||||||
mask(Processor::Mask5);
|
|
||||||
break;
|
|
||||||
case IE:
|
|
||||||
// Only the bottom 5 bits are used,
|
|
||||||
// but all are available for use.
|
|
||||||
break;
|
|
||||||
|
|
||||||
// LCD Display Registers
|
|
||||||
case LCDC:
|
|
||||||
break;
|
|
||||||
case STAT:
|
|
||||||
mask(Processor::Mask7);
|
|
||||||
break;
|
|
||||||
case SCY:
|
|
||||||
case SCX:
|
|
||||||
case LY:
|
|
||||||
case LYC:
|
|
||||||
case DMA:
|
|
||||||
case BGP:
|
|
||||||
case OBP0:
|
|
||||||
case OBP1:
|
|
||||||
case WY:
|
|
||||||
case WX:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
mask(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
|
void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
|
||||||
|
|
||||||
const auto value = DATA();
|
const auto value = DATA();
|
||||||
|
|
||||||
auto handled = false;
|
|
||||||
|
|
||||||
switch (address & 0xe000) {
|
switch (address & 0xe000) {
|
||||||
case 0x0000:
|
case 0x0000:
|
||||||
// Register 0: RAMCS gate data
|
// Register 0: RAMCS gate data
|
||||||
@ -152,7 +58,6 @@ void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
|
|||||||
assert((address >= 0x2000) && (address < 0x4000));
|
assert((address >= 0x2000) && (address < 0x4000));
|
||||||
assert((value > 0) && (value < 0x20));
|
assert((value > 0) && (value < 0x20));
|
||||||
m_romBank = value & Processor::Mask5;
|
m_romBank = value & Processor::Mask5;
|
||||||
handled = true;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x4000:
|
case 0x4000:
|
||||||
@ -175,77 +80,9 @@ void EightBit::GameBoy::Bus::Bus_WrittenByte(const uint16_t address) {
|
|||||||
default:
|
default:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
}
|
}
|
||||||
handled = true;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) {
|
|
||||||
switch (address) {
|
|
||||||
|
|
||||||
case BASE + P1:
|
|
||||||
m_scanP14 = (value & Processor::Bit4) == 0;
|
|
||||||
m_scanP15 = (value & Processor::Bit5) == 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BASE + SB: // R/W
|
|
||||||
case BASE + SC: // R/W
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BASE + DIV: // R/W
|
|
||||||
EightBit::Bus::reference() = 0;
|
|
||||||
m_timerCounter = m_divCounter.word = 0;
|
|
||||||
break;
|
|
||||||
case BASE + TIMA: // R/W
|
|
||||||
case BASE + TMA: // R/W
|
|
||||||
break;
|
|
||||||
case BASE + TAC: // R/W
|
|
||||||
m_timerRate = timerClockTicks();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BASE + IF: // R/W
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BASE + LCDC:
|
|
||||||
case BASE + STAT:
|
|
||||||
case BASE + SCY:
|
|
||||||
case BASE + SCX:
|
|
||||||
break;
|
|
||||||
case BASE + DMA:
|
|
||||||
m_dmaAddress.high = value;
|
|
||||||
m_dmaAddress.low = 0;
|
|
||||||
m_dmaTransferActive = true;
|
|
||||||
break;
|
|
||||||
case BASE + LY: // R/O
|
|
||||||
EightBit::Bus::reference() = 0;
|
|
||||||
break;
|
|
||||||
case BASE + BGP:
|
|
||||||
case BASE + OBP0:
|
|
||||||
case BASE + OBP1:
|
|
||||||
case BASE + WY:
|
|
||||||
case BASE + WX:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BASE + BOOT_DISABLE:
|
|
||||||
m_disableBootRom = value != 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::checkTimers(int cycles) {
|
|
||||||
incrementDIV(cycles);
|
|
||||||
checkTimer(cycles);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::checkTimer(int cycles) {
|
|
||||||
if (timerEnabled()) {
|
|
||||||
m_timerCounter -= cycles;
|
|
||||||
if (m_timerCounter <= 0) {
|
|
||||||
m_timerCounter += m_timerRate;
|
|
||||||
incrementTIMA();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EightBit::GameBoy::Bus::validateCartridgeType() {
|
void EightBit::GameBoy::Bus::validateCartridgeType() {
|
||||||
@ -322,3 +159,31 @@ void EightBit::GameBoy::Bus::validateCartridgeType() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t& EightBit::GameBoy::Bus::reference(uint16_t address, bool& rom) {
|
||||||
|
|
||||||
|
rom = true;
|
||||||
|
if ((address < 0x100) && m_ioPorts.bootRomEnabled())
|
||||||
|
return m_bootRom.reference(address);
|
||||||
|
if ((address < 0x4000) && gameRomEnabled())
|
||||||
|
return m_gameRomBanks[0].reference(address);
|
||||||
|
if ((address < 0x8000) && gameRomEnabled())
|
||||||
|
return m_gameRomBanks[m_romBank].reference(address - 0x4000);
|
||||||
|
|
||||||
|
rom = false;
|
||||||
|
if (address < 0xa000)
|
||||||
|
return VRAM().reference(address - 0x8000);
|
||||||
|
if (address < 0xc000)
|
||||||
|
return m_ramBanks.size() == 0 ? rom = true, placeDATA(0xff) : m_ramBanks[m_ramBank].reference(address - 0xa000);
|
||||||
|
if (address < 0xe000)
|
||||||
|
return m_lowInternalRam.reference(address - 0xc000);
|
||||||
|
if (address < 0xfe00)
|
||||||
|
return m_lowInternalRam.reference(address - 0xe000); // Low internal RAM mirror
|
||||||
|
if (address < 0xfea0)
|
||||||
|
return OAMRAM().reference(address - 0xfe00);
|
||||||
|
if (address < IoRegisters::BASE)
|
||||||
|
return rom = true, placeDATA(0xff);
|
||||||
|
if (address < 0xff80)
|
||||||
|
return m_ioPorts.reference(address - IoRegisters::BASE);
|
||||||
|
return m_highInternalRam.reference(address - 0xff80);
|
||||||
|
}
|
||||||
|
234
LR35902/src/IoRegisters.cpp
Normal file
234
LR35902/src/IoRegisters.cpp
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#include "IoRegisters.h"
|
||||||
|
#include "GameBoyBus.h"
|
||||||
|
|
||||||
|
EightBit::GameBoy::IoRegisters::IoRegisters(Bus& bus)
|
||||||
|
: Ram(0x80),
|
||||||
|
m_bus(bus),
|
||||||
|
m_disableBootRom(false),
|
||||||
|
m_timerCounter(0),
|
||||||
|
m_timerRate(0),
|
||||||
|
m_dmaTransferActive(false),
|
||||||
|
m_scanP15(false),
|
||||||
|
m_scanP14(false),
|
||||||
|
m_p15(true),
|
||||||
|
m_p14(true),
|
||||||
|
m_p13(true),
|
||||||
|
m_p12(true),
|
||||||
|
m_p11(true),
|
||||||
|
m_p10(true) {
|
||||||
|
m_bus.ReadingByte.connect(std::bind(&IoRegisters::Bus_ReadingByte, this, std::placeholders::_1));
|
||||||
|
m_bus.WrittenByte.connect(std::bind(&IoRegisters::Bus_WrittenByte, this, std::placeholders::_1));
|
||||||
|
m_divCounter.word = 0xabcc;
|
||||||
|
m_dmaAddress.word = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::reset() {
|
||||||
|
poke(NR52, 0xf1);
|
||||||
|
poke(LCDC, DisplayBackground | BackgroundCharacterDataSelection | LcdEnable);
|
||||||
|
m_divCounter.word = 0xabcc;
|
||||||
|
m_timerCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::transferDma() {
|
||||||
|
if (m_dmaTransferActive) {
|
||||||
|
m_bus.OAMRAM().poke(m_dmaAddress.low, m_bus.peek(m_dmaAddress.word));
|
||||||
|
m_dmaTransferActive = ++m_dmaAddress.low < 0xa0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::Bus_ReadingByte(const uint16_t address) {
|
||||||
|
const auto io = (address >= BASE) && (address < 0xff80);
|
||||||
|
if (io) {
|
||||||
|
auto port = address - BASE;
|
||||||
|
switch (port) {
|
||||||
|
|
||||||
|
// Port/Mode Registers
|
||||||
|
case P1: {
|
||||||
|
auto p14 = m_scanP14 && !m_p14;
|
||||||
|
auto p15 = m_scanP15 && !m_p15;
|
||||||
|
auto live = p14 || p15;
|
||||||
|
auto p10 = live && !m_p10;
|
||||||
|
auto p11 = live && !m_p11;
|
||||||
|
auto p12 = live && !m_p12;
|
||||||
|
auto p13 = live && !m_p13;
|
||||||
|
poke(port,
|
||||||
|
((int)!p10)
|
||||||
|
| ((int)!p11 << 1)
|
||||||
|
| ((int)!p12 << 2)
|
||||||
|
| ((int)!p13 << 3)
|
||||||
|
| Processor::Bit4 | Processor::Bit5
|
||||||
|
| Processor::Bit6 | Processor::Bit7);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SB:
|
||||||
|
break;
|
||||||
|
case SC:
|
||||||
|
mask(port, Processor::Bit7 | Processor::Bit0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Timer control
|
||||||
|
case DIV:
|
||||||
|
case TIMA:
|
||||||
|
case TMA:
|
||||||
|
break;
|
||||||
|
case TAC:
|
||||||
|
mask(port, Processor::Mask3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Interrupt Flags
|
||||||
|
case IF:
|
||||||
|
mask(port, Processor::Mask5);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// LCD Display Registers
|
||||||
|
case LCDC:
|
||||||
|
break;
|
||||||
|
case STAT:
|
||||||
|
mask(port, Processor::Mask7);
|
||||||
|
break;
|
||||||
|
case SCY:
|
||||||
|
case SCX:
|
||||||
|
case LY:
|
||||||
|
case LYC:
|
||||||
|
case DMA:
|
||||||
|
case BGP:
|
||||||
|
case OBP0:
|
||||||
|
case OBP1:
|
||||||
|
case WY:
|
||||||
|
case WX:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
mask(port, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::Bus_WrittenByte(const uint16_t address) {
|
||||||
|
|
||||||
|
const auto value = m_bus.DATA();
|
||||||
|
const auto port = address - BASE;
|
||||||
|
|
||||||
|
switch (port) {
|
||||||
|
|
||||||
|
case P1:
|
||||||
|
m_scanP14 = (value & Processor::Bit4) == 0;
|
||||||
|
m_scanP15 = (value & Processor::Bit5) == 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SB: // R/W
|
||||||
|
case SC: // R/W
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DIV: // R/W
|
||||||
|
poke(port, 0);
|
||||||
|
m_timerCounter = m_divCounter.word = 0;
|
||||||
|
break;
|
||||||
|
case TIMA: // R/W
|
||||||
|
case TMA: // R/W
|
||||||
|
break;
|
||||||
|
case TAC: // R/W
|
||||||
|
m_timerRate = timerClockTicks();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IF: // R/W
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LCDC:
|
||||||
|
case STAT:
|
||||||
|
case SCY:
|
||||||
|
case SCX:
|
||||||
|
break;
|
||||||
|
case DMA:
|
||||||
|
m_dmaAddress.high = value;
|
||||||
|
m_dmaAddress.low = 0;
|
||||||
|
m_dmaTransferActive = true;
|
||||||
|
break;
|
||||||
|
case LY: // R/O
|
||||||
|
poke(port, 0);
|
||||||
|
break;
|
||||||
|
case BGP:
|
||||||
|
case OBP0:
|
||||||
|
case OBP1:
|
||||||
|
case WY:
|
||||||
|
case WX:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BOOT_DISABLE:
|
||||||
|
m_disableBootRom = value != 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::checkTimers(int cycles) {
|
||||||
|
incrementDIV(cycles);
|
||||||
|
checkTimer(cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::checkTimer(int cycles) {
|
||||||
|
if (timerEnabled()) {
|
||||||
|
m_timerCounter -= cycles;
|
||||||
|
if (m_timerCounter <= 0) {
|
||||||
|
m_timerCounter += m_timerRate;
|
||||||
|
incrementTIMA();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int EightBit::GameBoy::IoRegisters::timerClockTicks() {
|
||||||
|
switch (timerClock()) {
|
||||||
|
case 0b00:
|
||||||
|
return 1024; // 4.096 Khz
|
||||||
|
case 0b01:
|
||||||
|
return 16; // 262.144 Khz
|
||||||
|
case 0b10:
|
||||||
|
return 64; // 65.536 Khz
|
||||||
|
case 0b11:
|
||||||
|
return 256; // 16.384 Khz
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
throw std::domain_error("Invalid timer clock specification");
|
||||||
|
}
|
||||||
|
|
||||||
|
int EightBit::GameBoy::IoRegisters::timerClock() {
|
||||||
|
return peek(TAC) & Processor::Mask2;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EightBit::GameBoy::IoRegisters::timerEnabled() {
|
||||||
|
return !timerDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EightBit::GameBoy::IoRegisters::timerDisabled() {
|
||||||
|
return (peek(TAC) & Processor::Bit2) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::incrementDIV(int cycles) {
|
||||||
|
m_divCounter.word += cycles;
|
||||||
|
poke(DIV, m_divCounter.high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::incrementTIMA() {
|
||||||
|
uint16_t updated = peek(TIMA) + 1;
|
||||||
|
if (updated & Processor::Bit8) {
|
||||||
|
triggerInterrupt(TimerOverflow);
|
||||||
|
updated = peek(TMA);
|
||||||
|
}
|
||||||
|
poke(TIMA, updated & Processor::Mask8);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::incrementLY() {
|
||||||
|
poke(LY, (peek(LY) + 1) % GameBoy::Bus::TotalLineCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::resetLY() {
|
||||||
|
poke(LY, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EightBit::GameBoy::IoRegisters::updateLcdStatusMode(int mode) {
|
||||||
|
const auto current = peek(STAT) & ~Processor::Mask2;
|
||||||
|
poke(STAT, current | mode);
|
||||||
|
DisplayStatusModeUpdated.fire(mode);
|
||||||
|
}
|
@ -289,8 +289,8 @@ void EightBit::GameBoy::LR35902::ccf(uint8_t& a, uint8_t& f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int EightBit::GameBoy::LR35902::runRasterLines() {
|
int EightBit::GameBoy::LR35902::runRasterLines() {
|
||||||
m_enabledLCD = !!(m_bus.peekRegister(Bus::LCDC) & Bus::LcdEnable);
|
m_enabledLCD = !!(m_bus.IO().peek(IoRegisters::LCDC) & IoRegisters::LcdEnable);
|
||||||
m_bus.resetLY();
|
m_bus.IO().resetLY();
|
||||||
return runRasterLines(Display::RasterHeight * Bus::CyclesPerLine, Display::RasterHeight);
|
return runRasterLines(Display::RasterHeight * Bus::CyclesPerLine, Display::RasterHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,26 +333,26 @@ int EightBit::GameBoy::LR35902::runRasterLine(int limit) {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
if (m_enabledLCD) {
|
if (m_enabledLCD) {
|
||||||
|
|
||||||
if ((m_bus.peekRegister(Bus::STAT) & Bit6) && (m_bus.peekRegister(Bus::LYC) == m_bus.peekRegister(Bus::LY)))
|
if ((m_bus.IO().peek(IoRegisters::STAT) & Bit6) && (m_bus.IO().peek(IoRegisters::LYC) == m_bus.IO().peek(IoRegisters::LY)))
|
||||||
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
|
m_bus.IO().triggerInterrupt(IoRegisters::Interrupts::DisplayControlStatus);
|
||||||
|
|
||||||
// Mode 2, OAM unavailable
|
// Mode 2, OAM unavailable
|
||||||
m_bus.updateLcdStatusMode(Bus::LcdStatusMode::SearchingOamRam);
|
m_bus.IO().updateLcdStatusMode(IoRegisters::LcdStatusMode::SearchingOamRam);
|
||||||
if (m_bus.peekRegister(Bus::STAT) & Bit5)
|
if (m_bus.IO().peek(IoRegisters::STAT) & Bit5)
|
||||||
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
|
m_bus.IO().triggerInterrupt(IoRegisters::Interrupts::DisplayControlStatus);
|
||||||
count += run(80); // ~19us
|
count += run(80); // ~19us
|
||||||
|
|
||||||
// Mode 3, OAM/VRAM unavailable
|
// Mode 3, OAM/VRAM unavailable
|
||||||
m_bus.updateLcdStatusMode(Bus::LcdStatusMode::TransferringDataToLcd);
|
m_bus.IO().updateLcdStatusMode(IoRegisters::LcdStatusMode::TransferringDataToLcd);
|
||||||
count += run(170); // ~41us
|
count += run(170); // ~41us
|
||||||
|
|
||||||
// Mode 0
|
// Mode 0
|
||||||
m_bus.updateLcdStatusMode(Bus::LcdStatusMode::HBlank);
|
m_bus.IO().updateLcdStatusMode(IoRegisters::LcdStatusMode::HBlank);
|
||||||
if (m_bus.peekRegister(Bus::STAT) & Bit3)
|
if (m_bus.IO().peek(IoRegisters::STAT) & Bit3)
|
||||||
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
|
m_bus.IO().triggerInterrupt(IoRegisters::Interrupts::DisplayControlStatus);
|
||||||
count += run(limit - count); // ~48.6us
|
count += run(limit - count); // ~48.6us
|
||||||
|
|
||||||
m_bus.incrementLY();
|
m_bus.IO().incrementLY();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
count += run(Bus::CyclesPerLine);
|
count += run(Bus::CyclesPerLine);
|
||||||
@ -386,10 +386,10 @@ int EightBit::GameBoy::LR35902::runVerticalBlankLines(int limit, int lines) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (m_enabledLCD) {
|
if (m_enabledLCD) {
|
||||||
m_bus.updateLcdStatusMode(Bus::LcdStatusMode::VBlank);
|
m_bus.IO().updateLcdStatusMode(IoRegisters::LcdStatusMode::VBlank);
|
||||||
if (m_bus.peekRegister(Bus::STAT) & Bit4)
|
if (m_bus.IO().peek(IoRegisters::STAT) & Bit4)
|
||||||
m_bus.triggerInterrupt(Bus::Interrupts::DisplayControlStatus);
|
m_bus.IO().triggerInterrupt(IoRegisters::Interrupts::DisplayControlStatus);
|
||||||
m_bus.triggerInterrupt(Bus::Interrupts::VerticalBlank);
|
m_bus.IO().triggerInterrupt(IoRegisters::Interrupts::VerticalBlank);
|
||||||
}
|
}
|
||||||
return runRasterLines(limit, lines);
|
return runRasterLines(limit, lines);
|
||||||
}
|
}
|
||||||
@ -398,36 +398,36 @@ int EightBit::GameBoy::LR35902::singleStep() {
|
|||||||
|
|
||||||
int current = 0;
|
int current = 0;
|
||||||
|
|
||||||
auto interruptEnable = m_bus.peekRegister(Bus::IE);
|
auto interruptEnable = m_bus.peek(IoRegisters::BASE + IoRegisters::IE);
|
||||||
auto interruptFlags = m_bus.peekRegister(Bus::IF);
|
auto interruptFlags = m_bus.IO().peek(IoRegisters::IF);
|
||||||
auto ime = IME();
|
auto ime = IME();
|
||||||
|
|
||||||
auto masked = interruptEnable & interruptFlags;
|
auto masked = interruptEnable & interruptFlags;
|
||||||
if (masked) {
|
if (masked) {
|
||||||
if (ime) {
|
if (ime) {
|
||||||
m_bus.pokeRegister(Bus::IF, 0);
|
m_bus.IO().poke(IoRegisters::IF, 0);
|
||||||
} else {
|
} else {
|
||||||
if (isHalted())
|
if (isHalted())
|
||||||
proceed();
|
proceed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ime && (masked & Bus::Interrupts::VerticalBlank)) {
|
if (ime && (masked & IoRegisters::Interrupts::VerticalBlank)) {
|
||||||
current += interrupt(0x40);
|
current += interrupt(0x40);
|
||||||
} else if (ime && (masked & Bus::Interrupts::DisplayControlStatus)) {
|
} else if (ime && (masked & IoRegisters::Interrupts::DisplayControlStatus)) {
|
||||||
current += interrupt(0x48);
|
current += interrupt(0x48);
|
||||||
} else if (ime && (masked & Bus::Interrupts::TimerOverflow)) {
|
} else if (ime && (masked & IoRegisters::Interrupts::TimerOverflow)) {
|
||||||
current += interrupt(0x50);
|
current += interrupt(0x50);
|
||||||
} else if (ime && (masked & Bus::Interrupts::SerialTransfer)) {
|
} else if (ime && (masked & IoRegisters::Interrupts::SerialTransfer)) {
|
||||||
current += interrupt(0x58);
|
current += interrupt(0x58);
|
||||||
} else if (ime && (masked & Bus::Interrupts::KeypadPressed)) {
|
} else if (ime && (masked & IoRegisters::Interrupts::KeypadPressed)) {
|
||||||
current += interrupt(0x60);
|
current += interrupt(0x60);
|
||||||
} else {
|
} else {
|
||||||
current += isHalted() ? 1 : step();
|
current += isHalted() ? 1 : step();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bus.checkTimers(current);
|
m_bus.IO().checkTimers(current);
|
||||||
m_bus.transferDma();
|
m_bus.IO().transferDma();
|
||||||
|
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
@ -750,7 +750,7 @@ void EightBit::GameBoy::LR35902::executeOther(int x, int y, int z, int p, int q)
|
|||||||
cycles += 2;
|
cycles += 2;
|
||||||
break;
|
break;
|
||||||
case 4: // GB: LD (FF00 + n),A
|
case 4: // GB: LD (FF00 + n),A
|
||||||
m_bus.writeRegister(fetchByte(), a);
|
m_bus.write(IoRegisters::BASE + fetchByte(), a);
|
||||||
cycles += 3;
|
cycles += 3;
|
||||||
break;
|
break;
|
||||||
case 5: { // GB: ADD SP,dd
|
case 5: { // GB: ADD SP,dd
|
||||||
@ -766,7 +766,7 @@ void EightBit::GameBoy::LR35902::executeOther(int x, int y, int z, int p, int q)
|
|||||||
cycles += 4;
|
cycles += 4;
|
||||||
break;
|
break;
|
||||||
case 6: // GB: LD A,(FF00 + n)
|
case 6: // GB: LD A,(FF00 + n)
|
||||||
a = m_bus.readRegister(fetchByte());
|
a = m_bus.read(IoRegisters::BASE + fetchByte());
|
||||||
cycles += 3;
|
cycles += 3;
|
||||||
break;
|
break;
|
||||||
case 7: { // GB: LD HL,SP + dd
|
case 7: { // GB: LD HL,SP + dd
|
||||||
@ -827,7 +827,7 @@ void EightBit::GameBoy::LR35902::executeOther(int x, int y, int z, int p, int q)
|
|||||||
cycles += 3;
|
cycles += 3;
|
||||||
break;
|
break;
|
||||||
case 4: // GB: LD (FF00 + C),A
|
case 4: // GB: LD (FF00 + C),A
|
||||||
m_bus.writeRegister(C(), a);
|
m_bus.write(IoRegisters::BASE + C(), a);
|
||||||
cycles += 2;
|
cycles += 2;
|
||||||
break;
|
break;
|
||||||
case 5: // GB: LD (nn),A
|
case 5: // GB: LD (nn),A
|
||||||
@ -836,7 +836,7 @@ void EightBit::GameBoy::LR35902::executeOther(int x, int y, int z, int p, int q)
|
|||||||
cycles += 4;
|
cycles += 4;
|
||||||
break;
|
break;
|
||||||
case 6: // GB: LD A,(FF00 + C)
|
case 6: // GB: LD A,(FF00 + C)
|
||||||
a = m_bus.readRegister(C());
|
a = m_bus.read(IoRegisters::BASE + C());
|
||||||
cycles += 2;
|
cycles += 2;
|
||||||
break;
|
break;
|
||||||
case 7: // GB: LD A,(nn)
|
case 7: // GB: LD A,(nn)
|
||||||
|
@ -144,6 +144,7 @@
|
|||||||
<ClInclude Include="..\inc\CharacterDefinition.h" />
|
<ClInclude Include="..\inc\CharacterDefinition.h" />
|
||||||
<ClInclude Include="..\inc\Disassembler.h" />
|
<ClInclude Include="..\inc\Disassembler.h" />
|
||||||
<ClInclude Include="..\inc\Display.h" />
|
<ClInclude Include="..\inc\Display.h" />
|
||||||
|
<ClInclude Include="..\inc\IoRegisters.h" />
|
||||||
<ClInclude Include="..\inc\LR35902.h" />
|
<ClInclude Include="..\inc\LR35902.h" />
|
||||||
<ClInclude Include="..\inc\ObjectAttribute.h" />
|
<ClInclude Include="..\inc\ObjectAttribute.h" />
|
||||||
<ClInclude Include="..\inc\Profiler.h" />
|
<ClInclude Include="..\inc\Profiler.h" />
|
||||||
@ -154,6 +155,7 @@
|
|||||||
<ClCompile Include="GameBoyBus.cpp" />
|
<ClCompile Include="GameBoyBus.cpp" />
|
||||||
<ClCompile Include="Disassembler.cpp" />
|
<ClCompile Include="Disassembler.cpp" />
|
||||||
<ClCompile Include="Display.cpp" />
|
<ClCompile Include="Display.cpp" />
|
||||||
|
<ClCompile Include="IoRegisters.cpp" />
|
||||||
<ClCompile Include="LR35902.cpp" />
|
<ClCompile Include="LR35902.cpp" />
|
||||||
<ClCompile Include="Profiler.cpp" />
|
<ClCompile Include="Profiler.cpp" />
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
|
@ -38,6 +38,9 @@
|
|||||||
<ClInclude Include="..\inc\ObjectAttribute.h">
|
<ClInclude Include="..\inc\ObjectAttribute.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\inc\IoRegisters.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp">
|
<ClCompile Include="stdafx.cpp">
|
||||||
@ -61,5 +64,8 @@
|
|||||||
<ClCompile Include="CharacterDefinition.cpp">
|
<ClCompile Include="CharacterDefinition.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="IoRegisters.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
x
Reference in New Issue
Block a user