Disassembly and status display in qtboi
authorslack <slack@codemaniacs.com>
Fri, 13 Mar 2009 19:01:31 +0000 (20:01 +0100)
committerslack <slack@codemaniacs.com>
Sat, 14 Mar 2009 14:04:04 +0000 (15:04 +0100)
12 files changed:
core/MBC.h
core/MBC1.cc
core/MBC1.h
core/NoMBC.h
qtboi/CMakeLists.txt
qtboi/QtBoiDisassemblyWindow.cc [new file with mode: 0644]
qtboi/QtBoiDisassemblyWindow.h [new file with mode: 0644]
qtboi/QtBoiEmuThread.cc
qtboi/QtBoiMainWindow.cc
qtboi/QtBoiMainWindow.h
qtboi/QtBoiStatusWindow.cc [new file with mode: 0644]
qtboi/QtBoiStatusWindow.h [new file with mode: 0644]

index c265589f2ecbad0a5a0fbb7564b868547545472f..2806be6da03c85317549ee85fee5a487ad9a4df4 100644 (file)
@@ -28,6 +28,10 @@ class MBC
        virtual u16  read16(u16 addr) const=0;
        virtual void write (u16 addr, u8 value)=0;
        virtual ~MBC() {};
+
+        // return a 32-bit <bank|addr> value encoding
+        // the current ROM/RAM bank and the given address
+        virtual u32 getUniqueAddress(u16 addr) const=0; 
 };
 
 
index f3a48b03b5e48784671d8925b598f56f19d302f3..b4451865f3f579492c493c45ba6c877a4b05f2e3 100644 (file)
@@ -112,4 +112,27 @@ void MBC1::write(u16 addr, u8 value)
        }
 }
 
+u32 MBC1::getUniqueAddress(u16 addr) const
+{
+       if (addr <= 0x3FFF)       // ROM Bank 0
+               return addr;
+       else if (addr <= 0x7FFF)  // ROM (switchable)
+       {
+               u8 rom_bank = rom_bank_low;
+               if (mode == ROM_BANKING_MODE) rom_bank |= (ram_bank << 5);
+
+               u32 base = 16384*rom_bank;
+               return base + (addr-0x4000);
+       }
+       else // if ((addr&0xE000) == 0xA000)   //(addr >= 0xA000 && addr <= 0xBFFF)
+       {
+               if (ram_enabled)
+               {
+                       u32 base = (mode == RAM_BANKING_MODE ? 8192*ram_bank : 0);
+                       return (base + (addr-0xA000)) | 0x80000000;
+               }
+               else return 0;
+       }
+       return 0;
+}
 
index 418a8c1defe14599e197459f1eda837e11ec65df..be99e1c80b4ce3891c03c03884e3ed64631ec97b 100644 (file)
@@ -46,6 +46,8 @@ class MBC1: public MBC
        u8   read  (u16 addr) const;
        u16  read16(u16 addr) const;
        void write (u16 addr, u8 value);
+
+        u32 getUniqueAddress(u16 addr) const;
 };
 
 #endif
index 08557f982712c4bc47d99e80c7800220da913cbd..e4cfc2e820cf89fe1921157bbd66fc917cc7ff12 100644 (file)
@@ -32,6 +32,7 @@ class NoMBC: public MBC
        u16  read16(u16 addr) const;
        void write (u16 addr, u8 value);
 
+        u32 getUniqueAddress(u16 addr) const { return addr; }
 };
 
 #endif
index b6c045c6f320bcafd18d599f3ce82ef61525d937..259bff4db0ae693913f85468de935e8284af2de8 100644 (file)
@@ -4,12 +4,16 @@ INCLUDE( ${QT_USE_FILE} )
 SET(QTBOI_MOC_HDRS
   QtBoiMainWindow.h
   QtBoiEmuThread.h
+  QtBoiDisassemblyWindow.h
+  QtBoiStatusWindow.h
 )
 
 SET(QTBOI_SOURCES
   qtboi.cc
   QtBoiMainWindow.cc
   QtBoiEmuThread.cc
+  QtBoiDisassemblyWindow.cc
+  QtBoiStatusWindow.cc
 )
 
 QT4_WRAP_CPP(QTBOI_MOC_SRCS ${QTBOI_MOC_HDRS})
