Deploying to gh-pages from @ sehugg/8bitworkshop@483675fded 🚀

This commit is contained in:
sehugg 2023-11-02 03:42:54 +00:00
parent 9bb6c517c6
commit c4567bbaee
28 changed files with 854 additions and 89 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

48
gen/chunk-UZ2OPGHD.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,285 @@
"use strict";
/*
https://github.com/eightbitjim/commodore-tape-maker/blob/master/maketape.py
# MIT License
#
# Copyright (c) 2018 eightbitjim
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
*/
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TAPFile = exports.OutputSoundFile = void 0;
class OutputSoundFile {
constructor(options) {
this.options = options;
this.sampleRate = 44100.0;
this.soundData = [];
//00000000 43 36 34 2d 54 41 50 45 2d 52 41 57 01 00 00 00 |C64-TAPE-RAW....|
//00000010 1e 62 03 00
this.tapData = [0x43, 0x36, 0x34, 0x2d, 0x54, 0x41, 0x50, 0x45, 0x2d, 0x52, 0x41, 0x57, 0x01, 0x00, 0x00, 0x00, 0, 0, 0, 0];
}
getWAVHeader() {
const header = new Uint8Array(44);
const view = new DataView(header.buffer);
view.setUint32(0, 0x52494646, false); // "RIFF"
view.setUint32(4, 44 + this.soundData.length, true); // ChunkSize
view.setUint32(8, 0x57415645, false); // "WAVE"
view.setUint32(12, 0x666d7420, false); // "fmt "
view.setUint32(16, 16, true); // Subchunk1Size
view.setUint16(20, 1, true); // AudioFormat (PCM)
view.setUint16(22, 1, true); // NumChannels
view.setUint32(24, this.sampleRate, true); // SampleRate
view.setUint32(28, this.sampleRate * 2, true); // ByteRate
view.setUint16(32, 1, true); // BlockAlign
view.setUint16(34, 8, true); // BitsPerSample
view.setUint32(36, 0x64617461, false); // "data"
view.setUint32(40, this.soundData.length, true); // Subchunk2Size
return header;
}
addSilence(lengthInSeconds) {
const numberOfSamples = Math.floor(this.sampleRate * lengthInSeconds);
for (let i = 0; i < numberOfSamples; i++) {
this.soundData.push(0);
}
//For v1 and v2 TAP files, a $00 value is followed by 3 bytes containing the actual duration measured in clock cycles (not divided by 8). These 3 bytes are in low-high format.
const numCycles = TAPFile.CLOCK_RATE * lengthInSeconds;
this.tapData.push(0);
this.tapData.push(numCycles & 0xff);
this.tapData.push((numCycles >> 8) & 0xff);
this.tapData.push((numCycles >> 16) & 0xff);
}
addCycle(cycles) {
this.tapData.push(cycles);
const numberOfSamples = Math.floor(this.sampleRate * TAPFile.TAP_LENGTH_IN_SECONDS * cycles);
for (let i = 0; i < numberOfSamples; i++) {
let value;
if (this.options.sine_wave) {
value = -Math.sin((i / numberOfSamples) * 2.0 * Math.PI);
}
else {
if (i < numberOfSamples / 2) {
value = -1;
}
else {
value = 1;
}
}
if (this.options.invert_waveform) {
value = -value;
}
this.soundData.push(Math.round(128 + value * 127));
}
}
updateTAPHeader() {
let datalen = this.tapData.length - 0x14;
// set bytes 0x10-0x13 to length
this.tapData[0x10] = datalen & 0xff;
this.tapData[0x11] = (datalen >> 8) & 0xff;
this.tapData[0x12] = (datalen >> 16) & 0xff;
this.tapData[0x13] = (datalen >> 24) & 0xff;
}
getTAPData() {
this.updateTAPHeader();
return new Uint8Array(this.tapData);
}
getSoundData() {
let header = this.getWAVHeader();
let data = new Uint8Array(header.length + this.soundData.length);
data.set(header, 0);
data.set(new Uint8Array(this.soundData), header.length);
return data;
}
}
exports.OutputSoundFile = OutputSoundFile;
class TAPFile {
constructor(filename, options) {
this.options = options;
this.checksum = 0;
this.data = new Uint8Array(0);
this.filenameData = this.makeFilename(filename);
this.startAddress = 0;
this.endAddress = 0;
this.fileType = _a.FILE_TYPE_NONE;
this.waveFile = null;
}
makeFilename(filename) {
const filenameBuffer = [];
const space = 0x20;
filename = filename.toUpperCase(); // for PETSCII
for (let i = 0; i < _a.FILENAME_BUFFER_SIZE; i++) {
if (filename.length <= i) {
filenameBuffer.push(space);
}
else {
let ch = filename.charCodeAt(i);
filenameBuffer.push(ch);
}
}
return filenameBuffer;
}
setContent(inputFile) {
this.data = inputFile.data;
this.startAddress = inputFile.startAddress;
this.endAddress = inputFile.startAddress + inputFile.data.length;
this.fileType = inputFile.type;
}
generateSound(outputWaveFile) {
this.waveFile = outputWaveFile;
this.addHeader(false);
this.addHeader(true);
outputWaveFile.addSilence(0.1);
this.addFile();
}
addTapCycle(tapValue) {
this.waveFile.addCycle(tapValue);
}
addBit(value) {
if (value === 0) {
this.addTapCycle(_a.SHORT_PULSE);
this.addTapCycle(_a.MEDIUM_PULSE);
}
else {
this.addTapCycle(_a.MEDIUM_PULSE);
this.addTapCycle(_a.SHORT_PULSE);
}
}
addDataMarker(moreToFollow) {
if (moreToFollow) {
this.addTapCycle(_a.LONG_PULSE);
this.addTapCycle(_a.MEDIUM_PULSE);
}
else {
this.addTapCycle(_a.LONG_PULSE);
this.addTapCycle(_a.SHORT_PULSE);
}
}
resetChecksum() {
this.checksum = 0;
}
addByteFrame(value, moreToFollow) {
let checkBit = 1;
for (let i = 0; i < 8; i++) {
const bit = (value & (1 << i)) !== 0 ? 1 : 0;
this.addBit(bit);
checkBit ^= bit;
}
this.addBit(checkBit);
this.addDataMarker(moreToFollow);
this.checksum ^= value;
}
addLeader(fileType) {
let numberofPulses;
if (fileType === _a.LEADER_TYPE_HEADER) {
numberofPulses = 0x6a00;
}
else if (fileType === _a.LEADER_TYPE_CONTENT) {
numberofPulses = 0x1a00;
}
else {
numberofPulses = 0x4f;
}
for (let i = 0; i < numberofPulses; i++) {
this.addTapCycle(_a.SHORT_PULSE);
}
}
addSyncChain(repeated) {
let value;
if (repeated) {
value = 0x09;
}
else {
value = 0x89;
}
let count = 9;
while (count > 0) {
this.addByteFrame(value, true);
value -= 1;
count -= 1;
}
}
addData() {
for (let i = 0; i < this.data.length; i++) {
this.addByteFrame(this.data[i], true);
}
}
addFilename() {
for (let i = 0; i < this.filenameData.length; i++) {
this.addByteFrame(this.filenameData[i], true);
}
}
addHeader(repeated) {
if (repeated) {
this.addLeader(_a.LEADER_TYPE_REPEATED);
}
else {
this.addLeader(_a.LEADER_TYPE_HEADER);
}
this.addDataMarker(true);
this.addSyncChain(repeated);
this.resetChecksum();
this.addByteFrame(this.fileType, true);
this.addByteFrame(this.startAddress & 0x00ff, true);
this.addByteFrame((this.startAddress & 0xff00) >> 8, true);
this.addByteFrame(this.endAddress & 0x00ff, true);
this.addByteFrame((this.endAddress & 0xff00) >> 8, true);
this.addFilename();
for (let i = 0; i < _a.NUMBER_OF_PADDING_BYTES; i++) {
this.addByteFrame(_a.PADDING_CHARACTER, true);
}
this.addByteFrame(this.checksum, false);
}
addFile() {
let repeated = false;
for (let i = 0; i < 2; i++) {
if (!repeated) {
this.addLeader(_a.LEADER_TYPE_CONTENT);
}
else {
this.addLeader(_a.LEADER_TYPE_REPEATED);
}
this.addDataMarker(true);
this.addSyncChain(repeated);
this.resetChecksum();
this.addData();
this.addByteFrame(this.checksum, false);
repeated = true;
}
this.addLeader(1);
}
}
exports.TAPFile = TAPFile;
_a = TAPFile;
TAPFile.CLOCK_RATE = 985248.0;
TAPFile.TAP_LENGTH_IN_SECONDS = 8.0 / _a.CLOCK_RATE;
TAPFile.FILENAME_BUFFER_SIZE = 0x10;
TAPFile.FILE_TYPE_NONE = 0;
TAPFile.FILE_TYPE_RELOCATABLE = 1;
TAPFile.FILE_TYPE_SEQUENTIAL = 2;
TAPFile.FILE_TYPE_NON_RELOCATABLE = 3;
TAPFile.LEADER_TYPE_HEADER = 0;
TAPFile.LEADER_TYPE_CONTENT = 1;
TAPFile.LEADER_TYPE_REPEATED = 2;
TAPFile.NUMBER_OF_PADDING_BYTES = 171;
TAPFile.PADDING_CHARACTER = 0x20;
TAPFile.SHORT_PULSE = 0x30;
TAPFile.MEDIUM_PULSE = 0x42;
TAPFile.LONG_PULSE = 0x56;
//# sourceMappingURL=CommodoreTape.js.map

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
import{a as d}from"./chunk-3XE5YOCV.js";import{o as c}from"./chunk-VD7T557T.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-AT7M4NIM.js";import{I as m,o as u,t as h}from"./chunk-TV4YGKI4.js";import{$ as i,B as o,J as l,m as n}from"./chunk-Z5CU44VP.js";import"./chunk-5XVCUSSZ.js";var s=31,a=class extends u{constructor(){super();this.cpuFrequency=1e6;this.defaultROMSize=32768;this.cpu=new h;this.ram=new Uint8Array(16384);this.read=i([[0,16383,16383,e=>this.ram[e]],[16384,16384,65535,e=>this.serial.byteAvailable()?128:0],[16385,16385,65535,e=>this.serial.recvByte()],[16386,16386,65535,e=>this.serial.clearToSend()?128:0],[32768,65535,32767,e=>this.rom&&this.rom[e]]]);this.write=i([[0,16383,16383,(e,t)=>{this.ram[e]=t}],[16387,16387,65535,(e,t)=>this.serial.sendByte(t)],[16399,16399,65535,(e,t)=>{this.inputs[s]=1}]]);this.connectCPUMemoryBus(this)}connectSerialIO(e){this.serial=e}readConst(e){return this.read(e)}advanceFrame(e){for(var t=0;t<this.cpuFrequency/60&&!(e&&e());)t+=this.advanceCPU();return t}advanceCPU(){if(this.isHalted())return 1;var e=super.advanceCPU();return this.serial&&this.serial.advance(e),e}reset(){this.inputs[s]=0,super.reset(),this.serial&&this.serial.reset()}isHalted(){return this.inputs[s]!=0}};var S=[{id:"hello.dasm",name:"Hello World (ASM)"}],f=class{constructor(e){e.style.overflowY="auto";var t=$('<div id="gameport"/>').appendTo(e);$('<p class="transcript-header">Serial Output</p>').appendTo(t);var y=$('<div id="windowport" class="transcript"/>').appendTo(t);this.div=y[0]}start(){this.tty=new d(this.div,!1)}reset(){this.tty.clear()}saveState(){return this.tty.saveState()}loadState(e){this.tty.loadState(e)}};function p(r){return r==10?"":r<32?String.fromCharCode(r+9216):String.fromCharCode(r)}var v=class{constructor(){this.bufferedRead=!0;this.cyclesPerByte=1e6/(57600/8);this.maxOutputBytes=4096}clearToSend(){return this.outputBytes.length<this.maxOutputBytes}sendByte(e){this.clearToSend()&&(this.outputBytes.push(e),this.viewer.tty.addtext(p(e),2|32),e==10&&this.viewer.tty.newline(),this.clearToSend()||(this.viewer.tty.newline(),this.viewer.tty.addtext("\u26A0\uFE0F OUTPUT BUFFER FULL \u26A0\uFE0F",4)))}byteAvailable(){return this.readIndex()>this.inputIndex}recvByte(){var e=this.readIndex();this.inputIndex=e;var t=(this.inputBytes&&this.inputBytes[e])|0;return this.viewer.tty.addtext(p(t),2|16),t==10&&this.viewer.tty.newline(),t}readIndex(){return this.bufferedRead?this.inputIndex+1:Math.floor(this.clk/this.cyclesPerByte)}reset(){this.inputIndex=-1,this.clk=0,this.outputBytes=[],this.bufin=""}advance(e){this.clk+=e}saveState(){return{clk:this.clk,idx:this.inputIndex,out:this.outputBytes.slice()}}loadState(e){this.clk=e.clk,this.inputIndex=e.idx,this.outputBytes=e.out.slice()}},x=class extends m{constructor(e){super(e);this.getMemoryMap=function(){return{main:[{name:"RAM",start:0,size:16384,type:"ram"},{name:"ROM",start:32768,size:32768,type:"rom"}]}};this.serview=new f(e)}async start(){super.start(),this.serial=new v,this.serial.viewer=this.serview,this.serview.start(),this.machine.connectSerialIO(this.serial)}reset(){this.serial.inputBytes=o(this.internalFiles["serialin.dat"]),super.reset(),this.serview.reset()}isBlocked(){return this.machine.isHalted()}advance(e){return this.isBlocked()?(this.internalFiles["serialout.dat"]=n(this.serial.outputBytes),c(),0):super.advance(e)}saveState(){var e=super.saveState();return e.serial=this.serial.saveState(),e.serview=this.serview.saveState(),e}loadState(e){super.loadState(e),this.serial.loadState(e.serial),this.serview.loadState(e.serview)}newMachine(){return new a}getPresets(){return S}getDefaultExtension(){return".dasm"}readAddress(e){return this.machine.readConst(e)}};l["devel-6502"]=x;export{v as SerialTestHarness};
//# sourceMappingURL=devel-TVAWHFKG.js.map
import{a as d}from"./chunk-3XE5YOCV.js";import{o as c}from"./chunk-UZ2OPGHD.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-JMKLUIDP.js";import{I as m,o as u,t as h}from"./chunk-TV4YGKI4.js";import{$ as i,B as o,J as l,m as n}from"./chunk-Z5CU44VP.js";import"./chunk-5XVCUSSZ.js";var s=31,a=class extends u{constructor(){super();this.cpuFrequency=1e6;this.defaultROMSize=32768;this.cpu=new h;this.ram=new Uint8Array(16384);this.read=i([[0,16383,16383,e=>this.ram[e]],[16384,16384,65535,e=>this.serial.byteAvailable()?128:0],[16385,16385,65535,e=>this.serial.recvByte()],[16386,16386,65535,e=>this.serial.clearToSend()?128:0],[32768,65535,32767,e=>this.rom&&this.rom[e]]]);this.write=i([[0,16383,16383,(e,t)=>{this.ram[e]=t}],[16387,16387,65535,(e,t)=>this.serial.sendByte(t)],[16399,16399,65535,(e,t)=>{this.inputs[s]=1}]]);this.connectCPUMemoryBus(this)}connectSerialIO(e){this.serial=e}readConst(e){return this.read(e)}advanceFrame(e){for(var t=0;t<this.cpuFrequency/60&&!(e&&e());)t+=this.advanceCPU();return t}advanceCPU(){if(this.isHalted())return 1;var e=super.advanceCPU();return this.serial&&this.serial.advance(e),e}reset(){this.inputs[s]=0,super.reset(),this.serial&&this.serial.reset()}isHalted(){return this.inputs[s]!=0}};var S=[{id:"hello.dasm",name:"Hello World (ASM)"}],f=class{constructor(e){e.style.overflowY="auto";var t=$('<div id="gameport"/>').appendTo(e);$('<p class="transcript-header">Serial Output</p>').appendTo(t);var y=$('<div id="windowport" class="transcript"/>').appendTo(t);this.div=y[0]}start(){this.tty=new d(this.div,!1)}reset(){this.tty.clear()}saveState(){return this.tty.saveState()}loadState(e){this.tty.loadState(e)}};function p(r){return r==10?"":r<32?String.fromCharCode(r+9216):String.fromCharCode(r)}var v=class{constructor(){this.bufferedRead=!0;this.cyclesPerByte=1e6/(57600/8);this.maxOutputBytes=4096}clearToSend(){return this.outputBytes.length<this.maxOutputBytes}sendByte(e){this.clearToSend()&&(this.outputBytes.push(e),this.viewer.tty.addtext(p(e),2|32),e==10&&this.viewer.tty.newline(),this.clearToSend()||(this.viewer.tty.newline(),this.viewer.tty.addtext("\u26A0\uFE0F OUTPUT BUFFER FULL \u26A0\uFE0F",4)))}byteAvailable(){return this.readIndex()>this.inputIndex}recvByte(){var e=this.readIndex();this.inputIndex=e;var t=(this.inputBytes&&this.inputBytes[e])|0;return this.viewer.tty.addtext(p(t),2|16),t==10&&this.viewer.tty.newline(),t}readIndex(){return this.bufferedRead?this.inputIndex+1:Math.floor(this.clk/this.cyclesPerByte)}reset(){this.inputIndex=-1,this.clk=0,this.outputBytes=[],this.bufin=""}advance(e){this.clk+=e}saveState(){return{clk:this.clk,idx:this.inputIndex,out:this.outputBytes.slice()}}loadState(e){this.clk=e.clk,this.inputIndex=e.idx,this.outputBytes=e.out.slice()}},x=class extends m{constructor(e){super(e);this.getMemoryMap=function(){return{main:[{name:"RAM",start:0,size:16384,type:"ram"},{name:"ROM",start:32768,size:32768,type:"rom"}]}};this.serview=new f(e)}async start(){super.start(),this.serial=new v,this.serial.viewer=this.serview,this.serview.start(),this.machine.connectSerialIO(this.serial)}reset(){this.serial.inputBytes=o(this.internalFiles["serialin.dat"]),super.reset(),this.serview.reset()}isBlocked(){return this.machine.isHalted()}advance(e){return this.isBlocked()?(this.internalFiles["serialout.dat"]=n(this.serial.outputBytes),c(),0):super.advance(e)}saveState(){var e=super.saveState();return e.serial=this.serial.saveState(),e.serview=this.serview.saveState(),e}loadState(e){super.loadState(e),this.serial.loadState(e.serial),this.serview.loadState(e.serview)}newMachine(){return new a}getPresets(){return S}getDefaultExtension(){return".dasm"}readAddress(e){return this.machine.readConst(e)}};l["devel-6502"]=x;export{v as SerialTestHarness};
//# sourceMappingURL=devel-VFD7BNDQ.js.map

