mirror of
https://github.com/risingPhil/PokeMe64.git
synced 2026-09-07 16:25:10 -05:00
126 lines
4.2 KiB
C++
Executable File
126 lines
4.2 KiB
C++
Executable File
#ifndef _TRANSFERPAKMANAGER_H
|
|
#define _TRANSFERPAKMANAGER_H
|
|
|
|
#include <libdragon.h>
|
|
|
|
#ifdef __GNUC__
|
|
#define likely(x) __builtin_expect(!!(x), 1)
|
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
#else
|
|
#define likely(x) (x)
|
|
#define unlikely(x) (x)
|
|
#endif
|
|
|
|
enum class TransferPakMode
|
|
{
|
|
ROM,
|
|
RAM
|
|
};
|
|
|
|
/** @brief Transfer Pak command block size (32 bytes) */
|
|
#define TPAK_BLOCK_SIZE 0x20
|
|
|
|
/**
|
|
* @brief This class manages the N64 transfer pak
|
|
* Both SRAM and ROM access are implemented in the same class here
|
|
* because the transfer pak itself has some shenanigans:
|
|
* - it has its own banking mechanism independent of the cartridge banks -> It can only access 16 KB of the gameboy address space at a time. However, libdragon
|
|
* takes care of most of the heavy lifting.
|
|
* - It can't do single byte read/writes. Everything must be done in multiples of 32 bytes.
|
|
* - switching transfer pak banks influences whether you are reading/writing the right addresses for ROM or SRAM.
|
|
*
|
|
* So you can't implement ROM and SRAM reading in separate classes.
|
|
* TransferPakManager manages and keeps track of all this and abstracts this complexity.
|
|
*/
|
|
class TransferPakManager
|
|
{
|
|
public:
|
|
TransferPakManager();
|
|
~TransferPakManager();
|
|
|
|
joypad_port_t getPort() const;
|
|
void setPort(joypad_port_t port);
|
|
|
|
bool hasTransferPak();
|
|
bool isPoweredOn() const;
|
|
bool setPower(bool on);
|
|
uint8_t getStatus();
|
|
|
|
bool readCartridgeHeader(gameboy_cartridge_header& cartridgeHeader);
|
|
|
|
/**
|
|
* @brief This function switches the Gameboy ROM bank index
|
|
* WARNING: it switches to transfer pak bank 0
|
|
*/
|
|
void switchGBROMBank(uint8_t bankIndex);
|
|
|
|
/**
|
|
* @brief Returns whether we called setRAMEnabled(true) on this TransferPakManager instance before
|
|
*/
|
|
bool isRAMEnabled() const;
|
|
|
|
/**
|
|
* @brief This function enables/disables gameboy RAM/RTC access.
|
|
* WARNING: it switches to transfer pak bank 0
|
|
*/
|
|
void setRAMEnabled(bool enabled);
|
|
|
|
/**
|
|
* @brief This function switches the Gameboy RAM bank index
|
|
* WARNING: it switches to transfer pak bank 1
|
|
*/
|
|
void switchGBSRAMBank(uint8_t bankIndex);
|
|
|
|
/**
|
|
* For MBC1 controllers, this will effect the ROM or RAM modes, depending on what is written in $6000-$7FFF.
|
|
* If the mode is 0 (default), the RAM bank will be locked to 0, but the extended banks (bankindex > 0x1F)
|
|
* can be accessed in the 0x0000 - 0x3FFF range (thereby replacing bank 0) by modifying the 0x4000-0x5FFF registers.
|
|
* However, in this mode, the RAM bank is locked to bank 0
|
|
*
|
|
* If the mode is 1, the RAM bank can be switched, but the extended rom banks (bankindex > 0x1F) can't be switched to.
|
|
* In this mode 0x0000 - 0x3FFF is locked to rom bank 0.
|
|
* Sources:
|
|
* - https://retrocomputing.stackexchange.com/questions/11732/how-does-the-gameboys-memory-bank-switching-work
|
|
* - https://gbdev.io/pandocs/MBC1.html
|
|
*
|
|
* For our use case though, we don't need mode 0.
|
|
*/
|
|
void switchMBC1BankingMode(uint8_t mode);
|
|
|
|
/**
|
|
* @brief This function reads data from the specified gameboy address
|
|
*/
|
|
void read(uint16_t gbAddress, uint8_t* data, uint16_t size);
|
|
|
|
/**
|
|
* @brief This function reads data from the specified SRAM bank offset
|
|
*/
|
|
void readSRAM(uint16_t SRAMBankOffset, uint8_t *data, uint16_t size);
|
|
|
|
/**
|
|
* @brief This function writes the given data to the given SRAMBankOffset
|
|
* The write is cached and won't be written until a new write outside the current 32 byte block
|
|
* is triggered or finishWrites() is called by the user.
|
|
*
|
|
* WARNING: Don't forget to call finishWrites() when you're done writing! Otherwise corruption will occur
|
|
* due to the cached writes
|
|
*/
|
|
void writeSRAM(uint16_t SRAMBankOffset, const uint8_t *data, uint16_t size);
|
|
|
|
/**
|
|
* @brief This function writes the current writeBuffer immediately
|
|
*/
|
|
void finishWrites();
|
|
protected:
|
|
private:
|
|
joypad_port_t port_;
|
|
bool isPoweredOn_;
|
|
bool ramEnabled_;
|
|
uint8_t currentSRAMBank_;
|
|
uint16_t readBufferBankOffset_;
|
|
uint16_t writeBufferSRAMBankOffset_;
|
|
uint8_t readBuffer_[TPAK_BLOCK_SIZE];
|
|
uint8_t writeBuffer_[TPAK_BLOCK_SIZE];
|
|
};
|
|
|
|
#endif |