diff --git a/qtboi/QtBoiDisassemblyWindow.cc b/qtboi/QtBoiDisassemblyWindow.cc
new file mode 100644 (file)
index 0000000..def4c51
--- /dev/null
@@ -0,0 +1,59 @@
+#include <string>
+#include <sstream>
+#include <iomanip>
+
+#include "QtBoiDisassemblyWindow.h"
+
+QtBoiDisassemblyWindow::QtBoiDisassemblyWindow(QWidget *parent, GameBoy *gb)
+        :gb(gb)
+{
+        setFocusPolicy(Qt::NoFocus);
+        setMinimumSize(350,500);
+}
+
+QtBoiDisassemblyWindow::~QtBoiDisassemblyWindow()
+{
+}
+
+void QtBoiDisassemblyWindow::gotoAddress(u16 addr)
+{
+        int start = addr;
+        int end   = start+200;
+        int pos   = start;
+
+        if (end > 0xFFFF) end = 0xFFFF;
+        std::ostringstream str;
+        
+        str << "<html><head><title>Disassembly</title></head><body>";
+        str << "<table cellpadding=0 cellspacing=0>";
+        str << "<tr bgcolor=#c0ffc0><td width=25%>Addr</td><td width=25%>Opcodes</td><td>Instruction</td></tr>";
+
+        bool hilightBG=true;
+
+        while (pos < end)
+        {
+                Instruction ins(gb->disassemble_opcode(pos));
+                str << "<tr bgcolor=" << (pos == gb->regs.PC ? "#ffc0c0" : (hilightBG ? "#d0d0d0" : "#ffffff")) << 
+                        "><td>0x" << std::hex << std::setw(4) << std::setfill('0') << 
+                        pos << "</td><td>";
+                for (int i=0; i<ins.length; i++)
+                        str << std::setw(2) << int(gb->memory.read(pos+i)) << " ";
+
+                str << "</td><td>";
+
+                str << ins.all << "</td></tr>";
+                pos += ins.length;
+
+                hilightBG = !hilightBG;
+        }
+
+        str << "</table></body></html>";
+
+        setHtml(QString(str.str().c_str()));
+}
+
+void QtBoiDisassemblyWindow::gotoPC()
+{
+        gotoAddress(gb->regs.PC);
+}
+
diff --git a/qtboi/QtBoiDisassemblyWindow.h b/qtboi/QtBoiDisassemblyWindow.h
new file mode 100644 (file)
index 0000000..e83c6d4
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef QTBOIDISASSEMBLYWINDOW_H
+#define QTBOIDISASSEMBLYWINDOW_H
+
+#include <QTextBrowser>
+#include "../core/GameBoy.h"
+
+class QtBoiDisassemblyWindow: public QTextBrowser
+{
+        Q_OBJECT
+
+        public:
+                QtBoiDisassemblyWindow(QWidget *parent, GameBoy *gb);
+                ~QtBoiDisassemblyWindow();
+
+                void gotoAddress(u16 addr);
+                void gotoPC();
+
+        private:
+                GameBoy *gb;
+
+};
+
+
+#endif
+
+
index 37c4c67c7902c04924a05e37e4a986df34e49e39..5d9287bef89da800bca25337d9717864596487f8 100644 (file)
@@ -118,7 +118,7 @@ void QtBoiEmuThread::run()
                {
                        resetRequested = false;
                        gb.load_ROM(romName.toStdString());
-                       cout << "Loaded ROM " << romName.toStdString() << endl;
+                        if (isPaused) emit emulationPaused();
                }
 
                if(isPaused)
index 1e4c70f0ca9171dd59c5a98a7411e2683742ae68..af44533ebc3742c5a54b7d4792214a902fb65c91 100644 (file)
@@ -1,5 +1,3 @@
-#include "QtBoiMainWindow.h"
-
 #include <QMenu>
 #include <QMenuBar>
 #include <QToolBar>
@@ -7,31 +5,35 @@
 #include <QFileDialog>
 #include <QPushButton>
 #include <QColor>
