Skip to content

Commit

Permalink
Initial Emscripten/WASM port of SheepShaver
Browse files Browse the repository at this point in the history
Mostly mirrors the BasiliskII setup, as far as making autoconf changes to direct
things on the correct path, use a custom video implementation (instead of SDL or
X11), etc.

To share code with the BasiliskII implementation the same approach as the other
BasiliskII/SheepShaver ports is create, and symlinks from SheepShaver into the
BasiliskII directory are created. The video_js.cpp implementation needs a bunch
of #if SHEEPSHAVER branches to make the BasiliskII implementatio work for
SheepShaver too, it's modeled after video_sdl.cpp.

SheepShaver peculiarities that had to be taken into account:
- We can't rely on being able to intercept SIGSEGVs to handle low memory
  globals, we instead put Emscripten on the same code path as the macOS x86-64
  port (see 5065fb9).
- Hardcode MAP_ANONYMOUS to being available when using mmap, otherwise we can't
  mmap the /dev/zero equivalent.
- We don't currently use WASM threads, so instead of a separate tick thread we
  periodically yield from the CPU emulator and trigger the VBL and tick
  interrupts

Does not include audio, input, ethernet, idlewait or other features, but is
enough to boot into the Finder given a Mac OS 9.0.4 image.

Also includes a bit of cleanup for both projects:
- Reduce the initial Emscriptem heap size from 512MB to 256MB -- we don't
  actually need that much.
- Remove unncessary manual config.h re-defines for BasiliskII (autoconf generats
  the correct values)

Updates mihaip/infinite-mac#34
  • Loading branch information
mihaip committed Oct 22, 2022
1 parent b9dd6b4 commit b8b8680
Show file tree
Hide file tree
Showing 19 changed files with 387 additions and 57 deletions.
258 changes: 237 additions & 21 deletions BasiliskII/src/Unix/JS/video_js.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include <emscripten.h>

#include <vector>

#include "SpookyV2.h"
#include "cpu_emulation.h"
#include "main.h"
Expand All @@ -14,9 +16,81 @@
#define DEBUG 0
#include "debug.h"

/*
* SheepShaver glue
*/

#ifdef SHEEPSHAVER

using std::vector;

// Color depth modes type
typedef int video_depth;

// 1, 2, 4 and 8 bit depths use a color palette
static inline bool IsDirectMode(VIDEO_MODE const& mode) {
return IsDirectMode(mode.viAppleMode);
}

// Abstract base class representing one (possibly virtual) monitor
// ("monitor" = rectangular display with a contiguous frame buffer)
class monitor_desc {
public:
monitor_desc(const vector<VIDEO_MODE>& available_modes,
video_depth default_depth,
uint32 default_id) {}
virtual ~monitor_desc() {}

// Get current Mac frame buffer base address
uint32 get_mac_frame_base(void) const { return screen_base; }

// Set Mac frame buffer base address (called from switch_to_mode())
void set_mac_frame_base(uint32 base) { screen_base = base; }

// Get current video mode
const VIDEO_MODE& get_current_mode(void) const { return VModes[cur_mode]; }

// Called by the video driver to switch the video mode on this display
// (must call set_mac_frame_base())
virtual void switch_to_current_mode(void) = 0;

// Called by the video driver to set the color palette (in indexed modes)
// or the gamma table (in direct modes)
virtual void set_palette(uint8* pal, int num) = 0;
};

// Vector of pointers to available monitor descriptions, filled by VideoInit()
static vector<monitor_desc*> VideoMonitors;

// Find Apple mode matching best specified dimensions
static int find_apple_resolution(int xsize, int ysize) {
if (xsize == 640 && ysize == 480)
return APPLE_640x480;
if (xsize == 800 && ysize == 600)
return APPLE_800x600;
if (xsize == 1024 && ysize == 768)
return APPLE_1024x768;
if (xsize == 1152 && ysize == 768)
return APPLE_1152x768;
if (xsize == 1152 && ysize == 900)
return APPLE_1152x900;
if (xsize == 1280 && ysize == 1024)
return APPLE_1280x1024;
if (xsize == 1600 && ysize == 1200)
return APPLE_1600x1200;
return APPLE_CUSTOM;
}

