mirror of
https://github.com/MoleskiCoder/EightBit.git
synced 2024-12-26 03:32:51 +00:00
73a34f4fae
Signed-off-by: Adrian.Conlon <adrian.conlon@gmail.com>
234 lines
4.5 KiB
C++
234 lines
4.5 KiB
C++
#pragma once
|
|
|
|
#include "Memory.h"
|
|
#include "Processor.h"
|
|
|
|
namespace EightBit {
|
|
class Bus : public Memory {
|
|
public:
|
|
|
|
enum CartridgeType {
|
|
ROM = 0,
|
|
ROM_MBC1 = 1,
|
|
ROM_MBC1_RAM = 2,
|
|
ROM_MBC1_RAM_BATTERY = 3,
|
|
};
|
|
|
|
enum {
|
|
TotalLineCount = 154,
|
|
RomPageSize = 0x4000
|
|
};
|
|
|
|
enum {
|
|
|
|
BASE = 0xFF00,
|
|
|
|
// Port/Mode Registers
|
|
P1 = 0x0,
|
|
SB = 0x1,
|
|
SC = 0x2,
|
|
DIV = 0x4,
|
|
TIMA = 0x5,
|
|
TMA = 0x6,
|
|
TAC = 0x7,
|
|
|
|
// Interrupt Flags
|
|
IF = 0xF,
|
|
IE = 0xFF,
|
|
|
|
// LCD Display Registers
|
|
LCDC = 0x40,
|
|
STAT = 0x41,
|
|
SCY = 0x42,
|
|
SCX = 0x43,
|
|
LY = 0x44,
|
|
LYC = 0x45,
|
|
DMA = 0x46,
|
|
BGP = 0x47,
|
|
OBP0 = 0x48,
|
|
OBP1 = 0x49,
|
|
WY = 0x4A,
|
|
WX = 0x4B,
|
|
|
|
// Sound Registers
|
|
NR10 = 0x10,
|
|
NR11 = 0x11,
|
|
NR12 = 0x12,
|
|
NR13 = 0x13,
|
|
NR14 = 0x14,
|
|
NR21 = 0x16,
|
|
NR22 = 0x17,
|
|
NR23 = 0x18,
|
|
NR24 = 0x19,
|
|
NR30 = 0x1A,
|
|
NR31 = 0x1B,
|
|
NR32 = 0x1C,
|
|
NR33 = 0x1D,
|
|
NR34 = 0x1E,
|
|
NR41 = 0x20,
|
|
NR42 = 0x21,
|
|
NR43 = 0x22,
|
|
NR44 = 0x23,
|
|
NR50 = 0x24,
|
|
NR51 = 0x25,
|
|
NR52 = 0x26,
|
|
|
|
WPRAM_START = 0x30,
|
|
WPRAM_END = 0x3F,
|
|
|
|
// 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
|
|
Keypad = Processor::Bit3 // Hi-Lo 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 {
|
|
CpuAccessAllowed = 0b00,
|
|
VerticalBlankingPeriod = 0b01,
|
|
SearchingOamRam = 0b10,
|
|
TransferringDataToLcd = 0b11
|
|
};
|
|
|
|
Bus();
|
|
|
|
void reset();
|
|
|
|
virtual void clear() override;
|
|
|
|
void triggerInterrupt(int cause) {
|
|
pokeRegister(IF, peekRegister(IF) | cause);
|
|
}
|
|
|
|
void writeRegister(int offset, uint8_t content) {
|
|
Memory::write(BASE + offset, content);
|
|
}
|
|
|
|
void pokeRegister(int offset, uint8_t content) {
|
|
poke(BASE + offset, content);
|
|
}
|
|
|
|
uint8_t readRegister(int offset) {
|
|
return Memory::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:
|
|
__assume(0);
|
|
}
|
|
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() {
|
|
auto current = peekRegister(DIV);
|
|
pokeRegister(DIV, ++current);
|
|
}
|
|
|
|
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 disableBootRom() { m_disableBootRom = true; }
|
|
void enableBootRom() { m_disableBootRom = false; }
|
|
|
|
void disableGameRom() { m_disableGameRom = true; }
|
|
void enableGameRom() { m_disableGameRom = false; }
|
|
|
|
bool bootRomDisabled() const { return m_disableBootRom; }
|
|
bool bootRomEnabled() const { return !bootRomDisabled(); }
|
|
|
|
bool gameRomDisabled() const { return m_disableGameRom; }
|
|
bool gameRomEnabled() const { return !gameRomDisabled(); }
|
|
|
|
void loadBootRom(const std::string& path);
|
|
void loadGameRom(const std::string& path);
|
|
|
|
private:
|
|
std::vector<uint8_t> m_bootRom;
|
|
std::vector<uint8_t> m_gameRom;
|
|
|
|
bool m_disableBootRom;
|
|
bool m_disableGameRom;
|
|
|
|
bool m_rom;
|
|
bool m_banked;
|
|
bool m_ram;
|
|
bool m_battery;
|
|
|
|
bool m_higherRomBank;
|
|
bool m_ramBankSwitching;
|
|
|
|
int m_romBank;
|
|
int m_ramBank;
|
|
|
|
int m_divCounter;
|
|
int m_timerCounter;
|
|
int m_timerRate;
|
|
|
|
void Bus_WrittenByte(const AddressEventArgs& e);
|
|
|
|
void checkDiv(int cycles);
|
|
void checkTimer(int cycles);
|
|
|
|
void validateCartridgeType();
|
|
|
|
virtual uint8_t& reference(uint16_t address, bool& rom);
|
|
};
|
|
} |