+#include <QGroupBox>
+#include <QHBoxLayout>
 #include <QVBoxLayout>
 #include <iostream>
 
+#include "QtBoiMainWindow.h"
+
 QtBoiMainWindow::QtBoiMainWindow(QWidget *parent)
        :QMainWindow(parent), emuThread(0)
 {
-       screen_image = new QImage(160, 144, QImage::Format_Indexed8);
-       screen_image->setNumColors(7);
+       screenImage = new QImage(160, 144, QImage::Format_Indexed8);
+       screenImage->setNumColors(7);
        // gray palette
-       //screen_image->setColor(6, qRgb(0,0,0));
-       //screen_image->setColor(5, qRgb(42,42,42));
-       //screen_image->setColor(4, qRgb(85,85,85));
-       //screen_image->setColor(3, qRgb(127,127,127));
-       //screen_image->setColor(2, qRgb(170,170,170));
-       //screen_image->setColor(1, qRgb(212,212,212));
-       //screen_image->setColor(0, qRgb(255,255,255));
+       //screenImage->setColor(6, qRgb(0,0,0));
+       //screenImage->setColor(5, qRgb(42,42,42));
+       //screenImage->setColor(4, qRgb(85,85,85));
+       //screenImage->setColor(3, qRgb(127,127,127));
+       //screenImage->setColor(2, qRgb(170,170,170));
+       //screenImage->setColor(1, qRgb(212,212,212));
+       //screenImage->setColor(0, qRgb(255,255,255));
        
        // greenish palette
-       screen_image->setColor(6, qRgb(64,64,64));
-       screen_image->setColor(5, qRgb(82,82,53));
-       screen_image->setColor(4, qRgb(101,101,42));
-       screen_image->setColor(3, qRgb(120,120,31));
-       screen_image->setColor(2, qRgb(139,139,21));
-       screen_image->setColor(1, qRgb(166,166,10));
-       screen_image->setColor(0, qRgb(192,192,0));
+       screenImage->setColor(6, qRgb(64,64,64));
+       screenImage->setColor(5, qRgb(82,82,53));
+       screenImage->setColor(4, qRgb(101,101,42));
+       screenImage->setColor(3, qRgb(120,120,31));
+       screenImage->setColor(2, qRgb(139,139,21));
+       screenImage->setColor(1, qRgb(166,166,10));
+       screenImage->setColor(0, qRgb(192,192,0));
 
        emuThread = new QtBoiEmuThread(this);
        emuThread->start();
@@ -54,8 +56,11 @@ QtBoiMainWindow::QtBoiMainWindow(QWidget *parent)
        createMenu();
        createToolbar();
 
+        statusbar = statusBar();
+
 
        connect(emulatorCont, SIGNAL(triggered()), emuThread, SLOT(cont()));
+       connect(emulatorCont, SIGNAL(triggered()), this, SLOT(onResume()));
        connect(emulatorStop, SIGNAL(triggered()), emuThread, SLOT(stop()));
        connect(emulatorPause, SIGNAL(triggered()), emuThread, SLOT(pause()));
        connect(emulatorStep, SIGNAL(triggered()), emuThread, SLOT(step()));
@@ -63,22 +68,40 @@ QtBoiMainWindow::QtBoiMainWindow(QWidget *parent)
        connect(emuThread, SIGNAL(redraw(const uchar*)), this, SLOT(onRedraw(const uchar*)));
        connect(emuThread, SIGNAL(emulationPaused()), this, SLOT(onPause()));
 
-       resize(640,480);
+       resize(800,600);
        centralWindow = new QWidget(this);
        setCentralWidget(centralWindow);
 
-       QVBoxLayout *vbox = new QVBoxLayout(centralWindow);
+        QHBoxLayout *topHBoxLayout = new QHBoxLayout;
+
+        QWidget *leftVBox  = new QWidget(centralWindow);
+        QWidget *rightVBox = new QWidget(centralWindow);
+       QVBoxLayout *leftVBoxLayout = new QVBoxLayout;
+       QVBoxLayout *rightVBoxLayout = new QVBoxLayout;
+        leftVBox->setLayout(leftVBoxLayout);
+        rightVBox->setLayout(rightVBoxLayout);
 
        screen = new QLabel(centralWindow);
        screen->resize(320,288);
+        uchar buf[160*144];
+        memset(buf, 0, 160*144);
+        onRedraw(buf);
 
-       status = new QLabel;
+       status = new QtBoiStatusWindow(centralWindow, &emuThread->gb);
        status->setFont(QFont("courier"));
 
-       vbox->addWidget(screen);
-       vbox->addWidget(status);
+        topHBoxLayout->addWidget(leftVBox);
+        topHBoxLayout->addWidget(rightVBox);
+       leftVBoxLayout->addWidget(screen);
+       leftVBoxLayout->addWidget(status);
+        
+        disassembly = new QtBoiDisassemblyWindow(centralWindow, &emuThread->gb);
+        disassembly->setOpenLinks(false);
+       disassembly->setFont(QFont("courier"));
+        
+        rightVBoxLayout->addWidget(disassembly);
        
-       centralWindow->setLayout(vbox);
+       centralWindow->setLayout(topHBoxLayout);
 
 }
 
