Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Support Qt for WebAssembly #294

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions .github/workflows/qt-wasm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Build for Qt for WebAssembly

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
runs-on: ubuntu-20.04

steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'recursive'

- name: Install cached emscripten
id: cache
uses: actions/cache@v3
with:
path: 'emsdk'
key: 'emsdk-1.39.8'

- name: Install emscripten
if: steps.cache.outputs.cache-hit != 'true'
run: |
curl -L https://github.com/emscripten-core/emsdk/archive/HEAD.tar.gz | tar xz
mv emsdk-* emsdk
./emsdk/emsdk install 1.39.8
./emsdk/emsdk activate 1.39.8

- name: Install Qt
uses: jurplel/install-qt-action@43ec12788e42f375acfcb2cec059edfb9572fbaa # v3
with:
version: '5.15.2'
host: 'linux'
target: 'desktop'
arch: 'wasm_32'
cache: true

- name: Build firebird
run: |
. ./emsdk/emsdk_env.sh
mkdir build
cd build
qmake ..
make -j4

- name: Upload zip
uses: actions/upload-artifact@v3
with:
name: firebird-emu-wasm
path: |
build/*.html
build/*.js
build/*.wasm
build/*.svg
if-no-files-found: error
2 changes: 1 addition & 1 deletion core/emu.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern "C" {
#endif

// Can also be set manually
#if !defined(__i386__) && !defined(__x86_64__) && !(defined(__arm__) && !defined(__thumb__)) && !(defined(__aarch64__))
#if !defined(__i386__) && !defined(__x86_64__) && !(defined(__arm__) && !defined(__thumb__)) && !(defined(__aarch64__)) && !defined(NO_TRANSLATION)
#define NO_TRANSLATION
#endif

Expand Down
1 change: 1 addition & 0 deletions core/os/os-emscripten.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include <emscripten.h>

Expand Down
6 changes: 3 additions & 3 deletions emscripten/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ CSOURCES := ../core/armsnippets_loader.c ../core/asmcode.c ../core/casplus.c

CPPSOURCES := ../core/arm_interpreter.cpp ../core/coproc.cpp ../core/cpu.cpp ../core/debug.cpp ../core/emu.cpp \
../core/flash.cpp ../core/gif.cpp ../core/thumb_interpreter.cpp ../core/usblink_queue.cpp main.cpp \
../core/fieldparser.cpp
../core/fieldparser.cpp ../core/cx2.cpp ../core/usb_cx2.cpp ../core/usblink_cx2.cpp

OBJS = $(patsubst %.c, %.bc, $(CSOURCES))
OBJS += $(patsubst %.cpp, %.bc, $(CPPSOURCES))

all: $(OUTPUT).js

%.bc: %.c Makefile
$(CC) $(CFLAGS) $< -o $@
$(CC) $(CFLAGS) -c $< -o $@

%.bc: %.cpp Makefile
$(CXX) $(CXXFLAGS) $< -o $@
$(CXX) $(CXXFLAGS) -c $< -o $@

test: $(OUTPUT).js
emrun --safe_firefox_profile --browser firefox $(OUTPUT).html
Expand Down
3 changes: 3 additions & 0 deletions emscripten/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include "core/mmu.h"
#include "core/debug.h"
#include "core/emu.h"
#include "core/lcd.h"
#include "core/schedule.h"
#include "core/mem.h"

void gui_do_stuff(bool wait)
{
Expand Down
135 changes: 88 additions & 47 deletions emuthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
#include <windows.h>
#endif

#include "core/debug.h"
#include "core/emu.h"
#include "core/mem.h"
#include "core/mmu.h"
#include "core/schedule.h"
#include "core/usblink_queue.h"

EmuThread emu_thread;
Expand Down Expand Up @@ -110,45 +112,36 @@ void throttle_timer_wait(unsigned int usec)
}

EmuThread::EmuThread(QObject *parent) :
QThread(parent)
QTimer(parent)
{
// There can only be one instance, this global one.
assert(&emu_thread == this);

// Set default settings
debug_on_start = debug_on_warn = false;

connect(this, &QTimer::timeout, this, &EmuThread::loopStep);
connect(this, &EmuThread::speedChanged, this, [&](double d) {
if(turbo_mode)
setInterval(0);
else
setInterval(std::max(interval() * d, 1.0));
});
}

//Called occasionally, only way to do something in the same thread the emulator runs in.
void EmuThread::doStuff(bool wait)
{
do
{
if(do_suspend)
{
bool success = emu_suspend(snapshot_path.c_str());
do_suspend = false;
emit suspended(success);
}

if(enter_debugger)
{
setPaused(false);
enter_debugger = false;
if(!in_debugger)
debugger(DBG_USER, 0);
}

if(is_paused && wait)
msleep(100);

} while(is_paused && wait);
}

void EmuThread::run()
void EmuThread::start()
{
setTerminationEnabled();

path_boot1 = QDir::toNativeSeparators(boot1).toStdString();
path_flash = QDir::toNativeSeparators(flash).toStdString();

Expand All @@ -162,28 +155,15 @@ void EmuThread::run()

do_resume = false;

if(success)
emu_loop(do_reset);

emit stopped();
if(success) {
startup(do_reset);
setInterval(1);
QTimer::start();
}
}

void EmuThread::throttleTimerWait(unsigned int usec)
{
if(usec <= 1)
return;

#ifdef Q_OS_WINDOWS
// QThread::usleep uses Sleep, which may sleep up to ~32ms more!
// Use nanosleep inside a timeBeginPeriod/timeEndPeriod block for accuracy.
timeBeginPeriod(10);
struct timespec ts{};
ts.tv_nsec = usec * 1000;
nanosleep(&ts, nullptr);
timeEndPeriod(10);
#else
QThread::usleep(usec);
#endif
}

void EmuThread::setTurboMode(bool enabled)
Expand All @@ -208,6 +188,75 @@ void EmuThread::debuggerInput(QString str)
debug_callback(debug_input.c_str());
}

void EmuThread::startup(bool reset)
{
if(reset)
{
memset(mem_areas[1].ptr, 0, mem_areas[1].size);

memset(&arm, 0, sizeof arm);
arm.control = 0x00050078;
arm.cpsr_low28 = MODE_SVC | 0xC0;
cpu_events &= EVENT_DEBUG_STEP;

sched_reset();
sched.items[SCHED_THROTTLE].clock = CLOCK_27M;
extern void throttle_interval_event(int index);
sched.items[SCHED_THROTTLE].proc = throttle_interval_event;

memory_reset();
}

addr_cache_flush();

sched_update_next_event(0);

exiting = false;
}

void EmuThread::loopStep()
{
int i = 1000;
while(i--)
{
sched_process_pending_events();
while (!exiting && cycle_count_delta < 0)
{
if (cpu_events & EVENT_RESET) {
gui_status_printf("Reset");
startup(true);
break;
}

if (cpu_events & EVENT_SLEEP) {
assert(emulate_cx2);
cycle_count_delta = 0;
break;
}

if (cpu_events & (EVENT_FIQ | EVENT_IRQ)) {
// Align PC in case the interrupt occurred immediately after a jump
if (arm.cpsr_low28 & 0x20)
arm.reg[15] &= ~1;
else
arm.reg[15] &= ~3;

if (cpu_events & EVENT_WAITING)
arm.reg[15] += 4; // Skip over wait instruction

arm.reg[15] += 4;
cpu_exception((cpu_events & EVENT_FIQ) ? EX_FIQ : EX_IRQ);
}
cpu_events &= ~EVENT_WAITING;

if (arm.cpsr_low28 & 0x20)
cpu_thumb_loop();
else
cpu_arm_loop();
}
}
}

void EmuThread::setPaused(bool paused)
{
this->is_paused = paused;
Expand All @@ -216,8 +265,7 @@ void EmuThread::setPaused(bool paused)

bool EmuThread::stop()
{
if(!isRunning())
return true;
QTimer::stop();

exiting = true;
setPaused(false);
Expand All @@ -226,13 +274,6 @@ bool EmuThread::stop()
// Cause the cpu core to leave the loop and check for events
cycle_count_delta = 0;

if(!this->wait(200))
{
terminate();
if(!this->wait(200))
return false;
}

emu_cleanup();
return true;
}
Expand Down
12 changes: 9 additions & 3 deletions emuthread.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#ifndef EMUTHREAD_H
#define EMUTHREAD_H

#include <QThread>
#include <QTimer>

class EmuThread : public QThread
class EmuThread : public QTimer
{
Q_OBJECT
public:
Expand All @@ -12,6 +12,8 @@ class EmuThread : public QThread
void doStuff(bool wait);
void throttleTimerWait(unsigned int usec);

bool isRunning() { return isActive(); };

QString boot1, flash;
unsigned int port_gdb = 0, port_rdbg = 0;

Expand Down Expand Up @@ -39,7 +41,7 @@ class EmuThread : public QThread
void debugInputRequested(bool b);

public slots:
virtual void run() override;
void start();

// State
void setPaused(bool is_paused);
Expand All @@ -56,7 +58,11 @@ public slots:
void enterDebugger();
void debuggerInput(QString str);

void loopStep();

private:
void startup(bool reset);

bool enter_debugger = false;
bool is_paused = false, do_suspend = false, do_resume = false;
std::string debug_input, snapshot_path;
Expand Down
12 changes: 9 additions & 3 deletions firebird.pro
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ unix: !android {

QMAKE_CFLAGS += -g -std=gnu11 -Wall -Wextra
QMAKE_CXXFLAGS += -g -Wall -Wextra -D QT_NO_CAST_FROM_ASCII
LIBS += -lz
wasm {
QMAKE_CFLAGS += -s USE_ZLIB
QMAKE_CXXFLAGS += -s USE_ZLIB
QMAKE_LFLAGS += -s USE_ZLIB
} else: LIBS += -lz

# Override bad default options to enable better optimizations
QMAKE_CFLAGS_RELEASE = -O3 -DNDEBUG
Expand All @@ -53,7 +57,9 @@ QMAKE_CXXFLAGS_RELEASE = -O3 -DNDEBUG
# with Qt's -reduce-relocations option (QTBUG-86173).
# MinGW fails with
# lto1.exe: internal compiler error: in gen_subprogram_die, at dwarf2out.c:22668
!clang | !if(linux|freebsd): !win32: CONFIG += ltcg
# wasm makes qmake misbehave: the various "-s FOO" options get their "-s" merged
# into a single one.
!clang | !if(linux|freebsd|wasm): !win32: CONFIG += ltcg

# noexecstack is not supported by MinGW's as
!win32 {
Expand All @@ -79,7 +85,7 @@ android {
QMAKE_LFLAGS += -Wl,-Bsymbolic
}

ios|android: DEFINES += MOBILE_UI
ios|android|wasm: DEFINES += MOBILE_UI

ios {
DEFINES += IS_IOS_BUILD
Expand Down
Loading