// Display error alert
static void ErrorAlert(int error) {
ErrorAlert(GetString(error));
}

#endif

class JS_monitor_desc : public monitor_desc {
public:
JS_monitor_desc(const vector<video_mode>& available_modes,
JS_monitor_desc(const vector<VIDEO_MODE>& available_modes,
video_depth default_depth,
uint32 default_id)
: monitor_desc(available_modes, default_depth, default_id),
Expand Down Expand Up @@ -54,19 +128,18 @@ void JS_monitor_desc::switch_to_current_mode() {

bool JS_monitor_desc::video_open() {
D(bug("video_open()\n"));
const video_mode& mode = get_current_mode();
const VIDEO_MODE& mode = get_current_mode();
D(bug("Current video mode:\n"));
D(bug(" %dx%d (ID %02x), %d bpp\n", mode.x, mode.y, mode.resolution_id,
1 << (mode.depth & 0x0f)));
D(bug(" %dx%d (ID %02x), %d bpp\n", VIDEO_MODE_X, VIDEO_MODE_Y, VIDEO_MODE_RESOLUTION,
1 << (VIDEO_MODE_DEPTH & 0x0f)));

mac_framebuffer_size = mode.bytes_per_row * mode.y;
mac_framebuffer_size = VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y;
mac_framebuffer = (uint8*)malloc(mac_framebuffer_size);
if (mac_framebuffer == NULL) {
return false;
}

set_mac_frame_base((unsigned int)Host2MacAddr((uint8*)mac_framebuffer));
InitFrameBufferMapping();

VisualFormat visualFormat;
visualFormat.depth = 32;
Expand All @@ -75,15 +148,14 @@ bool JS_monitor_desc::video_open() {
visualFormat.Gmask = 0xff0000;
visualFormat.Bmask = 0xff000000;

is_raw_screen_blit = !Screen_blitter_init(visualFormat, true, 1 << (mode.depth & 0x0f));
D(bug(" is_raw_screen_blit=%s\n", is_raw_screen_blit ? "true" : "false"));
browser_framebuffer_size = mode.y * 4 * mode.x;
is_raw_screen_blit = !Screen_blitter_init(visualFormat, true, 1 << (VIDEO_MODE_DEPTH & 0x0f));
browser_framebuffer_size = VIDEO_MODE_Y * 4 * VIDEO_MODE_X;
browser_framebuffer = (uint8*)malloc(browser_framebuffer_size);
if (browser_framebuffer == NULL) {
return false;
}

EM_ASM_({ workerApi.didOpenVideo($0, $1); }, mode.x, mode.y);
EM_ASM_({ workerApi.didOpenVideo($0, $1); }, VIDEO_MODE_X, VIDEO_MODE_Y);

return true;
}
Expand All @@ -95,9 +167,9 @@ void JS_monitor_desc::video_close() {
}

void JS_monitor_desc::video_blit() {
const video_mode& mode = get_current_mode();
const VIDEO_MODE& mode = get_current_mode();

uint64 hash = SpookyHash::Hash64(mac_framebuffer, mode.x * mode.y * 4, 0);
uint64 hash = SpookyHash::Hash64(mac_framebuffer, mac_framebuffer_size, 0);
if (!is_raw_screen_blit) {
hash ^= last_palette_hash;
}
Expand Down Expand Up @@ -125,7 +197,7 @@ void JS_monitor_desc::video_blit() {
}

void JS_monitor_desc::set_palette(uint8* pal, int num_in) {
const video_mode& mode = get_current_mode();
const VIDEO_MODE& mode = get_current_mode();

if (!IsDirectMode(mode)) {
for (int i = 0; i < 256; i++) {
Expand All @@ -145,7 +217,13 @@ void JS_monitor_desc::set_gamma(uint8* gamma, int num) {
// Not implemented
}

#ifdef SHEEPSHAVER
bool VideoInit(void) {
const bool classic = false;
#else
bool VideoInit(bool classic) {
#endif

D(bug("VideoInit(classic=%s)\n", classic ? "true" : "false"));

// Determine display type and default dimensions. "classic" is supposed to
Expand All @@ -164,7 +242,9 @@ bool VideoInit(bool classic) {
int h;
int resolution_id;
} defs[] = {{default_width, default_height, 0x80},
#ifndef SHEEPSHAVER // Omit Classic resolutions
{512, 384, 0x80},
#endif
{640, 480, 0x81},
{800, 600, 0x82},
{1024, 768, 0x83},
Expand All @@ -175,7 +255,7 @@ bool VideoInit(bool classic) {
0,
}};

static vector<video_mode> video_modes;
static vector<VIDEO_MODE> video_modes;

for (int i = 0; defs[i].w != 0; i++) {
const int w = defs[i].w;
Expand All @@ -184,19 +264,63 @@ bool VideoInit(bool classic) {
continue;
}
for (int d = VIDEO_DEPTH_1BIT; d <= VIDEO_DEPTH_32BIT; d++) {
video_mode mode;
mode.x = w;
mode.y = h;
VIDEO_MODE mode;
VIDEO_MODE_X = w;
VIDEO_MODE_Y = h;
#ifdef SHEEPSHAVER
mode.viAppleID = find_apple_resolution(w, h);
mode.viType = DIS_WINDOW;
#else
mode.resolution_id = defs[i].resolution_id;
mode.bytes_per_row = TrivialBytesPerRow(w, (video_depth)d);
mode.depth = (video_depth)d;
mode.user_data = 0;
#endif
VIDEO_MODE_ROW_BYTES = TrivialBytesPerRow(w, (video_depth)d);
VIDEO_MODE_DEPTH = (video_depth)d;
D(bug("adding mode with %dx%d (ID %02x, index: %d), %d bpp\n", VIDEO_MODE_X, VIDEO_MODE_Y,
VIDEO_MODE_RESOLUTION, video_modes.size(), 1 << (VIDEO_MODE_DEPTH & 0x0f)));

video_modes.push_back(mode);
}
}

// Find requested default mode with specified dimensions
bool found_default_mode = false;
uint32 default_id;
#ifdef SHEEPSHAVER
cur_mode = 0;
#endif
for (auto& mode : video_modes) {
if (VIDEO_MODE_X == default_width && VIDEO_MODE_Y == default_height &&
VIDEO_MODE_DEPTH == default_depth) {
default_id = VIDEO_MODE_RESOLUTION;
found_default_mode = true;
break;
}
#ifdef SHEEPSHAVER
cur_mode++;
#endif
}
if (!found_default_mode) {
const VIDEO_MODE& mode = video_modes[0];
default_depth = VIDEO_MODE_DEPTH;
default_id = VIDEO_MODE_RESOLUTION;
#ifdef SHEEPSHAVER
cur_mode = 0;
#endif
}

#ifdef SHEEPSHAVER
for (int i = 0; i < video_modes.size(); i++)
VModes[i] = video_modes[i];
VideoInfo* p = &VModes[video_modes.size()];
p->viType = DIS_INVALID; // End marker
p->viRowBytes = 0;
p->viXsize = p->viYsize = 0;
p->viAppleMode = 0;
p->viAppleID = 0;
#endif

JS_monitor_desc* monitor =
new JS_monitor_desc(video_modes, (video_depth)default_depth, video_modes[0].resolution_id);
new JS_monitor_desc(video_modes, (video_depth)default_depth, default_id);
VideoMonitors.push_back(monitor);

return monitor->video_open();
Expand All @@ -223,3 +347,95 @@ void VideoInterrupt() {
void VideoQuitFullScreen() {
// No-op
}

#ifdef SHEEPSHAVER

void VideoVBL(void) {
VideoRefresh();
}

int16 video_mode_change(VidLocals* csSave, uint32 ParamPtr) {
D(bug("video_mode_change()\n"));
/* return if no mode change */
if ((csSave->saveData == ReadMacInt32(ParamPtr + csData)) &&
(csSave->saveMode == ReadMacInt16(ParamPtr + csMode))) {
D(bug(" ...unchanged (save data %x, save mode %x, cur_mode=%d)\n", csSave->saveData,
csSave->saveMode, cur_mode));
return noErr;
}

/* first find video mode in table */
for (int i = 0; VModes[i].viType != DIS_INVALID; i++) {
if ((ReadMacInt16(ParamPtr + csMode) == VModes[i].viAppleMode) &&
(ReadMacInt32(ParamPtr + csData) == VModes[i].viAppleID)) {
csSave->saveMode = ReadMacInt16(ParamPtr + csMode);
csSave->saveData = ReadMacInt32(ParamPtr + csData);
csSave->savePage = ReadMacInt16(ParamPtr + csPage);

cur_mode = i;
D(bug(" ...switched to mode %d\n", cur_mode));
monitor_desc* monitor = VideoMonitors[0];
monitor->switch_to_current_mode();

WriteMacInt32(ParamPtr + csBaseAddr, screen_base);
csSave->saveBaseAddr = screen_base;
csSave->saveData = VModes[cur_mode].viAppleID; /* First mode ... */
csSave->saveMode = VModes[cur_mode].viAppleMode;

return noErr;
}
}
return paramErr;
}

// Find palette size for given color depth
static int palette_size(int mode) {
switch (mode) {
case VIDEO_DEPTH_1BIT:
return 2;
case VIDEO_DEPTH_2BIT:
return 4;
case VIDEO_DEPTH_4BIT:
return 16;
case VIDEO_DEPTH_8BIT:
return 256;
case VIDEO_DEPTH_16BIT:
return 32;
case VIDEO_DEPTH_32BIT:
return 256;
default:
return 0;
}
}

void video_set_palette(void) {
monitor_desc* monitor = VideoMonitors[0];
int n_colors = palette_size(monitor->get_current_mode().viAppleMode);
D(bug("video_set_palette(n_colors=%d)\n", n_colors));
uint8 pal[256 * 3];
for (int c = 0; c < n_colors; c++) {
pal[c * 3 + 0] = mac_pal[c].red;
pal[c * 3 + 1] = mac_pal[c].green;
pal[c * 3 + 2] = mac_pal[c].blue;
}
monitor->set_palette(pal, n_colors);
}

bool video_can_change_cursor(void) {
D(bug("video_can_change_cursor()\n"));
return false;
}

void video_set_cursor(void) {
D(bug("video_set_cursor()\n"));
}

void video_set_dirty_area(int x, int y, int w, int h) {
D(bug("video_set_dirty_area(%d, %d, %d, %d)\n", x, y, w, h));
}

void video_set_gamma(int n_colors) {
// TODO
}

#endif // SHEEPSHAVER
4 changes: 2 additions & 2 deletions BasiliskII/src/Unix/_emconfigure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -euo pipefail
em_flags=""
em_ldflags=""

em_ldflags+=" -s INITIAL_MEMORY=536870912"
em_ldflags+=" -s INITIAL_MEMORY=268435456"
em_ldflags+=" -s FORCE_FILESYSTEM=1"
em_ldflags+=" -s WASM=1"

Expand Down Expand Up @@ -41,7 +41,7 @@ export LDFLAGS="$em_flags $em_ldflags"
--disable-vosf \
--enable-emscripten \
--build="`uname -m`-unknown-linux-gnu" \
--cache-file="/tmp/config.cache.emscripten${macemujs_conf_debug:-}"
--cache-file="/tmp/basiliskii.config.cache.emscripten${macemujs_conf_debug:-}"

cat ./em_config.h >> ./config.h

Expand Down
18 changes: 0 additions & 18 deletions BasiliskII/src/Unix/em_config.h
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@

#undef SIZEOF_DOUBLE
#undef SIZEOF_FLOAT
#undef SIZEOF_INT
#undef SIZEOF_LONG
#undef SIZEOF_LONG_DOUBLE
#undef SIZEOF_LONG_LONG
#undef SIZEOF_SHORT
#undef SIZEOF_VOID_P
#define SIZEOF_DOUBLE 8
#define SIZEOF_FLOAT 4
#define SIZEOF_INT 4
#define SIZEOF_LONG 4
#define SIZEOF_LONG_DOUBLE 8
#define SIZEOF_LONG_LONG 8
#define SIZEOF_SHORT 2
#define SIZEOF_VOID_P 4

#define USE_CPU_EMUL_SERVICES 1

#define EMSCRIPTEN 1
Expand Down
Loading

0 comments on commit b8b8680

Please sign in to comment.