View File

@ -1,2 +1,2 @@
import{b as w,c as x}from"./chunk-AT7M4NIM.js";import{D as v,J as c,k as m,l as u,x as g,z as p}from"./chunk-Z5CU44VP.js";import{e as I}from"./chunk-5XVCUSSZ.js";var h=I(x()),i,a,l,y=function(e){if(!e||e.length==0)return{};for(var r={},t=0;t<e.length;++t){var o=e[t].split("=",2);o.length==1?r[o[0]]="":r[o[0]]=decodeURIComponent(o[1].replace(/\+/g," "))}return r}(window.location.search.substr(1).split("&"));function P(){typeof window.onerror=="object"&&(window.onerror=function(e,r,t,o,n){var s=e+" "+r+" "+t+":"+o+", "+n;$.get("/error?msg="+encodeURIComponent(s),"text")})}function M(){var e=!1;document.addEventListener("visibilitychange",function(r){document.visibilityState=="hidden"&&a.isRunning()?(a.pause(),e=!0):document.visibilityState=="visible"&&e&&(a.resume(),e=!1)}),$(window).on("focus",function(){e&&(a.resume(),e=!1)}),$(window).on("blur",function(){a.isRunning()&&(a.pause(),e=!0)})}async function k(e,r){if(!r){alert("No ROM found.");return}console.log(r.length+" bytes"),await a.loadROM(e,r),a.resume()}function R(){return $("#emulator").find("canvas")}function E(e,r,t){v("gif.js/dist/gif.js").then(()=>{var o=R()[0];if(!o){alert("Could not find canvas element to record video!");return}var n=0;o.style&&o.style.transform&&(o.style.transform.indexOf("rotate(-90deg)")>=0?n=-1:o.style.transform.indexOf("rotate(90deg)")>=0&&(n=1));var s=new GIF({workerScript:"gif.js/dist/gif.worker.js",workers:4,quality:10,rotate:n});s.on("finished",function(C){console.log("finished encoding GIF"),t(C)}),e=e||100+(Math.random()*256&3),r=r||100+(Math.random()*256&15);var f=0;console.log("Recording video",o);var d=()=>{f++>r?(console.log("Rendering video"),s.render()):(s.addFrame(o,{delay:e,copy:!0}),setTimeout(d,e))};d()})}async function S(e){if(!c[i])throw Error("Invalid platform '"+i+"'.");a=new c[i]($("#emuscreen")[0]),await a.start(),e.rec&&R().on("focus",()=>{a.resume()});var r=e.n||"Game",t,o=e.url,n=e.r;if(o)return console.log(o),g(o,f=>{k(r,f)},"arraybuffer"),!0;if(n){var s=u(atob(n));t=new m().decode(s)}return M(),k(r,t),!0}async function b(e){if(e.data&&(e=e.data),i=e.p,!i)throw new Error("No platform variable!");try{var r=await w(p(i));console.log("starting platform",i),await S(e)}catch(t){console.log(t),alert('Platform "'+i+'" not supported.')}}function F(){P(),y.p&&b(y)}window.addEventListener("message",O,!1);function O(e){if(e.data){var r=e.data.cmd;if(r=="start"&&!a)b(e);else if(r=="reset")a.reset(),l.reset();else if(r=="getReplay"){var t={frameCount:l.frameCount,checkpoints:l.checkpoints,framerecs:l.framerecs,checkpointInterval:l.checkpointInterval,maxCheckpoints:l.maxCheckpoints};e.source.postMessage({ack:r,replay:t},e.origin)}else if(r=="watchState"){var o=new Function("platform","state",e.data.fn);l.callbackNewCheckpoint=n=>{e.source.postMessage({ack:r,state:o(a,n)},e.origin)}}else r=="recordVideo"?E(e.data.intervalMsec,e.data.maxFrames,function(n){e.data.filename&&(0,h.saveAs)(n,e.data.filename),e.source.postMessage({ack:r,gif:n},e.origin)}):console.log("Unknown data.cmd: "+r)}}self===top&&(document.body.style.backgroundColor="#555");F();export{a as platform,i as platform_id,F as startEmbed,l as stateRecorder};
import{b as w,c as x}from"./chunk-JMKLUIDP.js";import{D as v,J as c,k as m,l as u,x as g,z as p}from"./chunk-Z5CU44VP.js";import{e as I}from"./chunk-5XVCUSSZ.js";var h=I(x()),i,a,l,y=function(e){if(!e||e.length==0)return{};for(var r={},t=0;t<e.length;++t){var o=e[t].split("=",2);o.length==1?r[o[0]]="":r[o[0]]=decodeURIComponent(o[1].replace(/\+/g," "))}return r}(window.location.search.substr(1).split("&"));function P(){typeof window.onerror=="object"&&(window.onerror=function(e,r,t,o,n){var s=e+" "+r+" "+t+":"+o+", "+n;$.get("/error?msg="+encodeURIComponent(s),"text")})}function M(){var e=!1;document.addEventListener("visibilitychange",function(r){document.visibilityState=="hidden"&&a.isRunning()?(a.pause(),e=!0):document.visibilityState=="visible"&&e&&(a.resume(),e=!1)}),$(window).on("focus",function(){e&&(a.resume(),e=!1)}),$(window).on("blur",function(){a.isRunning()&&(a.pause(),e=!0)})}async function k(e,r){if(!r){alert("No ROM found.");return}console.log(r.length+" bytes"),await a.loadROM(e,r),a.resume()}function R(){return $("#emulator").find("canvas")}function E(e,r,t){v("gif.js/dist/gif.js").then(()=>{var o=R()[0];if(!o){alert("Could not find canvas element to record video!");return}var n=0;o.style&&o.style.transform&&(o.style.transform.indexOf("rotate(-90deg)")>=0?n=-1:o.style.transform.indexOf("rotate(90deg)")>=0&&(n=1));var s=new GIF({workerScript:"gif.js/dist/gif.worker.js",workers:4,quality:10,rotate:n});s.on("finished",function(C){console.log("finished encoding GIF"),t(C)}),e=e||100+(Math.random()*256&3),r=r||100+(Math.random()*256&15);var f=0;console.log("Recording video",o);var d=()=>{f++>r?(console.log("Rendering video"),s.render()):(s.addFrame(o,{delay:e,copy:!0}),setTimeout(d,e))};d()})}async function S(e){if(!c[i])throw Error("Invalid platform '"+i+"'.");a=new c[i]($("#emuscreen")[0]),await a.start(),e.rec&&R().on("focus",()=>{a.resume()});var r=e.n||"Game",t,o=e.url,n=e.r;if(o)return console.log(o),g(o,f=>{k(r,f)},"arraybuffer"),!0;if(n){var s=u(atob(n));t=new m().decode(s)}return M(),k(r,t),!0}async function b(e){if(e.data&&(e=e.data),i=e.p,!i)throw new Error("No platform variable!");try{var r=await w(p(i));console.log("starting platform",i),await S(e)}catch(t){console.log(t),alert('Platform "'+i+'" not supported.')}}function F(){P(),y.p&&b(y)}window.addEventListener("message",O,!1);function O(e){if(e.data){var r=e.data.cmd;if(r=="start"&&!a)b(e);else if(r=="reset")a.reset(),l.reset();else if(r=="getReplay"){var t={frameCount:l.frameCount,checkpoints:l.checkpoints,framerecs:l.framerecs,checkpointInterval:l.checkpointInterval,maxCheckpoints:l.maxCheckpoints};e.source.postMessage({ack:r,replay:t},e.origin)}else if(r=="watchState"){var o=new Function("platform","state",e.data.fn);l.callbackNewCheckpoint=n=>{e.source.postMessage({ack:r,state:o(a,n)},e.origin)}}else r=="recordVideo"?E(e.data.intervalMsec,e.data.maxFrames,function(n){e.data.filename&&(0,h.saveAs)(n,e.data.filename),e.source.postMessage({ack:r,gif:n},e.origin)}):console.log("Unknown data.cmd: "+r)}}self===top&&(document.body.style.backgroundColor="#555");F();export{a as platform,i as platform_id,F as startEmbed,l as stateRecorder};
//# sourceMappingURL=embedui.js.map