@@ -129,21 +152,28 @@ void QtBoiMainWindow::onLoadROM()
        if (filename == "") return;
 
        emuThread->loadROM(filename);
-
+        statusbar->showMessage(tr("Loaded ROM ")+filename);
 }
 
 void QtBoiMainWindow::onRedraw(const uchar *buffer)
 {
-       uchar *pixels = screen_image->bits();
+       uchar *pixels = screenImage->bits();
        memcpy(pixels, buffer, 160*144);
-       screen->setPixmap(QPixmap::fromImage(screen_image->scaled(320,288)));
+       screen->setPixmap(QPixmap::fromImage(screenImage->scaled(320,288)));
 }
 
 void QtBoiMainWindow::onPause()
 {
        status->setText(QString(emuThread->gb.status_string().c_str()));
+        disassembly->gotoPC();
+        status->update();
+        statusbar->showMessage(tr("Emulation paused", "Status bar emulation paused msg"));
 }
 
+void QtBoiMainWindow::onResume()
+{
+        statusbar->showMessage(tr("Emulation running", "Status bar emulation running msg"));
+}
 
 void QtBoiMainWindow::keyPressEvent(QKeyEvent *event)
 {
index 29f3e16088e85a4c4b4dddcedb6b29d84f8f761d..9314f86a28ef56fe93473261db1d8022d84d57cd 100644 (file)
@@ -7,7 +7,12 @@
 #include <QPushButton>
 #include <QImage>
 #include <QLabel>
+#include <QStatusBar>
+#include <QTextBrowser>
+
 #include "QtBoiEmuThread.h"
+#include "QtBoiDisassemblyWindow.h"
+#include "QtBoiStatusWindow.h"
 
 class QtBoiMainWindow: public QMainWindow
 {
@@ -21,6 +26,7 @@ class QtBoiMainWindow: public QMainWindow
                void onLoadROM();
                void onRedraw(const uchar *buffer);
                void onPause();
+               void onResume();
 
        private:
                // private functions
@@ -35,8 +41,10 @@ class QtBoiMainWindow: public QMainWindow
 
                QWidget *centralWindow;
                QLabel  *screen;
-               QImage  *screen_image;
-               QLabel  *status;
+               QImage  *screenImage;
+                QStatusBar *statusbar;
+                QtBoiDisassemblyWindow *disassembly;
+               QtBoiStatusWindow *status;
 
                QAction *loadROM;
                QAction *quit;
diff --git a/qtboi/QtBoiStatusWindow.cc b/qtboi/QtBoiStatusWindow.cc
new file mode 100644 (file)
index 0000000..25348dd
--- /dev/null
@@ -0,0 +1,65 @@
+#include <string>
+#include <sstream>
+#include <iomanip>
+
+#include "QtBoiStatusWindow.h"
+
+QtBoiStatusWindow::QtBoiStatusWindow(QWidget *parent, GameBoy *gb)
+        :gb(gb)
+{
+        setFocusPolicy(Qt::NoFocus);
+        setMinimumSize(200,200);
+}
+
+QtBoiStatusWindow::~QtBoiStatusWindow()
+{
+}
+
+void QtBoiStatusWindow::update()
+{
+        std::ostringstream str;
+        
+        str << "<html><head><title>Status</title></head><body>";
+        
+        str << "<table cellpadding=0 cellspacing=0><tr><td>";
+        // 8-bit regs table
+        str << "<table cellpadding=0 cellspacing=0>";
+        str << "<tr bgcolor=#c0ffc0><th colspan=\"2\">8-bit</th></tr>";
+        str << "<tr><td>A&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.A) << "</td></tr>";
+        str << "<tr><td>B&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.B) << "</td></tr>";
+        str << "<tr><td>C&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.C) << "</td></tr>";
+        str << "<tr><td>D&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.D) << "</td></tr>";
+        str << "<tr><td>E&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.E) << "</td></tr>";
+        str << "<tr><td>H&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.H) << "</td></tr>";
+        str << "<tr><td>L&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(2) << std::setfill('0') << int(gb->regs.L) << "</td></tr>";
+        str << "</table>";
+        str << "</td><td width=20></td><td>";
+        // 16-bit regs table
+        str << "<table cellpadding=0 cellspacing=0>";
+        str << "<tr bgcolor=#c0ffc0><th colspan=\"2\">16-bit</th></tr>";
+        str << "<tr><td>BC&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.BC) << "</td></tr>";
+        str << "<tr><td>DE&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.DE) << "</td></tr>";
+        str << "<tr><td>HL&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.HL) << "</td></tr>";
+        str << "<tr></tr>";
+        str << "<tr><td>SP&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.SP) << "</td></tr>";
+        str << "<tr></tr>";
+        str << "<tr><td>PC&nbsp;=&nbsp;</td><td>0x" << std::hex << std::setw(4) << std::setfill('0') << int(gb->regs.PC) << "</td></tr>";
+        str << "</table>";
+        str << "</td><td width=20></td><td>";
+        // Flags & state table (TODO: Interpret IE and IF registers)
+        str << "<table cellpadding=0 cellspacing=0>";
+        str << "<tr bgcolor=#c0ffc0><th colspan=\"2\">Flags</th></tr>";
+        str << "<tr><td bgcolor=" << (gb->check_flag(GameBoy::ZERO_FLAG)? "#FF0000" : "#FFFFFF") << ">Z</td></tr>";
+        str << "<tr><td bgcolor=" << (gb->check_flag(GameBoy::ADD_SUB_FLAG)? "#FF0000" : "#FFFFFF") << ">N</td></tr>";
+        str << "<tr><td bgcolor=" << (gb->check_flag(GameBoy::HALF_CARRY_FLAG)? "#FF0000" : "#FFFFFF") << ">H</td></tr>";
+        str << "<tr><td bgcolor=" << (gb->check_flag(GameBoy::CARRY_FLAG)? "#FF0000" : "#FFFFFF") << ">C</td></tr>";
+       str << "<tr><td>IME&nbsp;=&nbsp;" << int(gb->IME) << "</td></tr>";
+       str << "<tr><td>IE&nbsp;&nbsp;=&nbsp;" << int(gb->memory.read(0xFFFF, GBMemory::DONT_WATCH)) << "</td></tr>";
+       str << "<tr><td>IF&nbsp;&nbsp;=&nbsp;" << int(gb->memory.read(0xFF0F, GBMemory::DONT_WATCH)) << "</td></tr>";
+        str << "</td></tr>";
+        str << "</table></body></html>";
+
+        setHtml(QString(str.str().c_str()));
+}
+
+
diff --git a/qtboi/QtBoiStatusWindow.h b/qtboi/QtBoiStatusWindow.h
new file mode 100644 (file)
index 0000000..a1bda3f
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef QTBOISTATUSWINDOW_H
+#define QTBOISTATUSWINDOW_H
+
+#include <QTextBrowser>
+#include "../core/GameBoy.h"
+
+class QtBoiStatusWindow: public QTextBrowser
+{
+        Q_OBJECT
+
+        public:
+                QtBoiStatusWindow(QWidget *parent, GameBoy *gb);
+                ~QtBoiStatusWindow();
+
+                void update();
+
+        private:
+                GameBoy *gb;
+
+};
+
+
+#endif
+
+