Add Game Boy emulator support using WasmBoy (#7717)

This commit is contained in:
Patrick Quist
2025-05-24 18:43:41 +02:00
committed by GitHub
parent 91a2d58aa2
commit 6fb3ab38e7
6 changed files with 87 additions and 8 deletions

View File

@@ -13,6 +13,7 @@ These are using Javascript and/or using external websites to facilitate emulatio
with Z88DK (target `+sms`) with Z88DK (target `+sms`)
- [Viciious](https://github.com/compiler-explorer/viciious) (https://static.ce-cdn.net/viciious/viciious.html) - for - [Viciious](https://github.com/compiler-explorer/viciious) (https://static.ce-cdn.net/viciious/viciious.html) - for
`.prg` files built with LLVM MOS C64 or CC65 (`--target c64`) `.prg` files built with LLVM MOS C64 or CC65 (`--target c64`)
- [WasmBoy](https://github.com/compiler-explorer/wasmboy) (https://static.ce-cdn.net/wasmboy/index.html) - for `.gb` ROM files built with z88dk (target `+gb`)
## Examples ## Examples

View File

@@ -129,6 +129,10 @@ export class z88dkCompiler extends BaseCompiler {
return `${this.outputFilebase}.sms`; return `${this.outputFilebase}.sms`;
} }
getGbfilename() {
return `${this.outputFilebase}.gb`;
}
override async objdump( override async objdump(
outputFilename: string, outputFilename: string,
result: any, result: any,
@@ -189,6 +193,11 @@ export class z88dkCompiler extends BaseCompiler {
if (await utils.fileExists(smsFilepath)) { if (await utils.fileExists(smsFilepath)) {
await addArtifactToResult(result, smsFilepath, ArtifactType.smsrom); await addArtifactToResult(result, smsFilepath, ArtifactType.smsrom);
} }
const gbFilepath = path.join(result.dirPath, this.getGbfilename());
if (await utils.fileExists(gbFilepath)) {
await addArtifactToResult(result, gbFilepath, ArtifactType.gbrom);
}
} }
return result; return result;

View File

@@ -1782,6 +1782,8 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.emulateC64Prg(artifact); this.emulateC64Prg(artifact);
} else if (artifact.type === ArtifactType.heaptracktxt) { } else if (artifact.type === ArtifactType.heaptracktxt) {
this.offerViewInSpeedscope(artifact); this.offerViewInSpeedscope(artifact);
} else if (artifact.type === ArtifactType.gbrom) {
this.emulateGameBoyROM(artifact);
} }
} }
} }
@@ -1807,9 +1809,9 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
'?' + '?' +
tmstr + tmstr +
'#customFilename=' + '#customFilename=' +
artifact.name + encodeURIComponent(artifact.name) +
'&b64data=' + '&b64data=' +
artifact.content; encodeURIComponent(artifact.content);
window.open(speedscope_url); window.open(speedscope_url);
}); });
}, },
@@ -1876,7 +1878,11 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
if ('contentWindow' in miracleMenuFrame) { if ('contentWindow' in miracleMenuFrame) {
const emuwindow = unwrap(miracleMenuFrame.contentWindow); const emuwindow = unwrap(miracleMenuFrame.contentWindow);
const tmstr = Date.now(); const tmstr = Date.now();
emuwindow.location = 'https://xania.org/miracle/miracle.html?' + tmstr + '#b64sms=' + image; emuwindow.location =
'https://xania.org/miracle/miracle.html?' +
tmstr +
'#b64sms=' +
encodeURIComponent(image);
} }
}); });
}, },
@@ -1905,7 +1911,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
const emuwindow = unwrap(speccyemuframe.contentWindow); const emuwindow = unwrap(speccyemuframe.contentWindow);
const tmstr = Date.now(); const tmstr = Date.now();
emuwindow.location = emuwindow.location =
'https://static.ce-cdn.net/jsspeccy/index.html?' + tmstr + '#b64tape=' + image; 'https://static.ce-cdn.net/jsspeccy/index.html?' +
tmstr +
'#b64tape=' +
encodeURIComponent(image);
} }
}); });
}, },
@@ -1932,7 +1941,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
const emuwindow = unwrap(jsbeebemuframe.contentWindow); const emuwindow = unwrap(jsbeebemuframe.contentWindow);
const tmstr = Date.now(); const tmstr = Date.now();
emuwindow.location = emuwindow.location =
'https://bbc.godbolt.org/?' + tmstr + '#embed&autoboot&disc1=b64data:' + bbcdiskimage; 'https://bbc.godbolt.org/?' +
tmstr +
'#embed&autoboot&disc1=b64data:' +
encodeURIComponent(bbcdiskimage);
} }
}); });
}, },
@@ -1959,7 +1971,10 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
const emuwindow = unwrap(jsnesemuframe.contentWindow); const emuwindow = unwrap(jsnesemuframe.contentWindow);
const tmstr = Date.now(); const tmstr = Date.now();
emuwindow.location = emuwindow.location =
'https://static.ce-cdn.net/jsnes-ceweb/index.html?' + tmstr + '#b64nes=' + nesrom; 'https://static.ce-cdn.net/jsnes-ceweb/index.html?' +
tmstr +
'#b64nes=' +
encodeURIComponent(nesrom);
} }
}); });
}, },
@@ -1981,9 +1996,9 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
'https://static.ce-cdn.net/viciious/viciious.html?' + 'https://static.ce-cdn.net/viciious/viciious.html?' +
tmstr + tmstr +
'#filename=' + '#filename=' +
prg.title + encodeURIComponent(prg.title) +
'&b64c64=' + '&b64c64=' +
prg.content; encodeURIComponent(prg.content);
window.open(url, '_blank'); window.open(url, '_blank');
}); });
@@ -1992,6 +2007,52 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
); );
} }
emulateGameBoyROM(prg: Artifact): void {
const dialog = $('#gbemu');
this.alertSystem.notify(
'Click <a target="_blank" id="emulink" style="cursor:pointer;" click="javascript:;">here</a> to emulate with a debugger, ' +
'or <a target="_blank" id="emulink-play" style="cursor:pointer;" click="javascript:;">here</a> to emulate just to play.',
{
group: 'emulation',
collapseSimilar: true,
dismissTime: 10000,
onBeforeShow: elem => {
elem.find('#emulink').on('click', () => {
const tmstr = Date.now();
const url =
'https://static.ce-cdn.net/wasmboy/index.html?' +
tmstr +
'#rom-name=' +
encodeURIComponent(prg.title) +
'&rom-data=' +
encodeURIComponent(prg.content);
window.open(url, '_blank');
});
elem.find('#emulink-play').on('click', () => {
BootstrapUtils.showModal(dialog);
const gbemuframe = dialog.find('#gbemuframe')[0];
assert(gbemuframe instanceof HTMLIFrameElement);
if ('contentWindow' in gbemuframe) {
const emuwindow = unwrap(gbemuframe.contentWindow);
const tmstr = Date.now();
const url =
'https://static.ce-cdn.net/wasmboy/iframe/index.html?' +
tmstr +
'#rom-name=' +
encodeURIComponent(prg.title) +
'&rom-data=' +
encodeURIComponent(prg.content);
emuwindow.location = url;
}
});
},
},
);
}
onEditorChange(editor: number, source: string, langId: string, compilerId?: number): void { onEditorChange(editor: number, source: string, langId: string, compilerId?: number): void {
if (this.sourceTreeId) { if (this.sourceTreeId) {
const tree = this.hub.getTreeById(this.sourceTreeId); const tree = this.hub.getTreeById(this.sourceTreeId);

View File

@@ -59,6 +59,7 @@ export enum ArtifactType {
timetrace = 'timetracejson', timetrace = 'timetracejson',
c64prg = 'c64prg', c64prg = 'c64prg',
heaptracktxt = 'heaptracktxt', heaptracktxt = 'heaptracktxt',
gbrom = 'gbrom',
} }
export type Artifact = { export type Artifact = {

View File

@@ -36,3 +36,5 @@ include jsspeccyemu
include miracleemu include miracleemu
include jsnesemu include jsnesemu
include gbemu

5
views/popups/gbemu.pug Normal file
View File

@@ -0,0 +1,5 @@
#gbemu.modal.fade.gl_keep(tabindex="-1" role="dialog")
.modal-dialog.modal-xl
.modal-content
.modal-body
iframe#gbemuframe(src="about:blank" width="670" height="560")