mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-06-05 02:29:31 +00:00
c64: more work on WASMMachine; use more await for ui
This commit is contained in:
parent
7d6e910e57
commit
4a1ac1bbac
|
@ -925,8 +925,8 @@ export function lookupSymbol(platform:Platform, addr:number, extra:boolean) {
|
|||
|
||||
/// new Machine platform adapters
|
||||
|
||||
import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsKeyInput, SavesState, SavesInputState, HasCPU, TrapCondition } from "./devices";
|
||||
import { Probeable, RasterFrameBased, AcceptsPaddleInput } from "./devices";
|
||||
import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsKeyInput, SavesState, SavesInputState, HasCPU, TrapCondition, CPU } from "./devices";
|
||||
import { Probeable, RasterFrameBased, AcceptsPaddleInput, SampledAudioSink } from "./devices";
|
||||
import { SampledAudio } from "./audio";
|
||||
import { ProbeRecorder } from "./recorder";
|
||||
|
||||
|
@ -986,7 +986,7 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
|
||||
start() {
|
||||
const m = this.machine;
|
||||
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
||||
var videoFrequency;
|
||||
if (hasVideo(m)) {
|
||||
var vp = m.getVideoParams();
|
||||
this.video = new RasterVideo(this.mainElement, vp.width, vp.height, {overscan:!!vp.overscan,rotate:vp.rotate|0});
|
||||
|
@ -997,7 +997,9 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
|
|||
this.video.setKeyboardEvents(m.setKeyInput.bind(m));
|
||||
this.poller = new ControllerPoller(m.setKeyInput.bind(m));
|
||||
}
|
||||
videoFrequency = vp.videoFrequency;
|
||||
}
|
||||
this.timer = new AnimationTimer(videoFrequency || 60, this.nextFrame.bind(this));
|
||||
if (hasAudio(m)) {
|
||||
var ap = m.getAudioParams();
|
||||
this.audio = new SampledAudio(ap.sampleRate);
|
||||
|
@ -1125,31 +1127,36 @@ export class WASMMachine implements Machine {
|
|||
pixel_dest : Uint32Array;
|
||||
pixel_src : Uint32Array;
|
||||
romptr : number;
|
||||
romlen : number;
|
||||
romarr : Uint8Array;
|
||||
|
||||
// TODO
|
||||
cpu = {
|
||||
getPC() : number {
|
||||
return 0;
|
||||
},
|
||||
getSP() : number {
|
||||
return 0;
|
||||
},
|
||||
isStable() : boolean {
|
||||
return false;
|
||||
},
|
||||
reset() {
|
||||
},
|
||||
connectMemoryBus() {
|
||||
},
|
||||
saveState() {
|
||||
},
|
||||
loadState() {
|
||||
}
|
||||
}
|
||||
stateptr : number;
|
||||
statearr : Uint8Array;
|
||||
cpu : CPU;
|
||||
audio : SampledAudioSink;
|
||||
audioarr : Float32Array;
|
||||
prgstart : number;
|
||||
initstring : string;
|
||||
initindex : number;
|
||||
|
||||
constructor(prefix: string) {
|
||||
this.prefix = prefix;
|
||||
var self = this;
|
||||
this.cpu = {
|
||||
getPC: self.getPC.bind(self),
|
||||
getSP: self.getSP.bind(self),
|
||||
isStable: self.isStable.bind(self),
|
||||
reset: self.reset.bind(self),
|
||||
saveState: () => {
|
||||
self.exports.machine_save_state(self.sys, self.stateptr);
|
||||
return self.getCPUState();
|
||||
},
|
||||
loadState: () => {
|
||||
console.log("loadState not implemented")
|
||||
},
|
||||
connectMemoryBus() {
|
||||
console.log("connectMemoryBus not implemented")
|
||||
},
|
||||
}
|
||||
}
|
||||
async loadWASM() {
|
||||
// fetch WASM
|
||||
|
@ -1170,28 +1177,71 @@ export class WASMMachine implements Machine {
|
|||
// init machine instance
|
||||
this.sys = this.exports.machine_init(cBIOSPointer);
|
||||
console.log('machine_init', this.sys);
|
||||
// create state buffer
|
||||
var statesize = this.exports.machine_get_state_size();
|
||||
this.stateptr = this.exports.malloc(statesize);
|
||||
this.statearr = new Uint8Array(this.exports.memory.buffer, this.stateptr, statesize);
|
||||
// create audio buffer
|
||||
var sampbufsize = 4096*4;
|
||||
this.audioarr = new Float32Array(this.exports.memory.buffer, this.exports.machine_get_sample_buffer(), sampbufsize);
|
||||
}
|
||||
reset() {
|
||||
this.exports.machine_reset(this.sys);
|
||||
// load rom
|
||||
if (this.romptr && this.romlen) {
|
||||
this.exports.machine_load_rom(this.sys, this.romptr, this.romlen);
|
||||
this.prgstart = this.romarr[0] + (this.romarr[1]<<8); // TODO: get starting address
|
||||
if (this.prgstart == 0x801) this.prgstart = 0x80d;
|
||||
}
|
||||
// set init string
|
||||
if (this.prgstart) {
|
||||
this.initstring = "\r\r\r\r\r\r\rSYS " + this.prgstart + "\r";
|
||||
this.initindex = 0;
|
||||
}
|
||||
}
|
||||
getPC() : number {
|
||||
return this.exports.machine_cpu_get_pc(this.sys);
|
||||
}
|
||||
getSP() : number {
|
||||
return this.exports.machine_cpu_get_sp(this.sys);
|
||||
}
|
||||
isStable() : boolean {
|
||||
return this.exports.machine_cpu_is_stable(this.sys);
|
||||
}
|
||||
loadROM(rom: Uint8Array) {
|
||||
if (!this.romptr) {
|
||||
this.romptr = this.exports.malloc(0x10000);
|
||||
this.romarr = new Uint8Array(this.exports.memory.buffer, this.romptr, 0x10000);
|
||||
}
|
||||
this.reset();
|
||||
// this.exports.c64_exec(this.sys, 1000000);
|
||||
this.romarr.set(rom);
|
||||
this.exports.machine_load_rom(this.sys, this.romptr, rom.length);
|
||||
this.romlen = rom.length;
|
||||
this.reset();
|
||||
}
|
||||
advanceFrame(trap: TrapCondition) : number {
|
||||
var i : number;
|
||||
var cpf = 19656; // TODO: pal, const
|
||||
if (trap) {
|
||||
// TODO
|
||||
for (i=0; i<cpf; i++) {
|
||||
if (trap && trap()) {
|
||||
break;
|
||||
}
|
||||
this.exports.machine_tick(this.sys);
|
||||
}
|
||||
} else {
|
||||
this.exports.c64_exec(this.sys, 16421);
|
||||
this.exports.machine_exec(this.sys, cpf);
|
||||
i = cpf;
|
||||
this.typeInitString(); // TODO: type init string into console (doesnt work on reset)
|
||||
}
|
||||
this.syncVideo();
|
||||
return 0; // TODO
|
||||
this.syncAudio();
|
||||
return i;
|
||||
}
|
||||
typeInitString() {
|
||||
if (this.initstring) {
|
||||
var ch = this.initstring.charCodeAt(this.initindex >> 1);
|
||||
this.setKeyInput(ch, 0, (this.initindex&1) ? KeyFlags.KeyUp : KeyFlags.KeyDown);
|
||||
this.initindex++;
|
||||
}
|
||||
}
|
||||
read(address: number) : number {
|
||||
return this.exports.machine_mem_read(this.sys, address & 0xffff);
|
||||
|
@ -1202,17 +1252,54 @@ export class WASMMachine implements Machine {
|
|||
write(address: number, value: number) : void {
|
||||
this.exports.machine_mem_write(this.sys, address & 0xffff, value & 0xff);
|
||||
}
|
||||
saveState() : EmuState {
|
||||
return null;
|
||||
getCPUState() {
|
||||
return {
|
||||
PC:this.getPC(),
|
||||
SP:this.getSP(),
|
||||
A:this.statearr[14],
|
||||
X:this.statearr[15],
|
||||
Y:this.statearr[16],
|
||||
S:this.statearr[17],
|
||||
flags:this.statearr[18],
|
||||
C:this.statearr[18] & 1,
|
||||
Z:this.statearr[18] & 2,
|
||||
I:this.statearr[18] & 4,
|
||||
D:this.statearr[18] & 8,
|
||||
V:this.statearr[18] & 64,
|
||||
N:this.statearr[18] & 128,
|
||||
}
|
||||
}
|
||||
loadState(state: EmuState) : void {
|
||||
/*
|
||||
setPC(pc: number) {
|
||||
this.exports.machine_save_state(this.sys, this.stateptr);
|
||||
this.statearr[10] = pc & 0xff;
|
||||
this.statearr[11] = pc >> 8;
|
||||
this.exports.machine_load_state(this.sys, this.stateptr);
|
||||
}
|
||||
*/
|
||||
saveState() {
|
||||
this.exports.machine_save_state(this.sys, this.stateptr);
|
||||
// TODO: take out CPU state, memory
|
||||
return {
|
||||
c:this.getCPUState(),
|
||||
state:this.statearr.slice(0)
|
||||
};
|
||||
}
|
||||
loadState(state) : void {
|
||||
this.statearr.set(state.state);
|
||||
this.exports.machine_load_state(this.sys, this.stateptr);
|
||||
}
|
||||
saveControlsState() : any {
|
||||
// TODO
|
||||
}
|
||||
loadControlsState(state) : void {
|
||||
// TODO
|
||||
}
|
||||
getVideoParams() {
|
||||
return {width:392, height:272, overscan:true}; // TODO: const
|
||||
return {width:392, height:272, overscan:true, videoFrequency:50}; // TODO: const
|
||||
}
|
||||
getAudioParams() {
|
||||
return {sampleRate:44100, stereo:false};
|
||||
}
|
||||
connectVideo(pixels:Uint32Array) : void {
|
||||
this.pixel_dest = pixels;
|
||||
|
@ -1227,10 +1314,25 @@ export class WASMMachine implements Machine {
|
|||
}
|
||||
}
|
||||
setKeyInput(key: number, code: number, flags: number): void {
|
||||
// TODO: handle shifted keys
|
||||
if (key == 16 || key == 17 || key == 18 || key == 224) return; // meta keys
|
||||
//console.log(key, code, flags);
|
||||
//if (flags & KeyFlags.Shift) { key += 64; }
|
||||
if (flags & KeyFlags.KeyDown) {
|
||||
this.exports.machine_key_down(this.sys, key);
|
||||
} else if (flags & KeyFlags.KeyUp) {
|
||||
this.exports.machine_key_up(this.sys, key);
|
||||
}
|
||||
}
|
||||
connectAudio(audio : SampledAudioSink) : void {
|
||||
this.audio = audio;
|
||||
}
|
||||
syncAudio() {
|
||||
if (this.audio != null) {
|
||||
var n = this.exports.machine_get_sample_count();
|
||||
for (var i=0; i<n; i++) {
|
||||
this.audio.feedSample(this.audioarr[i], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ export interface VideoParams {
|
|||
height : number;
|
||||
overscan? : boolean;
|
||||
rotate? : number;
|
||||
videoFrequency? : number; // default = 60
|
||||
}
|
||||
|
||||
// TODO: frame buffer optimization (apple2, etc)
|
||||
|
|
|
@ -299,30 +299,28 @@ function loadMainWindow(preset_id:string) {
|
|||
current_project.setMainFile(preset_id);
|
||||
}
|
||||
|
||||
function loadProject(preset_id:string) {
|
||||
async function loadProject(preset_id:string) {
|
||||
// set current file ID
|
||||
// TODO: this is done twice
|
||||
current_project.mainPath = preset_id;
|
||||
setLastPreset(preset_id);
|
||||
// load files from storage or web URLs
|
||||
current_project.loadFiles([preset_id]).then((result) => {
|
||||
measureTimeLoad = new Date(); // for timing calc.
|
||||
if (result && result.length) {
|
||||
// file found; continue
|
||||
loadMainWindow(preset_id);
|
||||
} else {
|
||||
getSkeletonFile(preset_id).then((skel) => {
|
||||
current_project.filedata[preset_id] = skel || "\n";
|
||||
loadMainWindow(preset_id);
|
||||
// don't alert if we selected "new file"
|
||||
if (!qs['newfile']) {
|
||||
alertInfo("Could not find file \"" + preset_id + "\". Loading default file.");
|
||||
}
|
||||
delete qs['newfile'];
|
||||
replaceURLState();
|
||||
});
|
||||
var result = await current_project.loadFiles([preset_id]);
|
||||
measureTimeLoad = new Date(); // for timing calc.
|
||||
if (result && result.length) {
|
||||
// file found; continue
|
||||
loadMainWindow(preset_id);
|
||||
} else {
|
||||
var skel = await getSkeletonFile(preset_id);
|
||||
current_project.filedata[preset_id] = skel || "\n";
|
||||
loadMainWindow(preset_id);
|
||||
// don't alert if we selected "new file"
|
||||
if (!qs['newfile']) {
|
||||
alertInfo("Could not find file \"" + preset_id + "\". Loading default file.");
|
||||
}
|
||||
});
|
||||
delete qs['newfile'];
|
||||
replaceURLState();
|
||||
}
|
||||
}
|
||||
|
||||
function reloadProject(id:string) {
|
||||
|
@ -932,21 +930,19 @@ function populateRepos(sel) {
|
|||
}
|
||||
}
|
||||
|
||||
function populateFiles(sel:JQuery, category:string, prefix:string, foundFiles:{}, callback:() => void) {
|
||||
store.keys().then( (keys:string[]) => {
|
||||
var numFound = 0;
|
||||
if (!keys) keys = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
if (key.startsWith(prefix) && !foundFiles[key]) {
|
||||
if (numFound++ == 0)
|
||||
sel.append($("<option />").text("------- " + category + " -------").attr('disabled','true'));
|
||||
var name = key.substring(prefix.length);
|
||||
sel.append($("<option />").val(key).text(name).attr('selected',(key==current_project.mainPath)?'selected':null));
|
||||
}
|
||||
async function populateFiles(sel:JQuery, category:string, prefix:string, foundFiles:{}) {
|
||||
var keys = await store.keys();
|
||||
var numFound = 0;
|
||||
if (!keys) keys = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
if (key.startsWith(prefix) && !foundFiles[key]) {
|
||||
if (numFound++ == 0)
|
||||
sel.append($("<option />").text("------- " + category + " -------").attr('disabled','true'));
|
||||
var name = key.substring(prefix.length);
|
||||
sel.append($("<option />").val(key).text(name).attr('selected',(key==current_project.mainPath)?'selected':null));
|
||||
}
|
||||
if (callback) { callback(); }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function finishSelector(sel) {
|
||||
|
@ -958,21 +954,19 @@ function finishSelector(sel) {
|
|||
}
|
||||
}
|
||||
|
||||
function updateSelector() {
|
||||
async function updateSelector() {
|
||||
var sel = $("#preset_select").empty();
|
||||
if (!repo_id) {
|
||||
// normal: populate repos, examples, and local files
|
||||
populateRepos(sel);
|
||||
var foundFiles = populateExamples(sel);
|
||||
populateFiles(sel, "Local Files", "", foundFiles, () => {
|
||||
finishSelector(sel);
|
||||
});
|
||||
await populateFiles(sel, "Local Files", "", foundFiles);
|
||||
finishSelector(sel);
|
||||
} else {
|
||||
sel.append($("<option />").val('/').text('Leave Repository'));
|
||||
// repo: populate all files
|
||||
populateFiles(sel, repo_id, "", {}, () => {
|
||||
finishSelector(sel);
|
||||
});
|
||||
await populateFiles(sel, repo_id, "", {});
|
||||
finishSelector(sel);
|
||||
}
|
||||
// set click handlers
|
||||
sel.off('change').change(function(e) {
|
||||
|
@ -1037,15 +1031,16 @@ function setCompileOutput(data: WorkerResult) {
|
|||
}
|
||||
}
|
||||
|
||||
function loadBIOSFromProject() {
|
||||
async function loadBIOSFromProject() {
|
||||
if (platform.loadBIOS) {
|
||||
var biospath = platform_id + '.rom';
|
||||
store.getItem(biospath).then( (biosdata) => {
|
||||
if (biosdata instanceof Uint8Array) {
|
||||
console.log('loading BIOS')
|
||||
platform.loadBIOS('BIOS', biosdata);
|
||||
}
|
||||
});
|
||||
var biosdata = await store.getItem(biospath);
|
||||
if (biosdata instanceof Uint8Array) {
|
||||
console.log('loading BIOS')
|
||||
platform.loadBIOS('BIOS', biosdata);
|
||||
} else {
|
||||
console.log('BIOS file must be binary')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1815,14 +1810,14 @@ async function startPlatform() {
|
|||
// start platform and load file
|
||||
replaceURLState();
|
||||
await platform.start();
|
||||
installGAHooks();
|
||||
loadBIOSFromProject();
|
||||
initProject();
|
||||
loadProject(qs['file']);
|
||||
await loadBIOSFromProject();
|
||||
await initProject();
|
||||
await loadProject(qs['file']);
|
||||
setupDebugControls();
|
||||
updateSelector();
|
||||
addPageFocusHandlers();
|
||||
showInstructions();
|
||||
installGAHooks();
|
||||
revealTopBar();
|
||||
}
|
||||
|
||||
|
|
|
@ -190,7 +190,7 @@ var PLATFORM_PARAMS = {
|
|||
'apple2': {
|
||||
define: '__APPLE2__',
|
||||
cfgfile: 'apple2-hgr.cfg',
|
||||
libargs: ['apple2.lib'],
|
||||
libargs: [ '--lib-path', '/share/target/apple2/drv', '-D', '__EXEHDR__=0', 'apple2.lib'],
|
||||
__CODE_RUN__: 16384,
|
||||
code_start: 0x803,
|
||||
},
|
||||
|
@ -241,7 +241,7 @@ var PLATFORM_PARAMS = {
|
|||
},
|
||||
'c64': {
|
||||
define: '__C64__',
|
||||
cfgfile: 'c64.cfg', // SYS 2058
|
||||
cfgfile: 'c64.cfg', // SYS 2061
|
||||
libargs: ['c64.lib'],
|
||||
//extra_link_files: ['c64-cart.cfg'],
|
||||
},
|
||||
|
@ -952,8 +952,6 @@ function linkLD65(step:BuildStep) {
|
|||
var cfgfile = params.cfgfile;
|
||||
var args = ['--cfg-path', '/share/cfg',
|
||||
'--lib-path', '/share/lib',
|
||||
'--lib-path', '/share/target/apple2/drv', // TODO
|
||||
'-D', '__EXEHDR__=0', // TODO
|
||||
'-C', cfgfile,
|
||||
'-Ln', 'main.vice',
|
||||
//'--dbgfile', 'main.dbg', // TODO: get proper line numbers
|
||||
|
|
BIN
wasm/c64.wasm
BIN
wasm/c64.wasm
Binary file not shown.
Loading…
Reference in New Issue
Block a user