PROJECT(wenboi)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0)
-FIND_PACKAGE(SDL REQUIRED)
ADD_SUBDIRECTORY(core)
-ADD_SUBDIRECTORY(wenboi)
-ADD_SUBDIRECTORY(wendi)
-FIND_PACKAGE(SDL REQUIRED)
-
-ADD_LIBRARY(wenboicore STATIC GameBoy.cc GBMemory.cc GBRom.cc GBVideo.cc MBC.cc NoMBC.cc MBC1.cc util.cc)
-TARGET_LINK_LIBRARIES(wenboicore ${SDL_LIBRARY})
-INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR})
+ADD_LIBRARY(wenboicore SHARED GameBoy.cc GBMemory.cc GBRom.cc GBVideo.cc MBC.cc NoMBC.cc MBC1.cc util.cc)
You should have received a copy of the GNU General Public License
along with wenboi. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <iostream>
+#include <algorithm>
+#include <vector>
+
#include "GBVideo.h"
#include "GameBoy.h"
#include "../common/Logger.h"
#include "util.h"
-#include <iostream>
-#include <algorithm>
-#include <vector>
GBVideo::GBVideo(GameBoy *core):
OAM(),
- display(0),
core(core),
cur_window_line(0),
mode(2),
OAM_BUSY(false),
VRAM_BUSY(false),
frames_rendered(0),
- t0(0),
- t_last_frame(0),
+ screen(0),
oldscreen(0), newscreen(0),
display_mode(NORMAL),
cycles_until_next_update(0)
{
- SDL_Init(SDL_INIT_VIDEO);
- display=SDL_SetVideoMode(320,288,32,SDL_SWSURFACE | SDL_DOUBLEBUF);
-
- t0 = SDL_GetTicks();
oldscreen = new u8[160*144];
newscreen = new u8[160*144];
-
- colors[0] = SDL_MapRGB(display->format, 192,192,0);
- colors[1] = SDL_MapRGB(display->format, 139,139,21);
- colors[2] = SDL_MapRGB(display->format, 101,101,42);
- colors[3] = SDL_MapRGB(display->format, 64,64,64);
- // colors[0] = SDL_MapRGB(display->format, 0xFF, 0xFF, 0xFF);
- // colors[1] = SDL_MapRGB(display->format, 0xAA, 0xAA, 0xAA);
- // colors[2] = SDL_MapRGB(display->format, 0x55, 0x55, 0x55);
- // colors[3] = SDL_MapRGB(display->format, 0x00, 0x00, 0x00);
+ screen = new u8[160*144]; // sum of old and new, 8 possible "colors" per pixel
}
GBVideo::~GBVideo()
{
- SDL_Quit();
delete [] oldscreen;
delete [] newscreen;
+ delete [] screen;
}
void GBVideo::reset()
core->irq(GameBoy::IRQ_LCD_STAT);
}
- if (display_mode == NORMAL)
- {
-
- u32 *pixels = static_cast<u32*>(display->pixels);
- u32 pixels_per_line = display->pitch/display->format->BytesPerPixel;
- for (int y=0; y<144; y++)
- for (int x=0; x<160; x++)
- {
- u32 offset = 160*y+x;
- u32 dst_offset = 2*y*pixels_per_line+2*x;
- u32 avg = colors[(newscreen[offset] + oldscreen[offset])>>1];
- pixels[dst_offset] = avg;
- pixels[dst_offset+1] = avg;
- pixels[dst_offset+pixels_per_line] = avg;
- pixels[dst_offset+pixels_per_line+1] = avg;
- }
- }
+ for (int i=0; i<144*160; i++)
+ screen[i]=oldscreen[i]+newscreen[i];
+
+ // do_vblank_callback
+ // replace oldscreen
memcpy(oldscreen, newscreen, 160*144);
- SDL_Flip(display);
frames_rendered++;
- u32 t1 = SDL_GetTicks();
- if (t1-t0 > 1000)
- {
- char buf[50];
- sprintf(buf, "%f FPS", frames_rendered/(0.001f*(t1-t0)));
- SDL_WM_SetCaption(buf, 0);
- t0=t1;
- frames_rendered=0;
- }
-
- //u32 delay = t1 - t_last_frame < 6 ? 16-(t1-t_last_frame) : 0;
- //SDL_Delay(16-delay);
- //t_last_frame = t1;
-
+
// reset window Y
cur_window_line=0;
}
}
+
+ // The code below should be moved to the emulator GUI
+#if 0
else if (display_mode == BG_MAP)
{
if (LY==0)
}
}
}
-}
-
-int GBVideo::poll_event(SDL_Event *ev)
-{
- return SDL_PollEvent(ev);
+#endif
}
#define GBVIDEO_H
#include "GBMemory.h"
-#include "SDL.h"
#include "util.h"
class GameBoy;
};
- SDL_Surface *display;
GameBoy *core;
u8 cur_window_line;
u32 colors[4];
u32 frames_rendered;
- u32 t0;
- u32 t_last_frame;
- u8 *oldscreen, *newscreen;
+ u8 *oldscreen, *newscreen, *screen;
public:
enum DisplayMode {
void reset();
+ const u8 *get_screen_buffer() { return screen; }
+
// VRAM/OAM access
u8 read_VRAM (int addr) const;
u8 read_OAM (int addr) const;
u32 update();
void set_display_mode(DisplayMode mode) { display_mode = mode; }
- // event processing
- int poll_event(SDL_Event *ev);
-
// status queries
u32 get_frames_rendered() { return frames_rendered; }
#include <string>
#include <cstring>
+bool GameBoy::is_pad[NUM_CONTROLS]={false, false, false, false, true, true, true, true};
+
GameBoy::GameBoy(std::string rom_name, GameBoyType type):
gameboy_type(type),
memory(this),
}
}
-GameBoy::run_status GameBoy::run()
+void GameBoy::push_control(Control b)
{
- static const int CYCLES_PER_INPUT_CHECK = 40000;
- static int c=0;
+ controls[b]=true;
+ bool direction_pressed = is_pad[b];
+ bool button_pressed = !is_pad[b];
- bool must_update_JOYP = false; // has any button changed state?
+ update_JOYP();
- // needed for firing joypad interrupt
- bool button_pressed = false;
- bool direction_pressed = false;
+ // fire IRQ if needed
+ u8 JOYP = memory.read(GBMemory::JOYP, GBMemory::DONT_WATCH);
+ if ((check_bit(JOYP,5)==false && button_pressed) ||
+ (check_bit(JOYP,4)==false && direction_pressed))
+ irq(IRQ_JOYPAD);
+}
- SDL_Event ev;
+void GameBoy::release_control(Control b)
+{
+ controls[b]=false;
+}
+GameBoy::run_status GameBoy::run()
+{
run_status status=NORMAL;
while (status == NORMAL || status == WAIT)
{
- ++c;
- if (c==CYCLES_PER_INPUT_CHECK)
- {
- c=0;
- while (video.poll_event(&ev))
- {
- switch(ev.type)
- {
- case SDL_KEYDOWN:
- switch(ev.key.keysym.sym)
- {
- case SDLK_ESCAPE:
- return PAUSED;
- case SDLK_q:
- return QUIT;
- case SDLK_UP:
- controls[PAD_UP]=true;
- direction_pressed=true;
- break;
- case SDLK_DOWN:
- controls[PAD_DOWN]=true;
- direction_pressed=true;
- break;
- case SDLK_LEFT:
- controls[PAD_LEFT]=true;
- direction_pressed=true;
- break;
- case SDLK_RIGHT:
- controls[PAD_RIGHT]=true;
- direction_pressed=true;
- break;
- case SDLK_z:
- controls[BUTTON_A]=true;
- button_pressed=true;
- break;
- case SDLK_x:
- controls[BUTTON_B]=true;
- button_pressed=true;
- break;
- case SDLK_SPACE:
- controls[BUTTON_START]=true;
- button_pressed=true;
- break;
- case SDLK_RETURN:
- controls[BUTTON_SELECT]=true;
- button_pressed=true;
- break;
- default:
- break;
- }
- must_update_JOYP=true;
- break;
- case SDL_KEYUP:
- switch(ev.key.keysym.sym)
- {
- case SDLK_UP:
- controls[PAD_UP]=false;
- break;
- case SDLK_DOWN:
- controls[PAD_DOWN]=false;
- break;
- case SDLK_LEFT:
- controls[PAD_LEFT]=false;
- break;
- case SDLK_RIGHT:
- controls[PAD_RIGHT]=false;
- break;
- case SDLK_z:
- controls[BUTTON_A]=false;
- break;
- case SDLK_x:
- controls[BUTTON_B]=false;
- break;
- case SDLK_SPACE:
- controls[BUTTON_START]=false;
- break;
- case SDLK_RETURN:
- controls[BUTTON_SELECT]=false;
- break;
- default:
- break;
- }
- must_update_JOYP=true;
- break;
- case SDL_QUIT:
- return QUIT;
- }
- }
-
- if (must_update_JOYP)
- update_JOYP();
-
- if (button_pressed || direction_pressed)
- {
- u8 JOYP = memory.read(GBMemory::JOYP, GBMemory::DONT_WATCH);
- if ((check_bit(JOYP,5)==false && button_pressed) ||
- (check_bit(JOYP,4)==false && direction_pressed))
- irq(IRQ_JOYPAD);
- }
- }
status = run_cycle();
}
union GBRom;
+/* A GameBoy with debugging facilities :) */
class GameBoy
{
public:
- enum GameBoyType { GAMEBOY, GAMEBOYCOLOR, SUPERGAMEBOY } gameboy_type;
- enum InterruptRequest {
- IRQ_VBLANK = 0x01,
- IRQ_LCD_STAT = 0x02,
- IRQ_TIMER = 0x04,
- IRQ_SERIAL = 0x08,
- IRQ_JOYPAD = 0x10,
- };
-
- enum Flag
- {
- ZERO_FLAG = 0x80,
- ADD_SUB_FLAG = 0x40,
- HALF_CARRY_FLAG = 0x20,
- CARRY_FLAG = 0x10,
- };
-
- enum Controls
- {
- BUTTON_A=0,
- BUTTON_B,
- BUTTON_START,
- BUTTON_SELECT,
- PAD_UP,
- PAD_DOWN,
- PAD_LEFT,
- PAD_RIGHT,
- NUM_CONTROLS
- };
-
-
- friend class GBMemory;
- friend class GBVideo;
- GBMemory memory;
- GBVideo video;
- GBRom *rom;
- bool controls[NUM_CONTROLS];
-
- // CPU Registers
- // ENDIANNESS WARNING!
- struct RegisterSet
- {
- union
- {
- u16 AF;
- struct { u8 flags; u8 A; };
- };
- union
+ enum GameBoyType { GAMEBOY, GAMEBOYCOLOR, SUPERGAMEBOY } gameboy_type;
+ enum Control
{
- u16 BC;
- struct { u8 C; u8 B; };
+ BUTTON_A=0,
+ BUTTON_B,
+ BUTTON_START,
+ BUTTON_SELECT,
+ PAD_UP,
+ PAD_DOWN,
+ PAD_LEFT,
+ PAD_RIGHT,
+ NUM_CONTROLS
};
- union
+
+ enum run_status
{
- u16 DE;
- struct { u8 E; u8 D; };
+ NORMAL = 0,
+ BREAKPOINT,
+ WATCHPOINT,
+ TRACEPOINT,
+ PAUSED,
+ QUIT,
+ WAIT,
};
- union
- {
- u16 HL;
- struct { u8 L; u8 H; };
+
+ enum InterruptRequest {
+ IRQ_VBLANK = 0x01,
+ IRQ_LCD_STAT = 0x02,
+ IRQ_TIMER = 0x04,
+ IRQ_SERIAL = 0x08,
+ IRQ_JOYPAD = 0x10,
};
- u16 SP;
- u16 PC;
- } __attribute__((packed)) regs;
+ // Constructors
+ GameBoy(std::string rom_name, GameBoyType type=GAMEBOY);
+
+ // running control methods
+ void irq(InterruptRequest i) { memory.write(0xFF0F, memory.read(0xFF0F) | i); }
+ void reset();
+ run_status run_cycle();
+ run_status run();
+
+ // button control methods
+ void push_control(Control b);
+ void release_control(Control b);
+ // video output
+ const u8 *get_screen_buffer(){ return video.get_screen_buffer(); }
- u8 IME; // Interrupt master enable flag
- u8 HALT; // Is the CPU halted waiting for an interrupt?
- u8 STOP; // Is the CPU & LCD halted waiting for a keypress?
+ // debug methods
+ int set_breakpoint (u16 addr);
+ void delete_breakpoint (int id);
+ void enable_breakpoint (int id);
+ void disable_breakpoint(int id);
- u32 cycle_count;
- u32 cycles_until_next_instruction;
- u8 divider_count; // resets every 256 cycles, so we don't need a cmp
- u32 timer_count;
- static const u32 CYCLE_STEP = 4;
+ std::string status_string();
+ Instruction disassemble_opcode(u16 addr);
+ static std::string get_port_name(int port);
- inline void do_call(u16 addr)
- {
- logger.debug("do_call(0x", std::hex, std::setw(4), std::setfill('0'), addr, ")");
- memory.write(regs.SP-1, regs.PC >> 8);
- memory.write(regs.SP-2, regs.PC & 0xFF);
- regs.SP -= 2;
- regs.PC = addr;
- }
-
- void set_flag (Flag f) { regs.flags |= f; }
- void reset_flag(Flag f) { regs.flags &= (~f); }
- bool check_flag(Flag f) const { return ((regs.flags & f) != 0); }
-
- enum run_status
- {
- NORMAL = 0,
- BREAKPOINT,
- WATCHPOINT,
- TRACEPOINT,
- PAUSED,
- QUIT,
- WAIT,
- };
-
- // Constructors
- GameBoy(std::string rom_name, GameBoyType type=GAMEBOY);
-
-
- void irq(InterruptRequest i) { memory.write(0xFF0F, memory.read(0xFF0F) | i); }
- void reset();
- run_status run_cycle();
- run_status run();
-
- // debug methods
- int set_breakpoint (u16 addr);
- void delete_breakpoint (int id);
- void enable_breakpoint (int id);
- void disable_breakpoint(int id);
-
- std::string status_string();
- Instruction disassemble_opcode(u16 addr);
- static std::string get_port_name(int port);
-
- // prevent object copying
private:
- GameBoy(const GameBoy&);
- GameBoy operator=(const GameBoy&);
+ friend class GBMemory;
+ friend class GBVideo;
+ GBMemory memory;
+ GBVideo video;
+ GBRom *rom;
+
+ bool controls[NUM_CONTROLS];
+ static bool is_pad[NUM_CONTROLS];
+ enum Flag
+ {
+ ZERO_FLAG = 0x80,
+ ADD_SUB_FLAG = 0x40,
+ HALF_CARRY_FLAG = 0x20,
+ CARRY_FLAG = 0x10,
+ };
- void update_JOYP();
-
- // debug things
- struct Breakpoint {
- int addr;
- bool enabled;
- Breakpoint(int a, bool e): addr(a), enabled(e) {}
- Breakpoint(): addr(-1), enabled(false) {}
- };
+ // CPU Registers
+ // ENDIANNESS WARNING!
+ struct RegisterSet
+ {
+ union
+ {
+ u16 AF;
+ struct { u8 flags; u8 A; };
+ };
+ union
+ {
+ u16 BC;
+ struct { u8 C; u8 B; };
+ };
+ union
+ {
+ u16 DE;
+ struct { u8 E; u8 D; };
+ };
+ union
+ {
+ u16 HL;
+ struct { u8 L; u8 H; };
+ };
+ u16 SP;
+ u16 PC;
+
+ } regs;
+
+
+ u8 IME; // Interrupt master enable flag
+ u8 HALT; // Is the CPU halted waiting for an interrupt?
+ u8 STOP; // Is the CPU & LCD halted waiting for a keypress?
+
+ u32 cycle_count;
+ u32 cycles_until_next_instruction;
+ u8 divider_count; // resets every 256 cycles, so we don't need a cmp
+ u32 timer_count;
+ static const u32 CYCLE_STEP = 4;
+
+ inline void do_call(u16 addr)
+ {
+ logger.debug("do_call(0x", std::hex, std::setw(4), std::setfill('0'), addr, ")");
+ memory.write(regs.SP-1, regs.PC >> 8);
+ memory.write(regs.SP-2, regs.PC & 0xFF);
+ regs.SP -= 2;
+ regs.PC = addr;
+ }
+
+ void set_flag (Flag f) { regs.flags |= f; }
+ void reset_flag(Flag f) { regs.flags &= (~f); }
+ int check_flag(Flag f) const { return ((regs.flags & f) != 0 ? 1 : 0); }
+
+ // prevent object copying
+ GameBoy(const GameBoy&);
+ GameBoy operator=(const GameBoy&);
+
+ // update JOYP register when controls are pushed/released
+ void update_JOYP();
+
+ // debug things
+ struct Breakpoint {
+ int addr;
+ bool enabled;
+
+ Breakpoint(int a, bool e): addr(a), enabled(e) {}
+ Breakpoint(): addr(-1), enabled(false) {}
+ };
- typedef std::map<int, Breakpoint> BreakpointMap;
-
- BreakpointMap breakpoints;
- int last_breakpoint_id;
+ typedef std::map<int, Breakpoint> BreakpointMap;
+
+ BreakpointMap breakpoints;
+ int last_breakpoint_id;
};
You should have received a copy of the GNU General Public License
along with wenboi. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "../core/GameBoy.h"
-#include "../common/Logger.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdlib>
#include <vector>
+#include "SDL.h"
+
+#include "../core/GameBoy.h"
+#include "../common/Logger.h"
+
using std::cin;
using std::cout;
using std::cerr;
{
if (status == GameBoy::BREAKPOINT)
{
- cout << "Breakpoint hit at " << gb.regs.PC << endl;
+ cout << "Breakpoint reached " << endl;
cout << gb.status_string() << endl;
}
else if (status == GameBoy::WATCHPOINT)
{
+ cout << "Watchpoint reached" << endl;
+ /*
cout << "Watchpoint 0x" << std::hex << std::setw(4) << std::setfill('0') <<
int(gb.memory.watchpoint_addr) << " hit at 0x" << gb.regs.PC;
+ */
+ // FIXME: Move/expose this things somewhere
if (gb.memory.watchpoint_newvalue == 0xFFFF)
{
cout << " (READ)" << endl <<
exit(EXIT_FAILURE);
}
GameBoy gb(argv[1]);
+
+ SDL_Init(SDL_INIT_VIDEO);
+ SDL_Surface *display=SDL_SetVideoMode(320,288,32,SDL_SWSURFACE | SDL_DOUBLEBUF);
+
cout << gb.status_string() << endl;