added comments
This commit is contained in:
parent
54d6fc8c1e
commit
ad4eafa882
|
@ -7,6 +7,13 @@
|
|||
#include "lcd.hpp"
|
||||
#include "rom.hpp"
|
||||
|
||||
// Why is this typedef? Lol
|
||||
// This was originally a C project believe it or not. I thought getting
|
||||
// this tiny performance increase was worth the trouble, but I guess that
|
||||
// eventually I ran into a problem that couldn't be easily solved in less
|
||||
// than 5 lines of C code. At least I thought so, but anyways, I decided
|
||||
// to rewrite the emulator in C++, that's why all this code looks so
|
||||
// fucked up and messy.
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
|
@ -18,6 +25,9 @@ typedef union
|
|||
} w;
|
||||
} TimerControl;
|
||||
|
||||
|
||||
// JoypadReg = The register in the gameboy
|
||||
// Joypad = A struct to keep the physical keyboard input
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
|
@ -38,32 +48,37 @@ struct Joypad
|
|||
bool a, b, up, down, left, right, start, select;
|
||||
};
|
||||
|
||||
// The Bus class contains all the stuff that I didn't know where else to put
|
||||
class Bus
|
||||
{
|
||||
public:
|
||||
Bus();
|
||||
~Bus();
|
||||
|
||||
// Used to conn
|
||||
void AttachCPU(CPU& cpu);
|
||||
void AttachLCD(LCD& lcd);
|
||||
void InsertROM(ROM& rom);
|
||||
|
||||
bool Tick();
|
||||
bool Execute();
|
||||
bool Frame();
|
||||
bool Tick(); // Execute ONE machine cycle (why would you do that lol)
|
||||
bool Execute(); // Execute ONE CPU instruction (better but still why)
|
||||
bool Frame(); // Execute CPU instructions until we rendered one full frame (there we go)
|
||||
|
||||
BYTE Read(WORD addr);
|
||||
void Write(WORD addr, BYTE val);
|
||||
BYTE Fetch(WORD addr);
|
||||
BYTE Read(WORD addr); // Read from the bus
|
||||
void Write(WORD addr, BYTE val); // Write to the bus
|
||||
BYTE Fetch(WORD addr); // This is literally the same as Read(). Like literally. the. exact. same.
|
||||
// But I use it a lot in the CPU class so I'm too lazy/afraid to remove it
|
||||
|
||||
private:
|
||||
BYTE& GetReference(WORD addr);
|
||||
BYTE& GetReference(WORD addr); // Leftovers of a really really really bad idea, but again it's used in a few places so I'm too scared to remove it
|
||||
|
||||
public:
|
||||
// Connected devices
|
||||
ROM* rom;
|
||||
CPU* cpu;
|
||||
LCD* lcd;
|
||||
|
||||
// These are I/O registers :)
|
||||
BYTE invalid;
|
||||
BYTE div;
|
||||
BYTE tima;
|
||||
|
@ -75,7 +90,6 @@ public:
|
|||
|
||||
Joypad joypad;
|
||||
|
||||
// std::array<BYTE, 0x2000> vram;
|
||||
std::array<BYTE, 0x2000> wram;
|
||||
std::array<BYTE, 0x80> hram;
|
||||
std::array<BYTE, 0x80> hram; // <-- This should be in the CPU class but who cares
|
||||
};
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
class Bus;
|
||||
|
||||
// Structure to represent a register (register = 16 bits, but split into 2 "sub registers" of 8 bits).
|
||||
// I also store the names of the regs for debug purposes
|
||||
struct Register
|
||||
{
|
||||
union
|
||||
|
@ -20,6 +22,7 @@ struct Register
|
|||
char name[3];
|
||||
};
|
||||
|
||||
// Convenience structure for the Interrupts
|
||||
typedef union {
|
||||
BYTE b;
|
||||
struct
|
||||
|
@ -46,6 +49,7 @@ typedef union
|
|||
} f;
|
||||
} StatusFlag;
|
||||
|
||||
// Read about opcode decoding, the link is in the cpu.cpp file
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
|
@ -66,6 +70,8 @@ typedef union
|
|||
} pq;
|
||||
} Opcode;
|
||||
|
||||
|
||||
// Contains everything related to the CPU
|
||||
class CPU
|
||||
{
|
||||
public:
|
||||
|
@ -100,11 +106,12 @@ public:
|
|||
|
||||
bool stopped;
|
||||
bool halted;
|
||||
bool justHaltedWithDI;
|
||||
bool justHaltedWithDI; // I don't even know
|
||||
|
||||
private:
|
||||
void WriteToRegister(BYTE reg, BYTE val);
|
||||
void WriteToRegister(BYTE reg, BYTE val); // The cycles, the god DAMN CPU CYCLES
|
||||
BYTE ReadFromRegister(BYTE reg);
|
||||
void ALU(BYTE operation, BYTE operand);
|
||||
void CBPrefixed();
|
||||
|
||||
void ALU(BYTE operation, BYTE operand); // Handle any ALU related instructions
|
||||
void CBPrefixed(); // Handle all CB prefixed instructions
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
class Bus;
|
||||
|
||||
// bunch of registers or smthn
|
||||
typedef union
|
||||
{
|
||||
BYTE b;
|
||||
|
@ -81,14 +82,13 @@ typedef union
|
|||
} b;
|
||||
} OAMEntry;
|
||||
|
||||
// The screen. With emphasis on ree
|
||||
class LCD
|
||||
{
|
||||
public:
|
||||
void Setup();
|
||||
void Tick();
|
||||
|
||||
BYTE& GetReferenceToAddress(WORD addr, bool& handled);
|
||||
|
||||
bool Read(WORD addr, BYTE& val);
|
||||
bool Write(WORD addr, BYTE val);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "util.hpp"
|
||||
|
||||
// The memory bank controller (MBC) needs to map addresses targeted at rom, to get the appropriate data from the ROM
|
||||
class IMBC
|
||||
{
|
||||
public:
|
||||
|
@ -11,7 +12,7 @@ public:
|
|||
|
||||
virtual ~IMBC() {}
|
||||
|
||||
virtual bool GetMappedRead(WORD address, DWORD& mappedAddr) = 0;
|
||||
virtual bool GetMappedRead(WORD address, DWORD& mappedAddr) = 0; // Convert CPU address to ROM internal address
|
||||
virtual bool GetMappedWrite(WORD address, BYTE val, DWORD& mappedAddr) = 0;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -8,17 +8,7 @@
|
|||
|
||||
class Bus;
|
||||
|
||||
struct MemoryBankController
|
||||
{
|
||||
BYTE w;
|
||||
struct
|
||||
{
|
||||
BYTE ROMBankNumber : 5;
|
||||
BYTE RAMBankNumber : 2;
|
||||
BYTE Mode : 1;
|
||||
} b;
|
||||
};
|
||||
|
||||
// Cartridge
|
||||
class ROM
|
||||
{
|
||||
public:
|
||||
|
|
42
src/bus.cpp
42
src/bus.cpp
|
@ -7,7 +7,7 @@ static WORD timerModuloLookup[4] = { 1024, 16, 64, 256 };
|
|||
|
||||
Bus::Bus()
|
||||
{
|
||||
// 8KB of VRAM
|
||||
// These are some default initializatsrions? We dont *necessarily* need them but eh, who cares
|
||||
invalid = 0;
|
||||
dmg_rom = 0;
|
||||
tac.b = 0;
|
||||
|
@ -50,21 +50,30 @@ void Bus::InsertROM(ROM& r)
|
|||
|
||||
bool Bus::Tick()
|
||||
{
|
||||
// Increase internal counter (used for the divider/timer register)
|
||||
internalCounter++;
|
||||
|
||||
// Tick the CPU forward one cycle if it isn't stopped
|
||||
if(!cpu->stopped)
|
||||
cpu->Tick();
|
||||
|
||||
// LCD and CPU operate on the same clock (I think they do at least,
|
||||
// the gbdev wiki is incredibly inconsistent about the use of the terms
|
||||
// "cycles", "dots" and "clocks" so I just took a guess
|
||||
lcd->Tick();
|
||||
|
||||
// The divider registers increases everytime the internal counter counts to 255
|
||||
if (!(internalCounter % 0xFF))
|
||||
div++;
|
||||
|
||||
// If the timer is enabled and the internal counter hit some number we do some stuff
|
||||
if (tac.w.enable && !(internalCounter % timerModuloLookup[tac.w.select]))
|
||||
{
|
||||
// Like increase the timer register
|
||||
tima++;
|
||||
if (tima == 0x00)
|
||||
{
|
||||
// if the timer overflows set it to tma and issue an interrupt
|
||||
tima = tma;
|
||||
cpu->interruptFlag.flags.timer = 1;
|
||||
}
|
||||
|
@ -75,6 +84,7 @@ bool Bus::Tick()
|
|||
|
||||
bool Bus::Execute()
|
||||
{
|
||||
// just Tick for one CPU instruction
|
||||
while (cpu->cycles > 0)
|
||||
{
|
||||
Tick();
|
||||
|
@ -88,6 +98,7 @@ bool Bus::Execute()
|
|||
|
||||
bool Bus::Frame()
|
||||
{
|
||||
// Just tick for one frame
|
||||
while (lcd->cycles > 0)
|
||||
{
|
||||
Tick();
|
||||
|
@ -101,26 +112,27 @@ bool Bus::Frame()
|
|||
|
||||
BYTE Bus::Read(WORD addr)
|
||||
{
|
||||
// Read from bus
|
||||
BYTE returnVal;
|
||||
if (lcd->Read(addr, returnVal))
|
||||
if (lcd->Read(addr, returnVal)) // If the address is in the LCD realm, then the PPU will handle it
|
||||
{
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
if ((addr >= 0x0000 && addr < 0x8000) || (addr >= 0xA000 && addr < 0xC000))
|
||||
if ((addr >= 0x0000 && addr < 0x8000) || (addr >= 0xA000 && addr < 0xC000)) // If it is in ROM space, the ROM will handle it
|
||||
{
|
||||
return rom->Read(addr);
|
||||
}
|
||||
|
||||
if (addr == 0xFF00)
|
||||
if (addr == 0xFF00) // All other I/O regs are handled the same, except the joypad one because it's weird
|
||||
{
|
||||
if (!joypadReg.w.selectButtonKeys) {
|
||||
if (!joypadReg.w.selectButtonKeys) { // Serious go to the gbdev wiki and read about how this register works
|
||||
joypadReg.w.rightA = joypad.a;
|
||||
joypadReg.w.leftB = joypad.b;
|
||||
joypadReg.w.upSelect = joypad.select;
|
||||
joypadReg.w.downStart = joypad.start;
|
||||
}
|
||||
else if (!joypadReg.w.selectDirKeys)
|
||||
else if (!joypadReg.w.selectDirKeys) // This is the best I could come up with because this is stupid
|
||||
{
|
||||
joypadReg.w.rightA = joypad.right;
|
||||
joypadReg.w.leftB = joypad.left;
|
||||
|
@ -128,39 +140,35 @@ BYTE Bus::Read(WORD addr)
|
|||
joypadReg.w.downStart = joypad.down;
|
||||
}
|
||||
|
||||
return joypadReg.b;
|
||||
return joypadReg.b; // That wasn't a joke, go read about register 0xFF00 in the gameboy
|
||||
}
|
||||
|
||||
return GetReference(addr);
|
||||
return GetReference(addr); // If none of the devices above care about the address, then the bus handles it
|
||||
}
|
||||
|
||||
BYTE Bus::Fetch(WORD addr)
|
||||
{
|
||||
return Read(addr);
|
||||
return Read(addr); // told ya it's literally just Read() lol
|
||||
}
|
||||
|
||||
void Bus::Write(WORD addr, BYTE val)
|
||||
{
|
||||
if (lcd->Write(addr, val))
|
||||
if (lcd->Write(addr, val)) // If the address is in the LCD realm, then the PPU will handle it
|
||||
return;
|
||||
|
||||
if ((addr >= 0x0000 && addr < 0x8000) || (addr >= 0xA000 && addr < 0xC000))
|
||||
if ((addr >= 0x0000 && addr < 0x8000) || (addr >= 0xA000 && addr < 0xC000)) // If it is in ROM space, the ROM will handle it
|
||||
{
|
||||
rom->Write(addr, val);
|
||||
return;
|
||||
}
|
||||
|
||||
GetReference(addr) = val;
|
||||
GetReference(addr) = val; // otherwise the bus will handle it
|
||||
undefined = 0xFF;
|
||||
}
|
||||
|
||||
BYTE& Bus::GetReference(WORD addr)
|
||||
{
|
||||
if (addr >= 0xA000 && addr < 0xC000) // Accessing external RAM
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
else if (addr >= 0xC000 && addr < 0xFE00) // Accessing WRAM / ECHO RAM
|
||||
if (addr >= 0xC000 && addr < 0xFE00) // Accessing WRAM / ECHO RAM
|
||||
{
|
||||
return wram[addr & 0x1FFF];
|
||||
}
|
||||
|
|
25
src/cpu.cpp
25
src/cpu.cpp
|
@ -13,22 +13,24 @@
|
|||
#define DBG_MSG(fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define PUSH(x) bus->Write(--(SP.w), (x))
|
||||
#define POP() bus->Read((SP.w)++)
|
||||
#define REG(z) (cbRegisterTable[z])
|
||||
// Macros are cool amirite
|
||||
#define PUSH(x) bus->Write(--(SP.w), (x)) // Push to stack
|
||||
#define POP() bus->Read((SP.w)++) // Pop from stack
|
||||
#define REG(z) (cbRegisterTable[z]) // Not used even once anywhere
|
||||
|
||||
#define HALF_CARRY_ADD(x, y) (((((x) & 0xF) + ((y) & 0xF)) & 0x10) == 0x10)
|
||||
#define HALF_CARRY_ADD(x, y) (((((x) & 0xF) + ((y) & 0xF)) & 0x10) == 0x10) // Copied from SO
|
||||
#define HALF_CARRY_SUB(x, y) (((((x) & 0xF) - ((y) & 0xF)) & 0x10) == 0x10)
|
||||
|
||||
// Sets up register fully automatically, assigning the strings using the variable name. kinda cool eh?
|
||||
#define SETUP_REGISTER(x) { \
|
||||
x.w = 0x0000; \
|
||||
strcpy(x.name, #x); \
|
||||
}
|
||||
#define REGNAME(x) x->name
|
||||
#define REGNAME_LO(x) x->name[0]
|
||||
#define REGNAME_HI(x) x->name[1]
|
||||
#define REGNAME(x) x->name // Not
|
||||
#define REGNAME_LO(x) x->name[0] // used
|
||||
#define REGNAME_HI(x) x->name[1] // anywhere
|
||||
|
||||
#define XZ_ID(op) (op.xyz.x * 8 + op.xyz.z)
|
||||
#define XZ_ID(op) (op.xyz.x * 8 + op.xyz.z) // what?
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifndef NO_LOG
|
||||
|
@ -74,6 +76,7 @@ void CPU::Powerup()
|
|||
|
||||
void CPU::Tick()
|
||||
{
|
||||
// If halted, then we have to pray to the gods an interrupt occurs to free us from this cursed existence
|
||||
if (halted)
|
||||
{
|
||||
if (interruptEnable.b & interruptFlag.b)
|
||||
|
@ -82,6 +85,7 @@ void CPU::Tick()
|
|||
return;
|
||||
}
|
||||
|
||||
// If we still have cycles left, then come back later and try again
|
||||
totalCycles++;
|
||||
if (cycles != 0)
|
||||
{
|
||||
|
@ -92,6 +96,7 @@ void CPU::Tick()
|
|||
// Check for interrupts
|
||||
if (ime)
|
||||
{
|
||||
// mask the interrupts and check which ones need to be handled
|
||||
BYTE interruptMask = interruptEnable.b & interruptFlag.b;
|
||||
int interruptType = 0;
|
||||
while (!(interruptMask & 0x1) && interruptMask)
|
||||
|
@ -102,15 +107,17 @@ void CPU::Tick()
|
|||
|
||||
if (interruptMask)
|
||||
{
|
||||
// reset interrupt flag
|
||||
interruptFlag.b &= ~(0x1 << interruptType);
|
||||
ime = 0;
|
||||
|
||||
// jump to interrupt vector
|
||||
PUSH(PC.b.hi);
|
||||
PUSH(PC.b.lo);
|
||||
PC.w = interruptVectors[interruptType];
|
||||
|
||||
// Will take 24 machine cycles
|
||||
cycles = 24;
|
||||
printf("INT%02xh\n", interruptVectors[interruptType]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
54
src/lcd.cpp
54
src/lcd.cpp
|
@ -6,6 +6,7 @@
|
|||
|
||||
static BYTE colormap[4] = { 0b00000000, 0b00100101, 0b01001010, 0b10010011 };
|
||||
|
||||
// Reverses a Byte (0111010 -> 0101110)
|
||||
BYTE Reverse(BYTE b) {
|
||||
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
|
||||
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
|
||||
|
@ -13,6 +14,7 @@ BYTE Reverse(BYTE b) {
|
|||
return b;
|
||||
}
|
||||
|
||||
// initializes a bunch of variables
|
||||
void LCD::Setup()
|
||||
{
|
||||
lcdc.b = 0;
|
||||
|
@ -36,18 +38,24 @@ void LCD::Setup()
|
|||
spriteFIFO.full = 0x00;
|
||||
}
|
||||
|
||||
// One LCD tick. Or clock? cycles? who even knows, the wiki uses all of
|
||||
// those terms interchangeably while still insisting they're all different
|
||||
void LCD::Tick()
|
||||
{
|
||||
// Update cycles
|
||||
scanlineCycles++;
|
||||
cycles++;
|
||||
|
||||
// if we're 455 dots into this scanline we gotta wrap back around
|
||||
// and go to the next scanline
|
||||
if (scanlineCycles > 455)
|
||||
{
|
||||
fetcher.cycle = 0;
|
||||
scanlineCycles = 0;
|
||||
ly += 1;
|
||||
|
||||
// if we reached the bottom then we gotta wrap
|
||||
// back up
|
||||
if (ly > 153)
|
||||
{
|
||||
cycles = 0;
|
||||
|
@ -77,9 +85,11 @@ void LCD::Tick()
|
|||
// Screen
|
||||
if (ly >= 0 && ly < 144)
|
||||
{
|
||||
// If we just started this scanline then start the OAM search phase
|
||||
if (scanlineCycles == 0)
|
||||
stat.w.mode = 2;
|
||||
|
||||
// Else if we entered screen space, go to the rendering phase
|
||||
else if (scanlineCycles == 81) {
|
||||
stat.w.mode = 3;
|
||||
bgFIFO.full = 0x00;
|
||||
|
@ -95,6 +105,8 @@ void LCD::Tick()
|
|||
// OAM Search
|
||||
if (stat.w.mode == 2)
|
||||
{
|
||||
// Go through all entries in the OAM table and fetch all sprites that are on the current scanline
|
||||
// if there are more than 10 sprites on the scanline, discard the rest by setting y = 0
|
||||
OAMEntry* entry;
|
||||
int counter = 0;
|
||||
for (int i = 0; i < 40; i++)
|
||||
|
@ -110,26 +122,26 @@ void LCD::Tick()
|
|||
}
|
||||
}
|
||||
|
||||
// Pixel Fetcher
|
||||
// Pixel Fetcher (oh lord)
|
||||
else if (stat.w.mode == 3)
|
||||
{
|
||||
if (lcdc.w.obj_enable)
|
||||
if (lcdc.w.obj_enable) // IF sprite rendering is enabled
|
||||
{
|
||||
OAMEntry* entry;
|
||||
for (int i = 0; i < 40; i++)
|
||||
for (int i = 0; i < 40; i++) // Go through all sprites
|
||||
{
|
||||
entry = (OAMEntry*)(oam.data() + i * 4);
|
||||
|
||||
if (x == entry->b.x - 8 && entry->b.y <= ly + 16 && ly + 8 + (8 * lcdc.w.obj_size) < entry->b.y)
|
||||
if (x == entry->b.x - 8 && entry->b.y <= ly + 16 && ly + 8 + (8 * lcdc.w.obj_size) < entry->b.y) // and if the sprite is rendered on the current coordinate
|
||||
{
|
||||
// Fetch Sprite!
|
||||
WORD yOffset = (ly - entry->b.y + 16);
|
||||
WORD yOffset = (ly - entry->b.y + 16); // offset of the tile data in vram
|
||||
if (entry->b.attr.yFlip)
|
||||
{
|
||||
yOffset = 8 * (1 + lcdc.w.obj_size) - yOffset;
|
||||
yOffset = 8 * (1 + lcdc.w.obj_size) - yOffset; // flip vertically by just doing this
|
||||
}
|
||||
|
||||
BYTE lo = vram[2 * yOffset + (entry->b.idx * 16 * (1 + lcdc.w.obj_size))];
|
||||
BYTE lo = vram[2 * yOffset + (entry->b.idx * 16 * (1 + lcdc.w.obj_size))]; // get lo and hi byte of tile data
|
||||
BYTE hi = vram[2 * yOffset + (entry->b.idx * 16 * (1 + lcdc.w.obj_size)) + 1];
|
||||
if (entry->b.attr.xFlip)
|
||||
{
|
||||
|
@ -137,22 +149,25 @@ void LCD::Tick()
|
|||
hi = Reverse(hi);
|
||||
}
|
||||
|
||||
// Feed it all into the spriteFIFO
|
||||
spriteFIFO.lowByte = lo;
|
||||
spriteFIFO.highByte = hi;
|
||||
spriteFIFO.full = 0xFF;
|
||||
|
||||
BYTE counter = 0;
|
||||
while (spriteFIFO.full)
|
||||
{
|
||||
BYTE color = ((spriteFIFO.highByte & 0x80) >> 6) | ((spriteFIFO.lowByte & 0x80) >> 7);
|
||||
while (spriteFIFO.full) // While theres data in the fifo
|
||||
{
|
||||
BYTE color = ((spriteFIFO.highByte & 0x80) >> 6) | ((spriteFIFO.lowByte & 0x80) >> 7); // Get color of sprite at that pixel
|
||||
|
||||
if (color != 0x00)
|
||||
if (color != 0x00) // if its not transparent
|
||||
{
|
||||
BYTE bgPriority = (bgFIFO.sprite & (0x80 >> counter)) >> 6;
|
||||
BYTE bgPriority = (bgFIFO.sprite & (0x80 >> counter)) >> 6; // See if there is already a sprite rendered at this pixel (if yes,
|
||||
// then dont render over it because sprites with the LOWER x coord get priority)
|
||||
|
||||
if (!bgPriority)
|
||||
{
|
||||
BYTE bgColor = ((bgFIFO.highByte & (0x80 >> counter)) >> 6) | ((bgFIFO.lowByte & (0x80 >> counter)) >> 7);
|
||||
if (entry->b.attr.bgPriority)
|
||||
BYTE bgColor = ((bgFIFO.highByte & (0x80 >> counter)) >> 6) | ((bgFIFO.lowByte & (0x80 >> counter)) >> 7); // Get the color of the background at this pixel
|
||||
if (entry->b.attr.bgPriority) // If the background/window are supposed to have priority do this
|
||||
{
|
||||
if (bgColor == 0x00)
|
||||
{
|
||||
|
@ -170,6 +185,7 @@ void LCD::Tick()
|
|||
}
|
||||
}
|
||||
|
||||
// Advance FIFOs
|
||||
spriteFIFO.full <<= 1;
|
||||
spriteFIFO.highByte <<= 1;
|
||||
spriteFIFO.lowByte <<= 1;
|
||||
|
@ -179,11 +195,12 @@ void LCD::Tick()
|
|||
}
|
||||
}
|
||||
|
||||
// Okay we're back at rendering the background now
|
||||
switch (fetcher.cycle)
|
||||
{
|
||||
case 0: // Get Tile
|
||||
{
|
||||
// TODO: Can be different (Window)
|
||||
// TODO: Implement the window
|
||||
WORD baseAddr = (lcdc.w.bg_tilemap ? 0x1C00 : 0x1800);
|
||||
BYTE fetcherX = ((scx + fetcher.x) & 0xFF) / 8;
|
||||
BYTE fetcherY = ((ly + scy) & 0xFF) / 8;
|
||||
|
@ -231,25 +248,28 @@ void LCD::Tick()
|
|||
// Draw pixels
|
||||
if (bgFIFO.full & 0x00FF) // If Data in FIFO
|
||||
{
|
||||
// calculate color
|
||||
BYTE color = ((bgFIFO.highByte & 0x80) >> 6) | ((bgFIFO.lowByte & 0x80) >> 7);
|
||||
|
||||
// set color (pretty easy huh)
|
||||
display[ly * 160 + x] = colormap[color];
|
||||
x++;
|
||||
|
||||
// advance fifo
|
||||
bgFIFO.highByte <<= 1;
|
||||
bgFIFO.lowByte <<= 1;
|
||||
bgFIFO.full <<= 1;
|
||||
bgFIFO.sprite <<= 1;
|
||||
}
|
||||
|
||||
if (x == 160)
|
||||
if (x == 160) // if we reached the end of the scanline, enable hblank
|
||||
{
|
||||
stat.w.mode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (ly == 144)
|
||||
else if (ly == 144) // if we're at the end of the screen, enable the vblanking period
|
||||
{
|
||||
stat.w.mode = 1;
|
||||
fetcher.y = -1;
|
||||
|
|
|
@ -12,6 +12,7 @@ static BYTE bios[0x100] = {
|
|||
|
||||
ROM::ROM(FILE* f)
|
||||
{
|
||||
// read in rom from file
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
rewind(f);
|
||||
|
@ -19,6 +20,7 @@ ROM::ROM(FILE* f)
|
|||
data = std::vector<BYTE>(fsize);
|
||||
fread(data.data(), 1, fsize, f);
|
||||
|
||||
// figure out how much ram we need (or dont need)
|
||||
switch (data[0x149])
|
||||
{
|
||||
case 0x01: ram = std::vector<BYTE>(0x800); break;
|
||||
|
@ -28,6 +30,7 @@ ROM::ROM(FILE* f)
|
|||
case 0x05: ram = std::vector<BYTE>(0x10000); break;
|
||||
}
|
||||
|
||||
// figure out how many rom banks there are
|
||||
WORD RomBanks = (WORD)0x2 << data[0x0148];
|
||||
if (data[0x0148] == 0x52)
|
||||
RomBanks = 72;
|
||||
|
@ -36,7 +39,8 @@ ROM::ROM(FILE* f)
|
|||
else if (data[0x0148] == 0x54)
|
||||
RomBanks = 96;
|
||||
|
||||
WORD RamBanks = 0x00;
|
||||
// figure out how many ram banks there are
|
||||
WORD RamBanks = 0x00;
|
||||
switch (data[0x0149])
|
||||
{
|
||||
case 0x03: RamBanks = 4; break;
|
||||
|
|
Loading…
Reference in a new issue