View File

@ -43,6 +43,7 @@ const baseviews_1 = require("./views/baseviews");
const treeviews_1 = require("./views/treeviews");
const file_saver_1 = require("file-saver");
const DOMPurify = require("dompurify");
const CommodoreTape_1 = require("../common/audio/CommodoreTape");
exports.qs = (0, util_1.decodeQueryString)(window.location.search || '?');
const isElectron = (0, util_1.parseBool)(exports.qs.electron);
const isEmbed = (0, util_1.parseBool)(exports.qs.embed);
@ -965,20 +966,37 @@ function _downloadCassetteFile_vcs(e) {
});
});
}
function _downloadCassetteFile_c64(e) {
var prefix = (0, util_1.getFilenamePrefix)(getCurrentMainFilename());
let audpath = prefix + ".tap";
let tapmaker = new CommodoreTape_1.TAPFile(prefix);
let outfile = new CommodoreTape_1.OutputSoundFile({ sine_wave: true });
let data = current_output;
let startAddress = data[0] + data[1] * 256;
data = data.slice(2); // remove header
tapmaker.setContent({ data, startAddress, type: CommodoreTape_1.TAPFile.FILE_TYPE_NON_RELOCATABLE });
tapmaker.generateSound(outfile);
let tapout = outfile.getTAPData();
//let audout = outfile.getSoundData();
if (tapout) {
//let blob = new Blob([audout], { type: "audio/wav" });
let blob = new Blob([tapout], { type: "application/octet-stream" });
(0, file_saver_1.saveAs)(blob, audpath);
}
}
function _getCassetteFunction() {
switch ((0, util_1.getBasePlatform)(exports.platform_id)) {
case 'vcs': return _downloadCassetteFile_vcs;
case 'apple2': return _downloadCassetteFile_apple2;
case 'c64': return _downloadCassetteFile_c64;
}
}
function _downloadCassetteFile(e) {
if (current_output == null) {
alertError("Please fix errors before exporting.");
return true;
}
var fn;
switch ((0, util_1.getBasePlatform)(exports.platform_id)) {
case 'vcs':
fn = _downloadCassetteFile_vcs;
break;
case 'apple2':
fn = _downloadCassetteFile_apple2;
break;
}
var fn = _getCassetteFunction();
if (fn === undefined) {
alertError("Cassette export is not supported on this platform.");
return true;
@ -1891,7 +1909,7 @@ function setupDebugControls() {
}
$("#item_download_allzip").click(_downloadAllFilesZipFile);
$("#item_record_video").click(_recordVideo);
if (exports.platform_id.startsWith('apple2') || exports.platform_id.startsWith('vcs')) // TODO: look for function
if (_getCassetteFunction())
$("#item_export_cassette").click(_downloadCassetteFile);
else
$("#item_export_cassette").hide();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
gen/pce-HUGTVEWU.js Normal file

File diff suppressed because one or more lines are too long

7
gen/pce-HUGTVEWU.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@ const emu_1 = require("../common/emu");
const PCE_PRESETS = [
{ id: 'test_conio.c', name: 'Hello World (conio)' },
{ id: 'siegegame.c', name: 'Siege Game (conio)' },
{ id: 'hello.wiz', name: 'Hello World (Wiz)' },
];
class PCEnginePlatform {
constructor(mainElement) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
import{b as a,c as b,d as c,e as d,f as e,g as f,h as g,i as h,j as i,k as j,l as k,m as l,n as m,o as n,p as o,q as p,r as q,s as r,t as s,u as t,v as u,w as v,x as w}from"./chunk-VD7T557T.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-AT7M4NIM.js";import"./chunk-TV4YGKI4.js";import"./chunk-Z5CU44VP.js";import"./chunk-5XVCUSSZ.js";export{l as clearBreakpoint,h as compparams,f as current_project,u as emulationHalted,p as getPlatformAndRepo,t as getSaveState,s as getTestOutput,n as haltEmulation,w as highlightSearch,i as lastDebugState,e as platform,b as platform_id,g as projectWindows,a as qs,v as reloadWorkspaceFile,d as repo_id,k as runToPC,m as setFrameRateUI,r as setTestInput,j as setupBreakpoint,o as setupSplits,q as startUI,c as store_id};
import{b as a,c as b,d as c,e as d,f as e,g as f,h as g,i as h,j as i,k as j,l as k,m as l,n as m,o as n,p as o,q as p,r as q,s as r,t as s,u as t,v as u,w as v,x as w}from"./chunk-UZ2OPGHD.js";import"./chunk-SXEVG2WS.js";import"./chunk-5SHCNQ2O.js";import"./chunk-JMKLUIDP.js";import"./chunk-TV4YGKI4.js";import"./chunk-Z5CU44VP.js";import"./chunk-5XVCUSSZ.js";export{l as clearBreakpoint,h as compparams,f as current_project,u as emulationHalted,p as getPlatformAndRepo,t as getSaveState,s as getTestOutput,n as haltEmulation,w as highlightSearch,i as lastDebugState,e as platform,b as platform_id,g as projectWindows,a as qs,v as reloadWorkspaceFile,d as repo_id,k as runToPC,m as setFrameRateUI,r as setTestInput,j as setupBreakpoint,o as setupSplits,q as startUI,c as store_id};
//# sourceMappingURL=ui.js.map

View File

@ -152,6 +152,7 @@ body {
<li><a class="dropdown-item" href="?platform=sms-gg-libcv">Sega Game Gear</a></li>
<li><a class="dropdown-item" href="?platform=atari8-5200">Atari 5200</a></li>
<li><a class="dropdown-item" href="?platform=atari7800">Atari 7800</a></li>
<li><a class="dropdown-item" href="?platform=pce">PC Engine</a></li>
<!--
<li><a class="dropdown-item" href="?platform=vectrex">Vectrex</a></li>
-->
@ -208,7 +209,6 @@ body {
<li><a class="dropdown-item" href="?platform=nes.mame">NES (MAME)</a></li>
<hr>
<li><a class="dropdown-item" href="?platform=williams">Williams (6809)</a></li>
<li><a class="dropdown-item" href="?platform=pce">PC Engine</a></li>
</ul>
</li>
</ul>

130
presets/pce/hello.wiz Normal file
View File

@ -0,0 +1,130 @@
import "pce";
bank zeropage @ 0x2000 : [vardata; 256];
bank stack @ 0x2100 : [vardata; 256];
bank prg @ 0xE000 : [constdata; 0x2000];
bank chr @ 0x8000 : [constdata; 0x4000];
in zeropage {
var b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 : u8;
var w0 @ &b0, w2 @ &b2, w4 @ &b4, w6 @ &b6, w8 @ &b8 : u16;
var mosaic : u8;
var timer : u8;
}
in prg {
#[fallthrough] func main() {
// Disable decimal arithmetic.
decimal = false;
// Disable interrupts.
nointerrupt = true;
// Enable turbo mode on CPU.
turbo_speed = true;
// mpr7 = a = 0; by power-on
// MPR0 = I/O page
mpr0 = a = 0xFF;
// MPR1 = RAM
mpr1 = a = 0xF8;
// MPR4/5 = data bank
mpr4 = a = 1;
mpr5 = a = 2;
// Prepare stack.
s = x = 0xFF;
a = pce.vdc.status;
pce.irq.disable = a = pce.irq.INTERRUPT_ALL;
pce.irq.acknowledge = a;
pce.timer.control = 0;
vdc_select = pce.vdc.SELECT_CONTROL;
vdc_data_l = 0;
vdc_data_h = 0;
vdc_select = pce.vdc.SELECT_SCANLINE_COMPARE;
vdc_data_l = 0;
vdc_data_h = 0;
vdc_select = pce.vdc.SELECT_SCROLL_X;
vdc_data_l = 0;
vdc_data_h = 0;
vdc_select = pce.vdc.SELECT_SCROLL_Y;
vdc_data_l = 0;
vdc_data_h = 0;
vdc_select = pce.vdc.SELECT_MEMORY_SIZE;
vdc_data_l = pce.vdc.DATA_L_MEMORY_SIZE_TILEMAP_32x32;
vdc_data_h = 0;
vdc_select = pce.vdc.SELECT_HSYNC_SETTING;
vdc_data_l = 0x02;
vdc_data_h = 0x02;
vdc_select = pce.vdc.SELECT_HDISP_SETTING;
vdc_data_l = 0x1F;
vdc_data_h = 0x04;
vdc_select = pce.vdc.SELECT_VSYNC_SETTING;
vdc_data_l = 0x07;
vdc_data_h = 0x0D;
vdc_select = pce.vdc.SELECT_VDISP_HEIGHT;
vdc_data_l = 0xDF;
vdc_data_h = 0x00;
vdc_select = pce.vdc.SELECT_VDISP_END;
vdc_data_l = 0x03;
vdc_data_h = 0x00;
vdc_select = pce.vdc.SELECT_DMA_CONTROL;
vdc_data_l = pce.vdc.DATA_L_DMA_CONTROL_SATB_AUTO_TRANSFER;
vdc_data_h = 0x00;
vdc_select = pce.vdc.SELECT_SATB_SOURCE_ADDRESS;
vdc_data_l = 0x00;
vdc_data_h = 0x7F;
pce.vce.control = a = pce.vce.CONTROL_FIELD_EXTRA_LINE;
pce.vce.address_l = a = 0;
pce.vce.address_h = a = 0;
load_alt_inc_repeat(&pce.vce.data_l as u16, &bkg_palette_data as u16, sizeof(typeof(bkg_palette_data)));
let TILESET_DEST_ADDRESS = 0x1000;
vdc_select = pce.vdc.SELECT_VRAM_WRITE_ADDRESS;
vdc_data_l = <:TILESET_DEST_ADDRESS;
vdc_data_h = >:TILESET_DEST_ADDRESS;
vdc_select = pce.vdc.SELECT_VRAM_DATA;
load_alt_inc_repeat(&pce.vdc.data_l as u16, &hello_tiles_chr as u16, sizeof(typeof(hello_tiles_chr)));
let START_X = 10;
let START_Y = 13;
let TILEMAP_ADDRESS = pce.vdc.DATA_VRAM_ADDRESS_TILEMAP + START_Y * 32 + START_X;
let TILE_BASE_INDEX = TILESET_DEST_ADDRESS / 16;
vdc_select = pce.vdc.SELECT_VRAM_WRITE_ADDRESS;
vdc_data_l = <:TILEMAP_ADDRESS;
vdc_data_h = >:TILEMAP_ADDRESS;
vdc_select = pce.vdc.SELECT_VRAM_DATA;
x = 0;
do {
pce.vdc.data_l = a = <:TILE_BASE_INDEX + message[x];
pce.vdc.data_h = a = >:TILE_BASE_INDEX +# 0;
x++;
} while x != message.len;
vdc_select = pce.vdc.SELECT_CONTROL;
vdc_data_l = pce.vdc.DATA_L_CONTROL_ENABLE_BG;
vdc_data_h = 0x00;
while true {}
}
let rgb_pack(r, g, b) = ((r & 0x7) << 3) | ((g & 0x7) << 6) | (b & 0x7);
const bkg_palette_data : [u16] = [
rgb_pack(6, 3, 4), rgb_pack(1, 1, 3), rgb_pack(0, 0, 0), rgb_pack(0, 0, 0),
rgb_pack(0, 0, 0), rgb_pack(0, 0, 0), rgb_pack(0, 0, 0), rgb_pack(0, 0, 0),
rgb_pack(0, 0, 0), rgb_pack(0, 0, 0), rgb_pack(0, 0, 0), rgb_pack(0, 0, 0),
rgb_pack(0, 0, 0), rgb_pack(0, 0, 0), rgb_pack(0, 0, 0), rgb_pack(7, 7, 7),
];
const message = "HELLO WORLD";
const @ 0xFFFE = main;
}
in chr {
const hello_tiles_chr = embed "hello_tiles.chr";
const minirpg_sprites_chr = embed "minirpg_sprites.chr";
}

View File

@ -0,0 +1,305 @@
/*
https://github.com/eightbitjim/commodore-tape-maker/blob/master/maketape.py
# MIT License
#
# Copyright (c) 2018 eightbitjim
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
*/
export class OutputSoundFile {
options: any;
sampleRate: number;
soundData: number[];
tapData: number[];
constructor(options: any) {
this.options = options;
this.sampleRate = 44100.0;
this.soundData = [];
//00000000 43 36 34 2d 54 41 50 45 2d 52 41 57 01 00 00 00 |C64-TAPE-RAW....|
//00000010 1e 62 03 00
this.tapData = [0x43,0x36,0x34,0x2d,0x54,0x41,0x50,0x45,0x2d,0x52,0x41,0x57,0x01,0x00,0x00,0x00,0,0,0,0];
}
getWAVHeader() {
const header = new Uint8Array(44);
const view = new DataView(header.buffer);
view.setUint32(0, 0x52494646, false); // "RIFF"
view.setUint32(4, 44 + this.soundData.length, true); // ChunkSize
view.setUint32(8, 0x57415645, false); // "WAVE"
view.setUint32(12, 0x666d7420, false); // "fmt "
view.setUint32(16, 16, true); // Subchunk1Size
view.setUint16(20, 1, true); // AudioFormat (PCM)
view.setUint16(22, 1, true); // NumChannels
view.setUint32(24, this.sampleRate, true); // SampleRate
view.setUint32(28, this.sampleRate * 2, true); // ByteRate
view.setUint16(32, 1, true); // BlockAlign
view.setUint16(34, 8, true); // BitsPerSample
view.setUint32(36, 0x64617461, false); // "data"
view.setUint32(40, this.soundData.length, true); // Subchunk2Size
return header;
}
addSilence(lengthInSeconds: number): void {
const numberOfSamples = Math.floor(this.sampleRate * lengthInSeconds);
for (let i = 0; i < numberOfSamples; i++) {
this.soundData.push(0);
}
//For v1 and v2 TAP files, a $00 value is followed by 3 bytes containing the actual duration measured in clock cycles (not divided by 8). These 3 bytes are in low-high format.
const numCycles = TAPFile.CLOCK_RATE * lengthInSeconds;
this.tapData.push(0);
this.tapData.push(numCycles & 0xff);
this.tapData.push((numCycles >> 8) & 0xff);
this.tapData.push((numCycles >> 16) & 0xff);
}
addCycle(cycles: number): void {
this.tapData.push(cycles);
const numberOfSamples = Math.floor(this.sampleRate * TAPFile.TAP_LENGTH_IN_SECONDS * cycles);
for (let i = 0; i < numberOfSamples; i++) {
let value;
if (this.options.sine_wave) {
value = - Math.sin((i / numberOfSamples) * 2.0 * Math.PI);
} else {
if (i < numberOfSamples / 2) {
value = -1;
} else {
value = 1;
}
}
if (this.options.invert_waveform) {
value = -value;
}
this.soundData.push(Math.round(128 + value * 127));
}
}
updateTAPHeader() {
let datalen = this.tapData.length - 0x14;
// set bytes 0x10-0x13 to length
this.tapData[0x10] = datalen & 0xff;
this.tapData[0x11] = (datalen >> 8) & 0xff;
this.tapData[0x12] = (datalen >> 16) & 0xff;
this.tapData[0x13] = (datalen >> 24) & 0xff;
}
getTAPData(): Uint8Array {
this.updateTAPHeader();
return new Uint8Array(this.tapData);
}
getSoundData(): Uint8Array {
let header = this.getWAVHeader();
let data = new Uint8Array(header.length + this.soundData.length);
data.set(header, 0);
data.set(new Uint8Array(this.soundData), header.length);
return data;
}
}
export class TAPFile {
static CLOCK_RATE = 985248.0;
static TAP_LENGTH_IN_SECONDS = 8.0 / this.CLOCK_RATE;
static FILENAME_BUFFER_SIZE = 0x10;
static FILE_TYPE_NONE = 0;
static FILE_TYPE_RELOCATABLE = 1;
static FILE_TYPE_SEQUENTIAL = 2;
static FILE_TYPE_NON_RELOCATABLE = 3;
static LEADER_TYPE_HEADER = 0;
static LEADER_TYPE_CONTENT = 1;
static LEADER_TYPE_REPEATED = 2;
static NUMBER_OF_PADDING_BYTES = 171;
static PADDING_CHARACTER = 0x20;
static SHORT_PULSE = 0x30;
static MEDIUM_PULSE = 0x42;
static LONG_PULSE = 0x56;
options: any;
checksum: number;
data: Uint8Array;
filenameData: number[];
startAddress: number;
endAddress: number;
fileType: number;
waveFile: OutputSoundFile;
constructor(filename: string, options?: any) {
this.options = options;
this.checksum = 0;
this.data = new Uint8Array(0);
this.filenameData = this.makeFilename(filename);
this.startAddress = 0;
this.endAddress = 0;
this.fileType = TAPFile.FILE_TYPE_NONE;
this.waveFile = null;
}
makeFilename(filename: string): number[] {
const filenameBuffer = [];
const space = 0x20;
filename = filename.toUpperCase(); // for PETSCII
for (let i = 0; i < TAPFile.FILENAME_BUFFER_SIZE; i++) {
if (filename.length <= i) {
filenameBuffer.push(space);
} else {
let ch = filename.charCodeAt(i);
filenameBuffer.push(ch);
}
}
return filenameBuffer;
}
setContent(inputFile: { data: Uint8Array, startAddress: number, type: number }): void {
this.data = inputFile.data;
this.startAddress = inputFile.startAddress;
this.endAddress = inputFile.startAddress + inputFile.data.length;
this.fileType = inputFile.type;
}
generateSound(outputWaveFile: OutputSoundFile): void {
this.waveFile = outputWaveFile;
this.addHeader(false);
this.addHeader(true);
outputWaveFile.addSilence(0.1);
this.addFile();
}
addTapCycle(tapValue: number): void {
this.waveFile.addCycle(tapValue);
}
addBit(value: number): void {
if (value === 0) {
this.addTapCycle(TAPFile.SHORT_PULSE);
this.addTapCycle(TAPFile.MEDIUM_PULSE);
} else {
this.addTapCycle(TAPFile.MEDIUM_PULSE);
this.addTapCycle(TAPFile.SHORT_PULSE);
}
}
addDataMarker(moreToFollow: boolean): void {
if (moreToFollow) {
this.addTapCycle(TAPFile.LONG_PULSE);
this.addTapCycle(TAPFile.MEDIUM_PULSE);
} else {
this.addTapCycle(TAPFile.LONG_PULSE);
this.addTapCycle(TAPFile.SHORT_PULSE);
}
}
resetChecksum(): void {
this.checksum = 0;
}
addByteFrame(value: number, moreToFollow: boolean): void {
let checkBit = 1;
for (let i = 0; i < 8; i++) {
const bit = (value & (1 << i)) !== 0 ? 1 : 0;
this.addBit(bit);
checkBit ^= bit;
}
this.addBit(checkBit);
this.addDataMarker(moreToFollow);
this.checksum ^= value;
}
addLeader(fileType: number): void {
let numberofPulses;
if (fileType === TAPFile.LEADER_TYPE_HEADER) {
numberofPulses = 0x6a00;
} else if (fileType === TAPFile.LEADER_TYPE_CONTENT) {
numberofPulses = 0x1a00;
} else {
numberofPulses = 0x4f;
}
for (let i = 0; i < numberofPulses; i++) {
this.addTapCycle(TAPFile.SHORT_PULSE);
}
}
addSyncChain(repeated: boolean): void {
let value;
if (repeated) {
value = 0x09;
} else {
value = 0x89;
}
let count = 9;
while (count > 0) {
this.addByteFrame(value, true);
value -= 1;
count -= 1;
}
}
addData(): void {
for (let i = 0; i < this.data.length; i++) {
this.addByteFrame(this.data[i], true);
}
}
addFilename(): void {
for (let i = 0; i < this.filenameData.length; i++) {
this.addByteFrame(this.filenameData[i], true);
}
}
addHeader(repeated: boolean): void {
if (repeated) {
this.addLeader(TAPFile.LEADER_TYPE_REPEATED);
} else {
this.addLeader(TAPFile.LEADER_TYPE_HEADER);
}
this.addDataMarker(true);
this.addSyncChain(repeated);
this.resetChecksum();
this.addByteFrame(this.fileType, true);
this.addByteFrame(this.startAddress & 0x00ff, true);
this.addByteFrame((this.startAddress & 0xff00) >> 8, true);
this.addByteFrame(this.endAddress & 0x00ff, true);
this.addByteFrame((this.endAddress & 0xff00) >> 8, true);
this.addFilename();
for (let i = 0; i < TAPFile.NUMBER_OF_PADDING_BYTES; i++) {
this.addByteFrame(TAPFile.PADDING_CHARACTER, true);
}
this.addByteFrame(this.checksum, false);
}
addFile(): void {
let repeated = false;
for (let i = 0; i < 2; i++) {
if (!repeated) {
this.addLeader(TAPFile.LEADER_TYPE_CONTENT);
} else {
this.addLeader(TAPFile.LEADER_TYPE_REPEATED);
}
this.addDataMarker(true);
this.addSyncChain(repeated);
this.resetChecksum();
this.addData();
this.addByteFrame(this.checksum, false);
repeated = true;
}
this.addLeader(1);
}
}

View File

@ -21,6 +21,7 @@ import { isMobileDevice } from "./views/baseviews";
import { CallStackView, DebugBrowserView } from "./views/treeviews";
import { saveAs } from "file-saver";
import DOMPurify = require("dompurify");
import { OutputSoundFile, TAPFile } from "../common/audio/CommodoreTape";
// external libs (TODO)
declare var Tour, GIF, Octokat;
@ -1027,16 +1028,39 @@ function _downloadCassetteFile_vcs(e) {
});
}
function _downloadCassetteFile_c64(e) {
var prefix = getFilenamePrefix(getCurrentMainFilename());
let audpath = prefix + ".tap";
let tapmaker = new TAPFile(prefix);
let outfile = new OutputSoundFile({sine_wave:true});
let data = current_output;
let startAddress = data[0] + data[1]*256;
data = data.slice(2); // remove header
tapmaker.setContent({ data, startAddress, type: TAPFile.FILE_TYPE_NON_RELOCATABLE });
tapmaker.generateSound(outfile);
let tapout = outfile.getTAPData();
//let audout = outfile.getSoundData();
if (tapout) {
//let blob = new Blob([audout], { type: "audio/wav" });
let blob = new Blob([tapout], { type: "application/octet-stream" });
saveAs(blob, audpath);
}
}
function _getCassetteFunction() {
switch (getBasePlatform(platform_id)) {
case 'vcs': return _downloadCassetteFile_vcs;
case 'apple2': return _downloadCassetteFile_apple2;
case 'c64': return _downloadCassetteFile_c64;
}
}
function _downloadCassetteFile(e) {
if (current_output == null) {
alertError("Please fix errors before exporting.");
return true;
}
var fn;
switch (getBasePlatform(platform_id)) {
case 'vcs': fn = _downloadCassetteFile_vcs; break;
case 'apple2': fn = _downloadCassetteFile_apple2; break;
}
var fn = _getCassetteFunction();
if (fn === undefined) {
alertError("Cassette export is not supported on this platform.");
return true;
@ -1949,7 +1973,7 @@ function setupDebugControls() {
}
$("#item_download_allzip").click(_downloadAllFilesZipFile);
$("#item_record_video").click(_recordVideo);
if (platform_id.startsWith('apple2') || platform_id.startsWith('vcs')) // TODO: look for function
if (_getCassetteFunction())
$("#item_export_cassette").click(_downloadCassetteFile);
else
$("#item_export_cassette").hide();

View File

@ -4,6 +4,7 @@ import { Keys, PLATFORMS, RasterVideo } from "../common/emu";
const PCE_PRESETS = [
{ id: 'test_conio.c', name: 'Hello World (conio)' },
{ id: 'siegegame.c', name: 'Siege Game (conio)' },
{ id: 'hello.wiz', name: 'Hello World (Wiz)' },
]
class PCEnginePlatform implements Platform {